## Adding Locals Queries

`locals.scm` queries teach Helix about variable scopes and definitions so that
local variables can be highlighted distinctly from global ones. When a
`@local.reference` identifier is resolved to a `@local.definition`, it inherits
the highlight class of that definition rather than the class assigned by
`highlights.scm`.

Query files should be placed in `runtime/queries/{language}/locals.scm` when
contributing to Helix. Like other query files, `locals.scm` may reuse another
language's with `; inherits: <lang>` on the first line. Locals are resolved in
their own pass with their own precedence, independently of `highlights.scm`.

## Captures

### `@local.scope`

Marks a node as a scope boundary. Definitions are only visible to references
that appear inside the same scope or a nested one. Typical scope nodes are
function bodies and blocks.

```scm
[
  (function_definition)
  (block)
] @local.scope
```

### `@local.definition.*`

Marks a name node as introducing a local symbol. The suffix after
`@local.definition.` becomes the highlight class applied to any reference that
resolves to this definition. For example, `@local.definition.variable.parameter`
causes matching references to be highlighted as `variable.parameter`.

```scm
(function_item
  (parameters
    (parameter
      pattern: (identifier) @local.definition.variable.parameter)))
```

Common suffixes mirror the highlight classes in `highlights.scm`:
`variable`, `variable.parameter`, `variable.builtin`, `variable.mutable`,
`function`, `namespace`, `type`, `constant`, etc.

### `@local.reference`

Marks an identifier node as a potential reference to a local definition.
Helix searches enclosing scopes for a matching definition and, if found,
highlights the reference with that definition's class instead.

```scm
(identifier) @local.reference
```

## Discard captures

Any capture in `locals.scm` that is not `@local.scope`, `@local.reference`, or
`@local.definition.*` acts as a discard. It prevents a `@local.reference` from
being resolved at that node without affecting the `highlights.scm` result. This
is useful for identifier nodes that look like references but should not be
treated as variable references, for example keyword argument names or struct
field names in struct literals.

```scm
; Keyword argument names in a call are not variable references.
(keyword_argument
  name: (identifier) @_)
```

Later patterns in a query file have higher precedence than earlier ones. A
discard pattern must appear after the `@local.reference` pattern it is intended
to override. Placing it later ensures it takes precedence and cancels the
reference resolution for nodes it matches.

The convention is to use a capture name beginning with an underscore (e.g. `@_`,
`@_keyword`) to make the discard intent clear, but any non-`@local.*` name works.

## How definitions and references are matched

Helix matches references to definitions by comparing the text of the reference
node against the text of definitions visible from the current scope. Definitions
in inner scopes shadow those in outer scopes. If no definition is found the
reference is left with its `highlights.scm` highlight.

## Relationship with `highlights.scm`

The locals system runs alongside `highlights.scm`, not instead of it.
`highlights.scm` always determines the baseline highlight for a node.
`locals.scm` can override that highlight only when a `@local.reference`
successfully resolves to a `@local.definition`, and only for that specific
resolution. Non-`@local.*` captures in `locals.scm` (i.e. discards) have no
effect on `highlights.scm` results.
