1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
|