From acf6fffb2f7a57ac10d6d2d889db6810783108ee Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Fri, 26 Sep 2025 19:29:29 +0200 Subject: [PATCH] refactor(dap): simplify hover tree rendering architecture - Add row/column tracking to Content for multi-line highlight support - Switch from Node:format() returning Content to Node:format_into(content) - Remove complex highlight offset calculations and manual text merging - Use content:current_line() instead of tracking line_number in nodes - Fix pointer child formatting to use item.name directly - Clean up unused imports and simplify render_node method --- lua/ow/dap/hover.lua | 8 ++-- lua/ow/dap/hover/content.lua | 78 +++++++++++++++++------------------- lua/ow/dap/hover/node.lua | 31 ++++++-------- lua/ow/dap/hover/tree.lua | 31 +++----------- 4 files changed, 57 insertions(+), 91 deletions(-) diff --git a/lua/ow/dap/hover.lua b/lua/ow/dap/hover.lua index 8c0d773..0257543 100644 --- a/lua/ow/dap/hover.lua +++ b/lua/ow/dap/hover.lua @@ -38,11 +38,9 @@ function Window.compute_width(lines) end ---Create and display hover window with tree content ----@param session dap.Session ----@param item ow.dap.Item ---@param lines string[] ---@param content ow.dap.hover.Content -function Window.show(session, item, lines, content) +function Window.show(lines, content) -- Create buffer local orig_buf = vim.api.nvim_get_current_buf() local buf = vim.api.nvim_create_buf(false, true) @@ -108,7 +106,7 @@ function Window.expand_at_cursor(buf) local ok, err = xpcall(function() -- Toggle expansion local cursor = vim.api.nvim_win_get_cursor(Window.current_win) - local success = Window.tree:toggle_at_line(cursor[1] - 1) + local success = Window.tree:toggle_at_line(cursor[1]) if not success then return end @@ -205,7 +203,7 @@ local function hover_eval( Window.tree = tree -- Show hover window - Window.show(session, item, lines, content) + Window.show(lines, content) end ---Public hover function diff --git a/lua/ow/dap/hover/content.lua b/lua/ow/dap/hover/content.lua index 292ae4f..3121068 100644 --- a/lua/ow/dap/hover/content.lua +++ b/lua/ow/dap/hover/content.lua @@ -1,5 +1,4 @@ -- Highlighted text content builder for DAP tree display -local log = require("ow.log") ---@class ow.dap.hover.content.Capture ---@field start_col integer ---@field end_col integer @@ -9,12 +8,15 @@ local log = require("ow.log") ---@class ow.dap.hover.Highlight ---@field group string Highlight group name +---@field start_row integer Start line (0-indexed) ---@field start_col integer Start column (0-indexed) +---@field end_row integer End line (0-indexed) ---@field end_col integer End column (0-indexed) ---@class ow.dap.hover.Content ---@field text string The complete text content ---@field highlights ow.dap.hover.Highlight[] List of highlights to apply +---@field _current_row integer Current line position (for building) ---@field _current_col integer Current column position (for building) local Content = {} Content.__index = Content @@ -25,15 +27,23 @@ function Content.new() return setmetatable({ text = "", highlights = {}, + _current_row = 0, _current_col = 0, }, Content) end +---@return integer +function Content:current_line() + return self._current_row + 1 +end + ---Add text with optional highlighting ----@param text string Text to add +---@param text string Text to add. May not contain line breaks. ---@param highlight_group? string Optional highlight group function Content:add(text, highlight_group) + local start_row = self._current_row local start_col = self._current_col + local end_row = self._current_row local end_col = start_col + #text self.text = self.text .. text @@ -42,16 +52,19 @@ function Content:add(text, highlight_group) if highlight_group then table.insert(self.highlights, { group = highlight_group, + start_row = start_row, start_col = start_col, + end_row = end_row, end_col = end_col, }) end end ---Add text with tree-sitter syntax highlighting ----@param text string The text to highlight ----@param lang string Language for tree-sitter +---@param text string Text to add. May not contain line breaks. +---@param lang string Language for tree-sitter highlighting function Content:add_with_treesitter(text, lang) + local start_row = self._current_row local start_col = self._current_col -- First, just add the text normally @@ -76,22 +89,23 @@ function Content:add_with_treesitter(text, lang) -- Add highlights for all captures (overlapping is fine) for id, node in query:iter_captures(tree:root(), text, 0, -1) do local capture_name = query.captures[id] - local start_row, start_col_rel, end_row, end_col_rel = node:range() + local start_row_rel, start_col_rel, end_row_rel, end_col_rel = + node:range() - -- TODO: keep track of text as lines instead, so we can handle multiline - -- highlights - if start_row == end_row then -- Single line only - -- Convert to absolute column positions - local abs_start_col = start_col + start_col_rel - local abs_end_col = start_col + end_col_rel + -- Convert to absolute positions + local abs_start_row = start_row + start_row_rel + local abs_end_row = start_row + end_row_rel + local abs_start_col = start_col + start_col_rel + local abs_end_col = start_col + end_col_rel - -- Add the highlight - table.insert(self.highlights, { - group = "@" .. capture_name, - start_col = abs_start_col, - end_col = abs_end_col, - }) - end + -- Add the highlight + table.insert(self.highlights, { + group = "@" .. capture_name, + start_row = abs_start_row, + start_col = abs_start_col, + end_row = abs_end_row, + end_col = abs_end_col, + }) end end @@ -99,6 +113,7 @@ end function Content:newline() self:add("\n") self._current_col = 0 + self._current_row = self._current_row + 1 end ---Get the lines as a table @@ -110,35 +125,14 @@ end ---Apply highlights to a buffer ---@param ns_id integer ---@param buf integer Buffer handle ----@param line_offset? integer Line offset to apply highlights at (default 0) -function Content:apply_highlights(ns_id, buf, line_offset) - line_offset = line_offset or 0 - local lines = self:get_lines() - local current_line = 0 - local line_start_col = 0 - +function Content:apply_highlights(ns_id, buf) for _, highlight in ipairs(self.highlights) do - -- Find which line this highlight belongs to - while - current_line < #lines - 1 - and highlight.start_col - >= line_start_col + #lines[current_line + 1] + 1 - do - line_start_col = line_start_col + #lines[current_line + 1] + 1 -- +1 for newline - current_line = current_line + 1 - end - - -- Calculate column positions relative to line start - local start_col = highlight.start_col - line_start_col - local end_col = highlight.end_col - line_start_col - - -- Apply highlight vim.hl.range( buf, ns_id, highlight.group, - { line_offset + current_line, start_col }, - { line_offset + current_line, end_col } + { highlight.start_row, highlight.start_col }, + { highlight.end_row, highlight.end_col } ) end end diff --git a/lua/ow/dap/hover/node.lua b/lua/ow/dap/hover/node.lua index e3ca745..62610aa 100644 --- a/lua/ow/dap/hover/node.lua +++ b/lua/ow/dap/hover/node.lua @@ -1,13 +1,10 @@ -- Tree node representation for DAP variables -local Content = require("ow.dap.hover.content") -local log = require("ow.log") ---@class ow.dap.hover.Node ---@field item ow.dap.Item The DAP item this node represents ---@field parent ow.dap.hover.Node? Parent node ---@field children ow.dap.hover.Node[] Child nodes ---@field is_expanded boolean Whether this node is expanded ----@field line_number integer Buffer line number where this node is displayed ---@field is_last_child boolean Whether this is the last child of its parent local Node = {} Node.__index = Node @@ -17,14 +14,15 @@ Node.__index = Node ---@param parent ow.dap.hover.Node? ---@return ow.dap.hover.Node function Node.new(item, parent) - return setmetatable({ + local node = setmetatable({ item = item, parent = parent, children = {}, is_expanded = false, - line_number = -1, is_last_child = false, }, Node) + + return node end ---Check if this node represents a container (struct/array) @@ -79,7 +77,7 @@ function Node:is_c_pointer_child() end ---@return string -function Node:format_c_expression() +function Node:format_c() if self:is_c_array_element() then return string.format( "%s = (%s) %s", @@ -90,7 +88,7 @@ function Node:format_c_expression() end if self:is_c_pointer_child() then - return string.format("*%s = %s", self.parent.item.name, self.item.value) + return string.format("%s = %s", self.item.name, self.item.value) end return string.format( @@ -101,13 +99,10 @@ function Node:format_c_expression() ) end ----Format this node as highlighted content +---Format this node into the provided content ---@param session dap.Session DAP session for making requests ----@return ow.dap.hover.Content -function Node:format(session) - local content = Content.new() - - -- Add expansion marker for containers +---@param content ow.dap.hover.Content +function Node:format_into(session, content) if self:is_container() then local marker = self.is_expanded and "-" or "+" content:add(marker .. " ", "@comment") @@ -115,27 +110,25 @@ function Node:format(session) content:add(" ") end - -- Add tree prefix local tree_prefix = self:get_tree_prefix() if tree_prefix ~= "" then content:add(tree_prefix, "@comment") end - local stmt + local text if session.filetype == "c" or session.filetype == "cpp" then - stmt = self:format_c_expression() + text = self:format_c() else error( string.format("Formatting for %s not implemented", session.filetype) ) end - content:add_with_treesitter(stmt, session.filetype) + + content:add_with_treesitter(text, session.filetype) if self.item.value == "" then content:add("...", "@comment") end - - return content end return Node diff --git a/lua/ow/dap/hover/tree.lua b/lua/ow/dap/hover/tree.lua index 47f9f1c..a2bdeb7 100644 --- a/lua/ow/dap/hover/tree.lua +++ b/lua/ow/dap/hover/tree.lua @@ -1,7 +1,6 @@ -- Tree-based DAP variable formatter with expand/collapse support local Content = require("ow.dap.hover.content") local Node = require("ow.dap.hover.node") -local log = require("ow.log") ---@class ow.dap.hover.Tree ---@field session dap.Session @@ -88,7 +87,7 @@ function Tree:render() local content = Content.new() self.line_to_node = {} - self:render_node(self.root_node, content, 0) + self:render_subtree(self.root_node, content) return content end @@ -96,38 +95,20 @@ end ---@async ---@param node ow.dap.hover.Node ---@param content ow.dap.hover.Content ----@param line_number integer Current line number ----@return integer new_line_number Updated line number after rendering -function Tree:render_node(node, content, line_number) +function Tree:render_subtree(node, content) -- Store line mapping - node.line_number = line_number - self.line_to_node[line_number] = node + self.line_to_node[content:current_line()] = node -- Format this node - local node_content = node:format(self.session) - content.text = content.text .. node_content.text - - -- Copy highlights, adjusting for current position - local text_offset = #content.text - #node_content.text - for _, highlight in ipairs(node_content.highlights) do - table.insert(content.highlights, { - group = highlight.group, - start_col = highlight.start_col + text_offset, - end_col = highlight.end_col + text_offset, - }) - end - - content:newline() - line_number = line_number + 1 + node:format_into(self.session, content) -- Render expanded children if node.is_expanded then for _, child in ipairs(node.children) do - line_number = self:render_node(child, content, line_number) + content:newline() + self:render_subtree(child, content) end end - - return line_number end ---Toggle expansion state of node at given line