summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/command_callback/inko_paths/test.inko0
-rw-r--r--test/command_callback/inko_paths/tests/test/test_foo.inko0
-rw-r--r--test/command_callback/php-intelephense-project/with-composer/composer.json0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe0
-rw-r--r--test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/bin/autoimport0
-rwxr-xr-xtest/command_callback/python_paths/with_virtualenv/env/bin/yamlfix0
-rw-r--r--test/command_callback/r_paths/.Rprofile0
-rw-r--r--test/command_callback/test_elixir_credo.vader7
-rw-r--r--test/command_callback/test_erlang_elvis_command_callback.vader16
-rw-r--r--test/command_callback/test_erlang_erlc_command_callback.vader40
-rw-r--r--test/command_callback/test_fecs_command_callback.vader1
-rw-r--r--test/command_callback/test_inko_inko_callbacks.vader20
-rw-r--r--test/command_callback/test_julia_languageserver_callbacks.vader8
-rw-r--r--test/command_callback/test_php_intelephense_command_callback.vader26
-rw-r--r--test/command_callback/test_r_languageserver_callbacks.vader22
-rw-r--r--test/command_callback/test_sorbet_command_callback.vader7
-rw-r--r--test/completion/test_lsp_completion_parsing.vader6
-rw-r--r--test/fixers/test_autoimport_fixer_callback.vader50
-rw-r--r--test/fixers/test_gofmt_fixer_callback.vader14
-rw-r--r--test/fixers/test_isort_fixer_callback.vader10
-rw-r--r--test/fixers/test_luafmt_fixer_callback.vader35
-rw-r--r--test/fixers/test_ormolu_fixer_callback.vader24
-rw-r--r--test/fixers/test_phpcbf_fixer_callback.vader11
-rw-r--r--test/fixers/test_yamlfix_fixer_callback.vader50
-rw-r--r--test/handler/test_dafny_handler.vader4
-rw-r--r--test/handler/test_erlang_elvis_handler.vader37
-rw-r--r--test/handler/test_inko_handler.vader54
-rw-r--r--test/handler/test_phpcs_handler.vader11
-rw-r--r--test/handler/test_salt_salt_lint.vader34
-rw-r--r--test/handler/test_tlint_handler.vader34
-rw-r--r--test/lsp/test_other_initialize_message_handling.vader6
-rw-r--r--test/lua_files/testfile.lua0
-rwxr-xr-xtest/script/check-duplicate-tags5
-rwxr-xr-xtest/script/check-supported-tools-tables6
-rwxr-xr-xtest/script/check-tag-alignment11
-rwxr-xr-xtest/script/check-tag-references22
-rwxr-xr-xtest/script/check-toc2
-rwxr-xr-xtest/script/custom-checks34
-rwxr-xr-xtest/script/custom-linting-rules21
-rw-r--r--test/test_code_action.vader4
-rw-r--r--test/test_code_action_python.vader59
-rw-r--r--test/test_codefix.vader549
-rw-r--r--test/test_floating_preview.vader92
-rw-r--r--test/test_hover.vader87
-rw-r--r--test/test_lint_on_enter_when_file_changed.vader2
-rw-r--r--test/test_redundant_tsserver_rendering_avoided.vader30
-rw-r--r--test/test_rename.vader6
-rw-r--r--test/test_shell_detection.vader48
49 files changed, 1454 insertions, 51 deletions
diff --git a/test/command_callback/inko_paths/test.inko b/test/command_callback/inko_paths/test.inko
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/inko_paths/test.inko
diff --git a/test/command_callback/inko_paths/tests/test/test_foo.inko b/test/command_callback/inko_paths/tests/test/test_foo.inko
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/inko_paths/tests/test/test_foo.inko
diff --git a/test/command_callback/php-intelephense-project/with-composer/composer.json b/test/command_callback/php-intelephense-project/with-composer/composer.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/php-intelephense-project/with-composer/composer.json
diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe
diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe
diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport b/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport
diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix b/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix
diff --git a/test/command_callback/r_paths/.Rprofile b/test/command_callback/r_paths/.Rprofile
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/r_paths/.Rprofile
diff --git a/test/command_callback/test_elixir_credo.vader b/test/command_callback/test_elixir_credo.vader
index 3eb88846..b14444c6 100644
--- a/test/command_callback/test_elixir_credo.vader
+++ b/test/command_callback/test_elixir_credo.vader
@@ -38,3 +38,10 @@ Execute(Builds credo command with suggest mode when set to 0):
AssertLinter 'mix',
\ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
\ . 'mix help credo && mix credo suggest --format=flycheck --read-from-stdin %s'
+
+Execute(Builds credo command with a custom config file):
+ let g:ale_elixir_credo_config_file = '/home/user/custom_credo.exs'
+
+ AssertLinter 'mix',
+ \ ale#path#CdString(ale#path#Simplify(g:dir . '/elixir_paths/mix_project'))
+ \ . 'mix help credo && mix credo suggest --config-file /home/user/custom_credo.exs --format=flycheck --read-from-stdin %s'
diff --git a/test/command_callback/test_erlang_elvis_command_callback.vader b/test/command_callback/test_erlang_elvis_command_callback.vader
new file mode 100644
index 00000000..4aab49d6
--- /dev/null
+++ b/test/command_callback/test_erlang_elvis_command_callback.vader
@@ -0,0 +1,16 @@
+Before:
+ let b:file = fnamemodify(bufname(''), ':.')
+ call ale#assert#SetUpLinterTest('erlang', 'elvis')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(Default command should be correct):
+ AssertLinter 'elvis',
+ \ ale#Escape('elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file)
+
+Execute(Executable should be configurable):
+ let b:ale_erlang_elvis_executable = '/path/to/elvis'
+
+ AssertLinter '/path/to/elvis',
+ \ ale#Escape('/path/to/elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file)
diff --git a/test/command_callback/test_erlang_erlc_command_callback.vader b/test/command_callback/test_erlang_erlc_command_callback.vader
new file mode 100644
index 00000000..7d659a07
--- /dev/null
+++ b/test/command_callback/test_erlang_erlc_command_callback.vader
@@ -0,0 +1,40 @@
+Before:
+ call ale#assert#SetUpLinterTest('erlang', 'erlc')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default command should be correct.):
+ let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr(''))
+ let g:regex = 'erlc.\+-o.\+%t'
+ let g:matched = match(g:cmd, g:regex)
+
+ " match returns -1 if not found
+ AssertNotEqual
+ \ g:matched,
+ \ -1,
+ \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']'
+
+Execute(The command should accept configured executable.):
+ let b:ale_erlang_erlc_executable = '/usr/bin/erlc'
+ let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr(''))
+ let g:regex = '/usr/bin/erlc.\+-o.\+%t'
+ let g:matched = match(g:cmd, g:regex)
+
+ " match returns -1 if not found
+ AssertNotEqual
+ \ g:matched,
+ \ -1,
+ \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']'
+
+Execute(The command should accept configured options.):
+ let b:ale_erlang_erlc_options = '-I include'
+ let g:cmd = ale_linters#erlang#erlc#GetCommand(bufnr(''))
+ let g:regex = 'erlc.\+-o.\+-I include.\+%t'
+ let g:matched = match(g:cmd, g:regex)
+
+ " match returns -1 if not found
+ AssertNotEqual
+ \ g:matched,
+ \ -1,
+ \ 'Command error: expected [' . g:cmd . '] to match [' . g:regex . ']'
diff --git a/test/command_callback/test_fecs_command_callback.vader b/test/command_callback/test_fecs_command_callback.vader
index f70ad084..4287d324 100644
--- a/test/command_callback/test_fecs_command_callback.vader
+++ b/test/command_callback/test_fecs_command_callback.vader
@@ -1,5 +1,6 @@
Before:
call ale#assert#SetUpLinterTest('javascript', 'fecs')
+ runtime autoload/ale/handlers/fecs.vim
After:
call ale#assert#TearDownLinterTest()
diff --git a/test/command_callback/test_inko_inko_callbacks.vader b/test/command_callback/test_inko_inko_callbacks.vader
new file mode 100644
index 00000000..93295c91
--- /dev/null
+++ b/test/command_callback/test_inko_inko_callbacks.vader
@@ -0,0 +1,20 @@
+Before:
+ call ale#assert#SetUpLinterTest('inko', 'inko')
+ call ale#test#SetFilename('inko_paths/test.inko')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default executable path should be correct):
+ AssertLinter 'inko', ale#Escape('inko') . ' build --check --format=json %s'
+
+Execute(The inko callback should include tests/ for test paths):
+ call ale#engine#Cleanup(bufnr(''))
+ noautocmd e! inko_paths/tests/test/test_foo.inko
+ call ale#engine#InitBufferInfo(bufnr(''))
+
+ AssertLinter 'inko',
+ \ ale#Escape('inko')
+ \ . ' build --check --format=json --include '
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/inko_paths/tests/'))
+ \ . ' %s'
diff --git a/test/command_callback/test_julia_languageserver_callbacks.vader b/test/command_callback/test_julia_languageserver_callbacks.vader
index 3bc46e3d..96df81f1 100644
--- a/test/command_callback/test_julia_languageserver_callbacks.vader
+++ b/test/command_callback/test_julia_languageserver_callbacks.vader
@@ -11,16 +11,16 @@ After:
Execute(The default executable path should be correct):
AssertLinter 'julia',
\ ale#Escape('julia') .
- \' --startup-file=no --history-file=no -e ' .
- \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);')
+ \' --project=@. --startup-file=no --history-file=no -e ' .
+ \ ale#Escape('using LanguageServer; using Pkg; import StaticLint; import SymbolServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, dirname(Pkg.Types.Context().env.project_file)); server.runlinter = true; run(server);')
Execute(The executable should be configurable):
let g:ale_julia_executable = 'julia-new'
AssertLinter 'julia-new',
\ ale#Escape('julia-new') .
- \' --startup-file=no --history-file=no -e ' .
- \ ale#Escape('using LanguageServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, false); server.runlinter = true; run(server);')
+ \' --project=@. --startup-file=no --history-file=no -e ' .
+ \ ale#Escape('using LanguageServer; using Pkg; import StaticLint; import SymbolServer; server = LanguageServer.LanguageServerInstance(isdefined(Base, :stdin) ? stdin : STDIN, isdefined(Base, :stdout) ? stdout : STDOUT, dirname(Pkg.Types.Context().env.project_file)); server.runlinter = true; run(server);')
Execute(The project root should be detected correctly):
AssertLSPProject ''
diff --git a/test/command_callback/test_php_intelephense_command_callback.vader b/test/command_callback/test_php_intelephense_command_callback.vader
new file mode 100644
index 00000000..dd6adb3d
--- /dev/null
+++ b/test/command_callback/test_php_intelephense_command_callback.vader
@@ -0,0 +1,26 @@
+Before:
+ call ale#assert#SetUpLinterTest('php', 'intelephense')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default executable path should be correct):
+ AssertLinter 'intelephense',
+ \ ale#Escape('intelephense') . ' --stdio'
+
+Execute(The project path should be correct for .git directories):
+ call ale#test#SetFilename('php-intelephense-project/with-git/test.php')
+ silent! call mkdir('php-intelephense-project/with-git/.git', 'p')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git')
+
+Execute(The project path should be correct for composer.json file):
+ call ale#test#SetFilename('php-intelephense-project/with-composer/test.php')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer')
+
+Execute(The project cache should be saved in a temp dir):
+ call ale#test#SetFilename('php-intelephense-project/with-composer/test.php')
+ let g:ale_php_intelephense_config = { 'storagePath': '/tmp/intelephense' }
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer')
diff --git a/test/command_callback/test_r_languageserver_callbacks.vader b/test/command_callback/test_r_languageserver_callbacks.vader
new file mode 100644
index 00000000..9a4a1f87
--- /dev/null
+++ b/test/command_callback/test_r_languageserver_callbacks.vader
@@ -0,0 +1,22 @@
+Before:
+ call ale#assert#SetUpLinterTest('r', 'languageserver')
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(The default executable path should be correct):
+ AssertLinter 'Rscript', 'Rscript --vanilla -e ' . ale#Escape('languageserver::run()')
+
+Execute(The project root should be detected correctly):
+ AssertLSPProject '.'
+
+ call ale#test#SetFilename('r_paths/dummy/test.R')
+
+ AssertLSPProject ale#path#Simplify(g:dir . '/r_paths')
+
+Execute(Should accept configuration settings):
+ AssertLSPConfig {}
+
+ let b:ale_r_languageserver_config = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}}
+
+ AssertLSPConfig {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}}
diff --git a/test/command_callback/test_sorbet_command_callback.vader b/test/command_callback/test_sorbet_command_callback.vader
index b46e90a4..fe758635 100644
--- a/test/command_callback/test_sorbet_command_callback.vader
+++ b/test/command_callback/test_sorbet_command_callback.vader
@@ -5,6 +5,7 @@ Before:
let g:ale_ruby_sorbet_executable = 'srb'
let g:ale_ruby_sorbet_options = ''
+ let g:ale_ruby_sorbet_enable_watchman = 0
After:
call ale#assert#TearDownLinterTest()
@@ -13,6 +14,12 @@ Execute(Executable should default to srb):
AssertLinter 'srb', ale#Escape('srb')
\ . ' tc --lsp --disable-watchman'
+Execute(Able to enable watchman):
+ let g:ale_ruby_sorbet_enable_watchman = 1
+
+ AssertLinter 'srb', ale#Escape('srb')
+ \ . ' tc --lsp'
+
Execute(Should be able to set a custom executable):
let g:ale_ruby_sorbet_executable = 'bin/srb'
diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader
index d989aefe..36228c10 100644
--- a/test/completion/test_lsp_completion_parsing.vader
+++ b/test/completion/test_lsp_completion_parsing.vader
@@ -40,6 +40,7 @@ Execute(Should handle Rust completion results correctly):
\ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from', 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
+ \ {'word': 'to_vec', 'menu': 'pub fn to_vec(&self) -> Vec<T> where T: Clone,', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
\],
\ ale#completion#ParseLSPCompletions({
\ "jsonrpc":"2.0",
@@ -184,6 +185,11 @@ Execute(Should handle Rust completion results correctly):
\ "label":"from",
\ "kind":3,
\ "detail":"fn from(s: Cow<'a, str>) -> String"
+ \ },
+ \ {
+ \ "label":"to_vec",
+ \ "kind":3,
+ \ "detail":"pub fn to_vec(&self) -> Vec<T>\nwhere\n T: Clone,"
\ }
\ ]
\ })
diff --git a/test/fixers/test_autoimport_fixer_callback.vader b/test/fixers/test_autoimport_fixer_callback.vader
new file mode 100644
index 00000000..6952cbb8
--- /dev/null
+++ b/test/fixers/test_autoimport_fixer_callback.vader
@@ -0,0 +1,50 @@
+Before:
+ Save g:ale_python_autoimport_executable
+ Save g:ale_python_autoimport_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_python_autoimport_executable = 'xxxinvalid'
+ let g:ale_python_autoimport_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+ let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
+
+After:
+ Restore
+
+ unlet! b:bin_dir
+
+ call ale#test#RestoreDirectory()
+
+Execute(The autoimport callback should return the correct default values):
+ AssertEqual
+ \ 0,
+ \ ale#fixers#autoimport#Fix(bufnr(''))
+
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+ AssertEqual
+ \ {
+ \ 'command': ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) . ' -',
+ \ },
+ \ ale#fixers#autoimport#Fix(bufnr(''))
+
+Execute(The autoimport callback should respect custom options):
+ let g:ale_python_autoimport_options = '--multi-line=3 --trailing-comma'
+
+ AssertEqual
+ \ 0,
+ \ ale#fixers#autoimport#Fix(bufnr(''))
+
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
+ AssertEqual
+ \ {
+ \ 'command': ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport'))
+ \ . ' --multi-line=3 --trailing-comma -',
+ \ },
+ \ ale#fixers#autoimport#Fix(bufnr(''))
diff --git a/test/fixers/test_gofmt_fixer_callback.vader b/test/fixers/test_gofmt_fixer_callback.vader
index 16659655..99407173 100644
--- a/test/fixers/test_gofmt_fixer_callback.vader
+++ b/test/fixers/test_gofmt_fixer_callback.vader
@@ -21,10 +21,7 @@ Execute(The gofmt callback should return the correct default values):
AssertEqual
\ {
- \ 'read_temporary_file': 1,
- \ 'command': ale#Escape('xxxinvalid')
- \ . ' -l -w'
- \ . ' %t',
+ \ 'command': ale#Escape('xxxinvalid'),
\ },
\ ale#fixers#gofmt#Fix(bufnr(''))
@@ -35,11 +32,8 @@ Execute(The gofmt callback should include custom gofmt options):
AssertEqual
\ {
- \ 'read_temporary_file': 1,
\ 'command': ale#Escape('xxxinvalid')
- \ . ' -l -w'
- \ . ' ' . g:ale_go_gofmt_options
- \ . ' %t',
+ \ . ' ' . g:ale_go_gofmt_options,
\ },
\ ale#fixers#gofmt#Fix(bufnr(''))
@@ -50,9 +44,7 @@ Execute(The gofmt callback should support Go environment variables):
AssertEqual
\ {
- \ 'read_temporary_file': 1,
\ 'command': ale#Env('GO111MODULE', 'off')
- \ . ale#Escape('xxxinvalid') . ' -l -w'
- \ . ' %t',
+ \ . ale#Escape('xxxinvalid')
\ },
\ ale#fixers#gofmt#Fix(bufnr(''))
diff --git a/test/fixers/test_isort_fixer_callback.vader b/test/fixers/test_isort_fixer_callback.vader
index 7f389dcf..3941f6dd 100644
--- a/test/fixers/test_isort_fixer_callback.vader
+++ b/test/fixers/test_isort_fixer_callback.vader
@@ -5,6 +5,7 @@ Before:
" Use an invalid global executable, so we don't match it.
let g:ale_python_isort_executable = 'xxxinvalid'
let g:ale_python_isort_options = ''
+ let g:ale_python_isort_auto_pipenv = 0
call ale#test#SetDirectory('/testplugin/test/fixers')
silent cd ..
@@ -48,3 +49,12 @@ Execute(The isort callback should respect custom options):
\ . ' --multi-line=3 --trailing-comma -',
\ },
\ ale#fixers#isort#Fix(bufnr(''))
+
+Execute(Pipenv is detected when python_isort_auto_pipenv is set):
+ let g:ale_python_isort_auto_pipenv = 1
+
+ call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
+
+ AssertEqual
+ \ {'command': ale#path#BufferCdString(bufnr('')) . ale#Escape('pipenv') . ' run isort -'},
+ \ ale#fixers#isort#Fix(bufnr(''))
diff --git a/test/fixers/test_luafmt_fixer_callback.vader b/test/fixers/test_luafmt_fixer_callback.vader
new file mode 100644
index 00000000..362da118
--- /dev/null
+++ b/test/fixers/test_luafmt_fixer_callback.vader
@@ -0,0 +1,35 @@
+Before:
+ Save g:ale_lua_luafmt_executable
+ Save g:ale_lua_luafmt_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_lua_luafmt_executable = 'xxxinvalid'
+ let g:ale_lua_luafmt_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+
+After:
+ Restore
+
+ call ale#test#RestoreDirectory()
+
+Execute(The luafmt callback should return the correct default values):
+ call ale#test#SetFilename('../lua_files/testfile.lua')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid') . ' --stdin',
+ \ },
+ \ ale#fixers#luafmt#Fix(bufnr(''))
+
+Execute(The luafmt callback should include custom luafmt options):
+ let g:ale_lua_luafmt_options = "--skip-children"
+ call ale#test#SetFilename('../lua_files/testfile.lua')
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('xxxinvalid')
+ \ . ' ' . g:ale_lua_luafmt_options
+ \ . ' --stdin',
+ \ },
+ \ ale#fixers#luafmt#Fix(bufnr(''))
diff --git a/test/fixers/test_ormolu_fixer_callback.vader b/test/fixers/test_ormolu_fixer_callback.vader
new file mode 100644
index 00000000..8df3fca9
--- /dev/null
+++ b/test/fixers/test_ormolu_fixer_callback.vader
@@ -0,0 +1,24 @@
+Before:
+ Save g:ale_haskell_ormolu_executable
+ Save g:ale_haskell_ormolu_options
+
+After:
+ Restore
+
+Execute(The ormolu callback should return the correct default values):
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('ormolu')
+ \ },
+ \ ale#fixers#ormolu#Fix(bufnr(''))
+
+Execute(The ormolu executable and options should be configurable):
+ let g:ale_nix_nixpkgsfmt_executable = '/path/to/ormolu'
+ let g:ale_nix_nixpkgsfmt_options = '-h'
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('/path/to/ormolu')
+ \ . ' -h',
+ \ },
+ \ ale#fixers#nixpkgsfmt#Fix(bufnr(''))
diff --git a/test/fixers/test_phpcbf_fixer_callback.vader b/test/fixers/test_phpcbf_fixer_callback.vader
index 1663c89c..f7bcc2d8 100644
--- a/test/fixers/test_phpcbf_fixer_callback.vader
+++ b/test/fixers/test_phpcbf_fixer_callback.vader
@@ -5,6 +5,7 @@ Before:
let g:ale_php_phpcbf_executable = 'phpcbf_test'
let g:ale_php_phpcbf_standard = ''
+ let g:ale_php_phpcbf_options = ''
let g:ale_php_phpcbf_use_global = 0
call ale#test#SetDirectory('/testplugin/test/fixers')
@@ -54,6 +55,15 @@ Execute(The phpcbf callback should include the phpcbf_standard option):
\ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'},
\ ale#fixers#phpcbf#Fix(bufnr(''))
+Execute(User provided options should be used):
+ let g:ale_php_phpcbf_options = '--my-user-provided-option my-value'
+ call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php')
+
+ AssertEqual
+ \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . ale#Pad('--my-user-provided-option my-value') . ' -'},
+ \ ale#fixers#phpcbf#Fix(bufnr(''))
+
+
Before:
Save g:ale_php_phpcbf_executable
Save g:ale_php_phpcbf_standard
@@ -61,6 +71,7 @@ Before:
let g:ale_php_phpcbf_executable = 'phpcbf_test'
let g:ale_php_phpcbf_standard = ''
+ let g:ale_php_phpcbf_options = ''
let g:ale_php_phpcbf_use_global = 0
call ale#test#SetDirectory('/testplugin/test/fixers')
diff --git a/test/fixers/test_yamlfix_fixer_callback.vader b/test/fixers/test_yamlfix_fixer_callback.vader
new file mode 100644
index 00000000..3ffda91e
--- /dev/null
+++ b/test/fixers/test_yamlfix_fixer_callback.vader
@@ -0,0 +1,50 @@
+Before:
+ Save g:ale_python_yamlfix_executable
+ Save g:ale_python_yamlfix_options
+
+ " Use an invalid global executable, so we don't match it.
+ let g:ale_python_yamlfix_executable = 'xxxinvalid'
+ let g:ale_python_yamlfix_options = ''
+
+ call ale#test#SetDirectory('/testplugin/test/fixers')
+ silent cd ..
+ silent cd command_callback
+ let g:dir = getcwd()
+
+ let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
+
+After:
+ Restore
+
+ unlet! b:bin_dir
+
+ call ale#test#RestoreDirectory()
+
+Execute(The yamlfix callback should return the correct default values):
+ AssertEqual
+ \ 0,
+ \ ale#fixers#yamlfix#Fix(bufnr(''))
+
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml')
+ AssertEqual
+ \ {
+ \ 'command': ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) . ' -',
+ \ },
+ \ ale#fixers#yamlfix#Fix(bufnr(''))
+
+Execute(The yamlfix callback should respect custom options):
+ let g:ale_yaml_yamlfix_options = '--multi-line=3 --trailing-comma'
+
+ AssertEqual
+ \ 0,
+ \ ale#fixers#yamlfix#Fix(bufnr(''))
+
+ silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml')
+ AssertEqual
+ \ {
+ \ 'command': ale#path#BufferCdString(bufnr(''))
+ \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix'))
+ \ . ' --multi-line=3 --trailing-comma -',
+ \ },
+ \ ale#fixers#yamlfix#Fix(bufnr(''))
diff --git a/test/handler/test_dafny_handler.vader b/test/handler/test_dafny_handler.vader
index 674f691d..797d348e 100644
--- a/test/handler/test_dafny_handler.vader
+++ b/test/handler/test_dafny_handler.vader
@@ -8,14 +8,14 @@ Execute(The Dafny handler should parse output correctly):
AssertEqual
\ [
\ {
- \ 'bufnr': 0,
+ \ 'filename': 'File.dfy',
\ 'col': 45,
\ 'lnum': 123,
\ 'text': 'A precondition for this call might not hold.',
\ 'type': 'E'
\ },
\ {
- \ 'bufnr': 0,
+ \ 'filename': 'File.dfy',
\ 'col': 90,
\ 'lnum': 678,
\ 'text': 'This is the precondition that might not hold.',
diff --git a/test/handler/test_erlang_elvis_handler.vader b/test/handler/test_erlang_elvis_handler.vader
new file mode 100644
index 00000000..365376c8
--- /dev/null
+++ b/test/handler/test_erlang_elvis_handler.vader
@@ -0,0 +1,37 @@
+Before:
+ runtime ale_linters/erlang/elvis.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(Warning messages should be handled):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 11,
+ \ 'text': "Replace the 'if' expression on line 11 with a 'case' expression or function clauses.",
+ \ 'type': 'W',
+ \ },
+ \ {
+ \ 'lnum': 20,
+ \ 'text': 'Remove the debug call to io:format/1 on line 20.',
+ \ 'type': 'W',
+ \ },
+ \ ],
+ \ ale_linters#erlang#elvis#Handle(bufnr(''), [
+ \ "src/foo.erl:11:no_if_expression:Replace the 'if' expression on line 11 with a 'case' expression or function clauses.",
+ \ 'src/foo.erl:20:no_debug_call:Remove the debug call to io:format/1 on line 20.',
+ \ ])
+
+Execute(Line length message shouldn't contain the line itself):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 24,
+ \ 'text': 'Line 24 is too long.',
+ \ 'type': 'W',
+ \ },
+ \ ],
+ \ ale_linters#erlang#elvis#Handle(bufnr(''), [
+ \ 'src/foo.erl:24:line_length:Line 24 is too long: io:format("Look ma, too long!"),.',
+ \ ])
diff --git a/test/handler/test_inko_handler.vader b/test/handler/test_inko_handler.vader
new file mode 100644
index 00000000..6621d2d6
--- /dev/null
+++ b/test/handler/test_inko_handler.vader
@@ -0,0 +1,54 @@
+Before:
+ runtime ale_linters/inko/inko.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The inko handler should parse errors correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'filename': ale#path#Simplify('/tmp/foo.inko'),
+ \ 'lnum': 4,
+ \ 'col': 5,
+ \ 'text': 'this is an error',
+ \ 'type': 'E',
+ \ }
+ \ ],
+ \ ale#handlers#inko#Handle(bufnr(''), [
+ \ '[',
+ \ ' {',
+ \ ' "file": "/tmp/foo.inko",',
+ \ ' "line": 4,',
+ \ ' "column": 5,',
+ \ ' "message": "this is an error",',
+ \ ' "level": "error"',
+ \ ' }',
+ \ ']'
+ \ ])
+
+Execute(The inko handler should parse warnings correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'filename': ale#path#Simplify('/tmp/foo.inko'),
+ \ 'lnum': 4,
+ \ 'col': 5,
+ \ 'text': 'this is a warning',
+ \ 'type': 'W',
+ \ }
+ \ ],
+ \ ale#handlers#inko#Handle(bufnr(''), [
+ \ '[',
+ \ ' {',
+ \ ' "file": "/tmp/foo.inko",',
+ \ ' "line": 4,',
+ \ ' "column": 5,',
+ \ ' "message": "this is a warning",',
+ \ ' "level": "warning"',
+ \ ' }',
+ \ ']'
+ \ ])
+
+Execute(The inko handler should handle empty output):
+ AssertEqual [], ale#handlers#inko#Handle(bufnr(''), [])
diff --git a/test/handler/test_phpcs_handler.vader b/test/handler/test_phpcs_handler.vader
index 18accece..26d35cb8 100644
--- a/test/handler/test_phpcs_handler.vader
+++ b/test/handler/test_phpcs_handler.vader
@@ -13,7 +13,16 @@ Execute(phpcs errors should be handled):
\ 'type': 'E',
\ 'sub_type': 'style',
\ 'text': 'Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)',
- \ }],
+ \ },
+ \ {
+ \ 'lnum': 22,
+ \ 'col': 3,
+ \ 'type': 'E',
+ \ 'sub_type': 'style',
+ \ 'text': 'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks)',
+ \ },
+ \ ],
\ ale_linters#php#phpcs#Handle(bufnr(''), [
\ '/path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)',
+ \ "/path/to/some-filename.php:22:3: error - All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '\"\n'.",
\ ])
diff --git a/test/handler/test_salt_salt_lint.vader b/test/handler/test_salt_salt_lint.vader
new file mode 100644
index 00000000..7e234785
--- /dev/null
+++ b/test/handler/test_salt_salt_lint.vader
@@ -0,0 +1,34 @@
+Before:
+ runtime ale_linters/salt/salt_lint.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The salt handler should parse lines correctly and show error in severity HIGH):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 5,
+ \ 'code': 207,
+ \ 'text': 'File modes should always be encapsulated in quotation marks',
+ \ 'type': 'E'
+ \ }
+ \ ],
+ \ ale_linters#salt#salt_lint#Handle(255, [
+ \ '[{"id": "207", "message": "File modes should always be encapsulated in quotation marks", "filename": "test.sls", "linenumber": 5, "line": " - mode: 0755", "severity": "HIGH"}]'
+ \ ])
+
+
+Execute(The salt handler should parse lines correctly and show error in severity not HIGH):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 27,
+ \ 'code': 204,
+ \ 'text': 'Lines should be no longer that 160 chars',
+ \ 'type': 'W'
+ \ }
+ \ ],
+ \ ale_linters#salt#salt_lint#Handle(255, [
+ \ '[{"id": "204", "message": "Lines should be no longer that 160 chars", "filename": "test2.sls", "linenumber": 27, "line": "this line is definitely longer than 160 chars, this line is definitely longer than 160 chars, this line is definitely longer than 160 chars", "severity": "VERY_LOW"}]'
+ \ ])
diff --git a/test/handler/test_tlint_handler.vader b/test/handler/test_tlint_handler.vader
new file mode 100644
index 00000000..e146346c
--- /dev/null
+++ b/test/handler/test_tlint_handler.vader
@@ -0,0 +1,34 @@
+Before:
+ runtime ale_linters/php/tlint.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The tlint handler should calculate line numbers):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': '5',
+ \ 'col': 0,
+ \ 'sub_type':
+ \ 'style',
+ \ 'type': 'W',
+ \ 'text': ['! There should be no unused imports.', 'There should be no unused imports.', '', '', '', '', '', '', '', '']
+ \ },
+ \ {
+ \ 'lnum': '15',
+ \ 'col': 0,
+ \ 'sub_type':
+ \ 'style',
+ \ 'type': 'W',
+ \ 'text': ['! There should be no method visibility in test methods.', 'There should be no method visibility in test methods.', '', '', '', '', '', '', '', '']
+ \ },
+ \ ],
+ \ ale_linters#php#tlint#Handle(347, [
+ \ "Lints for /Users/jose/Code/Tighten/tester/tests/Unit/ExampleTest.php",
+ \ "============",
+ \ "! There should be no unused imports.",
+ \ "5 : `use Illuminate\Foundation\Testing\RefreshDatabase;`",
+ \ "! There should be no method visibility in test methods.",
+ \ "15 : ` public function testBasicTest()`",
+ \ ])
diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader
index b6ef852a..f3b53843 100644
--- a/test/lsp/test_other_initialize_message_handling.vader
+++ b/test/lsp/test_other_initialize_message_handling.vader
@@ -23,6 +23,7 @@ Before:
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ 'symbol_search': 0,
+ \ 'code_actions': 0,
\ },
\}
@@ -102,6 +103,7 @@ Execute(Capabilities should bet set up correctly):
\ 'definition': 1,
\ 'symbol_search': 1,
\ 'rename': 1,
+ \ 'code_actions': 1,
\ },
\ b:conn.capabilities
AssertEqual [[1, 'initialized', {}]], g:message_list
@@ -125,7 +127,7 @@ Execute(Disabled capabilities should be recognised correctly):
\ 'referencesProvider': v:false,
\ 'textDocumentSync': 2,
\ 'documentFormattingProvider': v:true,
- \ 'codeActionProvider': v:true,
+ \ 'codeActionProvider': v:false,
\ 'signatureHelpProvider': {
\ 'triggerCharacters': ['(', ','],
\ },
@@ -146,6 +148,7 @@ Execute(Disabled capabilities should be recognised correctly):
\ 'definition': 0,
\ 'symbol_search': 0,
\ 'rename': 0,
+ \ 'code_actions': 0,
\ },
\ b:conn.capabilities
AssertEqual [[1, 'initialized', {}]], g:message_list
@@ -197,6 +200,7 @@ Execute(Capabilities should be enabled when send as Dictionaries):
\ 'typeDefinition': 1,
\ 'symbol_search': 1,
\ 'rename': 1,
+ \ 'code_actions': 1,
\ },
\ b:conn.capabilities
AssertEqual [[1, 'initialized', {}]], g:message_list
diff --git a/test/lua_files/testfile.lua b/test/lua_files/testfile.lua
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/lua_files/testfile.lua
diff --git a/test/script/check-duplicate-tags b/test/script/check-duplicate-tags
new file mode 100755
index 00000000..ec1de788
--- /dev/null
+++ b/test/script/check-duplicate-tags
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -e
+
+grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d
diff --git a/test/script/check-supported-tools-tables b/test/script/check-supported-tools-tables
index f4305707..d238e77f 100755
--- a/test/script/check-supported-tools-tables
+++ b/test/script/check-supported-tools-tables
@@ -15,14 +15,13 @@ while read -r; do
if [[ "$REPLY" =~ ^! ]]; then
language="${REPLY/!/}"
else
- # shellcheck disable=SC2001
echo "$language - $REPLY"
fi
done < <(
grep '^\*\|^ *\*' doc/ale-supported-languages-and-tools.txt \
| sed -e '1,2d' \
| sed 's/^\* */!/' \
- | sed 's/^ *\* *\|!!\|\^\|(.*)\|`//g' \
+ | sed -E 's/^ *\* *|!!|\^|\(.*\)|`//g' \
| sed 's/ *$//'
) > "$doc_file"
@@ -30,13 +29,12 @@ while read -r; do
if [[ "$REPLY" =~ ^! ]]; then
language="${REPLY/!/}"
else
- # shellcheck disable=SC2001
echo "$language - $REPLY"
fi
done < <(
grep '^\*\|^ *\*' supported-tools.md \
| sed 's/^\* */!/' \
- | sed 's/^ *\* *\|:floppy_disk:\|:warning:\|(.*)\|\[\|\].*\|-n flag//g' \
+ | sed -E 's/^ *\* *|:floppy_disk:|:warning:|\(.*\)|\[|\].*|-n flag//g' \
| sed 's/ *$//'
) > "$readme_file"
diff --git a/test/script/check-tag-alignment b/test/script/check-tag-alignment
new file mode 100755
index 00000000..d41db160
--- /dev/null
+++ b/test/script/check-tag-alignment
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+exit_code=0
+
+# Documentation tags need to be aligned to the right margin, so look for
+# tags which aren't at the right margin.
+grep ' \*[^*]\+\*$' doc/ -r \
+ | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \
+ | grep . && exit_code=1
+
+exit $exit_code
diff --git a/test/script/check-tag-references b/test/script/check-tag-references
new file mode 100755
index 00000000..45e741fb
--- /dev/null
+++ b/test/script/check-tag-references
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+
+exit_code=0
+tag_regex='[gb]\?:\?\(ale\|ALE\)[a-zA-Z_\-]\+'
+
+tags="$(mktemp -t tags.XXXXXXXX)"
+refs="$(mktemp -t refs.XXXXXXXX)"
+# Grep for tags and references, and complain if we find a reference without
+# a tag for the reference. Only our tags will be included.
+grep --exclude=tags -roh "\\*$tag_regex\\*" doc | sed 's/*//g' | sort -u > "$tags"
+grep --exclude=tags -roh "|$tag_regex|" doc | sed 's/|//g' | sort -u > "$refs"
+
+exit_code=0
+
+if ! [[ $(comm -23 $refs $tags | wc -l) -eq 0 ]]; then
+ exit_code=1
+fi
+
+rm "$tags"
+rm "$refs"
diff --git a/test/script/check-toc b/test/script/check-toc
index 87a61262..f3f8a9ea 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="$(/usr/bin/env 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 ca9069e4..83afb28c 100755
--- a/test/script/custom-checks
+++ b/test/script/custom-checks
@@ -13,7 +13,7 @@ echo 'Custom warnings/errors follow:'
echo
set -o pipefail
-docker run -a stdout "${docker_flags[@]}" test/script/custom-linting-rules . || exit_code=$?
+docker run "${docker_flags[@]}" test/script/custom-linting-rules . || exit_code=$?
set +o pipefail
echo
@@ -23,7 +23,10 @@ echo '========================================'
echo 'Duplicate tags follow:'
echo
-grep --exclude=tags -roh '\*.*\*$' doc | sort | uniq -d || exit_code=$?
+set -o pipefail
+docker run "${docker_flags[@]}" test/script/check-duplicate-tags . || exit_code=$?
+set +o pipefail
+echo
echo '========================================'
echo 'Checking for invalid tag references'
@@ -31,14 +34,9 @@ echo '========================================'
echo 'Invalid tag references tags follow:'
echo
-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 '^+[^+]' && exit_code=1
+set -o pipefail
+docker run "${docker_flags[@]}" test/script/check-tag-references || exit_code=$?
+set +o pipefail
echo '========================================'
echo 'diff supported-tools.md and doc/ale-supported-languages-and-tools.txt tables'
@@ -46,7 +44,9 @@ echo '========================================'
echo 'Differences follow:'
echo
-test/script/check-supported-tools-tables || exit_code=$?
+set -o pipefail
+docker run "${docker_flags[@]}" test/script/check-supported-tools-tables || exit_code=$?
+set +o pipefail
echo '========================================'
echo 'Look for badly aligned doc tags'
@@ -54,18 +54,18 @@ echo '========================================'
echo 'Badly aligned tags follow:'
echo
-# Documentation tags need to be aligned to the right margin, so look for
-# tags which aren't at the right margin.
-grep ' \*[^*]\+\*$' doc/ -r \
- | awk '{ sep = index($0, ":"); if (length(substr($0, sep + 1 )) < 79) { print } }' \
- | grep . && exit_code=1
+set -o pipefail
+docker run "${docker_flags[@]}" test/script/check-tag-alignment || exit_code=$?
+set +o pipefail
echo '========================================'
echo 'Look for table of contents issues'
echo '========================================'
echo
-test/script/check-toc || exit_code=$?
+set -o pipefail
+docker run "${docker_flags[@]}" test/script/check-toc || exit_code=$?
+set +o pipefail
echo '========================================'
echo 'Check Python code'
diff --git a/test/script/custom-linting-rules b/test/script/custom-linting-rules
index 981a9459..86294f71 100755
--- a/test/script/custom-linting-rules
+++ b/test/script/custom-linting-rules
@@ -53,17 +53,29 @@ check_errors() {
regex="$1"
message="$2"
include_arg=''
+ exclude_arg=''
if [ $# -gt 2 ]; then
include_arg="--include $3"
fi
+ if [ $# -gt 3 ]; then
+ shift
+ shift
+ shift
+
+ while (( "$#" )); do
+ exclude_arg="$exclude_arg --exclude $1"
+ shift
+ done
+ fi
+
for directory in "${directories[@]}"; do
# shellcheck disable=SC2086
while read -r; do
RETURN_CODE=1
echo "$REPLY $message"
- done < <(grep -H -n "$regex" $include_arg "$directory"/**/*.vim \
+ done < <(grep -H -n "$regex" $include_arg $exclude_arg "$directory"/**/*.vim \
| grep -v 'no-custom-checks' \
| grep -o '^[^:]\+:[0-9]\+' \
| sed 's:^\./::')
@@ -92,7 +104,7 @@ if (( FIX_ERRORS )); then
done
fi
-# The arguments are: regex, explanation, [filename_filter]
+# The arguments are: regex, explanation, [filename_filter], [list, of, exclusions]
check_errors \
'^function.*) *$' \
'Function without abort keyword (See :help except-compat)'
@@ -114,7 +126,10 @@ check_errors '==?' "Use 'is?' instead of '==?'. 0 ==? 'foobar' is true"
check_errors '!=#' "Use 'isnot#' instead of '!=#'. 0 !=# 'foobar' is false"
check_errors '!=?' "Use 'isnot?' instead of '!=?'. 0 !=? 'foobar' is false"
check_errors '^ *:\?echo' "Stray echo line. Use \`execute echo\` if you want to echo something"
-check_errors $'name.:.*\'[a-z_]*[^a-z_0-9][a-z_0-9]*\',$' 'Use snake_case names for linters' '*/ale_linters/*'
+# Exclusions for grandfathered-in exceptions
+exclusions="clojure/clj_kondo.vim elixir/elixir_ls.vim go/golangci_lint.vim swift/swiftformat.vim"
+# shellcheck disable=SC2086
+check_errors $'name.:.*\'[a-z_]*[^a-z_0-9][a-z_0-9]*\',$' 'Use snake_case names for linters' '*/ale_linters/*' $exclusions
# Checks for improving type checks.
check_errors $'\\(==.\\?\\|is\\) type([\'"]\+)' "Use 'is v:t_string' instead"
check_errors '\(==.\?\|is\) type([0-9]\+)' "Use 'is v:t_number' instead"
diff --git a/test/test_code_action.vader b/test/test_code_action.vader
index 2e5d1381..7eabb759 100644
--- a/test/test_code_action.vader
+++ b/test/test_code_action.vader
@@ -3,6 +3,9 @@ Before:
let g:ale_enabled = 0
+ " Enable fix end-of-line as tests below expect that
+ set fixeol
+
runtime autoload/ale/code_action.vim
runtime autoload/ale/util.vim
@@ -211,6 +214,7 @@ Execute(End of file can be modified):
\)
AssertEqual g:test.text + [
+ \ '',
\ 'type A: string',
\ 'type B: number',
\ '',
diff --git a/test/test_code_action_python.vader b/test/test_code_action_python.vader
new file mode 100644
index 00000000..fd30633d
--- /dev/null
+++ b/test/test_code_action_python.vader
@@ -0,0 +1,59 @@
+Given python(An example Python file):
+ def main():
+ a = 1
+ c = a + 1
+
+Execute():
+ let g:changes = [
+ \ {'end': {'offset': 7, 'line': 1}, 'newText': 'func_qtffgsv', 'start': {'offset': 5, 'line': 1}},
+ \ {'end': {'offset': 9, 'line': 1}, 'newText': '', 'start': {'offset': 8, 'line': 1}},
+ \ {'end': {'offset': 15, 'line': 3}, 'newText': " return c\n\n\ndef main():\n c = func_qtffgsvi()\n", 'start': {'offset': 15, 'line': 3}}
+ \]
+
+ call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0)
+
+Expect(The changes should be applied correctly):
+ def func_qtffgsvi():
+ a = 1
+ c = a + 1
+ return c
+
+
+ def main():
+ c = func_qtffgsvi()
+
+
+Given python(Second python example):
+ import sys
+ import exifread
+
+ def main():
+ with open(sys.argv[1], 'rb') as f:
+ exif = exifread.process_file(f)
+ dt = str(exif['Image DateTime'])
+ date = dt[:10].replace(':', '-')
+
+Execute():
+ let g:changes = [
+ \ {'end': {'offset': 16, 'line': 2}, 'newText': "\n\ndef func_ivlpdpao(f):\n exif = exifread.process_file(f)\n dt = str(exif['Image DateTime'])\n date = dt[:10].replace(':', '-')\n return date\n", 'start': {'offset': 16, 'line': 2}},
+ \ {'end': {'offset': 32, 'line': 6}, 'newText': 'date = func', 'start': {'offset': 9, 'line': 6}},
+ \ {'end': {'offset': 42, 'line': 8}, 'newText': "ivlpdpao(f)\n", 'start': {'offset': 33, 'line': 6}}
+ \]
+
+ call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0)
+
+Expect(The changes should be applied correctly):
+ import sys
+ import exifread
+
+
+ def func_ivlpdpao(f):
+ exif = exifread.process_file(f)
+ dt = str(exif['Image DateTime'])
+ date = dt[:10].replace(':', '-')
+ return date
+
+
+ def main():
+ with open(sys.argv[1], 'rb') as f:
+ date = func_ivlpdpao(f)
diff --git a/test/test_codefix.vader b/test/test_codefix.vader
new file mode 100644
index 00000000..fc5470aa
--- /dev/null
+++ b/test/test_codefix.vader
@@ -0,0 +1,549 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test')
+ call ale#test#SetFilename('dummy.txt')
+ Save g:ale_buffer_info
+
+ let g:ale_buffer_info = {}
+
+ 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/codefix.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, options) abort
+ let g:handle_code_action_called = 1
+ Assert !get(a:options, 'should_save')
+ call add(g:code_actions, a:code_action)
+ endfunction
+
+ function! ale#util#Input(message, value) abort
+ return '2'
+ endfunction
+
+After:
+ Restore
+
+ if g:conn_id isnot v:null
+ call ale#lsp#RemoveConnectionWithID(g:conn_id)
+ endif
+
+ 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/codefix.vim
+ runtime autoload/ale/code_action.vim
+
+Execute(Failed codefix responses should be handled correctly):
+ call ale#codefix#HandleTSServerResponse(
+ \ 1,
+ \ {'command': 'getCodeFixes', 'request_seq': 3}
+ \)
+ AssertEqual g:handle_code_action_called, 0
+
+Given typescript(Some typescript file):
+ foo
+ somelongerline ()
+ bazxyzxyzxyz
+
+Execute(getCodeFixes from tsserver should be handled):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1, {
+ \ 'command': 'getCodeFixes',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'type': 'response',
+ \ 'body': [
+ \ {
+ \ 'description': 'Import default "x" from module "./z"',
+ \ 'fixName': 'import',
+ \ 'changes': [
+ \ {
+ \ 'fileName': "/foo/bar/file1.ts",
+ \ 'textChanges': [
+ \ {
+ \ 'end': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ },
+ \ 'newText': 'import x from "./z";^@',
+ \ 'start': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ }
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \})
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [
+ \ {
+ \ 'description': 'codefix',
+ \ 'changes': [
+ \ {
+ \ 'fileName': "/foo/bar/file1.ts",
+ \ 'textChanges': [
+ \ {
+ \ 'end': {
+ \ 'line': 2,
+ \ 'offset': 1
+ \ },
+ \ 'newText': 'import x from "./z";^@',
+ \ 'start': {
+ \ 'line': 2,
+ \ 'offset': 1
+ \ }
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \ }
+ \ ],
+ \ g:code_actions
+
+Execute(getCodeFixes from tsserver should be handled with user input if there are more than one action):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1, {
+ \ 'command': 'getCodeFixes',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'type': 'response',
+ \ 'body': [
+ \ {
+ \ 'description': 'Import default "x" from module "./z"',
+ \ 'fixName': 'import',
+ \ 'changes': [
+ \ {
+ \ 'fileName': "/foo/bar/file1.ts",
+ \ 'textChanges': [
+ \ {
+ \ 'end': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ },
+ \ 'newText': 'import x from "./z";^@',
+ \ 'start': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ }
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \ },
+ \ {
+ \ 'description': 'Import default "x" from module "./y"',
+ \ 'fixName': 'import',
+ \ 'changes': [
+ \ {
+ \ 'fileName': "/foo/bar/file1.ts",
+ \ 'textChanges': [
+ \ {
+ \ 'end': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ },
+ \ 'newText': 'import x from "./y";^@',
+ \ 'start': {
+ \ 'line': 2,
+ \ 'offset': 1,
+ \ }
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \})
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [
+ \ {
+ \ 'description': 'codefix',
+ \ 'changes': [
+ \ {
+ \ 'fileName': "/foo/bar/file1.ts",
+ \ 'textChanges': [
+ \ {
+ \ 'end': {
+ \ 'line': 2,
+ \ 'offset': 1
+ \ },
+ \ 'newText': 'import x from "./y";^@',
+ \ 'start': {
+ \ 'line': 2,
+ \ 'offset': 1
+ \ }
+ \ }
+ \ ]
+ \ }
+ \ ]
+ \ }
+ \ ],
+ \ g:code_actions
+
+Execute(Prints a tsserver error message when getCodeFixes unsuccessful):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1, {
+ \ 'command': 'getCodeFixes',
+ \ 'request_seq': 3,
+ \ 'success': v:false,
+ \ 'message': 'something is wrong',
+ \})
+
+ AssertEqual g:handle_code_action_called, 0
+ AssertEqual ['echom ''Error while getting code fixes. Reason: something is wrong'''], g:expr_list
+
+Execute(Does nothing when where are no code fixes):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1, {
+ \ 'command': 'getCodeFixes',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': []
+ \})
+
+ AssertEqual g:handle_code_action_called, 0
+ AssertEqual ['echom ''No code fixes available.'''], g:expr_list
+
+Execute(tsserver codefix requests should be sent):
+ call ale#linter#Reset()
+
+ runtime ale_linters/typescript/tsserver.vim
+ let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'code': 2304}]}}
+ call setpos('.', [bufnr(''), 2, 16, 0])
+
+ " ALECodeAction
+ call ale#codefix#Execute(0)
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'code_actions', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#codefix#HandleTSServerResponse'')',
+ \ string(g:Callback)
+ AssertEqual
+ \ [
+ \ ale#lsp#tsserver_message#Change(bufnr('')),
+ \ [0, 'ts@getCodeFixes', {
+ \ 'startLine': 2,
+ \ 'startOffset': 16,
+ \ 'endLine': 2,
+ \ 'endOffset': 17,
+ \ 'file': expand('%:p'),
+ \ 'errorCodes': [2304],
+ \ }]
+ \ ],
+ \ g:message_list
+
+Execute(tsserver codefix requests should be sent only for error with code):
+ call ale#linter#Reset()
+
+ runtime ale_linters/typescript/tsserver.vim
+ let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 16}, {'lnum': 2, 'col': 16, 'code': 2304}]}}
+ call setpos('.', [bufnr(''), 2, 16, 0])
+
+ " ALECodeAction
+ call ale#codefix#Execute(0)
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'code_actions', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#codefix#HandleTSServerResponse'')',
+ \ string(g:Callback)
+ AssertEqual
+ \ [
+ \ ale#lsp#tsserver_message#Change(bufnr('')),
+ \ [0, 'ts@getCodeFixes', {
+ \ 'startLine': 2,
+ \ 'startOffset': 16,
+ \ 'endLine': 2,
+ \ 'endOffset': 17,
+ \ 'file': expand('%:p'),
+ \ 'errorCodes': [2304],
+ \ }]
+ \ ],
+ \ g:message_list
+
+Execute(getApplicableRefactors from tsserver should be handled):
+ call ale#codefix#SetMap({3: {
+ \ 'buffer': expand('%:p'),
+ \ 'line': 1,
+ \ 'column': 2,
+ \ 'end_line': 3,
+ \ 'end_column': 4,
+ \ 'connection_id': 0,
+ \}})
+ call ale#codefix#HandleTSServerResponse(1,
+ \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [{'actions': [{'description': 'Extract to constant in enclosing scope', 'name': 'constant_scope_0'}], 'description': 'Extract constant', 'name': 'Extract Symbol'}, {'actions': [{'description': 'Extract to function in module scope', 'name': 'function_scope_1'}], 'description': 'Extract function', 'name': 'Extract Symbol'}], 'command': 'getApplicableRefactors'})
+
+ AssertEqual
+ \ [
+ \ [0, 'ts@getEditsForRefactor', {
+ \ 'startLine': 1,
+ \ 'startOffset': 2,
+ \ 'endLine': 3,
+ \ 'endOffset': 5,
+ \ 'file': expand('%:p'),
+ \ 'refactor': 'Extract Symbol',
+ \ 'action': 'function_scope_1',
+ \ }]
+ \ ],
+ \ g:message_list
+
+Execute(getApplicableRefactors should print error on failure):
+ call ale#codefix#SetMap({3: {
+ \ 'buffer': expand('%:p'),
+ \ 'line': 1,
+ \ 'column': 2,
+ \ 'end_line': 3,
+ \ 'end_column': 4,
+ \ 'connection_id': 0,
+ \}})
+ call ale#codefix#HandleTSServerResponse(1,
+ \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getApplicableRefactors'})
+
+ AssertEqual ['echom ''Error while getting applicable refactors. Reason: oops'''], g:expr_list
+
+Execute(getApplicableRefactors should do nothing if there are no refactors):
+ call ale#codefix#SetMap({3: {
+ \ 'buffer': expand('%:p'),
+ \ 'line': 1,
+ \ 'column': 2,
+ \ 'end_line': 3,
+ \ 'end_column': 4,
+ \ 'connection_id': 0,
+ \}})
+ call ale#codefix#HandleTSServerResponse(1,
+ \ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [], 'command': 'getApplicableRefactors'})
+
+ AssertEqual ['echom ''No applicable refactors available.'''], g:expr_list
+
+Execute(getEditsForRefactor from tsserver should be handled):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1,
+ \{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': {'edits': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}], 'renameLocation': {'offset': 3, 'line': 8}, 'renameFilename': '/foo/bar/file.ts'}, 'command': 'getEditsForRefactor' }
+ \)
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [
+ \ {
+ \ 'description': 'editsForRefactor',
+ \ 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}],
+ \ }
+ \ ],
+ \ g:code_actions
+
+Execute(getEditsForRefactor should print error on failure):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleTSServerResponse(1,
+ \{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getEditsForRefactor' }
+ \)
+
+ AssertEqual ['echom ''Error while getting edits for refactor. Reason: oops'''], g:expr_list
+
+Execute(Failed LSP responses should be handled correctly):
+ call ale#codefix#HandleLSPResponse(
+ \ 1,
+ \ {'method': 'workspace/applyEdit', 'request_seq': 3}
+ \)
+ AssertEqual g:handle_code_action_called, 0
+
+Given python(Some python file):
+ def main():
+ a = 1
+ b = a + 2
+
+Execute("workspace/applyEdit" from LSP should be handled):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleLSPResponse(1,
+ \ {'id': 0, 'jsonrpc': '2.0', 'method': 'workspace/applyEdit', 'params': {'edit': {'changes': {'file:///foo/bar/file.ts': [{'range': {'end': {'character': 27, 'line': 7}, 'start': {'character': 27, 'line': 7}}, 'newText': ', Config'}, {'range': {'end': {'character': 12, 'line': 96}, 'start': {'character': 2, 'line': 94}}, 'newText': 'await newFunction(redis, imageKey, cover, config);'}, {'range': {'end': {'character': 2, 'line': 99}, 'start': {'character': 2, 'line': 99}}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@'}]}}}})
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [{'description': 'applyEdit', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 28, 'line': 8}, 'newText': ', Config', 'start': {'offset': 28, 'line': 8}}, {'end': {'offset': 13, 'line': 97}, 'newText': 'await newFunction(redis, imageKey, cover, config);', 'start': {'offset': 3, 'line': 95}}, {'end': {'offset': 3, 'line': 100}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@', 'start': {'offset': 3, 'line': 100}}]}]}],
+ \ g:code_actions
+
+Execute(Code Actions from LSP should be handled with user input if there are more than one action):
+ call ale#codefix#SetMap({2: {}})
+ call ale#codefix#HandleLSPResponse(1,
+ \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'title': 'fake for testing'}, {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'title': 'Add ''createVideo'' to existing import declaration from "./video"', 'command': '_typescript.applyWorkspaceEdit'}]})
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 32, 'line': 3}, 'newText': ', createVideo', 'start': {'offset': 32, 'line': 3}}]}]}],
+ \ g:code_actions
+
+Execute(Code Actions from LSP should be handled when returned with documentChanges):
+ call ale#codefix#SetMap({2: {}})
+ call ale#codefix#HandleLSPResponse(1,
+ \ {'id': 2, 'jsonrpc': '2.0', 'result': [{'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 4, 'line': 2}, 'start': {'character': 4, 'line': 1}}, 'newText': ''}, {'range': {'end': {'character': 9, 'line': 2}, 'start': {'character': 8, 'line': 2}}, 'newText': '(1)'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.inline', 'title': 'Inline variable', 'command': v:null}, {'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 0, 'line': 0}, 'start': {'character': 0, 'line': 0}}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@'}, {'range': {'end': {'character': 9, 'line': 1}, 'start': {'character': 8, 'line': 1}}, 'newText': 'func_bomdjnxh()^@'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.extract', 'title': 'Extract expression into function ''func_bomdjnxh''', 'command': v:null}]})
+
+ AssertEqual g:handle_code_action_called, 1
+ AssertEqual
+ \ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/test.py', 'textChanges': [{'end': {'offset': 1, 'line': 1}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@', 'start': {'offset': 1, 'line': 1}}, {'end': {'offset': 10, 'line': 2}, 'newText': 'func_bomdjnxh()^@', 'start': {'offset': 9, 'line': 2}}]}]}],
+ \ g:code_actions
+
+Execute(LSP Code Actions handles command responses):
+ call ale#codefix#SetMap({3: {
+ \ 'connection_id': 0,
+ \}})
+ call ale#codefix#HandleLSPResponse(1,
+ \ {'id': 3, 'jsonrpc': '2.0', 'result': [{'kind': 'refactor', 'title': 'Extract to inner function in function ''getVideo''', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_0', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to inner function in function ''getVideo''', 'command': '_typescript.applyRefactoring'}}, {'kind': 'refactor', 'title': 'Extract to function in module scope', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_1', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to function in module scope', 'command': '_typescript.applyRefactoring'}}]})
+
+ AssertEqual
+ \ [[0, 'workspace/executeCommand', {'arguments': [{'file': '/foo/bar/file.ts', 'action': 'function_scope_1', 'endOffset': 0, 'refactor': 'Extract Symbol', 'endLine': 68, 'startLine': 65, 'startOffset': 1}], 'command': '_typescript.applyRefactoring'}]],
+ \ g:message_list
+
+
+Execute(Prints message when LSP code action returns no results):
+ call ale#codefix#SetMap({3: {}})
+ call ale#codefix#HandleLSPResponse(1,
+ \ {'id': 3, 'jsonrpc': '2.0', 'result': []})
+
+ AssertEqual g:handle_code_action_called, 0
+ AssertEqual ['echom ''No code actions received from server'''], g:expr_list
+
+Execute(LSP code action requests should be sent):
+ call ale#linter#Reset()
+
+ runtime ale_linters/python/jedils.vim
+ let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}}
+ call setpos('.', [bufnr(''), 2, 5, 0])
+
+ " ALECodeAction
+ call ale#codefix#Execute(0)
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'code_actions', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#codefix#HandleLSPResponse'')',
+ \ string(g:Callback)
+ AssertEqual
+ \ [
+ \ [0, 'textDocument/codeAction', {
+ \ 'context': {
+ \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
+ \ },
+ \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}},
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
+ \ }]
+ \ ],
+ \ g:message_list[-1:]
+
+Execute(LSP code action requests should be sent only for error with code):
+ call ale#linter#Reset()
+
+ runtime ale_linters/python/jedils.vim
+ let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}}
+ call setpos('.', [bufnr(''), 2, 5, 0])
+
+ " ALECodeAction
+ call ale#codefix#Execute(0)
+
+ " We shouldn't register the callback yet.
+ AssertEqual '''''', string(g:Callback)
+
+ AssertEqual type(function('type')), type(g:InitCallback)
+ call g:InitCallback()
+
+ AssertEqual 'code_actions', g:capability_checked
+ AssertEqual
+ \ 'function(''ale#codefix#HandleLSPResponse'')',
+ \ string(g:Callback)
+ AssertEqual
+ \ [
+ \ [0, 'textDocument/codeAction', {
+ \ 'context': {
+ \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
+ \ },
+ \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}},
+ \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
+ \ }]
+ \ ],
+ \ g:message_list[-1:]
diff --git a/test/test_floating_preview.vader b/test/test_floating_preview.vader
new file mode 100644
index 00000000..43415556
--- /dev/null
+++ b/test/test_floating_preview.vader
@@ -0,0 +1,92 @@
+Before:
+ let g:ale_floating_preview = 0
+ let g:ale_hover_to_floating_preview = 0
+ let g:ale_detail_to_floating_preview = 0
+
+ runtime autoload/ale/floating_preview.vim
+
+ let g:floated_lines = []
+ let g:floating_preview_show_called = 0
+
+ " Stub out so we can track the call
+ function! ale#floating_preview#Show(lines, ...) abort
+ let g:floating_preview_show_called = 1
+ let g:floated_lines = a:lines
+ endfunction
+
+ let g:ale_buffer_info = {
+ \ bufnr('%'): {
+ \ 'loclist': [
+ \ {
+ \ 'lnum': 1,
+ \ 'col': 10,
+ \ 'bufnr': bufnr('%'),
+ \ 'vcol': 0,
+ \ 'linter_name': 'notalinter',
+ \ 'nr': -1,
+ \ 'type': 'E',
+ \ 'code': 'semi',
+ \ 'text': "Missing semicolon.\r",
+ \ 'detail': "Every statement should end with a semicolon\nsecond line",
+ \ },
+ \ ],
+ \ }
+ \}
+
+ call ale#linter#Reset()
+ call ale#linter#PreventLoading('javascript')
+
+After:
+ Restore
+
+ let g:ale_floating_preview = 0
+ let g:ale_hover_to_floating_preview = 0
+ let g:ale_detail_to_floating_preview = 0
+
+ call cursor(1, 1)
+
+ let g:ale_buffer_info = {}
+
+ " Close the preview window if it's open.
+ if &filetype is# 'ale-preview'
+ noautocmd :q!
+ endif
+
+ call ale#linter#Reset()
+
+
+Given javascript(A file with warnings/errors):
+ var x = 3 + 12345678
+ var x = 5*2 + parseInt("10");
+ // comment
+
+Execute(Floating preview is used with ALEDetail when g:ale_floating_preview set):
+ let g:ale_floating_preview = 1
+
+ call cursor(1, 10)
+
+ ALEDetail
+
+ let expected = ["Every statement should end with a semicolon", "second line"]
+
+ AssertEqual 1, g:floating_preview_show_called
+ AssertEqual expected, g:floated_lines
+
+Execute(Floating preview is used with ALEDetail when g:ale_detail_to_floating_preview set):
+ let g:ale_detail_to_floating_preview = 1
+
+ call cursor(1, 10)
+
+ ALEDetail
+
+ let expected = ["Every statement should end with a semicolon", "second line"]
+
+ AssertEqual 1, g:floating_preview_show_called
+ AssertEqual expected, g:floated_lines
+
+Execute(Floating preview is not used with ALEDetail by default):
+ call cursor(1, 10)
+
+ ALEDetail
+
+ AssertEqual 0, g:floating_preview_show_called
diff --git a/test/test_hover.vader b/test/test_hover.vader
index 9689cda2..7a9c8d91 100644
--- a/test/test_hover.vader
+++ b/test/test_hover.vader
@@ -7,9 +7,25 @@ Before:
let g:item_list = []
let g:show_message_arg_list = []
+ let g:ale_floating_preview = 0
+ let g:ale_hover_to_floating_preview = 0
+ let g:ale_detail_to_floating_preview = 0
+
runtime autoload/ale/linter.vim
runtime autoload/ale/lsp.vim
+ runtime autoload/ale/lsp_linter.vim
runtime autoload/ale/util.vim
+ runtime autoload/ale/floating_preview.vim
+ runtime autoload/ale/hover.vim
+
+ let g:floated_lines = []
+ let g:floating_preview_show_called = 0
+
+ " Stub out so we can track the call
+ function! ale#floating_preview#Show(lines, ...) abort
+ let g:floating_preview_show_called = 1
+ let g:floated_lines = a:lines
+ endfunction
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
let g:Callback = a:callback
@@ -50,6 +66,7 @@ Before:
\)
endfunction
+
After:
call ale#hover#SetMap({})
call ale#test#RestoreDirectory()
@@ -65,6 +82,7 @@ After:
runtime autoload/ale/lsp_linter.vim
runtime autoload/ale/lsp.vim
runtime autoload/ale/util.vim
+ runtime autoload/ale/floating_preview.vim
Given python(Some Python file):
foo
@@ -101,7 +119,7 @@ Execute(tsserver quickinfo responses will null missing bodies should be handled)
AssertEqual {}, ale#hover#GetMap()
Execute(tsserver quickinfo displayString values should be displayed):
- call ale#hover#SetMap({3: {}})
+ call ale#hover#SetMap({3: {'buffer': bufnr('')}})
call ale#hover#HandleTSServerResponse(
\ 1,
\ {
@@ -168,8 +186,30 @@ Execute(LSP hover response with lists of strings and marked strings should be ha
\], g:show_message_arg_list
AssertEqual {}, ale#hover#GetMap()
+Execute(LSP hover with ale_floating_preview should float):
+ let g:ale_floating_preview = 1
+
+ call HandleValidLSPResult({'contents': "the message\ncontinuing"})
+
+ AssertEqual 1, g:floating_preview_show_called
+ AssertEqual ["the message", "continuing"], g:floated_lines
+
+Execute(LSP hover ale_hover_to_floating_preview should float):
+ let g:ale_hover_to_floating_preview = 1
+
+ call HandleValidLSPResult({'contents': "the message\ncontinuing"})
+
+ AssertEqual 1, g:floating_preview_show_called
+ AssertEqual ["the message", "continuing"], g:floated_lines
+
+
+Execute(LSP hover by default should not float):
+ call HandleValidLSPResult({'contents': "the message\ncontinuing"})
+
+ AssertEqual 0, g:floating_preview_show_called
+
Execute(tsserver responses for documentation requests should be handled):
- call ale#hover#SetMap({3: {'show_documentation': 1}})
+ call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})
call ale#hover#HandleTSServerResponse(
\ 1,
@@ -187,3 +227,46 @@ Execute(tsserver responses for documentation requests should be handled):
" The preview window should show the text.
AssertEqual ['foo is a very good method'], ale#test#GetPreviewWindowText()
silent! pclose
+
+Execute(hover with show_documentation should be in the preview window, not floating):
+ let g:ale_hover_to_floating_preview = 1
+ let g:ale_floating_preview = 1
+
+ call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})
+
+ call ale#hover#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'quickinfo',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': {
+ \ 'documentation': 'foo is a very good method',
+ \ 'displayString': 'foo bar ',
+ \ },
+ \ }
+ \)
+
+ let expected = ["Every statement should end with a semicolon", "second line"]
+
+ AssertEqual 0, g:floating_preview_show_called
+
+Execute(TSServer hover without show_documentation and ale_floating_preview should float):
+ let g:ale_floating_preview = 1
+
+ call ale#hover#SetMap({3: {'buffer': bufnr('')}})
+
+ call ale#hover#HandleTSServerResponse(
+ \ 1,
+ \ {
+ \ 'command': 'quickinfo',
+ \ 'request_seq': 3,
+ \ 'success': v:true,
+ \ 'body': {
+ \ 'displayString': "the message\ncontinuing",
+ \ },
+ \ }
+ \)
+
+ AssertEqual 1, g:floating_preview_show_called
+ AssertEqual ["the message", "continuing"], g:floated_lines
diff --git a/test/test_lint_on_enter_when_file_changed.vader b/test/test_lint_on_enter_when_file_changed.vader
index 88493005..0d4c4af8 100644
--- a/test/test_lint_on_enter_when_file_changed.vader
+++ b/test/test_lint_on_enter_when_file_changed.vader
@@ -35,7 +35,7 @@ After:
Execute(The file changed event function should set b:ale_file_changed):
let g:ale_lint_on_enter = 0
- if has('gui')
+ if has('gui_running')
new
else
e test
diff --git a/test/test_redundant_tsserver_rendering_avoided.vader b/test/test_redundant_tsserver_rendering_avoided.vader
index 6125ebc2..bde5d152 100644
--- a/test/test_redundant_tsserver_rendering_avoided.vader
+++ b/test/test_redundant_tsserver_rendering_avoided.vader
@@ -111,7 +111,7 @@ Execute(An initial list of semantic errors should be handled):
Assert g:ale_handle_loclist_called
-Execute(Subsequent empty lists should be ignored):
+Execute(Subsequent empty lists should be ignored - semantic):
let g:ale_buffer_info[bufnr('')].semantic_loclist = []
call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', ''))
@@ -138,3 +138,31 @@ Execute(Non-empty then non-empty semantic errors should be handled):
call ale#lsp_linter#HandleLSPResponse(1, CreateError('semanticDiag', 'x'))
Assert g:ale_handle_loclist_called
+
+Execute(Subsequent empty lists should be ignored - suggestion):
+ let g:ale_buffer_info[bufnr('')].suggestion_loclist = []
+
+ call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', ''))
+
+ Assert !g:ale_handle_loclist_called
+
+Execute(Empty then non-empty suggestion messages should be handled):
+ let g:ale_buffer_info[bufnr('')].suggestion_loclist = []
+
+ call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', 'x'))
+
+ Assert g:ale_handle_loclist_called
+
+Execute(Non-empty then empt suggestion messages should be handled):
+ let g:ale_buffer_info[bufnr('')].suggestion_loclist = CreateLoclist('x')
+
+ call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', ''))
+
+ Assert g:ale_handle_loclist_called
+
+Execute(Non-empty then non-empty suggestion messages should be handled):
+ let g:ale_buffer_info[bufnr('')].suggestion_loclist = CreateLoclist('x')
+
+ call ale#lsp_linter#HandleLSPResponse(1, CreateError('suggestionDiag', 'x'))
+
+ Assert g:ale_handle_loclist_called
diff --git a/test/test_rename.vader b/test/test_rename.vader
index 2e8b746e..5bc655f4 100644
--- a/test/test_rename.vader
+++ b/test/test_rename.vader
@@ -269,7 +269,7 @@ Execute(tsserver rename requests should be sent):
\ }]
\ ],
\ g:message_list
- AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name', 'force_save': 0}},
+ AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name'}},
\ ale#rename#GetMap()
Given python(Some Python file):
@@ -470,7 +470,7 @@ Execute(LSP rename requests should be sent):
let b:ale_linters = ['pyls']
call setpos('.', [bufnr(''), 1, 5, 0])
- ALERename!
+ ALERename
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
@@ -500,5 +500,5 @@ Execute(LSP rename requests should be sent):
\ ],
\ g:message_list
- AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name', 'force_save': 1}},
+ AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name'}},
\ ale#rename#GetMap()
diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader
index 697054d0..11d801c3 100644
--- a/test/test_shell_detection.vader
+++ b/test/test_shell_detection.vader
@@ -127,3 +127,51 @@ Execute(The dash dialect should be used for the shell and the base function):
Execute(dash should be used for shellcheck):
AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a Bash shellcheck shell directive):
+ # shellcheck shell=bash
+
+Execute(bash dialect should be detected appropriately):
+ AssertEqual 'bash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a sh shellcheck shell directive):
+ #shellcheck shell=sh
+
+Execute(sh dialect should be detected appropriately):
+ AssertEqual 'sh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a tcsh shellcheck shell directive):
+ # shellcheck shell=tcsh
+
+Execute(tcsh dialect should be detected appropriately):
+ AssertEqual 'tcsh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a zsh shellcheck shell directive):
+ # shellcheck shell=zsh
+
+Execute(zsh dialect should be detected appropriately):
+ AssertEqual 'zsh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a csh shellcheck shell directive):
+ # shellcheck shell=csh
+
+Execute(zsh dialect should be detected appropriately):
+ AssertEqual 'csh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a ksh shellcheck shell directive):
+ # shellcheck shell=ksh
+
+Execute(ksh dialect should be detected appropriately):
+ AssertEqual 'ksh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a dash shellcheck shell directive):
+ # shellcheck shell=dash
+
+Execute(dash dialect should be detected appropriately):
+ AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a ash shellcheck shell directive):
+ # shellcheck shell=ash
+
+Execute(dash dialect should be detected for ash that shellcheck does not support):
+ AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))