-
-
Save aaronedev/ef03f3460052ebd885bc08d5cc6bf190 to your computer and use it in GitHub Desktop.
Inspect nvim highlight groups under mouse cursor in a floating window
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local M = {} | |
| -- Cache for module state | |
| local state = { | |
| buf = nil, | |
| win = nil, | |
| ns = nil, | |
| width = 34, | |
| uses_mousemoveevent = vim.o.mousemoveevent, | |
| } | |
| -- Initialize the module | |
| local function init() | |
| if not state.buf then | |
| state.buf = vim.api.nvim_create_buf(false, true) | |
| vim.bo[state.buf].filetype = "inspector" | |
| vim.bo[state.buf].bufhidden = "hide" | |
| end | |
| if not state.ns then | |
| state.ns = vim.api.nvim_create_namespace("inspect_word") | |
| end | |
| end | |
| -- Format the inspection info into displayable lines | |
| local function format_inspect_info(inspect_info) | |
| local lines = {} | |
| local highlights = {} | |
| -- Helper to add a line with optional highlighting | |
| local function add_line(text, hl_group) | |
| table.insert(lines, text) | |
| if hl_group then | |
| table.insert(highlights, { line = #lines - 1, hl_group = hl_group }) | |
| end | |
| end | |
| -- Treesitter highlights | |
| if inspect_info.treesitter and #inspect_info.treesitter > 0 then | |
| add_line("Treesitter", "@function.call.lua") | |
| for _, entry in ipairs(inspect_info.treesitter) do | |
| local text = string.format( | |
| " • %s → %s [%s]", | |
| entry.hl_group or "none", | |
| entry.hl_group_link or "none", | |
| entry.lang or "?" | |
| ) | |
| add_line(text) | |
| end | |
| add_line("") | |
| end | |
| -- Semantic tokens | |
| if inspect_info.semantic_tokens and #inspect_info.semantic_tokens > 0 then | |
| add_line("Semantic Tokens", "@function.lua") | |
| for _, entry in ipairs(inspect_info.semantic_tokens) do | |
| local text = string.format( | |
| " • %s → %s (p:%d)", | |
| entry.opts.hl_group or "none", | |
| entry.opts.hl_group_link or "none", | |
| entry.opts.priority or 0 | |
| ) | |
| add_line(text) | |
| end | |
| add_line("") | |
| end | |
| -- Extmarks | |
| if inspect_info.extmarks and #inspect_info.extmarks > 0 then | |
| add_line("Extmarks", "@function.lua") | |
| for _, entry in ipairs(inspect_info.extmarks) do | |
| local text = " • " .. (entry.opts.hl_group or "none") | |
| if entry.ns and #entry.ns > 0 then | |
| text = text .. " [" .. entry.ns .. "]" | |
| end | |
| add_line(text) | |
| end | |
| add_line("") | |
| end | |
| -- If no info found | |
| if #lines == 0 then | |
| add_line("No highlight information found") | |
| end | |
| return lines, highlights | |
| end | |
| -- Apply highlights to the buffer | |
| local function apply_highlights(highlights) | |
| vim.api.nvim_buf_clear_namespace(state.buf, state.ns, 0, -1) | |
| for _, hl in ipairs(highlights) do | |
| vim.api.nvim_buf_add_highlight(state.buf, state.ns, hl.hl_group, hl.line, 0, -1) | |
| end | |
| end | |
| -- Inspect at current position in a split | |
| function M.inspect_in_split() | |
| init() | |
| local pos = vim.api.nvim_win_get_cursor(0) | |
| local inspect_info = vim.inspect_pos(0, pos[1] - 1, pos[2]) | |
| local lines, highlights = format_inspect_info(inspect_info) | |
| -- Set buffer content | |
| vim.api.nvim_buf_set_lines(state.buf, 0, -1, false, lines) | |
| apply_highlights(highlights) | |
| -- Open in split | |
| vim.cmd("botright split") | |
| vim.cmd("resize " .. math.min(#lines + 2, 15)) | |
| vim.api.nvim_win_set_buf(0, state.buf) | |
| -- Set window options | |
| vim.wo.number = false | |
| vim.wo.relativenumber = false | |
| vim.wo.signcolumn = "no" | |
| vim.wo.foldcolumn = "0" | |
| vim.wo.statusline = " Inspector (q to close)" | |
| -- Add quit mapping for this buffer | |
| vim.api.nvim_buf_set_keymap(state.buf, "n", "q", "<cmd>close<cr>", { | |
| noremap = true, | |
| silent = true, | |
| nowait = true, | |
| }) | |
| end | |
| -- Toggle float inspector with mouse tracking | |
| function M.toggle_float_inspector() | |
| init() | |
| -- If window exists and is valid, close it | |
| if state.win and vim.api.nvim_win_is_valid(state.win) then | |
| vim.api.nvim_win_close(state.win, true) | |
| state.win = nil | |
| vim.o.mousemoveevent = state.uses_mousemoveevent | |
| return | |
| end | |
| -- Enable mouse move events | |
| vim.o.mousemoveevent = true | |
| -- Create floating window | |
| state.win = vim.api.nvim_open_win(state.buf, false, { | |
| relative = "cursor", | |
| width = 40, | |
| height = 1, | |
| row = 1, | |
| col = 1, | |
| style = "minimal", | |
| focusable = false, | |
| border = "rounded", | |
| zindex = 1000, | |
| }) | |
| -- Initial content | |
| vim.api.nvim_buf_set_lines(state.buf, 0, -1, false, { " Move mouse to inspect... " }) | |
| -- Update content on first call | |
| vim.schedule(function() | |
| M.update_float() | |
| end) | |
| end | |
| -- Update float window content based on mouse position | |
| function M.update_float() | |
| if not state.win or not vim.api.nvim_win_is_valid(state.win) then | |
| return "<MouseMove>" | |
| end | |
| vim.schedule(function() | |
| local pos = vim.fn.getmousepos() | |
| -- Skip if position is invalid | |
| if not pos.winid or pos.winid == 0 then | |
| return | |
| end | |
| -- Get buffer from mouse position window | |
| local buf = vim.api.nvim_win_get_buf(pos.winid) | |
| local inspect_info = vim.inspect_pos(buf, pos.line - 1, pos.column - 1) | |
| local lines, highlights = format_inspect_info(inspect_info) | |
| -- Update buffer content | |
| vim.api.nvim_buf_set_lines(state.buf, 0, -1, false, lines) | |
| apply_highlights(highlights) | |
| -- Calculate window size | |
| local width = 0 | |
| for _, line in ipairs(lines) do | |
| width = math.max(width, #line) | |
| end | |
| width = math.min(width + 2, 60) | |
| local height = math.min(#lines, 20) | |
| -- Update window configuration | |
| local screen_height = vim.o.lines | |
| local screen_width = vim.o.columns | |
| local row = pos.screenrow | |
| local col = pos.screencol | |
| -- Adjust position to keep window on screen | |
| if row + height + 2 > screen_height then | |
| row = math.max(1, row - height - 2) | |
| end | |
| if col + width > screen_width then | |
| col = math.max(1, screen_width - width) | |
| end | |
| vim.api.nvim_win_set_config(state.win, { | |
| relative = "editor", | |
| row = row, | |
| col = col, | |
| width = width, | |
| height = height, | |
| }) | |
| end) | |
| return "<MouseMove>" | |
| end | |
| -- Set up mouse move autocmd | |
| function M.setup_mouse_tracking() | |
| vim.keymap.set({ "", "i" }, "<MouseMove>", M.update_float, { expr = true, silent = true }) | |
| -- Clean up when window is closed | |
| vim.api.nvim_create_autocmd("WinClosed", { | |
| pattern = "*", | |
| callback = function(event) | |
| if state.win and tonumber(event.match) == state.win then | |
| state.win = nil | |
| vim.o.mousemoveevent = state.uses_mousemoveevent | |
| end | |
| end, | |
| }) | |
| end | |
| -- Inspect under cursor (simple version) | |
| function M.inspect_under_cursor() | |
| local pos = vim.api.nvim_win_get_cursor(0) | |
| local inspect_info = vim.inspect_pos(0, pos[1] - 1, pos[2]) | |
| -- Quick display in echo area | |
| local items = {} | |
| if inspect_info.treesitter and #inspect_info.treesitter > 0 then | |
| for _, entry in ipairs(inspect_info.treesitter) do | |
| table.insert(items, string.format("TS: %s", entry.hl_group or "none")) | |
| end | |
| end | |
| if inspect_info.semantic_tokens and #inspect_info.semantic_tokens > 0 then | |
| for _, entry in ipairs(inspect_info.semantic_tokens) do | |
| table.insert(items, string.format("LSP: %s", entry.opts.hl_group or "none")) | |
| end | |
| end | |
| if #items > 0 then | |
| vim.notify(table.concat(items, " | "), vim.log.levels.INFO) | |
| else | |
| vim.notify("No highlight groups found", vim.log.levels.WARN) | |
| end | |
| end | |
| return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment