From 827c9ac50694240b91fa43ae3457732c526b1bb4 Mon Sep 17 00:00:00 2001 From: omentic Date: Wed, 1 Nov 2023 04:44:44 +0000 Subject: deploy: 9663740245e2c18e160f6563d2f114f59d27b7c9 --- guides/adding_languages.html | 289 +++++++++++++++++++ guides/indent.html | 550 ++++++++++++++++++++++++++++++++++++ guides/index.html | 236 ++++++++++++++++ guides/injection.html | 298 +++++++++++++++++++ guides/rainbow_bracket_queries.html | 323 +++++++++++++++++++++ guides/textobject.html | 270 ++++++++++++++++++ 6 files changed, 1966 insertions(+) create mode 100644 guides/adding_languages.html create mode 100644 guides/indent.html create mode 100644 guides/index.html create mode 100644 guides/injection.html create mode 100644 guides/rainbow_bracket_queries.html create mode 100644 guides/textobject.html (limited to 'guides') diff --git a/guides/adding_languages.html b/guides/adding_languages.html new file mode 100644 index 00000000..13781aba --- /dev/null +++ b/guides/adding_languages.html @@ -0,0 +1,289 @@ + + + + + + Adding languages + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Adding new languages to Helix

+

In order to add a new language to Helix, you will need to follow the steps +below.

+

Language configuration

+
    +
  1. Add a new [[language]] entry in the languages.toml file and provide the +necessary configuration for the new language. For more information on +language configuration, refer to the +language configuration section of the documentation. +A new language server can be added by extending the [language-server] table in the same file.
  2. +
  3. If you are adding a new language or updating an existing language server +configuration, run the command cargo xtask docgen to update the +Language Support documentation.
  4. +
+
+

๐Ÿ’ก If you are adding a new Language Server configuration, make sure to update +the +Language Server Wiki +with the installation instructions.

+
+

Grammar configuration

+
    +
  1. If a tree-sitter grammar is available for the new language, add a new +[[grammar]] entry to the languages.toml file.
  2. +
  3. If you are testing the grammar locally, you can use the source.path key +with an absolute path to the grammar. However, before submitting a pull +request, make sure to switch to using source.git.
  4. +
+

Queries

+
    +
  1. In order to provide syntax highlighting and indentation for the new language, +you will need to add queries.
  2. +
  3. Create a new directory for the language with the path +runtime/queries/<name>/.
  4. +
  5. Refer to the +tree-sitter website +for more information on writing queries.
  6. +
  7. A list of highlight captures can be found on the themes page.
  8. +
+
+

๐Ÿ’ก In Helix, the first matching query takes precedence when evaluating +queries, which is different from other editors such as Neovim where the last +matching query supersedes the ones before it. See +this issue +for an example.

+
+

Common issues

+
    +
  • If you encounter errors when running Helix after switching branches, you may +need to update the tree-sitter grammars. Run the command hx --grammar fetch +to fetch the grammars and hx --grammar build to build any out-of-date +grammars.
  • +
  • If a parser is causing a segfault, or you want to remove it, make sure to +remove the compiled parser located at runtime/grammars/<name>.so.
  • +
  • If you are attempting to add queries and Helix is unable to locate them, ensure that the environment variable HELIX_RUNTIME is set to the location of the runtime folder you're developing in.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + diff --git a/guides/indent.html b/guides/indent.html new file mode 100644 index 00000000..a968f7bb --- /dev/null +++ b/guides/indent.html @@ -0,0 +1,550 @@ + + + + + + Adding indent queries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

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 (see +Indent queries). 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. See Capture types.

+

Indent queries

+

When Helix is inserting a new line through o, O, or <ret>, to determine +the indent level for the new line, the query in indents.scm is run on the +document. The starting position of the query is the end of the line above where +a new line will be inserted.

+

For o, the inserted line is the line below the cursor, so that starting +position of the query is the end of the current line.

+
#![allow(unused)]
+fn main() {
+fn need_hero(some_hero: Hero, life: Life) -> {
+    matches!(some_hero, Hero { // โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
+        strong: true,//โ†โ•ฎ  โ†‘  โ†‘                     โ”‚
+        fast: true,  // โ”‚  โ”‚  โ•ฐโ”€โ”€ query start       โ”‚
+        sure: true,  // โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€ cursor            โ”œโ”€ traversal 
+        soon: true,  // โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ new line inserted โ”‚  start node
+    }) &&            //                             โ”‚
+//  โ†‘                                               โ”‚
+//  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
+    some_hero > life
+}
+}
+

For O, the newly inserted line is the current line, so the starting position +of the query is the end of the line above the cursor.

+
#![allow(unused)]
+fn main() {
+fn need_hero(some_hero: Hero, life: Life) -> { // โ†โ”€โ•ฎ
+    matches!(some_hero, Hero { // โ†โ•ฎ          โ†‘     โ”‚
+        strong: true,//    โ†‘   โ•ญโ”€โ”€โ”€โ•ฏ          โ”‚     โ”‚
+        fast: true,  //    โ”‚   โ”‚ query start โ”€โ•ฏ     โ”‚
+        sure: true,  //    โ•ฐโ”€โ”€โ”€โ”ผ cursor             โ”œโ”€ traversal
+        soon: true,  //        โ•ฐ new line inserted  โ”‚  start node
+    }) &&            //                             โ”‚
+    some_hero > life //                             โ”‚
+} // โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
+}
+

From this starting node, the syntax tree is traversed up until the root node. +Each indent capture is collected along the way, and then combined according to +their capture types and scopes to a final indent +level for the line.

+

Capture types

+
    +
  • @indent (default scope tail): +Increase the indent level by 1. Multiple occurrences in the same line do not +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.
  • +
  • @indent.always (default scope tail): +Increase the indent level by 1. Multiple occurrences on the same line do +stack. The final indent level is @indent.always โ€“ @outdent.always. If +an @indent and an @indent.always are on the same line, the @indent is +ignored.
  • +
  • @outdent.always (default scope all): +Decrease the indent level by 1. The same rules as for @indent.always apply.
  • +
  • @align (default scope all): +Align everything inside this node to some anchor. The anchor is given +by the start of the node captured by @anchor in the same pattern. +Every pattern with an @align should contain exactly one @anchor. +Indent (and outdent) for nodes below (in terms of their starting line) +the @align node is added to the indentation required for alignment.
  • +
  • @extend: +Extend the range of this node to the end of the line and to lines that are +indented more than the line that this node starts on. This is useful for +languages like Python, where for the purpose of indentation some nodes (like +functions or classes) should also contain indented lines that follow them.
  • +
  • @extend.prevent-once: +Prevents the first extension of an ancestor of this node. For example, in Python +a return expression always ends the block that it is in. Note that this only +stops the extension of the next @extend capture. If multiple ancestors are +captured, only the extension of the innermost one is prevented. All other +ancestors are unaffected (regardless of whether the innermost ancestor would +actually have been extended).
  • +
+

@indent / @outdent

+

Consider this example:

+
#![allow(unused)]
+fn main() {
+fn shout(things: Vec<Thing>) {
+    //                       โ†‘
+    //                       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ indent level
+    //                    @indent                    โ”œโ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„
+    //                                               โ”‚
+    let it_all = |out| { things.filter(|thing| { //  โ”‚      1
+    //                 โ†‘                       โ†‘     โ”‚
+    //                 โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”ผโ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„
+    //              @indent                 @indent  โ”‚
+    //                                               โ”‚      2
+        thing.can_do_with(out) //                    โ”‚
+    })}; //                                          โ”œโ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„
+  //โ†‘โ†‘โ†‘                                              โ”‚      1
+} //โ•ฐโ”ผโ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”„
+// 3x @outdent
+}
+
((block) @indent)
+["}" ")"] @outdent
+
+

Note how on the second line, we have two blocks begin on the same line. In this +case, since both captures occur on the same line, they are combined and only +result in a net increase of 1. Also note that the closing }s are part of the +@indent captures, but the 3 @outdents also combine into 1 and result in that +line losing one indent level.

+

@extend / @extend.prevent-once

+

For an example of where @extend can be useful, consider Python, which is +whitespace-sensitive.

+
]
+  (parenthesized_expression)
+  (function_definition)
+  (class_definition)
+] @indent
+
+
+
class Hero:
+    def __init__(self, strong, fast, sure, soon):#  โ†โ”€โ•ฎ
+        self.is_strong = strong #                     โ”‚
+        self.is_fast = fast     # โ•ญโ”€โ”€โ”€ query start    โ”‚
+        self.is_sure = sure     # โ”‚ โ•ญโ”€ cursor         โ”‚
+        self.is_soon = soon     # โ”‚ โ”‚                 โ”‚
+        #     โ†‘            โ†‘      โ”‚ โ”‚                 โ”‚
+        #     โ”‚            โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚                 โ”‚
+        #     โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                 โ”‚
+        #                                             โ”œโ”€ traversal
+    def need_hero(self, life):         #              โ”‚  start node
+        return (                       #              โ”‚
+            self.is_strong             #              โ”‚
+            and self.is_fast           #              โ”‚
+            and self.is_sure           #              โ”‚
+            and self.is_soon           #              โ”‚
+            and self > life            #              โ”‚
+        ) # โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
+
+

Without braces to catch the scope of the function, the smallest descendant of +the cursor on a line feed ends up being the entire inside of the class. Because +of this, it will miss the entire function node and its indent capture, leading +to an indent level one too small.

+

To address this case, @extend tells helix to "extend" the captured node's span +to the line feed and every consecutive line that has a greater indent level than +the line of the node.

+
(parenthesized_expression) @indent
+
+]
+  (function_definition)
+  (class_definition)
+] @indent @extend
+
+
+
class Hero:
+    def __init__(self, strong, fast, sure, soon):#  โ†โ”€โ•ฎ
+        self.is_strong = strong #                     โ”‚
+        self.is_fast = fast     # โ•ญโ”€โ”€โ”€ query start    โ”œโ”€ traversal
+        self.is_sure = sure     # โ”‚ โ•ญโ”€ cursor         โ”‚  start node
+        self.is_soon = soon     # โ”‚ โ”‚ โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
+        #     โ†‘            โ†‘      โ”‚ โ”‚                 
+        #     โ”‚            โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚
+        #     โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
+    def need_hero(self, life):
+        return (
+            self.is_strong
+            and self.is_fast
+            and self.is_sure
+            and self.is_soon
+            and self > life
+        )
+
+

Furthermore, there are some cases where extending to everything with a greater +indent level may not be desirable. Consider the need_hero function above. If +our cursor is on the last line of the returned expression.

+
class Hero:
+    def __init__(self, strong, fast, sure, soon):
+        self.is_strong = strong
+        self.is_fast = fast
+        self.is_sure = sure
+        self.is_soon = soon
+
+    def need_hero(self, life):
+        return (
+            self.is_strong
+            and self.is_fast
+            and self.is_sure
+            and self.is_soon
+            and self > life
+        ) # โ†โ”€โ”€โ”€ cursor
+    #โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ where cursor should go on new line
+
+

In Python, the are a few tokens that will always end a scope, such as a return +statement. Since the scope ends, so should the indent level. But because the +function span is extended to every line with a greater indent level, a new line +would just continue on the same level. And an @outdent would not help us here +either, since it would cause everything in the parentheses to become outdented +as well.

+

To help, we need to signal an end to the extension. We can do this with +@extend.prevent-once.

+
(parenthesized_expression) @indent
+
+]
+  (function_definition)
+  (class_definition)
+] @indent @extend
+
+(return_statement) @extend.prevent-once
+
+

@indent.always / @outdent.always

+

As mentioned before, normally if there is more than one @indent or @outdent +capture on the same line, they are combined.

+

Sometimes, there are cases when you may want to ensure that every indent capture +is additive, regardless of how many occur on the same line. Consider this +example in YAML.

+
  - foo: bar
+# โ†‘ โ†‘
+# โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ start of map
+# โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ start of list element
+    baz: quux # โ†โ”€โ”€โ”€ cursor
+    # โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ where the cursor should go on a new line
+    garply: waldo
+  - quux:
+      bar: baz
+    xyzzy: thud
+    fred: plugh
+
+

In YAML, you often have lists of maps. In these cases, the syntax is such that +the list element and the map both start on the same line. But we really do want +to start an indentation for each of these so that subsequent keys in the map +hang over the list and align properly. This is where @indent.always helps.

+
((block_sequence_item) @item @indent.always @extend
+  (#not-one-line? @item))
+
+((block_mapping_pair
+    key: (_) @key
+    value: (_) @val
+    (#not-same-line? @key @val)
+  ) @indent.always @extend
+)
+
+

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:

+
(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.

    +
  • +
  • +

    #one-line?/#not-one-line?: +The captures given by the fist argument must/must span a total of one line.

    +
  • +
+

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):

+
    +
  • tail: +This scope applies to everything except for the first line of the +captured node.
  • +
  • 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.
  • +
+

For example, imagine we have the following function

+
#![allow(unused)]
+fn main() {
+fn aha() { // โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
+  let take = "on me";  // โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  scope:  โ”‚
+  let take = "me on";             //     โ”œโ”€ "tail"  โ”œโ”€ (block) @indent
+  let ill = be_gone_days(1 || 2); //     โ”‚          โ”‚
+} // โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€ "}" @outdent
+                                         //                scope: "all"
+}
+

We can write the following query with the #set! declaration:

+
((block) @indent
+ (#set! "scope" "tail"))
+("}" @outdent
+ (#set! "scope" "all"))
+
+

As we can see, the "tail" scope covers the node, except for the first line. +Everything up to and including the closing brace gets an indent level of 1. +Then, on the closing brace, we encounter an outdent with a scope of "all", which +means the first line is included, and the indent level is cancelled out on this +line. (Note these scopes are the defaults for @indent and @outdentโ€”they are +written explicitly for demonstration.)

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + diff --git a/guides/index.html b/guides/index.html new file mode 100644 index 00000000..0941314e --- /dev/null +++ b/guides/index.html @@ -0,0 +1,236 @@ + + + + + + Guides + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Guides

+

This section contains guides for adding new language server configurations, +tree-sitter grammars, textobject and rainbow bracket queries, and other similar items.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + diff --git a/guides/injection.html b/guides/injection.html new file mode 100644 index 00000000..fb4bb8f3 --- /dev/null +++ b/guides/injection.html @@ -0,0 +1,298 @@ + + + + + + Adding injection queries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Adding Injection Queries

+

Writing language injection queries allows one to highlight a specific node as a different language. +In addition to the standard language injection options used by tree-sitter, there +are a few Helix specific extensions that allow for more control.

+

And example of a simple query that would highlight all strings as bash in Nix:

+
((string_expression (string_fragment) @injection.content)
+  (#set! injection.language "bash"))
+
+

Capture Types

+
    +
  • +

    @injection.language (standard): +The captured node may contain the language name used to highlight the node captured by +@injection.content.

    +
  • +
  • +

    @injection.content (standard): +Marks the content to be highlighted as the language captured with @injection.language et al.

    +
  • +
  • +

    @injection.filename (extension): +The captured node may contain a filename with a file-extension known to Helix, +highlighting @injection.content as that language. This uses the language extensions defined in +both the default languages.toml distributed with Helix, as well as user defined languages.

    +
  • +
  • +

    @injection.shebang (extension): +The captured node may contain a shebang used to choose a language to highlight as. This also uses +the shebangs defined in the default and user languages.toml.

    +
  • +
+

Settings

+
    +
  • +

    injection.combined (standard): +Indicates that all the matching nodes in the tree should have their content parsed as one +nested document.

    +
  • +
  • +

    injection.language (standard): +Forces the captured content to be highlighted as the given language

    +
  • +
  • +

    injection.include-children (standard): +Indicates that the content nodeโ€™s entire text should be re-parsed, including the text of its child +nodes. By default, child nodesโ€™ text will be excluded from the injected document.

    +
  • +
  • +

    injection.include-unnamed-children (extension): +Same as injection.include-children but only for unnamed child nodes.

    +
  • +
+

Predicates

+
    +
  • +

    #eq? (standard): +The first argument (a capture) must be equal to the second argument +(a capture or a string).

    +
  • +
  • +

    #match? (standard): +The first argument (a capture) must match the regex given in the +second argument (a string).

    +
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + diff --git a/guides/rainbow_bracket_queries.html b/guides/rainbow_bracket_queries.html new file mode 100644 index 00000000..98f11d99 --- /dev/null +++ b/guides/rainbow_bracket_queries.html @@ -0,0 +1,323 @@ + + + + + + Adding rainbow bracket queries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Adding Rainbow Bracket Queries

+

Helix uses rainbows.scm tree-sitter query files to provide rainbow bracket +functionality.

+

Tree-sitter queries are documented in the tree-sitter online documentation. +If you're writing queries for the first time, be sure to check out the section +on syntax highlighting queries and on query syntax.

+

Rainbow queries have two captures: @rainbow.scope and @rainbow.bracket. +@rainbow.scope should capture any node that increases the nesting level +while @rainbow.bracket should capture any bracket nodes. Put another way: +@rainbow.scope switches to the next rainbow color for all nodes in the tree +under it while @rainbow.bracket paints captured nodes with the current +rainbow color.

+

For an example, let's add rainbow queries for the tree-sitter query (TSQ) +language itself. These queries will go into a +runtime/queries/tsq/rainbows.scm file in the repository root.

+

First we'll add the @rainbow.bracket captures. TSQ only has parentheses and +square brackets:

+
["(" ")" "[" "]"] @rainbow.bracket
+
+

The ordering of the nodes within the alternation (square brackets) is not +taken into consideration.

+
+

Note: Why are these nodes quoted? Most syntax highlights capture text +surrounded by parentheses. These are named nodes and correspond to the +names of rules in the grammar. Brackets are usually written in tree-sitter +grammars as literal strings, for example:

+
{
+  // ...
+  arguments: seq("(", repeat($.argument), ")"),
+  // ...
+}
+
+

Nodes written as literal strings in tree-sitter grammars may be captured +in queries with those same literal strings.

+
+

Then we'll add @rainbow.scope captures. The easiest way to do this is to +view the grammar.js file in the tree-sitter grammar's repository. For TSQ, +that file is here. As we scroll down the grammar.js, we +see that the (alternation), (L36) (group) (L57), (named_node) (L59), +(predicate) (L87) and (wildcard_node) (L97) nodes all contain literal +parentheses or square brackets in their definitions. These nodes are all +direct parents of brackets and happen to also be the nodes we want to change +to the next rainbow color, so we capture them as @rainbow.scope.

+
[
+  (group)
+  (named_node)
+  (wildcard_node)
+  (predicate)
+  (alternation)
+] @rainbow.scope
+
+

This strategy works as a rule of thumb for most programming and configuration +languages. Markup languages can be trickier and may take additional +experimentation to find the correct nodes to use for scopes and brackets.

+

The :tree-sitter-subtree command shows the syntax tree under the primary +selection in S-expression format and can be a useful tool for determining how +to write a query.

+

Properties

+

The rainbow.include-children property may be applied to @rainbow.scope +captures. By default, all @rainbow.bracket captures must be direct descendant +of a node captured with @rainbow.scope in a syntax tree in order to be +highlighted. The rainbow.include-children property disables that check and +allows @rainbow.bracket captures to be highlighted if they are direct or +indirect descendants of some node captured with @rainbow.scope.

+

For example, this property is used in the HTML rainbow queries.

+

For a document like <a>link</a>, the syntax tree is:

+
(element                   ; <a>link</a>
+  (start_tag               ; <a>
+    (tag_name))            ; a
+  (text)                   ; link
+  (end_tag                 ; </a>
+    (tag_name)))           ; a
+
+

If we want to highlight the <, > and </ nodes with rainbow colors, we +capture them as @rainbow.bracket:

+
["<" ">" "</"] @rainbow.bracket
+
+

And we capture (element) as @rainbow.scope because (element) nodes nest +within each other: they increment the nesting level and switch to the next +color in the rainbow.

+
(element) @rainbow.scope
+
+

But this combination of @rainbow.scope and @rainbow.bracket will not +highlight any nodes. <, > and </ are children of the (start_tag) and +(end_tag) nodes. We can't capture (start_tag) and (end_tag) as +@rainbow.scope because they don't nest other elements. We can fix this case +by removing the requirement that <, > and </ are direct descendants of +(element) using the rainbow.include-children property.

+
((element) @rainbow.scope
+ (#set! rainbow.include-children))
+
+

With this property set, <, >, and </ will highlight with rainbow colors +even though they aren't direct descendents of the (element) node.

+

rainbow.include-children is not necessary for the vast majority of programming +languages. It is only necessary when the node that increments the nesting level +(changes rainbow color) is not the direct parent of the bracket node.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + diff --git a/guides/textobject.html b/guides/textobject.html new file mode 100644 index 00000000..a47fd8a6 --- /dev/null +++ b/guides/textobject.html @@ -0,0 +1,270 @@ + + + + + + Adding textobject queries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Adding textobject queries

+

Helix supports textobjects that are language specific, such as functions, classes, etc. +These textobjects require an accompanying tree-sitter grammar and a textobjects.scm query file +to work properly. Tree-sitter allows us to query the source code syntax tree +and capture specific parts of it. The queries are written in a lisp dialect. +More information on how to write queries can be found in the official tree-sitter +documentation.

+

Query files should be placed in runtime/queries/{language}/textobjects.scm +when contributing to Helix. Note that to test the query files locally you should put +them under your local runtime directory (~/.config/helix/runtime on Linux +for example).

+

The following captures are recognized:

+
+ + + + + + + + + +
Capture Name
function.inside
function.around
class.inside
class.around
test.inside
test.around
parameter.inside
comment.inside
comment.around
+
+

Example query files can be found in the helix GitHub repository.

+

Queries for textobject based navigation

+

Tree-sitter based navigation in Helix is done using captures in the +following order:

+
    +
  • object.movement
  • +
  • object.around
  • +
  • object.inside
  • +
+

For example if a function.around capture has been already defined for a language +in its textobjects.scm file, function navigation should also work automatically. +function.movement should be defined only if the node captured by function.around +doesn't make sense in a navigation context.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + +
+ + -- cgit v1.2.3-70-g09d2