--- /dev/null
+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
+})