summaryrefslogtreecommitdiff
path: root/lua/blink/cmp/signature/trigger.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/blink/cmp/signature/trigger.lua')
-rw-r--r--lua/blink/cmp/signature/trigger.lua136
1 files changed, 136 insertions, 0 deletions
diff --git a/lua/blink/cmp/signature/trigger.lua b/lua/blink/cmp/signature/trigger.lua
new file mode 100644
index 0000000..2d874c5
--- /dev/null
+++ b/lua/blink/cmp/signature/trigger.lua
@@ -0,0 +1,136 @@
+-- Handles hiding and showing the signature help window. When a user types a trigger character
+-- (provided by the sources), we create a new `context`. This can be used downstream to determine
+-- if we should make new requests to the sources or not. When a user types a re-trigger character,
+-- we update the context's re-trigger counter.
+-- TODO: ensure this always calls *after* the completion trigger to avoid increasing latency
+
+--- @class blink.cmp.SignatureHelpContext
+--- @field id number
+--- @field bufnr number
+--- @field cursor number[]
+--- @field line string
+--- @field is_retrigger boolean
+--- @field active_signature_help lsp.SignatureHelp | nil
+--- @field trigger { kind: lsp.SignatureHelpTriggerKind, character?: string }
+
+--- @class blink.cmp.SignatureTrigger
+--- @field current_context_id number
+--- @field context? blink.cmp.SignatureHelpContext
+--- @field show_emitter blink.cmp.EventEmitter<{ context: blink.cmp.SignatureHelpContext }>
+--- @field hide_emitter blink.cmp.EventEmitter<{}>
+--- @field buffer_events? blink.cmp.BufferEvents
+---
+--- @field activate fun()
+--- @field is_trigger_character fun(char: string, is_retrigger?: boolean): boolean
+--- @field show_if_on_trigger_character fun()
+--- @field show fun(opts?: { trigger_character: string })
+--- @field hide fun()
+--- @field set_active_signature_help fun(signature_help: lsp.SignatureHelp)
+
+local config = require('blink.cmp.config').signature.trigger
+
+--- @type blink.cmp.SignatureTrigger
+--- @diagnostic disable-next-line: missing-fields
+local trigger = {
+ current_context_id = -1,
+ --- @type blink.cmp.SignatureHelpContext | nil
+ context = nil,
+ show_emitter = require('blink.cmp.lib.event_emitter').new('signature_help_show'),
+ hide_emitter = require('blink.cmp.lib.event_emitter').new('signature_help_hide'),
+}
+
+function trigger.activate()
+ trigger.buffer_events = require('blink.cmp.lib.buffer_events').new({
+ show_in_snippet = true,
+ has_context = function() return trigger.context ~= nil end,
+ })
+ trigger.buffer_events:listen({
+ on_char_added = function()
+ local cursor_col = vim.api.nvim_win_get_cursor(0)[2]
+ local char_under_cursor = vim.api.nvim_get_current_line():sub(cursor_col, cursor_col)
+
+ -- ignore if disabled
+ if not require('blink.cmp.config').enabled() then
+ return trigger.hide()
+ -- character forces a trigger according to the sources, refresh the existing context if it exists
+ elseif trigger.is_trigger_character(char_under_cursor) then
+ return trigger.show({ trigger_character = char_under_cursor })
+ -- character forces a re-trigger according to the sources, show if we have a context
+ elseif trigger.is_trigger_character(char_under_cursor, true) and trigger.context ~= nil then
+ return trigger.show()
+ end
+ end,
+ on_cursor_moved = function(event)
+ local cursor_col = vim.api.nvim_win_get_cursor(0)[2]
+ local char_under_cursor = vim.api.nvim_get_current_line():sub(cursor_col, cursor_col)
+ local is_on_trigger = trigger.is_trigger_character(char_under_cursor)
+
+ if config.show_on_insert_on_trigger_character and is_on_trigger and event == 'InsertEnter' then
+ trigger.show({ trigger_character = char_under_cursor })
+ elseif event == 'CursorMoved' and trigger.context ~= nil then
+ trigger.show()
+ end
+ end,
+ on_insert_leave = function() trigger.hide() end,
+ })
+end
+
+function trigger.is_trigger_character(char, is_retrigger)
+ -- TODO: should the get_mode() be moved to sources or somewhere else?
+ local mode = require('blink.cmp.completion.trigger.context').get_mode()
+
+ local res = require('blink.cmp.sources.lib').get_signature_help_trigger_characters(mode)
+ local trigger_characters = is_retrigger and res.retrigger_characters or res.trigger_characters
+ local is_trigger = vim.tbl_contains(trigger_characters, char)
+
+ local blocked_trigger_characters = is_retrigger and config.blocked_retrigger_characters
+ or config.blocked_trigger_characters
+ local is_blocked = vim.tbl_contains(blocked_trigger_characters, char)
+
+ return is_trigger and not is_blocked
+end
+
+function trigger.show_if_on_trigger_character()
+ if require('blink.cmp.completion.trigger.context').get_mode() ~= 'default' then return end
+
+ local cursor_col = vim.api.nvim_win_get_cursor(0)[2]
+ local char_under_cursor = vim.api.nvim_get_current_line():sub(cursor_col, cursor_col)
+ if trigger.is_trigger_character(char_under_cursor) then trigger.show({ trigger_character = char_under_cursor }) end
+end
+
+function trigger.show(opts)
+ opts = opts or {}
+
+ -- update context
+ local cursor = vim.api.nvim_win_get_cursor(0)
+ if trigger.context == nil then trigger.current_context_id = trigger.current_context_id + 1 end
+ trigger.context = {
+ id = trigger.current_context_id,
+ bufnr = vim.api.nvim_get_current_buf(),
+ cursor = cursor,
+ line = vim.api.nvim_buf_get_lines(0, cursor[1] - 1, cursor[1], false)[1],
+ trigger = {
+ kind = opts.trigger_character and vim.lsp.protocol.CompletionTriggerKind.TriggerCharacter
+ or vim.lsp.protocol.CompletionTriggerKind.Invoked,
+ character = opts.trigger_character,
+ },
+ is_retrigger = trigger.context ~= nil,
+ active_signature_help = trigger.context and trigger.context.active_signature_help or nil,
+ }
+
+ trigger.show_emitter:emit({ context = trigger.context })
+end
+
+function trigger.hide()
+ if not trigger.context then return end
+
+ trigger.context = nil
+ trigger.hide_emitter:emit()
+end
+
+function trigger.set_active_signature_help(signature_help)
+ if not trigger.context then return end
+ trigger.context.active_signature_help = signature_help
+end
+
+return trigger