From 2316ef9553588debbe8f7c3a2a545fef39d13493 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lukas=20H=C3=A4gele?= Date: Sun, 11 Oct 2026 13:33:58 +0200 Subject: [PATCH] add block hierarchy plugin --- plugin/block-hierarchy.lua | 96 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 plugin/block-hierarchy.lua diff --git a/plugin/block-hierarchy.lua b/plugin/block-hierarchy.lua new file mode 100644 index 0000000..b6d57ca --- /dev/null +++ b/plugin/block-hierarchy.lua @@ -0,0 +1,96 @@ +-- namespace +local ns_id = vim.api.nvim_create_namespace("block_hierarchy") + +--- helper: get current blocks from treesitter +--- @return { bufnr: integer, blocks: TSNode[] +local function get_current_blocks() + local bufnr = vim.api.nvim_get_current_buf() + + local ok, node = pcall(vim.treesitter.get_node) + if not ok or not node then + return nil, nil + end + + local blocks = {} + local current = node + while current do + if current:type() == "compound_statement" then + table.insert(blocks, current) + end + current = current:parent() + end + + return bufnr, blocks +end + +--- update extmarks for current blocks +local function update_extmarks() + local bufnr, blocks = get_current_blocks() + if not bufnr or not blocks then return end + + -- delete old extmarks + vim.api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1) + + for _, block in ipairs(blocks) do + local start_row, _, end_row, _ = block:range() + local header_row = math.max(0, start_row - 1) + + local lines = vim.api.nvim_buf_get_lines(bufnr, header_row, header_row + 1, false) + local header_text = lines[1] and vim.trim(lines[1]) or "" + + if header_text ~= "" then + pcall(vim.api.nvim_buf_set_extmark, bufnr, ns_id, end_row, 0, { + virt_text = { { header_text, "Comment" } }, + virt_text_pos = "eol", + hl_mode = "combine", + }) + end + end +end + +--- show hierarchy in quickfix list +local function show_block_hierarchy() + local bufnr, blocks = get_current_blocks() + if not bufnr or not blocks or #blocks == 0 then + vim.notify("no blocks at current cursor position", vim.log.levels.INFO) + return + end + + local qf_items = {} + + -- revert hierarchy + for i = #blocks, 1, -1 do + local block = blocks[i] + local start_row, _, _, _ = block:range() + local header_row = math.max(0, start_row - 1) + + local lines = vim.api.nvim_buf_get_lines(bufnr, header_row, header_row + 1, false) + local header_text = lines[1] and vim.trim(lines[1]) or "[unknown block]" + + table.insert(qf_items, { + bufnr = bufnr, + lnum = header_row + 1, -- quickfix expects 1-indexed lines + col = lines[1]:find("%S") or 1, -- find position of first non-whitespace character + text = header_text, + }) + end + + -- fill list and display + vim.fn.setqflist({}, "r", { + title = "block hierarchy", + items = qf_items, + }) + vim.cmd("copen") +end + +-- register user command: :BlockHierarchy +vim.api.nvim_create_user_command("BlockHierarchy", show_block_hierarchy, {}) + +-- register keymap +vim.keymap.set("n", "", ":BlockHierarchy", { silent = true, desc = "show block hierarchy" }) + +-- update extmarks after cursor movement +vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI", "BufEnter" }, { + pattern = {"*.c, *.h"}, + callback = update_extmarks, +}) -- 2.39.5