Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions CodeFormatter/Kernel/CodeFormatter.wl
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,94 @@ Options[CodeFormat] = {
"NewlinesInGroups" -> Automatic,
"NewlinesInScoping" -> Automatic,

(*
When deciding whether a scoping (Module/Block/With/Function) or control
(If/Switch/Which/...) construct must break to its fully-exploded form, look
at the width of the *leading* line (the head plus the binding list or
condition, e.g. `Block[{a, b},`) rather than the maximum width of the whole
construct. With this True the binding list / condition stays on the opener
line whenever that line fits, even if a body line is long:

Block[{a, b},
aLongBodyLineThatExceedsTheLineWidth[...]
]

When False (the default) a long body line forces the bindings onto their own
exploded lines.
*)
"KeepBindingsInline" -> False,

(*
When True, a space is inserted after the opener of a multiline control
structure (If, Switch, Which, ...) so the condition lines up with the
branches below it:

If[ cond,
then
,
else
]

Defaults to False (no space).
*)
"SpaceAfterControlOpener" -> False,

(*
When True, the RHS of a top-level assignment is kept on the operator line
instead of breaking after the operator:

f[x] := Block[{a, b}, (* True *)
body
]

f[x] := (* False, the default *)
Block[{a, b},
body
]

Defaults to False.
*)
"GlueAssignmentRHS" -> False,

(*
When True, a comma separating single-line elements is kept at the end of the
element's line:

{
a,
b,
c
}

rather than on its own line. When an element is itself multiline, the comma
still goes on its own line so it reads as an element boundary. Defaults to
False.
*)
"TrailingCommas" -> False,

(*
When True, a control structure (If/Switch/Which/...) whose arguments are all
single-line and which fits within LineWidth is kept on one line instead of
always being broken across lines (`If[a, b, c]`). When a branch is itself
multiline, or the one-line form would exceed LineWidth, the structure is
broken as usual. Defaults to False (the traditional always-break behavior).
*)
"InlineShortControl" -> False,

(*
When True, a space is inserted after a prefix Not, so `!cond` becomes
`! cond`. Defaults to False.
*)
"SpaceAfterPrefixNot" -> False,

(*
When True, the pattern operators Pattern (:), Optional (:), and PatternTest
(?) are spaced like ordinary operators (`name : pat`, `x_ ? NumericQ`) instead
of being kept tight. MessageName (::) and Power (^) remain tight. Defaults to
False.
*)
"SpaceAroundPatternOperators" -> False,

(*
Undocumented options
*)
Expand Down Expand Up @@ -440,6 +528,14 @@ Module[{
style["NewlinesInGroups"] = OptionValue["NewlinesInGroups"];
style["NewlinesInScoping"] = OptionValue["NewlinesInScoping"];

style["KeepBindingsInline"] = OptionValue["KeepBindingsInline"];
style["SpaceAfterControlOpener"] = OptionValue["SpaceAfterControlOpener"];
style["GlueAssignmentRHS"] = OptionValue["GlueAssignmentRHS"];
style["TrailingCommas"] = OptionValue["TrailingCommas"];
style["InlineShortControl"] = OptionValue["InlineShortControl"];
style["SpaceAfterPrefixNot"] = OptionValue["SpaceAfterPrefixNot"];
style["SpaceAroundPatternOperators"] = OptionValue["SpaceAroundPatternOperators"];

airiness = OptionValue[Airiness];
If[airiness === Automatic,
airiness = 0.0
Expand Down
131 changes: 117 additions & 14 deletions CodeFormatter/Kernel/Indent.wl
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ Module[{indentedRator, indentedRandSeq, indentedRand, indentedRandMultiline,
children =
Flatten[{
indentedRator,
If[tag === Not && TrueQ[$CurrentStyle["SpaceAfterPrefixNot"]], space[], Nothing],
indentedRandSeq,
indentedRand
}];
Expand Down Expand Up @@ -823,7 +824,18 @@ Module[{aggs, ratorsPat, split, definitelyDelete, definitelyInsert, definitelyAu

Which[
TrueQ[definitelyInsert],
split = {#}& /@ indentedGraphs;
(*
With "TrailingCommas" -> True, single-line elements keep the comma at
the end of the element line (a, b, c) rather than on its own line; when
any element is itself multiline the comma stays on its own line so it
reads as an element boundary.
*)
split =
If[TrueQ[$CurrentStyle["TrailingCommas"]] && !TrueQ[anyIndentedGraphsMultiline],
Split[indentedGraphs, MatchQ[#2, ratorsPat]&]
,
{#}& /@ indentedGraphs
];
,
TrueQ[definitelyDelete],
split = {indentedGraphs};
Expand Down Expand Up @@ -900,8 +912,16 @@ a^b
10^-9

*)
indentInfixRatorSurroundedByLeafs[Pattern | Optional | PatternTest | MessageName | Power][rator_] :=
rator
(*
Pattern (:), Optional (:), and PatternTest (?) are tight by default, but with
"SpaceAroundPatternOperators" -> True they are spaced like ordinary operators
(name : pat, x_ ? NumericQ). MessageName (::) and Power (^) stay tight either way.
*)
indentInfixRatorSurroundedByLeafs[tag : Pattern | Optional | PatternTest | MessageName | Power][rator_] :=
If[TrueQ[$CurrentStyle["SpaceAroundPatternOperators"]] && MatchQ[tag, Pattern | Optional | PatternTest],
indentInfixRator[tag][rator],
rator
]

indentInfixRatorSurroundedByLeafs[tag_][rator_] :=
indentInfixRator[tag][rator]
Expand Down Expand Up @@ -1118,7 +1138,7 @@ Module[{aggs, rators, ratorsPat, split,

Always line break after last rator, so redo newlines
*)
MemberQ[$SpecialBreakAfterLastRator, tag] && $Toplevel,
(MemberQ[$SpecialBreakAfterLastRator, tag] && $Toplevel) || (TrueQ[$CurrentStyle["GlueAssignmentRHS"]] && MemberQ[{Set, SetDelayed, UpSet, UpSetDelayed, TagSet, TagSetDelayed}, tag]),
(* lastRator = rators[[-1]];
lastRatorPos = Position[indentedGraphs, lastRator][[1]];
split = TakeDrop[indentedGraphs, lastRatorPos[[1]]]; *)
Expand Down Expand Up @@ -1177,9 +1197,18 @@ Module[{aggs, rators, ratorsPat, split,
,
TrueQ[definitelyAutomatic],
Which[
MemberQ[$SpecialBreakAfterLastRator, tag] && $Toplevel,
blockAfterLastRator @
(MemberQ[$SpecialBreakAfterLastRator, tag] && $Toplevel) || (TrueQ[$CurrentStyle["GlueAssignmentRHS"]] && MemberQ[{Set, SetDelayed, UpSet, UpSetDelayed, TagSet, TagSetDelayed}, tag]),
If[TrueQ[$CurrentStyle["GlueAssignmentRHS"]],
(*
Keep the RHS head on the operator line (f[x] := Block[{...},) instead
of breaking after the operator (f[x] :=\n Block[...]). The RHS node
supplies its own body indentation, so do not increment here.
*)
baseOperatorNodeIndent[type, tag, data, split, ratorsPat, anyIndentedGraphsMultiline, infixRatorSurroundedBy]
,
blockAfterLastRator @
baseOperatorNodeIndent[type, tag, data, split, ratorsPat, anyIndentedGraphsMultiline, infixRatorSurroundedBy]
]
(*
$Toplevel, so do not re-format if exceeding LineWidth, cannot re-indent safely at $Toplevel
*)
Expand Down Expand Up @@ -1596,7 +1625,7 @@ Module[{indentedHead, indentedCommaNode,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInScoping" -> Insert
*)
Expand Down Expand Up @@ -1711,7 +1740,7 @@ Module[{indentedHead,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInScoping" -> Insert
*)
Expand Down Expand Up @@ -1773,6 +1802,18 @@ Module[{indentedHead, indentedCommaNode,
commaChildren, groupChildren, child,
commaExtent, groupExtent, extent},

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -1850,6 +1891,7 @@ Module[{indentedHead, indentedCommaNode,
groupChildren =
Flatten[{
indent[opener],
If[TrueQ[$CurrentStyle["SpaceAfterControlOpener"]], space[], Nothing],
indent /@ {openerSeq},
blockAfterFirstRator @
InfixNode[Comma,
Expand All @@ -1873,7 +1915,7 @@ Module[{indentedHead, indentedCommaNode,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down Expand Up @@ -1908,6 +1950,18 @@ Module[{indentedTest, indentedTestSeq, indentedComma1, indentedVal, indentedValS
indentedVal = indent[val];
indentedValSeq = indent /@ {valSeq};

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -1965,7 +2019,7 @@ Module[{indentedTest, indentedTestSeq, indentedComma1, indentedVal, indentedValS

extent = computeExtent[children];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down Expand Up @@ -2019,6 +2073,18 @@ Module[{indentedHead, indentedCommaNode,
commaChildren, groupChildren, child,
commaExtent, groupExtent, extent},

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -2091,6 +2157,7 @@ Module[{indentedHead, indentedCommaNode,
groupChildren =
Flatten[{
indent[opener],
If[TrueQ[$CurrentStyle["SpaceAfterControlOpener"]], space[], Nothing],
indent[Insert[#, EndOfLine -> True, {3, 1}]]& /@ {openerSeq},
block @ {
InfixNode[Comma,
Expand All @@ -2115,7 +2182,7 @@ Module[{indentedHead, indentedCommaNode,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down Expand Up @@ -2175,6 +2242,18 @@ Module[{indentedHead, indentedCommaNode,
commaChildren, groupChildren, child,
commaExtent, groupExtent, extent},

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -2284,7 +2363,7 @@ Module[{indentedHead, indentedCommaNode,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down Expand Up @@ -2342,6 +2421,18 @@ Module[{indentedHead,
commaChildren, groupChildren, child,
commaExtent, groupExtent, extent},

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -2398,7 +2489,7 @@ Module[{indentedHead,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down Expand Up @@ -2501,6 +2592,18 @@ Module[{indentedHead,
commaChildren, groupChildren, child,
commaExtent, groupExtent, extent},

(*
InlineShortControl: if the whole control structure fits on one line and no
branch is itself multiline, keep it inline rather than always breaking.
*)
If[TrueQ[$CurrentStyle["InlineShortControl"]] && MatchQ[node, CallNode[_, _, _]],
With[{inlined = commonCallNodeIndent[node]},
If[!TrueQ[Lookup[inlined[[3]], "Multiline", False]] && getExtent[inlined][[1]] < $CurrentStyle["LineWidth"],
Throw[inlined]
]
]
];

definitelyDelete = OptionValue["NewlinesInControl"] === Delete || $CurrentStyle["NewlinesInControl"] === Delete;
definitelyInsert = OptionValue["NewlinesInControl"] === Insert || $CurrentStyle["NewlinesInControl"] === Insert;

Expand Down Expand Up @@ -2557,7 +2660,7 @@ Module[{indentedHead,
extent = computeExtent[indentedHead ~Join~ {child}];
];

If[extent[[1]] >= $CurrentStyle["LineWidth"],
If[extent[[If[TrueQ[$CurrentStyle["KeepBindingsInline"]], 3, 1]]] >= $CurrentStyle["LineWidth"],
(*
exceeding LineWidth, so re-indent with "NewlinesInControl" -> Insert
*)
Expand Down
Loading