neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit bdff50dee56ebf6de58d58315920abf2f8e262f7
parent aee4254b76206354d74d6fdd4d8e6b640279cedd
Author: Mathias Fußenegger <mfussenegger@users.noreply.github.com>
Date:   Sat, 27 Jul 2024 22:30:14 +0200

fix(lsp): revert text edit application order change (#29877)

Reverts https://github.com/neovim/neovim/pull/29212 and adds a few
additional test cases

From the spec

> All text edits ranges refer to positions in the document they are
> computed on. They therefore move a document from state S1 to S2 without
> describing any intermediate state. Text edits ranges must never overlap,
> that means no part of the original document must be manipulated by more
> than one edit. However, it is possible that multiple edits have the same
> start position: multiple inserts, or any number of inserts followed by a
> single remove or replace edit. If multiple inserts have the same
> position, the order in the array defines the order in which the inserted
> strings appear in the resulting text.

The previous fix seems wrong. The important part:

> If multiple inserts have the same position, the order in the array
> defines the order in which the inserted strings appear in the
> resulting text.

Emphasis on _appear in the resulting text_

Which means that in:

    local edits1 = {
      make_edit(0, 3, 0, 3, { 'World' }),
      make_edit(0, 3, 0, 3, { 'Hello' }),
    }

`World` must appear before `Hello` in the final text. That means the old
logic was correct, and the fix was wrong.
Diffstat:
Mruntime/lua/vim/lsp/util.lua | 5+----
Mtest/functional/plugin/lsp_spec.lua | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua @@ -396,10 +396,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if a.range.start.character ~= b.range.start.character then return a.range.start.character > b.range.start.character end - if a._index ~= b._index then - return a._index < b._index - end - return false + return a._index > b._index end) -- save and restore local marks since they get deleted by nvim_buf_set_lines diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua @@ -1756,6 +1756,58 @@ describe('LSP', function() } end + describe('apply vscode text_edits', function() + it('single replace', function() + insert('012345678901234567890123456789') + local edits = { + make_edit(0, 3, 0, 6, { 'Hello' }), + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') + eq({ '012Hello678901234567890123456789' }, buf_lines(1)) + end) + it('two replaces', function() + insert('012345678901234567890123456789') + local edits = { + make_edit(0, 3, 0, 6, { 'Hello' }), + make_edit(0, 6, 0, 9, { 'World' }), + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') + eq({ '012HelloWorld901234567890123456789' }, buf_lines(1)) + end) + it('same start pos insert are kept in order', function() + insert('012345678901234567890123456789') + local edits1 = { + make_edit(0, 3, 0, 3, { 'World' }), + make_edit(0, 3, 0, 3, { 'Hello' }), + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits1, 1, 'utf-16') + eq({ '012WorldHello345678901234567890123456789' }, buf_lines(1)) + end) + it('same start pos insert and replace are kept in order', function() + insert('012345678901234567890123456789') + local edits1 = { + make_edit(0, 3, 0, 3, { 'World' }), + make_edit(0, 3, 0, 3, { 'Hello' }), + make_edit(0, 3, 0, 8, { 'No' }), + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits1, 1, 'utf-16') + eq({ '012WorldHelloNo8901234567890123456789' }, buf_lines(1)) + end) + it('multiline', function() + exec_lua([[ + vim.api.nvim_buf_set_lines(1, 0, 0, true, {' {', ' "foo": "bar"', ' }'}) + ]]) + eq({ ' {', ' "foo": "bar"', ' }', '' }, buf_lines(1)) + local edits = { + make_edit(0, 0, 3, 0, { '' }), + make_edit(3, 0, 3, 0, { '{\n' }), + make_edit(3, 0, 3, 0, { ' "foo": "bar"\n' }), + make_edit(3, 0, 3, 0, { '}\n' }), + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') + eq({ '{', ' "foo": "bar"', '}', '' }, buf_lines(1)) + end) + end) describe('apply_text_edits', function() before_each(function() insert(dedent([[ @@ -1794,9 +1846,9 @@ describe('LSP', function() } exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ - '3', - 'foo', - '12Fbar', + '', + '123', + 'fooFbar', '123irst guy', 'baz line of text', 'The next line of text', @@ -1818,9 +1870,9 @@ describe('LSP', function() } exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ - '3', - 'foo', - '12Fbar', + '', + '123', + 'fooFbar', '123irst guy', 'baz line of text', 'The next line of text',