function! s:set_text(lines) % delete _ put =a:lines execute 'normal ggdd' execute 'file my-file' endfunction function! s:get_text() return lsp#utils#buffer#_get_lines(bufnr('$')) endfunction Describe lsp#utils#text_edit Before all let s:endofline_backup = &endofline set endofline End After all let &endofline = s:endofline_backup End Before each enew! End Describe lsp#utils#text_edit#apply_text_edits It insert newText call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': 'baz' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'bbazar', '']) End It insert empty newText call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'bar', '']) End It replace range string to newText call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': 'replaced' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'replacedar', '']) End It replace range string to empty newText call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'ar', '']) End It single line start character is -1 call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': -1 \ }, \ 'end': { \ 'line': 1, \ 'character': 3 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', '', '']) End It single line start character is 0 call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 3 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', '', '']) End It single line start character is 1 call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 3 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'b', '']) End It single line end character is `len(getline('.')) - 1` call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 2 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', 'r', '']) End It single line end character is `len(getline('.'))` call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 3 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', '', '']) End It single line end character is `len(getline('.')) + 1` call s:set_text(['foo', 'bar']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 4 \ } \ }, \ 'newText': '' \ }]) " if newline character deleting, need end position is next line zero character. let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['foo', '', '']) End It replace range string to empty newText, multiline top to top call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 0 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['bar', 'baz', '']) End It replace range string to empty newText, multiline top to tail call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 2 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['r', 'baz', '']) call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 3 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['', 'baz', '']) call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 4 \ } \ }, \ 'newText': '' \ }]) " if newline character deleting, need end position is next line zero character. let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['', 'baz', '']) End It replace range string to empty newText, multiline middle to middle call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 2 \ } \ }, \ 'newText': '' \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['fr', 'baz', '']) End It replaces entire buffer correctly call s:set_text(['foo', 'bar', 'baz']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 3, \ 'character': 0 \ } \ }, \ 'newText': "x\ny\nz\n" \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['x', 'y', 'z', '']) End It multiple textEdit with inserting \r. " Add some text to buffer call s:set_text(['class ABC {', ' private:', ' ', 'int a;};', ]) " Format call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [ \ { \ "range": { \ "end": { \ "character": 2, \ "line": 1 \ }, \ "start": { \ "character": 11, \ "line": 0 \ } \ }, \ "newText": "\n" \ }, \ { \ "range": { \ "end": { \ "character": 0, \ "line": 3 \ }, \ "start": { \ "character": 10, \ "line": 1 \ } \ }, \ "newText": "\n " \ }, \ { \ "range": { \ "end": { \ "character": 6, \ "line": 3 \ }, \ "start": { \ "character": 6, \ "line": 3 \ } \ }, \ "newText": "\n" \ } \ ]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['class ABC {', 'private:', ' int a;', '};', '']) End It preserves v:completed_item " Add some text to buffer call s:set_text(['foo', 'bar']) " Go to end of file and invoke completion execute "normal Gof\\" " Make sure that v:completed_item is set Assert Equals(v:completed_item["word"], "foo") let l:old_completed_item = v:completed_item " Perform some text edits " Insert call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': 'baz' \ }]) " Insert empty call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': '' \ }]) " Replace call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': 'replaced' \ }]) " Delete call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': '' \ }]) " Make sure v:completed_item is not changed Assert Equals(v:completed_item, l:old_completed_item) End It replaces entire buffer correctly when end column is 1 call s:set_text(['foo', 'b']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': "x\ny\nz\n" \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['x', 'y', 'z', '', '']) End It should apply edit that contains \r\n call s:set_text(['foo', 'b']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'newText': "x\r\ny\r\nz\r\n" \ }]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['x', 'y', 'z', '', '']) End It adds imports correctly call s:set_text(['package main', '', 'import java.util.ArrayList;', '', 'public class Main {}']) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ [{ \ "range": { \ "start": { \ "character": 0, \ "line": 2 \ }, \ "end": { \ "character": 0, \ "line": 3 \ } \ }, \ "newText": "" \ }, \ { \ "range": { \ "start": { \ "character": 0, \ "line": 2 \ }, \ "end": { \ "character": 0, \ "line": 2 \ } \ }, \ "newText": "import java.util.ArrayList;\n" \ } \ ]) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['package main', '', 'import java.util.ArrayList;', '', 'public class Main {}', '']) End It adds null let l:text = ['package main', '', 'import java.util.ArrayList;', '', 'public class Main {}'] call s:set_text(l:text) call lsp#utils#text_edit#apply_text_edits( \ expand('%'), \ v:null) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, l:text + ['']) End It should apply edits to unloaded file let l:target = globpath(&runtimepath, 'test/lsp/utils/text_edit.vimspec') call themis#log(l:target) call lsp#utils#text_edit#apply_text_edits(lsp#utils#path_to_uri(l:target), [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0, \ }, \ 'end': { \ 'line': 0, \ 'character': 0, \ } \ }, \ 'newText': "aiueo\n" \ }]) Assert Equals(getbufline(l:target, 1), ['aiueo']) End It should apply edits to buffer and unloaded file let l:text = ['plop'] call s:set_text(l:text) let l:buffer_text = s:get_text() Assert Equals(l:buffer_text, ['plop', '']) let l:target = globpath(&runtimepath, 'test/lsp/utils/text_edit.vimspec') call lsp#utils#text_edit#apply_text_edits( \ lsp#utils#path_to_uri(expand('%')), \ [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0, \ }, \ 'end': { \ 'line': 1, \ 'character': 0, \ } \ }, \ 'newText': "buffer\n" \ }]) call lsp#utils#text_edit#apply_text_edits(lsp#utils#path_to_uri(l:target), [{ \ 'range': { \ 'start': { \ 'line': 0, \ 'character': 0, \ }, \ 'end': { \ 'line': 1, \ 'character': 0, \ } \ }, \ 'newText': "unloaded\n" \ }]) Assert Equals(getbufline(l:target, 1), ['unloaded']) Assert Equals(getbufline(expand('%'), 1), ['buffer']) End End End