treemonkey.nvim

June 9, 2026 ยท View on GitHub

Yet another label-based node selection plugin.

There are some cases that start or end of the node position overlaps with the other nodes. If labels are overlayed, there is a difficulty on selecting the intended one (e.g., nvim-treehopper or leap-ast.nvim). If labels are inserted inline, your eyes may fail to track the node (e.g., flash.nvim).

Instead, this plugin does...

  1. Label start of nodes with lower case letters and end of nodes with the corresponding upper case letters.
  2. Ask user to input a label.
  3. If there are any labels hidden by the chosen label, then ask again to choose the label from the subset of choises.
  4. Select the node of the choice.

It's like monkey hanging around the Abstract Syntax Tree. Isn't it?

2023-12-29 12-39-32 mkv

Example

vim.keymap.set({"x", "o"}, "m", function()
  require("treemonkey").select({
    ignore_injections = false,
    highlight = { backdrop = "Comment" }
  })
end)

With lazy.nvim, ...

{
  "https://github.com/atusy/treemonkey.nvim",
  init = function()
    vim.keymap.set({"x", "o"}, "m", function()
      require("treemonkey").select({ ignore_injections = false })
    end)
  end
}

Backends

The candidate nodes can be gathered from two interchangeable backends that share the same label-based selection engine:

  • treemonkey.treesitter gathers nodes from vim.treesitter (the default). treemonkey.get and treemonkey.select are aliases of treemonkey.treesitter.get and treemonkey.treesitter.select.
  • treemonkey.lsp.selection_range gathers ranges from a language server via the textDocument/selectionRange request. It requires a language server with selectionRangeProvider attached to the buffer.
  • treemonkey.lsp.folding_range gathers folds from a language server via the textDocument/foldingRange request, restricted to the folds that contain the cursor and ordered innermost to outermost. It requires a language server with foldingRangeProvider attached to the buffer.
-- select based on the language server instead of treesitter
vim.keymap.set({ "x", "o" }, "m", function()
  require("treemonkey.lsp.selection_range").select()
end)

-- or select an enclosing fold reported by the language server
vim.keymap.set({ "x", "o" }, "m", function()
  require("treemonkey.lsp.folding_range").select()
end)