Skip to main content
Highlight queries assign Tree-sitter nodes to capture groups that define syntax highlighting. This feature is implemented in Neovim and documented at :h treesitter-highlight.
Your color scheme needs to define (or link) these captures as highlight groups. Use Neovim’s built-in :Inspect function to see which highlight groups are applied at a given position.

Basic Structure

Highlight queries use pattern matching to capture nodes:
(function_definition
  name: (identifier) @function)

(string) @string

(comment) @comment @spell

Valid Captures

The valid captures for Neovim are different from other editors like Helix. You cannot just copy queries from parser repositories. All valid captures are listed below. Verify your queries with make lintquery.

Identifiers

@variable
capture
Various variable names
@variable.builtin
capture
Built-in variable names (e.g. this, self)
@variable.parameter
capture
Parameters of a function
@variable.parameter.builtin
capture
Special parameters (e.g. _, it)
@variable.member
capture
Object and struct fields
@constant
capture
Constant identifiers
@constant.builtin
capture
Built-in constant values
@constant.macro
capture
Constants defined by the preprocessor
@module
capture
Modules or namespaces
@module.builtin
capture
Built-in modules or namespaces
@label
capture
GOTO and other labels (e.g. label: in C), including heredoc labels

Literals

@string
capture
String literals
@string.documentation
capture
String documenting code (e.g. Python docstrings)
@string.regexp
capture
Regular expressions
@string.escape
capture
Escape sequences
@string.special
capture
Other special strings (e.g. dates)
@string.special.symbol
capture
Symbols or atoms
@string.special.url
capture
URIs (e.g. hyperlinks)
@string.special.path
capture
Filenames
@character
capture
Character literals
@character.special
capture
Special characters (e.g. wildcards)
@boolean
capture
Boolean literals
@number
capture
Numeric literals
@number.float
capture
Floating-point number literals

Types

@type
capture
Type or class definitions and annotations
@type.builtin
capture
Built-in types
@type.definition
capture
Identifiers in type definitions (e.g. typedef <type> <identifier> in C)
@attribute
capture
Attribute annotations (e.g. Python decorators, Rust lifetimes)
@attribute.builtin
capture
Builtin annotations (e.g. @property in Python)
@property
capture
The key in key/value pairs

Functions

@function
capture
Function definitions
@function.builtin
capture
Built-in functions
@function.call
capture
Function calls
@function.macro
capture
Preprocessor macros
@function.method
capture
Method definitions
@function.method.call
capture
Method calls
@constructor
capture
Constructor calls and definitions
@operator
capture
Symbolic operators (e.g. + / *)

Keywords

@keyword
capture
Keywords not fitting into specific categories
@keyword.coroutine
capture
Keywords related to coroutines (e.g. go in Go, async/await in Python)
@keyword.function
capture
Keywords that define a function (e.g. func in Go, def in Python)
@keyword.operator
capture
Operators that are English words (e.g. and / or)
@keyword.import
capture
Keywords for including or exporting modules (e.g. import / from in Python)
@keyword.type
capture
Keywords describing namespaces and composite types (e.g. struct, enum)
@keyword.modifier
capture
Keywords modifying other constructs (e.g. const, static, public)
@keyword.repeat
capture
Keywords related to loops (e.g. for / while)
@keyword.return
capture
Keywords like return and yield
@keyword.debug
capture
Keywords related to debugging
@keyword.exception
capture
Keywords related to exceptions (e.g. throw / catch)
@keyword.conditional
capture
Keywords related to conditionals (e.g. if / else)
@keyword.conditional.ternary
capture
Ternary operator (e.g. ? / :)
@keyword.directive
capture
Various preprocessor directives & shebangs
@keyword.directive.define
capture
Preprocessor definition directives

Punctuation

@punctuation.delimiter
capture
Delimiters (e.g. ; / . / ,)
@punctuation.bracket
capture
Brackets (e.g. () / {} / [])
@punctuation.special
capture
Special symbols (e.g. {} in string interpolation)

Comments

@comment
capture
Line and block comments
@comment.documentation
capture
Comments documenting code
@comment.error
capture
Error-type comments (e.g. ERROR, FIXME, DEPRECATED)
@comment.warning
capture
Warning-type comments (e.g. WARNING, FIX, HACK)
@comment.todo
capture
Todo-type comments (e.g. TODO, WIP)
@comment.note
capture
Note-type comments (e.g. NOTE, INFO, XXX)

Markup

Mainly for markup languages.
@markup.strong
capture
Bold text
@markup.italic
capture
Italic text
@markup.strikethrough
capture
Struck-through text
@markup.underline
capture
Underlined text (only for literal underline markup!)
@markup.heading
capture
Headings, titles (including markers)
@markup.heading.1
capture
Top-level heading
@markup.heading.2
capture
Section heading
@markup.heading.3
capture
Subsection heading
@markup.heading.4
capture
Fourth-level heading
@markup.heading.5
capture
Fifth-level heading
@markup.heading.6
capture
Sixth-level heading
@markup.quote
capture
Block quotes
@markup.math
capture
Math environments (e.g. $ ... $ in LaTeX)
Text references, footnotes, citations, etc.
Link, reference descriptions
URL-style links
@markup.raw
capture
Literal or verbatim text (e.g. inline code)
@markup.raw.block
capture
Literal or verbatim text as a stand-alone block (use priority 90 for blocks with injections)
@markup.list
capture
List markers
@markup.list.checked
capture
Checked todo-style list markers
@markup.list.unchecked
capture
Unchecked todo-style list markers
@diff.plus
capture
Added text (for diff files)
@diff.minus
capture
Deleted text (for diff files)
@diff.delta
capture
Changed text (for diff files)
@tag
capture
XML-style tag names (and similar)
@tag.builtin
capture
Builtin tag names (e.g. HTML5 tags)
@tag.attribute
capture
XML-style tag attributes
@tag.delimiter
capture
XML-style tag delimiters

Non-highlighting Captures

@conceal
capture
Captures that are only meant to be concealed
See :h tree-sitter-highlight-conceal. The capture should be meaningful to allow proper highlighting when set conceallevel=0. A conceal can be restricted to part of the capture via the #offset! directive.
@spell
capture
For defining regions to be spellchecked
@nospell
capture
For defining regions that should NOT be spellchecked
The main types of nodes that should be spell checked are:
  • Comments
  • Strings; where it makes sense. Strings that have interpolation or are typically used for non-text purposes are not spell checked (e.g. bash).

Predicates

Captures can be restricted according to node contents using predicates.
For performance reasons, prefer predicates in this order:
  1. #eq? (literal match)
  2. #any-of? (one of several literal matches)
  3. #lua-match? (match against a Lua pattern)
  4. #match?/#vim-match? (match against a Vim regular expression)
Besides those provided by Neovim, nvim-treesitter also implements:
#kind-eq?      ; checks whether a capture corresponds to a given set of nodes
#any-kind-eq?  ; checks whether any of a list of captures corresponds to a given set of nodes

Examples

; Match specific literal
((identifier) @constant.builtin
  (#eq? @constant.builtin "self"))

; Match one of several literals
((identifier) @constant.builtin
  (#any-of? @constant.builtin "NotImplemented" "Ellipsis" "quit"))

; Match Lua pattern (uppercase followed by uppercase/digits)
((identifier) @constant
  (#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))

Directives

Nodes contain metadata that can be modified via directives.

Priority

Captures can be assigned a priority to control precedence of highlights via the #set! priority <number> directive (see :h treesitter-highlight-priority).
The default priority for treesitter highlights is 100. Queries should only set priorities between 90 and 120, to avoid conflict with other sources of highlighting (such as diagnostics or LSP semantic tokens).
Precedence is also influenced by pattern order in a query file. If possible, try to achieve the correct result by reordering patterns before resorting to explicit priorities.

Example

((decorator
  "@" @attribute)
  (#set! priority 101))

(decorator
  (identifier) @attribute)

Inheriting Languages

If your language is an extension of another language (TypeScript extends JavaScript, for example), you can include queries from your base language by adding this as the first line:
; inherits: lang1,(optionallang)
If you want to inherit a language but don’t want languages inheriting from yours to inherit it, mark the language as optional (by putting it in parentheses).

Real-World Examples

Python Function Highlighting

; Function definitions
(function_definition
  name: (identifier) @function)

; Method definitions
(class_definition
  body: (block
    (function_definition
      name: (identifier) @function.method)))

; Function calls
(call
  function: (identifier) @function.call)

; Method calls
(call
  function: (attribute
    attribute: (identifier) @function.method.call))

Rust Variable Conventions

(identifier) @variable

; Assume all-caps names are constants
((identifier) @constant
  (#lua-match? @constant "^[A-Z][A-Z%d_]*$"))

; Assume uppercase start = type
((identifier) @type
  (#lua-match? @type "^[A-Z]"))

Tools

The following tools can help when writing highlight queries:
  • ts_query_ls - A language server for treesitter queries with validation, autocomplete, and formatting
  • :InspectTree - Shows the parsed tree for a buffer and highlights corresponding text
  • :EditQuery - Opens a playground to write query patterns and see captures in real-time
  • make lintquery - Validates that all captures are valid for Neovim
  • make checkquery - Verifies patterns are valid for the parser
  • make formatquery - Automatically formats queries to standard style