diff --git a/lua/blink/cmp/accept/init.lua b/lua/blink/cmp/accept/init.lua index 98cd1d78..5aa0ccf9 100644 --- a/lua/blink/cmp/accept/init.lua +++ b/lua/blink/cmp/accept/init.lua @@ -6,55 +6,63 @@ local brackets_lib = require('blink.cmp.accept.brackets') local function accept(item) require('blink.cmp.trigger.completion').hide() - -- create an undo point - if require('blink.cmp.config').accept.create_undo_point then - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('u', true, true, true), 'n', true) - end + -- start the resolve immediately since text changes can invalidate the item + -- with some LSPs (i.e. rust-analyzer) causing them to return the item as-is + -- without i.e. auto-imports + require('blink.cmp.sources.lib').resolve(item):map(function(resolved_item) + local all_text_edits = + vim.deepcopy(resolved_item and resolved_item.additionalTextEdits or item.additionalTextEdits or {}) - item = vim.deepcopy(item) - item.textEdit = text_edits_lib.get_from_item(item) + -- create an undo point + if require('blink.cmp.config').accept.create_undo_point then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('u', true, true, true), 'n', true) + end - -- Add brackets to the text edit if needed - local brackets_status, text_edit_with_brackets, offset = brackets_lib.add_brackets(vim.bo.filetype, item) - item.textEdit = text_edit_with_brackets + item = vim.deepcopy(item) + item.textEdit = text_edits_lib.get_from_item(item) - -- Snippet - if item.insertTextFormat == vim.lsp.protocol.InsertTextFormat.Snippet then - -- We want to handle offset_encoding and the text edit api can do this for us - -- so we empty the newText and apply - local temp_text_edit = vim.deepcopy(item.textEdit) - temp_text_edit.newText = '' - text_edits_lib.apply_text_edits(item.client_id, { temp_text_edit }) + -- Add brackets to the text edit if needed + local brackets_status, text_edit_with_brackets, offset = brackets_lib.add_brackets(vim.bo.filetype, item) + item.textEdit = text_edit_with_brackets - -- Expand the snippet - vim.snippet.expand(item.textEdit.newText) + -- Snippet + if item.insertTextFormat == vim.lsp.protocol.InsertTextFormat.Snippet then + -- We want to handle offset_encoding and the text edit api can do this for us + -- so we empty the newText and apply + local temp_text_edit = vim.deepcopy(item.textEdit) + temp_text_edit.newText = '' + table.insert(all_text_edits, temp_text_edit) + text_edits_lib.apply_text_edits(item.client_id, all_text_edits) + + -- Expand the snippet + vim.snippet.expand(item.textEdit.newText) -- OR Normal: Apply the text edit and move the cursor - else - text_edits_lib.apply_text_edits(item.client_id, { item.textEdit }) - vim.api.nvim_win_set_cursor(0, { - item.textEdit.range.start.line + 1, - item.textEdit.range.start.character + #item.textEdit.newText + offset, - }) - end - - -- Check semantic tokens for brackets, if needed, and apply additional text edits - if brackets_status == 'check_semantic_token' then - -- todo: since we apply the additional text edits after, auto imported functions will not - -- get auto brackets. If we apply them before, we have to modify the textEdit to compensate - brackets_lib.add_brackets_via_semantic_token(vim.bo.filetype, item, function() + else + table.insert(all_text_edits, item.textEdit) + text_edits_lib.apply_text_edits(item.client_id, all_text_edits) + vim.api.nvim_win_set_cursor(0, { + item.textEdit.range.start.line + 1, + item.textEdit.range.start.character + #item.textEdit.newText + offset, + }) + end + + -- Check semantic tokens for brackets, if needed, and apply additional text edits + if brackets_status == 'check_semantic_token' then + -- todo: since we apply the additional text edits after, auto imported functions will not + -- get auto brackets. If we apply them before, we have to modify the textEdit to compensate + brackets_lib.add_brackets_via_semantic_token(vim.bo.filetype, item, function() + require('blink.cmp.trigger.completion').show_if_on_trigger_character() + require('blink.cmp.trigger.signature').show_if_on_trigger_character() + end) + else require('blink.cmp.trigger.completion').show_if_on_trigger_character() require('blink.cmp.trigger.signature').show_if_on_trigger_character() - text_edits_lib.apply_additional_text_edits(item) - end) - else - require('blink.cmp.trigger.completion').show_if_on_trigger_character() - require('blink.cmp.trigger.signature').show_if_on_trigger_character() - text_edits_lib.apply_additional_text_edits(item) - end - - -- Notify the rust module that the item was accessed - require('blink.cmp.fuzzy').access(item) + end + + -- Notify the rust module that the item was accessed + require('blink.cmp.fuzzy').access(item) + end) end return accept diff --git a/lua/blink/cmp/accept/text-edits.lua b/lua/blink/cmp/accept/text-edits.lua index f6af37b4..4971b5fc 100644 --- a/lua/blink/cmp/accept/text-edits.lua +++ b/lua/blink/cmp/accept/text-edits.lua @@ -55,22 +55,6 @@ function text_edits.undo_text_edit(text_edit) vim.lsp.util.apply_text_edits({ text_edit }, vim.api.nvim_get_current_buf(), 'utf-16') end ---- @param item blink.cmp.CompletionItem -function text_edits.apply_additional_text_edits(item) - -- Apply additional text edits - -- LSPs can either include these in the initial response or require a resolve - -- These are used for things like auto-imports - -- todo: if the main text edit was before this text edit, do we need to compensate? - if item.additionalTextEdits ~= nil and next(item.additionalTextEdits) ~= nil then - text_edits.apply_text_edits(item.client_id, item.additionalTextEdits) - else - require('blink.cmp.sources.lib').resolve(item, function(resolved_item) - resolved_item = resolved_item or item - text_edits.apply_text_edits(resolved_item.client_id, resolved_item.additionalTextEdits or {}) - end) - end -end - --- @param item blink.cmp.CompletionItem --- todo: doesnt work when the item contains characters not included in the context regex function text_edits.guess_text_edit(item) diff --git a/lua/blink/cmp/sources/lib/init.lua b/lua/blink/cmp/sources/lib/init.lua index 2a2485d4..c1094ebd 100644 --- a/lua/blink/cmp/sources/lib/init.lua +++ b/lua/blink/cmp/sources/lib/init.lua @@ -15,7 +15,7 @@ local config = require('blink.cmp.config') --- @field cancel_completions fun() --- @field listen_on_completions fun(callback: fun(context: blink.cmp.Context, items: blink.cmp.CompletionItem[])) --- @field apply_max_items_for_completions fun(context: blink.cmp.Context, items: blink.cmp.CompletionItem[]): blink.cmp.CompletionItem[] ---- @field resolve fun(item: blink.cmp.CompletionItem, callback: fun(resolved_item: lsp.CompletionItem | nil)): (fun(): nil) | nil +--- @field resolve fun(item: blink.cmp.CompletionItem): blink.cmp.Task --- @field get_signature_help_trigger_characters fun(): { trigger_characters: string[], retrigger_characters: string[] } --- @field get_signature_help fun(context: blink.cmp.SignatureHelpContext, callback: fun(signature_help: lsp.SignatureHelp | nil)): (fun(): nil) | nil --- @field cancel_signature_help fun() @@ -143,7 +143,7 @@ end --- Resolve --- -function sources.resolve(item, callback) +function sources.resolve(item) local item_source = nil for _, source in pairs(sources.providers) do if source.id == item.source_id then @@ -151,15 +151,9 @@ function sources.resolve(item, callback) break end end + if item_source == nil then return async.task.new(function(resolve) resolve() end) end - if item_source == nil then - callback(nil) - return function() end - end - return item_source:resolve(item):map(function(resolved_item) callback(resolved_item) end):catch(function(err) - vim.print('failed to resolve item with error: ' .. err) - callback(nil) - end) + return item_source:resolve(item):catch(function(err) vim.print('failed to resolve item with error: ' .. err) end) end --- Signature help --- @@ -239,7 +233,6 @@ function sources.get_lsp_capabilities(override, include_nvim_defaults) 'documentation', 'detail', 'additionalTextEdits', - 'textEdits', -- todo: support more properties? should test if it improves latency }, },