add treesitter plugin to highlight identical identifiers
authorLukas Hägele <lukas.haegele93@web.de>
Sun, 11 Oct 2026 13:52:36 +0000 (15:52 +0200)
committerLukas Hägele <lukas.haegele93@web.de>
Sun, 11 Oct 2026 13:52:36 +0000 (15:52 +0200)
plugin/treesitter/highlight-identifiers.lua [new file with mode: 0644]

diff --git a/plugin/treesitter/highlight-identifiers.lua b/plugin/treesitter/highlight-identifiers.lua
new file mode 100644 (file)
index 0000000..c399fa2
--- /dev/null
@@ -0,0 +1,63 @@
+local ns = vim.api.nvim_create_namespace("ts_highlight_identifiers")
+
+local function highlight_matching_identifiers()
+  local bufnr = vim.api.nvim_get_current_buf()
+  -- clear existing highlights from this namespace
+  vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
+
+  -- get the node under the cursor
+  local node = vim.treesitter.get_node()
+  if not node or not node:type():match("identifier") then
+    return
+  end
+
+  -- extract its text
+  local text = vim.treesitter.get_node_text(node, bufnr)
+  if not text or text == "" then return end
+
+  -- get the language parser and root tree
+  local ok_parser, parser = pcall(vim.treesitter.get_parser, bufnr)
+  if not ok_parser or not parser then return end
+  local tree = parser:parse()[1]
+  if not tree then return end
+  local root = tree:root()
+
+  -- build a dynamic treesitter query to find all nodes of any identifier type matching the text
+  local lang = parser:lang()
+  local query_string = string.format([[
+    (
+      (%s) @ident
+      (#eq? @ident "%s")
+    )
+  ]], node:type(), text)
+
+  local ok_query, query = pcall(vim.treesitter.query.parse, lang, query_string)
+  if not ok_query or not query then return end
+
+  -- todo: limit to current window area?
+  -- iterate through matches and apply extmarks
+  for _, match, _ in query:iter_matches(root, bufnr, 0, -1) do
+    for _, nodes in pairs(match) do
+      for _, node in ipairs(nodes) do
+        local start_row, start_col, end_row, end_col = node:range()
+        vim.api.nvim_buf_set_extmark(bufnr, ns, start_row, start_col, {
+          end_row = end_row,
+          end_col = end_col,
+          hl_group = "Search",
+        })
+      end
+    end
+  end
+end
+
+-- create command
+vim.api.nvim_create_user_command("HighlightIdentifiers", highlight_matching_identifiers, {})
+
+-- create keymap
+vim.keymap.set("n", "<leader>hi", highlight_matching_identifiers, { desc = "highlight identifier occurrences" })
+
+-- delete extmarks after cursor movement
+vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI", "BufEnter" }, {
+  desc = "delete identifier highlights after cursor movement",
+  callback = function() vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) end
+})