mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
fix(lsp): fix cursor position after snippet expansion (#30659)
Problem: on `CompleteDone` cursor can jump to the end of line instead of the end of the completed word. Solution: remove only inserted word for snippet expansion instead of everything until eol. Fixes #30656 Co-authored-by: Mathias Fussenegger <f.mathias@zignar.net> Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
parent
641c4b1a2a
commit
b3109084c2
@ -113,12 +113,11 @@ local function parse_snippet(input)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @param item lsp.CompletionItem
|
--- @param item lsp.CompletionItem
|
||||||
--- @param suffix? string
|
local function apply_snippet(item)
|
||||||
local function apply_snippet(item, suffix)
|
|
||||||
if item.textEdit then
|
if item.textEdit then
|
||||||
vim.snippet.expand(item.textEdit.newText .. suffix)
|
vim.snippet.expand(item.textEdit.newText)
|
||||||
elseif item.insertText then
|
elseif item.insertText then
|
||||||
vim.snippet.expand(item.insertText .. suffix)
|
vim.snippet.expand(item.insertText)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -539,15 +538,12 @@ local function on_complete_done()
|
|||||||
|
|
||||||
-- Remove the already inserted word.
|
-- Remove the already inserted word.
|
||||||
local start_char = cursor_col - #completed_item.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, cursor_col, { '' })
|
||||||
api.nvim_buf_set_text(bufnr, cursor_row, start_char, cursor_row, #line, { '' })
|
|
||||||
return line:sub(cursor_col + 1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param suffix? string
|
local function apply_snippet_and_command()
|
||||||
local function apply_snippet_and_command(suffix)
|
|
||||||
if expand_snippet then
|
if expand_snippet then
|
||||||
apply_snippet(completion_item, suffix)
|
apply_snippet(completion_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
local command = completion_item.command
|
local command = completion_item.command
|
||||||
@ -565,9 +561,9 @@ local function on_complete_done()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then
|
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)
|
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
|
elseif resolve_provider and type(completion_item) == 'table' then
|
||||||
local changedtick = vim.b[bufnr].changedtick
|
local changedtick = vim.b[bufnr].changedtick
|
||||||
|
|
||||||
@ -577,7 +573,7 @@ local function on_complete_done()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local suffix = clear_word()
|
clear_word()
|
||||||
if err then
|
if err then
|
||||||
vim.notify_once(err.message, vim.log.levels.WARN)
|
vim.notify_once(err.message, vim.log.levels.WARN)
|
||||||
elseif result and result.additionalTextEdits then
|
elseif result and result.additionalTextEdits then
|
||||||
@ -587,11 +583,11 @@ local function on_complete_done()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
apply_snippet_and_command(suffix)
|
apply_snippet_and_command()
|
||||||
end, bufnr)
|
end, bufnr)
|
||||||
else
|
else
|
||||||
local suffix = clear_word()
|
clear_word()
|
||||||
apply_snippet_and_command(suffix)
|
apply_snippet_and_command()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -471,6 +471,39 @@ describe('vim.lsp.completion: item conversion', function()
|
|||||||
)
|
)
|
||||||
end)
|
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()
|
describe('vim.lsp.completion: protocol', function()
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
@ -487,39 +520,6 @@ describe('vim.lsp.completion: protocol', function()
|
|||||||
|
|
||||||
after_each(clear)
|
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)
|
local function assert_matches(fn)
|
||||||
retry(nil, nil, function()
|
retry(nil, nil, function()
|
||||||
fn(exec_lua('return _G.capture.matches'))
|
fn(exec_lua('return _G.capture.matches'))
|
||||||
@ -726,3 +726,58 @@ describe('vim.lsp.completion: protocol', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
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 world<esc>0ih<c-x><c-o>')
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.fn.pumvisible()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
feed('<C-n><C-y>')
|
||||||
|
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('<tab>')
|
||||||
|
eq(
|
||||||
|
#'hello friends',
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.api.nvim_win_get_cursor(0)[2]
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user