diff --git a/lua/ow/dap/hover.lua b/lua/ow/dap/hover.lua index 180f6fd..e0aa2a5 100644 --- a/lua/ow/dap/hover.lua +++ b/lua/ow/dap/hover.lua @@ -3,14 +3,13 @@ local Tree = require("ow.dap.hover.tree") local Window = require("ow.dap.hover.window") local log = require("ow.log") ----Main hover entry point ---@async ----@param expr string Expression to evaluate ----@param session dap.Session DAP session ----@param frame_id number Current frame ID ----@param line_nr integer Line number for context ----@param col_nr integer Column number for context ----@param current_file string Current file path +---@param expr string +---@param session dap.Session +---@param frame_id number +---@param line_nr integer +---@param col_nr integer +---@param current_file string local function hover_eval( expr, session, @@ -20,10 +19,8 @@ local function hover_eval( current_file ) local win = Window.get_instance() - -- Close existing hover window win:close() - -- Evaluate expression local eval_request = { expression = expr, frameId = frame_id, @@ -41,27 +38,21 @@ local function hover_eval( return end - -- Create item and tree formatter local item = Item.new(expr, resp.type, resp.result, resp.variablesReference, 0) local tree = Tree.new(session) - -- Build and render tree tree:build(item) local content = tree:render() local lines = content:get_lines() - -- Store formatter for expansion win.tree = tree - -- Show hover window win:show(lines, content) end ----Public hover function ---@async local function hover_async() - -- Check if hover window is already open - focus it instead local win = Window.get_instance() if win.winid and vim.api.nvim_win_is_valid(win.winid) then vim.api.nvim_set_current_win(win.winid) @@ -86,11 +77,9 @@ local function hover_async() local col_nr = cursor_pos[2] + 1 -- nvim-dap sets columnsStartAt1=true local current_file = vim.api.nvim_buf_get_name(0) - -- Get expression under cursor local expr local mode = vim.api.nvim_get_mode() if mode.mode == "v" then - -- Visual mode selection local start_pos = vim.fn.getpos("v") local end_pos = vim.fn.getpos(".") @@ -127,7 +116,6 @@ local function hover_async() return end - -- Get thread and frame information local thread_id do local err, resp = session:request("threads", nil) @@ -149,11 +137,9 @@ local function hover_async() frame_id = resp.stackFrames[1].id end - -- Evaluate and display hover hover_eval(expr, session, frame_id, line_nr, col_nr, current_file) end ----Wrapped hover function with error handling local function hover() coroutine.wrap(function() local ok, err = xpcall(hover_async, debug.traceback) diff --git a/lua/ow/dap/hover/content.lua b/lua/ow/dap/hover/content.lua index 20358ca..0a5483c 100644 --- a/lua/ow/dap/hover/content.lua +++ b/lua/ow/dap/hover/content.lua @@ -1,4 +1,3 @@ --- Highlighted text content builder for DAP tree display ---@class ow.dap.hover.content.Capture ---@field start_col integer ---@field end_col integer @@ -7,21 +6,20 @@ ---@field priority integer ---@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) +---@field group string +---@field start_row integer 0-indexed +---@field start_col integer 0-indexed +---@field end_row integer 0-indexed +---@field end_col integer 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) +---@field text string +---@field highlights ow.dap.hover.Highlight[] +---@field _current_row integer +---@field _current_col integer local Content = {} Content.__index = Content ----Create new highlighted content ---@return ow.dap.hover.Content function Content.new() return setmetatable({ @@ -37,9 +35,8 @@ function Content:current_line() return self._current_row + 1 end ----Add text with optional highlighting ----@param text string Text to add. May not contain line breaks. ----@param highlight_group? string Optional highlight group +---@param text string May not contain line breaks +---@param highlight_group? string function Content:add(text, highlight_group) local start_row = self._current_row local start_col = self._current_col @@ -60,17 +57,14 @@ function Content:add(text, highlight_group) end end ----Add text with tree-sitter syntax highlighting ----@param text string Text to add. May not contain line breaks. ----@param lang string Language for tree-sitter highlighting +---@param text string May not contain line breaks +---@param lang string 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 self:add(text) - -- Then apply tree-sitter highlights on top local ok, parser = pcall(vim.treesitter.get_string_parser, text, lang) if not ok or not parser then return @@ -86,19 +80,16 @@ function Content:add_with_treesitter(text, lang) return end - -- 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_rel, start_col_rel, end_row_rel, end_col_rel = node:range() - -- 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_row = abs_start_row, @@ -109,23 +100,20 @@ function Content:add_with_treesitter(text, lang) end end ----Add a newline and reset column tracking function Content:newline() self:add("\n") self._current_col = 0 self._current_row = self._current_row + 1 end ----Get the lines as a table ---@return string[] function Content:get_lines() return vim.split(self.text, "\n", { trimempty = true }) end ----Apply highlights to a buffer ---@param ns_id integer ----@param buf integer Buffer handle ----@param row_offset integer +---@param buf integer +---@param row_offset integer 0-indexed function Content:apply_highlights(ns_id, buf, row_offset) for _, highlight in ipairs(self.highlights) do vim.hl.range( diff --git a/lua/ow/dap/hover/node.lua b/lua/ow/dap/hover/node.lua index 62610aa..e8e8061 100644 --- a/lua/ow/dap/hover/node.lua +++ b/lua/ow/dap/hover/node.lua @@ -1,15 +1,12 @@ --- Tree node representation for DAP variables - ---@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 is_last_child boolean Whether this is the last child of its parent +---@field item ow.dap.Item +---@field parent ow.dap.hover.Node? +---@field children ow.dap.hover.Node[] +---@field is_expanded boolean +---@field is_last_child boolean local Node = {} Node.__index = Node ----Create a new tree node ---@param item ow.dap.Item ---@param parent ow.dap.hover.Node? ---@return ow.dap.hover.Node @@ -25,23 +22,20 @@ function Node.new(item, parent) return node end ----Check if this node represents a container (struct/array) ---@return boolean function Node:is_container() return self.item.variablesReference and self.item.variablesReference > 0 or false end ----Get the tree prefix for this node (├─, └─, │, etc.) ---@return string function Node:get_tree_prefix() if not self.parent then - return "" -- Root node has no prefix + return "" end local prefix = "" - -- Walk up the tree to build the prefix local node = self.parent while node and node.parent do if node.is_last_child then @@ -52,7 +46,6 @@ function Node:get_tree_prefix() node = node.parent end - -- Add the final branch character if self.is_last_child then prefix = prefix .. "└─ " else @@ -99,8 +92,7 @@ function Node:format_c() ) end ----Format this node into the provided content ----@param session dap.Session DAP session for making requests +---@param session dap.Session ---@param content ow.dap.hover.Content function Node:format_into(session, content) if self:is_container() then diff --git a/lua/ow/dap/hover/tree.lua b/lua/ow/dap/hover/tree.lua index 124218b..207bfb4 100644 --- a/lua/ow/dap/hover/tree.lua +++ b/lua/ow/dap/hover/tree.lua @@ -1,4 +1,3 @@ --- Tree-based DAP variable formatter with expand/collapse support local Content = require("ow.dap.hover.content") local Node = require("ow.dap.hover.node") @@ -8,7 +7,6 @@ local Node = require("ow.dap.hover.node") local Tree = {} Tree.__index = Tree ----Create a new tree formatter ---@param session dap.Session ---@return ow.dap.hover.Tree function Tree.new(session) @@ -20,9 +18,8 @@ function Tree.new(session) }, Tree) end ----Build the tree from a DAP item ---@async ----@param item ow.dap.Item Root item to build tree from +---@param item ow.dap.Item function Tree:build(item) self.root = Node.new(item, nil) @@ -32,10 +29,9 @@ function Tree:build(item) end end ----Load children for a node ---@async ---@param node ow.dap.hover.Node ----@return boolean success Whether loading succeeded +---@return boolean success function Tree:load_children(node) if not node:is_container() or #node.children > 0 then return true -- Already loaded or not a container @@ -49,7 +45,6 @@ function Tree:load_children(node) return false end - -- Create child nodes for i, var in ipairs(resp.variables) do local child_item = { name = var.name, @@ -62,7 +57,6 @@ function Tree:load_children(node) local child = Node.new(child_item, node) child.is_last_child = (i == #resp.variables) - -- Format array indices properly if child_item.name:match("^%d+$") then child_item.name = "[" .. child_item.name .. "]" end @@ -73,7 +67,6 @@ function Tree:load_children(node) return true end ----Render the tree to highlighted content ---@async ---@return ow.dap.hover.Content function Tree:render() @@ -87,15 +80,12 @@ function Tree:render() return content end ----Render a single node and its expanded children ---@async ---@param node ow.dap.hover.Node ---@param content ow.dap.hover.Content function Tree:render_subtree(node, content) - -- Format this node node:format_into(self.session, content) - -- Render expanded children if node.is_expanded then for _, child in ipairs(node.children) do content:newline() @@ -145,10 +135,9 @@ function Tree:get_node_at_line(target_line) end end ----Toggle expansion state of node at given line ---@async ---@param node ow.dap.hover.Node ----@return boolean success Whether toggle succeeded +---@return boolean success function Tree:toggle_node(node) if not node.is_expanded then local success = self:load_children(node) @@ -157,7 +146,6 @@ function Tree:toggle_node(node) end end - -- Toggle state node.is_expanded = not node.is_expanded return true end