diff options
| author | Mike Vink <mike@pionative.com> | 2025-01-19 13:52:52 +0100 |
|---|---|---|
| committer | Mike Vink <mike@pionative.com> | 2025-01-19 13:52:52 +0100 |
| commit | b77413ff8f59f380612074f0c9bd49093d8db695 (patch) | |
| tree | 32c39a811ba96ed4ab0a1c81cce9f8d518ed7e31 /lua/blink/cmp/signature/window.lua | |
Squashed 'mut/neovim/pack/plugins/start/blink.cmp/' content from commit 1cc3b1a
git-subtree-dir: mut/neovim/pack/plugins/start/blink.cmp
git-subtree-split: 1cc3b1a908fbcfd15451c4772759549724f38524
Diffstat (limited to 'lua/blink/cmp/signature/window.lua')
| -rw-r--r-- | lua/blink/cmp/signature/window.lua | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/lua/blink/cmp/signature/window.lua b/lua/blink/cmp/signature/window.lua new file mode 100644 index 0000000..59a60b0 --- /dev/null +++ b/lua/blink/cmp/signature/window.lua @@ -0,0 +1,160 @@ +--- @class blink.cmp.SignatureWindow +--- @field win blink.cmp.Window +--- @field context? blink.cmp.SignatureHelpContext +--- +--- @field open_with_signature_help fun(context: blink.cmp.SignatureHelpContext, signature_help?: lsp.SignatureHelp) +--- @field close fun() +--- @field scroll_up fun(amount: number) +--- @field scroll_down fun(amount: number) +--- @field update_position fun() + +local config = require('blink.cmp.config').signature.window +local sources = require('blink.cmp.sources.lib') +local menu = require('blink.cmp.completion.windows.menu') + +local signature = { + win = require('blink.cmp.lib.window').new({ + min_width = config.min_width, + max_width = config.max_width, + max_height = config.max_height, + border = config.border, + winblend = config.winblend, + winhighlight = config.winhighlight, + scrollbar = config.scrollbar, + wrap = true, + filetype = 'blink-cmp-signature', + }), + context = nil, +} + +-- todo: deduplicate this +menu.position_update_emitter:on(function() signature.update_position() end) +vim.api.nvim_create_autocmd({ 'CursorMovedI', 'WinScrolled', 'WinResized' }, { + callback = function() + if signature.context then signature.update_position() end + end, +}) + +--- @param context blink.cmp.SignatureHelpContext +--- @param signature_help lsp.SignatureHelp | nil +function signature.open_with_signature_help(context, signature_help) + signature.context = context + -- check if there are any signatures in signature_help, since + -- convert_signature_help_to_markdown_lines errors with no signatures + if + signature_help == nil + or #signature_help.signatures == 0 + or signature_help.signatures[(signature_help.activeSignature or 0) + 1] == nil + then + signature.win:close() + return + end + + local active_signature = signature_help.signatures[(signature_help.activeSignature or 0) + 1] + + local labels = vim.tbl_map(function(signature) return signature.label end, signature_help.signatures) + + if signature.shown_signature ~= active_signature then + require('blink.cmp.lib.window.docs').render_detail_and_documentation({ + bufnr = signature.win:get_buf(), + detail = labels, + documentation = active_signature.documentation, + max_width = config.max_width, + use_treesitter_highlighting = config.treesitter_highlighting, + }) + end + signature.shown_signature = active_signature + + -- highlight active parameter + local _, active_highlight = vim.lsp.util.convert_signature_help_to_markdown_lines( + signature_help, + vim.bo.filetype, + sources.get_signature_help_trigger_characters().trigger_characters + ) + if active_highlight ~= nil then + -- TODO: nvim 0.11+ returns the start and end line which we should use + local start_region = vim.fn.has('nvim-0.11.0') == 1 and active_highlight[2] or active_highlight[1] + local end_region = vim.fn.has('nvim-0.11.0') == 1 and active_highlight[4] or active_highlight[2] + + vim.api.nvim_buf_add_highlight( + signature.win:get_buf(), + require('blink.cmp.config').appearance.highlight_ns, + 'BlinkCmpSignatureHelpActiveParameter', + 0, + start_region, + end_region + ) + end + + signature.win:open() + signature.update_position() +end + +function signature.close() + if not signature.win:is_open() then return end + signature.win:close() +end + +function signature.scroll_up(amount) + local winnr = signature.win:get_win() + local top_line = math.max(1, vim.fn.line('w0', winnr) - 1) + local desired_line = math.max(1, top_line - amount) + + vim.api.nvim_win_set_cursor(signature.win:get_win(), { desired_line, 0 }) +end + +function signature.scroll_down(amount) + local winnr = signature.win:get_win() + local line_count = vim.api.nvim_buf_line_count(signature.win:get_buf()) + local bottom_line = math.max(1, vim.fn.line('w$', winnr) + 1) + local desired_line = math.min(line_count, bottom_line + amount) + + vim.api.nvim_win_set_cursor(signature.win:get_win(), { desired_line, 0 }) +end + +function signature.update_position() + local win = signature.win + if not win:is_open() then return end + local winnr = win:get_win() + + win:update_size() + + local direction_priority = config.direction_priority + + -- if the menu window is open, we want to place the signature window on the opposite side + local menu_win_config = menu.win:get_win() and vim.api.nvim_win_get_config(menu.win:get_win()) + if menu.win:is_open() then + local cursor_screen_row = vim.fn.winline() + local menu_win_is_up = menu_win_config.row - cursor_screen_row < 0 + direction_priority = menu_win_is_up and { 's' } or { 'n' } + end + + local pos = win:get_vertical_direction_and_height(direction_priority) + + -- couldn't find anywhere to place the window + if not pos then + win:close() + return + end + + -- set height + vim.api.nvim_win_set_height(winnr, pos.height) + local height = win:get_height() + + -- default to the user's preference but attempt to use the other options + if menu_win_config then + assert(menu_win_config.relative == 'win', 'The menu window must be relative to a window') + local cursor_screen_row = vim.fn.winline() + local menu_win_is_up = menu_win_config.row - cursor_screen_row < 0 + vim.api.nvim_win_set_config(winnr, { + relative = menu_win_config.relative, + win = menu_win_config.win, + row = menu_win_is_up and menu_win_config.row + menu.win:get_height() + 1 or menu_win_config.row - height - 1, + col = menu_win_config.col, + }) + else + vim.api.nvim_win_set_config(winnr, { relative = 'cursor', row = pos.direction == 's' and 1 or -height, col = 0 }) + end +end + +return signature |
