From 49718e0ec6eb4b9068049048c9373167c19257c9 Mon Sep 17 00:00:00 2001 From: Dmitri Vereshchagin Date: Sun, 1 Sep 2019 17:36:40 +0300 Subject: [PATCH 01/30] Add Elvis handler for Erlang [Elvis][1] is an Erlang style reviewer. [1]: https://github.com/inaka/elvis --- ale_linters/erlang/elvis.vim | 39 +++++++++++++++++++ doc/ale-erlang.txt | 12 ++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + .../test_erlang_elvis_command_callback.vader | 16 ++++++++ test/handler/test_erlang_elvis_handler.vader | 37 ++++++++++++++++++ 7 files changed, 107 insertions(+) create mode 100644 ale_linters/erlang/elvis.vim create mode 100644 test/command_callback/test_erlang_elvis_command_callback.vader create mode 100644 test/handler/test_erlang_elvis_handler.vader diff --git a/ale_linters/erlang/elvis.vim b/ale_linters/erlang/elvis.vim new file mode 100644 index 00000000..31dea3dd --- /dev/null +++ b/ale_linters/erlang/elvis.vim @@ -0,0 +1,39 @@ +" Author: Dmitri Vereshchagin +" Description: Elvis linter for Erlang files + +call ale#Set('erlang_elvis_executable', 'elvis') + +function! ale_linters#erlang#elvis#Handle(buffer, lines) abort + let l:pattern = '\v:(\d+):[^:]+:(.+)' + let l:loclist = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:loclist, { + \ 'lnum': str2nr(l:match[1]), + \ 'text': s:AbbreviateMessage(l:match[2]), + \ 'type': 'W', + \}) + endfor + + return l:loclist +endfunction + +function! s:AbbreviateMessage(text) abort + let l:pattern = '\v\c^(line \d+ is too long):.*$' + + return substitute(a:text, l:pattern, '\1.', '') +endfunction + +function! s:GetCommand(buffer) abort + let l:file = ale#Escape(expand('#' . a:buffer . ':.')) + + return '%e rock --output-format=parsable ' . l:file +endfunction + +call ale#linter#Define('erlang', { +\ 'name': 'elvis', +\ 'callback': 'ale_linters#erlang#elvis#Handle', +\ 'executable': {b -> ale#Var(b, 'erlang_elvis_executable')}, +\ 'command': function('s:GetCommand'), +\ 'lint_file': 1, +\}) diff --git a/doc/ale-erlang.txt b/doc/ale-erlang.txt index 59993a99..38762f08 100644 --- a/doc/ale-erlang.txt +++ b/doc/ale-erlang.txt @@ -31,6 +31,18 @@ g:ale_erlang_dialyzer_rebar3_profile *g:ale_erlang_dialyzer_rebar3_profile* This variable can be changed to specify the profile that is used to run dialyzer with rebar3. + +------------------------------------------------------------------------------- +elvis *ale-erlang-elvis* + +g:ale_erlang_elvis_executable *g:ale_erlang_elvis_executable* + *b:ale_erlang_elvis_executable* + Type: |String| + Default: `'elvis'` + + This variable can be changed to specify the elvis executable. + + ------------------------------------------------------------------------------- erlc *ale-erlang-erlc* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index c6bcf421..73c2e473 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -140,6 +140,7 @@ Notes: * `erubis` * `ruumba` * Erlang + * `elvis`!! * `erlc` * `SyntaxErl` * Fish diff --git a/doc/ale.txt b/doc/ale.txt index d45cada7..4a1dc7cc 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2575,6 +2575,7 @@ documented in additional help files. elm-make..............................|ale-elm-elm-make| erlang..................................|ale-erlang-options| dialyzer..............................|ale-erlang-dialyzer| + elvis.................................|ale-erlang-elvis| erlc..................................|ale-erlang-erlc| syntaxerl.............................|ale-erlang-syntaxerl| eruby...................................|ale-eruby-options| diff --git a/supported-tools.md b/supported-tools.md index 66e46348..cfbed9ef 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -149,6 +149,7 @@ formatting. * [erubis](https://github.com/kwatch/erubis) * [ruumba](https://github.com/ericqweinstein/ruumba) * Erlang + * [elvis](https://github.com/inaka/elvis) :floppy_disk: * [erlc](http://erlang.org/doc/man/erlc.html) * [SyntaxErl](https://github.com/ten0s/syntaxerl) * Fish diff --git a/test/command_callback/test_erlang_elvis_command_callback.vader b/test/command_callback/test_erlang_elvis_command_callback.vader new file mode 100644 index 00000000..4aab49d6 --- /dev/null +++ b/test/command_callback/test_erlang_elvis_command_callback.vader @@ -0,0 +1,16 @@ +Before: + let b:file = fnamemodify(bufname(''), ':.') + call ale#assert#SetUpLinterTest('erlang', 'elvis') + +After: + call ale#assert#TearDownLinterTest() + +Execute(Default command should be correct): + AssertLinter 'elvis', + \ ale#Escape('elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file) + +Execute(Executable should be configurable): + let b:ale_erlang_elvis_executable = '/path/to/elvis' + + AssertLinter '/path/to/elvis', + \ ale#Escape('/path/to/elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file) diff --git a/test/handler/test_erlang_elvis_handler.vader b/test/handler/test_erlang_elvis_handler.vader new file mode 100644 index 00000000..365376c8 --- /dev/null +++ b/test/handler/test_erlang_elvis_handler.vader @@ -0,0 +1,37 @@ +Before: + runtime ale_linters/erlang/elvis.vim + +After: + call ale#linter#Reset() + +Execute(Warning messages should be handled): + AssertEqual + \ [ + \ { + \ 'lnum': 11, + \ 'text': "Replace the 'if' expression on line 11 with a 'case' expression or function clauses.", + \ 'type': 'W', + \ }, + \ { + \ 'lnum': 20, + \ 'text': 'Remove the debug call to io:format/1 on line 20.', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#erlang#elvis#Handle(bufnr(''), [ + \ "src/foo.erl:11:no_if_expression:Replace the 'if' expression on line 11 with a 'case' expression or function clauses.", + \ 'src/foo.erl:20:no_debug_call:Remove the debug call to io:format/1 on line 20.', + \ ]) + +Execute(Line length message shouldn't contain the line itself): + AssertEqual + \ [ + \ { + \ 'lnum': 24, + \ 'text': 'Line 24 is too long.', + \ 'type': 'W', + \ }, + \ ], + \ ale_linters#erlang#elvis#Handle(bufnr(''), [ + \ 'src/foo.erl:24:line_length:Line 24 is too long: io:format("Look ma, too long!"),.', + \ ]) From 20f6bebdf22afca71b136a1ce8cff9f8b5ec7410 Mon Sep 17 00:00:00 2001 From: Christian Keil Date: Wed, 9 Sep 2020 17:30:41 +0200 Subject: [PATCH 02/30] Fix handling of ranges at file end. --- autoload/ale/code_action.vim | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index 8c7263f3..849942ca 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -127,10 +127,20 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort endif call extend(l:middle, l:insertions[1:]) - let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :] + if l:end_line <= len(l:lines) + " Only extend the last line if end_line is within the range of + " lines. + let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :] + endif let l:lines_before_change = len(l:lines) - let l:lines = l:start + l:middle + l:lines[l:end_line :] + + if l:end_line < len(l:lines) + let l:end = l:lines[l:end_line :] + else + let l:end = [] + endif + let l:lines = l:start + l:middle + l:end let l:current_line_offset = len(l:lines) - l:lines_before_change let l:line_offset += l:current_line_offset From d3932c02424eec0520faa2afa42371c27122f5a7 Mon Sep 17 00:00:00 2001 From: Christian Keil Date: Wed, 9 Sep 2020 17:44:09 +0200 Subject: [PATCH 03/30] Fix format linting error. --- autoload/ale/code_action.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index 849942ca..1959d1b1 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -127,6 +127,7 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort endif call extend(l:middle, l:insertions[1:]) + if l:end_line <= len(l:lines) " Only extend the last line if end_line is within the range of " lines. @@ -140,6 +141,7 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort else let l:end = [] endif + let l:lines = l:start + l:middle + l:end let l:current_line_offset = len(l:lines) - l:lines_before_change From d1f48e5edef1d622817ffc054e6963e6ca956371 Mon Sep 17 00:00:00 2001 From: Christian Keil Date: Wed, 9 Sep 2020 17:49:21 +0200 Subject: [PATCH 04/30] Remove unnecessary length check. --- autoload/ale/code_action.vim | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index 1959d1b1..359bc0d6 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -135,14 +135,7 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort endif let l:lines_before_change = len(l:lines) - - if l:end_line < len(l:lines) - let l:end = l:lines[l:end_line :] - else - let l:end = [] - endif - - let l:lines = l:start + l:middle + l:end + 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 From 63a528eac29c9bc34811eba2e0e09eab24b53591 Mon Sep 17 00:00:00 2001 From: Arthur Arnold Date: Mon, 14 Sep 2020 07:57:22 -0300 Subject: [PATCH 05/30] Fix wording in Rust doc --- doc/ale-rust.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ale-rust.txt b/doc/ale-rust.txt index f4b4e7b7..3aa63673 100644 --- a/doc/ale-rust.txt +++ b/doc/ale-rust.txt @@ -22,12 +22,12 @@ Integration Information 3. rls -- If you have `rls` installed, you might prefer using this linter over cargo. rls implements the Language Server Protocol for incremental compilation of Rust code, and can check Rust files while you type. `rls` - requires Rust files to contained in Cargo projects. + requires Rust files to be contained in Cargo projects. 4. analyzer -- If you have rust-analyzer installed, you might prefer using this linter over cargo and rls. rust-analyzer also implements the Language Server Protocol for incremental compilation of Rust code, and is the next iteration of rls. rust-analyzer, like rls, requires Rust files - to contained in Cargo projects. + to be contained in Cargo projects. 5. rustfmt -- If you have `rustfmt` installed, you can use it as a fixer to consistently reformat your Rust code. From 9769565f882b43512105d1b63a2f4ceda7a39d86 Mon Sep 17 00:00:00 2001 From: Raphael Nepomuceno <58113327+rphln@users.noreply.github.com> Date: Mon, 14 Sep 2020 12:11:16 -0300 Subject: [PATCH 06/30] Collapse spaces and lines in the completion menu. --- autoload/ale/completion.vim | 5 ++++- test/completion/test_lsp_completion_parsing.vader | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index ecd93600..882fd5e6 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -606,11 +606,14 @@ function! ale#completion#ParseLSPCompletions(response) abort let l:doc = l:doc.value endif + " Collapse whitespaces and line breaks into a single space. + let l:detail = substitute(get(l:item, 'detail', ''), '\_s\+', ' ', 'g') + let l:result = { \ 'word': l:word, \ 'kind': ale#completion#GetCompletionSymbols(get(l:item, 'kind', '')), \ 'icase': 1, - \ 'menu': get(l:item, 'detail', ''), + \ 'menu': l:detail, \ 'info': (type(l:doc) is v:t_string ? l:doc : ''), \} " This flag is used to tell if this completion came from ALE or not. diff --git a/test/completion/test_lsp_completion_parsing.vader b/test/completion/test_lsp_completion_parsing.vader index d989aefe..36228c10 100644 --- a/test/completion/test_lsp_completion_parsing.vader +++ b/test/completion/test_lsp_completion_parsing.vader @@ -40,6 +40,7 @@ Execute(Should handle Rust completion results correctly): \ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ {'word': 'from', 'menu': 'fn from(s: Box) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, + \ {'word': 'to_vec', 'menu': 'pub fn to_vec(&self) -> Vec where T: Clone,', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})}, \], \ ale#completion#ParseLSPCompletions({ \ "jsonrpc":"2.0", @@ -184,6 +185,11 @@ Execute(Should handle Rust completion results correctly): \ "label":"from", \ "kind":3, \ "detail":"fn from(s: Cow<'a, str>) -> String" + \ }, + \ { + \ "label":"to_vec", + \ "kind":3, + \ "detail":"pub fn to_vec(&self) -> Vec\nwhere\n T: Clone," \ } \ ] \ }) From fec6b63494b4f491279c7a01c0d61e515b8d0f46 Mon Sep 17 00:00:00 2001 From: Arnold Chand Date: Tue, 15 Sep 2020 19:14:12 -0400 Subject: [PATCH 07/30] feat: add intelephense support for php --- ale_linters/php/intelephense.vim | 32 +++++++++++++++++ doc/ale-php.txt | 35 +++++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + .../with-composer/composer.json | 0 ...st_php_intelephense_command_callback.vader | 23 ++++++++++++ 7 files changed, 93 insertions(+) create mode 100644 ale_linters/php/intelephense.vim create mode 100644 test/command_callback/php-intelephense-project/with-composer/composer.json create mode 100644 test/command_callback/test_php_intelephense_command_callback.vader diff --git a/ale_linters/php/intelephense.vim b/ale_linters/php/intelephense.vim new file mode 100644 index 00000000..e9e07d1f --- /dev/null +++ b/ale_linters/php/intelephense.vim @@ -0,0 +1,32 @@ +" Author: Eric Stern , +" Arnold Chand +" Description: Intelephense language server integration for ALE + +call ale#Set('php_intelephense_executable', 'intelephense') +call ale#Set('php_intelephense_use_global', 1) +call ale#Set('php_intelephense_config', {}) + +function! ale_linters#php#intelephense#GetProjectRoot(buffer) abort + let l:composer_path = ale#path#FindNearestFile(a:buffer, 'composer.json') + + if (!empty(l:composer_path)) + return fnamemodify(l:composer_path, ':h') + endif + + let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git') + + return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : '' +endfunction + +function! ale_linters#php#intelephense#GetInitializationOptions() abort + return ale#Get('php_intelephense_config') +endfunction + +call ale#linter#Define('php', { +\ 'name': 'intelephense', +\ 'lsp': 'stdio', +\ 'initialization_options': function('ale_linters#php#intelephense#GetInitializationOptions'), +\ 'executable': {b -> ale#node#FindExecutable(b, 'php_intelephense', [])}, +\ 'command': '%e --stdio', +\ 'project_root': function('ale_linters#php#intelephense#GetProjectRoot'), +\}) diff --git a/doc/ale-php.txt b/doc/ale-php.txt index 9fe868f8..79d9a033 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -243,5 +243,40 @@ g:ale_php_php_executable *g:ale_php_php_executable* This variable sets the executable used for php. +=============================================================================== +intelephense *ale-php-intelephense* + +g:ale_php_intelephense_executable *g:ale_php_intelephense_executable* + *b:ale_php_intelephense_executable* + Type: |String| + Default: `'intelephense'` + + The variable can be set to configure the executable that will be used for + running the intelephense language server. `node_modules` directory + executable will be preferred instead of this setting if + |g:ale_php_intelephense_use_global| is `0`. + + See: |ale-integrations-local-executables| + + +g:ale_php_intelephense_use_global *g:ale_php_intelephense_use_global* + *b:ale_php_intelephense_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + This variable can be set to `1` to force the language server to be run with + the executable set for |g:ale_php_intelephense_executable|. + + See: |ale-integrations-local-executables| + +g:ale_php_intelephense_config *g:ale_php_intelephense_config* + *b:ale_php_intelephense_config* + Type: |Dictionary| + Default: `{}` + + The initialization options config specified by Intelephense. Refer to the + installation docs provided by intelephense (github.com/bmewburn/intelephense + -docs). + =============================================================================== 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 c6bcf421..7c8c9bac 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -324,6 +324,7 @@ Notes: * Perl6 * `perl6 -c` * PHP + * `intelephense` * `langserver` * `phan` * `phpcbf` diff --git a/doc/ale.txt b/doc/ale.txt index 6ef137c1..67ef531c 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2752,6 +2752,7 @@ documented in additional help files. psalm.................................|ale-php-psalm| php-cs-fixer..........................|ale-php-php-cs-fixer| php...................................|ale-php-php| + intelephense..........................|ale-php-intelephense| po......................................|ale-po-options| write-good............................|ale-po-write-good| pod.....................................|ale-pod-options| diff --git a/supported-tools.md b/supported-tools.md index 66e46348..559f841a 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -333,6 +333,7 @@ formatting. * Perl6 * [perl6 -c](https://perl6.org) :warning: * PHP + * [intelephense](https://github.com/bmewburn/intelephense-docs) * [langserver](https://github.com/felixfbecker/php-language-server) * [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions * [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer) diff --git a/test/command_callback/php-intelephense-project/with-composer/composer.json b/test/command_callback/php-intelephense-project/with-composer/composer.json new file mode 100644 index 00000000..e69de29b diff --git a/test/command_callback/test_php_intelephense_command_callback.vader b/test/command_callback/test_php_intelephense_command_callback.vader new file mode 100644 index 00000000..283fd615 --- /dev/null +++ b/test/command_callback/test_php_intelephense_command_callback.vader @@ -0,0 +1,23 @@ +Before: + call ale#assert#SetUpLinterTest('php', 'intelephense') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'intelephense', + \ ale#Escape('intelephense') . ' --stdio' + +Execute(The project path should be correct for .git directories): + call ale#test#SetFilename('php-intelephense-project/with-git/test.php') + silent! call mkdir('php-langserver-project/with-git/.git', 'p') + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git') + +Execute(The project path should be correct for composer.json file): + call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') + +Execute(The project should save to a temp dir): + call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') + let g:ale_php_intelephense_config = { 'storagePath': '/tmp/intelephense' } + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') From 8a855e3e6442a3b1384a07098d166c114a3e01f3 Mon Sep 17 00:00:00 2001 From: Arnold Chand Date: Thu, 17 Sep 2020 09:35:06 -0400 Subject: [PATCH 08/30] fix: tests --- .../test_php_intelephense_command_callback.vader | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/command_callback/test_php_intelephense_command_callback.vader b/test/command_callback/test_php_intelephense_command_callback.vader index 283fd615..164ca26e 100644 --- a/test/command_callback/test_php_intelephense_command_callback.vader +++ b/test/command_callback/test_php_intelephense_command_callback.vader @@ -10,14 +10,17 @@ Execute(The default executable path should be correct): Execute(The project path should be correct for .git directories): call ale#test#SetFilename('php-intelephense-project/with-git/test.php') - silent! call mkdir('php-langserver-project/with-git/.git', 'p') + silent! call mkdir('php-intelephense-project/with-git/.git') + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git') Execute(The project path should be correct for composer.json file): call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') -Execute(The project should save to a temp dir): +Execute(The project cache should be saved in a temp dir): call ale#test#SetFilename('php-intelephense-project/with-composer/test.php') let g:ale_php_intelephense_config = { 'storagePath': '/tmp/intelephense' } + AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer') From f8351c1b22d02f194235388779ad47028d5e7a25 Mon Sep 17 00:00:00 2001 From: Arnold Chand Date: Thu, 17 Sep 2020 09:58:45 -0400 Subject: [PATCH 09/30] fix: test: mkdir should create if it doesn't exist --- .../test_php_intelephense_command_callback.vader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/command_callback/test_php_intelephense_command_callback.vader b/test/command_callback/test_php_intelephense_command_callback.vader index 164ca26e..dd6adb3d 100644 --- a/test/command_callback/test_php_intelephense_command_callback.vader +++ b/test/command_callback/test_php_intelephense_command_callback.vader @@ -10,7 +10,7 @@ Execute(The default executable path should be correct): Execute(The project path should be correct for .git directories): call ale#test#SetFilename('php-intelephense-project/with-git/test.php') - silent! call mkdir('php-intelephense-project/with-git/.git') + silent! call mkdir('php-intelephense-project/with-git/.git', 'p') AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git') From 6bebdcfa30b492b7c71316b707d36c4f4d19a1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wo=C5=BAniak?= Date: Fri, 18 Sep 2020 12:11:05 +0200 Subject: [PATCH 10/30] Improves fixer performance for large buffers --- autoload/ale/util.vim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/autoload/ale/util.vim b/autoload/ale/util.vim index 1f396377..fcc03eb7 100644 --- a/autoload/ale/util.vim +++ b/autoload/ale/util.vim @@ -486,7 +486,7 @@ function! ale#util#Input(message, value) abort endfunction function! ale#util#HasBuflineApi() abort - return exists('*deletebufline') && exists('*setbufline') + return exists('*deletebufline') && exists('*appendbufline') && exists('*getpos') && exists('*setpos') endfunction " Sets buffer contents to lines @@ -507,8 +507,11 @@ function! ale#util#SetBufferContents(buffer, lines) abort " Use a Vim API for setting lines in other buffers, if available. if l:has_bufline_api - call setbufline(a:buffer, 1, l:new_lines) - call deletebufline(a:buffer, l:first_line_to_remove, '$') + let l:save_cursor = getpos('.') + call deletebufline(a:buffer, 1, '$') + call appendbufline(a:buffer, 1, l:new_lines) + call deletebufline(a:buffer, 1, 1) + call setpos('.', l:save_cursor) " Fall back on setting lines the old way, for the current buffer. else let l:old_line_length = line('$') From e08996940468ce90616a9704a439652f1ab73e31 Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Wed, 12 Aug 2020 11:06:35 +0200 Subject: [PATCH 11/30] fix(ale_linters/phpcs): add support for multiline error messages --- ale_linters/php/phpcs.vim | 2 +- test/handler/test_phpcs_handler.vader | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ale_linters/php/phpcs.vim b/ale_linters/php/phpcs.vim index 11b81e84..c5a3faa9 100644 --- a/ale_linters/php/phpcs.vim +++ b/ale_linters/php/phpcs.vim @@ -23,7 +23,7 @@ function! ale_linters#php#phpcs#Handle(buffer, lines) abort " Matches against lines like the following: " " /path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact) - let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) - \(.\+\) (\(.\+\))$' + let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) - \(.\+\) (\(.\+\)).*$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) diff --git a/test/handler/test_phpcs_handler.vader b/test/handler/test_phpcs_handler.vader index 18accece..26d35cb8 100644 --- a/test/handler/test_phpcs_handler.vader +++ b/test/handler/test_phpcs_handler.vader @@ -13,7 +13,16 @@ Execute(phpcs errors should be handled): \ 'type': 'E', \ 'sub_type': 'style', \ 'text': 'Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)', - \ }], + \ }, + \ { + \ 'lnum': 22, + \ 'col': 3, + \ 'type': 'E', + \ 'sub_type': 'style', + \ 'text': 'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks)', + \ }, + \ ], \ ale_linters#php#phpcs#Handle(bufnr(''), [ \ '/path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)', + \ "/path/to/some-filename.php:22:3: error - All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '\"\n'.", \ ]) From 9a8ab764d5a77d1ebf18b176ce57e08a0603a5f5 Mon Sep 17 00:00:00 2001 From: rgossiaux Date: Fri, 2 Oct 2020 22:51:54 -0400 Subject: [PATCH 12/30] Fix typo in ale-python.txt --- doc/ale-python.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 6b1a6d33..5cf7cd74 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -687,7 +687,7 @@ g:ale_python_pyre_auto_pipenv *g:ale_python_pyre_auto_pipenv* =============================================================================== pyright *ale-python-pyright* -The `pyrlight` linter requires a recent version of `pyright` which includes +The `pyright` linter requires a recent version of `pyright` which includes the `pyright-langserver` executable. You can install `pyright` on your system through `npm` with `sudo npm install -g pyright` or similar. From b496c4b1648cd15f8f6918d7c29853a8a04525c4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 Nov 2019 21:15:35 +0000 Subject: [PATCH 13/30] Add ormolu fixer. This commit adds a fixer for the Haskell language, ormolu (https://github.com/tweag/ormolu). Signed-off-by: David Wood --- autoload/ale/fix/registry.vim | 5 ++++ autoload/ale/fixers/ormolu.vim | 12 ++++++++++ doc/ale-haskell.txt | 20 ++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/fixers/test_ormolu_fixer_callback.vader | 24 ++++++++++++++++++++ 7 files changed, 64 insertions(+) create mode 100644 autoload/ale/fixers/ormolu.vim create mode 100644 test/fixers/test_ormolu_fixer_callback.vader diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index d71668f2..377edafd 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -380,6 +380,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['dhall'], \ 'description': 'Fix Dhall files with dhall-format.', \ }, +\ 'ormolu': { +\ 'function': 'ale#fixers#ormolu#Fix', +\ 'suggested_filetypes': ['haskell'], +\ 'description': 'A formatter for Haskell source code.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/ormolu.vim b/autoload/ale/fixers/ormolu.vim new file mode 100644 index 00000000..69b55c1f --- /dev/null +++ b/autoload/ale/fixers/ormolu.vim @@ -0,0 +1,12 @@ +call ale#Set('haskell_ormolu_executable', 'ormolu') +call ale#Set('haskell_ormolu_options', '') + +function! ale#fixers#ormolu#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'haskell_ormolu_executable') + let l:options = ale#Var(a:buffer, 'haskell_ormolu_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options), + \} +endfunction diff --git a/doc/ale-haskell.txt b/doc/ale-haskell.txt index 5dd3ec15..fde439fe 100644 --- a/doc/ale-haskell.txt +++ b/doc/ale-haskell.txt @@ -172,5 +172,25 @@ g:ale_haskell_hie_executable *g:ale_haskell_hie_executable* ide engine. i.e. `'hie-wrapper'` +=============================================================================== +ormolu *ale-haskell-ormolu* + +g:ale_haskell_ormolu_executable *g:ale_haskell_ormolu_executable* + *b:ale_haskell_ormolu_executable* + Type: |String| + Default: `'ormolu'` + + This variable can be changed to use a different executable for ormolu. + + +g:ale_haskell_ormolu_options *g:ale_haskell_ormolu_options* + *b:ale_haskell_ormolu_options* + Type: String + Default: '' + + This variable can be used to pass extra options to the underlying ormolu + executable. + + =============================================================================== 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 4f3afd85..57a50dd4 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -195,6 +195,7 @@ Notes: * `hie` * `hindent` * `hlint` + * `ormolu` * `stack-build`!! * `stack-ghc` * `stylish-haskell` diff --git a/doc/ale.txt b/doc/ale.txt index eb8f0275..01357cd1 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2642,6 +2642,7 @@ documented in additional help files. stack-ghc.............................|ale-haskell-stack-ghc| stylish-haskell.......................|ale-haskell-stylish-haskell| hie...................................|ale-haskell-hie| + ormolu................................|ale-haskell-ormolu| hcl.....................................|ale-hcl-options| terraform-fmt.........................|ale-hcl-terraform-fmt| html....................................|ale-html-options| diff --git a/supported-tools.md b/supported-tools.md index 49460892..6aa196b2 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -204,6 +204,7 @@ formatting. * [hie](https://github.com/haskell/haskell-ide-engine) * [hindent](https://hackage.haskell.org/package/hindent) * [hlint](https://hackage.haskell.org/package/hlint) + * [ormolu](https://github.com/tweag/ormolu) * [stack-build](https://haskellstack.org/) :floppy_disk: * [stack-ghc](https://haskellstack.org/) * [stylish-haskell](https://github.com/jaspervdj/stylish-haskell) diff --git a/test/fixers/test_ormolu_fixer_callback.vader b/test/fixers/test_ormolu_fixer_callback.vader new file mode 100644 index 00000000..8df3fca9 --- /dev/null +++ b/test/fixers/test_ormolu_fixer_callback.vader @@ -0,0 +1,24 @@ +Before: + Save g:ale_haskell_ormolu_executable + Save g:ale_haskell_ormolu_options + +After: + Restore + +Execute(The ormolu callback should return the correct default values): + AssertEqual + \ { + \ 'command': ale#Escape('ormolu') + \ }, + \ ale#fixers#ormolu#Fix(bufnr('')) + +Execute(The ormolu executable and options should be configurable): + let g:ale_nix_nixpkgsfmt_executable = '/path/to/ormolu' + let g:ale_nix_nixpkgsfmt_options = '-h' + + AssertEqual + \ { + \ 'command': ale#Escape('/path/to/ormolu') + \ . ' -h', + \ }, + \ ale#fixers#nixpkgsfmt#Fix(bufnr('')) From 513e6ee972ea4ee57b28b8b8c10e0b89bb674f25 Mon Sep 17 00:00:00 2001 From: Lyz Date: Fri, 23 Oct 2020 18:53:38 +0200 Subject: [PATCH 14/30] feat: add autoimport fixer --- autoload/ale/fix/registry.vim | 5 ++ autoload/ale/fixers/autoimport.vim | 25 ++++++++++ doc/ale-python.txt | 26 ++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + .../env/Scripts/autoimport.exe | 0 .../with_virtualenv/env/bin/autoimport | 0 .../test_autoimport_fixer_callback.vader | 50 +++++++++++++++++++ 9 files changed, 109 insertions(+) create mode 100644 autoload/ale/fixers/autoimport.vim create mode 100755 test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe create mode 100755 test/command_callback/python_paths/with_virtualenv/env/bin/autoimport create mode 100644 test/fixers/test_autoimport_fixer_callback.vader diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index d71668f2..ed9c4370 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -12,6 +12,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['help'], \ 'description': 'Align help tags to the right margin', \ }, +\ 'autoimport': { +\ 'function': 'ale#fixers#autoimport#Fix', +\ 'suggested_filetypes': ['python'], +\ 'description': 'Fix import issues with autoimport.', +\ }, \ 'autopep8': { \ 'function': 'ale#fixers#autopep8#Fix', \ 'suggested_filetypes': ['python'], diff --git a/autoload/ale/fixers/autoimport.vim b/autoload/ale/fixers/autoimport.vim new file mode 100644 index 00000000..37a52db8 --- /dev/null +++ b/autoload/ale/fixers/autoimport.vim @@ -0,0 +1,25 @@ +" Author: lyz-code +" Description: Fixing Python imports with autoimport. + +call ale#Set('python_autoimport_executable', 'autoimport') +call ale#Set('python_autoimport_options', '') +call ale#Set('python_autoimport_use_global', get(g:, 'ale_use_global_executables', 0)) + +function! ale#fixers#autoimport#Fix(buffer) abort + let l:options = ale#Var(a:buffer, 'python_autoimport_options') + + let l:executable = ale#python#FindExecutable( + \ a:buffer, + \ 'python_autoimport', + \ ['autoimport'], + \) + + if !executable(l:executable) + return 0 + endif + + return { + \ 'command': ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -', + \} +endfunction diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 6b1a6d33..c6a207ac 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -41,6 +41,32 @@ ALE will look for configuration files with the following filenames. > The first directory containing any of the files named above will be used. +=============================================================================== +autoimport *ale-python-autoimport* + +g:ale_python_autoimport_executable *g:ale_python_autoimport_executable* + *b:ale_python_autoimport_executable* + Type: |String| + Default: `'autoimport'` + + See |ale-integrations-local-executables| + + +g:ale_python_autoimport_options *g:ale_python_autoimport_options* + *b:ale_python_autoimport_options* + Type: |String| + Default: `''` + + This variable can be set to pass extra options to autoimport. + + +g:ale_python_autoimport_use_global *g:ale_python_autoimport_use_global* + *b:ale_python_autoimport_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + =============================================================================== autopep8 *ale-python-autopep8* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 4f3afd85..62eed26f 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -361,6 +361,7 @@ Notes: * `purescript-language-server` * `purty` * Python + * `autoimport` * `autopep8` * `bandit` * `black` diff --git a/doc/ale.txt b/doc/ale.txt index eb8f0275..204bd0e2 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2777,6 +2777,7 @@ documented in additional help files. pyrex (cython)..........................|ale-pyrex-options| cython................................|ale-pyrex-cython| python..................................|ale-python-options| + autoimport............................|ale-python-autoimport| autopep8..............................|ale-python-autopep8| bandit................................|ale-python-bandit| black.................................|ale-python-black| diff --git a/supported-tools.md b/supported-tools.md index 49460892..6325e906 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -370,6 +370,7 @@ formatting. * [purescript-language-server](https://github.com/nwolverson/purescript-language-server) * [purty](https://gitlab.com/joneshf/purty) * Python + * [autoimport](https://lyz-code.github.io/autoimport/) * [autopep8](https://github.com/hhatto/autopep8) * [bandit](https://github.com/PyCQA/bandit) :warning: * [black](https://github.com/ambv/black) diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe new file mode 100755 index 00000000..e69de29b diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport b/test/command_callback/python_paths/with_virtualenv/env/bin/autoimport new file mode 100755 index 00000000..e69de29b diff --git a/test/fixers/test_autoimport_fixer_callback.vader b/test/fixers/test_autoimport_fixer_callback.vader new file mode 100644 index 00000000..6952cbb8 --- /dev/null +++ b/test/fixers/test_autoimport_fixer_callback.vader @@ -0,0 +1,50 @@ +Before: + Save g:ale_python_autoimport_executable + Save g:ale_python_autoimport_options + + " Use an invalid global executable, so we don't match it. + let g:ale_python_autoimport_executable = 'xxxinvalid' + let g:ale_python_autoimport_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + silent cd .. + silent cd command_callback + let g:dir = getcwd() + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + Restore + + unlet! b:bin_dir + + call ale#test#RestoreDirectory() + +Execute(The autoimport callback should return the correct default values): + AssertEqual + \ 0, + \ ale#fixers#autoimport#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) . ' -', + \ }, + \ ale#fixers#autoimport#Fix(bufnr('')) + +Execute(The autoimport callback should respect custom options): + let g:ale_python_autoimport_options = '--multi-line=3 --trailing-comma' + + AssertEqual + \ 0, + \ ale#fixers#autoimport#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) + \ . ' --multi-line=3 --trailing-comma -', + \ }, + \ ale#fixers#autoimport#Fix(bufnr('')) From b74827de99e842dc7698d8d6274486550d90f05a Mon Sep 17 00:00:00 2001 From: Nathan Herald Date: Wed, 29 Jul 2020 18:23:00 +0200 Subject: [PATCH 15/30] Look for node packages in .yarn/sdks as well --- ale_linters/typescript/tsserver.vim | 1 + autoload/ale/handlers/eslint.vim | 1 + 2 files changed, 2 insertions(+) diff --git a/ale_linters/typescript/tsserver.vim b/ale_linters/typescript/tsserver.vim index 840889f3..4726e40d 100644 --- a/ale_linters/typescript/tsserver.vim +++ b/ale_linters/typescript/tsserver.vim @@ -9,6 +9,7 @@ call ale#linter#Define('typescript', { \ 'name': 'tsserver', \ 'lsp': 'tsserver', \ 'executable': {b -> ale#node#FindExecutable(b, 'typescript_tsserver', [ +\ '.yarn/sdks/typescript/bin/tsserver', \ 'node_modules/.bin/tsserver', \ ])}, \ 'command': '%e', diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim index e37d6902..b8610612 100644 --- a/autoload/ale/handlers/eslint.vim +++ b/autoload/ale/handlers/eslint.vim @@ -5,6 +5,7 @@ let s:executables = [ \ 'node_modules/.bin/eslint_d', \ 'node_modules/eslint/bin/eslint.js', \ 'node_modules/.bin/eslint', +\ '.yarn/sdks/eslint/bin/eslint', \] let s:sep = has('win32') ? '\' : '/' From 7c04ee5c200b64ef0886677216fbec1303ec9475 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 01:18:27 +0000 Subject: [PATCH 16/30] Close #1466 - Add GVIM refactor menu support Code actions and ALERename now appear in the right click context menu for GVim by default. --- autoload/ale/code_action.vim | 102 +++++++++ autoload/ale/codefix.vim | 396 ++++++++++++++++++++++------------- doc/ale.txt | 35 +++- plugin/ale.vim | 7 + test/test_codefix.vader | 75 +------ 5 files changed, 398 insertions(+), 217 deletions(-) diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index fe6b175f..506107f4 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -263,3 +263,105 @@ function! ale#code_action#BuildChangesList(changes_map) abort return l:changes endfunction + +function! s:EscapeMenuName(text) abort + return substitute(a:text, '\\\| \|\.\|&', '\\\0', 'g') +endfunction + +function! s:UpdateMenu(data, menu_items) abort + silent! aunmenu PopUp.Refactor\.\.\. + + if empty(a:data) + return + endif + + for [l:type, l:item] in a:menu_items + let l:name = l:type is# 'tsserver' ? l:item.name : l:item.title + let l:func_name = l:type is# 'tsserver' + \ ? 'ale#codefix#ApplyTSServerCodeAction' + \ : 'ale#codefix#ApplyLSPCodeAction' + + execute printf( + \ 'anoremenu PopUp.&Refactor\.\.\..%s' + \ . ' :call %s(%s, %s)', + \ s:EscapeMenuName(l:name), + \ l:func_name, + \ string(a:data), + \ string(l:item), + \) + endfor + + if empty(a:menu_items) + silent! anoremenu PopUp.Refactor\.\.\..(None) :silent + endif +endfunction + +function! s:GetCodeActions(linter, options) abort + let l:buffer = bufnr('') + let [l:line, l:column] = getpos('.')[1:2] + let l:column = min([l:column, len(getline(l:line))]) + + let l:location = { + \ 'buffer': l:buffer, + \ 'line': l:line, + \ 'column': l:column, + \ 'end_line': l:line, + \ 'end_column': l:column, + \} + let l:Callback = function('s:OnReady', [l:location, a:options]) + call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) +endfunction + +function! ale#code_action#GetCodeActions(options) abort + silent! aunmenu PopUp.Rename + silent! aunmenu PopUp.Refactor\.\.\. + + " Only display the menu items if there's an LSP server. + let l:has_lsp = 0 + + for l:linter in ale#linter#Get(&filetype) + if !empty(l:linter.lsp) + let l:has_lsp = 1 + + break + endif + endfor + + if l:has_lsp + if !empty(expand('')) + silent! anoremenu PopUp.Rename :ALERename + endif + + silent! anoremenu PopUp.Refactor\.\.\..(None) :silent + + call ale#codefix#Execute( + \ mode() is# 'v' || mode() is# "\", + \ function('s:UpdateMenu') + \) + endif +endfunction + +function! s:Setup(enabled) abort + augroup ALECodeActionsGroup + autocmd! + + if a:enabled + autocmd MenuPopup * :call ale#code_action#GetCodeActions({}) + endif + augroup END + + if !a:enabled + silent! augroup! ALECodeActionsGroup + + silent! aunmenu PopUp.Rename + silent! aunmenu PopUp.Refactor\.\.\. + endif +endfunction + +function! ale#code_action#EnablePopUpMenu() abort + call s:Setup(1) +endfunction + +function! ale#code_action#DisablePopUpMenu() abort + call s:Setup(0) +endfunction diff --git a/autoload/ale/codefix.vim b/autoload/ale/codefix.vim index b58f5e4b..3120e7cb 100644 --- a/autoload/ale/codefix.vim +++ b/autoload/ale/codefix.vim @@ -1,5 +1,5 @@ " Author: Dalius Dobravolskas -" Description: Code Fix support for tsserver +" Description: Code Fix support for tsserver and LSP servers let s:codefix_map = {} @@ -21,23 +21,65 @@ function! s:message(message) abort call ale#util#Execute('echom ' . string(a:message)) endfunction +function! ale#codefix#ApplyTSServerCodeAction(data, item) abort + if has_key(a:item, 'changes') + let l:changes = a:item.changes + + call ale#code_action#HandleCodeAction( + \ { + \ 'description': 'codefix', + \ 'changes': l:changes, + \ }, + \ {}, + \) + else + let l:message = ale#lsp#tsserver_message#GetEditsForRefactor( + \ a:data.buffer, + \ a:data.line, + \ a:data.column, + \ a:data.end_line, + \ a:data.end_column, + \ a:item.id[0], + \ a:item.id[1], + \) + + let l:request_id = ale#lsp#Send(a:data.connection_id, l:message) + + let s:codefix_map[l:request_id] = a:data + endif +endfunction + function! ale#codefix#HandleTSServerResponse(conn_id, response) abort if !has_key(a:response, 'request_seq') \ || !has_key(s:codefix_map, a:response.request_seq) return endif - let l:location = remove(s:codefix_map, a:response.request_seq) + let l:data = remove(s:codefix_map, a:response.request_seq) + let l:MenuCallback = get(l:data, 'menu_callback', v:null) if get(a:response, 'command', '') is# 'getCodeFixes' if get(a:response, 'success', v:false) is v:false + \&& l:MenuCallback is v:null let l:message = get(a:response, 'message', 'unknown') call s:message('Error while getting code fixes. Reason: ' . l:message) return endif - if len(a:response.body) == 0 + let l:result = get(a:response, 'body', []) + call filter(l:result, 'has_key(v:val, ''changes'')') + + if l:MenuCallback isnot v:null + call l:MenuCallback( + \ l:data, + \ map(copy(l:result), '[''tsserver'', v:val]') + \) + + return + endif + + if len(l:result) == 0 call s:message('No code fixes available.') return @@ -45,14 +87,15 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort let l:code_fix_to_apply = 0 - if len(a:response.body) == 1 + if len(l:result) == 1 let l:code_fix_to_apply = 1 else let l:codefix_no = 1 let l:codefixstring = "Code Fixes:\n" - for l:codefix in a:response.body - let l:codefixstring .= l:codefix_no . ') ' . l:codefix.description . "\n" + for l:codefix in l:result + let l:codefixstring .= l:codefix_no . ') ' + \ . l:codefix.description . "\n" let l:codefix_no += 1 endfor @@ -66,21 +109,22 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort endif endif - let l:changes = a:response.body[l:code_fix_to_apply - 1].changes - - call ale#code_action#HandleCodeAction({ - \ 'description': 'codefix', - \ 'changes': l:changes, - \}, {}) + call ale#codefix#ApplyTSServerCodeAction( + \ l:data, + \ l:result[l:code_fix_to_apply - 1], + \) elseif get(a:response, 'command', '') is# 'getApplicableRefactors' if get(a:response, 'success', v:false) is v:false + \&& l:MenuCallback is v:null let l:message = get(a:response, 'message', 'unknown') call s:message('Error while getting applicable refactors. Reason: ' . l:message) return endif - if len(a:response.body) == 0 + let l:result = get(a:response, 'body', []) + + if len(l:result) == 0 call s:message('No applicable refactors available.') return @@ -88,7 +132,7 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort let l:refactors = [] - for l:item in a:response.body + for l:item in l:result for l:action in l:item.actions call add(l:refactors, { \ 'name': l:action.description, @@ -97,11 +141,21 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort endfor endfor + if l:MenuCallback isnot v:null + call l:MenuCallback( + \ l:data, + \ map(copy(l:refactors), '[''tsserver'', v:val]') + \) + + return + endif + let l:refactor_no = 1 let l:refactorstring = "Applicable refactors:\n" for l:refactor in l:refactors - let l:refactorstring .= l:refactor_no . ') ' . l:refactor.name . "\n" + let l:refactorstring .= l:refactor_no . ') ' + \ . l:refactor.name . "\n" let l:refactor_no += 1 endfor @@ -116,19 +170,10 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort let l:id = l:refactors[l:refactor_to_apply - 1].id - let l:message = ale#lsp#tsserver_message#GetEditsForRefactor( - \ l:location.buffer, - \ l:location.line, - \ l:location.column, - \ l:location.end_line, - \ l:location.end_column, - \ l:id[0], - \ l:id[1], + call ale#codefix#ApplyTSServerCodeAction( + \ l:data, + \ l:refactors[l:refactor_to_apply - 1], \) - - let l:request_id = ale#lsp#Send(l:location.connection_id, l:message) - - let s:codefix_map[l:request_id] = l:location elseif get(a:response, 'command', '') is# 'getEditsForRefactor' if get(a:response, 'success', v:false) is v:false let l:message = get(a:response, 'message', 'unknown') @@ -137,10 +182,48 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort return endif - call ale#code_action#HandleCodeAction({ - \ 'description': 'editsForRefactor', - \ 'changes': a:response.body.edits, - \}, {}) + call ale#code_action#HandleCodeAction( + \ { + \ 'description': 'editsForRefactor', + \ 'changes': a:response.body.edits, + \ }, + \ {}, + \) + endif +endfunction + +function! ale#codefix#ApplyLSPCodeAction(data, item) abort + if has_key(a:item, 'command') + \&& type(a:item.command) == v:t_dict + let l:command = a:item.command + let l:message = ale#lsp#message#ExecuteCommand( + \ l:command.command, + \ l:command.arguments, + \) + + let l:request_id = ale#lsp#Send(a:data.connection_id, l:message) + elseif has_key(a:item, 'edit') || has_key(a:item, 'arguments') + if has_key(a:item, 'edit') + let l:topass = a:item.edit + else + let l:topass = a:item.arguments[0] + endif + + let l:changes_map = ale#code_action#GetChanges(l:topass) + + if empty(l:changes_map) + return + endif + + let l:changes = ale#code_action#BuildChangesList(l:changes_map) + + call ale#code_action#HandleCodeAction( + \ { + \ 'description': 'codeaction', + \ 'changes': l:changes, + \ }, + \ {}, + \) endif endfunction @@ -158,17 +241,32 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort let l:changes = ale#code_action#BuildChangesList(l:changes_map) - call ale#code_action#HandleCodeAction({ - \ 'description': 'applyEdit', - \ 'changes': l:changes, - \}, {}) + call ale#code_action#HandleCodeAction( + \ { + \ 'description': 'applyEdit', + \ 'changes': l:changes, + \ }, + \ {} + \) elseif has_key(a:response, 'id') \&& has_key(s:codefix_map, a:response.id) - let l:location = remove(s:codefix_map, a:response.id) + let l:data = remove(s:codefix_map, a:response.id) + let l:MenuCallback = get(l:data, 'menu_callback', v:null) - if !has_key(a:response, 'result') - \ || type(a:response.result) != v:t_list - \ || len(a:response.result) == 0 + let l:result = get(a:response, 'result') + + if type(l:result) != v:t_list + let l:result = [] + endif + + " Send the results to the menu callback, if set. + if l:MenuCallback isnot v:null + call l:MenuCallback(map(copy(l:result), '[''lsp'', v:val]')) + + return + endif + + if len(l:result) == 0 call s:message('No code actions received from server') return @@ -177,8 +275,9 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort let l:codeaction_no = 1 let l:codeactionstring = "Code Fixes:\n" - for l:codeaction in a:response.result - let l:codeactionstring .= l:codeaction_no . ') ' . l:codeaction.title . "\n" + for l:codeaction in l:result + let l:codeactionstring .= l:codeaction_no . ') ' + \ . l:codeaction.title . "\n" let l:codeaction_no += 1 endfor @@ -191,42 +290,44 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort return endif - let l:item = a:response.result[l:codeaction_to_apply - 1] + let l:item = l:result[l:codeaction_to_apply - 1] - if has_key(l:item, 'command') - \ && type(l:item.command) == v:t_dict - let l:command = l:item.command - let l:message = ale#lsp#message#ExecuteCommand( - \ l:command.command, - \ l:command.arguments, - \) - - let l:request_id = ale#lsp#Send(l:location.connection_id, l:message) - elseif has_key(l:item, 'edit') || has_key(l:item, 'arguments') - if has_key(l:item, 'edit') - let l:topass = l:item.edit - else - let l:topass = l:item.arguments[0] - endif - - let l:changes_map = ale#code_action#GetChanges(l:topass) - - if empty(l:changes_map) - return - endif - - let l:changes = ale#code_action#BuildChangesList(l:changes_map) - - call ale#code_action#HandleCodeAction({ - \ 'description': 'codeaction', - \ 'changes': l:changes, - \}, {}) - endif + call ale#codefix#ApplyLSPCodeAction(l:data, l:item) endif endfunction +function! s:FindError(buffer, line, column, end_line, end_column) abort + let l:nearest_error = v:null -function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abort + if a:line == a:end_line + \&& a:column == a:end_column + \&& has_key(g:ale_buffer_info, a:buffer) + let l:nearest_error_diff = -1 + + for l:error in get(g:ale_buffer_info[a:buffer], 'loclist', []) + if has_key(l:error, 'code') && l:error.lnum == a:line + let l:diff = abs(l:error.col - a:column) + + if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff + let l:nearest_error_diff = l:diff + let l:nearest_error = l:error + endif + endif + endfor + endif + + return l:nearest_error +endfunction + +function! s:OnReady( +\ line, +\ column, +\ end_line, +\ end_column, +\ MenuCallback, +\ linter, +\ lsp_details, +\) abort let l:id = a:lsp_details.connection_id if !ale#lsp#HasCapability(l:id, 'code_actions') @@ -236,32 +337,17 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo let l:buffer = a:lsp_details.buffer if a:linter.lsp is# 'tsserver' - if a:line == a:end_line && a:column == a:end_column - if !has_key(g:ale_buffer_info, l:buffer) - return - endif - - let l:nearest_error = v:null - let l:nearest_error_diff = -1 - - for l:error in get(g:ale_buffer_info[l:buffer], 'loclist', []) - if has_key(l:error, 'code') && l:error.lnum == a:line - let l:diff = abs(l:error.col - a:column) - - if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff - let l:nearest_error_diff = l:diff - let l:nearest_error = l:error.code - endif - endif - endfor + let l:nearest_error = + \ s:FindError(l:buffer, a:line, a:column, a:end_line, a:end_column) + if l:nearest_error isnot v:null let l:message = ale#lsp#tsserver_message#GetCodeFixes( \ l:buffer, \ a:line, \ a:column, \ a:line, \ a:column, - \ [l:nearest_error], + \ [l:nearest_error.code], \) else let l:message = ale#lsp#tsserver_message#GetApplicableRefactors( @@ -277,56 +363,37 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo " completions won't know what text is nearby. call ale#lsp#NotifyForChanges(l:id, l:buffer) - if a:line == a:end_line && a:column == a:end_column - if !has_key(g:ale_buffer_info, l:buffer) - return - endif + let l:diagnostics = [] + let l:nearest_error = + \ s:FindError(l:buffer, a:line, a:column, a:end_line, a:end_column) - let l:nearest_error = v:null - let l:nearest_error_diff = -1 - - for l:error in get(g:ale_buffer_info[l:buffer], 'loclist', []) - if has_key(l:error, 'code') && l:error.lnum == a:line - let l:diff = abs(l:error.col - a:column) - - if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff - let l:nearest_error_diff = l:diff - let l:nearest_error = l:error - endif - endif - endfor - - let l:diagnostics = [] - - if l:nearest_error isnot v:null - let l:diagnostics = [{ - \ 'code': l:nearest_error.code, - \ 'message': l:nearest_error.text, - \ 'range': { - \ 'start': { 'line': l:nearest_error.lnum - 1, 'character': l:nearest_error.col - 1 }, - \ 'end': { 'line': l:nearest_error.end_lnum - 1, 'character': l:nearest_error.end_col - 1 } - \} - \}] - endif - - let l:message = ale#lsp#message#CodeAction( - \ l:buffer, - \ a:line, - \ a:column, - \ a:end_line, - \ a:end_column, - \ l:diagnostics, - \) - else - let l:message = ale#lsp#message#CodeAction( - \ l:buffer, - \ a:line, - \ a:column, - \ a:end_line, - \ a:end_column, - \ [], - \) + if l:nearest_error isnot v:null + let l:diagnostics = [ + \ { + \ 'code': l:nearest_error.code, + \ 'message': l:nearest_error.text, + \ 'range': { + \ 'start': { + \ 'line': l:nearest_error.lnum - 1, + \ 'character': l:nearest_error.col - 1, + \ }, + \ 'end': { + \ 'line': l:nearest_error.end_lnum - 1, + \ 'character': l:nearest_error.end_col - 1, + \ }, + \ }, + \ }, + \] endif + + let l:message = ale#lsp#message#CodeAction( + \ l:buffer, + \ a:line, + \ a:column, + \ a:end_line, + \ a:end_column, + \ l:diagnostics, + \) endif let l:Callback = a:linter.lsp is# 'tsserver' @@ -338,22 +405,40 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo let l:request_id = ale#lsp#Send(l:id, l:message) let s:codefix_map[l:request_id] = { - \ 'connection_id': l:id, - \ 'buffer': l:buffer, - \ 'line': a:line, - \ 'column': a:column, - \ 'end_line': a:end_line, - \ 'end_column': a:end_column, + \ 'connection_id': l:id, + \ 'buffer': l:buffer, + \ 'line': a:line, + \ 'column': a:column, + \ 'end_line': a:end_line, + \ 'end_column': a:end_column, + \ 'menu_callback': a:MenuCallback, \} endfunction -function! s:ExecuteGetCodeFix(linter, range) abort +function! s:ExecuteGetCodeFix(linter, range, MenuCallback) abort let l:buffer = bufnr('') if a:range == 0 let [l:line, l:column] = getpos('.')[1:2] let l:end_line = l:line let l:end_column = l:column + + " Expand the range to cover the current word, if there is one. + let l:cword = expand('') + + if !empty(l:cword) + let l:search_pos = searchpos('\V' . l:cword, 'bn', l:line) + + if l:search_pos != [0, 0] + let l:column = l:search_pos[1] + let l:end_column = l:column + len(l:cword) - 1 + endif + endif + elseif mode() is# 'v' || mode() is# "\" + " You need to get the start and end in a different way when you're in + " visual mode. + let [l:line, l:column] = getpos('v')[1:2] + let [l:end_line, l:end_column] = getpos('.')[1:2] else let [l:line, l:column] = getpos("'<")[1:2] let [l:end_line, l:end_column] = getpos("'>")[1:2] @@ -363,11 +448,18 @@ function! s:ExecuteGetCodeFix(linter, range) abort let l:end_column = min([l:end_column, len(getline(l:end_line))]) let l:Callback = function( - \ 's:OnReady', [l:line, l:column, l:end_line, l:end_column]) + \ 's:OnReady', [l:line, l:column, l:end_line, l:end_column, a:MenuCallback] + \) + call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) endfunction -function! ale#codefix#Execute(range) abort +function! ale#codefix#Execute(range, ...) abort + if a:0 > 1 + throw 'Too many arguments' + endif + + let l:MenuCallback = get(a:000, 0, v:null) let l:lsp_linters = [] for l:linter in ale#linter#Get(&filetype) @@ -377,12 +469,16 @@ function! ale#codefix#Execute(range) abort endfor if empty(l:lsp_linters) - call s:message('No active LSPs') + if l:MenuCallback is v:null + call s:message('No active LSPs') + else + call l:MenuCallback({}, []) + endif return endif for l:lsp_linter in l:lsp_linters - call s:ExecuteGetCodeFix(l:lsp_linter, a:range) + call s:ExecuteGetCodeFix(l:lsp_linter, a:range, l:MenuCallback) endfor endfunction diff --git a/doc/ale.txt b/doc/ale.txt index 013b39f9..20baf355 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -673,13 +673,31 @@ for a full list of options. ------------------------------------------------------------------------------- 5.7 Refactoring: Rename, Actions *ale-refactor* -ALE supports renaming symbols in symbols in code such as variables or class -names with the |ALERename| command. +ALE supports renaming symbols in code such as variables or class names with +the |ALERename| command. |ALECodeAction| will execute actions on the cursor or applied to a visual range selection, such as automatically fixing errors. +Actions will appear in the right click mouse menu by default for GUI versions +of Vim, unless disabled by setting |g:ale_popup_menu_enabled| to `0`. +Make sure to set your Vim to move the cursor position whenever you right +click, and enable the mouse menu: > + + set mouse=a + set mousemodel=popup_setpos +< +You may wish to remove some other menu items you don't want to see: > + + silent! aunmenu PopUp.Select\ Word + silent! aunmenu PopUp.Select\ Sentence + silent! aunmenu PopUp.Select\ Paragraph + silent! aunmenu PopUp.Select\ Line + silent! aunmenu PopUp.Select\ Block + silent! aunmenu PopUp.Select\ Blockwise + silent! aunmenu PopUp.Select\ All +< =============================================================================== 6. Global Options *ale-options* @@ -1784,6 +1802,19 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* will not set buffer variables per |g:ale_pattern_options|. +g:ale_popup_menu_enabled *g:ale_popup_menu_enabled* + + Type: |Number| + Default: `has('gui')` + + When this option is set to `1`, ALE will show code actions and rename + capabilities in the right click mouse menu when there's a LSP server or + tsserver available. See |ale-refactor|. + + This setting must be set to `1` before ALE is loaded for this behavior + to be enabled. See |ale-lint-settings-on-startup|. + + g:ale_rename_tsserver_find_in_comments *g:ale_rename_tsserver_find_in_comments* Type: |Number| diff --git a/plugin/ale.vim b/plugin/ale.vim index c5c1d3d3..2398956e 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -158,6 +158,9 @@ let g:ale_python_auto_pipenv = get(g:, 'ale_python_auto_pipenv', 0) " This variable can be overridden to set the GO111MODULE environment variable. let g:ale_go_go111module = get(g:, 'ale_go_go111module', '') +" If 1, enable a popup menu for commands. +let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui')) + if g:ale_set_balloons call ale#balloon#Enable() endif @@ -166,6 +169,10 @@ if g:ale_completion_enabled call ale#completion#Enable() endif +if g:ale_popup_menu_enabled + call ale#code_action#EnablePopUpMenu() +endif + " Define commands for moving through warnings and errors. command! -bar -nargs=* ALEPrevious \ :call ale#loclist_jumping#WrapJump('before', ) diff --git a/test/test_codefix.vader b/test/test_codefix.vader index 63275d7f..deb97256 100644 --- a/test/test_codefix.vader +++ b/test/test_codefix.vader @@ -105,11 +105,9 @@ Execute(Failed codefix responses should be handled correctly): \) AssertEqual g:handle_code_action_called, 0 - - Given typescript(Some typescript file): foo - somelongerline + somelongerline () bazxyzxyzxyz Execute(getCodeFixes from tsserver should be handled): @@ -283,7 +281,7 @@ Execute(tsserver codefix requests should be sent): runtime ale_linters/typescript/tsserver.vim let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'code': 2304}]}} - call setpos('.', [bufnr(''), 2, 5, 0]) + call setpos('.', [bufnr(''), 2, 16, 0]) " ALECodeAction call ale#codefix#Execute(0) @@ -303,9 +301,9 @@ Execute(tsserver codefix requests should be sent): \ ale#lsp#tsserver_message#Change(bufnr('')), \ [0, 'ts@getCodeFixes', { \ 'startLine': 2, - \ 'startOffset': 5, + \ 'startOffset': 16, \ 'endLine': 2, - \ 'endOffset': 6, + \ 'endOffset': 17, \ 'file': expand('%:p'), \ 'errorCodes': [2304], \ }] @@ -316,8 +314,8 @@ Execute(tsserver codefix requests should be sent only for error with code): call ale#linter#Reset() runtime ale_linters/typescript/tsserver.vim - let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5}, {'lnum': 2, 'col': 5, 'code': 2304}]}} - call setpos('.', [bufnr(''), 2, 5, 0]) + let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 16}, {'lnum': 2, 'col': 16, 'code': 2304}]}} + call setpos('.', [bufnr(''), 2, 16, 0]) " ALECodeAction call ale#codefix#Execute(0) @@ -337,9 +335,9 @@ Execute(tsserver codefix requests should be sent only for error with code): \ ale#lsp#tsserver_message#Change(bufnr('')), \ [0, 'ts@getCodeFixes', { \ 'startLine': 2, - \ 'startOffset': 5, + \ 'startOffset': 16, \ 'endLine': 2, - \ 'endOffset': 6, + \ 'endOffset': 17, \ 'file': expand('%:p'), \ 'errorCodes': [2304], \ }] @@ -424,43 +422,6 @@ Execute(getEditsForRefactor should print error on failure): AssertEqual ['echom ''Error while getting edits for refactor. Reason: oops'''], g:expr_list -" TODO: I can't figure out how to run ALECodeAction on range -" in test function. Therefore I can't write properly working -" test. If somebody knows how to do that help is appreciated. -" -" Execute(tsserver getApplicableRefactors requests should be sent): -" call ale#linter#Reset() -" -" runtime ale_linters/typescript/tsserver.vim -" let g:ale_buffer_info = {bufnr(''): {'loclist': []}} -" call setpos('.', [bufnr(''), 2, 5, 0]) -" normal "v$" -" -" execute "ALECodeAction" -" -" " We shouldn't register the callback yet. -" AssertEqual '''''', string(g:Callback) -" -" AssertEqual type(function('type')), type(g:InitCallback) -" call g:InitCallback() -" -" AssertEqual 'code_actions', g:capability_checked -" AssertEqual -" \ 'function(''ale#codefix#HandleTSServerResponse'')', -" \ string(g:Callback) -" AssertEqual -" \ [ -" \ ale#lsp#tsserver_message#Change(bufnr('')), -" \ [0, 'ts@getApplicableRefactors', { -" \ 'startLine': 2, -" \ 'startOffset': 5, -" \ 'endLine': 2, -" \ 'endOffset': 15, -" \ 'file': expand('%:p'), -" \ }] -" \ ], -" \ g:message_list - Execute(Failed LSP responses should be handled correctly): call ale#codefix#HandleLSPResponse( \ 1, @@ -545,14 +506,6 @@ Execute(LSP code action requests should be sent): \ string(g:Callback) AssertEqual \ [ - \ [1, 'workspace/didChangeConfiguration', {'settings': {'python': {}}}], - \ [1, 'textDocument/didChange', { - \ 'contentChanges': [{'text': "def main():\n a = 1\n b = a + 2\n"}], - \ 'textDocument': { - \ 'uri': ale#path#ToURI(expand('%:p')), - \ 'version': g:ale_lsp_next_version_id - 1, - \ }, - \ }], \ [0, 'textDocument/codeAction', { \ 'context': { \ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] @@ -561,7 +514,7 @@ Execute(LSP code action requests should be sent): \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} \ }] \ ], - \ g:message_list + \ g:message_list[-1:] Execute(LSP code action requests should be sent only for error with code): call ale#linter#Reset() @@ -585,14 +538,6 @@ Execute(LSP code action requests should be sent only for error with code): \ string(g:Callback) AssertEqual \ [ - \ [1, 'workspace/didChangeConfiguration', {'settings': {'python': {}}}], - \ [1, 'textDocument/didChange', { - \ 'contentChanges': [{'text': "def main():\n a = 1\n b = a + 2\n"}], - \ 'textDocument': { - \ 'uri': ale#path#ToURI(expand('%:p')), - \ 'version': g:ale_lsp_next_version_id - 1, - \ }, - \ }], \ [0, 'textDocument/codeAction', { \ 'context': { \ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] @@ -601,4 +546,4 @@ Execute(LSP code action requests should be sent only for error with code): \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} \ }] \ ], - \ g:message_list + \ g:message_list[-1:] From d0b5909fd8cb96fa65363af44d02eb0038c6112c Mon Sep 17 00:00:00 2001 From: Dalius Dobravolskas Date: Sat, 21 Nov 2020 01:26:16 +0000 Subject: [PATCH 17/30] #3442 Fix code fix clangd issue --- autoload/ale/codefix.vim | 2 +- test/test_codefix.vader | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/autoload/ale/codefix.vim b/autoload/ale/codefix.vim index 3120e7cb..69bf36fa 100644 --- a/autoload/ale/codefix.vim +++ b/autoload/ale/codefix.vim @@ -379,7 +379,7 @@ function! s:OnReady( \ }, \ 'end': { \ 'line': l:nearest_error.end_lnum - 1, - \ 'character': l:nearest_error.end_col - 1, + \ 'character': l:nearest_error.end_col, \ }, \ }, \ }, diff --git a/test/test_codefix.vader b/test/test_codefix.vader index deb97256..fc5470aa 100644 --- a/test/test_codefix.vader +++ b/test/test_codefix.vader @@ -508,7 +508,7 @@ Execute(LSP code action requests should be sent): \ [ \ [0, 'textDocument/codeAction', { \ 'context': { - \ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] + \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] \ }, \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} @@ -540,7 +540,7 @@ Execute(LSP code action requests should be sent only for error with code): \ [ \ [0, 'textDocument/codeAction', { \ 'context': { - \ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] + \ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}] \ }, \ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, \ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))} From e5d16caebe8a57074ee52d9bf0c0334fba4106a9 Mon Sep 17 00:00:00 2001 From: zandr <7629614+deathlyfrantic@users.noreply.github.com> Date: Sat, 21 Nov 2020 10:59:50 -0500 Subject: [PATCH 18/30] Add luafmt fixer (#3289) --- autoload/ale/fix/registry.vim | 5 +++ autoload/ale/fixers/luafmt.vim | 13 ++++++++ doc/ale-lua.txt | 16 +++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/fixers/test_luafmt_fixer_callback.vader | 35 ++++++++++++++++++++ test/lua_files/testfile.lua | 0 8 files changed, 72 insertions(+) create mode 100644 autoload/ale/fixers/luafmt.vim create mode 100644 test/fixers/test_luafmt_fixer_callback.vader create mode 100644 test/lua_files/testfile.lua diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index d71668f2..9ea5331b 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -375,6 +375,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['html', 'htmldjango'], \ 'description': 'Fix HTML files with html-beautify.', \ }, +\ 'luafmt': { +\ 'function': 'ale#fixers#luafmt#Fix', +\ 'suggested_filetypes': ['lua'], +\ 'description': 'Fix Lua files with luafmt.', +\ }, \ 'dhall': { \ 'function': 'ale#fixers#dhall#Fix', \ 'suggested_filetypes': ['dhall'], diff --git a/autoload/ale/fixers/luafmt.vim b/autoload/ale/fixers/luafmt.vim new file mode 100644 index 00000000..6cb9ef4a --- /dev/null +++ b/autoload/ale/fixers/luafmt.vim @@ -0,0 +1,13 @@ +call ale#Set('lua_luafmt_executable', 'luafmt') +call ale#Set('lua_luafmt_options', '') + +function! ale#fixers#luafmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'lua_luafmt_executable') + let l:options = ale#Var(a:buffer, 'lua_luafmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . (empty(l:options) ? '' : ' ' . l:options) + \ . ' --stdin', + \} +endfunction diff --git a/doc/ale-lua.txt b/doc/ale-lua.txt index f1286f89..408f0c3c 100644 --- a/doc/ale-lua.txt +++ b/doc/ale-lua.txt @@ -30,5 +30,21 @@ g:ale_lua_luacheck_options *g:ale_lua_luacheck_options* This variable can be set to pass additional options to luacheck. +=============================================================================== +luafmt *ale-lua-luafmt* + +g:ale_lua_luafmt_executable *g:ale_lua_luafmt_executable* + *b:ale_lua_luafmt_executable* + Type: |String| + Default: `'luafmt'` + + This variable can be set to use a different executable for luafmt. + +g:ale_lua_luafmt_options *g:ale_lua_luafmt_options* + *b:ale_lua_luafmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the luafmt 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 ba96c37c..b95c3a9b 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -266,6 +266,7 @@ Notes: * Lua * `luac` * `luacheck` + * `luafmt` * Mail * `alex`!! * `languagetool`!! diff --git a/doc/ale.txt b/doc/ale.txt index fdd95ace..1a2938ee 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2744,6 +2744,7 @@ documented in additional help files. lua.....................................|ale-lua-options| luac..................................|ale-lua-luac| luacheck..............................|ale-lua-luacheck| + luafmt................................|ale-lua-luafmt| markdown................................|ale-markdown-options| markdownlint..........................|ale-markdown-markdownlint| mdl...................................|ale-markdown-mdl| diff --git a/supported-tools.md b/supported-tools.md index cf7ee25a..4567bf10 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -275,6 +275,7 @@ formatting. * Lua * [luac](https://www.lua.org/manual/5.1/luac.html) * [luacheck](https://github.com/mpeterv/luacheck) + * [luafmt](https://github.com/trixnz/lua-fmt) * Mail * [alex](https://github.com/wooorm/alex) :floppy_disk: * [languagetool](https://languagetool.org/) :floppy_disk: diff --git a/test/fixers/test_luafmt_fixer_callback.vader b/test/fixers/test_luafmt_fixer_callback.vader new file mode 100644 index 00000000..362da118 --- /dev/null +++ b/test/fixers/test_luafmt_fixer_callback.vader @@ -0,0 +1,35 @@ +Before: + Save g:ale_lua_luafmt_executable + Save g:ale_lua_luafmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_lua_luafmt_executable = 'xxxinvalid' + let g:ale_lua_luafmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The luafmt callback should return the correct default values): + call ale#test#SetFilename('../lua_files/testfile.lua') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') . ' --stdin', + \ }, + \ ale#fixers#luafmt#Fix(bufnr('')) + +Execute(The luafmt callback should include custom luafmt options): + let g:ale_lua_luafmt_options = "--skip-children" + call ale#test#SetFilename('../lua_files/testfile.lua') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' ' . g:ale_lua_luafmt_options + \ . ' --stdin', + \ }, + \ ale#fixers#luafmt#Fix(bufnr('')) diff --git a/test/lua_files/testfile.lua b/test/lua_files/testfile.lua new file mode 100644 index 00000000..e69de29b From 342e5af4e367e225eb1c96cb0ecb4a211c2a4f8e Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 16:19:56 +0000 Subject: [PATCH 19/30] Add a missing blank line in documentation --- doc/ale.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ale.txt b/doc/ale.txt index 95634171..71caf0e5 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1694,6 +1694,7 @@ g:ale_lsp_root *g:ale_lsp_root* If neither variable yields a result, a linter-specific function is invoked to detect a project root. If this, too, yields no result, the linter is disabled. + g:ale_max_buffer_history_size *g:ale_max_buffer_history_size* Type: |Number| From 06e7f2195ef6375be32a63f98b5e46070708a315 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 19:00:53 +0000 Subject: [PATCH 20/30] Fix #3332 - Modify everything for rename/actions ALE now just modifies every open buffer for rename and actions, and sets up a one-time use BufEnter event to reload buffers that are changed so you don't have to think about what to do with changed buffers. --- autoload/ale/code_action.vim | 42 ++++++++++++++++++++++-------------- autoload/ale/rename.vim | 5 +---- doc/ale.txt | 6 ------ plugin/ale.vim | 2 +- test/test_rename.vader | 6 +++--- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index 6b808b34..69d40933 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -1,28 +1,24 @@ " Author: Jerko Steiner " Description: Code action support for LSP / tsserver +function! ale#code_action#ReloadBuffer() abort + let l:buffer = bufnr('') + + execute 'augroup ALECodeActionReloadGroup' . l:buffer + autocmd! + augroup END + + silent! execute 'augroup! ALECodeActionReloadGroup' . l:buffer + + call ale#util#Execute(':e!') +endfunction + function! ale#code_action#HandleCodeAction(code_action, options) abort let l:current_buffer = bufnr('') let l:changes = a:code_action.changes let l:should_save = get(a:options, 'should_save') - let l:force_save = get(a:options, 'force_save') - let l:safe_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') - if !l:force_save - call ale#util#Execute('echom ''Aborting action, file is unsaved''') - - return - endif - else - call add(l:safe_changes, l:file_code_edit) - endif - endfor - - for l:file_code_edit in l:safe_changes call ale#code_action#ApplyChanges( \ l:file_code_edit.fileName, \ l:file_code_edit.textChanges, @@ -161,6 +157,20 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort call setpos('.', [0, l:pos[0], l:pos[1], 0]) endif + + if a:should_save && l:buffer > 0 && !l:is_current_buffer + " Set up a one-time use event that will delete itself to reload the + " buffer next time it's entered to view the changes made to it. + execute 'augroup ALECodeActionReloadGroup' . l:buffer + autocmd! + + execute printf( + \ 'autocmd BufEnter ' + \ . ' call ale#code_action#ReloadBuffer()', + \ l:buffer + \) + augroup END + endif endfunction function! s:UpdateCursor(cursor, start, end, offset) abort diff --git a/autoload/ale/rename.vim b/autoload/ale/rename.vim index 0d074c24..9030618e 100644 --- a/autoload/ale/rename.vim +++ b/autoload/ale/rename.vim @@ -85,7 +85,6 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort \ }, \ { \ 'should_save': 1, - \ 'force_save': get(l:options, 'force_save'), \ }, \) endfunction @@ -118,7 +117,6 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort \ }, \ { \ 'should_save': 1, - \ 'force_save': get(l:options, 'force_save'), \ }, \) endif @@ -177,7 +175,7 @@ function! s:ExecuteRename(linter, options) abort call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback) endfunction -function! ale#rename#Execute(options) abort +function! ale#rename#Execute() abort let l:lsp_linters = [] for l:linter in ale#linter#Get(&filetype) @@ -205,7 +203,6 @@ function! ale#rename#Execute(options) abort call s:ExecuteRename(l:lsp_linter, { \ 'old_name': l:old_name, \ 'new_name': l:new_name, - \ 'force_save': get(a:options, 'force_save') is 1, \}) endfor endfunction diff --git a/doc/ale.txt b/doc/ale.txt index 71caf0e5..a64f86f0 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3145,12 +3145,6 @@ ALERename *ALERename* The symbol where the cursor is resting will be the symbol renamed, and a prompt will open to request a new name. - ALE will refuse to complete a rename operation if there are files to modify - which have not yet been saved in Vim. If the command is run with a bang - (`:ALERename!`), all warnings will be suppressed, and files that are still - open in Vim and not saved will be ignored and left in a state where symbols - in those files will not be updated. - ALECodeAction *ALECodeAction* diff --git a/plugin/ale.vim b/plugin/ale.vim index 2398956e..1fde9df1 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -245,7 +245,7 @@ command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual') command! -bar ALEImport :call ale#completion#Import() " Rename symbols using tsserver and LSP -command! -bar -bang ALERename :call ale#rename#Execute({'force_save': '' is# '!'}) +command! -bar -bang ALERename :call ale#rename#Execute() " Apply code actions to a range. command! -bar -range ALECodeAction :call ale#codefix#Execute() diff --git a/test/test_rename.vader b/test/test_rename.vader index 2e8b746e..5bc655f4 100644 --- a/test/test_rename.vader +++ b/test/test_rename.vader @@ -269,7 +269,7 @@ Execute(tsserver rename requests should be sent): \ }] \ ], \ g:message_list - AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name', 'force_save': 0}}, + AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name'}}, \ ale#rename#GetMap() Given python(Some Python file): @@ -470,7 +470,7 @@ Execute(LSP rename requests should be sent): let b:ale_linters = ['pyls'] call setpos('.', [bufnr(''), 1, 5, 0]) - ALERename! + ALERename " We shouldn't register the callback yet. AssertEqual '''''', string(g:Callback) @@ -500,5 +500,5 @@ Execute(LSP rename requests should be sent): \ ], \ g:message_list - AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name', 'force_save': 1}}, + AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name'}}, \ ale#rename#GetMap() From a139599d3938b2f4fa5c8a97d3280b5f4f859321 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 20:12:09 +0000 Subject: [PATCH 21/30] Close #2727 - Add a hover-only setting for balloons --- autoload/ale/balloon.vim | 32 ++++++++++++++++++++++++-------- autoload/ale/hover.vim | 8 ++++++-- doc/ale.txt | 9 ++++++++- plugin/ale.vim | 2 +- test/test_hover.vader | 4 ++-- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/autoload/ale/balloon.vim b/autoload/ale/balloon.vim index 72f6b91c..8678376f 100644 --- a/autoload/ale/balloon.vim +++ b/autoload/ale/balloon.vim @@ -2,23 +2,39 @@ " Description: balloonexpr support for ALE. function! ale#balloon#MessageForPos(bufnr, lnum, col) abort + let l:set_balloons = ale#Var(a:bufnr, 'set_balloons') + let l:show_problems = 0 + let l:show_hover = 0 + + if l:set_balloons is 1 + let l:show_problems = 1 + let l:show_hover = 1 + elseif l:set_balloons is# 'hover' + let l:show_hover = 1 + endif + " Don't show balloons if they are disabled, or linting is disabled. - if !ale#Var(a:bufnr, 'set_balloons') + if !(l:show_problems || l:show_hover) \|| !g:ale_enabled \|| !getbufvar(a:bufnr, 'ale_enabled', 1) return '' endif - let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist - let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col) + if l:show_problems + let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist + let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col) + endif " Show the diagnostics message if found, 'Hover' output otherwise - if l:index >= 0 + if l:show_problems && l:index >= 0 return l:loclist[l:index].text - elseif exists('*balloon_show') || getbufvar( - \ a:bufnr, - \ 'ale_set_balloons_legacy_echo', - \ get(g:, 'ale_set_balloons_legacy_echo', 0) + elseif l:show_hover && ( + \ exists('*balloon_show') + \ || getbufvar( + \ a:bufnr, + \ 'ale_set_balloons_legacy_echo', + \ get(g:, 'ale_set_balloons_legacy_echo', 0) + \ ) \) " Request LSP/tsserver hover information, but only if this version of " Vim supports the balloon_show function, or if we turned a legacy diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim index 38b4b866..1d38f3b9 100644 --- a/autoload/ale/hover.vim +++ b/autoload/ale/hover.vim @@ -24,6 +24,8 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort if get(a:response, 'success', v:false) is v:true \&& get(a:response, 'body', v:null) isnot v:null + let l:set_balloons = ale#Var(l:options.buffer, 'set_balloons') + " If we pass the show_documentation flag, we should show the full " documentation, and always in the preview window. if get(l:options, 'show_documentation', 0) @@ -40,7 +42,7 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort endif elseif get(l:options, 'hover_from_balloonexpr', 0) \&& exists('*balloon_show') - \&& ale#Var(l:options.buffer, 'set_balloons') + \&& (l:set_balloons is 1 || l:set_balloons is# 'hover') call balloon_show(a:response.body.displayString) elseif get(l:options, 'truncated_echo', 0) call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0]) @@ -216,9 +218,11 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort let [l:commands, l:lines] = ale#hover#ParseLSPResult(l:result.contents) if !empty(l:lines) + let l:set_balloons = ale#Var(l:options.buffer, 'set_balloons') + if get(l:options, 'hover_from_balloonexpr', 0) \&& exists('*balloon_show') - \&& ale#Var(l:options.buffer, 'set_balloons') + \&& (l:set_balloons is 1 || l:set_balloons is# 'hover') call balloon_show(join(l:lines, "\n")) elseif get(l:options, 'truncated_echo', 0) call ale#cursor#TruncatedEcho(l:lines[0]) diff --git a/doc/ale.txt b/doc/ale.txt index a64f86f0..195e6f13 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1840,7 +1840,7 @@ g:ale_rename_tsserver_find_in_strings *g:ale_rename_tsserver_find_in_strings* g:ale_set_balloons *g:ale_set_balloons* *b:ale_set_balloons* - Type: |Number| + Type: |Number| or |String| Default: `has('balloon_eval') && has('gui_running')` When this option is set to `1`, balloon messages will be displayed for @@ -1851,6 +1851,13 @@ g:ale_set_balloons *g:ale_set_balloons* supporting "Hover" information, per |ale-hover|, then brief information about the symbol under the cursor will be displayed in a balloon. + This option can be set to `'hover'` to only enable balloons for hover + message, so diagnostics are never shown in balloons. You may wish to + configure use this setting only in GUI Vim like so: > + + let g:ale_set_balloons = has('gui_running') ? 'hover' : 0 +< + Balloons can be enabled for terminal versions of Vim that support balloons, but some versions of Vim will produce strange mouse behavior when balloons are enabled. To configure balloons for your terminal, you should first diff --git a/plugin/ale.vim b/plugin/ale.vim index 1fde9df1..b1abb10a 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -161,7 +161,7 @@ let g:ale_go_go111module = get(g:, 'ale_go_go111module', '') " If 1, enable a popup menu for commands. let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui')) -if g:ale_set_balloons +if g:ale_set_balloons is 1 || g:ale_set_balloons is# 'hover' call ale#balloon#Enable() endif diff --git a/test/test_hover.vader b/test/test_hover.vader index 9689cda2..ed756396 100644 --- a/test/test_hover.vader +++ b/test/test_hover.vader @@ -101,7 +101,7 @@ Execute(tsserver quickinfo responses will null missing bodies should be handled) AssertEqual {}, ale#hover#GetMap() Execute(tsserver quickinfo displayString values should be displayed): - call ale#hover#SetMap({3: {}}) + call ale#hover#SetMap({3: {'buffer': bufnr('')}}) call ale#hover#HandleTSServerResponse( \ 1, \ { @@ -169,7 +169,7 @@ Execute(LSP hover response with lists of strings and marked strings should be ha AssertEqual {}, ale#hover#GetMap() Execute(tsserver responses for documentation requests should be handled): - call ale#hover#SetMap({3: {'show_documentation': 1}}) + call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}}) call ale#hover#HandleTSServerResponse( \ 1, From 2e91f0e689362e33ae3172ddf8153c7a7631cdcf Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 20:16:32 +0000 Subject: [PATCH 22/30] Use has('gui_running') instead of has('gui') --- doc/ale.txt | 2 +- plugin/ale.vim | 2 +- test/test_lint_on_enter_when_file_changed.vader | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ale.txt b/doc/ale.txt index 195e6f13..e00d5f15 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1806,7 +1806,7 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* g:ale_popup_menu_enabled *g:ale_popup_menu_enabled* Type: |Number| - Default: `has('gui')` + Default: `has('gui_running')` When this option is set to `1`, ALE will show code actions and rename capabilities in the right click mouse menu when there's a LSP server or diff --git a/plugin/ale.vim b/plugin/ale.vim index b1abb10a..5b7be116 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -159,7 +159,7 @@ let g:ale_python_auto_pipenv = get(g:, 'ale_python_auto_pipenv', 0) let g:ale_go_go111module = get(g:, 'ale_go_go111module', '') " If 1, enable a popup menu for commands. -let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui')) +let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui_running')) if g:ale_set_balloons is 1 || g:ale_set_balloons is# 'hover' call ale#balloon#Enable() diff --git a/test/test_lint_on_enter_when_file_changed.vader b/test/test_lint_on_enter_when_file_changed.vader index 88493005..0d4c4af8 100644 --- a/test/test_lint_on_enter_when_file_changed.vader +++ b/test/test_lint_on_enter_when_file_changed.vader @@ -35,7 +35,7 @@ After: Execute(The file changed event function should set b:ale_file_changed): let g:ale_lint_on_enter = 0 - if has('gui') + if has('gui_running') new else e test From 2873be2d6a5e383edd1fead396a53a8a38eebd76 Mon Sep 17 00:00:00 2001 From: Dale Jung Date: Sat, 21 Nov 2020 15:19:02 -0500 Subject: [PATCH 23/30] Add php phpcbf options (#3383) * Taken from phpcs. add add_php_phpcbf_options #3382 * Updated docs for php_phpcbf_options #3382 * Added tests #3382 --- autoload/ale/fixers/phpcbf.vim | 3 ++- doc/ale-php.txt | 8 ++++++++ test/fixers/test_phpcbf_fixer_callback.vader | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/autoload/ale/fixers/phpcbf.vim b/autoload/ale/fixers/phpcbf.vim index f14b8406..0a61c657 100644 --- a/autoload/ale/fixers/phpcbf.vim +++ b/autoload/ale/fixers/phpcbf.vim @@ -2,6 +2,7 @@ " Description: Fixing files with phpcbf. call ale#Set('php_phpcbf_standard', '') +call ale#Set('php_phpcbf_options', '') call ale#Set('php_phpcbf_executable', 'phpcbf') call ale#Set('php_phpcbf_use_global', get(g:, 'ale_use_global_executables', 0)) @@ -20,6 +21,6 @@ function! ale#fixers#phpcbf#Fix(buffer) abort \ : '' return { - \ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ' -' + \ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ale#Pad(ale#Var(a:buffer, 'php_phpcbf_options')) . ' -' \} endfunction diff --git a/doc/ale-php.txt b/doc/ale-php.txt index 79d9a033..6da21046 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -85,6 +85,14 @@ g:ale_php_phpcbf_use_global *g:ale_php_phpcbf_use_global* See |ale-integrations-local-executables| +g:ale_php_phpcbf_options *g:ale_php_phpcbf_options* + *b:ale_php_phpcbf_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to php-cbf + + =============================================================================== phpcs *ale-php-phpcs* diff --git a/test/fixers/test_phpcbf_fixer_callback.vader b/test/fixers/test_phpcbf_fixer_callback.vader index 1663c89c..f7bcc2d8 100644 --- a/test/fixers/test_phpcbf_fixer_callback.vader +++ b/test/fixers/test_phpcbf_fixer_callback.vader @@ -5,6 +5,7 @@ Before: let g:ale_php_phpcbf_executable = 'phpcbf_test' let g:ale_php_phpcbf_standard = '' + let g:ale_php_phpcbf_options = '' let g:ale_php_phpcbf_use_global = 0 call ale#test#SetDirectory('/testplugin/test/fixers') @@ -54,6 +55,15 @@ Execute(The phpcbf callback should include the phpcbf_standard option): \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'}, \ ale#fixers#phpcbf#Fix(bufnr('')) +Execute(User provided options should be used): + let g:ale_php_phpcbf_options = '--my-user-provided-option my-value' + call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php') + + AssertEqual + \ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . ale#Pad('--my-user-provided-option my-value') . ' -'}, + \ ale#fixers#phpcbf#Fix(bufnr('')) + + Before: Save g:ale_php_phpcbf_executable Save g:ale_php_phpcbf_standard @@ -61,6 +71,7 @@ Before: let g:ale_php_phpcbf_executable = 'phpcbf_test' let g:ale_php_phpcbf_standard = '' + let g:ale_php_phpcbf_options = '' let g:ale_php_phpcbf_use_global = 0 call ale#test#SetDirectory('/testplugin/test/fixers') From e1184e31f6ed7907b0fdc51eaf4507af6f22d7d9 Mon Sep 17 00:00:00 2001 From: Jose Soto Date: Sat, 21 Nov 2020 12:29:33 -0800 Subject: [PATCH 24/30] Adds support for Tlint - A Tighten Opinionated PHP Linter (#3291) Co-authored-by: w0rp --- ale_linters/php/tlint.vim | 80 +++++++++++++++++++++++ doc/ale-php.txt | 30 +++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/handler/test_tlint_handler.vader | 34 ++++++++++ 6 files changed, 147 insertions(+) create mode 100644 ale_linters/php/tlint.vim create mode 100644 test/handler/test_tlint_handler.vader diff --git a/ale_linters/php/tlint.vim b/ale_linters/php/tlint.vim new file mode 100644 index 00000000..6bba8def --- /dev/null +++ b/ale_linters/php/tlint.vim @@ -0,0 +1,80 @@ +" Author: Jose Soto +" +" Description: Tighten Opinionated PHP Linting +" Website: https://github.com/tightenco/tlint + +call ale#Set('php_tlint_executable', 'tlint') +call ale#Set('php_tlint_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('php_tlint_options', '') + +function! ale_linters#php#tlint#GetProjectRoot(buffer) abort + let l:composer_path = ale#path#FindNearestFile(a:buffer, 'composer.json') + + if !empty(l:composer_path) + return fnamemodify(l:composer_path, ':h') + endif + + let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git') + + return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : '' +endfunction + +function! ale_linters#php#tlint#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'php_tlint', [ + \ 'vendor/bin/tlint', + \ 'tlint', + \]) +endfunction + +function! ale_linters#php#tlint#GetCommand(buffer) abort + let l:executable = ale_linters#php#tlint#GetExecutable(a:buffer) + let l:options = ale#Var(a:buffer, 'php_tlint_options') + + return ale#node#Executable(a:buffer, l:executable) + \ . (!empty(l:options) ? ' ' . l:options : '') + \ . ' lint %s' +endfunction + +function! ale_linters#php#tlint#Handle(buffer, lines) abort + " Matches against lines like the following: + " + " ! There should be 1 space around `.` concatenations, and additional lines should always start with a `.` + " 22 : ` $something = 'a'.'name';` + " + let l:loop_count = 0 + let l:messages_pattern = '^\! \(.*\)' + let l:output = [] + let l:pattern = '^\(\d\+\) \:' + let l:temp_messages = [] + + for l:message in ale#util#GetMatches(a:lines, l:messages_pattern) + call add(l:temp_messages, l:message) + endfor + + let l:loop_count = 0 + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:num = l:match[1] + let l:text = l:temp_messages[l:loop_count] + + call add(l:output, { + \ 'lnum': l:num, + \ 'col': 0, + \ 'text': l:text, + \ 'type': 'W', + \ 'sub_type': 'style', + \}) + + let l:loop_count += 1 + endfor + + return l:output +endfunction + +call ale#linter#Define('php', { +\ 'name': 'tlint', +\ 'executable': function('ale_linters#php#tlint#GetExecutable'), +\ 'command': function('ale_linters#php#tlint#GetCommand'), +\ 'callback': 'ale_linters#php#tlint#Handle', +\ 'project_root': function('ale_linters#php#tlint#GetProjectRoot'), +\}) diff --git a/doc/ale-php.txt b/doc/ale-php.txt index 6da21046..4ee016fb 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -251,6 +251,34 @@ g:ale_php_php_executable *g:ale_php_php_executable* This variable sets the executable used for php. + +=============================================================================== +tlint *ale-php-tlint* + +g:ale_php_tlint_executable *g:ale_php_tlint_executable* + *b:ale_php_tlint_executable* + Type: |String| + Default: `'tlint'` + + See |ale-integrations-local-executables| + + +g:ale_php_tlint_use_global *g:ale_php_tlint_use_global* + *b:ale_php_tlint_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + + +g:ale_php_tlint_options *g:ale_php_tlint_options* + *b:ale_php_tlint_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to tlint + + =============================================================================== intelephense *ale-php-intelephense* @@ -277,6 +305,7 @@ g:ale_php_intelephense_use_global *g:ale_php_intelephense_use_global* See: |ale-integrations-local-executables| + g:ale_php_intelephense_config *g:ale_php_intelephense_config* *b:ale_php_intelephense_config* Type: |Dictionary| @@ -286,5 +315,6 @@ g:ale_php_intelephense_config *g:ale_php_intelephense_config* installation docs provided by intelephense (github.com/bmewburn/intelephense -docs). + =============================================================================== 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 60b53c03..0ca82254 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -337,6 +337,7 @@ Notes: * `phpmd` * `phpstan` * `psalm`!! + * `tlint` * PO * `alex`!! * `msgfmt` diff --git a/doc/ale.txt b/doc/ale.txt index c5de4333..208bda4a 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2805,6 +2805,7 @@ documented in additional help files. psalm.................................|ale-php-psalm| php-cs-fixer..........................|ale-php-php-cs-fixer| php...................................|ale-php-php| + tlint.................................|ale-php-tlint| intelephense..........................|ale-php-intelephense| po......................................|ale-po-options| write-good............................|ale-po-write-good| diff --git a/supported-tools.md b/supported-tools.md index ec49ad19..6f467703 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -346,6 +346,7 @@ formatting. * [phpmd](https://phpmd.org) * [phpstan](https://github.com/phpstan/phpstan) * [psalm](https://getpsalm.org) :floppy_disk: + * [tlint](https://github.com/tightenco/tlint) * PO * [alex](https://github.com/wooorm/alex) :floppy_disk: * [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html) diff --git a/test/handler/test_tlint_handler.vader b/test/handler/test_tlint_handler.vader new file mode 100644 index 00000000..e146346c --- /dev/null +++ b/test/handler/test_tlint_handler.vader @@ -0,0 +1,34 @@ +Before: + runtime ale_linters/php/tlint.vim + +After: + call ale#linter#Reset() + +Execute(The tlint handler should calculate line numbers): + AssertEqual + \ [ + \ { + \ 'lnum': '5', + \ 'col': 0, + \ 'sub_type': + \ 'style', + \ 'type': 'W', + \ 'text': ['! There should be no unused imports.', 'There should be no unused imports.', '', '', '', '', '', '', '', ''] + \ }, + \ { + \ 'lnum': '15', + \ 'col': 0, + \ 'sub_type': + \ 'style', + \ 'type': 'W', + \ 'text': ['! There should be no method visibility in test methods.', 'There should be no method visibility in test methods.', '', '', '', '', '', '', '', ''] + \ }, + \ ], + \ ale_linters#php#tlint#Handle(347, [ + \ "Lints for /Users/jose/Code/Tighten/tester/tests/Unit/ExampleTest.php", + \ "============", + \ "! There should be no unused imports.", + \ "5 : `use Illuminate\Foundation\Testing\RefreshDatabase;`", + \ "! There should be no method visibility in test methods.", + \ "15 : ` public function testBasicTest()`", + \ ]) From b09ccc12c3001b4d551d35cb0c4195793d9b6d10 Mon Sep 17 00:00:00 2001 From: Gabriel Petrovay Date: Sat, 21 Nov 2020 21:32:58 +0100 Subject: [PATCH 25/30] Added the Vundle command in installation instructions (#3400) --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 69ff33ae..438af9b6 100644 --- a/README.md +++ b/README.md @@ -341,12 +341,14 @@ git clone https://github.com/dense-analysis/ale.git ### 3.iii. Installation with Vundle You can install this plugin using [Vundle](https://github.com/VundleVim/Vundle.vim) -by using the path on GitHub for this repository. +by adding the GitHub path for this repository to your `~/.vimrc`: ```vim Plugin 'dense-analysis/ale' ``` +Then run the command `:PluginInstall` in Vim. + See the Vundle documentation for more information. @@ -354,13 +356,16 @@ See the Vundle documentation for more information. ### 3.iiii. Installation with Vim-Plug You can install this plugin using [Vim-Plug](https://github.com/junegunn/vim-plug) -by adding the GitHub path for this repository to your `~/.vimrc` -and running `:PlugInstall`. +by adding the GitHub path for this repository to your `~/.vimrc`: ```vim Plug 'dense-analysis/ale' ``` +Then run the command `:PlugInstall` in Vim. + +See the Vim-Plug documentation for more information. + ## 4. Contributing From 5458a1b29167b1fefde5fb6545e371a9d77ca36e Mon Sep 17 00:00:00 2001 From: Horacio Sanson Date: Sun, 22 Nov 2020 05:49:31 +0900 Subject: [PATCH 26/30] Fix 3103 - add shellcheck shell directive detection. (#3216) * Fix 3103 - add shellcheck shell directive detection. Searches for shellcheck shell directive to detect dialects for scripts that do not have shebang. * Change order of detection of shellcheck dialect In a situation where the filetype can be wrong (example: something.sh which is written in bash dialect) and has no hash-bang (since it is meant to be sourced) then the override specified within the script will be ignored. It probably is the most right thing to do if the script author has added a specific directive; it should trump everything else. Co-authored-by: Horacio Sanson Co-authored-by: Dino Korah --- autoload/ale/handlers/shellcheck.vim | 26 ++++++++++++++- test/test_shell_detection.vader | 48 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/autoload/ale/handlers/shellcheck.vim b/autoload/ale/handlers/shellcheck.vim index 351d6d3f..701c43b2 100644 --- a/autoload/ale/handlers/shellcheck.vim +++ b/autoload/ale/handlers/shellcheck.vim @@ -1,8 +1,32 @@ " Author: w0rp " Description: This file adds support for using the shellcheck linter +" Shellcheck supports shell directives to define the shell dialect for scripts +" that do not have a shebang for some reason. +" https://github.com/koalaman/shellcheck/wiki/Directive#shell +function! ale#handlers#shellcheck#GetShellcheckDialectDirective(buffer) abort + let l:linenr = 0 + let l:pattern = '\s\{-}#\s\{-}shellcheck\s\{-}shell=\(.*\)' + let l:possible_shell = ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh'] + + while l:linenr < min([50, line('$')]) + let l:linenr += 1 + let l:match = matchlist(getline(l:linenr), l:pattern) + + if len(l:match) > 1 && index(l:possible_shell, l:match[1]) >= 0 + return l:match[1] + endif + endwhile + + return '' +endfunction + function! ale#handlers#shellcheck#GetDialectArgument(buffer) abort - let l:shell_type = ale#handlers#sh#GetShellType(a:buffer) + let l:shell_type = ale#handlers#shellcheck#GetShellcheckDialectDirective(a:buffer) + + if empty(l:shell_type) + let l:shell_type = ale#handlers#sh#GetShellType(a:buffer) + endif if !empty(l:shell_type) " Use the dash dialect for /bin/ash, etc. diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader index 697054d0..11d801c3 100644 --- a/test/test_shell_detection.vader +++ b/test/test_shell_detection.vader @@ -127,3 +127,51 @@ Execute(The dash dialect should be used for the shell and the base function): Execute(dash should be used for shellcheck): AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a Bash shellcheck shell directive): + # shellcheck shell=bash + +Execute(bash dialect should be detected appropriately): + AssertEqual 'bash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a sh shellcheck shell directive): + #shellcheck shell=sh + +Execute(sh dialect should be detected appropriately): + AssertEqual 'sh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a tcsh shellcheck shell directive): + # shellcheck shell=tcsh + +Execute(tcsh dialect should be detected appropriately): + AssertEqual 'tcsh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a zsh shellcheck shell directive): + # shellcheck shell=zsh + +Execute(zsh dialect should be detected appropriately): + AssertEqual 'zsh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a csh shellcheck shell directive): + # shellcheck shell=csh + +Execute(zsh dialect should be detected appropriately): + AssertEqual 'csh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a ksh shellcheck shell directive): + # shellcheck shell=ksh + +Execute(ksh dialect should be detected appropriately): + AssertEqual 'ksh', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a dash shellcheck shell directive): + # shellcheck shell=dash + +Execute(dash dialect should be detected appropriately): + AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) + +Given(A file with a ash shellcheck shell directive): + # shellcheck shell=ash + +Execute(dash dialect should be detected for ash that shellcheck does not support): + AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr('')) From 9692c0c64c83bb8f67962cf35e7dc3f1757aa8cc Mon Sep 17 00:00:00 2001 From: Eric Zhao <21zhaoe@protonmail.com> Date: Sat, 21 Nov 2020 15:56:38 -0500 Subject: [PATCH 27/30] Add support for R languageserver (#3370) --- ale_linters/r/languageserver.vim | 26 +++++++++++++++++++ doc/ale-r.txt | 23 ++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/command_callback/r_paths/.Rprofile | 0 .../test_r_languageserver_callbacks.vader | 22 ++++++++++++++++ 7 files changed, 74 insertions(+) create mode 100644 ale_linters/r/languageserver.vim create mode 100644 test/command_callback/r_paths/.Rprofile create mode 100644 test/command_callback/test_r_languageserver_callbacks.vader diff --git a/ale_linters/r/languageserver.vim b/ale_linters/r/languageserver.vim new file mode 100644 index 00000000..c625d8b4 --- /dev/null +++ b/ale_linters/r/languageserver.vim @@ -0,0 +1,26 @@ +" Author: Eric Zhao <21zhaoe@protonmail.com> +" Description: Implementation of the Language Server Protocol for R. + +call ale#Set('r_languageserver_cmd', 'languageserver::run()') +call ale#Set('r_languageserver_options', {}) + +function! ale_linters#r#languageserver#GetCommand(buffer) abort + let l:cmd_string = ale#Var(a:buffer, 'r_languageserver_cmd') + + return 'Rscript --vanilla -e ' . ale#Escape(l:cmd_string) +endfunction + +function! ale_linters#r#languageserver#GetProjectRoot(buffer) abort + let l:project_root = ale#path#FindNearestFile(a:buffer, '.Rprofile') + + return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : fnamemodify(a:buffer, ':h') +endfunction + +call ale#linter#Define('r', { +\ 'name': 'languageserver', +\ 'lsp': 'stdio', +\ 'lsp_config': {b -> ale#Var(b, 'r_languageserver_options')}, +\ 'executable': 'Rscript', +\ 'command': function('ale_linters#r#languageserver#GetCommand'), +\ 'project_root': function('ale_linters#r#languageserver#GetProjectRoot') +\}) diff --git a/doc/ale-r.txt b/doc/ale-r.txt index b5ccebe5..2078b218 100644 --- a/doc/ale-r.txt +++ b/doc/ale-r.txt @@ -2,6 +2,29 @@ ALE R Integration *ale-r-options* +=============================================================================== +languageserver *ale-r-languageserver* + +g:ale_r_languageserver_cmd *g:ale_r_languageserver_cmd* + *b:ale_r_languageserver_cmd* + Type: |String| + Default: `'languageserver::run()'` + + This option can be configured to change the execution command for + languageserver. + + See the languageserver documentation for more options. + + +g:ale_r_languageserver_options *g:ale_r_languageserver_options* + *b:ale_r_languageserver_options* + Type: |Dictionary| + Default: `{}` + + This option can be configured to change settings for languageserver. See the + languageserver documentation for more information. + + =============================================================================== lintr *ale-r-lintr* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index cc514649..f4673ec0 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -389,6 +389,7 @@ Notes: * `qmlfmt` * `qmllint` * R + * `languageserver` * `lintr` * `styler` * Racket diff --git a/doc/ale.txt b/doc/ale.txt index e8c681f9..00f43aa5 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2854,6 +2854,7 @@ documented in additional help files. qml.....................................|ale-qml-options| qmlfmt................................|ale-qml-qmlfmt| r.......................................|ale-r-options| + languageserver........................|ale-r-languageserver| lintr.................................|ale-r-lintr| styler................................|ale-r-styler| reasonml................................|ale-reasonml-options| diff --git a/supported-tools.md b/supported-tools.md index 70c99a8c..e1ac8873 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -398,6 +398,7 @@ formatting. * [qmlfmt](https://github.com/jesperhh/qmlfmt) * [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint) * R + * [languageserver](https://github.com/REditorSupport/languageserver) * [lintr](https://github.com/jimhester/lintr) * [styler](https://github.com/r-lib/styler) * Racket diff --git a/test/command_callback/r_paths/.Rprofile b/test/command_callback/r_paths/.Rprofile new file mode 100644 index 00000000..e69de29b diff --git a/test/command_callback/test_r_languageserver_callbacks.vader b/test/command_callback/test_r_languageserver_callbacks.vader new file mode 100644 index 00000000..7829165c --- /dev/null +++ b/test/command_callback/test_r_languageserver_callbacks.vader @@ -0,0 +1,22 @@ +Before: + call ale#assert#SetUpLinterTest('r', 'languageserver') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'Rscript', 'Rscript --vanilla -e ' . ale#Escape('languageserver::run()') + +Execute(The project root should be detected correctly): + AssertLSPProject '.' + + call ale#test#SetFilename('r_paths/dummy/test.R') + + AssertLSPProject ale#path#Simplify(g:dir . '/r_paths') + +Execute(Should accept configuration settings): + AssertLSPConfig {} + + let b:ale_r_languageserver_options = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} + + AssertLSPConfig {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} From 681a6e371d02cce9c2414c19f5deeae61aa321fa Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 21 Nov 2020 20:58:34 +0000 Subject: [PATCH 28/30] Use _config for LSP config options --- ale_linters/r/languageserver.vim | 4 ++-- doc/ale-r.txt | 10 +++++----- .../test_r_languageserver_callbacks.vader | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ale_linters/r/languageserver.vim b/ale_linters/r/languageserver.vim index c625d8b4..febe66bd 100644 --- a/ale_linters/r/languageserver.vim +++ b/ale_linters/r/languageserver.vim @@ -2,7 +2,7 @@ " Description: Implementation of the Language Server Protocol for R. call ale#Set('r_languageserver_cmd', 'languageserver::run()') -call ale#Set('r_languageserver_options', {}) +call ale#Set('r_languageserver_config', {}) function! ale_linters#r#languageserver#GetCommand(buffer) abort let l:cmd_string = ale#Var(a:buffer, 'r_languageserver_cmd') @@ -19,7 +19,7 @@ endfunction call ale#linter#Define('r', { \ 'name': 'languageserver', \ 'lsp': 'stdio', -\ 'lsp_config': {b -> ale#Var(b, 'r_languageserver_options')}, +\ 'lsp_config': {b -> ale#Var(b, 'r_languageserver_config')}, \ 'executable': 'Rscript', \ 'command': function('ale_linters#r#languageserver#GetCommand'), \ 'project_root': function('ale_linters#r#languageserver#GetProjectRoot') diff --git a/doc/ale-r.txt b/doc/ale-r.txt index 2078b218..3fabf702 100644 --- a/doc/ale-r.txt +++ b/doc/ale-r.txt @@ -16,8 +16,8 @@ g:ale_r_languageserver_cmd *g:ale_r_languageserver_cmd* See the languageserver documentation for more options. -g:ale_r_languageserver_options *g:ale_r_languageserver_options* - *b:ale_r_languageserver_options* +g:ale_r_languageserver_config *g:ale_r_languageserver_config* + *b:ale_r_languageserver_config* Type: |Dictionary| Default: `{}` @@ -45,7 +45,7 @@ g:ale_r_lintr_lint_package *g:ale_r_lintr_lint_package* Default: `0` When set to `1`, the file will be checked with `lintr::lint_package` instead - of `lintr::lint`. This prevents erroneous namespace warnings when linting + of `lintr::lint`. This prevents erroneous namespace warnings when linting package files. @@ -59,8 +59,8 @@ g:ale_r_styler_options *g:ale_r_styler_options* This option can be configured to change the options for styler. - The value of this option will be used as the `style` argument for the - `styler::style_file` options. Consult the styler documentation + The value of this option will be used as the `style` argument for the + `styler::style_file` options. Consult the styler documentation for more information. diff --git a/test/command_callback/test_r_languageserver_callbacks.vader b/test/command_callback/test_r_languageserver_callbacks.vader index 7829165c..9a4a1f87 100644 --- a/test/command_callback/test_r_languageserver_callbacks.vader +++ b/test/command_callback/test_r_languageserver_callbacks.vader @@ -17,6 +17,6 @@ Execute(The project root should be detected correctly): Execute(Should accept configuration settings): AssertLSPConfig {} - let b:ale_r_languageserver_options = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} + let b:ale_r_languageserver_config = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} AssertLSPConfig {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}} From c69d696e1b63c7797e8696f923856d131f02f5e9 Mon Sep 17 00:00:00 2001 From: Lyz Date: Tue, 24 Nov 2020 23:56:30 +0100 Subject: [PATCH 29/30] feat: add yamlfix fixer --- autoload/ale/fix/registry.vim | 5 ++ autoload/ale/fixers/yamlfix.vim | 25 ++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale-yaml.txt | 38 +++++++++++++- doc/ale.txt | 1 + supported-tools.md | 1 + .../with_virtualenv/env/Scripts/yamlfix.exe | 0 .../with_virtualenv/env/bin/yamlfix | 0 test/fixers/test_yamlfix_fixer_callback.vader | 50 +++++++++++++++++++ 9 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 autoload/ale/fixers/yamlfix.vim create mode 100644 test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe create mode 100755 test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix create mode 100644 test/fixers/test_yamlfix_fixer_callback.vader diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 3a88ede1..e7c8a6b5 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -110,6 +110,11 @@ let s:default_registry = { \ 'suggested_filetypes': [], \ 'description': 'Remove all trailing whitespace characters at the end of every line.', \ }, +\ 'yamlfix': { +\ 'function': 'ale#fixers#yamlfix#Fix', +\ 'suggested_filetypes': ['python'], +\ 'description': 'Fix yaml files with yamlfix.', +\ }, \ 'yapf': { \ 'function': 'ale#fixers#yapf#Fix', \ 'suggested_filetypes': ['python'], diff --git a/autoload/ale/fixers/yamlfix.vim b/autoload/ale/fixers/yamlfix.vim new file mode 100644 index 00000000..966556c9 --- /dev/null +++ b/autoload/ale/fixers/yamlfix.vim @@ -0,0 +1,25 @@ +" Author: lyz-code +" Description: Fixing yaml files with yamlfix. + +call ale#Set('yaml_yamlfix_executable', 'yamlfix') +call ale#Set('yaml_yamlfix_options', '') +call ale#Set('yaml_yamlfix_use_global', get(g:, 'ale_use_global_executables', 0)) + +function! ale#fixers#yamlfix#Fix(buffer) abort + let l:options = ale#Var(a:buffer, 'yaml_yamlfix_options') + + let l:executable = ale#python#FindExecutable( + \ a:buffer, + \ 'yaml_yamlfix', + \ ['yamlfix'], + \) + + if !executable(l:executable) + return 0 + endif + + return { + \ 'command': ale#path#BufferCdString(a:buffer) + \ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -', + \} +endfunction diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index f4673ec0..36e27932 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -524,6 +524,7 @@ Notes: * YAML * `prettier` * `swaglint` + * `yamlfix` * `yamllint` * YANG * `yang-lsp` diff --git a/doc/ale-yaml.txt b/doc/ale-yaml.txt index c9a12ea1..61bfc139 100644 --- a/doc/ale-yaml.txt +++ b/doc/ale-yaml.txt @@ -15,7 +15,6 @@ Install prettier either globally or locally: > npm install prettier -g # global npm install prettier # local < - =============================================================================== swaglint *ale-yaml-swaglint* @@ -49,6 +48,43 @@ g:ale_yaml_swaglint_use_global *g:ale_yaml_swaglint_use_global* See |ale-integrations-local-executables| +=============================================================================== +yamlfix *ale-yaml-yamlfix* + +Website: https://lyz-code.github.io/yamlfix + + +Installation +------------------------------------------------------------------------------- + +Install yamlfix: > + + pip install yamlfix +< + +Options +------------------------------------------------------------------------------- +g:ale_yaml_yamlfix_executable *g:ale_yaml_yamlfix_executable* + *b:ale_yaml_yamlfix_executable* + Type: |String| + Default: `'yamlfix'` + + See |ale-integrations-local-executables| + + +g:ale_yaml_yamlfix_options *g:ale_yaml_yamlfix_options* + *b:ale_yaml_yamlfix_options* + Type: |String| + Default: `''` + + This variable can be set to pass extra options to yamlfix. + +g:ale_yaml_yamlfix_use_global *g:ale_yaml_yamlfix_use_global* + *b:ale_yaml_yamlfix_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| =============================================================================== yamllint *ale-yaml-yamllint* diff --git a/doc/ale.txt b/doc/ale.txt index 00f43aa5..f9f40d12 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2971,6 +2971,7 @@ documented in additional help files. yaml....................................|ale-yaml-options| prettier..............................|ale-yaml-prettier| swaglint..............................|ale-yaml-swaglint| + yamlfix...............................|ale-yaml-yamlfix| yamllint..............................|ale-yaml-yamllint| yang....................................|ale-yang-options| yang-lsp..............................|ale-yang-lsp| diff --git a/supported-tools.md b/supported-tools.md index e1ac8873..96ef273b 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -533,6 +533,7 @@ formatting. * YAML * [prettier](https://github.com/prettier/prettier) * [swaglint](https://github.com/byCedric/swaglint) + * [yamlfix](https://lyz-code.github.io/yamlfix) * [yamllint](https://yamllint.readthedocs.io/) * YANG * [yang-lsp](https://github.com/theia-ide/yang-lsp) diff --git a/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe b/test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe new file mode 100644 index 00000000..e69de29b diff --git a/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix b/test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix new file mode 100755 index 00000000..e69de29b diff --git a/test/fixers/test_yamlfix_fixer_callback.vader b/test/fixers/test_yamlfix_fixer_callback.vader new file mode 100644 index 00000000..3ffda91e --- /dev/null +++ b/test/fixers/test_yamlfix_fixer_callback.vader @@ -0,0 +1,50 @@ +Before: + Save g:ale_python_yamlfix_executable + Save g:ale_python_yamlfix_options + + " Use an invalid global executable, so we don't match it. + let g:ale_python_yamlfix_executable = 'xxxinvalid' + let g:ale_python_yamlfix_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + silent cd .. + silent cd command_callback + let g:dir = getcwd() + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + Restore + + unlet! b:bin_dir + + call ale#test#RestoreDirectory() + +Execute(The yamlfix callback should return the correct default values): + AssertEqual + \ 0, + \ ale#fixers#yamlfix#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) . ' -', + \ }, + \ ale#fixers#yamlfix#Fix(bufnr('')) + +Execute(The yamlfix callback should respect custom options): + let g:ale_yaml_yamlfix_options = '--multi-line=3 --trailing-comma' + + AssertEqual + \ 0, + \ ale#fixers#yamlfix#Fix(bufnr('')) + + silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml') + AssertEqual + \ { + \ 'command': ale#path#BufferCdString(bufnr('')) + \ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) + \ . ' --multi-line=3 --trailing-comma -', + \ }, + \ ale#fixers#yamlfix#Fix(bufnr('')) From 12eb8d15234e6f4e77672c29659c29a7c0dcf2fb Mon Sep 17 00:00:00 2001 From: Lyz Date: Thu, 26 Nov 2020 12:42:50 +0100 Subject: [PATCH 30/30] fix: correct suggested filetype for yamlfix --- autoload/ale/fix/registry.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index e7c8a6b5..0f146faa 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -112,7 +112,7 @@ let s:default_registry = { \ }, \ 'yamlfix': { \ 'function': 'ale#fixers#yamlfix#Fix', -\ 'suggested_filetypes': ['python'], +\ 'suggested_filetypes': ['yaml'], \ 'description': 'Fix yaml files with yamlfix.', \ }, \ 'yapf': {