How to Set Up nvim-treesitter for Better Code Parsing in Neovim
The jdhao/nvim-config repository implements a lazy-loading nvim-treesitter setup that installs parsers on-demand via FileType autocmds and enhances code navigation with treesitter-textobjects.
Setting up nvim-treesitter for better code parsing requires more than just installing the plugin. The jdhao/nvim-config repository demonstrates a performance-conscious approach that defers parser installation until files are actually opened, while providing syntax-aware text objects for efficient code manipulation. This configuration leverages Lazy.nvim for plugin management and implements custom autocmds to handle parser lifecycle management.
Core Architecture Components
The implementation spans four interconnected layers that work together to provide fast, language-aware parsing capabilities.
Lazy.nvim Plugin Management
The plugin specification in lua/plugin_specs.lua defines nvim-treesitter with a build hook that ensures parsers stay current. Lines 66-74 declare the plugin with build = ":TSUpdate", which automatically updates all installed parsers whenever the plugin is updated through Lazy.nvim.
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", config = function()
require("config.treesitter")
end,
},
{ "nvim-treesitter/nvim-treesitter-textobjects", event = "VeryLazy",
config = function() require("config.treesitter-textobjects") end,
},
On-Demand Parser Installation
Rather than installing all parsers upfront, the configuration in lua/config/treesitter.lua uses a FileType autocmd to detect when you open a file and install only the required parser. The autocmd triggers on filetypes defined in the ensure_installed list, checks if the parser exists using vim.treesitter.language.add(), and downloads it via require("nvim-treesitter").install() if missing.
Text Objects Integration
The lua/config/treesitter-textobjects.lua file extends the editing model beyond raw text by mapping Tree-sitter captures to Vim operators. This allows you to select, delete, or change logical code units like functions and classes using standard Vim commands combined with Tree-sitter awareness.
Implementing the Lazy Parser Installation
The heart of the setup resides in the FileType autocmd defined in lua/config/treesitter.lua. This implementation caches available parsers and installs missing ones asynchronously before activating highlighting.
vim.api.nvim_create_autocmd("FileType", {
pattern = ensure_installed, -- list of languages we care about
callback = function(args)
local ft = vim.bo[args.buf].filetype
local lang = vim.treesitter.language.get_lang(ft)
-- 1️⃣ If the parser is not yet added, try to install it.
if not vim.treesitter.language.add(lang) then
local available = vim.g.ts_available or require("nvim-treesitter").get_available()
vim.g.ts_available = available -- cache for later calls
if vim.tbl_contains(available, lang) then
vim.print(string.format("Installing parsers and queries for %s", lang))
require("nvim-treesitter").install(lang) -- triggers a download
end
end
-- 2️⃣ Once the parser is present, start highlighting.
if vim.treesitter.language.add(lang) then
vim.treesitter.start(args.buf, lang)
-- Optional: enable folding and indentation
-- vim.wo[0][0].foldexpr = "v:lua.vim.treesitter.foldexpr()"
-- vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
end
end,
})
The code first attempts to add the language using vim.treesitter.language.add(). If this returns false, indicating the parser is not installed, it retrieves the list of available parsers (caching the result in vim.g.ts_available for subsequent calls) and triggers the installation. Once the parser is available, vim.treesitter.start() activates syntax highlighting for the buffer.
Configuring Treesitter Text Objects
After parsers are available, the textobjects configuration enables structural code navigation. The setup in lua/config/treesitter-textobjects.lua defines selection modes and binds them to operator-pending keys.
require("nvim-treesitter-textobjects").setup {
select = {
lookahead = true,
selection_modes = {
["@function.inner"] = "V", -- linewise
["@function.outer"] = "V",
["@class.outer"] = "V",
["@parameter.outer"]= "v", -- charwise
},
include_surrounding_whitespace = false,
},
}
-- Keybindings for function text objects
vim.keymap.set({ "x", "o" }, "af", function()
require("nvim-treesitter-textobjects.select")
.select_textobject("@function.outer", "textobjects")
end)
-- Analogous bindings exist for `if`, `ac`, `ic`, and `as`
These mappings allow syntactically correct selections across complex nesting:
af– Select outer function (includes signature and body)if– Select inner function (body only)ac– Select outer classic– Select inner classas– Select surrounding scope
Essential File Paths in jdhao/nvim-config
Understanding the repository structure helps when adapting this configuration to your own setup:
init.lua– Entry point that bootstraps Lazy.nvim and loads the plugin specificationslua/plugin_specs.lua– Contains the plugin declarations for both nvim-treesitter and nvim-treesitter-textobjects (lines 66-74)lua/config/treesitter.lua– Houses the FileType autocmd responsible for lazy parser installation and highlighting activationlua/config/treesitter-textobjects.lua– Configures selectable text objects and their associated keymapslua/utils.lua– Provides helper utilities used throughout the configuration
Summary
- Lazy.nvim manages the plugin installation and runs
:TSUpdateautomatically on plugin updates via thebuildhook - FileType autocmds in
lua/config/treesitter.luatrigger on-demand parser installation only when you open relevant filetypes, reducing startup time - On-demand installation uses
vim.treesitter.language.add()to check parser availability andrequire("nvim-treesitter").install()to download missing grammars - Highlighting activation occurs via
vim.treesitter.start()once the parser is confirmed present - Text objects defined in
lua/config/treesitter-textobjects.luaprovide syntax-aware selections using keys likeaf,if,ac,ic, andas
Frequently Asked Questions
How does the lazy parser installation work in jdhao/nvim-config?
The configuration creates a FileType autocmd that watches for specific filetypes defined in ensure_installed. When you open a file, the callback checks if the parser exists using vim.treesitter.language.add(). If the parser is missing, it consults the cached list of available parsers and runs require("nvim-treesitter").install(lang) to download it. Once installation completes, vim.treesitter.start() activates highlighting.
What text objects are available for code navigation?
The setup provides five primary text objects mapped to convenient keys: af (outer function), if (inner function), ac (outer class), ic (inner class), and as (surrounding scope). These work in visual, normal, and operator-pending modes, allowing operations like daf (delete a function) or vic (visual select inner class) that respect actual code structure rather than just line breaks.
How do I update or reinstall Tree-sitter parsers?
Run :TSUpdate to update all installed parsers to their latest versions. This command executes automatically when Lazy.nvim updates the nvim-treesitter plugin due to the build = ":TSUpdate" specification. For manual management, use :TSInstallInfo to list installed parsers, :TSUninstall <lang> to remove a specific parser, and :TSInstall <lang> to reinstall it.
Can I enable folding and indentation with this setup?
Yes. The autocmd in lua/config/treesitter.lua includes commented lines showing how to enable these features. Uncomment vim.wo[0][0].foldexpr = "v:lua.vim.treesitter.foldexpr()" to enable syntax-based folding, and vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" to enable Tree-sitter powered indentation. These settings leverage the parse tree for accurate code structure recognition.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →