--wip-- [skip ci]
parent
999c39768f
commit
6da959b69d
214
README.md
214
README.md
|
|
@ -1,2 +1,216 @@
|
|||
# signup.nvim
|
||||
|
||||
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).
|
||||
|
||||
## Features
|
||||
|
||||
- **Signature Help**: Displays function signatures and parameter information in a floating window.
|
||||
- **Toggle Mode**: Toggle signature help in normal mode.
|
||||
- **Customizable**: Highly customizable with options for icons, colors, and more.
|
||||
- **Integration**: Integrates with nvim-treesitter for syntax highlighting.
|
||||
- **Notifications**: Displays notifications for errors, warnings, and info messages.
|
||||
|
||||
## Installation
|
||||
|
||||
### Using [lazy.nvim](https://github.com/folke/lazy.nvim)
|
||||
|
||||
Add the following to your `init.lua`:
|
||||
|
||||
```lua
|
||||
require("lazy").setup({
|
||||
{
|
||||
"Dan7h3x/signup.nvim",
|
||||
config = function()
|
||||
require("signup").setup({
|
||||
-- Your configuration options here
|
||||
})
|
||||
end
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Using Vim-Plug
|
||||
|
||||
Add the following to your `init.vim`:
|
||||
|
||||
```vim
|
||||
Plug "Dan7h3x/signup.nvim"
|
||||
```
|
||||
|
||||
Then, in your `init.lua`:
|
||||
|
||||
```lua
|
||||
lua << EOF
|
||||
require('signup').setup({
|
||||
-- Your configuration options here
|
||||
})
|
||||
EOF
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
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({
|
||||
silent = false,
|
||||
number = true,
|
||||
icons = {
|
||||
parameter = " ",
|
||||
method = " ",
|
||||
documentation = " ",
|
||||
},
|
||||
colors = {
|
||||
parameter = "#86e1fc",
|
||||
method = "#c099ff",
|
||||
documentation = "#4fd6be",
|
||||
},
|
||||
border = "rounded",
|
||||
winblend = 10,
|
||||
override = true, -- Override default LSP handler for signatureHelp
|
||||
})
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Contributions are welcome! Please feel free to submit a pull request or open an issue if you encounter any problems or have suggestions for improvements.
|
||||
|
||||
## License
|
||||
|
||||
This plugin is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
||||
|
||||
---
|
||||
|
||||
## Good Color Schemes
|
||||
|
||||
To make your signature help window look even better, you can use the following color schemes:
|
||||
|
||||
### Gruvbox
|
||||
|
||||
```lua
|
||||
vim.cmd([[
|
||||
colorscheme gruvbox
|
||||
highlight NormalFloat guibg=#3c3836 guifg=#ebdbb2
|
||||
highlight FloatBorder guifg=#8ec07c
|
||||
highlight LspSignatureActiveParameter guifg=#fabd2f guibg=#3c3836 gui=bold
|
||||
highlight SignatureHelpMethod guifg=#83a598
|
||||
highlight SignatureHelpParameter guifg=#b8bb26
|
||||
highlight SignatureHelpDocumentation guifg=#d3869b
|
||||
highlight NotificationInfo guifg=#8ec07c guibg=#3c3836
|
||||
highlight NotificationWarn guifg=#fabd2f guibg=#3c3836
|
||||
highlight NotificationError guifg=#fb4934 guibg=#3c3836
|
||||
]])
|
||||
```
|
||||
|
||||
### Nord
|
||||
|
||||
```lua
|
||||
vim.cmd([[
|
||||
colorscheme nord
|
||||
highlight NormalFloat guibg=#3b4252 guifg=#e5e9f0
|
||||
highlight FloatBorder guifg=#81a1c1
|
||||
highlight LspSignatureActiveParameter guifg=#88c0d0 guibg=#3b4252 gui=bold
|
||||
highlight SignatureHelpMethod guifg=#81a1c1
|
||||
highlight SignatureHelpParameter guifg=#a3be8c
|
||||
highlight SignatureHelpDocumentation guifg=#b48ead
|
||||
highlight NotificationInfo guifg=#81a1c1 guibg=#3b4252
|
||||
highlight NotificationWarn guifg=#ebcb8b guibg=#3b4252
|
||||
highlight NotificationError guifg=#bf616a guibg=#3b4252
|
||||
]])
|
||||
```
|
||||
|
||||
### OneDark
|
||||
|
||||
```lua
|
||||
vim.cmd([[
|
||||
colorscheme onedark
|
||||
highlight NormalFloat guibg=#282c34 guifg=#abb2bf
|
||||
highlight FloatBorder guifg=#61afef
|
||||
highlight LspSignatureActiveParameter guifg=#e06c75 guibg=#282c34 gui=bold
|
||||
highlight SignatureHelpMethod guifg=#61afef
|
||||
highlight SignatureHelpParameter guifg=#98c379
|
||||
highlight SignatureHelpDocumentation guifg=#c678dd
|
||||
highlight NotificationInfo guifg=#61afef guibg=#282c34
|
||||
highlight NotificationWarn guifg=#e5c07b guibg=#282c34
|
||||
highlight NotificationError guifg=#e06c75 guibg=#282c34
|
||||
]])
|
||||
```
|
||||
|
||||
Feel free to customize these color schemes to match your personal preferences!
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,340 @@
|
|||
local api = vim.api
|
||||
|
||||
local M = {}
|
||||
|
||||
local SignatureHelp = {}
|
||||
SignatureHelp.__index = SignatureHelp
|
||||
|
||||
function SignatureHelp.new()
|
||||
return setmetatable({
|
||||
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",
|
||||
},
|
||||
border = "rounded",
|
||||
winblend = 10,
|
||||
}
|
||||
}, SignatureHelp)
|
||||
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
|
||||
for index, signature in ipairs(signatures) do
|
||||
table.insert(labels, #lines + 1)
|
||||
|
||||
local suffix = number and (' ' .. signature_index_comment(index)) or ''
|
||||
|
||||
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, "```")
|
||||
|
||||
if signature.parameters and #signature.parameters > 0 then
|
||||
table.insert(lines, "")
|
||||
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))
|
||||
end
|
||||
end
|
||||
|
||||
if signature.documentation then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, string.format("%s Documentation:", config.icons.documentation))
|
||||
vim.list_extend(lines, vim.split(signature.documentation.value or signature.documentation, "\n"))
|
||||
end
|
||||
|
||||
if index ~= #signatures then
|
||||
table.insert(lines, "---")
|
||||
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, 4)
|
||||
|
||||
local cursor = api.nvim_win_get_cursor(0)
|
||||
local row = cursor[1] - api.nvim_win_get_cursor(0)[1]
|
||||
|
||||
local win_config = {
|
||||
relative = "cursor",
|
||||
row = row - 7,
|
||||
col = 0,
|
||||
width = width,
|
||||
height = height,
|
||||
style = "minimal",
|
||||
border = self.config.border,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
api.nvim_buf_set_option(self.buf, "modifiable", true)
|
||||
api.nvim_buf_set_lines(self.buf, 0, -1, false, contents)
|
||||
api.nvim_buf_set_option(self.buf, "modifiable", false)
|
||||
api.nvim_win_set_option(self.win, "foldenable", false)
|
||||
api.nvim_win_set_option(self.win, "wrap", false)
|
||||
api.nvim_win_set_option(self.win, "winblend", self.config.winblend)
|
||||
|
||||
self.visible = true
|
||||
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
|
||||
self.visible = false
|
||||
self.current_signatures = nil
|
||||
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 .. "\\>")
|
||||
elseif type(label) == "table" then
|
||||
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:display(result)
|
||||
if not result or not result.signatures or #result.signatures == 0 then
|
||||
self:hide()
|
||||
return
|
||||
end
|
||||
|
||||
local markdown, labels = markdown_for_signature_list(result.signatures, self.config)
|
||||
|
||||
if vim.deep_equal(result.signatures, self.current_signatures) then
|
||||
return
|
||||
end
|
||||
|
||||
self.current_signatures = result.signatures
|
||||
|
||||
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()
|
||||
else
|
||||
self:hide()
|
||||
end
|
||||
end
|
||||
|
||||
function SignatureHelp:apply_treesitter_highlighting()
|
||||
if not pcall(require, "nvim-treesitter") then
|
||||
return
|
||||
end
|
||||
|
||||
require("nvim-treesitter.highlight").attach(self.buf, "markdown")
|
||||
end
|
||||
|
||||
function SignatureHelp:trigger()
|
||||
if not self.enabled then return end
|
||||
|
||||
local params = vim.lsp.util.make_position_params()
|
||||
vim.lsp.buf_request(0, "textDocument/signatureHelp", params, function(err, result, _, _)
|
||||
if err then
|
||||
if not self.config.silent then
|
||||
vim.notify("Error in LSP Signature Help: " .. vim.inspect(err), vim.log.levels.ERROR)
|
||||
end
|
||||
self:hide()
|
||||
return
|
||||
end
|
||||
|
||||
if result then
|
||||
self:display(result)
|
||||
else
|
||||
self:hide()
|
||||
if not self.config.silent then
|
||||
vim.notify("No signature help available", vim.log.levels.INFO)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function SignatureHelp:check_capability()
|
||||
local clients = vim.lsp.get_clients()
|
||||
for _, client in ipairs(clients) do
|
||||
if client.server_capabilities.signatureHelpProvider then
|
||||
self.enabled = true
|
||||
return
|
||||
end
|
||||
end
|
||||
self.enabled = false
|
||||
end
|
||||
|
||||
function SignatureHelp:toggle_normal_mode()
|
||||
self.normal_mode_active = not self.normal_mode_active
|
||||
if self.normal_mode_active then
|
||||
self:trigger()
|
||||
else
|
||||
self:hide()
|
||||
end
|
||||
end
|
||||
|
||||
function SignatureHelp:setup_autocmds()
|
||||
local group = api.nvim_create_augroup("LspSignatureHelp", { clear = true })
|
||||
|
||||
local function debounced_trigger()
|
||||
if self.timer then
|
||||
vim.fn.timer_stop(self.timer)
|
||||
end
|
||||
self.timer = vim.fn.timer_start(30, function()
|
||||
self:trigger()
|
||||
end)
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd({ "CursorMovedI", "TextChangedI" }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
local cmp_visible = vim.fn.exists('*cmp#visible') == 1 and vim.fn.eval('cmp#visible()') == 1
|
||||
if cmp_visible then
|
||||
self:hide()
|
||||
elseif vim.fn.pumvisible() == 0 then
|
||||
debounced_trigger()
|
||||
else
|
||||
self:hide()
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd({ "CursorMoved" }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
if self.normal_mode_active then
|
||||
debounced_trigger()
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd({ "InsertLeave", "BufHidden", "BufLeave" }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
self:hide()
|
||||
self.normal_mode_active = false
|
||||
end
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("LspAttach", {
|
||||
group = group,
|
||||
callback = function()
|
||||
vim.defer_fn(function()
|
||||
self:check_capability()
|
||||
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 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()
|
||||
|
||||
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" })
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
vim.cmd(string.format([[
|
||||
highlight default LspSignatureActiveParameter guifg=#c8d3f5 guibg=#4ec9b0 gui=bold
|
||||
highlight default link FloatBorder Normal
|
||||
highlight default NormalFloat guibg=#1e1e1e guifg=#d4d4d4
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
Loading…
Reference in New Issue