Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tree-sitter/tree-sitter/llms.txt

Use this file to discover all available pages before exploring further.

Predicates and directives allow you to specify arbitrary metadata and conditions associated with a pattern. They enable sophisticated filtering and annotation of query matches.

What are Predicates?

Predicates are S-expressions added to patterns that specify conditions for matching. They:
  • Start with a predicate name beginning with # and ending with ?
  • Contain @-prefixed capture names or strings as arguments
  • Filter matches based on captured node content
Directives are similar but end with ! instead of ? and associate metadata with patterns.
Predicates and directives are not handled directly by the Tree-sitter C library. They’re exposed in structured form for higher-level bindings to implement. The predicates documented here are supported by the CLI and most language bindings.

The eq? Predicate

The eq? predicate family allows matching against a single capture or string value.

Basic Usage

The first argument must be a capture. The second can be either:
  • Another capture (to compare their text)
  • A string literal (to compare against a fixed value)
((identifier) @variable.builtin
  (#eq? @variable.builtin "self"))
This matches any identifier that is exactly self.

Comparing Captures

Match key-value pairs where the value identifier has the same text as the key:
(
  (pair
    key: (property_identifier) @key-name
    value: (identifier) @value-name)
  (#eq? @key-name @value-name)
)
This matches patterns like { name: name } in JavaScript.

Variants

There are four variants in the eq? family:
Exact Match
((identifier) @var
  (#eq? @var "self"))
All captured nodes must match the value.
For quantified captures (using + or *), the default #eq? requires all nodes to match. Use #any-eq? to match if any node matches.

The match? Predicate

The match? predicate family uses regular expressions to match against capture text.

Basic Usage

The first argument must be a capture, and the second must be a string containing a regular expression. Match identifiers in SCREAMING_SNAKE_CASE:
((identifier) @constant
  (#match? @constant "^[A-Z][A-Z_]+"))

Documentation Comments

Match C documentation comments that begin with ///:
((comment)+ @comment.documentation
  (#match? @comment.documentation "^///\\s+.*"))

Complex Patterns

Match Cgo comments in Go (C code in comments before import "C"):
((comment)+ @injection.content
  .
  (import_declaration
    (import_spec path: (interpreted_string_literal) @_import_c))
  (#eq? @_import_c "\"C\"")
  (#match? @injection.content "^//"))

Variants

Like eq?, the match? family has four variants:
  • #match? - All captured nodes must match the regex
  • #not-match? - No captured nodes can match the regex
  • #any-match? - At least one captured node must match
  • #any-not-match? - At least one captured node must not match
Use raw strings or escape backslashes properly. The regex \s+ should be written as "\\s+" in query strings.

The any-of? Predicate

The any-of? predicate matches a capture against multiple strings, succeeding if the capture equals any of them.

Matching Multiple Values

Match builtin variables in JavaScript:
((identifier) @variable.builtin
  (#any-of? @variable.builtin
        "arguments"
        "module"
        "console"
        "window"
        "document"))
This is more readable than using multiple #eq? predicates or a complex regex.
#any-of? performs exact string matching, not regex matching. Use #match? with alternation (a|b|c) for regex-based alternatives.

The is? and is-not? Predicates

The is? predicate asserts that a capture has a given property. This is used for semantic properties that the query system provides.

Local Variable Check

Match builtin variables that are not local:
((identifier) @variable.builtin
  (#match? @variable.builtin "^(arguments|module|console|window|document)$")
  (#is-not? local))
The is? predicate depends on the query implementation providing semantic properties. Not all bindings support custom properties.

Directives

Directives are similar to predicates but end with ! instead of ?. They associate metadata with patterns rather than filtering matches.

The set! Directive

The set! directive associates arbitrary key-value pairs with a pattern.

Setting Injection Language

Mark a comment as containing Doxygen syntax:
((comment) @injection.content
  (#match? @injection.content "/[*\\/][!*\\/]<?[^a-zA-Z]")
  (#set! injection.language "doxygen"))
When iterating captures, you can access the injection.language property to parse the comment with the Doxygen parser.

Common Use Cases

((comment) @injection.content
  (#match? @injection.content "^```sql")
  (#set! injection.language "sql"))
Mark SQL code blocks in comments for syntax highlighting.
((function_declaration) @scope
  (#set! scope.type "function"))
Tag scopes with their type for semantic analysis.
((comment) @highlight
  (#match? @highlight "TODO")
  (#set! priority 100))
Set priority for overlapping highlights.

The select-adjacent! Directive

The select-adjacent! directive filters capture text to preserve only nodes adjacent to another capture. It takes two capture names as arguments:
  1. The capture to filter
  2. The capture to check adjacency against
See the code navigation documentation for detailed examples.

The strip! Directive

The strip! directive removes text from a capture using a regular expression. It takes two arguments:
  1. The capture to strip text from
  2. A regex pattern matching text to remove
Any text matched by the regex is removed from the capture’s text.
The strip! directive is commonly used to clean up captured text by removing comment markers, whitespace, or other syntactic noise.

Practical Examples

Finding Test Functions

((function_declaration
  name: (identifier) @test-function)
  (#match? @test-function "^test_.*"))

Matching Builtin Types

((type_identifier) @type.builtin
  (#any-of? @type.builtin
    "int"
    "float"
    "bool"
    "string"
    "void"))

Deprecated API Usage

((call_expression
  function: (member_expression
    property: (property_identifier) @deprecated))
  (#any-of? @deprecated "setTimeout" "eval" "execScript")
  (#set! deprecated true))

SQL Injection Detection

((call_expression
  function: (member_expression
    property: (property_identifier) @method)
  arguments: (arguments
    (binary_expression) @sql-concat))
  (#eq? @method "query")
  (#match? @sql-concat ".*\\+.*")
  (#set! warning "Possible SQL injection"))

Predicate Summary

#eq?

Direct match against capture or string. All nodes must match.

#match?

Regular expression match. All nodes must match the pattern.

#any-of?

Match against a list of strings. Node must equal one string.

#is?

Check for semantic property. Node must have the property.

Directive Summary

#set!

Associate key-value metadata with pattern

#select-adjacent!

Filter capture to adjacent nodes only

#strip!

Remove text matching regex from capture

Predicate Modifiers

((identifier)+ @names
  (#eq? @names "foo"))
All captured nodes must match the predicate.

Best Practices

1

Prefer Specific Predicates

Use #any-of? instead of complex regex alternations when matching fixed strings. It’s more readable and performant.
2

Combine Predicates

Use multiple predicates to create precise conditions. They’re evaluated as AND conditions.
3

Use Directives for Metadata

Don’t use predicates to filter when you just want to tag matches. Use #set! for metadata.
4

Test Quantified Predicates

When using + or *, verify whether you need #eq? (all) or #any-eq? (any) behavior.

Next Steps

Query API

Learn how to execute queries and access predicates programmatically

Query Syntax

Review basic query syntax and pattern matching