summaryrefslogtreecommitdiff
path: root/mut/neovim/pack/plugins/start/blink.cmp/lua/blink/cmp/signature/window.lua
blob: 59a60b0f78a2a9505552001217b56642e1113478 (plain)
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