vimtricks.wiki Concise Vim tricks, one at a time.

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 = 99 keeps all folds open by default so you are not confronted with collapsed code on every file open
  • Use za to toggle a fold, zR to open all folds, zM to 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

Next

What is the difference between the inner word (iw) and inner WORD (iW) text objects in Vim?