# How to Set Up nvim-treesitter for Better Code Parsing in Neovim

> Learn how to set up nvim-treesitter for enhanced code parsing and navigation in Neovim. This guide details lazy-loading parser installation and textobject integration.

- Repository: [jdhao/nvim-config](https://github.com/jdhao/nvim-config)
- Tags: how-to-guide
- Published: 2026-03-04

---

**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`](https://github.com/jdhao/nvim-config/blob/main/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.

```lua
{ "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`](https://github.com/jdhao/nvim-config/blob/main/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`](https://github.com/jdhao/nvim-config/blob/main/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`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter.lua). This implementation caches available parsers and installs missing ones asynchronously before activating highlighting.

```lua
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`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter-textobjects.lua) defines selection modes and binds them to operator-pending keys.

```lua
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 class
- **`ic`** – Select inner class
- **`as`** – 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`](https://github.com/jdhao/nvim-config/blob/main/init.lua)** – Entry point that bootstraps Lazy.nvim and loads the plugin specifications
- **[`lua/plugin_specs.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/plugin_specs.lua)** – Contains the plugin declarations for both nvim-treesitter and nvim-treesitter-textobjects (lines 66-74)
- **[`lua/config/treesitter.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter.lua)** – Houses the FileType autocmd responsible for lazy parser installation and highlighting activation
- **[`lua/config/treesitter-textobjects.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter-textobjects.lua)** – Configures selectable text objects and their associated keymaps
- **[`lua/utils.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/utils.lua)** – Provides helper utilities used throughout the configuration

## Summary

- **Lazy.nvim** manages the plugin installation and runs `:TSUpdate` automatically on plugin updates via the `build` hook
- **FileType autocmds** in [`lua/config/treesitter.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter.lua) trigger 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 and `require("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.lua`](https://github.com/jdhao/nvim-config/blob/main/lua/config/treesitter-textobjects.lua) provide syntax-aware selections using keys like `af`, `if`, `ac`, `ic`, and `as`

## 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`](https://github.com/jdhao/nvim-config/blob/main/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.