GitPedia

Nvim treeclimber

Neovim structured editing plugin

From Dkendal·Updated June 7, 2026·View on GitHub·

Neovim plugin for treesitter based navigation and selection. Takes inspiration from [ParEdit](https://calva.io/paredit/). The project is written primarily in Lua, distributed under the Apache License 2.0 license, first published in 2021. Key topics include: lua, neovim, neovim-lua-plugin, neovim-plugin, nvim-treesitter.

Nvim-Treeclimber

Neovim plugin for treesitter based navigation and selection.
Takes inspiration from ParEdit.

Requires neovim >= 0.10.

Usage

Plug MappingModeActionDemo
<Plug>(treeclimber-select-previous)n,x,oSelect the previous sibling nodeselect-prev
<Plug>(treeclimber-select-shrink)n,x,oShrink selection (select child node)select-shrink
<Plug>(treeclimber-select-parent)n,x,oExpand selection (select parent node)select-expand
<Plug>(treeclimber-select-next)n,x,oSelect the next sibling nodeselect-next
<Plug>(treeclimber-select-grow-forward)n,x,oGrow selection forward (add next sibling)grow-selection-next
<Plug>(treeclimber-select-grow-backward)n,x,oGrow selection backward (add previous sibling)grow-selection-prev
<Plug>(treeclimber-select-siblings-backward)n,x,oSelect first siblingselect-first-sibling
<Plug>(treeclimber-select-siblings-forward)n,x,oSelect last siblingselect-last-sibling
<Plug>(treeclimber-select-top-level)n,x,oSelect top-level nodeselect-top-level
<Plug>(treeclimber-select-backward)n,x,oSelect and move to node start
<Plug>(treeclimber-select-forward-end)n,x,oSelect and move to node end
<Plug>(treeclimber-show-control-flow)nShow control flow
<Plug>(treeclimber-select-current-node)x,oSelect current node (inner)
<Plug>(treeclimber-select-expand)x,oSelect parent node (around)

Commands

:TCDiffThis

Diff two visual selections based on their AST difference.
Requires that difft is available in your path.

To use, make your first selection and call :TCDiffThis, then make your second selection and call :TCDiffThis again.

tc-diff-this.webm

:TCShowControlFlow

Populate the quick fix with all branches required to reach the current node.

https://user-images.githubusercontent.com/3162299/203097777-a9a84c2d-8dec-4db8-a4c7-4c9a66ca26fe.mp4

Installation

Use your preferred package manager, or the built-in package system (:help packages).

sh
mkdir -p ~/.config/nvim/pack/dkendal/opt cd ~/.config/nvim/pack/dkendal/opt git clone https://github.com/dkendal/nvim-treeclimber.git
lua
-- ~/.config/nvim/init.lua vim.cmd.packadd('nvim-treeclimber') require('nvim-treeclimber').setup()

Configuration

The plugin can be configured in three ways:

  1. Using setup() function:
lua
require('nvim-treeclimber').setup({ highlight = true -- or other highlight options (see below) })
  1. Using lazy.nvim opts:
lua
{ "dkendal/nvim-treeclimber", opts = { highlight = true } }
  1. Setting vim.g.treeclimber directly:
lua
vim.g.treeclimber = { highlight = true }

Configuration Options

highlight

Controls the visual highlighting of treesitter nodes during navigation. Can be:

  • true (default) - Enable automatic highlighting with default blend value (50)
  • false - Disable all highlighting
  • number - Enable highlighting with custom blend value between the Visual and Normal highlights (0-100, where 0 is the same as Visual and 100 is the same as Normal)
  • function - Custom function for dynamic highlight setup (e.g., colorscheme changes)

Examples:

lua
-- Disable highlighting require('nvim-treeclimber').setup({ highlight = false }) -- Custom blend value (lighter highlighting) require('nvim-treeclimber').setup({ highlight = 25 }) -- Dynamic highlight function (adapts to colorscheme changes) require('nvim-treeclimber').setup({ highlight = function() local hi = require("nvim-treeclimber.hi") local Normal = hi.get_hl("Normal", { follow = true }) local normal = hi.HSLUVHighlight:new(Normal) local Visual = hi.get_hl("Visual", { follow = true }) local visual = hi.HSLUVHighlight:new(Visual) local blend_value = 50 local set_hl = vim.api.nvim_set_hl set_hl(0, "TreeClimberSiblingBoundary", { background = visual.bg.mix(normal.bg, blend_value).hex }) set_hl(0, "TreeClimberSibling", { background = visual.bg.mix(normal.bg, blend_value).hex }) set_hl(0, "TreeClimberParent", { background = visual.bg.mix(normal.bg, blend_value).hex }) set_hl(0, "TreeClimberParentStart", { background = visual.bg.mix(normal.bg, blend_value).hex }) end }) -- For hardcoded highlights, disable the plugin's highlighting and set your own: require('nvim-treeclimber').setup({ highlight = false }) vim.api.nvim_set_hl(0, "TreeClimberSiblingBoundary", { bg = "#3c3836" }) vim.api.nvim_set_hl(0, "TreeClimberSibling", { bg = "#504945" }) vim.api.nvim_set_hl(0, "TreeClimberParent", { bg = "#665c54" }) vim.api.nvim_set_hl(0, "TreeClimberParentStart", { bg = "#7c6f64" })

The plugin automatically creates these highlight groups that blend the Visual highlight with the Normal background color:

  • TreeClimberSiblingBoundary - Highlights sibling boundaries
  • TreeClimberSibling - Highlights sibling nodes
  • TreeClimberParent - Highlights parent nodes
  • TreeClimberParentStart - Highlights parent node start

Lazy.nvim Configuration

If you're using lazy.nvim, you can configure nvim-treeclimber with lazy-loaded key mappings:

lua
return { "dkendal/nvim-treeclimber", config = function() require('nvim-treeclimber').setup() end, keys = { -- Core navigation { "<M-h>", "<Plug>(treeclimber-select-previous)", mode = { "n", "x", "o" }, desc = "Select previous node" }, { "<M-l>", "<Plug>(treeclimber-select-next)", mode = { "n", "x", "o" }, desc = "Select next node" }, { "<M-k>", "<Plug>(treeclimber-select-parent)", mode = { "n", "x", "o" }, desc = "Select parent node" }, { "<M-j>", "<Plug>(treeclimber-select-shrink)", mode = { "n", "x", "o" }, desc = "Select child node" }, -- Growth selection { "<M-H>", "<Plug>(treeclimber-select-grow-backward)", mode = { "n", "x", "o" }, desc = "Grow selection backward" }, { "<M-L>", "<Plug>(treeclimber-select-grow-forward)", mode = { "n", "x", "o" }, desc = "Grow selection forward" }, -- Sibling navigation { "<M-[>", "<Plug>(treeclimber-select-siblings-backward)", mode = { "n", "x", "o" }, desc = "Select first sibling" }, { "<M-]>", "<Plug>(treeclimber-select-siblings-forward)", mode = { "n", "x", "o" }, desc = "Select last sibling" }, -- Top level { "<M-g>", "<Plug>(treeclimber-select-top-level)", mode = { "n", "x", "o" }, desc = "Select top-level node" }, -- Movement selection { "<M-b>", "<Plug>(treeclimber-select-backward)", mode = { "n", "x", "o" }, desc = "Select and move to node start" }, { "<M-e>", "<Plug>(treeclimber-select-forward-end)", mode = { "n", "x", "o" }, desc = "Select and move to node end" }, -- Visual/operator mode specific { "i.", "<Plug>(treeclimber-select-current-node)", mode = { "x", "o" }, desc = "Select current node (inner)" }, { "a.", "<Plug>(treeclimber-select-expand)", mode = { "x", "o" }, desc = "Select parent node (around)" }, -- Commands { "<leader>k", "<Plug>(treeclimber-show-control-flow)", mode = "n", desc = "Show control flow" }, }, cmd = { "TCDiffThis", "TCShowControlFlow", "TCHighlightExternalDefinitions" }, }

Custom Key Bindings

To customize key bindings, use the <Plug> mappings from the table above. For example, to use H/L for navigation:

lua
local tc = require('nvim-treeclimber') -- Use custom keys vim.keymap.set({ "n", "x", "o" }, "H", "<Plug>(treeclimber-select-previous)") vim.keymap.set({ "n", "x", "o" }, "L", "<Plug>(treeclimber-select-next)")

Or use the keys attribute if you're using lazy.nvim.


Copyright Dylan Kendal 2025.

Contributors

Showing top 3 contributors by commit count.

View all contributors on GitHub →

This article is auto-generated from Dkendal/nvim-treeclimber via the GitHub API.Last fetched: 6/27/2026