diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index e74f993356..14fad987e0 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -113,12 +113,11 @@ local function parse_snippet(input) end --- @param item lsp.CompletionItem ---- @param suffix? string -local function apply_snippet(item, suffix) +local function apply_snippet(item) if item.textEdit then - vim.snippet.expand(item.textEdit.newText .. suffix) + vim.snippet.expand(item.textEdit.newText) elseif item.insertText then - vim.snippet.expand(item.insertText .. suffix) + vim.snippet.expand(item.insertText) end end @@ -539,15 +538,12 @@ local function on_complete_done() -- Remove the already inserted word. local start_char = cursor_col - #completed_item.word - local line = api.nvim_buf_get_lines(bufnr, cursor_row, cursor_row + 1, true)[1] - api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, #line, { '' }) - return line:sub(cursor_col + 1) + api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, cursor_col, { '' }) end - --- @param suffix? string - local function apply_snippet_and_command(suffix) + local function apply_snippet_and_command() if expand_snippet then - apply_snippet(completion_item, suffix) + apply_snippet(completion_item) end local command = completion_item.command @@ -565,9 +561,9 @@ local function on_complete_done() end if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then - local suffix = clear_word() + clear_word() lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding) - apply_snippet_and_command(suffix) + apply_snippet_and_command() elseif resolve_provider and type(completion_item) == 'table' then local changedtick = vim.b[bufnr].changedtick @@ -577,7 +573,7 @@ local function on_complete_done() return end - local suffix = clear_word() + clear_word() if err then vim.notify_once(err.message, vim.log.levels.WARN) elseif result and result.additionalTextEdits then @@ -587,11 +583,11 @@ local function on_complete_done() end end - apply_snippet_and_command(suffix) + apply_snippet_and_command() end, bufnr) else - local suffix = clear_word() - apply_snippet_and_command(suffix) + clear_word() + apply_snippet_and_command() end end diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index cb49526f56..ec2f88ec32 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -471,6 +471,39 @@ describe('vim.lsp.completion: item conversion', function() ) end) +--- @param completion_result lsp.CompletionList +--- @return integer +local function create_server(completion_result) + return exec_lua(function() + local server = _G._create_server({ + capabilities = { + completionProvider = { + triggerCharacters = { '.' }, + }, + }, + handlers = { + ['textDocument/completion'] = function(_, _, callback) + callback(nil, completion_result) + end, + }, + }) + + local bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + return vim.lsp.start({ + name = 'dummy', + cmd = server.cmd, + on_attach = function(client, bufnr0) + vim.lsp.completion.enable(true, client.id, bufnr0, { + convert = function(item) + return { abbr = item.label:gsub('%b()', '') } + end, + }) + end, + }) + end) +end + describe('vim.lsp.completion: protocol', function() before_each(function() clear() @@ -487,39 +520,6 @@ describe('vim.lsp.completion: protocol', function() after_each(clear) - --- @param completion_result lsp.CompletionList - --- @return integer - local function create_server(completion_result) - return exec_lua(function() - local server = _G._create_server({ - capabilities = { - completionProvider = { - triggerCharacters = { '.' }, - }, - }, - handlers = { - ['textDocument/completion'] = function(_, _, callback) - callback(nil, completion_result) - end, - }, - }) - - local bufnr = vim.api.nvim_get_current_buf() - vim.api.nvim_win_set_buf(0, bufnr) - return vim.lsp.start({ - name = 'dummy', - cmd = server.cmd, - on_attach = function(client, bufnr0) - vim.lsp.completion.enable(true, client.id, bufnr0, { - convert = function(item) - return { abbr = item.label:gsub('%b()', '') } - end, - }) - end, - }) - end) - end - local function assert_matches(fn) retry(nil, nil, function() fn(exec_lua('return _G.capture.matches')) @@ -726,3 +726,58 @@ describe('vim.lsp.completion: protocol', function() end) end) end) + +describe('vim.lsp.completion: integration', function() + before_each(function() + clear() + exec_lua(create_server_definition) + exec_lua(function() + vim.fn.complete = vim.schedule_wrap(vim.fn.complete) + end) + end) + + after_each(clear) + + it('puts cursor at the end of completed word', function() + local completion_list = { + isIncomplete = false, + items = { + { + label = 'hello', + insertText = '${1:hello} friends', + insertTextFormat = 2, + }, + }, + } + exec_lua(function() + vim.o.completeopt = 'menuone,noselect' + end) + create_server(completion_list) + feed('i world0ih') + retry(nil, nil, function() + eq( + 1, + exec_lua(function() + return vim.fn.pumvisible() + end) + ) + end) + feed('') + eq( + { true, { 'hello friends world' } }, + exec_lua(function() + return { + vim.snippet.active({ direction = 1 }), + vim.api.nvim_buf_get_lines(0, 0, -1, true), + } + end) + ) + feed('') + eq( + #'hello friends', + exec_lua(function() + return vim.api.nvim_win_get_cursor(0)[2] + end) + ) + end) +end)