Queries are pattern-matching expressions that extract information from syntax trees. They power syntax highlighting, folding, indentation, and language injections in nvim-treesitter.
What is a query?
A tree-sitter query uses a Lisp-like syntax to match patterns in the syntax tree and assign semantic meaning through captures :
(function_definition
name: (identifier) @function)
This matches any function definition and captures the name as @function, which can be highlighted by your color scheme.
Queries are stored in runtime/queries/{language}/*.scm files. nvim-treesitter includes a comprehensive collection of queries for all supported languages.
Query types
nvim-treesitter uses five types of query files:
highlights.scm
injections.scm
folds.scm
indents.scm
locals.scm
Defines syntax highlighting by matching syntax tree nodes to highlight groups. "return" @keyword.return
(function_definition
name: (identifier) @function)
(string) @string
These captures map to highlight groups defined by your colorscheme. Specifies embedded languages (like SQL in Python strings or CSS in HTML). ((function_call
name: (identifier) @_func
arguments: (arguments
(string content: _ @injection.content)))
(#eq? @_func "sql")
(#set! injection.language "sql"))
This highlights SQL syntax inside sql("...") function calls. Determines which syntax nodes can be folded. (function_definition) @fold
(class_definition) @fold
(if_statement) @fold
These nodes can be collapsed in the editor. Controls automatic indentation behavior. (function_definition) @indent.begin
[
"}"
"]"
")"
] @indent.dedent
This tells the indenter when to increase or decrease indentation. Tracks definitions and references (legacy, not used by nvim-treesitter). (function_definition
name: (identifier) @local.definition.function)
(identifier) @local.reference
Provided for backward compatibility with external tools.
Query syntax fundamentals
Basic matching
Match nodes by type:
(function_definition) @function
Field matching
Match specific fields within a node:
(function_definition
name: (identifier) @function.name
parameters: (parameters) @function.params)
Anonymous nodes
Match literal syntax elements:
"return" @keyword.return
"class" @keyword.type
Alternatives
Match any of several patterns:
[
"if"
"else"
"elif"
] @keyword.conditional
Grouping
Match multiple nodes in sequence:
(if_statement
condition: (_) @condition
consequence: (_) @consequence)
Predicates and directives
Predicates
Filter matches based on node content:
((identifier) @constant
(#eq? @constant "True"))
Matches identifiers that are exactly “True”.
#any-of? - Multiple options
((identifier) @constant.builtin
(#any-of? @constant.builtin "None" "True" "False"))
Matches any of the specified values.
#lua-match? - Pattern matching
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
Matches identifiers matching the Lua pattern (all caps).
((identifier) @constant
(#vim-match? @constant "^[A-Z]\\+$"))
Matches using Vim’s regular expression syntax.
For performance, prefer predicates in this order:
#eq? (fastest)
#any-of?
#lua-match?
#vim-match? (slowest)
Directives
Modify capture behavior:
#set! priority - Control precedence
((comment) @comment
(#set! priority 110))
Sets highlight priority (default is 100). Higher priorities override lower ones.
#offset! - Adjust capture range
((string) @string
(#offset! @string 0 1 0 -1))
Adjusts the captured range: (start_row, start_col, end_row, end_col).
#set! injection.language - Language injection
((function_call
arguments: (arguments (string content: _ @injection.content)))
(#set! injection.language "sql"))
Specifies the language for embedded code.
Highlight captures
nvim-treesitter defines standard captures for syntax highlighting. Here are the most common:
Keywords and operators
"return" @keyword.return
"if" @keyword.conditional
"for" @keyword.repeat
"import" @keyword.import
"+" @operator
"and" @keyword.operator
Identifiers
(identifier) @variable
(type_identifier) @type
(function_definition
name: (identifier) @function)
(parameter
name: (identifier) @variable.parameter)
Literals
(string) @string
(number) @number
(boolean) @boolean
(comment) @comment
(escape_sequence) @string.escape
Functions and methods
(call_expression
function: (identifier) @function.call)
(method_definition
name: (property_identifier) @function.method)
Real-world query examples
Here are actual queries from nvim-treesitter:
Lua highlighting
; Keywords
"return" @keyword.return
[
"goto"
"in"
"local"
] @keyword
(while_statement
[
"while"
"do"
"end"
] @keyword.repeat)
(if_statement
[
"if"
"elseif"
"else"
"then"
"end"
] @keyword.conditional)
; Functions
(function_definition
name: [
(identifier) @function
(dot_index_expression
field: (identifier) @function)
])
(function_call
name: [
(identifier) @function.call
(dot_index_expression
field: (identifier) @function.call)
])
Lua injections
; Vim command injection
((function_call
name: (_) @_vimcmd
arguments: (arguments
(string content: _ @injection.content)))
(#set! injection.language "vim")
(#any-of? @_vimcmd "vim.cmd" "vim.api.nvim_command"))
; Query injection in strings
(string
content: _ @injection.content
(#lua-match? @injection.content "^%s*;+%s?query")
(#set! injection.language "query"))
; LuaDoc comments
(comment
content: (_) @injection.content
(#lua-match? @injection.content "^[-][%s]*[@|]")
(#set! injection.language "luadoc")
(#offset! @injection.content 0 1 0 0))
Python constant detection
; Reset highlighting in f-string interpolations
(interpolation) @none @nospell
; Identifier naming conventions
((identifier) @type
(#lua-match? @type "^[A-Z].*[a-z]"))
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
((identifier) @constant.builtin
(#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$"))
((identifier) @constant.builtin
(#any-of? @constant.builtin
"NotImplemented" "Ellipsis" "quit" "exit"))
Query inheritance
Languages can inherit queries from others:
; inherits: javascript,(jsx)
Place this as the first line of your query file.
javascript is required
(jsx) is optional (only inherited if available)
Typescript queries inherit from Javascript, and C++ queries inherit from C.
Writing custom queries
You can add or override queries by placing files in your config:
~/.config/nvim/
queries/
python/
highlights.scm
injections.scm
Extending queries
To add patterns without replacing built-in queries:
; extends
; Your custom patterns here
((identifier) @my.custom.capture
(#eq? @my.custom.capture "special_case"))
The ; extends directive at the top tells nvim-treesitter to merge your patterns with the default queries.
Preserve specific formatting:
; format-ignore
(complex_node
(deeply) (nested)
(structure))
Tree-sitter playground
Use Neovim’s built-in tools:
:InspectTree " View syntax tree
:EditQuery " Test query patterns
:Inspect " Show highlight groups
:InspectTree opens a live view of the syntax tree that updates as you edit. Click nodes to see their source location.
Query validation
Validate your queries:
# Lint all queries
make lintquery
# Check query validity
make checkquery
# Format queries
make formatquery
Or use the all-in-one command:
ts_query_ls
Install ts_query_ls for:
Query autocomplete
Syntax validation
Formatting
Documentation on hover
Use specific node types instead of wildcards when possible
Order predicates from fastest to slowest (see warning above)
Avoid overly broad patterns that match too many nodes
Use field matching to narrow down matches
Set priorities only when necessary
Queries run on every keystroke during highlighting. Inefficient queries can noticeably slow down editing.
Common patterns
Spell checking
(comment) @spell
(string) @spell
; Disable spell check in code
(function_call) @nospell
Concealing
((operator) @conceal
(#eq? @conceal "->")
(#set! conceal "→"))
Context-aware highlighting
; Highlight TODO in comments
((comment) @comment.error
(#lua-match? @comment.error "TODO|FIXME|BUG"))
; Highlight docstring differently
(function_definition
body: (block
(expression_statement
(string) @string.documentation)))
Next steps
Learn how queries power specific features:
Features - Highlights, folds, indents, and injections
Parsers - Understanding the foundation of tree-sitter