2550f5d952
* code_action: Don't move cursor when change covers entire file * code_action: Refactor/simplify ApplyChanges * code_action: Fix EOL at EOF corner cases while performing no changes * code_action: Fix column around EOL corner cases * code_action: Handle positions out of bounds * code_action: Add instructions for verifying corner case tests against vscode
179 lines
8.4 KiB
Text
179 lines
8.4 KiB
Text
" Tests for various corner cases of applying code changes from LSP.
|
|
"
|
|
" These can be verified against the reference vscode implementation using the
|
|
" following javascript program:
|
|
"
|
|
" const { TextDocument } = require('vscode-languageserver-textdocument');
|
|
" const { TextEdit, Position, Range } = require('vscode-languageserver-types');
|
|
" function MkPos(line, offset) { return Position.create(line - 1, offset - 1); }
|
|
" function MkInsert(pos, newText) { return TextEdit.insert(pos, newText); }
|
|
" function MkDelete(start, end) { return TextEdit.del(Range.create(start, end)); }
|
|
" function TestChanges(s, es) {
|
|
" return TextDocument.applyEdits(TextDocument.create(null, null, null, s), es);
|
|
" }
|
|
"
|
|
" const fs = require("fs");
|
|
" const assert = require('assert').strict;
|
|
" const testRegex = /(?<!vscode skip.*)AssertEqual\s+("[^"]*"),\s*TestChanges\(("[^"]*"),\s*(\[[^\]]*\])/g;
|
|
" const data = fs.readFileSync(0, "utf-8");
|
|
" const tests = data.matchAll(testRegex);
|
|
" for (const test of tests) {
|
|
" console.log(test[0]);
|
|
" assert.equal(eval(test[1]), TestChanges(eval(test[2]), eval(test[3])));
|
|
" }
|
|
"
|
|
" Save it to test_code_action_corner_cases.js and invoke it using:
|
|
"
|
|
" $ npm install vscode-languageserver-{textdocument,types}
|
|
" $ node test_code_action_corner_cases.js <test_code_action_corner_cases.vader
|
|
|
|
Before:
|
|
Save &fixeol
|
|
set nofixeol
|
|
|
|
Save &fileformats
|
|
set fileformats=unix
|
|
|
|
" two files, one accessed through a buffer, the other using write/readfile only
|
|
let g:files = [tempname(), tempname()]
|
|
|
|
function! TestChanges(contents, changes, mode) abort
|
|
let l:file = g:files[a:mode is 'file' ? 0 : 1]
|
|
call writefile(split(a:contents, '\n', 1), l:file, 'bS')
|
|
if a:mode isnot 'file'
|
|
execute 'edit ' . l:file
|
|
endif
|
|
call ale#code_action#ApplyChanges(l:file, a:changes, a:mode isnot 'buffer')
|
|
if a:mode is 'buffer'
|
|
execute 'write ' . l:file
|
|
endif
|
|
return join(readfile(l:file, 'b'), "\n")
|
|
endfunction!
|
|
|
|
function! MkPos(line, offset) abort
|
|
return {'line': a:line, 'offset': a:offset}
|
|
endfunction!
|
|
|
|
function! MkInsert(pos, newText) abort
|
|
return {'start': a:pos, 'end': a:pos, 'newText': a:newText}
|
|
endfunction!
|
|
|
|
function! MkDelete(start, end) abort
|
|
return {'start': a:start, 'end': a:end, 'newText': ''}
|
|
endfunction!
|
|
|
|
After:
|
|
for g:file in g:files
|
|
if bufnr(g:file) != -1
|
|
execute ':bp! | :bd! ' . bufnr(g:file)
|
|
endif
|
|
if filereadable(g:file)
|
|
call delete(g:file)
|
|
endif
|
|
endfor
|
|
unlet! g:files g:file
|
|
|
|
unlet! g:mode
|
|
|
|
delfunction TestChanges
|
|
delfunction MkPos
|
|
delfunction MkInsert
|
|
delfunction MkDelete
|
|
|
|
Restore
|
|
|
|
Execute(Preserve (no)eol at eof):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "noeol", TestChanges("noeol", [], g:mode)
|
|
AssertEqual "eol\n", TestChanges("eol\n", [], g:mode)
|
|
AssertEqual "eols\n\n", TestChanges("eols\n\n", [], g:mode)
|
|
endfor
|
|
|
|
" there doesn't seem to be a way to tell if a buffer is empty or contains one
|
|
" empty line :-(
|
|
AssertEqual "", TestChanges("", [], 'file')
|
|
|
|
Execute(Respect fixeol):
|
|
set fixeol
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
silent echo "vscode skip" | AssertEqual "noeol\n", TestChanges("noeol", [], g:mode)
|
|
silent echo "vscode skip" | AssertEqual "eol\n", TestChanges("eol\n", [], g:mode)
|
|
endfor
|
|
|
|
Execute(Add/del eol at eof):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "addeol\n", TestChanges("addeol", [MkInsert(MkPos(1, 7), "\n")], g:mode)
|
|
AssertEqual "deleol", TestChanges("deleol\n", [MkDelete(MkPos(1, 7), MkPos(1, 8))], g:mode)
|
|
endfor
|
|
|
|
Execute(One character insertions to first line):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "xabc\ndef1\nghi\n", TestChanges("abc\ndef1\nghi\n", [MkInsert(MkPos(1, 0), "x")], g:mode)
|
|
AssertEqual "xabc\ndef2\nghi\n", TestChanges("abc\ndef2\nghi\n", [MkInsert(MkPos(1, 1), "x")], g:mode)
|
|
AssertEqual "axbc\ndef3\nghi\n", TestChanges("abc\ndef3\nghi\n", [MkInsert(MkPos(1, 2), "x")], g:mode)
|
|
AssertEqual "abcx\ndef4\nghi\n", TestChanges("abc\ndef4\nghi\n", [MkInsert(MkPos(1, 4), "x")], g:mode)
|
|
AssertEqual "abc\nxdef5\nghi\n", TestChanges("abc\ndef5\nghi\n", [MkInsert(MkPos(1, 5), "x")], g:mode)
|
|
AssertEqual "abc\nxdef6\nghi\n", TestChanges("abc\ndef6\nghi\n", [MkInsert(MkPos(1, 6), "x")], g:mode)
|
|
endfor
|
|
|
|
Execute(One character + newline insertions to first line):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "x\nabc\ndef1\nghi\n", TestChanges("abc\ndef1\nghi\n", [MkInsert(MkPos(1, 0), "x\n")], g:mode)
|
|
AssertEqual "x\nabc\ndef2\nghi\n", TestChanges("abc\ndef2\nghi\n", [MkInsert(MkPos(1, 1), "x\n")], g:mode)
|
|
AssertEqual "ax\nbc\ndef3\nghi\n", TestChanges("abc\ndef3\nghi\n", [MkInsert(MkPos(1, 2), "x\n")], g:mode)
|
|
AssertEqual "abcx\n\ndef4\nghi\n", TestChanges("abc\ndef4\nghi\n", [MkInsert(MkPos(1, 4), "x\n")], g:mode)
|
|
AssertEqual "abc\nx\ndef5\nghi\n", TestChanges("abc\ndef5\nghi\n", [MkInsert(MkPos(1, 5), "x\n")], g:mode)
|
|
AssertEqual "abc\nx\ndef6\nghi\n", TestChanges("abc\ndef6\nghi\n", [MkInsert(MkPos(1, 6), "x\n")], g:mode)
|
|
endfor
|
|
|
|
Execute(One character insertions near end):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "abc\ndef1\nghxi\n", TestChanges("abc\ndef1\nghi\n", [MkInsert(MkPos(3, 3), "x")], g:mode)
|
|
AssertEqual "abc\ndef2\nghix\n", TestChanges("abc\ndef2\nghi\n", [MkInsert(MkPos(3, 4), "x")], g:mode)
|
|
AssertEqual "abc\ndef3\nghi\nx", TestChanges("abc\ndef3\nghi\n", [MkInsert(MkPos(3, 5), "x")], g:mode)
|
|
AssertEqual "abc\ndef4\nghi\nx", TestChanges("abc\ndef4\nghi\n", [MkInsert(MkPos(3, 6), "x")], g:mode)
|
|
AssertEqual "abc\ndef5\nghi\nx", TestChanges("abc\ndef5\nghi\n", [MkInsert(MkPos(4, 1), "x")], g:mode)
|
|
AssertEqual "abc\ndef6\nghi\nx", TestChanges("abc\ndef6\nghi\n", [MkInsert(MkPos(4, 2), "x")], g:mode)
|
|
AssertEqual "abc\ndef7\nghi\nx", TestChanges("abc\ndef7\nghi\n", [MkInsert(MkPos(5, 1), "x")], g:mode)
|
|
AssertEqual "abc\ndef8\nghi\nx", TestChanges("abc\ndef8\nghi\n", [MkInsert(MkPos(5, 2), "x")], g:mode)
|
|
endfor
|
|
|
|
Execute(One character + newline insertions near end):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "abc\ndef1\nghx\ni\n", TestChanges("abc\ndef1\nghi\n", [MkInsert(MkPos(3, 3), "x\n")], g:mode)
|
|
AssertEqual "abc\ndef2\nghix\n\n", TestChanges("abc\ndef2\nghi\n", [MkInsert(MkPos(3, 4), "x\n")], g:mode)
|
|
AssertEqual "abc\ndef3\nghi\nx\n", TestChanges("abc\ndef3\nghi\n", [MkInsert(MkPos(3, 5), "x\n")], g:mode)
|
|
AssertEqual "abc\ndef4\nghi\nx\n", TestChanges("abc\ndef4\nghi\n", [MkInsert(MkPos(3, 6), "x\n")], g:mode)
|
|
AssertEqual "abc\ndef5\nghi\nx\n", TestChanges("abc\ndef5\nghi\n", [MkInsert(MkPos(4, 1), "x\n")], g:mode)
|
|
AssertEqual "abc\ndef6\nghi\nx\n", TestChanges("abc\ndef6\nghi\n", [MkInsert(MkPos(4, 2), "x\n")], g:mode)
|
|
endfor
|
|
|
|
Execute(Newline insertions near end):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "abc\ndef1\ngh\ni\n", TestChanges("abc\ndef1\nghi\n", [MkInsert(MkPos(3, 3), "\n")], g:mode)
|
|
AssertEqual "abc\ndef2\nghi\n\n", TestChanges("abc\ndef2\nghi\n", [MkInsert(MkPos(3, 4), "\n")], g:mode)
|
|
AssertEqual "abc\ndef3\nghi\n\n", TestChanges("abc\ndef3\nghi\n", [MkInsert(MkPos(3, 5), "\n")], g:mode)
|
|
AssertEqual "abc\ndef4\nghi\n\n", TestChanges("abc\ndef4\nghi\n", [MkInsert(MkPos(3, 6), "\n")], g:mode)
|
|
AssertEqual "abc\ndef5\nghi\n\n", TestChanges("abc\ndef5\nghi\n", [MkInsert(MkPos(4, 1), "\n")], g:mode)
|
|
endfor
|
|
|
|
Execute(Single char deletions):
|
|
for g:mode in ['save', 'file', 'buffer']
|
|
Log g:mode
|
|
AssertEqual "bc\ndef1\nghi\n", TestChanges("abc\ndef1\nghi\n", [MkDelete(MkPos(1, 1), MkPos(1, 2))], g:mode)
|
|
AssertEqual "ab\ndef2\nghi\n", TestChanges("abc\ndef2\nghi\n", [MkDelete(MkPos(1, 3), MkPos(1, 4))], g:mode)
|
|
AssertEqual "abcdef3\nghi\n", TestChanges("abc\ndef3\nghi\n", [MkDelete(MkPos(1, 4), MkPos(1, 5))], g:mode)
|
|
AssertEqual "abcdef4\nghi\n", TestChanges("abc\ndef4\nghi\n", [MkDelete(MkPos(1, 4), MkPos(1, 6))], g:mode)
|
|
AssertEqual "abc\ndef5\ngh\n", TestChanges("abc\ndef5\nghi\n", [MkDelete(MkPos(3, 3), MkPos(3, 4))], g:mode)
|
|
AssertEqual "abc\ndef6\nghi", TestChanges("abc\ndef6\nghi\n", [MkDelete(MkPos(3, 4), MkPos(3, 5))], g:mode)
|
|
AssertEqual "abc\ndef7\nghi", TestChanges("abc\ndef7\nghi\n", [MkDelete(MkPos(3, 4), MkPos(3, 6))], g:mode)
|
|
AssertEqual "abc\ndef8\nghi\n", TestChanges("abc\ndef8\nghi\n", [MkDelete(MkPos(4, 1), MkPos(4, 2))], g:mode)
|
|
endfor
|