diff options
Diffstat (limited to 'test')
34 files changed, 1920 insertions, 194 deletions
diff --git a/test/ada_files/testfile.adb b/test/ada_files/testfile.adb new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/ada_files/testfile.adb diff --git a/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi b/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/command_callback/python_paths/with_virtualenv/subdir/foo/bar.pyi diff --git a/test/command_callback/test_languagetool_command_callback.vader b/test/command_callback/test_languagetool_command_callback.vader index a79662b9..ff6b2064 100644 --- a/test/command_callback/test_languagetool_command_callback.vader +++ b/test/command_callback/test_languagetool_command_callback.vader @@ -12,4 +12,11 @@ Execute(Should be able to set a custom executable): let g:ale_languagetool_executable = 'foobar' AssertLinter 'foobar' , ale#Escape('foobar') - \ . ' --autoDetect %s' + \ . ' --autoDetect %s' + +Execute(Should be able to include custom languagetool options): + let g:ale_languagetool_options = '--language en' + + " is now 'foobar' based on above global + AssertLinter 'foobar', ale#Escape('foobar') + \ . ' --language en %s' diff --git a/test/command_callback/test_markdown_mdl_command_callback.vader b/test/command_callback/test_markdown_mdl_command_callback.vader index e029bf9b..1ce4db1a 100644 --- a/test/command_callback/test_markdown_mdl_command_callback.vader +++ b/test/command_callback/test_markdown_mdl_command_callback.vader @@ -5,15 +5,15 @@ After: call ale#assert#TearDownLinterTest() Execute(The default command should be correct): - AssertLinter 'mdl', ale#Escape('mdl') + AssertLinter 'mdl', ale#Escape('mdl') . ' -j' Execute(The executable and options should be configurable): let g:ale_markdown_mdl_executable = 'foo bar' let g:ale_markdown_mdl_options = '--wat' - AssertLinter 'foo bar', ale#Escape('foo bar') . ' --wat' + AssertLinter 'foo bar', ale#Escape('foo bar') . ' -j --wat' Execute(Setting bundle appends 'exec mdl'): let g:ale_markdown_mdl_executable = 'path to/bundle' - AssertLinter 'path to/bundle', ale#Escape('path to/bundle') . ' exec mdl' + AssertLinter 'path to/bundle', ale#Escape('path to/bundle') . ' exec mdl -j' diff --git a/test/command_callback/test_solc_command_callback.vader b/test/command_callback/test_solc_command_callback.vader new file mode 100644 index 00000000..23521f6a --- /dev/null +++ b/test/command_callback/test_solc_command_callback.vader @@ -0,0 +1,13 @@ +Before: + call ale#assert#SetUpLinterTest('solidity', 'solc') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'solc', 'solc %s' + +Execute(The options should be configurable): + let g:ale_solidity_solc_options = '--foobar' + + AssertLinter 'solc', 'solc --foobar %s' diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader index 6bc0035e..e06ac98b 100644 --- a/test/completion/test_completion_events.vader +++ b/test/completion/test_completion_events.vader @@ -47,6 +47,14 @@ Before: AssertEqual a:expect_success, g:get_completions_called endfunction + let g:handle_code_action_called = 0 + function! MockHandleCodeAction() abort + " delfunction! ale#code_action#HandleCodeAction + function! ale#code_action#HandleCodeAction(action) abort + let g:handle_code_action_called += 1 + endfunction + endfunction + After: Restore @@ -54,6 +62,7 @@ After: unlet! g:output unlet! g:fake_mode unlet! g:get_completions_called + unlet! g:handle_code_action_called unlet! b:ale_old_omnifunc unlet! b:ale_old_completeopt unlet! b:ale_completion_info @@ -61,6 +70,12 @@ After: unlet! b:ale_complete_done_time delfunction CheckCompletionCalled + delfunction ale#code_action#HandleCodeAction + delfunction MockHandleCodeAction + + if exists('*CompleteCallback') + delfunction CompleteCallback + endif " Stop any timers we left behind. " This stops the tests from failing randomly. @@ -73,6 +88,7 @@ After: endfunction runtime autoload/ale/completion.vim + runtime autoload/ale/code_action.vim runtime autoload/ale/util.vim Execute(ale#completion#GetCompletions should be called when the cursor position stays the same): @@ -333,6 +349,30 @@ Execute(b:ale_completion_info should be set up correctly for other sources): \ b:ale_completion_info Assert !exists('b:ale_completion_result') +Execute(b:ale_completion_info should be set up correctly when requesting completions via callback): + let b:ale_completion_result = [] + call setpos('.', [bufnr(''), 3, 14, 0]) + + function! CompleteCallback() abort + echo 'Called' + endfunction + + + call ale#completion#GetCompletions('ale-callback', {'callback': funcref('CompleteCallback')}) + + AssertEqual + \ { + \ 'request_id': 0, + \ 'conn_id': 0, + \ 'column': 14, + \ 'line_length': 14, + \ 'line': 3, + \ 'prefix': 'ab', + \ 'source': 'ale-callback', + \ }, + \ b:ale_completion_info + Assert !exists('b:ale_completion_result') + Execute(The correct keybinds should be configured): redir => g:output silent map <Plug>(ale_show_completion_menu) @@ -357,3 +397,44 @@ Execute(Running the normal mode <Plug> keybind should reset the settings): AssertEqual 'menu', &l:completeopt Assert !has_key(b:, 'ale_old_omnifunc') Assert !has_key(b:, 'ale_old_completeopt') + +Execute(HandleUserData should call ale#code_action#HandleCodeAction): + let b:ale_completion_info = {'source': 'ale-manual'} + call MockHandleCodeAction() + + call ale#completion#HandleUserData({}) + AssertEqual g:handle_code_action_called, 0 + + call ale#completion#HandleUserData({ + \ 'user_data': '' + \}) + AssertEqual g:handle_code_action_called, 0 + + call ale#completion#HandleUserData({ + \ 'user_data': '{}' + \}) + AssertEqual g:handle_code_action_called, 0 + + call ale#completion#HandleUserData({ + \ 'user_data': '{"codeActions": []}' + \}) + AssertEqual g:handle_code_action_called, 0 + + call ale#completion#HandleUserData({ + \ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' + \}) + AssertEqual g:handle_code_action_called, 1 + + let b:ale_completion_info = {'source': 'ale-automatic'} + call ale#completion#HandleUserData({ + \ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' + \}) + AssertEqual g:handle_code_action_called, 2 + +Execute(ale#code_action#HandleCodeAction should not be called when when source is not ALE): + call MockHandleCodeAction() + let b:ale_completion_info = {'source': 'syntastic'} + call ale#completion#HandleUserData({ + \ 'user_data': '{"codeActions": [{"description":"", "changes": []}]}' + \}) + AssertEqual g:handle_code_action_called, 0 diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader index 6bd241a8..b997ac86 100644 --- a/test/completion/test_lsp_completion_messages.vader +++ b/test/completion/test_lsp_completion_messages.vader @@ -116,7 +116,13 @@ Execute(The right message should be sent for the initial tsserver request): \ string(g:Callback) " We should send the right message. AssertEqual - \ [[0, 'ts@completions', {'file': expand('%:p'), 'line': 1, 'offset': 3, 'prefix': 'fo'}]], + \ [[0, 'ts@completions', { + \ 'file': expand('%:p'), + \ 'line': 1, + \ 'offset': 3, + \ 'prefix': 'fo', + \ 'includeExternalModuleExports': g:ale_completion_tsserver_autoimport, + \ }]], \ g:message_list " We should set up the completion info correctly. AssertEqual @@ -151,7 +157,7 @@ Execute(The right message sent to the tsserver LSP when the first completion mes \ 'body': [ \ {'name': 'Baz'}, \ {'name': 'dingDong'}, - \ {'name': 'Foo'}, + \ {'name': 'Foo', 'source': '/path/to/foo.ts'}, \ {'name': 'FooBar'}, \ {'name': 'frazzle'}, \ {'name': 'FFS'}, @@ -160,8 +166,16 @@ Execute(The right message sent to the tsserver LSP when the first completion mes " We should save the names we got in the buffer, as TSServer doesn't return " details for every name. - AssertEqual - \ ['Foo', 'FooBar', 'frazzle'], + AssertEqual [{ + \ 'word': 'Foo', + \ 'source': '/path/to/foo.ts', + \ }, { + \ 'word': 'FooBar', + \ 'source': '', + \ }, { + \ 'word': 'frazzle', + \ 'source': '', + \}], \ get(b:, 'ale_tsserver_completion_names', []) " The entry details messages should have been sent. @@ -171,7 +185,16 @@ Execute(The right message sent to the tsserver LSP when the first completion mes \ 'ts@completionEntryDetails', \ { \ 'file': expand('%:p'), - \ 'entryNames': ['Foo', 'FooBar', 'frazzle'], + \ 'entryNames': [{ + \ 'name': 'Foo', + \ 'source': '/path/to/foo.ts', + \ }, { + \ 'name': 'FooBar', + \ 'source': '', + \ }, { + \ 'name': 'frazzle', + \ 'source': '', + \ }], \ 'offset': 1, \ 'line': 1, \ }, diff --git a/test/completion/test_tsserver_completion_parsing.vader b/test/completion/test_tsserver_completion_parsing.vader index dbc4f9e2..dbb8de32 100644 --- a/test/completion/test_tsserver_completion_parsing.vader +++ b/test/completion/test_tsserver_completion_parsing.vader @@ -6,10 +6,24 @@ Execute(TypeScript completions responses should be parsed correctly): \ ale#completion#ParseTSServerCompletions({ \ 'body': [], \}) - AssertEqual ['foo', 'bar', 'baz'], + AssertEqual + \ [ + \ { + \ 'word': 'foo', + \ 'source': '/path/to/foo.ts', + \ }, + \ { + \ 'word': 'bar', + \ 'source': '', + \ }, + \ { + \ 'word': 'baz', + \ 'source': '', + \ } + \ ], \ ale#completion#ParseTSServerCompletions({ \ 'body': [ - \ {'name': 'foo'}, + \ {'name': 'foo', 'source': '/path/to/foo.ts'}, \ {'name': 'bar'}, \ {'name': 'baz'}, \ ], @@ -24,6 +38,7 @@ Execute(TypeScript completion details responses should be parsed correctly): \ 'info': '', \ 'kind': 'f', \ 'icase': 1, + \ 'dup': g:ale_completion_tsserver_autoimport, \ }, \ { \ 'word': 'def', @@ -31,6 +46,7 @@ Execute(TypeScript completion details responses should be parsed correctly): \ 'info': 'foo bar baz', \ 'kind': 'f', \ 'icase': 1, + \ 'dup': g:ale_completion_tsserver_autoimport, \ }, \ { \ 'word': 'ghi', @@ -38,6 +54,7 @@ Execute(TypeScript completion details responses should be parsed correctly): \ 'info': '', \ 'kind': 'f', \ 'icase': 1, + \ 'dup': g:ale_completion_tsserver_autoimport, \ }, \ ], \ ale#completion#ParseTSServerCompletionEntryDetails({ @@ -96,16 +113,26 @@ Execute(TypeScript completion details responses should be parsed correctly): \}) Execute(Entries without details should be included in the responses): - let b:ale_tsserver_completion_names = ['xyz'] + let b:ale_tsserver_completion_names = [{ + \ 'word': 'xyz', + \ 'source': '/path/to/xyz.ts', + \ }] AssertEqual \ [ \ { \ 'word': 'abc', - \ 'menu': '(property) Foo.abc: number', + \ 'menu': 'import { def } from "./Foo"; (property) Foo.abc: number', \ 'info': '', \ 'kind': 'f', \ 'icase': 1, + \ 'user_data': json_encode({ + \ 'codeActions': [{ + \ 'description': 'import { def } from "./Foo";', + \ 'changes': [], + \ }], + \ }), + \ 'dup': g:ale_completion_tsserver_autoimport, \ }, \ { \ 'word': 'def', @@ -113,6 +140,7 @@ Execute(Entries without details should be included in the responses): \ 'info': 'foo bar baz', \ 'kind': 'f', \ 'icase': 1, + \ 'dup': g:ale_completion_tsserver_autoimport, \ }, \ { \ 'word': 'xyz', @@ -139,6 +167,10 @@ Execute(Entries without details should be included in the responses): \ {'text': ' '}, \ {'text': 'number'}, \ ], + \ 'codeActions': [{ + \ 'description': 'import { def } from "./Foo";', + \ 'changes': [], + \ }], \ }, \ { \ 'name': 'def', diff --git a/test/d_files/test.d b/test/d_files/test.d new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/d_files/test.d diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader index 9dd04213..c3dd20e4 100644 --- a/test/fix/test_ale_fix.vader +++ b/test/fix/test_ale_fix.vader @@ -40,8 +40,8 @@ Before: return map(a:lines, '''^'' . v:val') endfunction - function AddDollars(buffer, lines) abort - return map(a:lines, '''$'' . v:val') + function Capitalize(buffer, lines) abort + return map(a:lines, 'toupper(v:val)') endfunction function DoNothing(buffer, lines) abort @@ -196,7 +196,7 @@ After: unlet! b:ale_fix_on_save unlet! b:ale_quitting delfunction AddCarets - delfunction AddDollars + delfunction Capitalize delfunction DoNothing delfunction CatLine delfunction CatLineOneArg @@ -265,28 +265,28 @@ Expect(The first function should be used): ^c Execute(ALEFix should apply simple functions in a chain): - let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + let g:ale_fixers.testft = ['AddCarets', 'Capitalize'] ALEFix call ale#test#FlushJobs() Expect(Both functions should be used): - $^a - $^b - $^c + ^A + ^B + ^C Execute(ALEFix should allow 0 to be returned to skip functions): - let g:ale_fixers.testft = ['DoNothing', 'AddDollars'] + let g:ale_fixers.testft = ['DoNothing', 'Capitalize'] ALEFix call ale#test#FlushJobs() Expect(Only the second function should be applied): - $a - $b - $c + A + B + C Execute(The * fixers shouldn't be used if an empty list is set for fixers): let g:ale_fixers.testft = [] - let g:ale_fixers['*'] = ['AddDollars'] + let g:ale_fixers['*'] = ['Capitalize'] ALEFix call ale#test#FlushJobs() @@ -296,14 +296,14 @@ Expect(Nothing should be changed): c Execute(* fixers should be used if no filetype is matched): - let g:ale_fixers = {'*': ['AddDollars']} + let g:ale_fixers = {'*': ['Capitalize']} ALEFix call ale#test#FlushJobs() Expect(The file should be changed): - $a - $b - $c + A + B + C Execute(ALEFix should allow commands to be run): if has('win32') @@ -323,13 +323,13 @@ Expect(An extra line should be added): Execute(ALEFix should use fixers passed in commandline when provided): let g:ale_fixers.testft = ['RemoveLastLine'] - ALEFix AddCarets AddDollars + ALEFix AddCarets Capitalize call ale#test#FlushJobs() Expect(Only fixers passed via command line should be run): - $^a - $^b - $^c + ^A + ^B + ^C Execute(ALEFix should allow temporary files to be read): if has('win32') @@ -364,43 +364,43 @@ Expect(An extra line should be added): Execute(ALEFix should allow jobs and simple functions to be combined): if has('win32') " Just skip this test on Windows, we can't run it. - call setline(1, ['$x']) + call setline(1, ['X']) 2,3d else - let g:ale_fixers.testft = ['ReplaceWithTempFile', 'AddDollars'] + let g:ale_fixers.testft = ['ReplaceWithTempFile', 'Capitalize'] ALEFix call ale#test#FlushJobs() endif Expect(The lines from the temporary file should be modified): - $x + X Execute(ALEFix should send lines modified by functions to jobs): if has('win32') " Just skip this test on Windows, we can't run it. - call setline(1, ['$a', '$b', '$c', 'd']) + call setline(1, ['A', 'B', 'C', 'd']) else - let g:ale_fixers.testft = ['AddDollars', 'CatLine'] + let g:ale_fixers.testft = ['Capitalize', 'CatLine'] ALEFix call ale#test#FlushJobs() endif Expect(The lines should first be modified by the function, then the job): - $a - $b - $c + A + B + C d Execute(ALEFix should skip commands when jobs fail to run): let g:ale_emulate_job_failure = 1 - let g:ale_fixers.testft = ['CatLine', 'AddDollars'] + let g:ale_fixers.testft = ['CatLine', 'Capitalize'] ALEFix call ale#test#FlushJobs() Expect(Only the second function should be applied): - $a - $b - $c + A + B + C Execute(ALEFix should handle strings for selecting a single function): let g:ale_fixers.testft = 'AddCarets' @@ -459,7 +459,7 @@ Expect(There should be an extra line): d Execute(ALEFix should user buffer-local fixer settings): - let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + let g:ale_fixers.testft = ['AddCarets', 'Capitalize'] let b:ale_fixers = {'testft': ['RemoveLastLine']} ALEFix call ale#test#FlushJobs() @@ -469,7 +469,7 @@ Expect(There should be only two lines): b Execute(ALEFix should allow Lists to be used for buffer-local fixer settings): - let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + let g:ale_fixers.testft = ['AddCarets', 'Capitalize'] let b:ale_fixers = ['RemoveLastLine'] ALEFix call ale#test#FlushJobs() @@ -492,7 +492,7 @@ Execute(ALEFix should fix files on the save event): execute 'noautocmd silent file ' . fnameescape(g:test_filename) call writefile(getline(1, '$'), g:test_filename) - let g:ale_fixers.testft = ['AddDollars'] + let g:ale_fixers.testft = ['Capitalize'] " We have to set the buftype to empty so the file will be written. setlocal buftype= @@ -502,7 +502,7 @@ Execute(ALEFix should fix files on the save event): call ale#test#FlushJobs() " We should save the file. - AssertEqual ['$a', '$b', '$c'], readfile(g:test_filename) + AssertEqual ['A', 'B', 'C'], readfile(g:test_filename) Assert !&modified, 'The file was marked as ''modified''' if !has('win32') @@ -521,9 +521,9 @@ Execute(ALEFix should fix files on the save event): endif Expect(The buffer should be modified): - $a - $b - $c + A + B + C Given testft (A file with three lines): a @@ -540,7 +540,7 @@ Execute(ALEFix should run the linters with b:ale_lint_on_save = 1): execute 'noautocmd silent file ' . fnameescape(g:test_filename) call writefile(getline(1, '$'), g:test_filename) - let g:ale_fixers.testft = ['AddDollars'] + let g:ale_fixers.testft = ['Capitalize'] " We have to set the buftype to empty so the file will be written. setlocal buftype= @@ -550,7 +550,7 @@ Execute(ALEFix should run the linters with b:ale_lint_on_save = 1): call ale#test#FlushJobs() " We should save the file. - AssertEqual ['$a', '$b', '$c'], readfile(g:test_filename) + AssertEqual ['A', 'B', 'C'], readfile(g:test_filename) Assert !&modified, 'The file was marked as ''modified''' if !has('win32') @@ -569,9 +569,9 @@ Execute(ALEFix should run the linters with b:ale_lint_on_save = 1): endif Expect(The buffer should be modified): - $a - $b - $c + A + B + C Execute(ALEFix should not fix files on :wq): let g:ale_fix_on_save = 1 @@ -582,7 +582,7 @@ Execute(ALEFix should not fix files on :wq): execute 'noautocmd silent file ' . fnameescape(g:test_filename) call writefile(getline(1, '$'), g:test_filename) - let g:ale_fixers.testft = ['AddDollars'] + let g:ale_fixers.testft = ['Capitalize'] " We have to set the buftype to empty so the file will be written. setlocal buftype= diff --git a/test/fixers/test_black_fixer_callback.vader b/test/fixers/test_black_fixer_callback.vader index 25ad05db..75864479 100644 --- a/test/fixers/test_black_fixer_callback.vader +++ b/test/fixers/test_black_fixer_callback.vader @@ -36,6 +36,14 @@ Execute(The black callback should include options): \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' --some-option -' }, \ ale#fixers#black#Fix(bufnr('')) +Execute(The black callback should include --pyi for .pyi files): + let g:ale_python_black_change_directory = 0 + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.pyi') + AssertEqual + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/black')) . ' --pyi -' }, + \ ale#fixers#black#Fix(bufnr('')) + Execute(Pipenv is detected when python_black_auto_pipenv is set): let g:ale_python_black_auto_pipenv = 1 let g:ale_python_black_change_directory = 0 diff --git a/test/fixers/test_dfmt_fixer_callback.vader b/test/fixers/test_dfmt_fixer_callback.vader new file mode 100644 index 00000000..5ecb56e6 --- /dev/null +++ b/test/fixers/test_dfmt_fixer_callback.vader @@ -0,0 +1,40 @@ +Before: + Save g:ale_d_dfmt_executable + Save g:ale_d_dfmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_d_dfmt_executable = 'xxxinvalid' + let g:ale_d_dfmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The dfmt callback should return the correct default values): + call ale#test#SetFilename('../d_files/test.d') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i' + \ . ' %t', + \ }, + \ ale#fixers#dfmt#Fix(bufnr('')) + +Execute(The dfmt callback should include custom dfmt options): + let g:ale_d_dfmt_options = "--space-after-cast" + call ale#test#SetFilename('../d_files/test.d') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('xxxinvalid') + \ . ' -i' + \ . ' ' . g:ale_d_dfmt_options + \ . ' %t', + \ }, + \ ale#fixers#dfmt#Fix(bufnr('')) diff --git a/test/fixers/test_gnatpp_fixer_callback.vader b/test/fixers/test_gnatpp_fixer_callback.vader new file mode 100644 index 00000000..a2bf898e --- /dev/null +++ b/test/fixers/test_gnatpp_fixer_callback.vader @@ -0,0 +1,28 @@ +Before: + call ale#assert#SetUpFixerTest('ada', 'gnatpp') + +After: + " Reset fixers, variables, etc. + " + " Vader's 'Restore' command will be called here. + call ale#assert#TearDownFixerTest() + +Execute(The default command should be correct): + call ale#test#SetFilename('../ada_files/testfile.adb') + + AssertFixer + \ { + \ 'command': ale#Escape(g:ale_ada_gnatpp_executable) .' %t', + \ 'read_temporary_file': 1, + \ } + +Execute(The version check should be correct): + call ale#test#SetFilename('../ada_files/testfile.adb') + let g:ale_ada_gnatpp_options = '--no-alignment' + + AssertFixer + \ { + \ 'command': ale#Escape(g:ale_ada_gnatpp_executable) + \ . ' --no-alignment %t', + \ 'read_temporary_file': 1, + \ } diff --git a/test/fixers/test_hindent_fixer_callback.vader b/test/fixers/test_hindent_fixer_callback.vader new file mode 100644 index 00000000..2e5a8b9f --- /dev/null +++ b/test/fixers/test_hindent_fixer_callback.vader @@ -0,0 +1,18 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The hindent callback should return the correct default values): + call ale#test#SetFilename('../haskell_files/testfile.hs') + + AssertEqual + \ { + \ 'read_temporary_file': 1, + \ 'command': ale#Escape('hindent') + \ . ' %t', + \ }, + \ ale#fixers#hindent#Fix(bufnr('')) diff --git a/test/fixers/test_sqlformat_fixer_callback.vader b/test/fixers/test_sqlformat_fixer_callback.vader new file mode 100644 index 00000000..4bace089 --- /dev/null +++ b/test/fixers/test_sqlformat_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_sql_sqlformat_executable + Save g:ale_sql_sqlformat_options + +After: + Restore + +Execute(The sqlformat callback should return the correct default values): + AssertEqual + \ { + \ 'command': ale#Escape('sqlformat') . ' -' + \ }, + \ ale#fixers#sqlformat#Fix(bufnr('')) + +Execute(The sqlformat executable and options should be configurable): + let g:ale_sql_sqlformat_executable = '/path/to/sqlformat' + let g:ale_sql_sqlformat_options = '-a' + + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/sqlformat') + \ . ' -a -' + \ }, + \ ale#fixers#sqlformat#Fix(bufnr('')) diff --git a/test/handler/test_mdl_handler.vader b/test/handler/test_mdl_handler.vader new file mode 100644 index 00000000..d01b52af --- /dev/null +++ b/test/handler/test_mdl_handler.vader @@ -0,0 +1,25 @@ +Before: + runtime ale_linters/markdown/mdl.vim + +After: + call ale#linter#Reset() + +Execute(The mdl handler should parse output correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'code': 'MD002/first-header-h1', + \ 'text': 'First header should be a top level header', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 18, + \ 'code': 'MD033/no-inline-html', + \ 'text': 'Inline HTML', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#markdown#mdl#Handle(0, [ + \ '[{"filename":"README.md","line":1,"rule":"MD002","aliases":["first-header-h1"],"description":"First header should be a top level header"},{"filename":"README.md","line":18,"rule":"MD033","aliases":["no-inline-html"],"description":"Inline HTML"}]' + \ ]) diff --git a/test/handler/test_powershell_handler.vader b/test/handler/test_powershell_handler.vader index 77c3dc65..77c3dc65 100755..100644 --- a/test/handler/test_powershell_handler.vader +++ b/test/handler/test_powershell_handler.vader diff --git a/test/handler/test_shell_handler.vader b/test/handler/test_shell_handler.vader index 2465f179..c61cf37d 100644 --- a/test/handler/test_shell_handler.vader +++ b/test/handler/test_shell_handler.vader @@ -40,3 +40,138 @@ Execute(The shell handler should parse lines correctly): \ 'qfm:22: :11: :33: :44:', \ 'foo.sh: syntax error at line 9: `done'' unexpected', \ ]) + +Execute(The shell handler should parse Simplified Chinese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': '未预期的符号“done”附近有语法错误', + \ }, + \ { + \ 'lnum': 90, + \ 'text': '寻找匹配的“"”时遇到了未预期的文件结束符', + \ }, + \ { + \ 'lnum': 111, + \ 'text': '语法错误: 未预期的文件结尾', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh:行0: 未预期的符号“done”附近有语法错误', + \ '/tmp/nvimWL5sOL/2/a.sh:行90: 寻找匹配的“"”时遇到了未预期的文件结束符', + \ '/tmp/nvimWL5sOL/2/a.sh:行111: 语法错误: 未预期的文件结尾', + \ '/tmp/nvimWL5sOL/2/a.sh:行22: :11: :33: :44:', + \ ]) + +Execute(The shell handler should parse Traditional Chinese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': '未預期的字組「(」附近有語法錯誤', + \ }, + \ { + \ 'lnum': 90, + \ 'text': '尋找匹配的「"」時遇到了未預期的檔案結束符', + \ }, + \ { + \ 'lnum': 111, + \ 'text': '語法錯誤: 未預期的檔案結尾', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: 列 0: 未預期的字組「(」附近有語法錯誤', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 90: 尋找匹配的「"」時遇到了未預期的檔案結束符', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 111: 語法錯誤: 未預期的檔案結尾', + \ '/tmp/nvimWL5sOL/2/a.sh: 列 22: :11: :33: :44:', + \ ]) + +Execute(The shell handler should parse Japanese lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': "予期しないトークン `(' 周辺に構文エラーがあります", + \ }, + \ { + \ 'lnum': 90, + \ 'text': "予期しないトークン `done' 周辺に構文エラーがあります", + \ }, + \ { + \ 'lnum': 111, + \ 'text': "対応する `\"' を探索中に予期しないファイル終了 (EOF) です", + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ "/tmp/nvimWL5sOL/2/a.sh: 行 0: 予期しないトークン `(' 周辺に構文エラーがあります", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 90: 予期しないトークン `done' 周辺に構文エラーがあります", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 111: 対応する `\"' を探索中に予期しないファイル終了 (EOF) です", + \ "/tmp/nvimWL5sOL/2/a.sh: 行 22: :11: :33: :44:", + \ ]) + +Execute(The shell handler should parse Greek lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': 'συντακτικό σφάλμα κοντά στο μη αναμενόμενο σύμβολο «done»', + \ }, + \ { + \ 'lnum': 90, + \ 'text': 'syntax error: μη αναμενόμενο τέλος αρχείου', + \ }, + \ { + \ 'lnum': 111, + \ 'text': 'μη αναμενόμενο EOF κατά την αναζήτηση «"»', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 0: συντακτικό σφάλμα κοντά στο μη αναμενόμενο σύμβολο «done»', + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 90: syntax error: μη αναμενόμενο τέλος αρχείου', + \ '/tmp/nvimWL5sOL/2/a.sh: γραμμή 111: μη αναμενόμενο EOF κατά την αναζήτηση «"»', + \ "/tmp/nvimWL5sOL/2/a.sh: γραμμή 22: :11: :33: :44:", + \ ]) + +Execute(The shell handler should parse Russian lines correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 0, + \ 'text': 'синтаксическая ошибка рядом с неожиданным маркером «done»', + \ }, + \ { + \ 'lnum': 90, + \ 'text': 'синтаксическая ошибка: неожиданный конец файла', + \ }, + \ { + \ 'lnum': 111, + \ 'text': 'неожиданный конец файла во время поиска «"»', + \ }, + \ { + \ 'lnum': 22, + \ 'text': ':11: :33: :44:', + \ }, + \ ], + \ ale_linters#sh#shell#Handle(347, [ + \ '/tmp/nvimWL5sOL/2/a.sh: строка 0: синтаксическая ошибка рядом с неожиданным маркером «done»', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 90: синтаксическая ошибка: неожиданный конец файла', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 111: неожиданный конец файла во время поиска «"»', + \ '/tmp/nvimWL5sOL/2/a.sh: строка 22: :11: :33: :44:', + \ ]) diff --git a/test/handler/test_solc_handler.vader b/test/handler/test_solc_handler.vader new file mode 100644 index 00000000..8c197507 --- /dev/null +++ b/test/handler/test_solc_handler.vader @@ -0,0 +1,30 @@ +Before: + runtime ale_linters/solidity/solc.vim + +After: + call ale#linter#Reset() + +Execute(Check solc output parsing): + AssertEqual + \ [ + \ { + \ 'lnum': 40, + \ 'col': 48, + \ 'text': 'This declaration shadows an existing declaration.', + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 23, + \ 'col': 16, + \ 'text': 'Member "getSinleSignature" not found or not visible after argument-dependent lookup in type(contract OneToN).', + \ 'type': 'E', + \ }, + \ ], + \ ale_linters#solidity#solc#Handle(bufnr(''), [ + \ 'raiden_contracts/data/source/raiden/Token.sol:40:48: Warning: This declaration shadows an existing declaration.', + \ ' function decimals() external view returns (uint8 decimals);', + \ ' ^------------^', + \ '/home/karl/raiden-contracts/raiden_contracts/data/source/test/OneToNInternalsTest.sol:23:16: Error: Member "getSinleSignature" not found or not visible after argument-dependent lookup in type(contract OneToN).', + \ ' return OneToN.getSinleSignature(signatures, i);', + \ ' ^----------------------^', + \ ]) diff --git a/test/handler/test_terraform_handler.vader b/test/handler/test_terraform_handler.vader index 976ce12a..976ce12a 100755..100644 --- a/test/handler/test_terraform_handler.vader +++ b/test/handler/test_terraform_handler.vader diff --git a/test/lsp/test_lsp_client_messages.vader b/test/lsp/test_lsp_client_messages.vader index 90a20832..bc91bf68 100644 --- a/test/lsp/test_lsp_client_messages.vader +++ b/test/lsp/test_lsp_client_messages.vader @@ -275,9 +275,10 @@ Execute(ale#lsp#tsserver_message#Completions() should return correct messages): \ 'line': 347, \ 'offset': 12, \ 'prefix': 'abc', + \ 'includeExternalModuleExports': 1, \ } \ ], - \ ale#lsp#tsserver_message#Completions(bufnr(''), 347, 12, 'abc') + \ ale#lsp#tsserver_message#Completions(bufnr(''), 347, 12, 'abc', 1) Execute(ale#lsp#tsserver_message#CompletionEntryDetails() should return correct messages): AssertEqual diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index 0372765d..6473e283 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -17,6 +17,7 @@ Before: \ 'init_queue': [], \ 'capabilities': { \ 'hover': 0, + \ 'rename': 0, \ 'references': 0, \ 'completion': 0, \ 'completion_trigger_characters': [], @@ -100,6 +101,7 @@ Execute(Capabilities should bet set up correctly): \ 'hover': 1, \ 'definition': 1, \ 'symbol_search': 1, + \ 'rename': 1, \ }, \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list @@ -110,7 +112,7 @@ Execute(Disabled capabilities should be recognised correctly): \ 'id': 1, \ 'result': { \ 'capabilities': { - \ 'renameProvider': v:true, + \ 'renameProvider': v:false, \ 'executeCommandProvider': { \ 'commands': [], \ }, @@ -143,6 +145,7 @@ Execute(Disabled capabilities should be recognised correctly): \ 'hover': 0, \ 'definition': 0, \ 'symbol_search': 0, + \ 'rename': 0, \ }, \ b:conn.capabilities AssertEqual [[1, 'initialized', {}]], g:message_list diff --git a/test/script/check-supported-tools-tables b/test/script/check-supported-tools-tables index 65270029..f4305707 100755 --- a/test/script/check-supported-tools-tables +++ b/test/script/check-supported-tools-tables @@ -3,8 +3,9 @@ set -e set -u -# This script compares the table of supported tools in both the README file -# and the doc/ale.txt file, so we can complain if they don't match up. +# This script compares the table of supported tools in both supported-tools.md +# (for GitHub) and doc/ale-supported-languages-and-tools.txt (for vim), so we +# can complain if they don't match up. doc_file="$(mktemp -t doc.XXXXXXXX)" doc_sorted_file="$(mktemp -t doc-sorted.XXXXXXXX)" diff --git a/test/script/check-toc b/test/script/check-toc index f3a57ed2..87a61262 100755 --- a/test/script/check-toc +++ b/test/script/check-toc @@ -35,7 +35,7 @@ sed -n "$toc_start_line,$toc_end_line"p doc/ale.txt \ > "$toc_file" # Get all of the doc files in a natural sorted order. -doc_files="$(/bin/ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" +doc_files="$(/usr/bin/env ls -1v doc | grep ^ale- | sed 's/^/doc\//' | paste -sd ' ' -)" # shellcheck disable=SC2086 grep -h '\*ale-.*-options\|^[a-z].*\*ale-.*\*$' $doc_files \ diff --git a/test/script/custom-checks b/test/script/custom-checks index 20dbfb80..ca9069e4 100755 --- a/test/script/custom-checks +++ b/test/script/custom-checks @@ -36,12 +36,12 @@ tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+' # Grep for tags and references, and complain if we find a reference without # a tag for the reference. Only our tags will be included. diff -u \ - <(grep --exclude=tags -roh "\*$tag_regex\*" doc | sort -u | sed 's/*//g') \ + <(grep --exclude=tags -roh "\\*$tag_regex\\*" doc | sort -u | sed 's/*//g') \ <(grep --exclude=tags -roh "|$tag_regex|" doc | sort -u | sed 's/|//g') \ | grep '^+[^+]' && exit_code=1 echo '========================================' -echo 'diff README.md and doc/ale.txt tables' +echo 'diff supported-tools.md and doc/ale-supported-languages-and-tools.txt tables' echo '========================================' echo 'Differences follow:' echo diff --git a/test/test_ale_info.vader b/test/test_ale_info.vader index decd49e9..2bc8c635 100644 --- a/test/test_ale_info.vader +++ b/test/test_ale_info.vader @@ -1,50 +1,114 @@ Before: Save g:ale_buffer_info Save g:ale_cache_executable_check_failures + Save g:ale_change_sign_column_color + Save g:ale_command_wrapper Save g:ale_completion_delay Save g:ale_completion_enabled Save g:ale_completion_max_suggestions + Save g:ale_echo_cursor + Save g:ale_echo_msg_error_str + Save g:ale_echo_msg_format + Save g:ale_echo_msg_info_str + Save g:ale_echo_msg_warning_str + Save g:ale_fix_on_save Save g:ale_fixers + Save g:ale_history_enabled Save g:ale_history_log_output + Save g:ale_keep_list_window_open + Save g:ale_lint_delay + Save g:ale_lint_on_enter + Save g:ale_lint_on_filetype_changed Save g:ale_lint_on_insert_leave + Save g:ale_lint_on_save Save g:ale_lint_on_text_changed Save g:ale_linters + Save g:ale_linters_explicit + Save g:ale_list_vertical + Save g:ale_list_window_size + Save g:ale_loclist_msg_format Save g:ale_lsp_error_messages + Save g:ale_lsp_root + Save g:ale_max_buffer_history_size + Save g:ale_max_signs Save g:ale_maximum_file_size + Save g:ale_open_list Save g:ale_pattern_options Save g:ale_pattern_options_enabled Save g:ale_set_balloons + Save g:ale_set_highlights + Save g:ale_set_loclist + Save g:ale_set_quickfix + Save g:ale_set_signs + Save g:ale_sign_column_always Save g:ale_sign_error Save g:ale_sign_info + Save g:ale_sign_offset Save g:ale_sign_style_error Save g:ale_sign_style_warning Save g:ale_sign_warning + Save g:ale_sign_highlight_linenrs Save g:ale_statusline_format Save g:ale_type_map + Save g:ale_use_global_executables + Save g:ale_virtualtext_cursor + Save g:ale_warn_about_trailing_blank_lines Save g:ale_warn_about_trailing_whitespace unlet! b:ale_history let g:ale_buffer_info = {} let g:ale_cache_executable_check_failures = 0 + let g:ale_change_sign_column_color = 0 + let g:ale_command_wrapper = '' let g:ale_completion_delay = 100 let g:ale_completion_enabled = 0 let g:ale_completion_max_suggestions = 50 + let g:ale_echo_cursor = 1 + let g:ale_echo_msg_error_str = 'Error' + let g:ale_echo_msg_format = '%code: %%s' + let g:ale_echo_msg_info_str = 'Info' + let g:ale_echo_msg_warning_str = 'Warning' + let g:ale_fix_on_save = 0 + let g:ale_history_enabled = 1 let g:ale_history_log_output = 1 + let g:ale_keep_list_window_open = 0 + let g:ale_lint_delay = 200 + let g:ale_lint_on_enter = 1 + let g:ale_lint_on_filetype_changed = 1 let g:ale_lint_on_insert_leave = 1 + let g:ale_lint_on_save = 1 let g:ale_lint_on_text_changed = 'normal' + let g:ale_linters_explicit = 0 + let g:ale_list_vertical = 0 + let g:ale_list_window_size = 10 + let g:ale_loclist_msg_format = '%code: %%s' let g:ale_lsp_error_messages = {} + let g:ale_lsp_root = {} + let g:ale_max_buffer_history_size = 20 + let g:ale_max_signs = -1 let g:ale_maximum_file_size = 0 + let g:ale_open_list = 0 let g:ale_pattern_options = {} let g:ale_pattern_options_enabled = 0 let g:ale_set_balloons = 0 + let g:ale_set_highlights = 1 + let g:ale_set_loclist = 1 + let g:ale_set_quickfix = 0 + let g:ale_set_signs = 1 + let g:ale_sign_column_always = 0 let g:ale_sign_error = '>>' let g:ale_sign_info = '--' + let g:ale_sign_offset = 1000000 let g:ale_sign_style_error = '>>' let g:ale_sign_style_warning = '--' let g:ale_sign_warning = '--' + let g:ale_sign_highlight_linenrs = 0 let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK'] let g:ale_type_map = {} + let g:ale_use_global_executables = v:null + let g:ale_virtualtext_cursor = 0 + let g:ale_warn_about_trailing_blank_lines = 1 let g:ale_warn_about_trailing_whitespace = 1 let g:testlinter1 = {'name': 'testlinter1', 'executable': 'testlinter1', 'command': 'testlinter1', 'callback': 'testCB1', 'output_stream': 'stdout'} @@ -116,6 +180,7 @@ Before: \ 'let g:ale_sign_style_error = ''>>''', \ 'let g:ale_sign_style_warning = ''--''', \ 'let g:ale_sign_warning = ''--''', + \ 'let g:ale_sign_highlight_linenrs = 0', \ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']', \ 'let g:ale_type_map = {}', \ 'let g:ale_use_global_executables = v:null', @@ -164,6 +229,8 @@ After: unlet! b:ale_testft2_testlinter2_foo unlet! g:ale_testft2_testlinter2_bar unlet! g:info_test_file + unlet! g:ale_testft_build_dir_names + unlet! g:ale_testft_testlinter2_option delfunction CheckInfo call ale#test#RestoreDirectory() @@ -632,3 +699,52 @@ Execute (LSP errors for other linters shouldn't appear): \ + g:globals_lines \ + g:command_header \) + +Given testft.testft2 (Empty buffer with two filetypes): +Execute (ALEInfo should include linter global options): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft2', g:testlinter2) + + " eg: like g:c_build_dir_names + let g:ale_testft_build_dir_names = ['build', 'bin'] + + call add(g:variables_lines, 'let g:ale_testft_build_dir_names = [''build'', ''bin'']') + + call CheckInfo( + \ [ + \ ' Current Filetype: testft.testft2', + \ 'Available Linters: [''testlinter1'', ''testlinter2'']', + \ ' Enabled Linters: [''testlinter1'', ''testlinter2'']', + \ ] + \ + g:fixer_lines + \ + g:variables_lines + \ + g:globals_lines + \ + g:command_header + \) + +Given testft (Empty buffer with two filetypes): +Execute (ALEInfo should include linter global options for enabled linters): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + + let g:ale_linters = {'testft': ['testlinter1']} + + " should not appear, since not enabled + let g:ale_testft_testlinter2_option = 'test' + + let g:globals_lines[index(g:globals_lines, 'let g:ale_linters = {}')] + \ = 'let g:ale_linters = {''testft'': [''testlinter1'']}' + + call CheckInfo( + \ [ + \ ' Current Filetype: testft', + \ 'Available Linters: [''testlinter1'', ''testlinter2'']', + \ ' Enabled Linters: [''testlinter1'']', + \ ] + \ + g:fixer_lines + \ + g:variables_lines + \ + g:globals_lines + \ + g:command_header + \) + + diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader index d0bca329..a0763cb1 100644 --- a/test/test_ale_toggle.vader +++ b/test/test_ale_toggle.vader @@ -36,6 +36,7 @@ Before: \ 'ALEEvents', \ 'ALEHighlightBufferGroup', \] + let g:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace') function! ToggleTestCallback(buffer, output) return [{ @@ -91,6 +92,7 @@ After: unlet! g:expected_groups unlet! b:ale_enabled unlet! g:output + unlet! g:has_nvim_highlight call ale#linter#Reset() @@ -120,9 +122,14 @@ Execute(ALEToggle should reset everything and then run again): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + " Only check the legacy matches if not using the new NeoVIM API. + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -133,7 +140,11 @@ Execute(ALEToggle should reset everything and then run again): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif + AssertEqual g:expected_groups, ParseAuGroups() " Toggle ALE on, everything should be set up and run again. @@ -142,9 +153,13 @@ Execute(ALEToggle should reset everything and then run again): AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -228,9 +243,13 @@ Execute(ALEReset should reset everything for a buffer): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -241,7 +260,10 @@ Execute(ALEReset should reset everything for a buffer): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif AssertEqual 1, g:ale_enabled @@ -254,9 +276,13 @@ Execute(ALEToggleBuffer should reset everything and then run again): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -266,7 +292,10 @@ Execute(ALEToggleBuffer should reset everything and then run again): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif " Toggle ALE on, everything should be set up and run again. ALEToggleBuffer @@ -274,9 +303,13 @@ Execute(ALEToggleBuffer should reset everything and then run again): AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual g:expected_groups, ParseAuGroups() AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist @@ -325,9 +358,13 @@ Execute(ALEResetBuffer should reset everything for a buffer): " First check that everything is there... AssertEqual g:expected_loclist, ale#test#GetLoclistWithoutModule() AssertEqual [0, [[2, 1000001, 'ALEErrorSign']]], ale#sign#FindCurrentSigns(bufnr('%')) - AssertEqual - \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], - \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + + if !g:has_nvim_highlight + AssertEqual + \ [{'group': 'ALEError', 'pos1': [2, 3, 1]}], + \ map(getmatches(), '{''group'': v:val.group, ''pos1'': v:val.pos1}') + endif + AssertEqual [{'lnum': 2, 'bufnr': bufnr(''), 'col': 3, 'linter_name': 'testlinter', 'vcol': 0, 'nr': -1, 'type': 'E', 'text': 'foo bar', 'sign_id': 1000001}], g:ale_buffer_info[bufnr('')].loclist " Now Toggle ALE off. @@ -338,7 +375,10 @@ Execute(ALEResetBuffer should reset everything for a buffer): Assert !has_key(g:ale_buffer_info, bufnr('')), 'The g:ale_buffer_info Dictionary was not removed' AssertEqual [], ale#test#GetLoclistWithoutModule(), 'The loclist was not cleared' AssertEqual [0, []], ale#sign#FindCurrentSigns(bufnr('%')), 'The signs were not cleared' - AssertEqual [], getmatches(), 'The highlights were not cleared' + + if !g:has_nvim_highlight + AssertEqual [], getmatches(), 'The highlights were not cleared' + endif AssertEqual 1, g:ale_enabled AssertEqual 1, get(b:, 'ale_enabled', 1) diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index 241e7d3e..355b4c77 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -188,6 +188,10 @@ Execute (ALECleanupGroup should include the right commands): \], CheckAutocmd('ALECleanupGroup') endif +Execute(ALECompletionActions should always be set up): + AssertEqual [ + \ 'CompleteDone * call ale#completion#HandleUserData(v:completed_item)', + \], CheckAutocmd('ALECompletionActions') Execute(Enabling completion should set up autocmd events correctly): let g:ale_completion_enabled = 0 diff --git a/test/test_c_flag_parsing.vader b/test/test_c_flag_parsing.vader index 045554a3..8ae6f9dc 100644 --- a/test/test_c_flag_parsing.vader +++ b/test/test_c_flag_parsing.vader @@ -14,7 +14,7 @@ Execute(The CFlags parser should be able to parse include directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c']) AssertEqual @@ -25,14 +25,14 @@ Execute(ParseCFlags should ignore -c and -o): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir'), + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')), \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -c file.c -o a.out']) Execute(The CFlags parser should be able to parse macro directives): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=1', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=1 -c file.c']) @@ -40,7 +40,7 @@ Execute(The CFlags parser should be able to parse macro directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=$(( 2 * 4 ))', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=$(( 2 * 4 )) -c file.c']) @@ -48,14 +48,14 @@ Execute(The CFlags parser should be able to parse shell directives with spaces): call ale#test#SetFilename('test_c_projects/makefile_project/subdir/file.c') AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlagsFromMakeOutput(bufnr(''), ['gcc -Isubdir -DTEST=`date +%s` -c file.c']) Execute(ParseCFlags should be able to parse flags with relative paths): AssertEqual - \ '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), @@ -67,8 +67,8 @@ Execute(ParseCFlags should be able to parse flags with relative paths): Execute(ParseCFlags should be able to parse -Dgoal): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), @@ -77,16 +77,30 @@ Execute(ParseCFlags should be able to parse -Dgoal): \ . ' -DTEST=`date +%s` -c file.c' \ ) +Execute(ParseCFlags should ignore -T and other arguments): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '--sysroot=subdir' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) + \ . ' -DTEST=`date +%s`', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir --sysroot=subdir ' + \ . '-I'. ale#path#Simplify('kernel/include') + \ . ' -DTEST=`date +%s` -c file.c' + \ ) + Execute(ParseCFlags should handle paths with spaces in double quotes): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/"dir with spaces"') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-I"dir with spaces"' . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -94,28 +108,29 @@ Execute(ParseCFlags should handle paths with spaces in double quotes): Execute(ParseCFlags should handle paths with spaces in single quotes): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. "/test_c_projects/makefile_project/'dir with spaces'") - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' - \ . "-I'dir with spaces'" . ' -I'. ale#path#Simplify('kernel/include') + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) Execute(ParseCFlags should handle paths with minuses): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' - \ . ' -Idir-with-dash' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -123,14 +138,17 @@ Execute(ParseCFlags should handle paths with minuses): Execute(ParseCFlags should handle -D with minuses): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) \ . ' -Dmacro-with-dash' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include') + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')) \ . ' -DTEST=`date +%s`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' \ . '-Dmacro-with-dash ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ . ' -DTEST=`date +%s` -c file.c' \ ) @@ -138,11 +156,16 @@ Execute(ParseCFlags should handle -D with minuses): Execute(ParseCFlags should handle flags at the end of the line): AssertEqual \ '-Dgoal=9' - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir') - \ . ' ' . '-I' . ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include'), + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' -Dmacro-with-dash' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 -Isubdir ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . '-Dmacro-with-dash ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash' \ . ' -I'. ale#path#Simplify('kernel/include') \ ) @@ -156,14 +179,14 @@ Execute(ParseCompileCommandsFlags should parse some basic flags): silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags(bufnr(''), { "xmms2-mpris.c": [ \ { - \ 'directory': '/foo/bar/xmms2-mpris', - \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2' + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', - \ 'file': '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), \ }, \ ] }, {}) @@ -183,14 +206,14 @@ Execute(ParseCompileCommandsFlags should fall back to files in the same director silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I ' . ale#path#Simplify('/usr/include/xmms2'), \ ale#c#ParseCompileCommandsFlags(bufnr(''), {}, { "src": [ \ { - \ 'directory': '/foo/bar/xmms2-mpris', - \ 'command': '/usr/bin/cc -I' . '/usr/include/xmms2' + \ 'directory': ale#path#Simplify('/foo/bar/xmms2-mpris'), + \ 'command': '/usr/bin/cc -I' . ale#path#Simplify('/usr/include/xmms2') \ . ' -o CMakeFiles/xmms2-mpris.dir/src/xmms2-mpris.c.o' - \ . ' -c ' . '/foo/bar/xmms2-mpris/src/xmms2-mpris.c', - \ 'file': (has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c', + \ . ' -c ' . ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.c'), + \ 'file': ale#path#Simplify((has('win32') ? 'C:' : '') . '/foo/bar/xmms2-mpris/src/xmms2-other.c'), \ }, \ ] }) @@ -198,7 +221,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .c files fo silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -220,7 +243,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.hpp')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -242,7 +265,7 @@ Execute(ParseCompileCommandsFlags should take commands from matching .cpp files silent noautocmd execute 'file! ' . fnameescape(ale#path#Simplify('/foo/bar/xmms2-mpris/src/xmms2-mpris.h')) AssertEqual - \ '-I/usr/include/xmms2', + \ '-I /usr/include/xmms2', \ ale#c#ParseCompileCommandsFlags( \ bufnr(''), \ { @@ -282,13 +305,64 @@ Execute(ParseCompileCommandsFlags should not take commands from .c files for .h \ }, \ ) +Execute(ParseCFlags should not merge flags): + AssertEqual + \ '-Dgoal=9' + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/subdir')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir with spaces')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/dir-with-dash')) + \ . ' ' . '-I' . ' ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/kernel/include')), + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla -Isubdir ' + \ . 'subdir/somedep1.o ' . 'subdir/somedep2.o ' + \ . '-I''dir with spaces''' . ' -Idir-with-dash ' + \ . 'subdir/somedep3.o ' . 'subdir/somedep4.o ' + \ . ' -I'. ale#path#Simplify('kernel/include') . ' ' + \ . 'subdir/somedep5.o ' . 'subdir/somedep6.o ' + \ ) + Execute(ParseCFlags should handle parenthesis and quotes): AssertEqual - \ '-Dgoal=9 -Dtest1="('' '')" file1.o -Dtest2=''(` `)'' file2.o -Dtest3=`(" ")` file3.o', + \ '-Dgoal=9 -Dtest1="('' '')" -Dtest2=''(` `)'' -Dtest3=`(" ")`', \ ale#c#ParseCFlags( \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), - \ 'gcc -Dgoal=9 ' + \ 'gcc -Dgoal=9 -Tlinkerfile.ld blabla ' \ . '-Dtest1="('' '')" file1.o ' \ . '-Dtest2=''(` `)'' file2.o ' \ . '-Dtest3=`(" ")` file3.o ' \ ) + +Execute(CFlags we want to pass): + AssertEqual + \ '-I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/inc')) + \ . ' -I ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/include')) + \ . ' -iquote ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incquote')) + \ . ' -isystem ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incsystem')) + \ . ' -idirafter ' . ale#Escape(ale#path#Simplify(g:dir. '/test_c_projects/makefile_project/incafter')) + \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' + \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' + \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' + \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' + \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' + \ . ' -iplugindir=dir -march=native -w', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc' + \ . ' -Iinc -I include -iquote incquote -isystem incsystem -idirafter incafter' + \ . ' -Dmacro=value -D macro2 -Bbdir -B bdir2' + \ . ' -iprefix prefix -iwithprefix prefix2 -iwithprefixbefore prefix3' + \ . ' -isysroot sysroot --sysroot=test --no-sysroot-suffix -imultilib multidir' + \ . ' -Wsome-warning -std=c89 -pedantic -pedantic-errors -ansi' + \ . ' -foption -O2 -C -CC -trigraphs -nostdinc -nostdinc++' + \ . ' -iplugindir=dir -march=native -w' + \ ) + +Execute(CFlags we dont want to pass): + AssertEqual + \ '', + \ ale#c#ParseCFlags( + \ ale#path#Simplify(g:dir. '/test_c_projects/makefile_project'), + \ 'gcc -Wl,option -Wa,option -Wp,option filename.c somelib.a ' + \ . '-fdump-file=name -fdiagnostics-arg -fno-show-column' + \ ) diff --git a/test/test_code_action.vader b/test/test_code_action.vader new file mode 100644 index 00000000..ffaca630 --- /dev/null +++ b/test/test_code_action.vader @@ -0,0 +1,334 @@ +Before: + runtime autoload/ale/code_action.vim + runtime autoload/ale/util.vim + + let g:file1 = tempname() + let g:file2 = tempname() + let g:test = {} + + let g:test.create_change = {line, offset, end_line, end_offset, value -> + \{ + \ 'changes': [{ + \ 'fileName': g:file1, + \ 'textChanges': [{ + \ 'start': { + \ 'line': line, + \ 'offset': offset, + \ }, + \ 'end': { + \ 'line': end_line, + \ 'offset': end_offset, + \ }, + \ 'newText': value, + \ }], + \ }] + \}} + + function! WriteFileAndEdit() abort + let g:test.text = [ + \ 'class Name {', + \ ' value: string', + \ '}', + \] + call writefile(g:test.text, g:file1, 'S') + execute 'edit ' . g:file1 + endfunction! + +After: + " Close the extra buffers if we opened it. + if bufnr(g:file1) != -1 + execute ':bp | :bd ' . bufnr(g:file1) + endif + if bufnr(g:file2) != -1 + execute ':bp | :bd ' . bufnr(g:file2) + endif + + if filereadable(g:file1) + call delete(g:file1) + endif + if filereadable(g:file2) + call delete(g:file2) + endif + + unlet g:file1 + unlet g:file2 + unlet g:test + delfunction WriteFileAndEdit + + runtime autoload/ale/code_action.vim + runtime autoload/ale/util.vim + + +Execute(It should modify and save multiple files): + call writefile([ + \ 'class Name {', + \ ' value: string', + \ '}', + \ '', + \ 'class B {', + \ ' constructor(readonly a: Name) {}', + \ '}' + \], g:file1, 'S') + call writefile([ + \ 'import A from "A"', + \ 'import {', + \ ' B,', + \ ' C,', + \ '} from "module"', + \ 'import D from "D"', + \], g:file2, 'S') + + call ale#code_action#HandleCodeAction({ + \ 'changes': [{ + \ 'fileName': g:file1, + \ 'textChanges': [{ + \ 'start': { + \ 'line': 1, + \ 'offset': 7, + \ }, + \ 'end': { + \ 'line': 1, + \ 'offset': 11, + \ }, + \ 'newText': 'Value', + \ }, { + \ 'start': { + \ 'line': 6, + \ 'offset': 27, + \ }, + \ 'end': { + \ 'line': 6, + \ 'offset': 31, + \ }, + \ 'newText': 'Value', + \ }], + \ }, { + \ 'fileName': g:file2, + \ 'textChanges': [{ + \ 'start': { + \ 'line': 2, + \ 'offset': 1, + \ }, + \ 'end': { + \ 'line': 6, + \ 'offset': 1, + \ }, + \ 'newText': "import {A, B} from 'module'\n\n", + \ }] + \ }], + \}) + + AssertEqual [ + \ 'class Value {', + \ ' value: string', + \ '}', + \ '', + \ 'class B {', + \ ' constructor(readonly a: Value) {}', + \ '}', + \ '', + \], readfile(g:file1, 'b') + + AssertEqual [ + \ 'import A from "A"', + \ 'import {A, B} from ''module''', + \ '', + \ 'import D from "D"', + \ '', + \], readfile(g:file2, 'b') + + +Execute(Beginning of file can be modified): + let g:test.text = [ + \ 'class Name {', + \ ' value: string', + \ '}', + \] + call writefile(g:test.text, g:file1, 'S') + + call ale#code_action#HandleCodeAction({ + \ 'changes': [{ + \ 'fileName': g:file1, + \ 'textChanges': [{ + \ 'start': { + \ 'line': 1, + \ 'offset': 1, + \ }, + \ 'end': { + \ 'line': 1, + \ 'offset': 1, + \ }, + \ 'newText': "type A: string\ntype B: number\n", + \ }], + \ }] + \}) + + AssertEqual [ + \ 'type A: string', + \ 'type B: number', + \] + g:test.text + [''], readfile(g:file1, 'b') + + +Execute(End of file can be modified): + let g:test.text = [ + \ 'class Name {', + \ ' value: string', + \ '}', + \] + call writefile(g:test.text, g:file1, 'S') + + call ale#code_action#HandleCodeAction({ + \ 'changes': [{ + \ 'fileName': g:file1, + \ 'textChanges': [{ + \ 'start': { + \ 'line': 4, + \ 'offset': 1, + \ }, + \ 'end': { + \ 'line': 4, + \ 'offset': 1, + \ }, + \ 'newText': "type A: string\ntype B: number\n", + \ }], + \ }] + \}) + + AssertEqual g:test.text + [ + \ 'type A: string', + \ 'type B: number', + \ '', + \], readfile(g:file1, 'b') + + +Execute(Current buffer contents will be reloaded): + let g:test.text = [ + \ 'class Name {', + \ ' value: string', + \ '}', + \] + call writefile(g:test.text, g:file1, 'S') + + execute 'edit ' . g:file1 + let g:test.buffer = bufnr(g:file1) + + call ale#code_action#HandleCodeAction({ + \ 'changes': [{ + \ 'fileName': g:file1, + \ 'textChanges': [{ + \ 'start': { + \ 'line': 1, + \ 'offset': 1, + \ }, + \ 'end': { + \ 'line': 1, + \ 'offset': 1, + \ }, + \ 'newText': "type A: string\ntype B: number\n", + \ }], + \ }] + \}) + + AssertEqual [ + \ 'type A: string', + \ 'type B: number', + \] + g:test.text + [''], readfile(g:file1, 'b') + + AssertEqual [ + \ 'type A: string', + \ 'type B: number', + \] + g:test.text, getbufline(g:test.buffer, 1, '$') + + +# Tests for cursor repositioning. In comments `=` designates change range, and +# `C` cursor position + +# C === +Execute(Cursor will not move when it is before text change): + call WriteFileAndEdit() + let g:test.changes = g:test.create_change(2, 3, 2, 8, 'value2') + + call setpos('.', [0, 1, 1, 0]) + call ale#code_action#HandleCodeAction(g:test.changes) + AssertEqual [1, 1], getpos('.')[1:2] + + call setpos('.', [0, 2, 2, 0]) + call ale#code_action#HandleCodeAction(g:test.changes) + AssertEqual [2, 2], getpos('.')[1:2] + +# ====C==== +Execute(Cursor column will move to the change end when cursor between start/end): + let g:test.changes = g:test.create_change(2, 3, 2, 8, 'value2') + + for r in range(3, 8) + call WriteFileAndEdit() + call setpos('.', [0, 2, r, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.changes) + AssertEqual ' value2: string', getline('.') + AssertEqual [2, 9], getpos('.')[1:2] + endfor + + +# ====C +Execute(Cursor column will move back when new text is shorter): + call WriteFileAndEdit() + call setpos('.', [0, 2, 8, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(2, 3, 2, 8, 'val')) + AssertEqual ' val: string', getline('.') + AssertEqual [2, 6], getpos('.')[1:2] + + +# ==== C +Execute(Cursor column will move forward when new text is longer): + call WriteFileAndEdit() + + call setpos('.', [0, 2, 8, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(2, 3, 2, 8, 'longValue')) + AssertEqual ' longValue: string', getline('.') + AssertEqual [2, 12], getpos('.')[1:2] + +# ========= +# = +# C +Execute(Cursor line will move when updates are happening on lines above): + call WriteFileAndEdit() + call setpos('.', [0, 3, 1, 0]) + AssertEqual '}', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(1, 1, 2, 1, "test\ntest\n")) + AssertEqual '}', getline('.') + AssertEqual [4, 1], getpos('.')[1:2] + + +# ========= +# =C +Execute(Cursor line and column will move when change on lines above and just before cursor column): + call WriteFileAndEdit() + call setpos('.', [0, 2, 2, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(1, 1, 2, 1, "test\ntest\n123")) + AssertEqual '123 value: string', getline('.') + AssertEqual [3, 5], getpos('.')[1:2] + +# ========= +# ======C== +# = +Execute(Cursor line and column will move at the end of changes): + call WriteFileAndEdit() + call setpos('.', [0, 2, 10, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(1, 1, 3, 1, "test\n")) + AssertEqual '}', getline('.') + AssertEqual [2, 1], getpos('.')[1:2] + +# C == +# === +Execute(Cursor will not move when changes happening on lines >= cursor, but after cursor): + call WriteFileAndEdit() + call setpos('.', [0, 2, 3, 0]) + AssertEqual ' value: string', getline('.') + call ale#code_action#HandleCodeAction(g:test.create_change(2, 10, 3, 1, "number\n")) + AssertEqual ' value: number', getline('.') + AssertEqual [2, 3], getpos('.')[1:2] diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader index af028041..9c40cb23 100644 --- a/test/test_filetype_linter_defaults.vader +++ b/test/test_filetype_linter_defaults.vader @@ -66,3 +66,7 @@ Execute(The defaults for the verilog filetype should be correct): let g:ale_linters_explicit = 1 AssertEqual [], GetLinterNames('verilog') + +Execute(Default aliases for React should be defined): + AssertEqual ['javascript', 'jsx'], ale#linter#ResolveFiletype('javascriptreact') + AssertEqual ['typescript', 'tsx'], ale#linter#ResolveFiletype('typescriptreact') diff --git a/test/test_highlight_placement.vader b/test/test_highlight_placement.vader index 87ac6073..c062018b 100644 --- a/test/test_highlight_placement.vader +++ b/test/test_highlight_placement.vader @@ -8,6 +8,8 @@ Before: Save g:ale_set_quickfix Save g:ale_set_signs + runtime autoload/ale/highlight.vim + let g:ale_run_synchronously = 1 unlet! g:ale_run_synchronously_callbacks let g:ale_set_highlights = 1 @@ -42,16 +44,54 @@ Before: \] endfunction + let g:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace') + let g:nvim_highlight_matches = {} + + function! ale#highlight#nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) abort + if a:line_end != -1 + throw 'nvim api behavior not supported' + endif + + let l:matches = get(g:nvim_highlight_matches, a:buffer, []) + call filter( + \ l:matches, + \ {_, val -> val.pos1[0] < (a:line_start + 1) }, + \) + endfunction + + function! ale#highlight#nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) abort + if a:col_end == -1 + throw 'nvim api behavior not supported' + endif + + let l:matches = get(g:nvim_highlight_matches, a:buffer, []) + let g:nvim_highlight_matches[a:buffer] = l:matches + + let l:new_match = { + \ 'group': a:hl_group, + \ 'priority': 10, + \ 'pos1': [a:line + 1, a:col_start + 1, a:col_end - a:col_start], + \} + + call add(l:matches, l:new_match) + " sort by line number to emulate getmatches faithfully + call sort(l:matches, {m1, m2 -> m1.pos1[0] - m2.pos1[0]}) + endfunction + " We don't care what the IDs are, just that we have some matches. " The IDs are generated. function! GetMatchesWithoutIDs() abort - let l:list = getmatches() + if g:has_nvim_highlight + return get(g:nvim_highlight_matches, bufnr(''), []) + else + let l:list = getmatches() - for l:item in l:list - call remove(l:item, 'id') - endfor + for l:item in l:list + call remove(l:item, 'id') + endfor - return l:list + return l:list + endif endfunction call ale#linter#Define('testft', { @@ -68,6 +108,8 @@ After: unlet! g:ale_run_synchronously_callbacks unlet! g:items unlet! b:ale_enabled + unlet! g:has_nvim_highlight + unlet! g:nvim_highlight_matches delfunction GenerateResults call ale#linter#Reset() @@ -75,6 +117,8 @@ After: sign unplace * highlight clear SomeOtherGroup + runtime autoload/ale/highlight.vim + Given testft(A Javscript file with warnings/errors): foo bar @@ -102,9 +146,12 @@ Execute(Highlights set by ALE should be removed when buffer cleanup is done): \ {'bufnr': bufnr('%'), 'type': 'E', 'lnum': 3, 'col': 2}, \]) - AssertEqual - \ [{'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}], - \ GetMatchesWithoutIDs() + if !g:has_nvim_highlight + " This check doesn't work with the new API, for some reason. + AssertEqual + \ [{'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}], + \ GetMatchesWithoutIDs() + endif call ale#engine#Cleanup(bufnr('%')) @@ -145,31 +192,53 @@ Execute(Only ALE highlights should be restored when buffers are restored): call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) " We should have both highlights. - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " When the newer NeoVim API is used, we don't have to worry about + " other highlights, namespacing is available. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ sort(GetMatchesWithoutIDs(), {m1, m2 -> m1.group < m2.group ? -1 : 1}) + endif call ale#highlight#BufferHidden(bufnr('%')) " We should remove our highlight, but not the other one. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]} - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual [], GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]} + \ ], + \ GetMatchesWithoutIDs() + endif call ale#highlight#UpdateHighlights() " Our highlight should apper again. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 2, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ sort(GetMatchesWithoutIDs(), {m1, m2 -> m1.group < m2.group ? -1 : 1}) + endif Execute(Higlight end columns should set an appropriate size): call ale#highlight#SetHighlights(bufnr('%'), [ @@ -216,32 +285,67 @@ Execute(Highlighting should support errors spanning many lines): call ale#highlight#SetHighlights(bufnr(''), g:items) - " We should set 2 highlights for the item, as we can only add 8 at a time. - AssertEqual - \ [ - \ { - \ 'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824], - \ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6], - \ 'pos7': [7], 'pos8': [8], - \ }, - \ { - \ 'group': 'ALEError', 'priority': 10, - \ 'pos1': [9], 'pos2': [10, 1, 3] - \ }, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " The newer NeoVim highlight API produces different output. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [2, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [3, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [4, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [5, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [6, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [7, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [8, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [9, 1, 1073741824]}, + \ {'group': 'ALEError', 'priority': 10, 'pos1': [10, 1, 3]}, + \ ], + \ GetMatchesWithoutIDs() + else + " We should set 2 highlights for the item, as we can only add 8 at a time. + AssertEqual + \ [ + \ { + \ 'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1073741824], + \ 'pos2': [2], 'pos3': [3], 'pos4': [4], 'pos5': [5], 'pos6': [6], + \ 'pos7': [7], 'pos8': [8], + \ }, + \ { + \ 'group': 'ALEError', 'priority': 10, + \ 'pos1': [9], 'pos2': [10, 1, 3] + \ }, + \ ], + \ GetMatchesWithoutIDs() + endif Execute(Highlights should always be cleared when the buffer highlight list is empty): - " Add our highlights and something else. - call matchaddpos('ALEError', [[1, 1, 1]]) - call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) + if g:has_nvim_highlight + " The newer API uses namespacing. We'll emulate it here. + call ale#highlight#nvim_buf_add_highlight( + \ bufnr(''), + \ 1, + \ 'ALEError', + \ 0, + \ 0, + \ 1, + \) + + AssertEqual + \ [{'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}], + \ GetMatchesWithoutIDs() + else + " Add our highlights and something else. + call matchaddpos('ALEError', [[1, 1, 1]]) + call matchaddpos('SomeOtherGroup', [[1, 1, 1]]) + + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, + \ ], + \ GetMatchesWithoutIDs() + endif - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() " Set the List we use for holding highlights for buffers. let b:ale_highlight_items = [] @@ -251,11 +355,13 @@ Execute(Highlights should always be cleared when the buffer highlight list is em call ale#highlight#UpdateHighlights() " Check that we remove our highlights. - AssertEqual - \ [ - \ {'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + AssertEqual [], GetMatchesWithoutIDs() + else + AssertEqual + \ [{'group': 'SomeOtherGroup', 'priority': 10, 'pos1': [1, 1, 1]}], + \ GetMatchesWithoutIDs() + endif Execute(Highlights should be cleared when ALE is disabled): let g:ale_enabled = 1 @@ -291,16 +397,30 @@ Execute(Line highlights should be set when signs are disabled): \ {'bufnr': bufnr(''), 'type': 'I', 'lnum': 3, 'col': 1}, \]) - AssertEqual - \ [ - \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, - \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, - \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, - \ {'group': 'aleerrorline', 'priority': 10, 'pos1': [1]}, - \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2]}, - \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3]}, - \ ], - \ GetMatchesWithoutIDs() + if g:has_nvim_highlight + " The output is different with the newer NeoVIM highlight API. + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'ALEErrorLine', 'priority': 10, 'pos1': [1, 1, 1073741824]}, + \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, + \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2, 1, 1073741824]}, + \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, + \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3, 1, 1073741824]} + \ ], + \ GetMatchesWithoutIDs() + else + AssertEqual + \ [ + \ {'group': 'ALEError', 'priority': 10, 'pos1': [1, 1, 1]}, + \ {'group': 'ALEWarning', 'priority': 10, 'pos1': [2, 1, 1]}, + \ {'group': 'ALEInfo', 'priority': 10, 'pos1': [3, 1, 1]}, + \ {'group': 'aleerrorline', 'priority': 10, 'pos1': [1]}, + \ {'group': 'ALEWarningLine', 'priority': 10, 'pos1': [2]}, + \ {'group': 'ALEInfoLine', 'priority': 10, 'pos1': [3]}, + \ ], + \ GetMatchesWithoutIDs() + endif " All of the highlights should be removed. call ale#highlight#RemoveHighlights() diff --git a/test/test_organize_imports.vader b/test/test_organize_imports.vader new file mode 100644 index 00000000..137326a9 --- /dev/null +++ b/test/test_organize_imports.vader @@ -0,0 +1,171 @@ +Before: + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('dummy.txt') + + let g:old_filename = expand('%:p') + let g:Callback = '' + let g:expr_list = [] + let g:message_list = [] + let g:handle_code_action_called = 0 + let g:code_actions = [] + let g:options = {} + let g:capability_checked = '' + let g:conn_id = v:null + let g:InitCallback = v:null + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/organize_imports.vim + runtime autoload/ale/code_action.vim + + function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) + + if a:linter.lsp is# 'tsserver' + call ale#lsp#MarkConnectionAsTsserver(g:conn_id) + endif + + let l:details = { + \ 'command': 'foobar', + \ 'buffer': a:buffer, + \ 'connection_id': g:conn_id, + \ 'project_root': '/foo/bar', + \} + + let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)} + endfunction + + function! ale#lsp#HasCapability(conn_id, capability) abort + let g:capability_checked = a:capability + + return 1 + endfunction + + function! ale#lsp#RegisterCallback(conn_id, callback) abort + let g:Callback = a:callback + endfunction + + function! ale#lsp#Send(conn_id, message) abort + call add(g:message_list, a:message) + + return 42 + endfunction + + function! ale#util#Execute(expr) abort + call add(g:expr_list, a:expr) + endfunction + + function! ale#code_action#HandleCodeAction(code_action) abort + let g:handle_code_action_called = 1 + call add(g:code_actions, a:code_action) + endfunction + +After: + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + + call ale#references#SetMap({}) + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + unlet! g:capability_checked + unlet! g:InitCallback + unlet! g:old_filename + unlet! g:conn_id + unlet! g:Callback + unlet! g:message_list + unlet! g:expr_list + unlet! b:ale_linters + unlet! g:options + unlet! g:code_actions + unlet! g:handle_code_action_called + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/organize_imports.vim + runtime autoload/ale/code_action.vim + +Execute(Other messages for the tsserver handler should be ignored): + call ale#organize_imports#HandleTSServerResponse(1, {'command': 'foo'}) + AssertEqual g:handle_code_action_called, 0 + +Execute(Failed organizeImports responses should be handled correctly): + call ale#organize_imports#HandleTSServerResponse( + \ 1, + \ {'command': 'organizeImports', 'request_seq': 3} + \) + AssertEqual g:handle_code_action_called, 0 + +Execute(Code actions from tsserver should be handled): + call ale#organize_imports#HandleTSServerResponse(1, { + \ 'command': 'organizeImports', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': [], + \}) + AssertEqual g:handle_code_action_called, 1 + AssertEqual [{ + \ 'description': 'Organize Imports', + \ 'changes': [], + \}], g:code_actions + +Given typescript(Some typescript file): + foo + somelongerline + bazxyzxyzxyz + +Execute(tsserver organize imports requests should be sent): + call ale#linter#Reset() + runtime ale_linters/typescript/tsserver.vim + + ALEOrganizeImports + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual + \ 'function(''ale#organize_imports#HandleTSServerResponse'')', + \ string(g:Callback) + + AssertEqual + \ [ + \ ale#lsp#tsserver_message#Change(bufnr('')), + \ [0, 'ts@organizeImports', { + \ 'scope': { + \ 'type': 'file', + \ 'args': { + \ 'file': expand('%:p'), + \ }, + \ }, + \ }] + \ ], + \ g:message_list + +Given python(Some Python file): + foo + somelongerline + bazxyzxyzxyz + +Execute(Should result in error message): + call ale#linter#Reset() + runtime ale_linters/python/pyls.vim + let b:ale_linters = ['pyls'] + + ALEOrganizeImports + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual [ + \ 'echom ''OrganizeImports currently only works with tsserver''', + \], g:expr_list diff --git a/test/test_rename.vader b/test/test_rename.vader new file mode 100644 index 00000000..98e3ef30 --- /dev/null +++ b/test/test_rename.vader @@ -0,0 +1,394 @@ +Before: + call ale#test#SetDirectory('/testplugin/test') + call ale#test#SetFilename('dummy.txt') + + let g:old_filename = expand('%:p') + let g:Callback = '' + let g:expr_list = [] + let g:message_list = [] + let g:handle_code_action_called = 0 + let g:code_actions = [] + let g:options = {} + let g:capability_checked = '' + let g:conn_id = v:null + let g:InitCallback = v:null + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/rename.vim + runtime autoload/ale/code_action.vim + + function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) + + if a:linter.lsp is# 'tsserver' + call ale#lsp#MarkConnectionAsTsserver(g:conn_id) + endif + + let l:details = { + \ 'command': 'foobar', + \ 'buffer': a:buffer, + \ 'connection_id': g:conn_id, + \ 'project_root': '/foo/bar', + \} + + let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)} + endfunction + + function! ale#lsp#HasCapability(conn_id, capability) abort + let g:capability_checked = a:capability + + return 1 + endfunction + + function! ale#lsp#RegisterCallback(conn_id, callback) abort + let g:Callback = a:callback + endfunction + + function! ale#lsp#Send(conn_id, message) abort + call add(g:message_list, a:message) + + return 42 + endfunction + + function! ale#util#Execute(expr) abort + call add(g:expr_list, a:expr) + endfunction + + function! ale#code_action#HandleCodeAction(code_action) abort + let g:handle_code_action_called = 1 + call add(g:code_actions, a:code_action) + endfunction + + function! ale#util#Input(message, value) abort + return 'a-new-name' + endfunction + + call ale#rename#SetMap({ + \ 3: { + \ 'old_name': 'oldName', + \ 'new_name': 'aNewName', + \ }, + \}) + +After: + if g:conn_id isnot v:null + call ale#lsp#RemoveConnectionWithID(g:conn_id) + endif + + call ale#rename#SetMap({}) + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + unlet! g:capability_checked + unlet! g:InitCallback + unlet! g:old_filename + unlet! g:conn_id + unlet! g:Callback + unlet! g:message_list + unlet! g:expr_list + unlet! b:ale_linters + unlet! g:options + unlet! g:code_actions + unlet! g:handle_code_action_called + + runtime autoload/ale/lsp_linter.vim + runtime autoload/ale/lsp.vim + runtime autoload/ale/util.vim + runtime autoload/ale/rename.vim + runtime autoload/ale/code_action.vim + +Execute(Other messages for the tsserver handler should be ignored): + call ale#rename#HandleTSServerResponse(1, {'command': 'foo'}) + AssertEqual g:handle_code_action_called, 0 + +Execute(Failed rename responses should be handled correctly): + call ale#rename#SetMap({3: {'old_name': 'oldName', 'new_name': 'a-test'}}) + call ale#rename#HandleTSServerResponse( + \ 1, + \ {'command': 'rename', 'request_seq': 3} + \) + AssertEqual g:handle_code_action_called, 0 + +Given typescript(Some typescript file): + foo + somelongerline + bazxyzxyzxyz + +Execute(Code actions from tsserver should be handled): + call ale#rename#HandleTSServerResponse(1, { + \ 'command': 'rename', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': { + \ 'locs': [ + \ { + \ 'file': '/foo/bar/file1.ts', + \ 'locs': [ + \ { + \ 'start': { + \ 'line': 1, + \ 'offset': 2, + \ }, + \ 'end': { + \ 'line': 3, + \ 'offset': 4, + \ }, + \ }, + \ ], + \ }, + \ { + \ 'file': '/foo/bar/file2.ts', + \ 'locs': [ + \ { + \ 'start': { + \ 'line': 10, + \ 'offset': 20, + \ }, + \ 'end': { + \ 'line': 30, + \ 'offset': 40, + \ }, + \ }, + \ ], + \ }, + \ ] + \ }, + \}) + + AssertEqual + \ [ + \ { + \ 'description': 'rename', + \ 'changes': [ + \ { + \ 'fileName': '/foo/bar/file1.ts', + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 1, + \ 'offset': 2, + \ }, + \ 'end': { + \ 'line': 3, + \ 'offset': 4, + \ }, + \ 'newText': 'aNewName', + \ }, + \ ], + \ }, + \ { + \ 'fileName': '/foo/bar/file2.ts', + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 10, + \ 'offset': 20, + \ }, + \ 'end': { + \ 'line': 30, + \ 'offset': 40, + \ }, + \ 'newText': 'aNewName', + \ }, + \ ], + \ }, + \ ], + \ } + \ ], + \ g:code_actions + +Execute(HandleTSServerResponse does nothing when no data in rename_map): + call ale#rename#HandleTSServerResponse(1, { + \ 'command': 'rename', + \ 'request_seq': -9, + \ 'success': v:true, + \ 'body': {} + \}) + + AssertEqual g:handle_code_action_called, 0 + +Execute(Prints a tsserver error message when unsuccessful): + call ale#rename#HandleTSServerResponse(1, { + \ 'command': 'rename', + \ 'request_seq': 3, + \ 'success': v:false, + \ 'message': 'This symbol cannot be renamed', + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''Error renaming "oldName" to: "aNewName". ' . + \ 'Reason: This symbol cannot be renamed'''], g:expr_list + +Execute(Does nothing when no changes): + call ale#rename#HandleTSServerResponse(1, { + \ 'command': 'rename', + \ 'request_seq': 3, + \ 'success': v:true, + \ 'body': { + \ 'locs': [] + \ } + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''Error renaming "oldName" to: "aNewName"'''], g:expr_list + +Execute(tsserver rename requests should be sent): + call ale#rename#SetMap({}) + call ale#linter#Reset() + + runtime ale_linters/typescript/tsserver.vim + call setpos('.', [bufnr(''), 2, 5, 0]) + + ALERename + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'rename', g:capability_checked + AssertEqual + \ 'function(''ale#rename#HandleTSServerResponse'')', + \ string(g:Callback) + AssertEqual + \ [ + \ ale#lsp#tsserver_message#Change(bufnr('')), + \ [0, 'ts@rename', { + \ 'file': expand('%:p'), + \ 'line': 2, + \ 'offset': 5, + \ 'arguments': { + \ 'findInComments': g:ale_rename_tsserver_find_in_comments, + \ 'findInStrings': g:ale_rename_tsserver_find_in_strings, + \ }, + \ }] + \ ], + \ g:message_list + AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name'}}, + \ ale#rename#GetMap() + +Given python(Some Python file): + foo + somelongerline + bazxyzxyzxyz + +Execute(Code actions from LSP should be handled): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \ 'result': { + \ 'changes': { + \ 'file:///foo/bar/file1.ts': [ + \ { + \ 'range': { + \ 'start': { + \ 'line': 1, + \ 'character': 2, + \ }, + \ 'end': { + \ 'line': 3, + \ 'character': 4, + \ }, + \ }, + \ 'newText': 'bla123' + \ }, + \ ], + \ }, + \ }, + \}) + + AssertEqual + \ [ + \ { + \ 'description': 'rename', + \ 'changes': [ + \ { + \ 'fileName': '/foo/bar/file1.ts', + \ 'textChanges': [ + \ { + \ 'start': { + \ 'line': 2, + \ 'offset': 3, + \ }, + \ 'end': { + \ 'line': 4, + \ 'offset': 5, + \ }, + \ 'newText': 'bla123', + \ }, + \ ], + \ }, + \ ], + \ } + \ ], + \ g:code_actions + +Execute(LSP should perform no action when no result): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''No rename result received from server'''], g:expr_list + +Execute(LSP should perform no action when no changes): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \ 'result': {}, + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''No changes received from server'''], g:expr_list + +Execute(LSP should perform no action when changes is empty): + call ale#rename#HandleLSPResponse(1, { + \ 'id': 3, + \ 'result': { + \ 'changes': [], + \ }, + \}) + + AssertEqual g:handle_code_action_called, 0 + AssertEqual ['echom ''No changes received from server'''], g:expr_list + +Execute(LSP rename requests should be sent): + call ale#rename#SetMap({}) + runtime ale_linters/python/pyls.vim + let b:ale_linters = ['pyls'] + call setpos('.', [bufnr(''), 1, 5, 0]) + + ALERename + + " We shouldn't register the callback yet. + AssertEqual '''''', string(g:Callback) + + AssertEqual type(function('type')), type(g:InitCallback) + call g:InitCallback() + + AssertEqual 'rename', g:capability_checked + AssertEqual + \ 'function(''ale#rename#HandleLSPResponse'')', + \ string(g:Callback) + + AssertEqual + \ [ + \ [1, 'textDocument/didChange', { + \ 'textDocument': { + \ 'uri': ale#path#ToURI(expand('%:p')), + \ 'version': g:ale_lsp_next_version_id - 1, + \ }, + \ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}] + \ }], + \ [0, 'textDocument/rename', { + \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}, + \ 'position': {'line': 0, 'character': 2}, + \ 'newName': 'a-new-name', + \ }], + \ ], + \ g:message_list + + AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name'}}, + \ ale#rename#GetMap() |