View Source Styler.Style behaviour (Styler v1.1.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

@type context() :: %{comments: [map()], file: :stdin | String.t()}

Callbacks

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

Link to this function

displace_comments(comments, range)

View Source

Set the line of all comments with line in range_start..range_end to instead have line range_start

Link to this function

ensure_block_parent(zipper)

View Source
@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

Link to this function

find_nearest_block(zipper)

View Source
@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.

Link to this function

fix_line_numbers(nodes, max)

View Source

"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.

Link to this function

reset_newlines(list, acc)

View Source
Link to this function

set_line(ast_node, line, opts \\ [])

View Source

Recursively sets :line meta to line. Deletes :newlines unless delete_lines: false is passed

Link to this function

shift_comments(comments, shifts)

View Source

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.

Link to this function

shift_comments(comments, range, delta)

View Source

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.

Link to this function

shift_line(ast_node, delta)

View Source

Recursively updates :line meta by adding delta

Link to this function

update_all_meta(node, meta_fun)

View Source

Traverses an ast node, updating all nodes' meta with meta_fun