(fixed) bugs, (feat) new dock mode, more optimized, please read the Readme to set opts well.
parent
06722cbff6
commit
2b21a2aa51
170
README.md
170
README.md
|
|
@ -6,13 +6,18 @@ A little (smart maybe) lsp signature helper for neovim.
|
|||
|
||||
# Neovim Signature Help Plugin
|
||||
|
||||
This Neovim plugin provides a signature help feature for LSP (Language Server Protocol) clients. It displays function signatures and parameter information in a floating window as you type in insert mode or move the cursor in normal mode. The plugin also includes a notification system to display messages with different levels of severity (info, warning, error).
|
||||
This Neovim plugin provides a signature help feature for LSP (Language Server Protocol) clients. I can't tell much, just watch the showcases.
|
||||
|
||||
# ScreenShots (WIP)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
We have `dock` mode but its under dev for now, please take low expectations:
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
|
|
@ -26,17 +31,18 @@ This Neovim plugin provides a signature help feature for LSP (Language Server Pr
|
|||
|
||||
### Using [lazy.nvim](https://github.com/folke/lazy.nvim)
|
||||
|
||||
Add the following to your `init.lua`:
|
||||
Add the following to your `init.lua` and use `main` branch always:
|
||||
|
||||
```lua
|
||||
require("lazy").setup({
|
||||
{
|
||||
"Dan7h3x/signup.nvim",
|
||||
branch = "main",
|
||||
config = function()
|
||||
require("signup").setup({
|
||||
-- Your configuration options here
|
||||
})
|
||||
opts = {
|
||||
-- Your configuration options here
|
||||
},
|
||||
config = function(_,opts)
|
||||
require("signup").setup(opts)
|
||||
end
|
||||
}
|
||||
})
|
||||
|
|
@ -62,109 +68,51 @@ EOF
|
|||
|
||||
## Configuration
|
||||
|
||||
The plugin comes with a default configuration, but you can customize it according to your preferences. Here are the available options:
|
||||
The plugin comes with a default configuration, but you can customize it
|
||||
according to your preferences. Here are the available options:
|
||||
|
||||
```lua
|
||||
require('signup').setup(
|
||||
{
|
||||
win = nil,
|
||||
buf = nil,
|
||||
timer = nil,
|
||||
visible = false,
|
||||
current_signatures = nil,
|
||||
enabled = false,
|
||||
normal_mode_active = false,
|
||||
config = {
|
||||
silent = false,
|
||||
number = true,
|
||||
icons = {
|
||||
parameter = " ",
|
||||
method = " ",
|
||||
documentation = " ",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#86e1fc",
|
||||
method = "#c099ff",
|
||||
documentation = "#4fd6be",
|
||||
},
|
||||
active_parameter_colors = {
|
||||
bg = "#86e1fc",
|
||||
fg = "#1a1a1a",
|
||||
},
|
||||
border = "solid",
|
||||
winblend = 10,
|
||||
}
|
||||
opts = {
|
||||
silent = false,
|
||||
number = true,
|
||||
icons = {
|
||||
parameter = "",
|
||||
method = "",
|
||||
documentation = "",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#86e1fc",
|
||||
method = "#c099ff",
|
||||
documentation = "#4fd6be",
|
||||
default_value = "#a80888",
|
||||
},
|
||||
active_parameter_colors = {
|
||||
bg = "#86e1fc",
|
||||
fg = "#1a1a1a",
|
||||
},
|
||||
border = "solid",
|
||||
winblend = 10,
|
||||
auto_close = true,
|
||||
trigger_chars = { "(", "," },
|
||||
max_height = 10,
|
||||
max_width = 40,
|
||||
floating_window_above_cur_line = true,
|
||||
preview_parameters = true,
|
||||
debounce_time = 30,
|
||||
dock_toggle_key = "<Leader>sd",
|
||||
toggle_key = "<C-k>",
|
||||
dock_mode = {
|
||||
enabled = false,
|
||||
position = "bottom",
|
||||
height = 3,
|
||||
padding = 1,
|
||||
},
|
||||
render_style = {
|
||||
separator = true,
|
||||
compact = true,
|
||||
align_icons = true,
|
||||
},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- **silent**: If `true`, suppresses notifications. Default is `false`.
|
||||
- **number**: If `true`, displays the signature index. Default is `true`.
|
||||
- **icons**: Custom icons for method, parameter, and documentation.
|
||||
- **colors**: Custom colors for method, parameter, and documentation.
|
||||
- **border**: Border style for the floating window. Default is `"rounded"`.
|
||||
- **winblend**: Transparency level for the floating window. Default is `10`.
|
||||
- **override**: If `true`, overrides the default LSP handler for `textDocument/signatureHelp`. Default is `true`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Toggle Signature Help in Normal Mode
|
||||
|
||||
You can toggle the signature help in normal mode using the default keybinding `<C-k>`. You can customize this keybinding in the setup function:
|
||||
|
||||
```lua
|
||||
require('signup').setup({
|
||||
toggle_key = "<C-k>", -- Customize the toggle key here
|
||||
})
|
||||
```
|
||||
|
||||
### Trigger Signature Help in Insert Mode
|
||||
|
||||
The signature help is automatically triggered when you move the cursor or change text in insert mode.
|
||||
|
||||
### Notifications
|
||||
|
||||
The plugin includes a notification system to display messages with different levels of severity (info, warning, error). These notifications are displayed in a floating window and automatically disappear after a few seconds.
|
||||
|
||||
## Highlight Groups
|
||||
|
||||
The plugin defines the following highlight groups:
|
||||
|
||||
- **LspSignatureActiveParameter**: Highlight for the active parameter.
|
||||
- **SignatureHelpMethod**: Highlight for method icons.
|
||||
- **SignatureHelpParameter**: Highlight for parameter icons.
|
||||
- **SignatureHelpDocumentation**: Highlight for documentation icons.
|
||||
- **NotificationInfo**: Highlight for info notifications.
|
||||
- **NotificationWarn**: Highlight for warning notifications.
|
||||
- **NotificationError**: Highlight for error notifications.
|
||||
|
||||
## Examples
|
||||
|
||||
### Customizing Icons and Colors
|
||||
|
||||
```lua
|
||||
require('signup').setup({
|
||||
icons = {
|
||||
parameter = " ",
|
||||
method = " ",
|
||||
documentation = " ",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#ffa500",
|
||||
method = "#8a2be2",
|
||||
documentation = "#008000",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Disabling Notifications
|
||||
|
||||
```lua
|
||||
require('signup').setup({
|
||||
silent = true,
|
||||
})
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
|
@ -176,3 +124,7 @@ Contributions are welcome! Please feel free to submit a pull request or open an
|
|||
This plugin is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,35 +6,64 @@ local SignatureHelp = {}
|
|||
SignatureHelp.__index = SignatureHelp
|
||||
|
||||
function SignatureHelp.new()
|
||||
return setmetatable({
|
||||
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(),
|
||||
timer = nil,
|
||||
visible = false,
|
||||
current_signatures = nil,
|
||||
enabled = false,
|
||||
normal_mode_active = false,
|
||||
config = {
|
||||
silent = false,
|
||||
number = true,
|
||||
icons = {
|
||||
parameter = " ",
|
||||
method = " ",
|
||||
documentation = " ",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#86e1fc",
|
||||
method = "#c099ff",
|
||||
documentation = "#4fd6be",
|
||||
},
|
||||
active_parameter_colors = {
|
||||
bg = "#86e1fc",
|
||||
fg = "#1a1a1a",
|
||||
},
|
||||
border = "solid",
|
||||
winblend = 10,
|
||||
}
|
||||
current_signature_idx = nil,
|
||||
config = nil,
|
||||
}, SignatureHelp)
|
||||
|
||||
instance._default_config = {
|
||||
silent = false,
|
||||
number = true,
|
||||
icons = {
|
||||
parameter = "",
|
||||
method = "",
|
||||
documentation = "",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#86e1fc",
|
||||
method = "#c099ff",
|
||||
documentation = "#4fd6be",
|
||||
default_value = "#a80888",
|
||||
},
|
||||
active_parameter_colors = {
|
||||
bg = "#86e1fc",
|
||||
fg = "#1a1a1a",
|
||||
},
|
||||
border = "solid",
|
||||
winblend = 10,
|
||||
auto_close = true,
|
||||
trigger_chars = { "(", "," },
|
||||
max_height = 10,
|
||||
max_width = 40,
|
||||
floating_window_above_cur_line = true,
|
||||
preview_parameters = true,
|
||||
debounce_time = 30,
|
||||
dock_toggle_key = "<Leader>sd",
|
||||
toggle_key = "<C-k>",
|
||||
dock_mode = {
|
||||
enabled = false,
|
||||
position = "bottom",
|
||||
height = 3,
|
||||
padding = 1,
|
||||
},
|
||||
render_style = {
|
||||
separator = true,
|
||||
compact = true,
|
||||
align_icons = true,
|
||||
},
|
||||
}
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
local function signature_index_comment(index)
|
||||
|
|
@ -48,51 +77,95 @@ 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 %s%s", config.icons.method, signature.label, suffix))
|
||||
-- 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
|
||||
-- table.insert(lines, "")
|
||||
-- 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
|
||||
-- table.insert(lines, string.format(" • %s", param.label))
|
||||
-- local param_doc = param.documentation and
|
||||
-- string.format(" - %s", param.documentation.value or param.documentation) or ""
|
||||
-- table.insert(lines, string.format(" • %s = %s", param.label, param_doc))
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Documentation section
|
||||
if signature.documentation then
|
||||
table.insert(lines, "")
|
||||
if config.render_style.separator then
|
||||
table.insert(lines, string.rep("-", 40))
|
||||
end
|
||||
table.insert(lines, string.format("%s Documentation:", config.icons.documentation))
|
||||
vim.list_extend(lines, vim.split(signature.documentation.value or signature.documentation, "\n"))
|
||||
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 then
|
||||
table.insert(lines, "---")
|
||||
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 width = math.min(45, vim.o.columns)
|
||||
local height = math.min(#contents, 10)
|
||||
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 row = cursor[1] - api.nvim_win_get_cursor(0)[1]
|
||||
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 + 1,
|
||||
row = row_offset - 1,
|
||||
col = 0,
|
||||
width = width,
|
||||
height = height,
|
||||
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
|
||||
|
|
@ -115,60 +188,86 @@ end
|
|||
|
||||
function SignatureHelp:hide()
|
||||
if self.visible then
|
||||
pcall(api.nvim_win_close, self.win, true)
|
||||
pcall(api.nvim_buf_delete, self.buf, { force = true })
|
||||
self.win = nil
|
||||
self.buf = nil
|
||||
-- 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
|
||||
|
||||
self.visible = false
|
||||
self.current_signatures = nil
|
||||
|
||||
-- Restore focus
|
||||
pcall(api.nvim_set_current_win, current_win)
|
||||
pcall(api.nvim_set_current_buf, current_buf)
|
||||
end
|
||||
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 return end
|
||||
--
|
||||
-- api.nvim_buf_clear_namespace(self.buf, -1, 0, -1)
|
||||
--
|
||||
-- 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
|
||||
-- -- vim.fn.matchadd("LspSignatureActiveParameter", "\\<" .. label .. "\\>")
|
||||
-- vim.fn.matchadd("LspSignatureActiveParameter", "\\V\\<" .. vim.fn.escape(label, '\\') .. "\\>")
|
||||
-- elseif type(label) == "table" then
|
||||
-- -- api.nvim_buf_add_highlight(self.buf, -1, "LspSignatureActiveParameter", labels[index], unpack(label))
|
||||
-- assert(self.buf and labels[index] and label[1] and label[2], "Invalid arguments")
|
||||
-- api.nvim_buf_add_highlight(self.buf, -1, "LspSignatureActiveParameter", labels[index], unpack(label))
|
||||
-- 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: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 = [[\b]] .. escaped_label .. [[\b]]
|
||||
local start_pos = signature_str:find(pattern)
|
||||
|
||||
if not start_pos then
|
||||
-- Fallback: try finding exact match if word boundary search fails
|
||||
start_pos = signature_str:find(escaped_label)
|
||||
end
|
||||
|
||||
if not start_pos then return nil, nil end
|
||||
|
||||
local end_pos = start_pos + #parameter_label - 1
|
||||
return start_pos, end_pos
|
||||
end
|
||||
|
||||
function SignatureHelp:extract_default_value(param_info)
|
||||
-- Check if parameter has documentation that might contain default value
|
||||
if not param_info.documentation then return nil end
|
||||
|
||||
local doc = type(param_info.documentation) == "string"
|
||||
and param_info.documentation
|
||||
or param_info.documentation.value
|
||||
|
||||
-- Look for common default value patterns
|
||||
local patterns = {
|
||||
"default:%s*([^%s]+)",
|
||||
"defaults%s+to%s+([^%s]+)",
|
||||
"%(default:%s*([^%)]+)%)",
|
||||
}
|
||||
|
||||
for _, pattern in ipairs(patterns) do
|
||||
local default = doc:match(pattern)
|
||||
if default then return default end
|
||||
end
|
||||
|
||||
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 return end
|
||||
|
||||
-- Clear existing highlights
|
||||
api.nvim_buf_clear_namespace(self.buf, -1, 0, -1)
|
||||
|
||||
-- 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
|
||||
|
|
@ -178,8 +277,8 @@ function SignatureHelp:set_active_parameter_highlights(active_parameter, signatu
|
|||
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 - 1,
|
||||
end_pos - 1)
|
||||
api.nvim_buf_add_highlight(self.buf, -1, "LspSignatureActiveParameter", labels[index], start_pos,
|
||||
end_pos)
|
||||
end
|
||||
elseif type(label) == "table" then
|
||||
local start_pos, end_pos = unpack(label)
|
||||
|
|
@ -202,27 +301,39 @@ function SignatureHelp:set_active_parameter_highlights(active_parameter, signatu
|
|||
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, start_col + #icon)
|
||||
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:find_parameter_range(signature_str, parameter_label)
|
||||
local start_pos = signature_str:find(parameter_label)
|
||||
if not start_pos then return nil, nil end
|
||||
function SignatureHelp:highlight_icons()
|
||||
local icon_highlights = {
|
||||
{ self.config.icons.method, "SignatureHelpMethod" },
|
||||
{ self.config.icons.parameter, "SignatureHelpParameter" },
|
||||
{ self.config.icons.documentation, "SignatureHelpDocumentation" },
|
||||
}
|
||||
|
||||
local end_pos = start_pos + #parameter_label - 1
|
||||
|
||||
-- Ensure the parameter label is not part of a larger word
|
||||
local before_char = signature_str:sub(start_pos - 1, start_pos - 1)
|
||||
local after_char = signature_str:sub(end_pos + 1, end_pos + 1)
|
||||
if (start_pos > 1 and not before_char:match("%s")) or (end_pos < #signature_str and not after_char:match("%s")) then
|
||||
return nil, nil
|
||||
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
|
||||
|
||||
return start_pos - 1, end_pos
|
||||
end
|
||||
|
||||
function SignatureHelp:display(result)
|
||||
|
|
@ -231,30 +342,67 @@ function SignatureHelp:display(result)
|
|||
return
|
||||
end
|
||||
|
||||
local markdown, labels = markdown_for_signature_list(result.signatures, self.config)
|
||||
-- Store current window and buffer
|
||||
local current_win = api.nvim_get_current_win()
|
||||
local current_buf = api.nvim_get_current_buf()
|
||||
|
||||
if vim.deep_equal(result.signatures, self.current_signatures) then
|
||||
-- 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 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
|
||||
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()
|
||||
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()
|
||||
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
|
||||
|
||||
require("nvim-treesitter.highlight").attach(self.buf, "markdown")
|
||||
-- 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)
|
||||
end
|
||||
|
||||
function SignatureHelp:trigger()
|
||||
|
|
@ -270,11 +418,12 @@ function SignatureHelp:trigger()
|
|||
return
|
||||
end
|
||||
|
||||
if result then
|
||||
if result and result.signatures and #result.signatures > 0 then
|
||||
self:display(result)
|
||||
else
|
||||
self:hide()
|
||||
if not self.config.silent then
|
||||
-- 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)
|
||||
end
|
||||
end
|
||||
|
|
@ -364,42 +513,266 @@ function SignatureHelp:setup_autocmds()
|
|||
})
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
opts = opts or {}
|
||||
local signature_help = SignatureHelp.new()
|
||||
signature_help.config = vim.tbl_deep_extend("force", signature_help.config, opts)
|
||||
signature_help:setup_autocmds()
|
||||
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()
|
||||
|
||||
local toggle_key = opts.toggle_key or "<C-k>"
|
||||
vim.keymap.set("n", toggle_key, function()
|
||||
signature_help:toggle_normal_mode()
|
||||
end, { noremap = true, silent = true, desc = "Toggle signature help in normal mode" })
|
||||
-- Update dock window ID for current buffer
|
||||
self.dock_win_id = "signature_help_dock_" .. current_buf
|
||||
|
||||
if pcall(require, "nvim-treesitter") then
|
||||
require("nvim-treesitter").define_modules({
|
||||
signature_help_highlighting = {
|
||||
module_path = "signature_help.highlighting",
|
||||
is_supported = function(lang)
|
||||
return lang == "markdown"
|
||||
end,
|
||||
},
|
||||
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
|
||||
function SignatureHelp:next_signature()
|
||||
if not self.current_signatures then return end
|
||||
self.current_signature_idx = (self.current_signature_idx or 0) + 1
|
||||
if self.current_signature_idx > #self.current_signatures then
|
||||
self.current_signature_idx = 1
|
||||
end
|
||||
self:display({
|
||||
signatures = self.current_signatures,
|
||||
activeParameter = self.current_active_parameter,
|
||||
activeSignature = self.current_signature_idx - 1
|
||||
})
|
||||
end
|
||||
|
||||
function SignatureHelp:prev_signature()
|
||||
if not self.current_signatures then return end
|
||||
self.current_signature_idx = (self.current_signature_idx or 1) - 1
|
||||
if self.current_signature_idx < 1 then
|
||||
self.current_signature_idx = #self.current_signatures
|
||||
end
|
||||
self:display({
|
||||
signatures = self.current_signatures,
|
||||
activeParameter = self.current_active_parameter,
|
||||
activeSignature = self.current_signature_idx - 1
|
||||
})
|
||||
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
|
||||
vim.api.nvim_set_hl(0, "LspSignatureActiveParameter",
|
||||
{ fg = opts.config.active_parameter_colors.fg, bg = opts.config.active_parameter_colors.bg })
|
||||
vim.cmd(string.format([[
|
||||
highlight default SignatureHelpMethod guifg=%s
|
||||
highlight default SignatureHelpParameter guifg=%s
|
||||
highlight default SignatureHelpDocumentation guifg=%s
|
||||
]], signature_help.config.colors.method, signature_help.config.colors.parameter,
|
||||
signature_help.config.colors.documentation))
|
||||
|
||||
if opts.override then
|
||||
vim.lsp.handlers["textDocument/signatureHelp"] = function(_, result, context, config)
|
||||
config = vim.tbl_deep_extend("force", signature_help.config, config or {})
|
||||
signature_help:display(result)
|
||||
-- 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
|
||||
local dock_toggle_key = self.config.dock_toggle_key
|
||||
|
||||
if toggle_key then
|
||||
vim.keymap.set("n", toggle_key, function()
|
||||
self:toggle_normal_mode()
|
||||
end, { noremap = true, silent = true, desc = "Toggle signature help in normal mode" })
|
||||
end
|
||||
|
||||
if dock_toggle_key then
|
||||
vim.keymap.set("n", dock_toggle_key, function()
|
||||
self:toggle_dock_mode()
|
||||
end, { noremap = true, silent = true, desc = "Toggle between dock and float mode" })
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
-- Ensure setup is called only once
|
||||
if M._initialized then
|
||||
return M._instance
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
local signature_help = SignatureHelp.new()
|
||||
|
||||
-- Deep merge user config with defaults
|
||||
signature_help.config = vim.tbl_deep_extend("force",
|
||||
signature_help._default_config,
|
||||
opts
|
||||
)
|
||||
|
||||
-- Setup highlights with user config
|
||||
local function setup_highlights()
|
||||
local colors = signature_help.config.colors
|
||||
local highlights = {
|
||||
SignatureHelpDock = { link = "NormalFloat" },
|
||||
SignatureHelpBorder = { link = "FloatBorder" },
|
||||
SignatureHelpMethod = { fg = colors.method },
|
||||
SignatureHelpParameter = { fg = colors.parameter },
|
||||
SignatureHelpDocumentation = { fg = colors.documentation },
|
||||
SignatureHelpDefaultValue = { fg = colors.default_value, italic = true },
|
||||
LspSignatureActiveParameter = {
|
||||
fg = signature_help.config.active_parameter_colors.fg,
|
||||
bg = signature_help.config.active_parameter_colors.bg,
|
||||
},
|
||||
}
|
||||
|
||||
for group, hl_opts in pairs(highlights) do
|
||||
vim.api.nvim_set_hl(0, group, hl_opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup highlights and ensure they persist across colorscheme changes
|
||||
setup_highlights()
|
||||
vim.api.nvim_create_autocmd("ColorScheme", {
|
||||
group = vim.api.nvim_create_augroup("LspSignatureColors", { clear = true }),
|
||||
callback = setup_highlights,
|
||||
})
|
||||
|
||||
-- Setup autocmds and keymaps
|
||||
signature_help:setup_autocmds()
|
||||
signature_help:setup_keymaps()
|
||||
|
||||
-- Store instance for potential reuse
|
||||
M._initialized = true
|
||||
M._instance = signature_help
|
||||
|
||||
return signature_help
|
||||
end
|
||||
|
||||
-- Add version and metadata for lazy.nvim compatibility
|
||||
M.version = "1.0.0"
|
||||
M.dependencies = {
|
||||
"nvim-treesitter/nvim-treesitter",
|
||||
}
|
||||
|
||||
-- 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