summaryrefslogtreecommitdiff
path: root/lua/blink/cmp/signature/window.lua
diff options
context:
space:
mode:
authorMike Vink <mike@pionative.com>2025-01-19 13:52:52 +0100
committerMike Vink <mike@pionative.com>2025-01-19 13:52:52 +0100
commitb77413ff8f59f380612074f0c9bd49093d8db695 (patch)
tree32c39a811ba96ed4ab0a1c81cce9f8d518ed7e31 /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.lua160
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