Skip to content

Commit

Permalink
Rework function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
piegamesde committed May 19, 2023
1 parent 487d313 commit 6ce18ad
Show file tree
Hide file tree
Showing 13 changed files with 1,061 additions and 820 deletions.
38 changes: 22 additions & 16 deletions src/Nixfmt/Predoc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ data DocAnn
-- in docs should be converted to line breaks. This does not affect softlines,
-- those will be expanded only as necessary and with a lower priority.
--
-- The boolean argument determines how to handle whitespace directly before the
-- group or at the start of the group. By default (False), it gets pulled out
-- in front of the group, which is what you want in most cases. If set to True,
-- whitespace before the group will be pulled in instead.
= Group Bool
-- The boolean arguments determine how to handle whitespace directly before/after the
-- group or at the start/end of the group. By default (False), it gets pulled out the
-- group, which is what you want in most cases. If set to True,
-- whitespace before/after the group will be pulled in instead.
= Group Bool Bool
-- | Node (Nest n) doc indicates all line starts in doc should be indented
-- by n more spaces than the surrounding Base.
| Nest Int
Expand Down Expand Up @@ -110,12 +110,15 @@ text t = [Text t]
-- | Group document elements together (see Node Group documentation)
-- Any whitespace at the start of the group will get pulled out in front of it.
group :: Pretty a => a -> Doc
group = pure . Node (Group False) . pretty
group = pure . Node (Group False False) . pretty

-- | Group document elements together (see Node Group documentation)
-- Any whitespace directly before the group will be pulled into it.
group' :: Pretty a => a -> Doc
group' = pure . Node (Group True) . pretty
-- Any whitespace directly before and/or after the group will be pulled into it.
-- Use with caution, and only in situations where you control the surroundings of
-- that group. Especially, never use as a top-level element of a `pretty` instance,
-- or you'll get some *very* confusing bugs …
group' :: Pretty a => Bool -> Bool -> a -> Doc
group' pre post = pure . Node (Group pre post) . pretty

-- | @nest n doc@ sets the indentation for lines in @doc@ to @n@ more than the
-- indentation of the part before it. This is based on the actual indentation of
Expand Down Expand Up @@ -216,10 +219,13 @@ moveLinesIn :: Doc -> Doc
moveLinesIn [] = []
-- Move space before Nest in
moveLinesIn (Spacing l : Node (Nest level) xs : ys) =
Node (Nest level) (moveLinesIn (Spacing l : xs)) : moveLinesIn ys
-- Move space before (Group True) in
moveLinesIn (Spacing l : Node (Group True) xs : ys) =
Node (Group False) (moveLinesIn (Spacing l : xs)) : moveLinesIn ys
moveLinesIn ((Node (Nest level) (Spacing l : xs)) : ys)
-- Move space before (Group True _) in
moveLinesIn (Spacing l : Node ann@(Group True _) xs : ys) =
moveLinesIn ((Node ann (Spacing l : xs)) : ys)
-- Move space after (Group _ True) in
moveLinesIn (Node ann@(Group _ True) xs : Spacing l : ys) =
moveLinesIn ((Node ann (xs ++ [Spacing l])) : ys)

moveLinesIn (Node ann xs : ys) =
Node ann (moveLinesIn xs) : moveLinesIn ys
Expand Down Expand Up @@ -273,7 +279,7 @@ firstLineFits targetWidth maxWidth docs = go maxWidth docs
go c (Text t : xs) = go (c - textWidth t) xs
go c (Spacing Hardspace : xs) = go (c - 1) xs
go c (Spacing _ : _) = maxWidth - c <= targetWidth
go c (Node (Group _) ys : xs) =
go c (Node (Group _ _) ys : xs) =
case fits (c - firstLineWidth xs) ys of
Nothing -> go c (ys ++ xs)
Just t -> go (c - textWidth t) xs
Expand All @@ -298,7 +304,7 @@ unChunk (Chunk _ doc) = doc
-- Only for the tokens starting on the next line the current
-- indentation will match the target indentation.
layoutGreedy :: Int -> Doc -> Text
layoutGreedy tw doc = Text.concat $ go 0 0 [Chunk 0 $ Node (Group False) doc]
layoutGreedy tw doc = Text.concat $ go 0 0 [Chunk 0 $ Node (Group False False) doc]
where go :: Int -> Int -> [Chunk] -> [Text]
go _ _ [] = []
go cc ci (Chunk ti x : xs) = case x of
Expand All @@ -324,7 +330,7 @@ layoutGreedy tw doc = Text.concat $ go 0 0 [Chunk 0 $ Node (Group False) doc]

Node (Nest l) ys -> go cc ci $ map (Chunk (ti + l)) ys ++ xs
Node Base ys -> go cc ci $ map (Chunk ci) ys ++ xs
Node (Group _) ys ->
Node (Group _ _) ys ->
-- Does the group (plus whatever comes after it on that line) fit in one line?
-- This is where treating whitespace as "compact" happens
case fits (tw - cc - firstLineWidth (map unChunk xs)) ys of
Expand Down
44 changes: 33 additions & 11 deletions src/Nixfmt/Pretty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

module Nixfmt.Pretty where

import Debug.Trace
import Prelude hiding (String)

import Data.Char (isSpace)
Expand Down Expand Up @@ -79,7 +80,7 @@ instance Pretty Binder where
-- `inherit (foo) bar` statement
pretty (Inherit inherit source ids semicolon)
= base $ group (pretty inherit <> hardspace <> nest 2 (
(group' (line <> pretty source)) <> line
(group' True False (line <> pretty source)) <> line
<> sepBy line ids
<> line' <> pretty semicolon
))
Expand All @@ -95,7 +96,7 @@ instance Pretty Binder where
(Term t) | isAbsorbable t -> hardspace <> group expr <> pretty semicolon
-- Non-absorbable term
-- If it is multi-line, force it to start on a new line with indentation
(Term _) -> group' (line <> pretty expr) <> pretty semicolon
(Term _) -> group' True False (line <> pretty expr) <> pretty semicolon
-- Function calls and with expressions
-- Try to absorb and keep the semicolon attached, spread otherwise
(Application _ _) -> group (softline <> pretty expr <> softline' <> pretty semicolon)
Expand Down Expand Up @@ -243,11 +244,6 @@ absorbElse (If if_ cond then_ expr0 else_ expr1)
absorbElse x
= hardline <> nest 2 (group x)

absorbApp :: Expression -> Doc
absorbApp (Application f x) = softline <> pretty f <> absorbApp x
absorbApp (Term t) | isAbsorbable t = hardspace <> group (prettyTerm t)
absorbApp x = softline <> pretty x

instance Pretty Expression where
pretty (Term t) = pretty t

Expand Down Expand Up @@ -288,25 +284,51 @@ instance Pretty Expression where
pretty (Abstraction param colon body)
= pretty param <> pretty colon <> absorbSet body

pretty (Application f x) = group $ pretty f <> absorbApp x
-- Function application
-- Some example mapping of Nix code to Doc (using parentheses as groups, but omitting the outermost group
-- and groups around the expressions for conciseness):
-- `f a` -> (f line*) a
-- `f g a` -> (f line g line*) a
-- `f g h a` -> ((f line g) line h line*) a
-- `f g h i a` -> (((f line g) line h) line i line*) a
-- As you can see, it separates the elements by `line` whitespace. However, there are two tricks to make it look good:
-- Firstly, for each function call (imagine the fully parenthesised Nix code), we group it. Due to the greedy expansion
-- of groups this means that it will place as many function calls on the first line as possible, but then all the remaining
-- ones on a separate line each.
-- Secondly, the `line` between the second-to-last and last argument (marked with asterisk above) is moved into its preceding
-- group. This allows the last argument to be multi-line without forcing the preceding arguments to be multiline.
pretty (Application f a)
= let
absorbApp (Application f' a') = (group $ absorbApp f') <> line <> (group a')
absorbApp expr = pretty expr

absorbLast (Term t) | isAbsorbable t
= prettyTerm t
absorbLast (Term (Parenthesized open expr close))
= base $ group $ pretty open <> line' <> nest 2 (group expr) <> line' <> pretty close
absorbLast arg = group arg
in
group $
(group' False True $ (absorbApp f) <> line) <> (absorbLast a)

-- '//' operator
pretty operation@(Operation a op@(Ann TUpdate _ _) b)
pretty (Operation a op@(Ann TUpdate _ _) b)
= pretty a <> softline <> pretty op <> hardspace <> pretty b
-- all other operators
pretty operation@(Operation _ op _)
= let
-- Walk the operation tree and put a list of things on the same level
flatten :: Expression -> [Expression]
flatten (Operation a op' b) | op' == op = (flatten a) ++ (flatten b)
flatten x = [x]
flattened = flatten operation

-- Some children need nesting
absorbOperation :: Expression -> Doc
absorbOperation (Term t) | isAbsorbable t = pretty t
absorbOperation x@(Operation _ _ _) = nest 2 (pretty x)
absorbOperation x = base $ nest 2 (pretty x)
in
group $ sepBy (line <> pretty op <> hardspace) (map absorbOperation flattened)
group $ (sepBy (line <> pretty op <> hardspace) . map absorbOperation . flatten) operation

pretty (MemberCheck expr qmark sel)
= pretty expr <> softline
Expand Down
30 changes: 30 additions & 0 deletions test/diff/apply/in.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@

name2 = function arg {
asdf = 1;
# multiline
}
argument;

name3 = function arg {
asdf = 1;
# multiline
} {
qwer = 12345;
}
Expand Down Expand Up @@ -97,4 +99,32 @@
# For each supported platform,
utils.lib.eachDefaultSystem (system: {});
}
{
escapeSingleline = libStr.escape [
"\\"
''"''
"\${"
];
escapeMultiline =
libStr.replaceStrings
[
"\${"
"''"
]
[
"''\${"
"'''"
];
test = foo
[ # multiline
1 2 3
]
[]
{}
[]
[ 1 2 3 # multiline
];
looooooooong = (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys aaaaaaaa; } sections);
looooooooong' = toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys aaaaaaaa; } sections;
}
]
126 changes: 96 additions & 30 deletions test/diff/apply/out.nix
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
[
(a b)
((a b) (a b) (a # b
c) ( # a
((a b) (a b)
(a # b
c)
( # a
b # c
d # e
))
''
otherModules=${
pkgs.writeText "other-modules.json" (l.toJSON (l.mapAttrs (
pname: subOutputs:
let
pkg = subOutputs.packages."${pname}".overrideAttrs (old: {
buildScript = "true";
installMethod = "copy";
});
in
"${pkg}/lib/node_modules/${pname}/node_modules"
) outputs.subPackages))
pkgs.writeText "other-modules.json" (
l.toJSON (
l.mapAttrs
(
pname: subOutputs:
let
pkg = subOutputs.packages."${pname}".overrideAttrs (
old: {
buildScript = "true";
installMethod = "copy";
}
);
in
"${pkg}/lib/node_modules/${pname}/node_modules"
)
outputs.subPackages
)
)
}
''
{
Expand All @@ -29,31 +39,50 @@
{
name1 = function arg { asdf = 1; };

name2 = function arg { asdf = 1; } argument;
name2 = function arg
{
asdf = 1;
# multiline
}
argument;

name3 = function arg { asdf = 1; } { qwer = 12345; } argument;
name3 = function arg
{
asdf = 1;
# multiline
}
{ qwer = 12345; }
argument;
}
{
name4 = function arg { asdf = 1; } {
qwer = 12345;
qwer2 = 54321;
} argument;
name4 = function arg { asdf = 1; }
{
qwer = 12345;
qwer2 = 54321;
}
argument;
}
{
option1 = function arg { asdf = 1; } {
qwer = 12345;
qwer2 = 54321;
} lastArg;
option1 = function arg { asdf = 1; }
{
qwer = 12345;
qwer2 = 54321;
}
lastArg;

option2 = function arg { asdf = 1; } {
qwer = 12345;
qwer2 = 54321;
} lastArg;
option2 = function arg { asdf = 1; }
{
qwer = 12345;
qwer2 = 54321;
}
lastArg;

option3 = function arg { asdf = 1; } {
qwer = 12345;
qwer2 = 54321;
} lastArg;
option3 = function arg { asdf = 1; }
{
qwer = 12345;
qwer2 = 54321;
}
lastArg;
}
# https://github.com/kamadorueda/alejandra/issues/372#issuecomment-1435083516
{
Expand All @@ -65,4 +94,41 @@
utils.lib.eachDefaultSystem (system: { })
;
}
{
escapeSingleline = libStr.escape [
"\\"
''"''
"\${"
];
escapeMultiline = libStr.replaceStrings
[
"\${"
"''"
]
[
"''\${"
"'''"
];
test = foo
[ # multiline
1
2
3
]
[ ]
{ }
[ ]
[
1
2
3 # multiline
];
looooooooong =
(toINI
{ inherit mkSectionName mkKeyValue listsAsDuplicateKeys aaaaaaaa; }
sections);
looooooooong' = toINI
{ inherit mkSectionName mkKeyValue listsAsDuplicateKeys aaaaaaaa; }
sections;
}
]
Loading

0 comments on commit 6ce18ad

Please sign in to comment.