Skip to main content
Queries are patterns that match tree-sitter nodes to enable features like highlighting, folding, and indentation. You can customize existing queries or create new ones.

Query Files Overview

nvim-treesitter uses several types of query files:
  • highlights.scm: Syntax highlighting
  • injections.scm: Multi-language injection (e.g., JS in HTML)
  • folds.scm: Code folding regions
  • indents.scm: Indentation rules
  • locals.scm: Definitions and references (backward compatibility)
For a language to support a feature, both the parser and the corresponding query file must exist.

Where to Place Custom Queries

Custom queries go in your Neovim config under queries/<language>/:
~/.config/nvim/
└── queries/
    ├── lua/
    │   ├── highlights.scm
    │   └── indents.scm
    ├── python/
    │   └── highlights.scm
    └── rust/
        └── folds.scm
Queries in your config directory replace default queries unless you use the ; extends modeline.

Extending vs Replacing Queries

Extending Default Queries

To add to existing queries without replacing them:
~/.config/nvim/queries/lua/highlights.scm
; extends

; Add custom highlight for a specific pattern
(function_call
  name: (identifier) @custom.function
  (#eq? @custom.function "mySpecialFunction"))
The ; extends modeline tells Neovim to merge your queries with the defaults.

Replacing Default Queries

To completely replace default queries, omit ; extends:
~/.config/nvim/queries/python/highlights.scm
; This file replaces the default highlights

(function_definition
  name: (identifier) @function)

(string) @string
Most of the time you want to extend, not replace.

Writing Highlight Queries

Highlight queries assign tree-sitter nodes to capture groups that map to highlight groups.

Basic Syntax

; Match a specific node type
(function_definition) @function

; Match specific text
"return" @keyword.return

; Match multiple alternatives
[
  "if"
  "else"
  "elif"
] @keyword.conditional

Using Fields

; Capture specific fields of a node
(function_definition
  name: (identifier) @function.name
  parameters: (parameters) @function.parameters)

Common Capture Groups

Here are the most commonly used captures:
@keyword                   ; General keywords
@keyword.function          ; def, function
@keyword.return            ; return, yield  
@keyword.conditional       ; if, else, elif
@keyword.repeat            ; for, while
@keyword.import            ; import, require

Using Predicates

Predicates filter matches based on conditions:
; Match exact text
(identifier) @variable.builtin
  (#eq? @variable.builtin "self")

; Match one of several values
(identifier) @constant.builtin
  (#any-of? @constant.builtin "None" "True" "False")

; Match a Lua pattern
(identifier) @constant
  (#lua-match? @constant "^[A-Z_]+$")

; Match a Vim regex  
(identifier) @constant
  (#match? @constant "^[A-Z_]+$")
For performance, prefer #eq? over #any-of? over #lua-match? over #match?.

Setting Priority

Control highlight precedence with priority directives:
; Default priority is 100
(comment) @comment

; Higher priority takes precedence
(comment
  content: (tag) @comment.todo
  (#eq? @comment.todo "TODO"))
  (#set! priority 110)
Only set priorities between 90 and 120 to avoid conflicts with other highlight sources.

Writing Fold Queries

Fold queries mark regions that can be folded:
~/.config/nvim/queries/lua/folds.scm
; extends

; Fold function bodies
(function_definition) @fold

; Fold table constructors
(table_constructor) @fold

; Fold multi-line comments
(comment) @fold
  (#lua-match? @fold "^%-%-%-")

Good Fold Candidates

  • Function/method definitions
  • Class/struct definitions
  • Blocks (if, while, for)
  • Array/object literals
  • Multi-line comments
  • Import statement groups

Bad Fold Candidates

  • Single-line expressions
  • Variable assignments
  • Chain expressions (e.g., obj.prop.prop)

Writing Indent Queries

Indentation queries are experimental and may change.
Indent queries control automatic indentation:
~/.config/nvim/queries/python/indents.scm
; extends

; Indent inside function bodies
(function_definition
  body: (block) @indent.begin)

; Dedent at closing keywords
["end" "else" "elif"] @indent.branch

; Maintain indentation for comments
(comment) @indent.auto

Indent Captures

@indent.begin     ; Start indented region
@indent.end       ; End indented region (next line dedents)
@indent.branch    ; Dedent at this node (e.g., else, elif)

Example: Function Indentation

; Indent function bodies
(function_definition) @indent.begin

; Dedent at "end" keyword
"end" @indent.end

; Align function arguments
(parameters) @indent.align
  (#set! indent.open_delimiter "(")
  (#set! indent.close_delimiter ")")

Writing Injection Queries

Injection queries enable parsing of embedded languages:
~/.config/nvim/queries/html/injections.scm
; extends

; JavaScript in <script> tags
(script_element
  (raw_text) @injection.content
  (#set! injection.language "javascript"))

; CSS in <style> tags
(style_element
  (raw_text) @injection.content
  (#set! injection.language "css"))

Dynamic Language Detection

; Detect language from attribute
(script_element
  (start_tag
    (attribute
      (attribute_name) @_attr
      (quoted_attribute_value (attribute_value) @injection.language)
      (#eq? @_attr "type")))
  (raw_text) @injection.content)
This allows <script type="module"> to be highlighted as JavaScript.

Filename-based Injection

; Inject based on filepath
(import_statement
  path: (string_content) @injection.filename
  content: (_) @injection.content)

Testing Your Queries

1

Use :InspectTree

Open a file and run :InspectTree to see the parse tree. Hover over nodes to see corresponding text.
2

Use :EditQuery

Run :EditQuery to open an interactive query editor. Write patterns and see matches in real-time.
3

Use :Inspect

Position cursor and run :Inspect to see what highlight groups are applied at that location.

Query Development Tools

tree-sitter CLI

# Parse a file and show the tree
tree-sitter parse file.lua

# Test a query
tree-sitter query queries/lua/highlights.scm file.lua

ts_query_ls

ts_query_ls is a language server for query files:
  • Validation
  • Autocompletion
  • Formatting
  • Error diagnostics
Install and configure it in your Neovim LSP config.

Advanced Techniques

Inheriting from Other Languages

TypeScript extends JavaScript:
~/.config/nvim/queries/typescript/highlights.scm
; inherits: javascript

; Add TypeScript-specific patterns
(type_annotation) @type
(interface_declaration) @type.definition

Optional Inheritance

Use parentheses for optional inheritance:
; inherits: javascript,(jsx)
This inherits JavaScript, and optionally JSX if available.

Format Preservation

; format-ignore
(complex_pattern
  (nested
    (very_long_pattern) @capture
    (#some-predicate? @capture)))
The ; format-ignore directive prevents auto-formatting of the following pattern.

Concealing Text

; Conceal lambda keyword
"lambda" @conceal
  (#set! conceal "λ")

; Conceal part of a capture
(string
  "\"" @string.delimiter
    (#offset! @string.delimiter 0 1 0 0)
    (#set! conceal ""))

Spell Checking

; Enable spell checking in comments
(comment) @spell

; Disable in code strings
(string) @nospell

Real-World Examples

Example 1: Custom Python Highlights

~/.config/nvim/queries/python/highlights.scm
; extends

; Highlight self parameter specially
(parameters
  (identifier) @variable.builtin
  (#eq? @variable.builtin "self"))

; Highlight type hints
(type_annotation
  (type) @type)

; Highlight decorators
(decorator) @attribute

; Highlight f-string expressions
(interpolation) @string.escape

Example 2: Lua Function Folds

~/.config/nvim/queries/lua/folds.scm
; extends

; Fold function definitions
[
  (function_definition)
  (function_declaration)
] @fold

; Fold table constructors with multiple lines
(table_constructor) @fold

; Fold do...end blocks
(do_statement) @fold

; Fold if statements
(if_statement) @fold

Example 3: Markdown Injections

~/.config/nvim/queries/markdown/injections.scm
; extends

; Inject language based on fence info string
(fenced_code_block
  (info_string
    (language) @injection.language)
  (code_fence_content) @injection.content)

; Handle inline code as plain text
(inline
  (code_span) @none)

Troubleshooting

Query Not Loading

  1. Check file location: ~/.config/nvim/queries/<language>/
  2. Verify filename: highlights.scm, folds.scm, etc.
  3. Check for syntax errors: :messages
  4. Ensure parser is installed: :TSInstallInfo

Invalid Pattern Errors

Invalid pattern at line 5
  1. Use :InspectTree to see valid node names
  2. Check field names in the parser grammar
  3. Test with :EditQuery

Highlights Not Applying

  1. Check priority with :Inspect
  2. Verify capture group names are valid
  3. Test predicates match correctly
  4. Ensure colorscheme defines the highlight group

Performance Issues

Complex queries with many predicates can slow down highlighting.
  • Use #eq? instead of #match? when possible
  • Limit the scope of patterns
  • Avoid overlapping captures
  • Profile with :profile start profile.log and :profile func *treesitter*

Query Syntax Reference

For complete query syntax documentation, see:

Next Steps