From b868257ef905ddffa21ab060958f2f1027a2bc2a Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Thu, 22 May 2025 08:22:47 -0500 Subject: [PATCH] fix(lsp): fix error with InsertReplaceEdit events #33973 Problem: Some LSPs cause the following completion error (reformatted slightly): Error executing vim.schedule lua callback: .../runtime/lua/vim/lsp/completion.lua:373 attempt to index field 'range' (a nil value) This is because an internal function assumes edits are either missing or of type `TextEdit`, but there's a third [possibility][0] that's not handled: the `InsertReplaceEdit`. This was previously reported in at least two issues: - https://github.com/neovim/neovim/issues/33142 - https://github.com/neovim/neovim/issues/33224 Solution: Don't assume the edit is a `TextEdit`. This implicitly handles `InsertReplaceEdit`s. Also, add a test case for this, which previously caused an error. [0]: https://github.com/neovim/neovim/blob/2c07428966a74c76003e00e2a37bf98eb8802c93/runtime/lua/vim/lsp/_meta/protocol.lua#L1099 (cherry picked from commit 927927e143bd365264d7a4ef59ad1d2425006cba) --- runtime/lua/vim/lsp/completion.lua | 2 +- .../functional/plugin/lsp/completion_spec.lua | 53 ++++++++++++++----- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index d905337abb..60574a94cd 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -370,7 +370,7 @@ end local function adjust_start_col(lnum, line, items, encoding) local min_start_char = nil for _, item in pairs(items) do - if item.textEdit and item.textEdit.range.start.line == lnum then + if item.textEdit and item.textEdit.range and item.textEdit.range.start.line == lnum then if min_start_char and min_start_char ~= item.textEdit.range.start.character then return nil end diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index 7772a47589..af4e7d401e 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -618,24 +618,53 @@ describe('vim.lsp.completion: item conversion', function() }, }, }, + { + label = 'insert_replace_edit', + kind = 9, + textEdit = { + newText = 'foobar', + insert = { + start = { line = 0, character = 7 }, + ['end'] = { line = 0, character = 11 }, + }, + replace = { + start = { line = 0, character = 0 }, + ['end'] = { line = 0, character = 0 }, + }, + }, + }, }, } local expected = { - abbr = ' this_thread', - dup = 1, - empty = 1, - icase = 1, - info = '', - kind = 'Module', - menu = '', - abbr_hlgroup = '', - word = 'this_thread', + { + abbr = ' this_thread', + dup = 1, + empty = 1, + icase = 1, + info = '', + kind = 'Module', + menu = '', + abbr_hlgroup = '', + word = 'this_thread', + }, + { + abbr = 'insert_replace_edit', + dup = 1, + empty = 1, + icase = 1, + info = '', + kind = 'Module', + menu = '', + abbr_hlgroup = '', + word = 'foobar', + }, } local result = complete(' std::this|', completion_list) eq(7, result.server_start_boundary) - local item = result.items[1] - item.user_data = nil - eq(expected, item) + for _, item in ipairs(result.items) do + item.user_data = nil + end + eq(expected, result.items) end) it('should search from start boundary to cursor position', function()