lots of reworking. Seperate out signature help from documentation.
parent
867ac5b1df
commit
c291a7df66
|
|
@ -0,0 +1,310 @@
|
|||
local api = vim.api
|
||||
|
||||
local function get_parameter_label(result)
|
||||
local signatures = result.signatures
|
||||
|
||||
local activeSignature = result.activeSignature or 0
|
||||
activeSignature = activeSignature + 1
|
||||
local signature = signatures[activeSignature]
|
||||
|
||||
if signature == nil or signature.parameters == nil then -- no parameter
|
||||
return ""
|
||||
end
|
||||
|
||||
local activeParameter = signature.activeParameter or result.activeParameter
|
||||
|
||||
if activeParameter == nil or activeParameter < 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
if signature.parameters == nil then
|
||||
return ""
|
||||
end
|
||||
|
||||
if activeParameter > #signature.parameters then
|
||||
activeParameter = 0
|
||||
end
|
||||
|
||||
-- local nextParameter = signature.parameters[activeParameter + 1]
|
||||
local param = signature.parameters[activeParameter + 1].label
|
||||
local param_str
|
||||
-- Handle both string and table parameter labels
|
||||
if type(param) == "table" then
|
||||
local s = param[1]
|
||||
local e = param[2]
|
||||
param_str = string.sub(signature.label, s, e)
|
||||
elseif type(param) == "string" then
|
||||
param_str = label
|
||||
end
|
||||
|
||||
return param_str
|
||||
end
|
||||
|
||||
---@class opts table
|
||||
---@field brk_chars string string of characters to break lines at
|
||||
---@field indent_str string string to indent wrapped lines with
|
||||
---@param str string string to wrap into lines
|
||||
---@param maxlen integer maximum length of lines
|
||||
---@return table table of wrapped lines
|
||||
local function _wrap_lines(str, maxlen, opts)
|
||||
local atoms = {}
|
||||
opts = opts or {}
|
||||
local brk_chars = opts.brk_chars or ",()"
|
||||
local indent_str = opts.indent_str or " "
|
||||
local end_char = string.sub(brk_chars, 1, 1)
|
||||
|
||||
str = str .. end_char -- add break char at end of string, so we can match the end of the line
|
||||
for w in string.gmatch(str, "[^" .. brk_chars .. "]*" .. "[" .. brk_chars .. "]") do
|
||||
table.insert(atoms, w)
|
||||
end
|
||||
atoms[#atoms] = string.gsub(atoms[#atoms], end_char .. "$", "") -- remove break char at end
|
||||
|
||||
local lines = {}
|
||||
lines[1] = ""
|
||||
local line_num = 1
|
||||
|
||||
local padding = opts.padding or 2
|
||||
local is = ""
|
||||
for i, w in ipairs(atoms) do
|
||||
if line_num == 1 then
|
||||
is = ""
|
||||
else
|
||||
is = indent_str
|
||||
end
|
||||
if string.len(lines[line_num]) + string.len(w) + (padding * 2) <= maxlen then
|
||||
lines[line_num] = lines[line_num] .. w
|
||||
else
|
||||
lines[line_num] = string.rep(" ", padding) .. is .. lines[line_num] .. string.rep(" ", padding) --add padding
|
||||
line_num = line_num + 1
|
||||
lines[line_num] = string.gsub(w, "^%s", "")
|
||||
end
|
||||
if i == #atoms then
|
||||
lines[line_num] = string.rep(" ", padding) .. is .. lines[line_num] .. string.rep(" ", padding) --add padding to last line
|
||||
end
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
---@param signature_help table
|
||||
---@param maxlen integer
|
||||
local function convert_signature_help_to_lines(signature_help, maxlen)
|
||||
if not signature_help.signatures then
|
||||
return
|
||||
end
|
||||
local lines = {}
|
||||
local active_signature = signature_help.activeSignature or 0
|
||||
if active_signature >= #signature_help.signatures or active_signature < 0 then
|
||||
active_signature = 0
|
||||
end
|
||||
local signature = signature_help.signatures[active_signature + 1]
|
||||
if not signature then
|
||||
return
|
||||
end
|
||||
|
||||
vim.list_extend(lines, vim.split(signature.label, "\n", { plain = true, trimempty = true }))
|
||||
|
||||
local wrapped_lines = {}
|
||||
for _, ll in ipairs(lines) do
|
||||
vim.list_extend(wrapped_lines, _wrap_lines(ll, maxlen))
|
||||
end
|
||||
return wrapped_lines
|
||||
end
|
||||
|
||||
local function apply_treesitter_highlighting(buf, syntax)
|
||||
if not api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
if not pcall(require, "nvim-treesitter") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Apply treesitter highlighting
|
||||
pcall(function()
|
||||
require("nvim-treesitter.highlight").attach(buf, syntax)
|
||||
end)
|
||||
|
||||
-- Restore focus
|
||||
api.nvim_set_current_win(current_win)
|
||||
api.nvim_set_current_buf(current_buf)
|
||||
end
|
||||
|
||||
local _make_floating_popup_size = function(contents, opts)
|
||||
opts = opts or {}
|
||||
local max_width = opts.max_width or 80
|
||||
local max_height = opts.max_height or 10
|
||||
local line_widths = {}
|
||||
|
||||
local width = 0
|
||||
for i, line in ipairs(contents) do
|
||||
line_widths[i] = vim.fn.strdisplaywidth(line:gsub("%z", "\n"))
|
||||
width = math.max(line_widths[i], width)
|
||||
end
|
||||
|
||||
-- local border_width = get_border_size(opts).width
|
||||
local border_width = 2 --TODO: do we need to deal with this parametrically?
|
||||
local screen_width = api.nvim_win_get_width(0)
|
||||
width = math.min(width, screen_width)
|
||||
width = math.min(width, max_width)
|
||||
|
||||
-- make sure borders are always inside the screen
|
||||
if width + border_width > screen_width then
|
||||
width = screen_width - border_width
|
||||
end
|
||||
|
||||
local height = #contents
|
||||
height = 0
|
||||
if vim.tbl_isempty(line_widths) then
|
||||
for _, line in ipairs(contents) do
|
||||
local line_width = vim.fn.strdisplaywidth(line:gsub("%z", "\n"))
|
||||
height = height + math.ceil(line_width / max_width)
|
||||
end
|
||||
else
|
||||
for i = 1, #contents do
|
||||
height = height + math.max(1, math.ceil(line_widths[i] / max_width))
|
||||
end
|
||||
end
|
||||
if max_height then
|
||||
height = math.min(height, max_height)
|
||||
end
|
||||
|
||||
return width, height
|
||||
end
|
||||
|
||||
---@param content table window content
|
||||
---@param opts table window options
|
||||
---@returns (table) Options
|
||||
local function _make_floating_popup_options(content, opts)
|
||||
local width, height = _make_floating_popup_size(content, opts)
|
||||
|
||||
local offset_y = opts.offset_y or 0
|
||||
local offset_x = opts.offset_x or 0
|
||||
|
||||
local anchor_bias = opts.anchor_bias or "auto"
|
||||
local relative = opts.relative or "cursor"
|
||||
local anchor = ""
|
||||
local row, col
|
||||
|
||||
local lines_above = opts.relative == "mouse" and vim.fn.getmousepos().line - 1 or vim.fn.winline() - 1
|
||||
local lines_below = vim.fn.winheight(0) - lines_above
|
||||
|
||||
if
|
||||
lines_above < height
|
||||
or (anchor_bias == "below" and lines_below > height + offset_y)
|
||||
or (anchor_bias == "auto" and lines_below > lines_above)
|
||||
then
|
||||
anchor = anchor .. "N"
|
||||
height = math.min(lines_below - offset_y, height)
|
||||
row = 1 + offset_y
|
||||
else
|
||||
anchor = anchor .. "S"
|
||||
height = math.min(lines_above - offset_y, height)
|
||||
row = 0 - offset_y
|
||||
end
|
||||
|
||||
local wincol = opts.relative == "mouse" and vim.fn.getmousepos().column or vim.fn.wincol()
|
||||
|
||||
if wincol + width + offset_x <= vim.o.columns then
|
||||
anchor = anchor .. "W"
|
||||
col = 0 - wincol
|
||||
else
|
||||
anchor = anchor .. "E"
|
||||
col = 1 - wincol
|
||||
end
|
||||
|
||||
offset_x = opts.offset_x or 0
|
||||
|
||||
return {
|
||||
anchor = anchor,
|
||||
row = row,
|
||||
col = col + offset_x,
|
||||
width = width,
|
||||
height = height,
|
||||
focusable = opts.focusable,
|
||||
relative = relative,
|
||||
style = "minimal",
|
||||
border = opts.border or "rounded",
|
||||
zindex = opts.zindex or 50,
|
||||
}
|
||||
end
|
||||
|
||||
local function create_float_window(content, syntax, opts)
|
||||
local npcall = vim.F.npcall
|
||||
vim.validate({
|
||||
contents = { content, "t" },
|
||||
syntax = { syntax, "s", true },
|
||||
opts = { opts, "t", true },
|
||||
})
|
||||
opts = opts or {}
|
||||
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local existing_float = npcall(api.nvim_buf_get_var, bufnr, "lsp_floating_preview")
|
||||
|
||||
if existing_float and api.nvim_win_is_valid(existing_float) then
|
||||
api.nvim_win_close(existing_float, true)
|
||||
end
|
||||
|
||||
-- Create the buffer
|
||||
local floating_bufnr = api.nvim_create_buf(false, true)
|
||||
|
||||
if syntax then
|
||||
vim.bo[floating_bufnr].syntax = syntax
|
||||
vim.bo[floating_bufnr].ft = syntax
|
||||
vim.treesitter.start(floating_bufnr)
|
||||
end
|
||||
api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, content)
|
||||
|
||||
local float_options = _make_floating_popup_options(content, opts)
|
||||
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_options)
|
||||
|
||||
vim.wo[floating_winnr].conceallevel = 2
|
||||
vim.wo[floating_winnr].foldenable = false
|
||||
vim.bo[floating_bufnr].modifiable = false
|
||||
vim.bo[floating_bufnr].bufhidden = "wipe"
|
||||
|
||||
api.nvim_buf_set_keymap(
|
||||
floating_bufnr,
|
||||
"n",
|
||||
"q",
|
||||
"<cmd>bdelete<cr>",
|
||||
{ silent = true, noremap = true, nowait = true }
|
||||
)
|
||||
|
||||
-- save focus_id
|
||||
api.nvim_buf_set_var(bufnr, "lsp_floating_preview", floating_winnr)
|
||||
|
||||
return floating_bufnr, floating_winnr
|
||||
end
|
||||
|
||||
local function close_float_window(win, buf)
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
if win and api.nvim_win_is_valid(win) then
|
||||
pcall(api.nvim_win_close, win, true)
|
||||
end
|
||||
if buf and api.nvim_buf_is_valid(buf) then
|
||||
pcall(api.nvim_buf_delete, buf, { force = true })
|
||||
end
|
||||
win = nil
|
||||
buf = nil
|
||||
|
||||
-- Restore focus
|
||||
pcall(api.nvim_set_current_win, current_win)
|
||||
pcall(api.nvim_set_current_buf, current_buf)
|
||||
-- end
|
||||
end
|
||||
|
||||
return {
|
||||
get_parameter_label = get_parameter_label,
|
||||
convert_signature_help_to_lines = convert_signature_help_to_lines,
|
||||
apply_treesitter_highlighting = apply_treesitter_highlighting,
|
||||
create_float_window = create_float_window,
|
||||
close_float_window = close_float_window,
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
local api = vim.api
|
||||
local helper = require("signup.helper")
|
||||
|
||||
local M = {}
|
||||
|
||||
|
|
@ -7,11 +8,13 @@ SignatureHelp.__index = SignatureHelp
|
|||
|
||||
function SignatureHelp.new()
|
||||
local instance = setmetatable({
|
||||
win = nil,
|
||||
buf = nil,
|
||||
dock_win = nil,
|
||||
dock_buf = nil,
|
||||
dock_win_id = "signature_help_dock_" .. vim.api.nvim_get_current_buf(),
|
||||
sig_win = nil,
|
||||
sig_buf = nil,
|
||||
doc_win = nil,
|
||||
doc_buf = nil,
|
||||
showing = false,
|
||||
ns = nil,
|
||||
markid = nil,
|
||||
timer = nil,
|
||||
visible = false,
|
||||
current_signatures = nil,
|
||||
|
|
@ -39,13 +42,22 @@ function SignatureHelp.new()
|
|||
bg = "#86e1fc",
|
||||
fg = "#1a1a1a",
|
||||
},
|
||||
border = "solid",
|
||||
hi_parameter = "LspSignatureActiveParameter",
|
||||
win_opts = {
|
||||
border = "rounded",
|
||||
winblend = 10,
|
||||
auto_close = true,
|
||||
trigger_chars = { "(", "," },
|
||||
zindex = 200,
|
||||
focusable = false,
|
||||
max_height = 10,
|
||||
max_width = 40,
|
||||
floating_window_above_cur_line = true,
|
||||
max_width = 80,
|
||||
anchor_bias = "below", -- below|above|auto
|
||||
relative = "cursor",
|
||||
offset_y = 5,
|
||||
offset_x = 5,
|
||||
},
|
||||
|
||||
trigger_chars = { "(", "," },
|
||||
auto_close = true,
|
||||
preview_parameters = true,
|
||||
debounce_time = 100,
|
||||
dock_toggle_key = "<Leader>sd",
|
||||
|
|
@ -66,167 +78,122 @@ function SignatureHelp.new()
|
|||
return instance
|
||||
end
|
||||
|
||||
local function signature_index_comment(index)
|
||||
if #vim.bo.commentstring ~= 0 then
|
||||
return vim.bo.commentstring:format(index)
|
||||
else
|
||||
return "(" .. index .. ")"
|
||||
end
|
||||
end
|
||||
-- local function signature_index_comment(index)
|
||||
-- if #vim.bo.commentstring ~= 0 then
|
||||
-- return vim.bo.commentstring:format(index)
|
||||
-- else
|
||||
-- return "(" .. index .. ")"
|
||||
-- end
|
||||
-- end
|
||||
|
||||
local function markdown_for_signature_list(signatures, config)
|
||||
local lines, labels = {}, {}
|
||||
local number = config.number and #signatures > 1
|
||||
local max_method_len = 0
|
||||
|
||||
-- First pass to calculate alignment
|
||||
if config.render_style.align_icons then
|
||||
for _, signature in ipairs(signatures) do
|
||||
max_method_len = math.max(max_method_len, #signature.label)
|
||||
end
|
||||
end
|
||||
|
||||
for index, signature in ipairs(signatures) do
|
||||
if not config.render_style.compact then
|
||||
table.insert(lines, "")
|
||||
end
|
||||
table.insert(labels, #lines + 1)
|
||||
|
||||
local suffix = number and (" " .. signature_index_comment(index)) or ""
|
||||
local padding = config.render_style.align_icons and string.rep(" ", max_method_len - #signature.label) or " "
|
||||
|
||||
-- Method signature with syntax highlighting
|
||||
table.insert(lines, string.format("```%s", vim.bo.filetype))
|
||||
-- table.insert(lines, string.format("%s Method:", config.icons.method))
|
||||
table.insert(lines, string.format("%s %s%s%s", config.icons.method, signature.label, padding, suffix))
|
||||
table.insert(lines, "```")
|
||||
|
||||
-- -- Parameters section
|
||||
-- if signature.parameters and #signature.parameters > 0 then
|
||||
-- local function markdown_for_signature_list(signatures, config)
|
||||
-- local lines, labels = {}, {}
|
||||
-- local number = config.number and #signatures > 1
|
||||
-- local max_method_len = 0
|
||||
--
|
||||
-- -- First pass to calculate alignment
|
||||
-- if config.render_style.align_icons then
|
||||
-- for _, signature in ipairs(signatures) do
|
||||
-- max_method_len = math.max(max_method_len, #signature.label)
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- for index, signature in ipairs(signatures) do
|
||||
-- if not config.render_style.compact then
|
||||
-- table.insert(lines, "")
|
||||
-- end
|
||||
-- table.insert(labels, #lines + 1)
|
||||
--
|
||||
-- local suffix = number and (" " .. signature_index_comment(index)) or ""
|
||||
-- local padding = config.render_style.align_icons and string.rep(" ", max_method_len - #signature.label) or " "
|
||||
--
|
||||
-- -- Method signature with syntax highlighting
|
||||
-- table.insert(lines, string.format("```%s", vim.bo.filetype))
|
||||
-- -- table.insert(lines, string.format("%s Method:", config.icons.method))
|
||||
-- table.insert(lines, string.format("%s %s%s%s", config.icons.method, signature.label, padding, suffix))
|
||||
-- table.insert(lines, "```")
|
||||
--
|
||||
-- -- -- Parameters section
|
||||
-- -- if signature.parameters and #signature.parameters > 0 then
|
||||
-- -- if config.render_style.separator then
|
||||
-- -- table.insert(lines, string.rep("─", 40))
|
||||
-- -- end
|
||||
-- -- table.insert(lines, string.format("%s Parameters:", config.icons.parameter))
|
||||
-- -- for _, param in ipairs(signature.parameters) do
|
||||
-- -- local param_doc = param.documentation and string.format(" - %s", vim.inspect(param.documentation))
|
||||
-- -- table.insert(lines, string.format(" • %s = %s", vim.inspect(param.label), param_doc))
|
||||
-- -- end
|
||||
-- -- end
|
||||
--
|
||||
-- -- Documentation section
|
||||
-- if signature.documentation then
|
||||
-- if config.render_style.separator then
|
||||
-- table.insert(lines, string.rep("─", 40))
|
||||
-- end
|
||||
-- table.insert(lines, string.format("%s Parameters:", config.icons.parameter))
|
||||
-- for _, param in ipairs(signature.parameters) do
|
||||
-- local param_doc = param.documentation and string.format(" - %s", vim.inspect(param.documentation))
|
||||
-- table.insert(lines, string.format(" • %s = %s", vim.inspect(param.label), param_doc))
|
||||
-- table.insert(lines, string.format("%s Documentation:", config.icons.documentation))
|
||||
-- local doc_lines = vim.split(signature.documentation.value or signature.documentation, "\n")
|
||||
-- for _, line in ipairs(doc_lines) do
|
||||
-- table.insert(lines, " " .. line)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Documentation section
|
||||
if signature.documentation then
|
||||
if config.render_style.separator then
|
||||
table.insert(lines, string.rep("─", 40))
|
||||
end
|
||||
table.insert(lines, string.format("%s Documentation:", config.icons.documentation))
|
||||
local doc_lines = vim.split(signature.documentation.value or signature.documentation, "\n")
|
||||
for _, line in ipairs(doc_lines) do
|
||||
table.insert(lines, " " .. line)
|
||||
end
|
||||
end
|
||||
|
||||
if index ~= #signatures and config.render_style.separator then
|
||||
table.insert(lines, string.rep("═", 40))
|
||||
end
|
||||
end
|
||||
return lines, labels
|
||||
end
|
||||
|
||||
function SignatureHelp:create_float_window(contents)
|
||||
local max_width = math.min(self.config.max_width, vim.o.columns)
|
||||
local max_height = math.min(self.config.max_height, #contents)
|
||||
|
||||
-- Calculate optimal position
|
||||
local cursor = api.nvim_win_get_cursor(0)
|
||||
local cursor_line = cursor[1]
|
||||
local screen_line = vim.fn.screenpos(0, cursor_line, 1).row
|
||||
|
||||
local row_offset = self.config.floating_window_above_cur_line and -max_height - 1 or 1
|
||||
if screen_line + row_offset < 1 then
|
||||
row_offset = 2 -- Show below if not enough space above
|
||||
end
|
||||
|
||||
local win_config = {
|
||||
relative = "cursor",
|
||||
row = row_offset - 1,
|
||||
col = 0,
|
||||
width = max_width,
|
||||
height = max_height,
|
||||
style = "minimal",
|
||||
border = self.config.border,
|
||||
zindex = 50, -- Ensure it's above most other floating windows
|
||||
}
|
||||
|
||||
if self.win and api.nvim_win_is_valid(self.win) then
|
||||
api.nvim_win_set_config(self.win, win_config)
|
||||
api.nvim_win_set_buf(self.win, self.buf)
|
||||
else
|
||||
self.buf = api.nvim_create_buf(false, true)
|
||||
self.win = api.nvim_open_win(self.buf, false, win_config)
|
||||
end
|
||||
|
||||
vim.bo[self.buf].modifiable = true
|
||||
api.nvim_buf_set_lines(self.buf, 0, -1, false, contents)
|
||||
vim.bo[self.buf].modifiable = false
|
||||
vim.wo[self.win][self.buf].foldenable = false
|
||||
vim.wo[self.win][self.buf].wrap = true
|
||||
vim.wo[self.win][self.buf].winblend = self.config.winblend
|
||||
self.visible = true
|
||||
end
|
||||
|
||||
function SignatureHelp:hide()
|
||||
if self.visible then
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Close appropriate window based on mode
|
||||
if self.config.dock_mode.enabled then
|
||||
self:close_dock_window()
|
||||
else
|
||||
if self.win and api.nvim_win_is_valid(self.win) then
|
||||
pcall(api.nvim_win_close, self.win, true)
|
||||
end
|
||||
if self.buf and api.nvim_buf_is_valid(self.buf) then
|
||||
pcall(api.nvim_buf_delete, self.buf, { force = true })
|
||||
end
|
||||
self.win = nil
|
||||
self.buf = nil
|
||||
end
|
||||
|
||||
--
|
||||
-- if index ~= #signatures and config.render_style.separator then
|
||||
-- table.insert(lines, string.rep("═", 40))
|
||||
-- end
|
||||
-- end
|
||||
-- return lines, labels
|
||||
-- end
|
||||
--
|
||||
-- function SignatureHelp:create_float_window(contents)
|
||||
-- local max_width = math.min(self.config.max_width, vim.o.columns)
|
||||
-- local max_height = math.min(self.config.max_height, #contents)
|
||||
--
|
||||
-- -- Calculate optimal position
|
||||
-- local cursor = api.nvim_win_get_cursor(0)
|
||||
-- local cursor_line = cursor[1]
|
||||
-- local screen_line = vim.fn.screenpos(0, cursor_line, 1).row
|
||||
--
|
||||
-- local row_offset = self.config.floating_window_above_cur_line and -max_height - 1 or 1
|
||||
-- if screen_line + row_offset < 1 then
|
||||
-- row_offset = 2 -- Show below if not enough space above
|
||||
-- end
|
||||
--
|
||||
-- local win_config = {
|
||||
-- relative = "cursor",
|
||||
-- row = row_offset - 1,
|
||||
-- col = 0,
|
||||
-- width = max_width,
|
||||
-- height = max_height,
|
||||
-- style = "minimal",
|
||||
-- border = self.config.border,
|
||||
-- zindex = 50, -- Ensure it's above most other floating windows
|
||||
-- }
|
||||
--
|
||||
-- if self.win and api.nvim_win_is_valid(self.win) then
|
||||
-- api.nvim_win_set_config(self.win, win_config)
|
||||
-- api.nvim_win_set_buf(self.win, self.buf)
|
||||
-- else
|
||||
-- self.buf = api.nvim_create_buf(false, true)
|
||||
-- self.win = api.nvim_open_win(self.buf, false, win_config)
|
||||
-- end
|
||||
--
|
||||
-- vim.bo[self.buf].modifiable = true
|
||||
-- api.nvim_buf_set_lines(self.buf, 0, -1, false, contents)
|
||||
-- vim.bo[self.buf].modifiable = false
|
||||
-- vim.wo[self.win][self.buf].foldenable = false
|
||||
-- vim.wo[self.win][self.buf].wrap = true
|
||||
-- vim.wo[self.win][self.buf].winblend = self.config.winblend
|
||||
-- self.visible = true
|
||||
-- end
|
||||
--
|
||||
function SignatureHelp:hide_sig_win()
|
||||
helper.close_float_window(self.sig_win, self.sig_buf)
|
||||
self.current_signatures = nil
|
||||
self.visible = false
|
||||
|
||||
-- Restore focus
|
||||
pcall(api.nvim_set_current_win, current_win)
|
||||
pcall(api.nvim_set_current_buf, current_buf)
|
||||
end
|
||||
self.showing = false
|
||||
end
|
||||
|
||||
function SignatureHelp:find_parameter_range(signature_str, parameter_label)
|
||||
-- Handle both string and table parameter labels
|
||||
if type(parameter_label) == "table" then
|
||||
return parameter_label[1], parameter_label[2]
|
||||
end
|
||||
|
||||
-- Escape special pattern characters in parameter_label
|
||||
local escaped_label = vim.pesc(parameter_label)
|
||||
|
||||
-- Look for the parameter with word boundaries
|
||||
local pattern = [[\<]] .. escaped_label .. [[\>]]
|
||||
local start_pos, end_pos = signature_str:find(pattern)
|
||||
|
||||
if not start_pos then
|
||||
-- Fallback: try finding exact match if word boundary search fails
|
||||
start_pos, end_pos = signature_str:find(escaped_label)
|
||||
end
|
||||
|
||||
if not start_pos then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- local end_pos = start_pos + #parameter_label
|
||||
return start_pos, end_pos
|
||||
function SignatureHelp:hide_doc_win()
|
||||
helper.close_float_window(self.doc_win, self.doc_buf)
|
||||
end
|
||||
|
||||
function SignatureHelp:extract_default_value(param_info)
|
||||
|
|
@ -255,166 +222,61 @@ function SignatureHelp:extract_default_value(param_info)
|
|||
return nil
|
||||
end
|
||||
|
||||
function SignatureHelp:set_active_parameter_highlights(active_parameter, signatures, labels)
|
||||
if not self.buf or not api.nvim_buf_is_valid(self.buf) then
|
||||
function SignatureHelp:set_active_parameter_highlights(param_str, lines)
|
||||
if not self.sig_buf or not api.nvim_buf_is_valid(self.sig_buf) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Clear existing highlights
|
||||
api.nvim_buf_clear_namespace(self.buf, -1, 0, -1)
|
||||
self.ns = api.nvim_create_namespace("lsp_signature_hi_parameter")
|
||||
local hi = self.config.hi_parameter
|
||||
|
||||
-- Iterate over signatures to highlight the active parameter
|
||||
for index, signature in ipairs(signatures) do
|
||||
local parameter = signature.activeParameter or active_parameter
|
||||
if parameter and parameter >= 0 and parameter < #signature.parameters then
|
||||
local label = signature.parameters[parameter + 1].label
|
||||
if type(label) == "string" then
|
||||
-- Parse the signature string to find the exact range of the active parameter
|
||||
local signature_str = signature.label
|
||||
local start_pos, end_pos = self:find_parameter_range(signature_str, label)
|
||||
if start_pos and end_pos then
|
||||
api.nvim_buf_add_highlight(
|
||||
self.buf,
|
||||
-1,
|
||||
"LspSignatureActiveParameter",
|
||||
labels[index],
|
||||
start_pos,
|
||||
end_pos
|
||||
local s, e, line_num
|
||||
for l, line in ipairs(lines) do
|
||||
local ss, ee, _ = string.find(line, param_str, 1, true)
|
||||
if ss ~= nil then
|
||||
line_num = l - 1
|
||||
s = ss
|
||||
e = ee
|
||||
end
|
||||
end
|
||||
|
||||
if s and e and s > 0 then
|
||||
self.markid = api.nvim_buf_set_extmark(
|
||||
self.sig_buf,
|
||||
self.ns,
|
||||
line_num,
|
||||
s,
|
||||
{ id = self.markid, end_line = line_num, end_col = e, hl_group = hi, strict = false }
|
||||
)
|
||||
end
|
||||
elseif type(label) == "table" then
|
||||
local start_pos, end_pos = unpack(label)
|
||||
api.nvim_buf_add_highlight(
|
||||
self.buf,
|
||||
-1,
|
||||
"LspSignatureActiveParameter",
|
||||
labels[index],
|
||||
start_pos + 6,
|
||||
end_pos + 6
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add icon highlights
|
||||
local icon_highlights = {
|
||||
{ self.config.icons.method, "SignatureHelpMethod" },
|
||||
{ self.config.icons.parameter, "SignatureHelpParameter" },
|
||||
{ self.config.icons.documentation, "SignatureHelpDocumentation" },
|
||||
}
|
||||
|
||||
for _, icon_hl in ipairs(icon_highlights) do
|
||||
local icon, hl_group = unpack(icon_hl)
|
||||
local line_num = 0
|
||||
while line_num < api.nvim_buf_line_count(self.buf) do
|
||||
local line = api.nvim_buf_get_lines(self.buf, line_num, line_num + 1, false)[1]
|
||||
local start_col = line:find(vim.pesc(icon))
|
||||
if start_col then
|
||||
api.nvim_buf_add_highlight(self.buf, -1, hl_group, line_num, start_col - 1, start_col + #icon - 1)
|
||||
end
|
||||
line_num = line_num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SignatureHelp:highlight_icons()
|
||||
local icon_highlights = {
|
||||
{ self.config.icons.method, "SignatureHelpMethod" },
|
||||
{ self.config.icons.parameter, "SignatureHelpParameter" },
|
||||
{ self.config.icons.documentation, "SignatureHelpDocumentation" },
|
||||
}
|
||||
|
||||
for _, icon_hl in ipairs(icon_highlights) do
|
||||
local icon, hl_group = unpack(icon_hl)
|
||||
local line_num = 0
|
||||
while line_num < api.nvim_buf_line_count(self.buf) do
|
||||
local line = api.nvim_buf_get_lines(self.buf, line_num, line_num + 1, false)[1]
|
||||
local start_col = line:find(vim.pesc(icon))
|
||||
if start_col then
|
||||
api.nvim_buf_add_highlight(self.buf, -1, hl_group, line_num, start_col - 1, start_col - 1 + #icon)
|
||||
end
|
||||
line_num = line_num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SignatureHelp:display(result)
|
||||
if not result or not result.signatures or #result.signatures == 0 then
|
||||
self:hide()
|
||||
self:hide_sig_win()
|
||||
return
|
||||
end
|
||||
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Prevent duplicate displays of identical content
|
||||
if
|
||||
self.current_signatures
|
||||
and vim.deep_equal(result.signatures, self.current_signatures)
|
||||
and result.activeParameter == self.current_active_parameter
|
||||
self.current_signatures and vim.deep_equal(result.signatures, self.current_signatures)
|
||||
-- and result.activeParameter == self.current_active_parameter
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
local markdown, labels = markdown_for_signature_list(result.signatures, self.config)
|
||||
self.current_signatures = result.signatures
|
||||
self.current_active_parameter = result.activeParameter
|
||||
|
||||
if #markdown > 0 then
|
||||
if self.config.dock_mode.enabled then
|
||||
local win, buf = self:create_dock_window()
|
||||
if win and buf then
|
||||
api.nvim_buf_set_option(buf, "modifiable", true)
|
||||
api.nvim_buf_set_lines(buf, 0, -1, false, markdown)
|
||||
api.nvim_buf_set_option(buf, "modifiable", false)
|
||||
self:set_active_parameter_highlights(result.activeParameter, result.signatures, labels)
|
||||
self:apply_treesitter_highlighting()
|
||||
local content = helper.convert_signature_help_to_lines(result, self.config.win_opts.max_width)
|
||||
if not self.showing then
|
||||
self.sig_buf, self.sig_win = helper.create_float_window(content, "lua", self.config.win_opts) -- TODO: programmatically set file type:
|
||||
self.showing = true
|
||||
end
|
||||
else
|
||||
self:create_float_window(markdown)
|
||||
api.nvim_buf_set_option(self.buf, "filetype", "markdown")
|
||||
self:set_active_parameter_highlights(result.activeParameter, result.signatures, labels)
|
||||
self:apply_treesitter_highlighting()
|
||||
end
|
||||
else
|
||||
self:hide()
|
||||
end
|
||||
|
||||
-- Restore focus to original window and buffer
|
||||
api.nvim_set_current_win(current_win)
|
||||
api.nvim_set_current_buf(current_buf)
|
||||
end
|
||||
|
||||
function SignatureHelp:apply_treesitter_highlighting()
|
||||
local buf = self.config.dock_mode.enabled and self.dock_buf or self.buf
|
||||
if not buf or not api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
if not pcall(require, "nvim-treesitter") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Apply treesitter highlighting
|
||||
pcall(function()
|
||||
require("nvim-treesitter.highlight").attach(buf, "markdown")
|
||||
end)
|
||||
|
||||
-- Restore focus
|
||||
api.nvim_set_current_win(current_win)
|
||||
api.nvim_set_current_buf(current_buf)
|
||||
local param_str = helper.get_parameter_label(result)
|
||||
self:set_active_parameter_highlights(param_str, content)
|
||||
end
|
||||
|
||||
function SignatureHelp:trigger()
|
||||
if not self.enabled then
|
||||
return
|
||||
end
|
||||
vim.notify("trigger")
|
||||
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
vim.lsp.buf_request(0, "textDocument/signatureHelp", params, function(err, result, _, _)
|
||||
|
|
@ -422,14 +284,14 @@ function SignatureHelp:trigger()
|
|||
if not self.config.silent then
|
||||
vim.notify("Error in LSP Signature Help: " .. vim.inspect(err), vim.log.levels.ERROR)
|
||||
end
|
||||
self:hide()
|
||||
self:hide_sig_win()
|
||||
return
|
||||
end
|
||||
|
||||
if result and result.signatures and #result.signatures > 0 then
|
||||
self:display(result)
|
||||
else
|
||||
self:hide()
|
||||
self:hide_sig_win()
|
||||
-- Only notify if not silent and if there was actually no signature help
|
||||
if not self.config.silent and result then
|
||||
vim.notify("No signature help available", vim.log.levels.INFO)
|
||||
|
|
@ -454,7 +316,7 @@ function SignatureHelp:toggle_normal_mode()
|
|||
if self.normal_mode_active then
|
||||
self:trigger()
|
||||
else
|
||||
self:hide()
|
||||
self:hide_sig_win()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -501,7 +363,7 @@ function SignatureHelp:setup_autocmds()
|
|||
api.nvim_create_autocmd({ "InsertLeave" }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
self:hide()
|
||||
self:hide_sig_win()
|
||||
self.normal_mode_active = false
|
||||
end,
|
||||
})
|
||||
|
|
@ -514,137 +376,6 @@ function SignatureHelp:setup_autocmds()
|
|||
end, 100)
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("ColorScheme", {
|
||||
group = group,
|
||||
callback = function()
|
||||
if self.visible then
|
||||
self:apply_treesitter_highlighting()
|
||||
self:set_active_parameter_highlights(
|
||||
self.current_signatures.activeParameter,
|
||||
self.current_signatures,
|
||||
{}
|
||||
)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function SignatureHelp:create_dock_window()
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Update dock window ID for current buffer
|
||||
self.dock_win_id = "signature_help_dock_" .. current_buf
|
||||
|
||||
if not self.dock_win or not api.nvim_win_is_valid(self.dock_win) then
|
||||
-- Create dock buffer if needed
|
||||
if not self.dock_buf or not api.nvim_buf_is_valid(self.dock_buf) then
|
||||
self.dock_buf = api.nvim_create_buf(false, true)
|
||||
api.nvim_buf_set_option(self.dock_buf, "buftype", "nofile")
|
||||
api.nvim_buf_set_option(self.dock_buf, "bufhidden", "hide")
|
||||
api.nvim_buf_set_option(self.dock_buf, "modifiable", false)
|
||||
api.nvim_buf_set_option(self.dock_buf, "filetype", "markdown")
|
||||
|
||||
-- Set buffer name with ID for easier tracking
|
||||
api.nvim_buf_set_name(self.dock_buf, self.dock_win_id)
|
||||
end
|
||||
|
||||
-- Calculate dock position and dimensions
|
||||
local win_height = api.nvim_win_get_height(current_win)
|
||||
local win_width = api.nvim_win_get_width(current_win)
|
||||
local dock_height = math.min(self.config.dock_mode.height, math.floor(win_height * 0.3))
|
||||
local padding = self.config.dock_mode.padding
|
||||
local dock_width = win_width - (padding * 2)
|
||||
|
||||
local row = self.config.dock_mode.position == "bottom" and win_height - dock_height - padding or padding
|
||||
|
||||
-- Create dock window with enhanced config
|
||||
self.dock_win = api.nvim_open_win(self.dock_buf, false, {
|
||||
relative = "win",
|
||||
win = current_win,
|
||||
width = dock_width,
|
||||
height = dock_height,
|
||||
row = row,
|
||||
col = padding,
|
||||
style = "minimal",
|
||||
border = self.config.border,
|
||||
zindex = 45,
|
||||
focusable = false, -- Make window non-focusable to prevent focus issues
|
||||
})
|
||||
|
||||
-- Apply window options
|
||||
local win_opts = {
|
||||
wrap = true,
|
||||
winblend = self.config.winblend,
|
||||
foldenable = false,
|
||||
cursorline = false,
|
||||
winhighlight = "Normal:SignatureHelpDock,FloatBorder:SignatureHelpBorder",
|
||||
signcolumn = "no",
|
||||
}
|
||||
|
||||
for opt, value in pairs(win_opts) do
|
||||
api.nvim_win_set_option(self.dock_win, opt, value)
|
||||
end
|
||||
|
||||
-- Set up dock window keymaps
|
||||
local dock_buf_keymaps = {
|
||||
["q"] = function()
|
||||
self:hide()
|
||||
end,
|
||||
["<Esc>"] = function()
|
||||
self:hide()
|
||||
end,
|
||||
["<C-c>"] = function()
|
||||
self:hide()
|
||||
end,
|
||||
["<C-n>"] = function()
|
||||
self:next_signature()
|
||||
end,
|
||||
["<C-p>"] = function()
|
||||
self:prev_signature()
|
||||
end,
|
||||
}
|
||||
|
||||
for key, func in pairs(dock_buf_keymaps) do
|
||||
vim.keymap.set("n", key, func, { buffer = self.dock_buf, silent = true, nowait = true })
|
||||
end
|
||||
|
||||
-- Set window ID as a window variable
|
||||
api.nvim_win_set_var(self.dock_win, "signature_help_id", self.dock_win_id)
|
||||
end
|
||||
|
||||
-- Ensure focus returns to original window
|
||||
api.nvim_set_current_win(current_win)
|
||||
|
||||
return self.dock_win, self.dock_buf
|
||||
end
|
||||
|
||||
function SignatureHelp:close_dock_window()
|
||||
-- Fast check for existing dock window
|
||||
if not self.dock_win_id then
|
||||
return
|
||||
end
|
||||
|
||||
-- Try to find window by ID
|
||||
local wins = api.nvim_list_wins()
|
||||
for _, win in ipairs(wins) do
|
||||
local ok, win_id = pcall(api.nvim_win_get_var, win, "signature_help_id")
|
||||
if ok and win_id == self.dock_win_id then
|
||||
pcall(api.nvim_win_close, win, true)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Clean up buffer
|
||||
if self.dock_buf and api.nvim_buf_is_valid(self.dock_buf) then
|
||||
pcall(api.nvim_buf_delete, self.dock_buf, { force = true })
|
||||
end
|
||||
|
||||
-- Reset dock window state
|
||||
self.dock_win = nil
|
||||
self.dock_buf = nil
|
||||
end
|
||||
|
||||
-- Add navigation between multiple signatures
|
||||
|
|
@ -678,43 +409,6 @@ function SignatureHelp:prev_signature()
|
|||
})
|
||||
end
|
||||
|
||||
function SignatureHelp:toggle_dock_mode()
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
-- Store current signatures
|
||||
local current_sigs = self.current_signatures
|
||||
local current_active = self.current_active_parameter
|
||||
|
||||
-- Close existing windows efficiently
|
||||
if self.config.dock_mode.enabled then
|
||||
self:close_dock_window()
|
||||
else
|
||||
if self.win and api.nvim_win_is_valid(self.win) then
|
||||
pcall(api.nvim_win_close, self.win, true)
|
||||
pcall(api.nvim_buf_delete, self.buf, { force = true })
|
||||
self.win = nil
|
||||
self.buf = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Toggle mode
|
||||
self.config.dock_mode.enabled = not self.config.dock_mode.enabled
|
||||
|
||||
-- Redisplay if we had signatures
|
||||
if current_sigs then
|
||||
self:display({
|
||||
signatures = current_sigs,
|
||||
activeParameter = current_active,
|
||||
})
|
||||
end
|
||||
|
||||
-- Restore focus
|
||||
pcall(api.nvim_set_current_win, current_win)
|
||||
pcall(api.nvim_set_current_buf, current_buf)
|
||||
end
|
||||
|
||||
function SignatureHelp:setup_keymaps()
|
||||
-- Setup toggle keys using the actual config
|
||||
local toggle_key = self.config.toggle_key
|
||||
|
|
@ -791,16 +485,5 @@ M.dependencies = {
|
|||
}
|
||||
|
||||
-- Add API methods for external use
|
||||
M.toggle_dock = function()
|
||||
if M._instance then
|
||||
M._instance:toggle_dock_mode()
|
||||
end
|
||||
end
|
||||
|
||||
M.toggle_normal_mode = function()
|
||||
if M._instance then
|
||||
M._instance:toggle_normal_mode()
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
Loading…
Reference in New Issue