summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml5
-rw-r--r--.travis.yml4
-rw-r--r--README.md8
-rw-r--r--ale_linters/erlang/dialyzer.vim4
-rw-r--r--ale_linters/eruby/ruumba.vim2
-rw-r--r--ale_linters/ink/ls.vim35
-rw-r--r--[-rwxr-xr-x]ale_linters/javascript/flow.vim0
-rw-r--r--ale_linters/javascript/standard.vim1
-rw-r--r--[-rwxr-xr-x]ale_linters/less/lessc.vim0
-rw-r--r--ale_linters/markdown/mdl.vim11
-rw-r--r--ale_linters/php/psalm.vim6
-rw-r--r--[-rwxr-xr-x]ale_linters/powershell/powershell.vim0
-rw-r--r--ale_linters/ruby/brakeman.vim2
-rw-r--r--ale_linters/ruby/debride.vim42
-rw-r--r--ale_linters/ruby/rails_best_practices.vim2
-rw-r--r--ale_linters/ruby/reek.vim2
-rw-r--r--ale_linters/ruby/rubocop.vim2
-rw-r--r--ale_linters/ruby/sorbet.vim2
-rw-r--r--ale_linters/ruby/standardrb.vim2
-rw-r--r--ale_linters/scala/metals.vim48
-rw-r--r--ale_linters/sh/shell.vim4
-rw-r--r--ale_linters/solidity/solc.vim35
-rw-r--r--[-rwxr-xr-x]ale_linters/terraform/terraform.vim0
-rw-r--r--autoload/ale/c.vim18
-rw-r--r--autoload/ale/code_action.vim163
-rw-r--r--autoload/ale/completion.vim63
-rw-r--r--autoload/ale/fix.vim4
-rw-r--r--autoload/ale/fix/registry.vim17
-rw-r--r--autoload/ale/fixers/dfmt.vim18
-rw-r--r--autoload/ale/fixers/nixpkgsfmt.vim12
-rw-r--r--autoload/ale/fixers/rubocop.vim2
-rw-r--r--autoload/ale/fixers/sorbet.vim2
-rw-r--r--autoload/ale/fixers/sqlformat.vim16
-rw-r--r--autoload/ale/fixers/standardrb.vim2
-rw-r--r--autoload/ale/handlers/languagetool.vim5
-rw-r--r--autoload/ale/handlers/ruby.vim8
-rw-r--r--autoload/ale/highlight.vim6
-rw-r--r--autoload/ale/linter.vim3
-rw-r--r--autoload/ale/lsp.vim6
-rw-r--r--autoload/ale/lsp/message.vim10
-rw-r--r--autoload/ale/lsp/tsserver_message.vim28
-rw-r--r--autoload/ale/organize_imports.vim59
-rw-r--r--autoload/ale/path.vim4
-rw-r--r--autoload/ale/rename.vim225
-rw-r--r--autoload/ale/ruby.vim7
-rw-r--r--autoload/ale/sign.vim71
-rw-r--r--autoload/ale/util.vim3
-rw-r--r--doc/ale-cs.txt23
-rw-r--r--doc/ale-d.txt9
-rw-r--r--doc/ale-development.txt11
-rw-r--r--doc/ale-ink.txt40
-rw-r--r--doc/ale-nix.txt24
-rw-r--r--doc/ale-ruby.txt26
-rw-r--r--doc/ale-scala.txt26
-rw-r--r--doc/ale-solidity.txt12
-rw-r--r--doc/ale-sql.txt18
-rw-r--r--doc/ale-supported-languages-and-tools.txt8
-rw-r--r--doc/ale.txt125
-rw-r--r--plugin/ale.vim7
-rw-r--r--supported-tools.md12
-rw-r--r--[-rwxr-xr-x]test/command_callback/ink_paths/story/main.ink (renamed from test/command_callback/psalm-project/vendor/bin/psalm-language-server)0
-rwxr-xr-xtest/command_callback/psalm-project/vendor/bin/psalm0
-rw-r--r--test/command_callback/test_ink_ls_command_callbacks.vader22
-rw-r--r--test/command_callback/test_languagetool_command_callback.vader9
-rw-r--r--test/command_callback/test_markdown_mdl_command_callback.vader6
-rw-r--r--test/command_callback/test_psalm_command_callbacks.vader10
-rw-r--r--test/command_callback/test_scala_metals.vader20
-rw-r--r--test/command_callback/test_solc_command_callback.vader13
-rw-r--r--test/completion/test_completion_events.vader53
-rw-r--r--test/completion/test_lsp_completion_messages.vader33
-rw-r--r--test/completion/test_tsserver_completion_parsing.vader40
-rw-r--r--test/d_files/test.d0
-rw-r--r--test/fix/test_ale_fix.vader90
-rw-r--r--test/fixers/test_dfmt_fixer_callback.vader40
-rw-r--r--test/fixers/test_nixpkgsfmt_fixer_callback.vader24
-rw-r--r--test/fixers/test_sqlformat_fixer_callback.vader24
-rw-r--r--test/handler/test_debride_handler.vader27
-rw-r--r--test/handler/test_mdl_handler.vader25
-rw-r--r--[-rwxr-xr-x]test/handler/test_powershell_handler.vader0
-rw-r--r--test/handler/test_shell_handler.vader135
-rw-r--r--test/handler/test_solc_handler.vader30
-rw-r--r--[-rwxr-xr-x]test/handler/test_terraform_handler.vader0
-rw-r--r--test/lsp/test_lsp_client_messages.vader3
-rw-r--r--test/lsp/test_other_initialize_message_handling.vader5
-rwxr-xr-xtest/script/check-supported-tools-tables5
-rwxr-xr-xtest/script/custom-checks4
-rw-r--r--test/sign/test_linting_sets_signs.vader14
-rw-r--r--test/sign/test_sign_column_highlighting.vader2
-rw-r--r--test/sign/test_sign_limits.vader2
-rw-r--r--test/sign/test_sign_parsing.vader103
-rw-r--r--test/sign/test_sign_placement.vader39
-rw-r--r--test/test_ale_toggle.vader4
-rw-r--r--test/test_autocmd_commands.vader4
-rw-r--r--test/test_code_action.vader334
-rw-r--r--test/test_filetype_linter_defaults.vader4
-rw-r--r--test/test_highlight_placement.vader2
-rw-r--r--test/test_organize_imports.vader171
-rw-r--r--test/test_rename.vader394
-rw-r--r--test/test_results_not_cleared_when_opening_loclist.vader2
-rw-r--r--test/test_setting_problems_found_in_previous_buffers.vader2
100 files changed, 2803 insertions, 202 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 54113953..aca83191 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -5,6 +5,11 @@ clone_depth: 10
# Use the directory C:\testplugin so test directories will mostly work.
clone_folder: C:\testplugin
+branches:
+ only:
+ - master
+ - /v\d+\.\d+\.(x|\d+)/
+
# Cache the vim and vader directories between builds.
cache:
- C:\vim -> .appveyor.yml
diff --git a/.travis.yml b/.travis.yml
index d48c1e08..00baff1c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,10 @@ sudo: required
services:
- docker
language: generic
+branches:
+ only:
+ - master
+ - /^v\d+\.\d+\.(x|\d+)$/
env:
- OPTIONS=--vim-80-only
- OPTIONS=--vim-81-only
diff --git a/README.md b/README.md
index 81c372c3..1488d756 100644
--- a/README.md
+++ b/README.md
@@ -189,6 +189,14 @@ completion manually with `<C-x><C-o>`.
set omnifunc=ale#completion#OmniFunc
```
+When working with TypeScript files, ALE supports automatic imports from
+external modules. This behavior is disabled by default and can be enabled by
+setting:
+
+```vim
+let g:ale_completion_tsserver_autoimport = 1
+```
+
See `:help ale-completion` for more information.
<a name="usage-go-to-definition"></a>
diff --git a/ale_linters/erlang/dialyzer.vim b/ale_linters/erlang/dialyzer.vim
index 7af64c4f..395647a0 100644
--- a/ale_linters/erlang/dialyzer.vim
+++ b/ale_linters/erlang/dialyzer.vim
@@ -15,10 +15,10 @@ endfunction
function! ale_linters#erlang#dialyzer#FindPlt(buffer) abort
let l:plt_file = ''
let l:rebar3_profile = ale_linters#erlang#dialyzer#GetRebar3Profile(a:buffer)
- let l:plt_file_directory = ale#path#FindNearestDirectory(a:buffer, '_build' . l:rebar3_profile)
+ let l:plt_file_directory = ale#path#FindNearestDirectory(a:buffer, '_build/' . l:rebar3_profile)
if !empty(l:plt_file_directory)
- let l:plt_file = split(globpath(l:plt_file_directory, '/*_plt'), '\n')
+ let l:plt_file = globpath(l:plt_file_directory, '*_plt', 0, 1)
endif
if !empty(l:plt_file)
diff --git a/ale_linters/eruby/ruumba.vim b/ale_linters/eruby/ruumba.vim
index e68bb51d..2e84acf7 100644
--- a/ale_linters/eruby/ruumba.vim
+++ b/ale_linters/eruby/ruumba.vim
@@ -8,7 +8,7 @@ call ale#Set('eruby_ruumba_options', '')
function! ale_linters#eruby#ruumba#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'eruby_ruumba_executable')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'ruumba')
+ return ale#ruby#EscapeExecutable(l:executable, 'ruumba')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'eruby_ruumba_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
diff --git a/ale_linters/ink/ls.vim b/ale_linters/ink/ls.vim
new file mode 100644
index 00000000..1cc93583
--- /dev/null
+++ b/ale_linters/ink/ls.vim
@@ -0,0 +1,35 @@
+" Author: Andreww Hayworth <ahayworth@gmail.com>
+" Description: Integrate ALE with ink-language-server
+
+call ale#Set('ink_ls_executable', 'ink-language-server')
+call ale#Set('ink_ls_use_global', get(g:, 'ale_use_global_executables', 0))
+call ale#Set('ink_ls_initialization_options', {})
+
+function! ale_linters#ink#ls#GetExecutable(buffer) abort
+ return ale#node#FindExecutable(a:buffer, 'ink_ls', [
+ \ 'ink-language-server',
+ \ 'node_modules/.bin/ink-language-server',
+ \])
+endfunction
+
+function! ale_linters#ink#ls#GetCommand(buffer) abort
+ let l:executable = ale_linters#ink#ls#GetExecutable(a:buffer)
+
+ return ale#Escape(l:executable) . ' --stdio'
+endfunction
+
+function! ale_linters#ink#ls#FindProjectRoot(buffer) abort
+ let l:main_file = get(ale#Var(a:buffer, 'ink_ls_initialization_options'), 'mainStoryPath', 'main.ink')
+ let l:config = ale#path#ResolveLocalPath(a:buffer, l:main_file, expand('#' . a:buffer . ':p'))
+
+ return ale#path#Dirname(l:config)
+endfunction
+
+call ale#linter#Define('ink', {
+\ 'name': 'ink-language-server',
+\ 'lsp': 'stdio',
+\ 'executable': function('ale_linters#ink#ls#GetExecutable'),
+\ 'command': function('ale_linters#ink#ls#GetCommand'),
+\ 'project_root': function('ale_linters#ink#ls#FindProjectRoot'),
+\ 'initialization_options': {b -> ale#Var(b, 'ink_ls_initialization_options')},
+\})
diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim
index 3135e2e9..3135e2e9 100755..100644
--- a/ale_linters/javascript/flow.vim
+++ b/ale_linters/javascript/flow.vim
diff --git a/ale_linters/javascript/standard.vim b/ale_linters/javascript/standard.vim
index 4cd2c303..203a803e 100644
--- a/ale_linters/javascript/standard.vim
+++ b/ale_linters/javascript/standard.vim
@@ -8,6 +8,7 @@ call ale#Set('javascript_standard_options', '')
function! ale_linters#javascript#standard#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_standard', [
\ 'node_modules/standard/bin/cmd.js',
+ \ 'node_modules/semistandard/bin/cmd.js',
\ 'node_modules/.bin/standard',
\])
endfunction
diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim
index 4ec8b00e..4ec8b00e 100755..100644
--- a/ale_linters/less/lessc.vim
+++ b/ale_linters/less/lessc.vim
diff --git a/ale_linters/markdown/mdl.vim b/ale_linters/markdown/mdl.vim
index 305f5359..fd44de6e 100644
--- a/ale_linters/markdown/mdl.vim
+++ b/ale_linters/markdown/mdl.vim
@@ -17,18 +17,17 @@ function! ale_linters#markdown#mdl#GetCommand(buffer) abort
let l:options = ale#Var(a:buffer, 'markdown_mdl_options')
return ale#Escape(l:executable) . l:exec_args
- \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' -j' . (!empty(l:options) ? ' ' . l:options : '')
endfunction
function! ale_linters#markdown#mdl#Handle(buffer, lines) abort
- " matches: '(stdin):173: MD004 Unordered list style'
- let l:pattern = ':\(\d*\): \(.*\)$'
let l:output = []
- for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ for l:error in ale#util#FuzzyJSONDecode(a:lines, [])
call add(l:output, {
- \ 'lnum': l:match[1] + 0,
- \ 'text': l:match[2],
+ \ 'lnum': l:error['line'],
+ \ 'code': l:error['rule'] . '/' . join(l:error['aliases'], '/'),
+ \ 'text': l:error['description'],
\ 'type': 'W',
\})
endfor
diff --git a/ale_linters/php/psalm.vim b/ale_linters/php/psalm.vim
index 3cdb026a..834d0993 100644
--- a/ale_linters/php/psalm.vim
+++ b/ale_linters/php/psalm.vim
@@ -1,7 +1,7 @@
" Author: Matt Brown <https://github.com/muglug>
" Description: plugin for Psalm, static analyzer for PHP
-call ale#Set('psalm_langserver_executable', 'psalm-language-server')
+call ale#Set('psalm_langserver_executable', 'psalm')
call ale#Set('psalm_langserver_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#php#psalm#GetProjectRoot(buffer) abort
@@ -14,8 +14,8 @@ call ale#linter#Define('php', {
\ 'name': 'psalm',
\ 'lsp': 'stdio',
\ 'executable': {b -> ale#node#FindExecutable(b, 'psalm_langserver', [
-\ 'vendor/bin/psalm-language-server',
+\ 'vendor/bin/psalm',
\ ])},
-\ 'command': '%e',
+\ 'command': '%e --language-server',
\ 'project_root': function('ale_linters#php#psalm#GetProjectRoot'),
\})
diff --git a/ale_linters/powershell/powershell.vim b/ale_linters/powershell/powershell.vim
index a63191fd..a63191fd 100755..100644
--- a/ale_linters/powershell/powershell.vim
+++ b/ale_linters/powershell/powershell.vim
diff --git a/ale_linters/ruby/brakeman.vim b/ale_linters/ruby/brakeman.vim
index a8088080..2dc48740 100644
--- a/ale_linters/ruby/brakeman.vim
+++ b/ale_linters/ruby/brakeman.vim
@@ -36,7 +36,7 @@ function! ale_linters#ruby#brakeman#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_brakeman_executable')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'brakeman')
+ return ale#ruby#EscapeExecutable(l:executable, 'brakeman')
\ . ' -f json -q '
\ . ale#Var(a:buffer, 'ruby_brakeman_options')
\ . ' -p ' . ale#Escape(l:rails_root)
diff --git a/ale_linters/ruby/debride.vim b/ale_linters/ruby/debride.vim
new file mode 100644
index 00000000..0a45644e
--- /dev/null
+++ b/ale_linters/ruby/debride.vim
@@ -0,0 +1,42 @@
+" Author: Eddie Lebow https://github.com/elebow
+" Description: debride, a dead method detector for Ruby files
+
+call ale#Set('ruby_debride_executable', 'debride')
+call ale#Set('ruby_debride_options', '')
+
+function! ale_linters#ruby#debride#GetCommand(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'ruby_debride_executable')
+
+ return ale#handlers#ruby#EscapeExecutable(l:executable, 'debride')
+ \ . ale#Var(a:buffer, 'ruby_debride_options')
+ \ . ' %s'
+endfunction
+
+function! ale_linters#ruby#debride#HandleOutput(buffer, lines) abort
+ let l:output = []
+
+ for l:line in a:lines
+ if l:line !~# '^ '
+ continue
+ endif
+
+ let l:elements = split(l:line)
+ let l:method_name = l:elements[0]
+ let l:lnum = split(l:elements[1], ':')[1]
+
+ call add(l:output, {
+ \ 'lnum': 0 + l:lnum,
+ \ 'text': 'Possible unused method: ' . l:method_name,
+ \ 'type': 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'debride',
+\ 'executable': {b -> ale#Var(b, 'ruby_debride_executable')},
+\ 'command': function('ale_linters#ruby#debride#GetCommand'),
+\ 'callback': 'ale_linters#ruby#debride#HandleOutput',
+\})
diff --git a/ale_linters/ruby/rails_best_practices.vim b/ale_linters/ruby/rails_best_practices.vim
index a94fb671..36646647 100644
--- a/ale_linters/ruby/rails_best_practices.vim
+++ b/ale_linters/ruby/rails_best_practices.vim
@@ -33,7 +33,7 @@ function! ale_linters#ruby#rails_best_practices#GetCommand(buffer) abort
let l:output_file = has('win32') ? '%t ' : '/dev/stdout '
let l:cat_file = has('win32') ? '; type %t' : ''
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'rails_best_practices')
+ return ale#ruby#EscapeExecutable(l:executable, 'rails_best_practices')
\ . ' --silent -f json --output-file ' . l:output_file
\ . ale#Var(a:buffer, 'ruby_rails_best_practices_options')
\ . ale#Escape(l:rails_root)
diff --git a/ale_linters/ruby/reek.vim b/ale_linters/ruby/reek.vim
index e39e366f..226b452e 100644
--- a/ale_linters/ruby/reek.vim
+++ b/ale_linters/ruby/reek.vim
@@ -14,7 +14,7 @@ function! ale_linters#ruby#reek#GetCommand(buffer, version) abort
\ ? ' --stdin-filename %s'
\ : ''
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'reek')
+ return ale#ruby#EscapeExecutable(l:executable, 'reek')
\ . ' -f json --no-progress --no-color --force-exclusion'
\ . l:display_name_args
endfunction
diff --git a/ale_linters/ruby/rubocop.vim b/ale_linters/ruby/rubocop.vim
index 8b9e9c84..410ed0ea 100644
--- a/ale_linters/ruby/rubocop.vim
+++ b/ale_linters/ruby/rubocop.vim
@@ -7,7 +7,7 @@ call ale#Set('ruby_rubocop_options', '')
function! ale_linters#ruby#rubocop#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_rubocop_executable')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'rubocop')
+ return ale#ruby#EscapeExecutable(l:executable, 'rubocop')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'ruby_rubocop_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
diff --git a/ale_linters/ruby/sorbet.vim b/ale_linters/ruby/sorbet.vim
index ee765a6e..cae0683c 100644
--- a/ale_linters/ruby/sorbet.vim
+++ b/ale_linters/ruby/sorbet.vim
@@ -5,7 +5,7 @@ function! ale_linters#ruby#sorbet#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_sorbet_executable')
let l:options = ale#Var(a:buffer, 'ruby_sorbet_options')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'srb')
+ return ale#ruby#EscapeExecutable(l:executable, 'srb')
\ . ' tc'
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --lsp --disable-watchman'
diff --git a/ale_linters/ruby/standardrb.vim b/ale_linters/ruby/standardrb.vim
index f075a7d5..f751e803 100644
--- a/ale_linters/ruby/standardrb.vim
+++ b/ale_linters/ruby/standardrb.vim
@@ -8,7 +8,7 @@ call ale#Set('ruby_standardrb_options', '')
function! ale_linters#ruby#standardrb#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_standardrb_executable')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'standardrb')
+ return ale#ruby#EscapeExecutable(l:executable, 'standardrb')
\ . ' --format json --force-exclusion '
\ . ale#Var(a:buffer, 'ruby_standardrb_options')
\ . ' --stdin ' . ale#Escape(expand('#' . a:buffer . ':p'))
diff --git a/ale_linters/scala/metals.vim b/ale_linters/scala/metals.vim
new file mode 100644
index 00000000..f78c7119
--- /dev/null
+++ b/ale_linters/scala/metals.vim
@@ -0,0 +1,48 @@
+" Author: Jeffrey Lau - https://github.com/zoonfafer
+" Description: Metals Language Server for Scala https://scalameta.org/metals/
+
+call ale#Set('scala_metals_executable', 'metals-vim')
+call ale#Set('scala_metals_project_root', '')
+
+function! ale_linters#scala#metals#GetProjectRoot(buffer) abort
+ let l:project_root = ale#Var(a:buffer, 'scala_metals_project_root')
+
+ if !empty(l:project_root)
+ return l:project_root
+ endif
+
+ let l:potential_roots = [
+ \ 'build.sc',
+ \ 'build.sbt',
+ \ '.bloop',
+ \ '.metals',
+ \]
+
+ for l:root in l:potential_roots
+ let l:project_root = ale#path#ResolveLocalPath(
+ \ a:buffer,
+ \ l:root,
+ \ ''
+ \)
+
+ if !empty(l:project_root)
+ return fnamemodify(
+ \ l:project_root,
+ \ ':h',
+ \)
+ endif
+ endfor
+endfunction
+
+function! ale_linters#scala#metals#GetCommand(buffer) abort
+ return '%e' . ale#Pad('stdio')
+endfunction
+
+call ale#linter#Define('scala', {
+\ 'name': 'metals',
+\ 'lsp': 'stdio',
+\ 'language': 'scala',
+\ 'executable': {b -> ale#Var(b, 'scala_metals_executable')},
+\ 'command': function('ale_linters#scala#metals#GetCommand'),
+\ 'project_root': function('ale_linters#scala#metals#GetProjectRoot'),
+\})
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index 189dc21d..171fe64e 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -34,8 +34,10 @@ function! ale_linters#sh#shell#Handle(buffer, lines) abort
" Matches patterns line the following:
"
" bash: line 13: syntax error near unexpected token `d'
+ " bash:行0: 未预期的符号“done”附近有语法错误
+ " bash: 列 90: 尋找匹配的「"」時遇到了未預期的檔案結束符
" sh: 11: Syntax error: "(" unexpected
- let l:pattern = '\v(line |: ?)(\d+): (.+)$'
+ let l:pattern = '\v([^:]+:\D*)(\d+): (.+)$'
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
diff --git a/ale_linters/solidity/solc.vim b/ale_linters/solidity/solc.vim
new file mode 100644
index 00000000..e4f220ac
--- /dev/null
+++ b/ale_linters/solidity/solc.vim
@@ -0,0 +1,35 @@
+" Author: Karl Bartel <karl42@gmail.com> - http://karl.berlin/
+" Description: Report solc compiler errors in Solidity code
+
+call ale#Set('solidity_solc_options', '')
+
+function! ale_linters#solidity#solc#Handle(buffer, lines) abort
+ " Matches patterns like the following:
+ " /path/to/file/file.sol:1:10: Error: Identifier not found or not unique.
+ let l:pattern = '\v^[^:]+:(\d+):(\d+): (Error|Warning): (.*)$'
+ let l:output = []
+
+ for l:match in ale#util#GetMatches(a:lines, l:pattern)
+ let l:isError = l:match[3] is? 'error'
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': l:match[4],
+ \ 'type': l:isError ? 'E' : 'W',
+ \})
+ endfor
+
+ return l:output
+endfunction
+
+function! ale_linters#solidity#solc#GetCommand(buffer) abort
+ return 'solc' . ale#Pad(ale#Var(a:buffer, 'solidity_solc_options')) . ' %s'
+endfunction
+
+call ale#linter#Define('solidity', {
+\ 'name': 'solc',
+\ 'executable': 'solc',
+\ 'command': function('ale_linters#solidity#solc#GetCommand'),
+\ 'callback': 'ale_linters#solidity#solc#Handle',
+\ 'output_stream': 'stderr',
+\})
diff --git a/ale_linters/terraform/terraform.vim b/ale_linters/terraform/terraform.vim
index 0429cb7a..0429cb7a 100755..100644
--- a/ale_linters/terraform/terraform.vim
+++ b/ale_linters/terraform/terraform.vim
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index 5540ec14..9b428700 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -265,6 +265,16 @@ function! s:GetLookupFromCompileCommandsFile(compile_commands_file) abort
return l:empty
endfunction
+function! ale#c#GetCompileCommand(json_item) abort
+ if has_key(a:json_item, 'command')
+ return a:json_item.command
+ elseif has_key(a:json_item, 'arguments')
+ return join(a:json_item.arguments, ' ')
+ endif
+
+ return ''
+endfunction
+
function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
" Search for an exact file match first.
let l:basename = tolower(expand('#' . a:buffer . ':t'))
@@ -287,15 +297,14 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
for l:item in l:file_list
" Load the flags for this file, or for a source file matching the
" header file.
- if has_key(l:item, 'command')
- \&& (
+ if (
\ bufnr(l:item.file) is a:buffer
\ || (
\ !empty(l:source_file)
\ && l:item.file[-len(l:source_file):] is? l:source_file
\ )
\)
- return ale#c#ParseCFlags(l:item.directory, l:item.command)
+ return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item))
endif
endfor
@@ -307,8 +316,7 @@ function! ale#c#ParseCompileCommandsFlags(buffer, file_lookup, dir_lookup) abort
for l:item in l:dir_list
if ale#path#Simplify(fnamemodify(l:item.file, ':h')) is? l:dir
- \&& has_key(l:item, 'command')
- return ale#c#ParseCFlags(l:item.directory, l:item.command)
+ return ale#c#ParseCFlags(l:item.directory, ale#c#GetCompileCommand(l:item))
endif
endfor
diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim
new file mode 100644
index 00000000..0af1bb70
--- /dev/null
+++ b/autoload/ale/code_action.vim
@@ -0,0 +1,163 @@
+" Author: Jerko Steiner <jerko.steiner@gmail.com>
+" Description: Code action support for LSP / tsserver
+
+function! ale#code_action#HandleCodeAction(code_action) abort
+ let l:current_buffer = bufnr('')
+ let l:changes = a:code_action.changes
+
+ for l:file_code_edit in l:changes
+ let l:buf = bufnr(l:file_code_edit.fileName)
+
+ if l:buf != -1 && l:buf != l:current_buffer && getbufvar(l:buf, '&mod')
+ call ale#util#Execute('echom ''Aborting action, file is unsaved''')
+
+ return
+ endif
+ endfor
+
+ for l:file_code_edit in l:changes
+ call ale#code_action#ApplyChanges(
+ \ l:file_code_edit.fileName, l:file_code_edit.textChanges)
+ endfor
+endfunction
+
+function! ale#code_action#ApplyChanges(filename, changes) abort
+ let l:current_buffer = bufnr('')
+ " The buffer is used to determine the fileformat, if available.
+ let l:buffer = bufnr(a:filename)
+ let l:is_current_buffer = l:buffer > 0 && l:buffer == l:current_buffer
+
+ if l:buffer > 0
+ let l:lines = getbufline(l:buffer, 1, '$')
+ else
+ let l:lines = readfile(a:filename, 'b')
+ endif
+
+ if l:is_current_buffer
+ let l:pos = getpos('.')[1:2]
+ else
+ let l:pos = [1, 1]
+ endif
+
+ " We have to keep track of how many lines we have added, and offset
+ " changes accordingly.
+ let l:line_offset = 0
+ let l:column_offset = 0
+ let l:last_end_line = 0
+
+ for l:code_edit in a:changes
+ if l:code_edit.start.line isnot l:last_end_line
+ let l:column_offset = 0
+ endif
+
+ let l:line = l:code_edit.start.line + l:line_offset
+ let l:column = l:code_edit.start.offset + l:column_offset
+ let l:end_line = l:code_edit.end.line + l:line_offset
+ let l:end_column = l:code_edit.end.offset + l:column_offset
+ let l:text = l:code_edit.newText
+
+ let l:cur_line = l:pos[0]
+ let l:cur_column = l:pos[1]
+
+ let l:last_end_line = l:end_line
+
+ " Adjust the ends according to previous edits.
+ if l:end_line > len(l:lines)
+ let l:end_line_len = 0
+ else
+ let l:end_line_len = len(l:lines[l:end_line - 1])
+ endif
+
+ let l:insertions = split(l:text, '\n', 1)
+
+ if l:line is 1
+ " Same logic as for column below. Vimscript's slice [:-1] will not
+ " be an empty list.
+ let l:start = []
+ else
+ let l:start = l:lines[: l:line - 2]
+ endif
+
+ if l:column is 1
+ " We need to handle column 1 specially, because we can't slice an
+ " empty string ending on index 0.
+ let l:middle = [l:insertions[0]]
+ else
+ let l:middle = [l:lines[l:line - 1][: l:column - 2] . l:insertions[0]]
+ endif
+
+ call extend(l:middle, l:insertions[1:])
+ let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :]
+
+ let l:lines_before_change = len(l:lines)
+ let l:lines = l:start + l:middle + l:lines[l:end_line :]
+
+ let l:current_line_offset = len(l:lines) - l:lines_before_change
+ let l:line_offset += l:current_line_offset
+ let l:column_offset = len(l:middle[-1]) - l:end_line_len
+
+ let l:pos = s:UpdateCursor(l:pos,
+ \ [l:line, l:column],
+ \ [l:end_line, l:end_column],
+ \ [l:current_line_offset, l:column_offset])
+ endfor
+
+ if l:lines[-1] is# ''
+ call remove(l:lines, -1)
+ endif
+
+ call ale#util#Writefile(l:buffer, l:lines, a:filename)
+
+ if l:is_current_buffer
+ call ale#util#Execute(':e!')
+ call setpos('.', [0, l:pos[0], l:pos[1], 0])
+ endif
+endfunction
+
+function! s:UpdateCursor(cursor, start, end, offset) abort
+ let l:cur_line = a:cursor[0]
+ let l:cur_column = a:cursor[1]
+ let l:line = a:start[0]
+ let l:column = a:start[1]
+ let l:end_line = a:end[0]
+ let l:end_column = a:end[1]
+ let l:line_offset = a:offset[0]
+ let l:column_offset = a:offset[1]
+
+ if l:end_line < l:cur_line
+ " both start and end lines are before the cursor. only line offset
+ " needs to be updated
+ let l:cur_line += l:line_offset
+ elseif l:end_line == l:cur_line
+ " end line is at the same location as cursor, which means
+ " l:line <= l:cur_line
+ if l:line < l:cur_line || l:column <= l:cur_column
+ " updates are happening either before or around the cursor
+ if l:end_column < l:cur_column
+ " updates are happening before the cursor, update the
+ " column offset for cursor
+ let l:cur_line += l:line_offset
+ let l:cur_column += l:column_offset
+ else
+ " updates are happening around the cursor, move the cursor
+ " to the end of the changes
+ let l:cur_line += l:line_offset
+ let l:cur_column = l:end_column + l:column_offset
+ endif
+ " else is not necessary, it means modifications are happening
+ " after the cursor so no cursor updates need to be done
+ endif
+ else
+ " end line is after the cursor
+ if l:line < l:cur_line || l:line == l:cur_line && l:column <= l:cur_column
+ " changes are happening around the cursor, move the cursor
+ " to the end of the changes
+ let l:cur_line = l:end_line + l:line_offset
+ let l:cur_column = l:end_column + l:column_offset
+ " else is not necesary, it means modifications are happening
+ " after the cursor so no cursor updates need to be done
+ endif
+ endif
+
+ return [l:cur_line, l:cur_column]
+endfunction
diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index ebf32909..177a6acb 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -15,6 +15,7 @@ onoremap <silent> <Plug>(ale_show_completion_menu) <Nop>
let g:ale_completion_delay = get(g:, 'ale_completion_delay', 100)
let g:ale_completion_excluded_words = get(g:, 'ale_completion_excluded_words', [])
let g:ale_completion_max_suggestions = get(g:, 'ale_completion_max_suggestions', 50)
+let g:ale_completion_tsserver_autoimport = get(g:, 'ale_completion_tsserver_autoimport', 0)
let s:timer_id = -1
let s:last_done_pos = []
@@ -296,7 +297,10 @@ function! ale#completion#ParseTSServerCompletions(response) abort
let l:names = []
for l:suggestion in a:response.body
- call add(l:names, l:suggestion.name)
+ call add(l:names, {
+ \ 'word': l:suggestion.name,
+ \ 'source': get(l:suggestion, 'source', ''),
+ \})
endfor
return l:names
@@ -310,6 +314,10 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
for l:suggestion in a:response.body
let l:displayParts = []
+ for l:action in get(l:suggestion, 'codeActions', [])
+ call add(l:displayParts, l:action.description . ' ')
+ endfor
+
for l:part in l:suggestion.displayParts
call add(l:displayParts, l:part.text)
endfor
@@ -330,13 +338,22 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
endif
" See :help complete-items
- call add(l:results, {
+ let l:result = {
\ 'word': l:suggestion.name,
\ 'kind': l:kind,
\ 'icase': 1,
\ 'menu': join(l:displayParts, ''),
+ \ 'dup': g:ale_completion_tsserver_autoimport,
\ 'info': join(l:documentationParts, ''),
- \})
+ \}
+
+ if has_key(l:suggestion, 'codeActions')
+ let l:result.user_data = json_encode({
+ \ 'codeActions': l:suggestion.codeActions,
+ \ })
+ endif
+
+ call add(l:results, l:result)
endfor
let l:names = getbufvar(l:buffer, 'ale_tsserver_completion_names', [])
@@ -345,12 +362,12 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
let l:names_with_details = map(copy(l:results), 'v:val.word')
let l:missing_names = filter(
\ copy(l:names),
- \ 'index(l:names_with_details, v:val) < 0',
+ \ 'index(l:names_with_details, v:val.word) < 0',
\)
for l:name in l:missing_names
call add(l:results, {
- \ 'word': l:name,
+ \ 'word': l:name.word,
\ 'kind': 'v',
\ 'icase': 1,
\ 'menu': '',
@@ -472,13 +489,22 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort
call setbufvar(l:buffer, 'ale_tsserver_completion_names', l:names)
if !empty(l:names)
+ let l:identifiers = []
+
+ for l:name in l:names
+ call add(l:identifiers, {
+ \ 'name': l:name.word,
+ \ 'source': get(l:name, 'source', ''),
+ \})
+ endfor
+
let b:ale_completion_info.request_id = ale#lsp#Send(
\ b:ale_completion_info.conn_id,
\ ale#lsp#tsserver_message#CompletionEntryDetails(
\ l:buffer,
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
- \ l:names,
+ \ l:identifiers,
\ ),
\)
endif
@@ -525,6 +551,7 @@ function! s:OnReady(linter, lsp_details) abort
\ b:ale_completion_info.line,
\ b:ale_completion_info.column,
\ b:ale_completion_info.prefix,
+ \ g:ale_completion_tsserver_autoimport,
\)
else
" Send a message saying the buffer has changed first, otherwise
@@ -692,6 +719,26 @@ function! ale#completion#Queue() abort
let s:timer_id = timer_start(g:ale_completion_delay, function('s:TimerHandler'))
endfunction
+function! ale#completion#HandleUserData(completed_item) abort
+ let l:source = get(get(b:, 'ale_completion_info', {}), 'source', '')
+
+ if l:source isnot# 'ale-automatic' && l:source isnot# 'ale-manual'
+ return
+ endif
+
+ let l:user_data_json = get(a:completed_item, 'user_data', '')
+
+ if empty(l:user_data_json)
+ return
+ endif
+
+ let l:user_data = json_decode(l:user_data_json)
+
+ for l:code_action in get(l:user_data, 'codeActions', [])
+ call ale#code_action#HandleCodeAction(l:code_action)
+ endfor
+endfunction
+
function! ale#completion#Done() abort
silent! pclose
@@ -700,6 +747,10 @@ function! ale#completion#Done() abort
let s:last_done_pos = getpos('.')[1:2]
endfunction
+augroup ALECompletionActions
+ autocmd CompleteDone * call ale#completion#HandleUserData(v:completed_item)
+augroup END
+
function! s:Setup(enabled) abort
augroup ALECompletionGroup
autocmd!
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index 9987fbdd..dad9e2bc 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -47,7 +47,7 @@ function! ale#fix#ApplyQueuedFixes(buffer) abort
set nomodified
endif
else
- call writefile(l:new_lines, expand(a:buffer . ':p')) " no-custom-checks
+ call writefile(l:new_lines, expand('#' . a:buffer . ':p')) " no-custom-checks
call setbufvar(a:buffer, '&modified', 0)
endif
endif
@@ -74,7 +74,7 @@ endfunction
function! ale#fix#ApplyFixes(buffer, output) abort
let l:data = g:ale_fix_buffer_data[a:buffer]
let l:data.output = a:output
- let l:data.changes_made = l:data.lines_before != l:data.output
+ let l:data.changes_made = l:data.lines_before !=# l:data.output " no-custom-checks
let l:data.done = 1
call ale#command#RemoveManagedFiles(a:buffer)
diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim
index 7a553ccc..2a96a7d6 100644
--- a/autoload/ale/fix/registry.vim
+++ b/autoload/ale/fix/registry.vim
@@ -27,6 +27,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix PEP8 issues with black.',
\ },
+\ 'dfmt': {
+\ 'function': 'ale#fixers#dfmt#Fix',
+\ 'suggested_filetypes': ['d'],
+\ 'description': 'Fix D files with dfmt.',
+\ },
\ 'fecs': {
\ 'function': 'ale#fixers#fecs#Fix',
\ 'suggested_filetypes': ['javascript', 'css', 'html'],
@@ -255,6 +260,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['sql'],
\ 'description': 'Fix SQL files with sqlfmt.',
\ },
+\ 'sqlformat': {
+\ 'function': 'ale#fixers#sqlformat#Fix',
+\ 'suggested_filetypes': ['sql'],
+\ 'description': 'Fix SQL files with sqlformat.',
+\ },
\ 'google_java_format': {
\ 'function': 'ale#fixers#google_java_format#Fix',
\ 'suggested_filetypes': ['java'],
@@ -312,7 +322,7 @@ let s:default_registry = {
\ },
\ 'styler': {
\ 'function': 'ale#fixers#styler#Fix',
-\ 'suggested_filetypes': ['r', 'rmarkdown'],
+\ 'suggested_filetypes': ['r', 'rmarkdown', 'rmd'],
\ 'description': 'Fix R files with styler.',
\ },
\ 'latexindent': {
@@ -335,6 +345,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['ada'],
\ 'description': 'Format Ada files with gnatpp.',
\ },
+\ 'nixpkgs-fmt': {
+\ 'function': 'ale#fixers#nixpkgsfmt#Fix',
+\ 'suggested_filetypes': ['nix'],
+\ 'description': 'A formatter for Nix code',
+\ },
\}
" Reset the function registry to the default entries.
diff --git a/autoload/ale/fixers/dfmt.vim b/autoload/ale/fixers/dfmt.vim
new file mode 100644
index 00000000..0072e045
--- /dev/null
+++ b/autoload/ale/fixers/dfmt.vim
@@ -0,0 +1,18 @@
+" Author: theoldmoon0602
+" Description: Integration of dfmt with ALE.
+
+call ale#Set('d_dfmt_executable', 'dfmt')
+call ale#Set('d_dfmt_options', '')
+
+function! ale#fixers#dfmt#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'd_dfmt_executable')
+ let l:options = ale#Var(a:buffer, 'd_dfmt_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . ' -i'
+ \ . (empty(l:options) ? '' : ' ' . l:options)
+ \ . ' %t',
+ \ 'read_temporary_file': 1,
+ \}
+endfunction
diff --git a/autoload/ale/fixers/nixpkgsfmt.vim b/autoload/ale/fixers/nixpkgsfmt.vim
new file mode 100644
index 00000000..403ce798
--- /dev/null
+++ b/autoload/ale/fixers/nixpkgsfmt.vim
@@ -0,0 +1,12 @@
+call ale#Set('nix_nixpkgsfmt_executable', 'nixpkgs-fmt')
+call ale#Set('nix_nixpkgsfmt_options', '')
+
+function! ale#fixers#nixpkgsfmt#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'nix_nixpkgsfmt_executable')
+ let l:options = ale#Var(a:buffer, 'nix_nixpkgsfmt_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . (empty(l:options) ? '' : ' ' . l:options),
+ \}
+endfunction
diff --git a/autoload/ale/fixers/rubocop.vim b/autoload/ale/fixers/rubocop.vim
index 33ba6887..0c7441e4 100644
--- a/autoload/ale/fixers/rubocop.vim
+++ b/autoload/ale/fixers/rubocop.vim
@@ -6,7 +6,7 @@ function! ale#fixers#rubocop#GetCommand(buffer) abort
let l:config = ale#path#FindNearestFile(a:buffer, '.rubocop.yml')
let l:options = ale#Var(a:buffer, 'ruby_rubocop_options')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'rubocop')
+ return ale#ruby#EscapeExecutable(l:executable, 'rubocop')
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --auto-correct --force-exclusion %t'
diff --git a/autoload/ale/fixers/sorbet.vim b/autoload/ale/fixers/sorbet.vim
index 182f7300..7c12fa1e 100644
--- a/autoload/ale/fixers/sorbet.vim
+++ b/autoload/ale/fixers/sorbet.vim
@@ -5,7 +5,7 @@ function! ale#fixers#sorbet#GetCommand(buffer) abort
let l:executable = ale#Var(a:buffer, 'ruby_sorbet_executable')
let l:options = ale#Var(a:buffer, 'ruby_sorbet_options')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'srb')
+ return ale#ruby#EscapeExecutable(l:executable, 'srb')
\ . ' tc'
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --autocorrect --file %t'
diff --git a/autoload/ale/fixers/sqlformat.vim b/autoload/ale/fixers/sqlformat.vim
new file mode 100644
index 00000000..6319c1ac
--- /dev/null
+++ b/autoload/ale/fixers/sqlformat.vim
@@ -0,0 +1,16 @@
+" Author: Cluas <Cluas@live.cn>
+" Description: Fixing files with sqlformat.
+
+call ale#Set('sql_sqlformat_executable', 'sqlformat')
+call ale#Set('sql_sqlformat_options', '')
+
+function! ale#fixers#sqlformat#Fix(buffer) abort
+ let l:executable = ale#Var(a:buffer, 'sql_sqlformat_executable')
+ let l:options = ale#Var(a:buffer, 'sql_sqlformat_options')
+
+ return {
+ \ 'command': ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' -'
+ \}
+endfunction
diff --git a/autoload/ale/fixers/standardrb.vim b/autoload/ale/fixers/standardrb.vim
index fab1e2bc..54330a37 100644
--- a/autoload/ale/fixers/standardrb.vim
+++ b/autoload/ale/fixers/standardrb.vim
@@ -9,7 +9,7 @@ function! ale#fixers#standardrb#GetCommand(buffer) abort
let l:config = ale#path#FindNearestFile(a:buffer, '.standard.yml')
let l:options = ale#Var(a:buffer, 'ruby_standardrb_options')
- return ale#handlers#ruby#EscapeExecutable(l:executable, 'standardrb')
+ return ale#ruby#EscapeExecutable(l:executable, 'standardrb')
\ . (!empty(l:config) ? ' --config ' . ale#Escape(l:config) : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --fix --force-exclusion %t'
diff --git a/autoload/ale/handlers/languagetool.vim b/autoload/ale/handlers/languagetool.vim
index 10e049df..73974ceb 100644
--- a/autoload/ale/handlers/languagetool.vim
+++ b/autoload/ale/handlers/languagetool.vim
@@ -2,6 +2,7 @@
" Description: languagetool for markdown files
"
call ale#Set('languagetool_executable', 'languagetool')
+call ale#Set('languagetool_options', '--autoDetect')
function! ale#handlers#languagetool#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'languagetool_executable')
@@ -9,8 +10,10 @@ endfunction
function! ale#handlers#languagetool#GetCommand(buffer) abort
let l:executable = ale#handlers#languagetool#GetExecutable(a:buffer)
+ let l:options = ale#Var(a:buffer, 'languagetool_options')
- return ale#Escape(l:executable) . ' --autoDetect %s'
+ return ale#Escape(l:executable)
+ \ . (empty(l:options) ? '' : ' ' . l:options) . ' %s'
endfunction
function! ale#handlers#languagetool#HandleOutput(buffer, lines) abort
diff --git a/autoload/ale/handlers/ruby.vim b/autoload/ale/handlers/ruby.vim
index c28b8b75..7a1c5765 100644
--- a/autoload/ale/handlers/ruby.vim
+++ b/autoload/ale/handlers/ruby.vim
@@ -36,11 +36,3 @@ endfunction
function! ale#handlers#ruby#HandleSyntaxErrors(buffer, lines) abort
return s:HandleSyntaxError(a:buffer, a:lines)
endfunction
-
-function! ale#handlers#ruby#EscapeExecutable(executable, bundle_exec) abort
- let l:exec_args = a:executable =~? 'bundle'
- \ ? ' exec ' . a:bundle_exec
- \ : ''
-
- return ale#Escape(a:executable) . l:exec_args
-endfunction
diff --git a/autoload/ale/highlight.vim b/autoload/ale/highlight.vim
index 20ef19dd..82ad57e0 100644
--- a/autoload/ale/highlight.vim
+++ b/autoload/ale/highlight.vim
@@ -34,7 +34,11 @@ endif
" Wrappers are necessary to test this functionality by faking the calls in tests.
function! ale#highlight#nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) abort
- call nvim_buf_add_highlight(a:buffer, a:ns_id, a:hl_group, a:line, a:col_start, a:col_end)
+ " Ignore all errors for adding highlights.
+ try
+ call nvim_buf_add_highlight(a:buffer, a:ns_id, a:hl_group, a:line, a:col_start, a:col_end)
+ catch
+ endtry
endfunction
function! ale#highlight#nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) abort
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index 78dcd3a2..a85f06e2 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -12,9 +12,12 @@ let s:linters = {}
let s:default_ale_linter_aliases = {
\ 'Dockerfile': 'dockerfile',
\ 'csh': 'sh',
+\ 'javascriptreact': ['javascript', 'jsx'],
\ 'plaintex': 'tex',
\ 'rmarkdown': 'r',
+\ 'rmd': 'r',
\ 'systemverilog': 'verilog',
+\ 'typescriptreact': ['typescript', 'tsx'],
\ 'verilog_systemverilog': ['verilog_systemverilog', 'verilog'],
\ 'vimwiki': 'markdown',
\ 'vue': ['vue', 'javascript'],
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index 017096cd..2509174e 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -37,6 +37,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
\ 'init_queue': [],
\ 'capabilities': {
\ 'hover': 0,
+ \ 'rename': 0,
\ 'references': 0,
\ 'completion': 0,
\ 'completion_trigger_characters': [],
@@ -199,6 +200,10 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.references = 1
endif
+ if get(a:capabilities, 'renameProvider') is v:true
+ let a:conn.capabilities.rename = 1
+ endif
+
if !empty(get(a:capabilities, 'completionProvider'))
let a:conn.capabilities.completion = 1
endif
@@ -317,6 +322,7 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn.capabilities.completion_trigger_characters = ['.']
let l:conn.capabilities.definition = 1
let l:conn.capabilities.symbol_search = 1
+ let l:conn.capabilities.rename = 1
endfunction
function! s:SendInitMessage(conn) abort
diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim
index b6b14a22..5b0cb8b7 100644
--- a/autoload/ale/lsp/message.vim
+++ b/autoload/ale/lsp/message.vim
@@ -162,3 +162,13 @@ function! ale#lsp#message#DidChangeConfiguration(buffer, config) abort
\ 'settings': a:config,
\}]
endfunction
+
+function! ale#lsp#message#Rename(buffer, line, column, new_name) abort
+ return [0, 'textDocument/rename', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
+ \ },
+ \ 'position': {'line': a:line - 1, 'character': a:column - 1},
+ \ 'newName': a:new_name,
+ \}]
+endfunction
diff --git a/autoload/ale/lsp/tsserver_message.vim b/autoload/ale/lsp/tsserver_message.vim
index d6919516..b9fafaa0 100644
--- a/autoload/ale/lsp/tsserver_message.vim
+++ b/autoload/ale/lsp/tsserver_message.vim
@@ -36,12 +36,14 @@ function! ale#lsp#tsserver_message#Geterr(buffer) abort
return [1, 'ts@geterr', {'files': [expand('#' . a:buffer . ':p')]}]
endfunction
-function! ale#lsp#tsserver_message#Completions(buffer, line, column, prefix) abort
+function! ale#lsp#tsserver_message#Completions(
+\ buffer, line, column, prefix, include_external) abort
return [0, 'ts@completions', {
\ 'line': a:line,
\ 'offset': a:column,
\ 'file': expand('#' . a:buffer . ':p'),
\ 'prefix': a:prefix,
+ \ 'includeExternalModuleExports': a:include_external,
\}]
endfunction
@@ -77,3 +79,27 @@ function! ale#lsp#tsserver_message#Quickinfo(buffer, line, column) abort
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction
+
+function! ale#lsp#tsserver_message#Rename(
+\ buffer, line, column, find_in_comments, find_in_strings) abort
+ return [0, 'ts@rename', {
+ \ 'line': a:line,
+ \ 'offset': a:column,
+ \ 'file': expand('#' . a:buffer . ':p'),
+ \ 'arguments': {
+ \ 'findInComments': a:find_in_comments,
+ \ 'findInStrings': a:find_in_strings,
+ \ }
+ \}]
+endfunction
+
+function! ale#lsp#tsserver_message#OrganizeImports(buffer) abort
+ return [0, 'ts@organizeImports', {
+ \ 'scope': {
+ \ 'type': 'file',
+ \ 'args': {
+ \ 'file': expand('#' . a:buffer . ':p'),
+ \ },
+ \ },
+ \}]
+endfunction
diff --git a/autoload/ale/organize_imports.vim b/autoload/ale/organize_imports.vim
new file mode 100644
index 00000000..bc9b829e
--- /dev/null
+++ b/autoload/ale/organize_imports.vim
@@ -0,0 +1,59 @@
+" Author: Jerko Steiner <jerko.steiner@gmail.com>
+" Description: Organize imports support for tsserver
+"
+function! ale#organize_imports#HandleTSServerResponse(conn_id, response) abort
+ if get(a:response, 'command', '') isnot# 'organizeImports'
+ return
+ endif
+
+ if get(a:response, 'success', v:false) isnot v:true
+ return
+ endif
+
+ let l:file_code_edits = a:response.body
+
+ call ale#code_action#HandleCodeAction({
+ \ 'description': 'Organize Imports',
+ \ 'changes': l:file_code_edits,
+ \})
+endfunction
+
+function! s:OnReady(linter, lsp_details) abort
+ let l:id = a:lsp_details.connection_id
+
+ if a:linter.lsp isnot# 'tsserver'
+ call ale#util#Execute('echom ''OrganizeImports currently only works with tsserver''')
+
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
+ let l:Callback = function('ale#organize_imports#HandleTSServerResponse')
+
+ call ale#lsp#RegisterCallback(l:id, l:Callback)
+
+ let l:message = ale#lsp#tsserver_message#OrganizeImports(l:buffer)
+
+ let l:request_id = ale#lsp#Send(l:id, l:message)
+endfunction
+
+function! s:OrganizeImports(linter) abort
+ let l:buffer = bufnr('')
+ let [l:line, l:column] = getpos('.')[1:2]
+
+ if a:linter.lsp isnot# 'tsserver'
+ let l:column = min([l:column, len(getline(l:line))])
+ endif
+
+ let l:Callback = function('s:OnReady')
+ call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
+endfunction
+
+function! ale#organize_imports#Execute() abort
+ for l:linter in ale#linter#Get(&filetype)
+ if !empty(l:linter.lsp)
+ call s:OrganizeImports(l:linter)
+ endif
+ endfor
+endfunction
diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim
index 84c26d0a..30550503 100644
--- a/autoload/ale/path.vim
+++ b/autoload/ale/path.vim
@@ -54,14 +54,14 @@ function! ale#path#FindNearestDirectory(buffer, directory_name) abort
return ''
endfunction
-" Given a buffer, a string to search for, an a global fallback for when
+" Given a buffer, a string to search for, and a global fallback for when
" the search fails, look for a file in parent paths, and if that fails,
" use the global fallback path instead.
function! ale#path#ResolveLocalPath(buffer, search_string, global_fallback) abort
" Search for a locally installed file first.
let l:path = ale#path#FindNearestFile(a:buffer, a:search_string)
- " If the serach fails, try the global executable instead.
+ " If the search fails, try the global executable instead.
if empty(l:path)
let l:path = a:global_fallback
endif
diff --git a/autoload/ale/rename.vim b/autoload/ale/rename.vim
new file mode 100644
index 00000000..02b7b579
--- /dev/null
+++ b/autoload/ale/rename.vim
@@ -0,0 +1,225 @@
+" Author: Jerko Steiner <jerko.steiner@gmail.com>
+" Description: Rename symbol support for LSP / tsserver
+
+let s:rename_map = {}
+
+" Used to get the rename map in tests.
+function! ale#rename#GetMap() abort
+ return deepcopy(s:rename_map)
+endfunction
+
+" Used to set the rename map in tests.
+function! ale#rename#SetMap(map) abort
+ let s:rename_map = a:map
+endfunction
+
+function! ale#rename#ClearLSPData() abort
+ let s:rename_map = {}
+endfunction
+
+let g:ale_rename_tsserver_find_in_comments = get(g:, 'ale_rename_tsserver_find_in_comments')
+let g:ale_rename_tsserver_find_in_strings = get(g:, 'ale_rename_tsserver_find_in_strings')
+
+function! s:message(message) abort
+ call ale#util#Execute('echom ' . string(a:message))
+endfunction
+
+function! ale#rename#HandleTSServerResponse(conn_id, response) abort
+ if get(a:response, 'command', '') isnot# 'rename'
+ return
+ endif
+
+ if !has_key(s:rename_map, a:response.request_seq)
+ return
+ endif
+
+ let l:old_name = s:rename_map[a:response.request_seq].old_name
+ let l:new_name = s:rename_map[a:response.request_seq].new_name
+ call remove(s:rename_map, a:response.request_seq)
+
+ if get(a:response, 'success', v:false) is v:false
+ let l:message = get(a:response, 'message', 'unknown')
+ call s:message('Error renaming "' . l:old_name . '" to: "' . l:new_name
+ \ . '". Reason: ' . l:message)
+
+ return
+ endif
+
+ let l:changes = []
+
+ for l:response_item in a:response.body.locs
+ let l:filename = l:response_item.file
+ let l:text_changes = []
+
+ for l:loc in l:response_item.locs
+ call add(l:text_changes, {
+ \ 'start': {
+ \ 'line': l:loc.start.line,
+ \ 'offset': l:loc.start.offset,
+ \ },
+ \ 'end': {
+ \ 'line': l:loc.end.line,
+ \ 'offset': l:loc.end.offset,
+ \ },
+ \ 'newText': l:new_name,
+ \})
+ endfor
+
+ call add(l:changes, {
+ \ 'fileName': l:filename,
+ \ 'textChanges': l:text_changes,
+ \})
+ endfor
+
+ if empty(l:changes)
+ call s:message('Error renaming "' . l:old_name . '" to: "' . l:new_name . '"')
+
+ return
+ endif
+
+ call ale#code_action#HandleCodeAction({
+ \ 'description': 'rename',
+ \ 'changes': l:changes,
+ \})
+endfunction
+
+function! ale#rename#HandleLSPResponse(conn_id, response) abort
+ if has_key(a:response, 'id')
+ \&& has_key(s:rename_map, a:response.id)
+ call remove(s:rename_map, a:response.id)
+
+ if !has_key(a:response, 'result')
+ call s:message('No rename result received from server')
+
+ return
+ endif
+
+ let l:workspace_edit = a:response.result
+
+ if !has_key(l:workspace_edit, 'changes') || empty(l:workspace_edit.changes)
+ call s:message('No changes received from server')
+
+ return
+ endif
+
+ let l:changes = []
+
+ for l:file_name in keys(l:workspace_edit.changes)
+ let l:text_edits = l:workspace_edit.changes[l:file_name]
+ let l:text_changes = []
+
+ for l:edit in l:text_edits
+ let l:range = l:edit.range
+ let l:new_text = l:edit.newText
+
+ call add(l:text_changes, {
+ \ 'start': {
+ \ 'line': l:range.start.line + 1,
+ \ 'offset': l:range.start.character + 1,
+ \ },
+ \ 'end': {
+ \ 'line': l:range.end.line + 1,
+ \ 'offset': l:range.end.character + 1,
+ \ },
+ \ 'newText': l:new_text,
+ \})
+ endfor
+
+ call add(l:changes, {
+ \ 'fileName': ale#path#FromURI(l:file_name),
+ \ 'textChanges': l:text_changes,
+ \})
+ endfor
+
+ call ale#code_action#HandleCodeAction({
+ \ 'description': 'rename',
+ \ 'changes': l:changes,
+ \})
+ endif
+endfunction
+
+function! s:OnReady(line, column, old_name, new_name, linter, lsp_details) abort
+ let l:id = a:lsp_details.connection_id
+
+ if !ale#lsp#HasCapability(l:id, 'rename')
+ return
+ endif
+
+ let l:buffer = a:lsp_details.buffer
+
+ let l:Callback = a:linter.lsp is# 'tsserver'
+ \ ? function('ale#rename#HandleTSServerResponse')
+ \ : function('ale#rename#HandleLSPResponse')
+
+ call ale#lsp#RegisterCallback(l:id, l:Callback)
+
+ if a:linter.lsp is# 'tsserver'
+ let l:message = ale#lsp#tsserver_message#Rename(
+ \ l:buffer,
+ \ a:line,
+ \ a:column,
+ \ g:ale_rename_tsserver_find_in_comments,
+ \ g:ale_rename_tsserver_find_in_strings,
+ \)
+ else
+ " Send a message saying the buffer has changed first, or the
+ " rename position probably won't make sense.
+ call ale#lsp#NotifyForChanges(l:id, l:buffer)
+
+ let l:message = ale#lsp#message#Rename(
+ \ l:buffer,
+ \ a:line,
+ \ a:column,
+ \ a:new_name
+ \)
+ endif
+
+ let l:request_id = ale#lsp#Send(l:id, l:message)
+
+ let s:rename_map[l:request_id] = {
+ \ 'new_name': a:new_name,
+ \ 'old_name': a:old_name,
+ \}
+endfunction
+
+function! s:ExecuteRename(linter, old_name, new_name) abort
+ let l:buffer = bufnr('')
+ let [l:line, l:column] = getpos('.')[1:2]
+
+ if a:linter.lsp isnot# 'tsserver'
+ let l:column = min([l:column, len(getline(l:line))])
+ endif
+
+ let l:Callback = function(
+ \ 's:OnReady', [l:line, l:column, a:old_name, a:new_name])
+ call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
+endfunction
+
+function! ale#rename#Execute() abort
+ let l:lsp_linters = []
+
+ for l:linter in ale#linter#Get(&filetype)
+ if !empty(l:linter.lsp)
+ call add(l:lsp_linters, l:linter)
+ endif
+ endfor
+
+ if empty(l:lsp_linters)
+ call s:message('No active LSPs')
+
+ return
+ endif
+
+ let l:old_name = expand('<cword>')
+ let l:new_name = ale#util#Input('New name: ', l:old_name)
+
+ if empty(l:new_name)
+ call s:message('New name cannot be empty!')
+
+ return
+ endif
+
+ for l:lsp_linter in l:lsp_linters
+ call s:ExecuteRename(l:lsp_linter, l:old_name, l:new_name)
+ endfor
+endfunction
diff --git a/autoload/ale/ruby.vim b/autoload/ale/ruby.vim
index 15e835c9..d941bb2c 100644
--- a/autoload/ale/ruby.vim
+++ b/autoload/ale/ruby.vim
@@ -74,3 +74,10 @@ function! ale#ruby#HandleRubocopOutput(buffer, lines) abort
return l:output
endfunction
+function! ale#ruby#EscapeExecutable(executable, bundle_exec) abort
+ let l:exec_args = a:executable =~? 'bundle'
+ \ ? ' exec ' . a:bundle_exec
+ \ : ''
+
+ return ale#Escape(a:executable) . l:exec_args
+endfunction
diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim
index 829de118..7430c7f2 100644
--- a/autoload/ale/sign.vim
+++ b/autoload/ale/sign.vim
@@ -14,6 +14,7 @@ let g:ale_sign_style_error = get(g:, 'ale_sign_style_error', g:ale_sign_error)
let g:ale_sign_warning = get(g:, 'ale_sign_warning', '--')
let g:ale_sign_style_warning = get(g:, 'ale_sign_style_warning', g:ale_sign_warning)
let g:ale_sign_info = get(g:, 'ale_sign_info', g:ale_sign_warning)
+let g:ale_sign_priority = get(g:, 'ale_sign_priority', 30)
" This variable sets an offset which can be set for sign IDs.
" This ID can be changed depending on what IDs are set for other plugins.
" The dummy sign will use the ID exactly equal to the offset.
@@ -147,24 +148,59 @@ function! ale#sign#GetSignName(sublist) abort
return 'ALEErrorSign'
endfunction
+function! s:PriorityCmd() abort
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ return ' priority=' . g:ale_sign_priority . ' '
+ else
+ return ''
+ endif
+endfunction
+
+function! s:GroupCmd() abort
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ return ' group=ale '
+ else
+ return ' '
+ endif
+endfunction
+
" Read sign data for a buffer to a list of lines.
function! ale#sign#ReadSigns(buffer) abort
redir => l:output
- silent execute 'sign place buffer=' . a:buffer
+ silent execute 'sign place ' . s:GroupCmd() . s:PriorityCmd()
+ \ . ' buffer=' . a:buffer
redir end
return split(l:output, "\n")
endfunction
+function! ale#sign#ParsePattern() abort
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ " Matches output like :
+ " line=4 id=1 group=ale name=ALEErrorSign
+ " строка=1 id=1000001 группа=ale имя=ALEErrorSign
+ " 行=1 識別子=1000001 グループ=ale 名前=ALEWarningSign
+ " línea=12 id=1000001 grupo=ale nombre=ALEWarningSign
+ " riga=1 id=1000001 gruppo=ale nome=ALEWarningSign
+ " Zeile=235 id=1000001 Gruppe=ale Name=ALEErrorSign
+ let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=ale>.*\=(ALE[a-zA-Z]+Sign)'
+ else
+ " Matches output like :
+ " line=4 id=1 name=ALEErrorSign
+ " строка=1 id=1000001 имя=ALEErrorSign
+ " 行=1 識別子=1000001 名前=ALEWarningSign
+ " línea=12 id=1000001 nombre=ALEWarningSign
+ " riga=1 id=1000001 nome=ALEWarningSign
+ " Zeile=235 id=1000001 Name=ALEErrorSign
+ let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=(ALE[a-zA-Z]+Sign)'
+ endif
+
+ return l:pattern
+endfunction
+
" Given a list of lines for sign output, return a List of [line, id, group]
function! ale#sign#ParseSigns(line_list) abort
- " Matches output like :
- " line=4 id=1 name=ALEErrorSign
- " строка=1 id=1000001 имя=ALEErrorSign
- " 行=1 識別子=1000001 名前=ALEWarningSign
- " línea=12 id=1000001 nombre=ALEWarningSign
- " riga=1 id=1000001, nome=ALEWarningSign
- let l:pattern = '\v^.*\=(\d+).*\=(\d+).*\=(ALE[a-zA-Z]+Sign)'
+ let l:pattern =ale#sign#ParsePattern()
let l:result = []
let l:is_dummy_sign_set = 0
@@ -319,8 +355,10 @@ function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
if !l:is_dummy_sign_set && (!empty(a:sign_map) || g:ale_sign_column_always)
call add(l:command_list, 'sign place '
\ . g:ale_sign_offset
- \ . ' line=1 name=ALEDummySign buffer='
- \ . a:buffer
+ \ . s:GroupCmd()
+ \ . s:PriorityCmd()
+ \ . ' line=1 name=ALEDummySign '
+ \ . ' buffer=' . a:buffer
\)
let l:is_dummy_sign_set = 1
endif
@@ -337,6 +375,8 @@ function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
if index(l:info.current_id_list, l:info.new_id) < 0
call add(l:command_list, 'sign place '
\ . (l:info.new_id)
+ \ . s:GroupCmd()
+ \ . s:PriorityCmd()
\ . ' line=' . l:line_str
\ . ' name=' . (l:info.new_name)
\ . ' buffer=' . a:buffer
@@ -351,6 +391,7 @@ function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
if l:current_id isnot l:info.new_id
call add(l:command_list, 'sign unplace '
\ . l:current_id
+ \ . s:GroupCmd()
\ . ' buffer=' . a:buffer
\)
endif
@@ -361,6 +402,7 @@ function! ale#sign#GetSignCommands(buffer, was_sign_set, sign_map) abort
if l:is_dummy_sign_set && !g:ale_sign_column_always
call add(l:command_list, 'sign unplace '
\ . g:ale_sign_offset
+ \ . s:GroupCmd()
\ . ' buffer=' . a:buffer
\)
endif
@@ -415,3 +457,12 @@ function! ale#sign#SetSigns(buffer, loclist) abort
highlight link SignColumn ALESignColumnWithoutErrors
endif
endfunction
+
+" Remove all signs.
+function! ale#sign#Clear() abort
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ sign unplace group=ale *
+ else
+ sign unplace *
+ endif
+endfunction
diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim
index e7563608..99cd856a 100644
--- a/autoload/ale/util.vim
+++ b/autoload/ale/util.vim
@@ -477,3 +477,6 @@ function! ale#util#FindItemAtCursor(buffer) abort
return [l:info, l:loc]
endfunction
+function! ale#util#Input(message, value) abort
+ return input(a:message, a:value)
+endfunction
diff --git a/doc/ale-cs.txt b/doc/ale-cs.txt
index abcc43eb..bb13863f 100644
--- a/doc/ale-cs.txt
+++ b/doc/ale-cs.txt
@@ -11,22 +11,21 @@ csc *ale-cs-csc*
The |ale-cs-csc| linter checks for semantic errors when files are opened or
saved.
-
+
See |ale-lint-file-linters| for more information on linters which do not
check for problems while you type.
- The csc linter uses the mono csc compiler providing full c# 7 and newer
- support to generate a temporary module target file (/t:module). The module
- includes including all '*.cs' files contained in the directory tree rooted
- at the path defined by the |g:ale_cs_csc_source| or |b:ale_cs_csc_source|
- variabl and all sub directories.
-
+ The csc linter uses the mono csc compiler, providing full C# 7 and newer
+ support, to generate a temporary module target file (/t:module). The module
+ includes all '*.cs' files contained in the directory tree rooted at the path
+ defined by the |g:ale_cs_csc_source| or |b:ale_cs_csc_source| variable and
+ all sub directories.
+
It will in future replace the |ale-cs-mcs| and |ale-cs-mcsc| linters as both
- utilizer the mcsc compiler which according to mono porject ist further
- developed and as of writint these lines only receives maintenance updates.
- The down is that the csc compiler does not support the -sytax option any more
- and therefore |ale-cs-csc| linter doese not offer any as you type syntax
- checking like the |ale-cs-mcsc| linter doesn't.
+ utilize the mcsc compiler which, according to the mono project, is no longer
+ actively developed, and only receives maintenance updates. However, because
+ the csc compiler does not support the -syntax option, this linter does not
+ offer any as-you-type syntax checking, similar to the |ale-cs-mcsc| linter.
The paths to search for additional assembly files can be specified using the
|g:ale_cs_csc_assembly_path| or |b:ale_cs_csc_assembly_path| variables.
diff --git a/doc/ale-d.txt b/doc/ale-d.txt
index 55596062..72349a20 100644
--- a/doc/ale-d.txt
+++ b/doc/ale-d.txt
@@ -1,6 +1,15 @@
===============================================================================
ALE D Integration *ale-d-options*
+===============================================================================
+dfmt *ale-d-dfmt*
+
+g:ale_d_dfmt_options *g:ale_d_dfmt_options*
+ *b:ale_d_dfmt_options*
+ Type: |String|
+ Default: `''`
+
+This variable can be set to pass additional options to the dfmt fixer.
===============================================================================
dls *ale-d-dls*
diff --git a/doc/ale-development.txt b/doc/ale-development.txt
index 16b16483..faa570c1 100644
--- a/doc/ale-development.txt
+++ b/doc/ale-development.txt
@@ -184,13 +184,12 @@ tests: https://github.com/junegunn/vader.vim
See |ale-development-linter-tests| for more information on how to write linter
tests.
-When you add new linters or fixers, make sure to add them into the table in
-the README, and also into the |ale-support| list in the main help file. If you
-forget to keep them both in sync, you should see an error like the following
-in Travis CI. >
-
+When you add new linters or fixers, make sure to add them into the tables in
+supported-tools.md and |ale-supported-languages-and-tools.txt|. If you forget to
+keep them both in sync, you should see an error like the following in Travis CI.
+>
========================================
- diff README.md and doc/ale.txt tables
+ diff supported-tools.md and doc/ale-supported-languages-and-tools.txt tables
========================================
Differences follow:
diff --git a/doc/ale-ink.txt b/doc/ale-ink.txt
new file mode 100644
index 00000000..9412a09f
--- /dev/null
+++ b/doc/ale-ink.txt
@@ -0,0 +1,40 @@
+===============================================================================
+ALE Ink Integration *ale-ink-options*
+
+
+===============================================================================
+ink-language-server *ale-ink-language-server*
+
+Ink Language Server
+ (https://github.com/ephraim/ink-language-server)
+
+g:ale_ink_ls_executable g:ale_ink_ls_executable
+ b:ale_ink_ls_executable
+ Type: |String|
+ Default: `'ink-language-server'`
+
+ Ink language server executable.
+
+g:ale_ink_ls_initialization_options
+ g:ale_ink_ls_initialization_options
+ b:ale_ink_ls_initialization_options
+ Type: |Dictionary|
+ Default: `{}`
+
+ Dictionary containing configuration settings that will be passed to the
+ language server at startup. For certain platforms and certain story
+ structures, the defaults will suffice. However, many projects will need to
+ change these settings - see the ink-language-server website for more
+ information.
+
+ An example of setting non-default options:
+ {
+ \ 'ink': {
+ \ 'mainStoryPath': 'init.ink',
+ \ 'inklecateExecutablePath': '/usr/local/bin/inklecate',
+ \ 'runThroughMono': v:false
+ \ }
+ \}
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-nix.txt b/doc/ale-nix.txt
new file mode 100644
index 00000000..5b2bd6cb
--- /dev/null
+++ b/doc/ale-nix.txt
@@ -0,0 +1,24 @@
+===============================================================================
+ALE Nix Integration *ale-nix-options*
+
+
+===============================================================================
+nixpkgs-fmt *ale-nix-nixpkgs-fmt*
+
+g:ale_nix_nixpkgsfmt_executable *g:ale_nix_nixpkgsfmt_executable*
+ *b:ale_nix_nixpkgsfmt_executable*
+ Type: |String|
+ Default: `'nixpkgs-fmt'`
+
+ This variable sets executable used for nixpkgs-fmt.
+
+g:ale_nix_nixpkgsfmt_options *g:ale_nix_nixpkgsfmt_options*
+ *b:ale_nix_nixpkgsfmt_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to the nixpkgs-fmt fixer.
+
+
+===============================================================================
+ vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt
index e373ab8e..a27a20b2 100644
--- a/doc/ale-ruby.txt
+++ b/doc/ale-ruby.txt
@@ -21,6 +21,26 @@ g:ale_ruby_brakeman_options *g:ale_ruby_brakeman_options*
The contents of this variable will be passed through to brakeman.
+===============================================================================
+debride *ale-ruby-debride*
+
+g:ale_ruby_debride_executable *g:ale_ruby_debride_executable*
+ *b:ale_ruby_debride_executable*
+ Type: String
+ Default: `'debride'`
+
+ Override the invoked debride binary. Set this to `'bundle'` to invoke
+ `'bundle` `exec` debride'.
+
+
+g:ale_ruby_debride_options *g:ale_ruby_debride_options*
+ *b:ale_ruby_debride_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to modify flags given to debride.
+
+
===============================================================================
rails_best_practices *ale-ruby-rails_best_practices*
@@ -91,7 +111,7 @@ g:ale_ruby_rubocop_options *g:ale_ruby_rubocop_options*
Type: |String|
Default: `''`
- This variable can be change to modify flags given to rubocop.
+ This variable can be changed to modify flags given to rubocop.
===============================================================================
@@ -146,7 +166,7 @@ g:ale_ruby_sorbet_options *g:ale_ruby_sorbet_options*
Type: |String|
Default: `''`
- This variable can be change to modify flags given to sorbet.
+ This variable can be changed to modify flags given to sorbet.
===============================================================================
@@ -166,7 +186,7 @@ g:ale_ruby_standardrb_options *g:ale_ruby_standardrb_options*
Type: |String|
Default: `''`
- This variable can be change to modify flags given to standardrb.
+ This variable can be changed to modify flags given to standardrb.
===============================================================================
diff --git a/doc/ale-scala.txt b/doc/ale-scala.txt
index ff43cd6c..c9638baf 100644
--- a/doc/ale-scala.txt
+++ b/doc/ale-scala.txt
@@ -3,6 +3,32 @@ ALE Scala Integration *ale-scala-options*
===============================================================================
+metals *ale-scala-metals*
+
+`metals` requires either an SBT project, a Mill project, or a running Bloop
+server.
+
+
+g:ale_scala_metals_executable *g:ale_scala_metals_executable*
+ *b:ale_scala_metals_executable*
+ Type: |String|
+ Default: `'metals-vim'`
+
+ Override the invoked `metals` binary.
+
+
+g:ale_scala_metals_project_root *g:ale_scala_metals_project_root*
+ *b:ale_scala_metals_project_root*
+ Type: |String|
+ Default: `''`
+
+ By default the project root is found by searching upwards for `build.sbt`,
+ `build.sc`, `.bloop` or `.metals`.
+ If the project root is elsewhere, you can override the project root
+ directory.
+
+
+===============================================================================
sbtserver *ale-scala-sbtserver*
`sbtserver` requires a running ^1.1.x sbt shell to connect to. It will attempt
diff --git a/doc/ale-solidity.txt b/doc/ale-solidity.txt
index 4b74a27a..b6e48675 100644
--- a/doc/ale-solidity.txt
+++ b/doc/ale-solidity.txt
@@ -3,6 +3,18 @@ ALE Solidity Integration *ale-solidity-options*
===============================================================================
+solc *ale-solidity-solc*
+
+
+g:ale_solidity_solc_options *g:ale_solidity_solc_options*
+ *b:ale_solidity_solc_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass extra options to solc.
+
+
+===============================================================================
solhint *ale-solidity-solhint*
Solhint should work out-of-the-box. You can further configure it using a
diff --git a/doc/ale-sql.txt b/doc/ale-sql.txt
index f9bc6ac2..2807271b 100644
--- a/doc/ale-sql.txt
+++ b/doc/ale-sql.txt
@@ -40,4 +40,22 @@ g:ale_sql_sqlfmt_options *g:ale_sql_sqlfmt_options*
===============================================================================
+sqlformat *ale-sql-sqlformat*
+
+g:ale_sql_sqlformat_executable *g:ale_sql_sqlformat_executable*
+ *b:ale_sql_sqlformat_executable*
+ Type: |String|
+ Default: `'sqlformat'`
+
+ This variable sets executable used for sqlformat.
+
+g:ale_sql_sqlformat_options *g:ale_sql_sqlformat_options*
+ *b:ale_sql_sqlformat_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be set to pass additional options to the sqlformat fixer.
+
+
+===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
index 37345f7b..a0a346e5 100644
--- a/doc/ale-supported-languages-and-tools.txt
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -103,6 +103,7 @@ Notes:
* Cython (pyrex filetype)
* `cython`
* D
+ * `dfmt`
* `dls`
* `dmd`
* `uncrustify`
@@ -200,6 +201,8 @@ Notes:
* `write-good`
* Idris
* `idris`
+* Ink
+ * `ink-language-server`
* ISPC
* `ispc`!!
* Java
@@ -281,6 +284,7 @@ Notes:
* `nim check`!!
* nix
* `nix-instantiate`
+ * `nixpkgs-fmt`
* nroff
* `alex`!!
* `proselint`
@@ -388,6 +392,7 @@ Notes:
* `rpmlint`
* Ruby
* `brakeman`
+ * `debride`
* `rails_best_practices`!!
* `reek`
* `rubocop`
@@ -406,6 +411,7 @@ Notes:
* `stylelint`
* Scala
* `fsc`
+ * `metals`
* `sbtserver`
* `scalac`
* `scalafmt`
@@ -420,11 +426,13 @@ Notes:
* SML
* `smlnj`
* Solidity
+ * `solc`
* `solhint`
* `solium`
* SQL
* `pgformatter`
* `sqlfmt`
+ * `sqlformat`
* `sqlint`
* Stylus
* `stylelint`
diff --git a/doc/ale.txt b/doc/ale.txt
index 142f1a70..3282cbfd 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -9,7 +9,8 @@ CONTENTS *ale-contents*
1. Introduction.........................|ale-introduction|
2. Supported Languages & Tools..........|ale-support|
3. Linting..............................|ale-lint|
- 3.1 Other Sources.....................|ale-lint-other-sources|
+ 3.1 Adding Language Servers...........|ale-lint-language-servers|
+ 3.2 Other Sources.....................|ale-lint-other-sources|
4. Fixing Problems......................|ale-fix|
5. Language Server Protocol Support.....|ale-lsp|
5.1 Completion........................|ale-completion|
@@ -147,7 +148,48 @@ ALE offers several options for controlling which linters are run.
-------------------------------------------------------------------------------
-3.1 Other Sources *ale-lint-other-sources*
+3.1 Adding Language Servers *ale-lint-language-servers*
+
+ALE comes with many default configurations for language servers, so they can
+be detected and run automatically. ALE can connect to other language servers
+by defining a new linter for a filetype. New linters can be defined in |vimrc|,
+in plugin files, or `ale_linters` directories in |runtimepath|.
+
+See |ale-linter-loading-behavior| for more information on loading linters.
+
+A minimal configuration for a language server linter might look so. >
+
+ call ale#linter#Define('filetype_here', {
+ \ 'name': 'any_name_you_want',
+ \ 'lsp': 'stdio',
+ \ 'executable': '/path/to/executable',
+ \ 'command': '%e run',
+ \ 'project_root': '/path/to/root_of_project',
+ \})
+<
+For language servers that use a TCP socket connection, you should define the
+address to connect to instead. >
+
+ call ale#linter#Define('filetype_here', {
+ \ 'name': 'any_name_you_want',
+ \ 'lsp': 'stdio',
+ \ 'address': 'servername:1234',
+ \ 'project_root': '/path/to/root_of_project',
+ \})
+<
+ Most of the options for a language server can be replaced with a |Funcref|
+ for a function accepting a buffer number for dynamically computing values
+ such as the executable path, the project path, the server address, etc,
+ most of which can also be determined based on executing some other
+ asynchronous task. See |ale#command#Run()| for computing linter options
+ based on asynchronous results.
+
+ See |ale#linter#Define()| for a detailed explanation of all of the options
+ for configuring linters.
+
+
+-------------------------------------------------------------------------------
+3.2 Other Sources *ale-lint-other-sources*
Problems for a buffer can be taken from other sources and rendered by ALE.
This allows ALE to be used in combination with other plugins which also want
@@ -376,6 +418,10 @@ The |ALEComplete| command can be used to show completion suggestions manually,
even when |g:ale_completion_enabled| is set to `0`. For manually requesting
completion information with Deoplete, consult Deoplete's documentation.
+When working with TypeScript files, ALE by can support automatic imports
+from external modules. This behavior can be enabled by setting the
+|g:ale_completion_tsserver_autoimport| variable to `1`.
+
*ale-completion-completeopt-bug*
ALE Automatic completion implementation replaces |completeopt| before opening
@@ -597,6 +643,16 @@ b:ale_completion_enabled *b:ale_completion_enabled*
See |ale-completion|
+g:ale_completion_tsserver_autoimport *g:ale_completion_tsserver_autoimport*
+
+ Type: Number
+ Default: `0`
+
+ When this option is set to `0`, ALE will not try to automatically import
+ completion results from external modules. It can be enabled by setting it
+ to `1`.
+
+
g:ale_completion_excluded_words *g:ale_completion_excluded_words*
*b:ale_completion_excluded_words*
Type: |List|
@@ -1027,9 +1083,12 @@ g:ale_linter_aliases *g:ale_linter_aliases*
{
\ 'Dockerfile': 'dockerfile',
\ 'csh': 'sh',
+ \ 'javascriptreact': ['javascript', 'jsx'],
\ 'plaintex': 'tex',
\ 'rmarkdown': 'r',
+ \ 'rmd': 'r',
\ 'systemverilog': 'verilog',
+ \ 'typescriptreact': ['typescript', 'tsx'],
\ 'verilog_systemverilog': ['verilog_systemverilog', 'verilog'],
\ 'vimwiki': 'markdown',
\ 'vue': ['vue', 'javascript'],
@@ -1317,6 +1376,27 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled*
will not set buffer variables per |g:ale_pattern_options|.
+g:ale_rename_tsserver_find_in_comments *g:ale_rename_tsserver_find_in_comments*
+
+ Type: |Number|
+ Default: `0`
+
+ If enabled, this option will tell tsserver to find and replace text in
+ comments when calling |ALERename|. It can be enabled by settings the value
+ to `1`.
+
+
+g:ale_rename_tsserver_find_in_strings *g:ale_rename_tsserver_find_in_strings*
+
+
+ Type: |Number|
+ Default: `0`
+
+ If enabled, this option will tell tsserver to find and replace text in
+ strings when calling |ALERename|. It can be enabled by settings the value to
+ `1`.
+
+
g:ale_set_balloons *g:ale_set_balloons*
*b:ale_set_balloons*
@@ -1463,6 +1543,16 @@ g:ale_set_signs *g:ale_set_signs*
To limit the number of signs ALE will set, see |g:ale_max_signs|.
+g:ale_sign_priority *g:ale_sign_priority*
+
+ Type: |Number|
+ Default: `30`
+
+ From Neovim 0.4.0 and Vim 8.1, ALE can set sign priority to all signs. The
+ larger this value is, the higher priority ALE signs have over other plugin
+ signs. See |sign-priority| for further details on how priority works.
+
+
g:ale_shell *g:ale_shell*
Type: |String|
@@ -1555,7 +1645,7 @@ g:ale_sign_warning *g:ale_sign_warning*
The sign for warnings in the sign gutter.
-g:ale_sign_highlight_linenrs *g:ale_sign_highlight_linenrs*
+g:ale_sign_highlight_linenrs *g:ale_sign_highlight_linenrs*
Type: |Number|
Default: `0`
@@ -1967,6 +2057,14 @@ g:ale_languagetool_executable *g:ale_languagetool_executable*
The executable to run for languagetool.
+g:ale_languagetool_options *g:ale_languagetool_options*
+ *b:ale_languagetool_options*
+ Type: |String|
+ Default: `'--autoDetect'`
+
+ This variable can be set to pass additional options to languagetool.
+
+
-------------------------------------------------------------------------------
7.3. Options for write-good *ale-write-good-options*
@@ -2066,6 +2164,7 @@ documented in additional help files.
nvcc..................................|ale-cuda-nvcc|
clang-format..........................|ale-cuda-clangformat|
d.......................................|ale-d-options|
+ dfmt..................................|ale-d-dfmt|
dls...................................|ale-d-dls|
uncrustify............................|ale-d-uncrustify|
dart....................................|ale-dart-options|
@@ -2149,6 +2248,8 @@ documented in additional help files.
write-good............................|ale-html-write-good|
idris...................................|ale-idris-options|
idris.................................|ale-idris-idris|
+ ink.....................................|ale-ink-options|
+ ink-language-server...................|ale-ink-language-server|
ispc....................................|ale-ispc-options|
ispc..................................|ale-ispc-ispc|
java....................................|ale-java-options|
@@ -2204,6 +2305,8 @@ documented in additional help files.
mmc...................................|ale-mercury-mmc|
nasm....................................|ale-nasm-options|
nasm..................................|ale-nasm-nasm|
+ nix.....................................|ale-nix-options|
+ nixpkgs-fmt...........................|ale-nix-nixpkgs-fmt|
nroff...................................|ale-nroff-options|
write-good............................|ale-nroff-write-good|
objc....................................|ale-objc-options|
@@ -2294,6 +2397,7 @@ documented in additional help files.
write-good............................|ale-restructuredtext-write-good|
ruby....................................|ale-ruby-options|
brakeman..............................|ale-ruby-brakeman|
+ debride...............................|ale-ruby-debride|
rails_best_practices..................|ale-ruby-rails_best_practices|
reek..................................|ale-ruby-reek|
rubocop...............................|ale-ruby-rubocop|
@@ -2311,6 +2415,7 @@ documented in additional help files.
sasslint..............................|ale-sass-sasslint|
stylelint.............................|ale-sass-stylelint|
scala...................................|ale-scala-options|
+ metals................................|ale-scala-metals|
sbtserver.............................|ale-scala-sbtserver|
scalafmt..............................|ale-scala-scalafmt|
scalastyle............................|ale-scala-scalastyle|
@@ -2326,6 +2431,7 @@ documented in additional help files.
sml.....................................|ale-sml-options|
smlnj.................................|ale-sml-smlnj|
solidity................................|ale-solidity-options|
+ solc..................................|ale-solidity-solc|
solhint...............................|ale-solidity-solhint|
solium................................|ale-solidity-solium|
spec....................................|ale-spec-options|
@@ -2333,6 +2439,7 @@ documented in additional help files.
sql.....................................|ale-sql-options|
pgformatter...........................|ale-sql-pgformatter|
sqlfmt................................|ale-sql-sqlfmt|
+ sqlformat.............................|ale-sql-sqlformat|
stylus..................................|ale-stylus-options|
stylelint.............................|ale-stylus-stylelint|
sugarss.................................|ale-sugarss-options|
@@ -2534,6 +2641,18 @@ ALEHover *ALEHover*
A plug mapping `<Plug>(ale_hover)` is defined for this command.
+ALEOrganizeImports *ALEOrganizeImports*
+
+ Organize imports using tsserver. Currently not implemented for LSPs.
+
+
+ALERename *ALERename*
+
+ Rename a symbol using TypeScript server or Language Server.
+
+ The user will be prompted for a new name.
+
+
ALESymbolSearch `<query>` *ALESymbolSearch*
Search for symbols in the workspace, taken from any available LSP linters.
diff --git a/plugin/ale.vim b/plugin/ale.vim
index 6262a7c4..1912a9c0 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -221,6 +221,12 @@ command! -nargs=1 ALESymbolSearch :call ale#symbol#Search(<q-args>)
command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual')
+" Rename symbols using tsserver and LSP
+command! -bar ALERename :call ale#rename#Execute()
+
+" Organize import statements using tsserver
+command! -bar ALEOrganizeImports :call ale#organize_imports#Execute()
+
" <Plug> mappings for commands
nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return>
nnoremap <silent> <Plug>(ale_previous_wrap) :ALEPreviousWrap<Return>
@@ -259,6 +265,7 @@ nnoremap <silent> <Plug>(ale_find_references) :ALEFindReferences<Return>
nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return>
nnoremap <silent> <Plug>(ale_documentation) :ALEDocumentation<Return>
inoremap <silent> <Plug>(ale_complete) <C-\><C-O>:ALEComplete<Return>
+nnoremap <silent> <Plug>(ale_rename) :ALERename<Return>
" Set up autocmd groups now.
call ale#events#Init()
diff --git a/supported-tools.md b/supported-tools.md
index c933f510..15b91d52 100644
--- a/supported-tools.md
+++ b/supported-tools.md
@@ -112,6 +112,7 @@ formatting.
* Cython (pyrex filetype)
* [cython](http://cython.org/)
* D
+ * [dfmt](https://github.com/dlang-community/dfmt)
* [dls](https://github.com/d-language-server/dls)
* [dmd](https://dlang.org/dmd-linux.html)
* [uncrustify](https://github.com/uncrustify/uncrustify)
@@ -209,6 +210,8 @@ formatting.
* [write-good](https://github.com/btford/write-good)
* Idris
* [idris](http://www.idris-lang.org/)
+* Ink
+ * [ink-language-server](https://github.com/ephread/ink-language-server)
* ISPC
* [ispc](https://ispc.github.io/) :floppy_disk:
* Java
@@ -223,7 +226,7 @@ formatting.
* [eslint](http://eslint.org/)
* [fecs](http://fecs.baidu.com/)
* [flow](https://flowtype.org/)
- * [jscs](http://jscs.info/)
+ * [jscs](https://jscs-dev.github.io/)
* [jshint](http://jshint.com/)
* [prettier](https://github.com/prettier/prettier)
* [prettier-eslint](https://github.com/prettier/prettier-eslint-cli)
@@ -248,7 +251,7 @@ formatting.
* [lacheck](https://www.ctan.org/pkg/lacheck)
* [proselint](http://proselint.com/)
* [redpen](http://redpen.cc/)
- * [texlab](https://texlab.netlify.com) ([Rust rewrite](https://github.com/latex-lsp/texlab/tree/rust))
+ * [texlab](https://texlab.netlify.com)
* [textlint](https://textlint.github.io/)
* [vale](https://github.com/ValeLint/vale)
* [write-good](https://github.com/btford/write-good)
@@ -290,6 +293,7 @@ formatting.
* [nim check](https://nim-lang.org/docs/nimc.html) :floppy_disk:
* nix
* [nix-instantiate](http://nixos.org/nix/manual/#sec-nix-instantiate)
+ * [nixpkgs-fmt](https://github.com/nix-community/nixpkgs-fmt)
* nroff
* [alex](https://github.com/wooorm/alex) :floppy_disk:
* [proselint](http://proselint.com/)
@@ -397,6 +401,7 @@ formatting.
* [rpmlint](https://github.com/rpm-software-management/rpmlint) :warning: (see `:help ale-integration-spec`)
* Ruby
* [brakeman](http://brakemanscanner.org/) :floppy_disk:
+ * [debride](https://github.com/seattlerb/debride) :floppy_disk:
* [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) :floppy_disk:
* [reek](https://github.com/troessner/reek)
* [rubocop](https://github.com/bbatsov/rubocop)
@@ -415,6 +420,7 @@ formatting.
* [stylelint](https://github.com/stylelint/stylelint)
* Scala
* [fsc](https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/tools/fsc.html)
+ * [metals](https://scalameta.org/metals/)
* [sbtserver](https://www.scala-sbt.org/1.x/docs/sbt-server.html)
* [scalac](http://scala-lang.org)
* [scalafmt](https://scalameta.org/scalafmt/)
@@ -429,11 +435,13 @@ formatting.
* SML
* [smlnj](http://www.smlnj.org/)
* Solidity
+ * [solc](https://solidity.readthedocs.io/)
* [solhint](https://github.com/protofire/solhint)
* [solium](https://github.com/duaraghav8/Solium)
* SQL
* [pgformatter](https://github.com/darold/pgFormatter)
* [sqlfmt](https://github.com/jackc/sqlfmt)
+ * [sqlformat](https://github.com/andialbrecht/sqlparse)
* [sqlint](https://github.com/purcell/sqlint)
* Stylus
* [stylelint](https://github.com/stylelint/stylelint)
diff --git a/test/command_callback/psalm-project/vendor/bin/psalm-language-server b/test/command_callback/ink_paths/story/main.ink
index e69de29b..e69de29b 100755..100644
--- a/test/command_callback/psalm-project/vendor/bin/psalm-language-server
+++ b/test/command_callback/ink_paths/story/main.ink
diff --git a/test/command_callback/psalm-project/vendor/bin/psalm b/test/command_callback/psalm-project/vendor/bin/psalm
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/test/command_callback/psalm-project/vendor/bin/psalm
diff --git a/test/command_callback/test_ink_ls_command_callbacks.vader b/test/command_callback/test_ink_ls_command_callbacks.vader
new file mode 100644
index 00000000..5074506c
--- /dev/null
+++ b/test/command_callback/test_ink_ls_command_callbacks.vader
@@ -0,0 +1,22 @@
+Before:
+ call ale#assert#SetUpLinterTest('ink', 'ls')
+ set ft=ink
+
+After:
+ call ale#assert#TearDownLinterTest()
+
+Execute(should set correct defaults):
+ AssertLinter 'ink-language-server', ale#Escape('ink-language-server') . ' --stdio'
+
+Execute(should set correct LSP values):
+ call ale#test#SetFilename('ink_paths/story/main.ink')
+
+ AssertLSPLanguage 'ink'
+ AssertLSPOptions {}
+ AssertLSPConfig {}
+ AssertLSPProject ale#path#Simplify(g:dir . '/ink_paths/story')
+
+Execute(should accept configuration settings):
+ AssertLSPConfig {}
+ let b:ale_ink_ls_initialization_options = {'ink': {'runThroughMono': v:true}}
+ AssertLSPOptions {'ink': {'runThroughMono': v:true}}
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_psalm_command_callbacks.vader b/test/command_callback/test_psalm_command_callbacks.vader
index 33d770c2..74c68d43 100644
--- a/test/command_callback/test_psalm_command_callbacks.vader
+++ b/test/command_callback/test_psalm_command_callbacks.vader
@@ -9,18 +9,18 @@ After:
call ale#assert#TearDownLinterTest()
Execute(The default executable path should be correct):
- AssertLinter 'psalm-language-server',
- \ ale#Escape('psalm-language-server')
+ AssertLinter 'psalm',
+ \ ale#Escape('psalm') . ' --language-server'
Execute(Vendor executables should be detected):
call ale#test#SetFilename('psalm-project/test.php')
AssertLinter
- \ ale#path#Simplify(g:dir . '/psalm-project/vendor/bin/psalm-language-server'),
+ \ ale#path#Simplify(g:dir . '/psalm-project/vendor/bin/psalm'),
\ ale#Escape(ale#path#Simplify(
\ g:dir
- \ . '/psalm-project/vendor/bin/psalm-language-server'
- \ ))
+ \ . '/psalm-project/vendor/bin/psalm'
+ \ )) . ' --language-server'
Execute(The project path should be correct for .git directories):
call ale#test#SetFilename('psalm-project/test.php')
diff --git a/test/command_callback/test_scala_metals.vader b/test/command_callback/test_scala_metals.vader
new file mode 100644
index 00000000..70e14c1a
--- /dev/null
+++ b/test/command_callback/test_scala_metals.vader
@@ -0,0 +1,20 @@
+" Author: Jeffrey Lau https://github.com/zoonfafer
+" Description: Tests for the Scala Metals linter
+
+Before:
+ call ale#assert#SetUpLinterTest('scala', 'metals')
+
+After:
+ call ale#assert#TearDownLinterTest()
+Execute(should set metals for sbt project with build.sbt):
+ call ale#test#SetFilename('../scala_fixtures/valid_sbt_project/Main.scala')
+ AssertLSPLanguage 'scala'
+ AssertLSPOptions {}
+ AssertLSPConfig {}
+ AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../scala_fixtures/valid_sbt_project')
+Execute(should not set metals for sbt project without build.sbt):
+ call ale#test#SetFilename('../scala_fixtures/invalid_sbt_project/Main.scala')
+ AssertLSPLanguage 'scala'
+ AssertLSPOptions {}
+ AssertLSPConfig {}
+ AssertLSPProject ''
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 5672f8e5..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,8 @@ After:
unlet! b:ale_complete_done_time
delfunction CheckCompletionCalled
+ delfunction ale#code_action#HandleCodeAction
+ delfunction MockHandleCodeAction
if exists('*CompleteCallback')
delfunction CompleteCallback
@@ -77,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):
@@ -385,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_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_nixpkgsfmt_fixer_callback.vader b/test/fixers/test_nixpkgsfmt_fixer_callback.vader
new file mode 100644
index 00000000..0065f77b
--- /dev/null
+++ b/test/fixers/test_nixpkgsfmt_fixer_callback.vader
@@ -0,0 +1,24 @@
+Before:
+ Save g:ale_nix_nixpkgsfmt_executable
+ Save g:ale_nix_nixpkgsfmt_options
+
+After:
+ Restore
+
+Execute(The nixpkgs-fmt callback should return the correct default values):
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('nixpkgs-fmt')
+ \ },
+ \ ale#fixers#nixpkgsfmt#Fix(bufnr(''))
+
+Execute(The nixpkgs-fmt executable and options should be configurable):
+ let g:ale_nix_nixpkgsfmt_executable = '/path/to/nixpkgs-fmt'
+ let g:ale_nix_nixpkgsfmt_options = '-h'
+
+ AssertEqual
+ \ {
+ \ 'command': ale#Escape('/path/to/nixpkgs-fmt')
+ \ . ' -h',
+ \ },
+ \ ale#fixers#nixpkgsfmt#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_debride_handler.vader b/test/handler/test_debride_handler.vader
new file mode 100644
index 00000000..62851468
--- /dev/null
+++ b/test/handler/test_debride_handler.vader
@@ -0,0 +1,27 @@
+Before:
+ runtime ale_linters/ruby/debride.vim
+
+After:
+ call ale#linter#Reset()
+
+Execute(The debride linter parses output correctly):
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 2,
+ \ 'text': 'Possible unused method: image_tags',
+ \ 'type': 'W',
+ \ },
+ \ {
+ \ 'lnum': 7,
+ \ 'text': 'Possible unused method: not_deleted',
+ \ 'type': 'W',
+ \ }
+ \ ],
+ \ ale_linters#ruby#debride#HandleOutput(0, [
+ \ 'These methods MIGHT not be called:',
+ \ '',
+ \ 'Image',
+ \ ' image_tags app/models/image.rb:2',
+ \ ' not_deleted app/models/image.rb:7'
+ \])
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/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/sign/test_linting_sets_signs.vader b/test/sign/test_linting_sets_signs.vader
index a8d5761f..bb042679 100644
--- a/test/sign/test_linting_sets_signs.vader
+++ b/test/sign/test_linting_sets_signs.vader
@@ -21,7 +21,7 @@ Before:
let g:ale_set_highlights = 0
let g:ale_echo_cursor = 0
- sign unplace *
+ call ale#sign#Clear()
function! TestCallback(buffer, output)
return [
@@ -32,16 +32,20 @@ Before:
function! CollectSigns()
redir => l:output
- silent exec 'sign place'
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ silent exec 'sign place group=ale'
+ else
+ silent exec 'sign place'
+ endif
redir END
let l:actual_sign_list = []
for l:line in split(l:output, "\n")
- let l:match = matchlist(l:line, '\v^.*\=(\d+).*\=\d+.*\=(ALE[a-zA-Z]+Sign)')
+ let l:match = matchlist(l:line, ale#sign#ParsePattern())
if len(l:match) > 0
- call add(l:actual_sign_list, [l:match[1], l:match[2]])
+ call add(l:actual_sign_list, [l:match[1], l:match[3]])
endif
endfor
@@ -60,7 +64,7 @@ After:
delfunction CollectSigns
unlet! g:ale_run_synchronously_callbacks
- sign unplace *
+ call ale#sign#Clear()
call ale#linter#Reset()
Execute(The signs should be updated after linting is done):
diff --git a/test/sign/test_sign_column_highlighting.vader b/test/sign/test_sign_column_highlighting.vader
index 0b506fa7..7ea5eb0f 100644
--- a/test/sign/test_sign_column_highlighting.vader
+++ b/test/sign/test_sign_column_highlighting.vader
@@ -30,7 +30,7 @@ After:
delfunction SetHighlight
unlet! g:sign_highlight
- sign unplace *
+ call ale#sign#Clear()
Execute(The SignColumn highlight shouldn't be changed if the option is off):
let g:ale_change_sign_column_color = 0
diff --git a/test/sign/test_sign_limits.vader b/test/sign/test_sign_limits.vader
index b8868aeb..d7a4e2f5 100644
--- a/test/sign/test_sign_limits.vader
+++ b/test/sign/test_sign_limits.vader
@@ -30,7 +30,7 @@ After:
delfunction SetNProblems
- sign unplace *
+ call ale#sign#Clear()
Execute(There should be no limit on signs with negative numbers):
AssertEqual range(1, 42), SetNProblems(42)
diff --git a/test/sign/test_sign_parsing.vader b/test/sign/test_sign_parsing.vader
index 07848afb..8fb7f8e0 100644
--- a/test/sign/test_sign_parsing.vader
+++ b/test/sign/test_sign_parsing.vader
@@ -1,35 +1,88 @@
Execute (Parsing English signs should work):
- AssertEqual
- \ [0, [[9, 1000001, 'ALEWarningSign']]],
- \ ale#sign#ParseSigns([
- \ 'Signs for app.js:',
- \ ' line=9 id=1000001 name=ALEWarningSign',
- \ ])
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[9, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([
+ \ 'Signs for app.js:',
+ \ ' line=9 id=1000001 group=ale name=ALEWarningSign',
+ \ ])
+ else
+ AssertEqual
+ \ [0, [[9, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([
+ \ 'Signs for app.js:',
+ \ ' line=9 id=1000001 name=ALEWarningSign',
+ \ ])
+ endif
Execute (Parsing Russian signs should work):
- AssertEqual
- \ [0, [[1, 1000001, 'ALEErrorSign']]],
- \ ale#sign#ParseSigns([' строка=1 id=1000001 имя=ALEErrorSign'])
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([' строка=1 id=1000001 группа=ale имя=ALEErrorSign'])
+ else
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([' строка=1 id=1000001 имя=ALEErrorSign'])
+ endif
Execute (Parsing Japanese signs should work):
- AssertEqual
- \ [0, [[1, 1000001, 'ALEWarningSign']]],
- \ ale#sign#ParseSigns([' 行=1 識別子=1000001 名前=ALEWarningSign'])
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' 行=1 識別子=1000001 グループ=ale 名前=ALEWarningSign'])
+ else
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' 行=1 識別子=1000001 名前=ALEWarningSign'])
+ endif
Execute (Parsing Spanish signs should work):
- AssertEqual
- \ [0, [[12, 1000001, 'ALEWarningSign']]],
- \ ale#sign#ParseSigns([' línea=12 id=1000001 nombre=ALEWarningSign'])
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[12, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' línea=12 id=1000001 grupo=ale nombre=ALEWarningSign'])
+ else
+ AssertEqual
+ \ [0, [[12, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' línea=12 id=1000001 nombre=ALEWarningSign'])
+ endif
Execute (Parsing Italian signs should work):
- AssertEqual
- \ [0, [[1, 1000001, 'ALEWarningSign']]],
- \ ale#sign#ParseSigns([' riga=1 id=1000001, nome=ALEWarningSign'])
- \
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' riga=1 id=1000001, gruppo=ale nome=ALEWarningSign'])
+ else
+ AssertEqual
+ \ [0, [[1, 1000001, 'ALEWarningSign']]],
+ \ ale#sign#ParseSigns([' riga=1 id=1000001, nome=ALEWarningSign'])
+ endif
+
+Execute (Parsing German signs should work):
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [0, [[235, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([' Zeile=235 id=1000001 Gruppe=ale Name=ALEErrorSign'])
+ else
+ AssertEqual
+ \ [0, [[235, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([' Zeile=235 id=1000001 Name=ALEErrorSign'])
+ endif
+
Execute (The sign parser should indicate if the dummy sign is set):
- AssertEqual
- \ [1, [[1, 1000001, 'ALEErrorSign']]],
- \ ale#sign#ParseSigns([
- \ ' строка=1 id=1000001 имя=ALEErrorSign',
- \ ' line=1 id=1000000 name=ALEDummySign',
- \ ])
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ AssertEqual
+ \ [1, [[1, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([
+ \ ' строка=1 id=1000001 group=ale имя=ALEErrorSign',
+ \ ' line=1 id=1000000 group=ale name=ALEDummySign',
+ \ ])
+ else
+ AssertEqual
+ \ [1, [[1, 1000001, 'ALEErrorSign']]],
+ \ ale#sign#ParseSigns([
+ \ ' строка=1 id=1000001 имя=ALEErrorSign',
+ \ ' line=1 id=1000000 name=ALEDummySign',
+ \ ])
+ endif
diff --git a/test/sign/test_sign_placement.vader b/test/sign/test_sign_placement.vader
index f0b3ba2f..97bd9302 100644
--- a/test/sign/test_sign_placement.vader
+++ b/test/sign/test_sign_placement.vader
@@ -17,7 +17,7 @@ Before:
let g:ale_echo_cursor = 0
call ale#linter#Reset()
- sign unplace *
+ call ale#sign#Clear()
function! GenerateResults(buffer, output)
return [
@@ -68,12 +68,16 @@ Before:
function! ParseSigns()
redir => l:output
- silent sign place
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ silent sign place group=ale
+ else
+ silent sign place
+ endif
redir END
return map(
\ split(l:output, '\n')[2:],
- \ 'matchlist(v:val, ''^.*=\(\d\+\).*=\(\d\+\).*=\(.*\)$'')[1:3]',
+ \ 'matchlist(v:val, ''' . ale#sign#ParsePattern() . ''')[1:3]',
\)
endfunction
@@ -92,7 +96,7 @@ After:
delfunction GenerateResults
delfunction ParseSigns
call ale#linter#Reset()
- sign unplace *
+ call ale#sign#Clear()
Execute(ale#sign#GetSignName should return the right sign names):
AssertEqual 'ALEErrorSign', ale#sign#GetSignName([{'type': 'E'}])
@@ -148,9 +152,15 @@ Execute(The current signs should be set for running a job):
\ ParseSigns()
Execute(Loclist items with sign_id values should be kept):
- exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('')
- exec 'sign place 1000348 line=15 name=ALEErrorSign buffer=' . bufnr('')
- exec 'sign place 1000349 line=16 name=ALEWarningSign buffer=' . bufnr('')
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ exec 'sign place 1000347 group=ale line=3 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000348 group=ale line=15 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000349 group=ale line=16 name=ALEWarningSign buffer=' . bufnr('')
+ else
+ exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000348 line=15 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000349 line=16 name=ALEWarningSign buffer=' . bufnr('')
+ endif
let g:loclist = [
\ {'bufnr': bufnr(''), 'lnum': 1, 'col': 1, 'type': 'E', 'text': 'a', 'sign_id': 1000348},
@@ -287,10 +297,17 @@ Execute(No exceptions should be thrown when setting signs for invalid buffers):
Execute(Signs should be removed when lines have multiple sign IDs on them):
" We can fail to remove signs if there are multiple signs on one line,
" say after deleting lines in Vim, etc.
- exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('')
- exec 'sign place 1000348 line=3 name=ALEWarningSign buffer=' . bufnr('')
- exec 'sign place 1000349 line=10 name=ALEErrorSign buffer=' . bufnr('')
- exec 'sign place 1000350 line=10 name=ALEWarningSign buffer=' . bufnr('')
+ if has('nvim-0.4.0') || (v:version >= 801 && has('patch614'))
+ exec 'sign place 1000347 group=ale line=3 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000348 group=ale line=3 name=ALEWarningSign buffer=' . bufnr('')
+ exec 'sign place 1000349 group=ale line=10 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000350 group=ale line=10 name=ALEWarningSign buffer=' . bufnr('')
+ else
+ exec 'sign place 1000347 line=3 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000348 line=3 name=ALEWarningSign buffer=' . bufnr('')
+ exec 'sign place 1000349 line=10 name=ALEErrorSign buffer=' . bufnr('')
+ exec 'sign place 1000350 line=10 name=ALEWarningSign buffer=' . bufnr('')
+ endif
call ale#sign#SetSigns(bufnr(''), [])
AssertEqual [], ParseSigns()
diff --git a/test/test_ale_toggle.vader b/test/test_ale_toggle.vader
index a0763cb1..1debcee6 100644
--- a/test/test_ale_toggle.vader
+++ b/test/test_ale_toggle.vader
@@ -82,7 +82,7 @@ Before:
\ 'read_buffer': 0,
\})
- sign unplace *
+ call ale#sign#Clear()
After:
Restore
@@ -105,7 +105,7 @@ After:
delfunction ParseAuGroups
call setloclist(0, [])
- sign unplace *
+ call ale#sign#Clear()
call clearmatches()
Given foobar (Some imaginary filetype):
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_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 c062018b..3b259655 100644
--- a/test/test_highlight_placement.vader
+++ b/test/test_highlight_placement.vader
@@ -114,7 +114,7 @@ After:
delfunction GenerateResults
call ale#linter#Reset()
call clearmatches()
- sign unplace *
+ call ale#sign#Clear()
highlight clear SomeOtherGroup
runtime autoload/ale/highlight.vim
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()
diff --git a/test/test_results_not_cleared_when_opening_loclist.vader b/test/test_results_not_cleared_when_opening_loclist.vader
index 4a53d356..5621eb35 100644
--- a/test/test_results_not_cleared_when_opening_loclist.vader
+++ b/test/test_results_not_cleared_when_opening_loclist.vader
@@ -8,7 +8,7 @@ After:
call setloclist(0, [])
call clearmatches()
- sign unplace *
+ call ale#sign#Clear()
Given foobar (Some file):
abc
diff --git a/test/test_setting_problems_found_in_previous_buffers.vader b/test/test_setting_problems_found_in_previous_buffers.vader
index 36eeb4ca..a5c8e0d3 100644
--- a/test/test_setting_problems_found_in_previous_buffers.vader
+++ b/test/test_setting_problems_found_in_previous_buffers.vader
@@ -38,7 +38,7 @@ After:
" Items and markers, etc.
call setloclist(0, [])
call clearmatches()
- sign unplace *
+ call ale#sign#Clear()
Given foobar(A file with some lines):
foo