feat: implement interactable dap hover

This commit is contained in:
2025-09-26 17:21:04 +02:00
parent 5256beea0f
commit cff07f8317
6 changed files with 801 additions and 510 deletions
+141
View File
@@ -0,0 +1,141 @@
-- 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
---Create a new tree node
---@param item ow.dap.Item
---@param parent ow.dap.hover.Node?
---@return ow.dap.hover.Node
function Node.new(item, parent)
return setmetatable({
item = item,
parent = parent,
children = {},
is_expanded = false,
line_number = -1,
is_last_child = false,
}, 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
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
prefix = " " .. prefix
else
prefix = "" .. prefix
end
node = node.parent
end
-- Add the final branch character
if self.is_last_child then
prefix = prefix .. "└─ "
else
prefix = prefix .. "├─ "
end
return prefix
end
---@return boolean
function Node:is_c_array_element()
return self.item.name:match("^%[?%d+%]?$") ~= nil
end
---@return boolean
function Node:is_c_pointer_child()
return self.parent
and self.parent.item.type:match(
"%*%s*[const%s]*[volatile%s]*[restrict%s]*$"
) ~= nil
or false
end
---@return string
function Node:format_c_expression()
if self:is_c_array_element() then
return string.format(
"%s = (%s) %s",
self.item.name,
self.item.type,
self.item.value
)
end
if self:is_c_pointer_child() then
return string.format("*%s = %s", self.parent.item.name, self.item.value)
end
return string.format(
"%s %s = %s",
self.item.type,
self.item.name,
self.item.value
)
end
---Format this node as highlighted 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
if self:is_container() then
local marker = self.is_expanded and "-" or "+"
content:add(marker .. " ", "@comment")
else
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
if session.filetype == "c" or session.filetype == "cpp" then
stmt = self:format_c_expression()
else
error(
string.format("Formatting for %s not implemented", session.filetype)
)
end
content:add_with_treesitter(stmt, session.filetype)
if self.item.value == "" then
content:add("...", "@comment")
end
return content
end
return Node