Syntax highlighting
Tree-sitter highlighting is more accurate and maintainable than regex-based approaches because it’s based on the actual syntax tree structure.How it works
Highlighting works in three steps:- Parse: The tree-sitter parser creates a syntax tree
- Query:
highlights.scmmatches tree nodes to capture names - Highlight: Capture names map to highlight groups in your colorscheme
Enabling highlighting
- Per-filetype (ftplugin)
- Global (autocommand)
- All languages
Create This enables highlighting for Python files only.
~/.config/nvim/ftplugin/python.lua:Tree-sitter highlighting is provided by Neovim core, not nvim-treesitter. nvim-treesitter only provides the query files.
Highlight groups
Tree-sitter uses semantic capture names that map to highlight groups:| Capture | Highlight Group | Description |
|---|---|---|
@keyword.return | @keyword.return | Return statements |
@function | @function | Function definitions |
@function.call | @function.call | Function calls |
@variable | @variable | Variable names |
@string | @string | String literals |
@comment | @comment | Comments |
@type | @type | Type names |
Use
:Inspect to see which highlight groups are applied at the cursor position.Inspecting highlights
Debug highlighting issues:Highlight priority
When multiple captures overlap, priority determines precedence:Code folding
Tree-sitter folding is based on the syntax tree structure, making it more accurate than indent-based or marker-based folding.Enabling folds
You may also want to set
foldlevel and foldnestmax for better default behavior:Defining folds
Thefolds.scm query marks foldable nodes:
What should be foldable?
- Good fold candidates
- Poor fold candidates
- Function and method definitions
- Class and interface definitions
- Control flow statements (if/while/for)
- Blocks with clear delimiters
- Arrays and objects
- Import statements (consecutive)
- Line comments (consecutive)
- Documentation blocks
Fold commands
Indentation
Tree-sitter indentation uses the syntax tree to determine proper indentation levels, providing more intelligent indentation than simple indent-on-tab.Enabling indentation
Note the specific quote usage: double quotes for the string, single quotes for the module name.
Indent captures
Theindents.scm query defines indentation behavior:
@indent.begin
@indent.begin
Increase indentation for the next line:Optionally, add immediate indent even when block is empty:
@indent.end
@indent.end
Mark the end of an indented region:
@indent.branch
@indent.branch
Start dedented region on the same line:
@indent.dedent
@indent.dedent
Dedent on the following line:
@indent.align
@indent.align
Align arguments/parameters:This handles both styles:
@indent.auto
@indent.auto
Behave like Vim’s
autoindent:@indent.ignore and @indent.zero
@indent.ignore and @indent.zero
Indentation edge cases
Avoiding last-line clashes
For Python-style indentation where closing delimiter shouldn’t match next line:Language injections
Injections enable syntax highlighting for embedded languages (SQL in strings, CSS in HTML, code in markdown).How injections work
- Detect:
injections.scmidentifies embedded language regions - Parse: A separate parser parses the embedded content
- Highlight: The embedded language’s queries provide highlighting
Injections are automatically enabled when you use tree-sitter highlighting. No additional setup required.
Injection types
- Static language
- Dynamic language
- Filename-based
Explicitly specify the language:Highlights SQL in:
sql("SELECT * FROM users")Real injection examples
Vim commands in Lua
Tree-sitter queries in strings
HTML in JavaScript
Injection directives
Feature status by language
Different languages support different features. Check the supported languages table:| Language | Highlights | Folds | Indents | Injections | Locals |
|---|---|---|---|---|---|
| python | H | F | I | J | L |
| rust | H | F | I | J | L |
| javascript | H | F | I | J | L |
| lua | H | F | I | J | L |
| markdown | H | F | I | J | - |
See SUPPORTED_LANGUAGES.md for the complete feature matrix.
Combining features
A typical setup enables all features for your preferred languages:Troubleshooting features
Highlighting doesn’t work
- Check parser is installed:
:checkhealth treesitter - Verify queries exist:
:echo stdpath('data') . '/site/queries/{lang}' - Inspect tree:
:InspectTree - Check highlights:
:Inspect
Folding is incorrect
- Verify fold query exists:
runtime/queries/{lang}/folds.scm - Check folding is enabled:
:set foldmethod? foldexpr? - Test fold query:
:EditQuery folds
Indentation issues
- Check indent query exists:
runtime/queries/{lang}/indents.scm - Verify
indentexpris set correctly - Test with
:IndentBlanklineToggleif using indent-blankline.nvim
Injections don’t highlight
- Ensure embedded language parser is installed
- Check injection query:
:EditQuery injections - Verify language name matches parser name
Performance considerations
Tree-sitter features are generally fast, but can impact performance on very large files:- Highlighting: Incremental and fast for files < 10K lines
- Folding: Calculated once when opening file
- Indentation: Computed per line during editing
- Injections: Adds overhead for embedded languages
For files > 50K lines, consider disabling tree-sitter features: