docs: Expand local variable highlighting section

This commit is contained in:
Max Brunsfeld 2020-02-12 17:23:29 -08:00
parent 360b188644
commit cf80f094ac

View file

@ -21,10 +21,14 @@ Highlighting is controlled by *three* different types of query files that can be
* The local variable query (optional, with default name `locals.scm`)
* The language injection query (optional, with default name `injections.scm`)
The default names for the query files use the `.scm` file. We chose this extension because it commonly used for files written in [Scheme](https://en.wikipedia.org/wiki/Scheme_%28programming_language%29), a popular dialect of Lisp, and these query files use a Lisp-like syntax. But alternatively, you can think of `SCM` as an acronym for "Source Code Matching".
## Highlights Query
The most important query is called the highlights query. The highlights query uses *captures* to assign arbitrary *highlight names* to different nodes in the tree. Each highlight name can then be mapped to a color. Commonly used highlight names include `keyword`, `function`, `type`, `property`, and `string`. Names can also be dot-separated like `function.builtin`.
#### Example Input
For example, consider the following Go code:
```go
@ -52,6 +56,8 @@ With this syntax tree:
right: (int_literal)))))))
```
#### Example Query
Suppose we wanted to render this code with the following colors:
* keywords `func` and `return` in purple
* function `increment` in blue
@ -60,16 +66,14 @@ Suppose we wanted to render this code with the following colors:
We can assign each of these categories a *highlight name* using a query like this:
```
```clj
; highlights.scm
"func" @keyword
"return" @keyword
(function_declaration
name: (identifier) @function)
(type_identifier) @type
(int_literal) @number
(function_declaration name: (identifier) @function)
```
And we could map each of these highlight names to a color:
@ -85,9 +89,17 @@ And we could map each of these highlight names to a color:
}
```
#### Result
<pre class='highlight' style='border: 1px solid #aaa;'>
<span style='color: purple;'>func</span> <span style='color: #005fd7;'>increment</span>(<span>a</span> <span style='color: #005f5f;'>int</span>) <span style='color: #005f5f;'>int</span> {
<span style='color: purple;'>return</span> <span>a</span> <span style='font-weight: bold;color: #4e4e4e;'>+</span> <span style='font-weight: bold;color: #875f00;'>1</span>
}
</pre>
## Local Variable Query
Good syntax highlighting helps the reader to quickly distinguish between the different types of 'entities' in their code. Ideally, if a given entity appears in *multiple* places, it should be colored the same in each place. The Tree-sitter syntax highlighting system can help you to achieve this by keeping track of local scopes and variables.
Good syntax highlighting helps the reader to quickly distinguish between the different types of *entities* in their code. Ideally, if a given entity appears in *multiple* places, it should be colored the same in each place. The Tree-sitter syntax highlighting system can help you to achieve this by keeping track of local scopes and variables.
The *local variables* query is different from the highlights query in that, while the highlights query uses *arbitrary* capture names which can then be mapped to colors, the locals variable query uses a fixed set of capture names, each of which has a special meaning.
@ -99,14 +111,109 @@ The capture names are as follows:
When highlighting a file, Tree-sitter will keep track of the set of scopes that contains any given position, and the set of definitions within each scope. When processing a syntax node that is captured as a `local.reference`, Tree-sitter will try to find a definition for a name that that matches the node's text. If it finds a match, Tree-sitter will ensure that the *reference* and the *definition* are colored the same.
For example, consider this Ruby code:
#### Example Input
```
def increment_all(list)
Consider this Ruby code:
```ruby
def process_list(list)
context = current_context
list.map do |item|
item + 1
process_item(item, context)
end
end
item = 5
list = [item]
```
With this syntax tree:
```
(program
(method
name: (identifier)
parameters: (method_parameters
(identifier))
(assignment
left: (identifier)
right: (identifier))
(method_call
method: (call
receiver: (identifier)
method: (identifier))
block: (do_block
(block_parameters
(identifier))
(method_call
method: (identifier)
arguments: (argument_list
(identifier)
(identifier))))))
(assignment
left: (identifier)
right: (integer))
(assignment
left: (identifier)
right: (array
(identifier))))
```
There are several different types of names within this method:
* `process_list` is a method.
* Within this method, `list` is a formal parameter
* `context` is a local variable.
* `current_context` is *not* a local variable, so it must be a method.
* Within the `do` block, `item` is a formal parameter
* Later on, `item` and `list` are both local variables (not formal parameters).
#### Example Queries
Let's write some queries that let us clearly distinguish between these types of names. First, set up the highlighting query, as described in the previous section. We'll assign distinct colors to method calls, method definitions, and formal parameters:
```clj
; highlights.scm
(call method: (identifier) @function.method)
(method_call method: (identifier) @function.method)
(method name: (identifier) @function.method)
(method_parameters (identifier) @variable.parameter)
(block_parameters (identifier) @variable.parameter)
```
Then, we'll set up a local variable query to keep track of the variables and scopes. Here, we're indicating that methods and blocks create local *scopes*, parameters and assignments create *definitions*, and other identifiers should be considered *references*:
```clj
; locals.scm
(method) @local.scope
(do_block) @local.scope
(method_parameters (identifier) @local.definition)
(block_parameters (identifier) @local.definition)
(assignment left:(identifier) @local.definition)
(identifier) @local.reference
```
#### Result
<pre class='highlight' style='border: 1px solid #aaa;'>
<span style='color: purple;'>def</span> <span style='color: #005fd7;'>process_list</span><span style='color: #4e4e4e;'>(</span><span style='text-decoration: underline;'>list</span><span style='color: #4e4e4e;'>)</span>
<span>context</span> <span style='font-weight: bold;color: #4e4e4e;'>=</span> <span style='color: #005fd7;'>current_context</span>
<span style='text-decoration: underline;'>list</span><span style='color: #4e4e4e;'>.</span><span style='color: #005fd7;'>map</span> <span style='color: purple;'>do</span> |<span style='text-decoration: underline;'>item</span>|
<span style='color: #005fd7;'>process_item</span>(<span style='text-decoration: underline;'>item</span><span style='color: #4e4e4e;'>,</span> <span>context</span><span style='color: #4e4e4e;'>)</span>
<span style='color: purple;'>end</span>
<span style='color: purple;'>end</span>
<span>item</span> <span style='font-weight: bold;color: #4e4e4e;'>=</span> <span style='font-weight: bold;color: #875f00;'>5</span>
<span>list</span> <span style='font-weight: bold;color: #4e4e4e;'>=</span> [<span>item</span><span style='color: #4e4e4e;'>]</span>
</pre>
## Language Injection Query