Skip to main content

Tree-sitter query syntax

We use the tree-sitter query language to define our parse tree patterns. In addition to the official tree-sitter query documentation, we support a couple of additional features.

Relationships

In addition to the node corresponding to the scope itself (which we call its content range), you can tag different aspects / relationships of the scope. Assuming the internal identifier of our scope is foo, we can tag the following aspects of the scope:

  • @foo.domain indicates the domain of the scope. For example, you could use @collectionKey.domain to indicate that the domain of a key is the containing item, which would allow you to say "take key" from within the value of a key-value pair to select the key.
  • @foo.leading and @foo.trailing indicate the leading and trailing delimiters of the scope. For example, you could use @collectionKey.trailing to include all the way up to the start of the value as the trailing delimiter, so that "chuck key" will leave just the value.
  • @foo.removal to indicate the removal range of the scope. Note that it is preferred to use @foo.leading or @foo.trailing instead of @foo.removal in situations where you just need to include a leading or trailing delimiter in the removal range.
  • @foo.interior to indicate the interior of the scope, used for "inside". For example, you could use @namedFunction.interior to indicate the interior of a function, which would usually be the function body itself, without any leading or trailing delimiters.
  • @foo.iteration to indicate the iteration scope of the scope. For example, you could use @namedFunction.iteration to indicate that the iteration scope for functions is a class. Note that unlike the other aspects, the iteration scope is not a part of the scope itself, but rather a separate scope that is used to determine the iteration scope of the given scope. Thus, it should nearly always appear in a separate pattern from the scope itself, unlike the other aspects, which must appear in the same pattern as the scope itself.

Inline operators

In addition to the above aspects, you can also use the following inline operators to modify the scope:

  • @foo.start and @foo.end to construct the scope using a range between two nodes (inclusive).
  • @foo.startOf and @foo.endOf to refer to the start and end positions of a node. For example, you could use @foo.start.endOf to indicate that the scope should start at the end of the node.

Query predicate operators

We also support a number of query predicate operators for modifying the scope. See queryPredicateOperators.ts for a list of available operators.