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

How do I use Treesitter for syntax-aware text selection in Neovim?

Answer

Treesitter incremental selection with grn/grc

Explanation

nvim-treesitter provides incremental selection that expands based on the syntax tree rather than simple text patterns. Starting from the cursor, it selects the smallest syntax node, then expands to the parent node with each press.

Setup

require('nvim-treesitter.configs').setup {
  incremental_selection = {
    enable = true,
    keymaps = {
      init_selection = 'grn',    -- Start selection
      node_incremental = 'grn',  -- Expand to next node
      node_decremental = 'grc',  -- Shrink to previous node
      scope_incremental = 'grs', -- Expand to scope
    },
  },
}

How it works

With cursor on a variable name in JavaScript:

const result = items.filter(item => item.active)
  1. grn — selects item (identifier)
  2. grn — selects item.active (member expression)
  3. grn — selects item => item.active (arrow function)
  4. grn — selects items.filter(item => item.active) (call expression)
  5. grn — selects the entire assignment
  6. grc — shrinks back one level

Treesitter text objects

With nvim-treesitter-textobjects:

-- Select around/inside functions, classes, etc.
vim.keymap.set({'x', 'o'}, 'af', '@function.outer')  -- around function
vim.keymap.set({'x', 'o'}, 'if', '@function.inner')  -- inside function
vim.keymap.set({'x', 'o'}, 'ac', '@class.outer')     -- around class
vim.keymap.set({'x', 'o'}, 'ic', '@class.inner')     -- inside class

Now daf deletes an entire function, cif changes the function body.

Tips

  • Treesitter understands the AST, so selections are always syntactically correct
  • Works across all languages with Treesitter parsers installed
  • nvim-treesitter-textobjects adds @parameter, @conditional, @loop text objects too
  • :TSPlaygroundToggle lets you explore the syntax tree visually
  • Install parsers with :TSInstall python javascript go etc.

Next

How do I run the same command across all windows, buffers, or tabs?