--- /dev/null
+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\<C-p>\<Esc>"
+
+ " 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
+