How do I use Treesitter-aware code folding in Neovim for accurate fold regions based on syntax?
Answer
vim.treesitter.foldexpr()
Explanation
Neovim's built-in Treesitter integration includes vim.treesitter.foldexpr(), which acts as a foldexpr function that uses the parsed syntax tree to determine accurate fold boundaries. Unlike foldmethod=indent (which can break with mixed indentation) or foldmethod=syntax (which relies on often-incomplete regex-based syntax files), Treesitter folding understands the actual structure of the code.
How it works
Set foldmethod=expr and point foldexpr to vim.treesitter.foldexpr(). Neovim calls this function for each line to determine its fold level using the Treesitter parse tree for the active filetype.
-- In init.lua:
vim.opt.foldmethod = 'expr'
vim.opt.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
vim.opt.foldlevel = 99 -- start with all folds open
vim.opt.foldlevelstart = 99 -- open all folds when opening a file
vim.opt.foldenable = true
Example
With a Go file open and a Treesitter parser installed for Go, each function body, struct block, and if/for block becomes a fold region based on the actual AST, not indentation heuristics.
Tips
foldlevel = 99keeps all folds open by default so you are not confronted with collapsed code on every file open- Use
zato toggle a fold,zRto open all folds,zMto close all folds - A Treesitter parser for the filetype must be installed (
:TSInstall {lang}) for this to work; Neovim includes parsers for common languages by default - Pair with
vim.opt.foldtext = ''in Neovim 0.10+ for a minimal fold display that shows the folded code's first line with real syntax highlighting