View Source Styler.Style behaviour (Styler v1.2.1)
A Style takes AST and returns a transformed version of that AST.
Because these transformations involve traversing trees (the "T" in "AST"), we wrap the AST in a structure called a Zipper to facilitate walking the trees.
Summary
Callbacks
run
will be used with Zipper.traverse_while/3
, meaning it will be executed on every node of the AST.
Functions
Set the line of all comments with line
in range_start..range_end
to instead have line range_start
Returns the current node (wrapped in a __block__
if necessary) if it's a valid place to insert additional nodes
Returns a zipper focused on the nearest node where additional nodes can be inserted (a "block").
"Fixes" the line numbers of nodes who have had their orders changed via sorting or other methods. This "fix" simply ensures that comments don't get wrecked as part of us moving AST nodes willy-nilly.
Takes a list of nodes and clumps them up, setting end_of_expression: [newlines: x]
to 1 for all but the final node,
which gets 2 instead, (hopefully!) creating an empty line before whatever follows.
Recursively sets :line
meta to line
. Deletes :newlines
unless delete_lines: false
is passed
Perform a series of shifts in a single pass.
Change the line
of all comments with line
in range
by adding delta
to it.
A positive delta will move the lines further down a file, while a negative delta will move them up.
Recursively updates :line
meta by adding delta
Traverses an ast node, updating all nodes' meta with meta_fun
Types
Callbacks
@callback run(Styler.Zipper.t(), context()) :: {Styler.Zipper.command(), Styler.Zipper.t(), context()}
run
will be used with Zipper.traverse_while/3
, meaning it will be executed on every node of the AST.
You can skip traversing parts of the tree by returning a Zipper that's further along in the traversal, for example
by calling Zipper.skip(zipper)
to skip an entire subtree you know is of no interest to your Style.
Functions
Set the line of all comments with line
in range_start..range_end
to instead have line range_start
@spec ensure_block_parent(Styler.Zipper.t()) :: {:ok, Styler.Zipper.t()} | :error
Returns the current node (wrapped in a __block__
if necessary) if it's a valid place to insert additional nodes
@spec find_nearest_block(Styler.Zipper.t()) :: Styler.Zipper.t()
Returns a zipper focused on the nearest node where additional nodes can be inserted (a "block").
The nearest node is either the current node, an ancestor, or one of those two but wrapped in a new :__block__
node.
"Fixes" the line numbers of nodes who have had their orders changed via sorting or other methods. This "fix" simply ensures that comments don't get wrecked as part of us moving AST nodes willy-nilly.
The fix is rather naive, and simply enforces the following property on the code: A given node must have a line number less than the following node. Et voila! Comments behave much better.
In Detail
For example, given document
1: defmodule ... 2: alias B 3: # this is foo 4: def foo ... 5: alias A
Sorting aliases the ast node for would put alias A
(line 5) before alias B
(line 2).
1: defmodule ... 5: alias A 2: alias B 3: # this is foo 4: def foo ...
Elixir's document algebra would then encounter line: 5
and immediately dump all comments with line <= 5
,
meaning after running through the formatter we'd end up with
1: defmodule 2: # hi 3: # this is foo 4: alias A 5: alias B 6: 7: def foo ...
This function fixes that by seeing that alias A
has a higher line number than its following sibling alias B
and so
updates alias A
's line to be preceding alias B
's line.
Running the results of this function through the formatter now no longer dumps the comments prematurely
1: defmodule ... 2: alias A 3: alias B 4: # this is foo 5: def foo ...
Takes a list of nodes and clumps them up, setting end_of_expression: [newlines: x]
to 1 for all but the final node,
which gets 2 instead, (hopefully!) creating an empty line before whatever follows.
Recursively sets :line
meta to line
. Deletes :newlines
unless delete_lines: false
is passed
Perform a series of shifts in a single pass.
When shifting comments from block A to block B, naively using two passes of shift_comments/3
would result
in all comments ending up in either region A or region B (because A would move to B, then all B back to A)
This function exists to make sure that a comment is only moved once during the swap.
Change the line
of all comments with line
in range
by adding delta
to it.
A positive delta will move the lines further down a file, while a negative delta will move them up.
Recursively updates :line
meta by adding delta
Traverses an ast node, updating all nodes' meta with meta_fun