Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indentation rework #1562

Merged
merged 22 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
- [Guides](./guides/README.md)
- [Adding Languages](./guides/adding_languages.md)
- [Adding Textobject Queries](./guides/textobject.md)
- [Adding Indent Queries](./guides/indent.md)
79 changes: 79 additions & 0 deletions book/src/guides/indent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Adding Indent Queries

Helix uses tree-sitter to correctly indent new lines. This requires
a tree-sitter grammar and an `indent.scm` query file placed in
`runtime/queries/{language}/indents.scm`. The indentation for a line
is calculated by traversing the syntax tree from the lowest node at the
beginning of the new line. Each of these nodes contributes to the total
indent when it is captured by the query (in what way depends on the name
of the capture).

Note that it matters where these added indents begin. For example,
multiple indent level increases that start on the same line only increase
the total indent level by 1.

## Scopes

Added indents don't always apply to the whole node. For example, in most
cases when a node should be indented, we actually only want everything
except for its first line to be indented. For this, there are several
scopes (more scopes may be added in the future if required):

- `all`:
This scope applies to the whole captured node. This is only different from
`tail` when the captured node is the first node on its line.

- `tail`:
This scope applies to everything except for the first line of the
captured node.

Every capture type has a default scope which should do the right thing
in most situations. When a different scope is required, this can be
changed by using a `#set!` declaration anywhere in the pattern:
```scm
(assignment_expression
right: (_) @indent
(#set! "scope" "all"))
```

## Capture Types

- `@indent` (default scope `tail`):
Increase the indent level by 1. Multiple occurences in the same line
don't stack. If there is at least one `@indent` and one `@outdent`
capture on the same line, the indent level isn't changed at all.

- `@outdent` (default scope `all`):
Decrease the indent level by 1. The same rules as for `@indent` apply.

## Predicates

In some cases, an S-expression cannot express exactly what pattern should be matched.
For that, tree-sitter allows for predicates to appear anywhere within a pattern,
similar to how `#set!` declarations work:
```scm
(some_kind
(child_kind) @indent
(#predicate? arg1 arg2 ...)
)
```
The number of arguments depends on the predicate that's used.
Each argument is either a capture (`@name`) or a string (`"some string"`).
The following predicates are supported by tree-sitter:

- `#eq?`/`#not-eq?`:
The first argument (a capture) must/must not be equal to the second argument
(a capture or a string).

- `#match?`/`#not-match?`:
The first argument (a capture) must/must not match the regex given in the
second argument (a string).

Additionally, we support some custom predicates for indent queries:

- `#not-kind-eq?`:
The kind of the first argument (a capture) must not be equal to the second
argument (a string).

- `#same-line?`/`#not-same-line?`:
The captures given by the 2 arguments must/must not start on the same line.
Loading