From 732d8e3ed6deb1b16bf47e21ce1a823e5e23228b Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Thu, 9 Nov 2017 09:49:44 -0200 Subject: [PATCH 01/23] Add support for linting less files with lessc --- README.md | 1 + ale_linters/less/lessc.vim | 36 ++++++++++++++++++++++++++++++++++++ doc/ale-less.txt | 11 +++++++++++ doc/ale.txt | 2 ++ 4 files changed, 50 insertions(+) create mode 100755 ale_linters/less/lessc.vim diff --git a/README.md b/README.md index 9c5bc5a..7c6488c 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ formatting. | JSON | [jsonlint](http://zaa.ch/jsonlint/), [prettier](https://github.com/prettier/prettier) | | Kotlin | [kotlinc](https://kotlinlang.org) !!, [ktlint](https://ktlint.github.io) !! see `:help ale-integration-kotlin` for configuration instructions | | LaTeX | [chktex](http://www.nongnu.org/chktex/), [lacheck](https://www.ctan.org/pkg/lacheck), [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | +| Less | [lessc](https://www.npmjs.com/package/less) | | LLVM | [llc](https://llvm.org/docs/CommandGuide/llc.html) | | Lua | [luacheck](https://github.com/mpeterv/luacheck) | | Mail | [proselint](http://proselint.com/), [vale](https://github.com/ValeLint/vale) | diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim new file mode 100755 index 0000000..3f6e265 --- /dev/null +++ b/ale_linters/less/lessc.vim @@ -0,0 +1,36 @@ +" Author: zanona , w0rp +" Description: This file adds support for checking Less code with lessc. + +call ale#Set('less_lessc_options', '') + +function! ale_linters#less#lessc#GetCommand(buffer) abort + return 'lessc' + \ . ' --no-color --lint ' + \ . ale#Var(a:buffer, 'less_lessc_options') + \ . ' %t' +endfunction + +function! ale_linters#less#lessc#Handle(buffer, lines) abort + " Matches patterns like the following: + let l:pattern = '^\(\w\+\): \(.\{-}\) in \(.\{-}\) on line \(\d\+\), column \(\d\+\):$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[4] + 0, + \ 'col': l:match[5] + 0, + \ 'text': l:match[2], + \ 'type': 'E', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('less', { +\ 'name': 'lessc', +\ 'executable': 'lessc', +\ 'output_stream': 'stderr', +\ 'command_callback': 'ale_linters#less#lessc#GetCommand', +\ 'callback': 'ale_linters#less#lessc#Handle', +\}) diff --git a/doc/ale-less.txt b/doc/ale-less.txt index a6b5998..cac9c9a 100644 --- a/doc/ale-less.txt +++ b/doc/ale-less.txt @@ -2,6 +2,17 @@ ALE Less Integration *ale-less-options* +=============================================================================== +lessc *ale-less-lessc* + +g:ale_less_lessc_options *g:ale_less_lessc_options* + *b:ale_less_lessc_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to lessc. + + =============================================================================== prettier *ale-less-prettier* diff --git a/doc/ale.txt b/doc/ale.txt index 2ee2c2d..ffe7ac7 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -105,6 +105,7 @@ CONTENTS *ale-contents* latex.................................|ale-latex-options| write-good..........................|ale-latex-write-good| less..................................|ale-less-options| + lessc...............................|ale-less-lessc-options| prettier............................|ale-less-prettier| llvm..................................|ale-llvm-options| llc.................................|ale-llvm-llc| @@ -293,6 +294,7 @@ Notes: * JSON: `jsonlint`, `prettier` * Kotlin: `kotlinc`, `ktlint` * LaTeX (tex): `chktex`, `lacheck`, `proselint`, `write-good` +* Less: `lessc` * LLVM: `llc` * Lua: `luacheck` * Mail: `proselint`, `vale` From decf8188bc07a097e855af2b424d68374438c635 Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Thu, 9 Nov 2017 13:39:38 -0200 Subject: [PATCH 02/23] Adjust formatting on less#lessc linter --- ale_linters/less/lessc.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index 3f6e265..941607b 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -5,9 +5,9 @@ call ale#Set('less_lessc_options', '') function! ale_linters#less#lessc#GetCommand(buffer) abort return 'lessc' - \ . ' --no-color --lint ' - \ . ale#Var(a:buffer, 'less_lessc_options') - \ . ' %t' + \ . ' --no-color --lint' + \ . ' ' . ale#Var(a:buffer, 'less_lessc_options') + \ . ' %t' endfunction function! ale_linters#less#lessc#Handle(buffer, lines) abort From 7ed82ab712324eb410279b95d852659e97ff9021 Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Thu, 9 Nov 2017 13:40:13 -0200 Subject: [PATCH 03/23] Adjust author info on less#lessc linter --- ale_linters/less/lessc.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index 941607b..db871ac 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -1,4 +1,4 @@ -" Author: zanona , w0rp +" Author: zanona " Description: This file adds support for checking Less code with lessc. call ale#Set('less_lessc_options', '') From 4bc31fcd18bda03f0de516be0380d6885cdd3d9b Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Thu, 9 Nov 2017 13:45:14 -0200 Subject: [PATCH 04/23] Fix imported files path lookup on less#lessc linter Ale saves a temporary file (%t) which does not share the same path as the original file, breaking import statements with relative URLs. This fix sends content to `lessc` over stdin and adds the current file (%s) as one of the included paths, so statements like `@import '../utils' will correctly resolve based on the current file path. --- ale_linters/less/lessc.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index db871ac..1c5df91 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -4,10 +4,10 @@ call ale#Set('less_lessc_options', '') function! ale_linters#less#lessc#GetCommand(buffer) abort - return 'lessc' - \ . ' --no-color --lint' + return 'cat %t | lessc' + \ . ' --no-color --lint --include-path=' . expand('%:p:h') \ . ' ' . ale#Var(a:buffer, 'less_lessc_options') - \ . ' %t' + \ . ' -' endfunction function! ale_linters#less#lessc#Handle(buffer, lines) abort From 1ad7d5e6ca34c285dc7abbb98222b05a32b4f70d Mon Sep 17 00:00:00 2001 From: Marcus Zanona Date: Thu, 9 Nov 2017 19:52:08 -0200 Subject: [PATCH 05/23] Remove unnecessary cat command from less#lessc --- ale_linters/less/lessc.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index 1c5df91..76b7f13 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -4,7 +4,7 @@ call ale#Set('less_lessc_options', '') function! ale_linters#less#lessc#GetCommand(buffer) abort - return 'cat %t | lessc' + return 'lessc' \ . ' --no-color --lint --include-path=' . expand('%:p:h') \ . ' ' . ale#Var(a:buffer, 'less_lessc_options') \ . ' -' From 911b6d8f71213d065bd14548a16ba9140f1790ee Mon Sep 17 00:00:00 2001 From: wuqiong4945 Date: Thu, 9 Nov 2017 21:14:29 +0800 Subject: [PATCH 06/23] add 'output_stream': 'stderr', let golint work --- ale_linters/go/golint.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/ale_linters/go/golint.vim b/ale_linters/go/golint.vim index cc807fe..708cf15 100644 --- a/ale_linters/go/golint.vim +++ b/ale_linters/go/golint.vim @@ -3,6 +3,7 @@ call ale#linter#Define('go', { \ 'name': 'golint', +\ 'output_stream': 'stderr', \ 'executable': 'golint', \ 'command': 'golint %t', \ 'callback': 'ale#handlers#unix#HandleAsWarning', From a8c5e0f4dce14b9dad8e4a238ebc93d8aa6ed28e Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 9 Nov 2017 23:42:54 +0000 Subject: [PATCH 07/23] Simplfy semver handling and share the semver version cache across everything --- ale_linters/javascript/flow.vim | 13 ++-- ale_linters/python/flake8.vim | 43 ++----------- ale_linters/rust/cargo.vim | 62 ++++-------------- ale_linters/sh/shellcheck.vim | 31 ++------- ale_linters/vim/vint.vim | 20 ++---- autoload/ale/handlers/gcc.vim | 12 ---- autoload/ale/semver.vim | 52 +++++++++++---- .../test_cargo_command_callbacks.vader | 1 + .../test_flake8_command_callback.vader | 11 ++-- test/handler/test_gcc_handler.vader | 11 ---- test/test_flow_command.vader | 1 + test/test_semver_utils.vader | 64 ++++++++++++++----- 12 files changed, 133 insertions(+), 188 deletions(-) diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim index 0dd6453..6d51628 100644 --- a/ale_linters/javascript/flow.vim +++ b/ale_linters/javascript/flow.vim @@ -23,18 +23,15 @@ function! ale_linters#javascript#flow#GetCommand(buffer, version_lines) abort return '' endif - let l:use_respect_pragma = 1 + let l:executable = ale_linters#javascript#flow#GetExecutable(a:buffer) + let l:version = ale#semver#GetVersion(l:executable, a:version_lines) " If we can parse the version number, then only use --respect-pragma " if the version is >= 0.36.0, which added the argument. - for l:match in ale#util#GetMatches(a:version_lines, '\v\d+\.\d+\.\d+$') - let l:use_respect_pragma = ale#semver#GreaterOrEqual( - \ ale#semver#Parse(l:match[0]), - \ [0, 36, 0] - \) - endfor + let l:use_respect_pragma = empty(l:version) + \ || ale#semver#GTE(l:version, [0, 36]) - return ale#Escape(ale_linters#javascript#flow#GetExecutable(a:buffer)) + return ale#Escape(l:executable) \ . ' check-contents' \ . (l:use_respect_pragma ? ' --respect-pragma': '') \ . ' --json --from ale %s' diff --git a/ale_linters/python/flake8.vim b/ale_linters/python/flake8.vim index 8aa4c4d..501db0b 100644 --- a/ale_linters/python/flake8.vim +++ b/ale_linters/python/flake8.vim @@ -10,10 +10,6 @@ let g:ale_python_flake8_options = \ get(g:, 'ale_python_flake8_options', s:default_options) let g:ale_python_flake8_use_global = get(g:, 'ale_python_flake8_use_global', 0) -" A map from Python executable paths to semver strings parsed for those -" executables, so we don't have to look up the version number constantly. -let s:version_cache = {} - function! s:UsingModule(buffer) abort return ale#Var(a:buffer, 'python_flake8_options') =~# ' *-m flake8' endfunction @@ -26,62 +22,35 @@ function! ale_linters#python#flake8#GetExecutable(buffer) abort return ale#Var(a:buffer, 'python_flake8_executable') endfunction -function! ale_linters#python#flake8#ClearVersionCache() abort - let s:version_cache = {} -endfunction - function! ale_linters#python#flake8#VersionCheck(buffer) abort let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) " If we have previously stored the version number in a cache, then " don't look it up again. - if has_key(s:version_cache, l:executable) + if ale#semver#HasVersion(l:executable) " Returning an empty string skips this command. return '' endif - let l:executable = ale#Escape(ale_linters#python#flake8#GetExecutable(a:buffer)) + let l:executable = ale#Escape(l:executable) let l:module_string = s:UsingModule(a:buffer) ? ' -m flake8' : '' return l:executable . l:module_string . ' --version' endfunction -" Get the flake8 version from the output, or the cache. -function! s:GetVersion(buffer, version_output) abort - let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) - let l:version = [] - - " Get the version from the cache. - if has_key(s:version_cache, l:executable) - return s:version_cache[l:executable] - endif - - if !empty(a:version_output) - " Parse the version string, and store it in the cache. - let l:version = ale#semver#Parse(a:version_output[0]) - let s:version_cache[l:executable] = l:version - endif - - return l:version -endfunction - -" flake8 versions 3 and up support the --stdin-display-name argument. -function! s:SupportsDisplayName(version) abort - return !empty(a:version) && ale#semver#GreaterOrEqual(a:version, [3, 0, 0]) -endfunction - function! ale_linters#python#flake8#GetCommand(buffer, version_output) abort - let l:version = s:GetVersion(a:buffer, a:version_output) + let l:executable = ale_linters#python#flake8#GetExecutable(a:buffer) + let l:version = ale#semver#GetVersion(l:executable, a:version_output) " Only include the --stdin-display-name argument if we can parse the " flake8 version, and it is recent enough to support it. - let l:display_name_args = s:SupportsDisplayName(l:version) + let l:display_name_args = ale#semver#GTE(l:version, [3, 0, 0]) \ ? ' --stdin-display-name %s' \ : '' let l:options = ale#Var(a:buffer, 'python_flake8_options') - return ale#Escape(ale_linters#python#flake8#GetExecutable(a:buffer)) + return ale#Escape(l:executable) \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' --format=default' \ . l:display_name_args . ' -' diff --git a/ale_linters/rust/cargo.vim b/ale_linters/rust/cargo.vim index f41cb4b..ae26fab 100644 --- a/ale_linters/rust/cargo.vim +++ b/ale_linters/rust/cargo.vim @@ -4,8 +4,6 @@ call ale#Set('rust_cargo_use_check', 1) call ale#Set('rust_cargo_check_all_targets', 1) -let s:version_cache = {} - function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort if ale#path#FindNearestFile(a:bufnr, 'Cargo.toml') isnot# '' return 'cargo' @@ -17,59 +15,23 @@ function! ale_linters#rust#cargo#GetCargoExecutable(bufnr) abort endfunction function! ale_linters#rust#cargo#VersionCheck(buffer) abort - if has_key(s:version_cache, 'cargo') - return '' - endif - - return 'cargo --version' -endfunction - -function! s:GetVersion(executable, output) abort - let l:version = get(s:version_cache, a:executable, []) - - for l:match in ale#util#GetMatches(a:output, '\v\d+\.\d+\.\d+') - let l:version = ale#semver#Parse(l:match[0]) - let s:version_cache[a:executable] = l:version - endfor - - return l:version -endfunction - -function! s:CanUseCargoCheck(buffer, version) abort - " Allow `cargo check` to be disabled. - if !ale#Var(a:buffer, 'rust_cargo_use_check') - return 0 - endif - - return !empty(a:version) - \ && ale#semver#GreaterOrEqual(a:version, [0, 17, 0]) -endfunction - -function! s:CanUseAllTargets(buffer, version) abort - if !ale#Var(a:buffer, 'rust_cargo_use_check') - return 0 - endif - - if !ale#Var(a:buffer, 'rust_cargo_check_all_targets') - return 0 - endif - - return !empty(a:version) - \ && ale#semver#GreaterOrEqual(a:version, [0, 22, 0]) + return !ale#semver#HasVersion('cargo') + \ ? 'cargo --version' + \ : '' endfunction function! ale_linters#rust#cargo#GetCommand(buffer, version_output) abort - let l:version = s:GetVersion('cargo', a:version_output) - let l:command = s:CanUseCargoCheck(a:buffer, l:version) - \ ? 'check' - \ : 'build' - let l:all_targets = s:CanUseAllTargets(a:buffer, l:version) - \ ? ' --all-targets' - \ : '' + let l:version = ale#semver#GetVersion('cargo', a:version_output) + + let l:use_check = ale#Var(a:buffer, 'rust_cargo_use_check') + \ && ale#semver#GTE(l:version, [0, 17, 0]) + let l:use_all_targets = l:use_check + \ && ale#Var(a:buffer, 'rust_cargo_check_all_targets') + \ && ale#semver#GTE(l:version, [0, 22, 0]) return 'cargo ' - \ . l:command - \ . l:all_targets + \ . (l:use_check ? 'check' : 'build') + \ . (l:use_all_targets ? ' --all-targets' : '') \ . ' --frozen --message-format=json -q' endfunction diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim index 1f6bdf8..e79b3b8 100644 --- a/ale_linters/sh/shellcheck.vim +++ b/ale_linters/sh/shellcheck.vim @@ -15,8 +15,6 @@ let g:ale_sh_shellcheck_executable = let g:ale_sh_shellcheck_options = \ get(g:, 'ale_sh_shellcheck_options', '') -let s:version_cache = {} - function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort return ale#Var(a:buffer, 'sh_shellcheck_executable') endfunction @@ -49,38 +47,19 @@ function! ale_linters#sh#shellcheck#VersionCheck(buffer) abort let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer) " Don't check the version again if we've already cached it. - if has_key(s:version_cache, l:executable) - return '' - endif - - return ale#Escape(l:executable) . ' --version' -endfunction - -" Get the shellcheck version from the cache, or parse it and cache it. -function! s:GetVersion(executable, output) abort - let l:version = get(s:version_cache, a:executable, []) - - for l:match in ale#util#GetMatches(a:output, '\v\d+\.\d+\.\d+') - let l:version = ale#semver#Parse(l:match[0]) - let s:version_cache[a:executable] = l:version - endfor - - return l:version -endfunction - -function! s:CanUseExternalOption(version) abort - return !empty(a:version) - \ && ale#semver#GreaterOrEqual(a:version, [0, 4, 0]) + return !ale#semver#HasVersion(l:executable) + \ ? ale#Escape(l:executable) . ' --version' + \ : '' endfunction function! ale_linters#sh#shellcheck#GetCommand(buffer, version_output) abort let l:executable = ale_linters#sh#shellcheck#GetExecutable(a:buffer) - let l:version = s:GetVersion(l:executable, a:version_output) + let l:version = ale#semver#GetVersion(l:executable, a:version_output) let l:options = ale#Var(a:buffer, 'sh_shellcheck_options') let l:exclude_option = ale#Var(a:buffer, 'sh_shellcheck_exclusions') let l:dialect = ale_linters#sh#shellcheck#GetDialectArgument(a:buffer) - let l:external_option = s:CanUseExternalOption(l:version) ? ' -x' : '' + let l:external_option = ale#semver#GTE(l:version, [0, 4, 0]) ? ' -x' : '' return ale#path#BufferCdString(a:buffer) \ . ale#Escape(l:executable) diff --git a/ale_linters/vim/vint.vim b/ale_linters/vim/vint.vim index adf2b4a..dfa00dc 100644 --- a/ale_linters/vim/vint.vim +++ b/ale_linters/vim/vint.vim @@ -6,25 +6,19 @@ let g:ale_vim_vint_show_style_issues = \ get(g:, 'ale_vim_vint_show_style_issues', 1) let s:enable_neovim = has('nvim') ? ' --enable-neovim ' : '' let s:format = '-f "{file_path}:{line_number}:{column_number}: {severity}: {description} (see {reference})"' -let s:vint_version = [] function! ale_linters#vim#vint#VersionCommand(buffer) abort - if empty(s:vint_version) - " Check the Vint version if we haven't checked it already. - return 'vint --version' - endif - - return '' + " Check the Vint version if we haven't checked it already. + return !ale#semver#HasVersion('vint') + \ ? 'vint --version' + \ : '' endfunction function! ale_linters#vim#vint#GetCommand(buffer, version_output) abort - if empty(s:vint_version) && !empty(a:version_output) - " Parse the version out of the --version output. - let s:vint_version = ale#semver#Parse(join(a:version_output, "\n")) - endif + let l:version = ale#semver#GetVersion('vint', a:version_output) - let l:can_use_no_color_flag = empty(s:vint_version) - \ || ale#semver#GreaterOrEqual(s:vint_version, [0, 3, 7]) + let l:can_use_no_color_flag = empty(l:version) + \ || ale#semver#GTE(l:version, [0, 3, 7]) let l:warning_flag = ale#Var(a:buffer, 'vim_vint_show_style_issues') ? '-s' : '-w' diff --git a/autoload/ale/handlers/gcc.vim b/autoload/ale/handlers/gcc.vim index 256cd01..9ec7b11 100644 --- a/autoload/ale/handlers/gcc.vim +++ b/autoload/ale/handlers/gcc.vim @@ -18,18 +18,6 @@ function! s:RemoveUnicodeQuotes(text) abort return l:text endfunction -function! ale#handlers#gcc#ParseGCCVersion(lines) abort - for l:line in a:lines - let l:match = matchstr(l:line, '\d\.\d\.\d') - - if !empty(l:match) - return ale#semver#Parse(l:match) - endif - endfor - - return [] -endfunction - function! ale#handlers#gcc#HandleGCCFormat(buffer, lines) abort " Look for lines like the following. " diff --git a/autoload/ale/semver.vim b/autoload/ale/semver.vim index b153dd1..6b0fd34 100644 --- a/autoload/ale/semver.vim +++ b/autoload/ale/semver.vim @@ -1,27 +1,55 @@ -" Given some text, parse a semantic versioning string from the text -" into a triple of integeers [major, minor, patch]. +let s:version_cache = {} + +" Reset the version cache used for parsing the version. +function! ale#semver#ResetVersionCache() abort + let s:version_cache = {} +endfunction + +" Given an executable name and some lines of output, which can be empty, +" parse the version from the lines of output, or return the cached version +" triple [major, minor, patch] " -" If no match can be performed, then an empty List will be returned instead. -function! ale#semver#Parse(text) abort - let l:match = matchlist(a:text, '^ *\(\d\+\)\.\(\d\+\)\.\(\d\+\)') +" If the version cannot be found, an empty List will be returned instead. +function! ale#semver#GetVersion(executable, version_lines) abort + let l:version = get(s:version_cache, a:executable, []) - if empty(l:match) - return [] - endif + for l:line in a:version_lines + let l:match = matchlist(l:line, '\v(\d+)\.(\d+)\.(\d+)') - return [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0] + if !empty(l:match) + let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[3] + 0] + let s:version_cache[a:executable] = l:version + + break + endif + endfor + + return l:version +endfunction + +" Return 1 if the semver version has been cached for a given executable. +function! ale#semver#HasVersion(executable) abort + return has_key(s:version_cache, a:executable) endfunction " Given two triples of integers [major, minor, patch], compare the triples -" and return 1 if the lhs is greater than or equal to the rhs. -function! ale#semver#GreaterOrEqual(lhs, rhs) abort +" and return 1 if the LHS is greater than or equal to the RHS. +" +" Pairs of [major, minor] can also be used for either argument. +" +" 0 will be returned if the LHS is an empty List. +function! ale#semver#GTE(lhs, rhs) abort + if empty(a:lhs) + return 0 + endif + if a:lhs[0] > a:rhs[0] return 1 elseif a:lhs[0] == a:rhs[0] if a:lhs[1] > a:rhs[1] return 1 elseif a:lhs[1] == a:rhs[1] - return a:lhs[2] >= a:rhs[2] + return get(a:lhs, 2) >= get(a:rhs, 2) endif endif diff --git a/test/command_callback/test_cargo_command_callbacks.vader b/test/command_callback/test_cargo_command_callbacks.vader index d808e19..1053551 100644 --- a/test/command_callback/test_cargo_command_callbacks.vader +++ b/test/command_callback/test_cargo_command_callbacks.vader @@ -17,6 +17,7 @@ After: call ale#test#RestoreDirectory() call ale#linter#Reset() + call ale#semver#ResetVersionCache() Execute(An empty string should be returned for the cargo executable when there's no Cargo.toml file): AssertEqual diff --git a/test/command_callback/test_flake8_command_callback.vader b/test/command_callback/test_flake8_command_callback.vader index a510f4c..47d5c0f 100644 --- a/test/command_callback/test_flake8_command_callback.vader +++ b/test/command_callback/test_flake8_command_callback.vader @@ -23,7 +23,7 @@ After: call ale#test#RestoreDirectory() call ale#linter#Reset() - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() Execute(The flake8 callbacks should return the correct default values): AssertEqual @@ -35,8 +35,9 @@ Execute(The flake8 callbacks should return the correct default values): AssertEqual \ ale#Escape('flake8') . ' --format=default --stdin-display-name %s -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.0']) + " Try with older versions. - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() AssertEqual \ ale#Escape('flake8') . ' --format=default -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9']) @@ -49,7 +50,9 @@ Execute(The flake8 command callback should let you set options): \ . ' --some-option --format=default' \ . ' --stdin-display-name %s -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['3.0.4']) - call ale_linters#python#flake8#ClearVersionCache() + + call ale#semver#ResetVersionCache() + AssertEqual \ ale#Escape('flake8') \ . ' --some-option --format=default -', @@ -140,7 +143,7 @@ Execute(Using `python -m flake8` should be supported for running flake8): \ ale#Escape('python') . ' -m flake8 --some-option --format=default -', \ ale_linters#python#flake8#GetCommand(bufnr(''), ['2.9.9']) - call ale_linters#python#flake8#ClearVersionCache() + call ale#semver#ResetVersionCache() " Leading spaces shouldn't matter let g:ale_python_flake8_options = ' -m flake8 --some-option' diff --git a/test/handler/test_gcc_handler.vader b/test/handler/test_gcc_handler.vader index 9324273..79f1789 100644 --- a/test/handler/test_gcc_handler.vader +++ b/test/handler/test_gcc_handler.vader @@ -71,17 +71,6 @@ Execute(GCC errors from included files should be parsed correctly): \ ' ^', \ ]) -Execute(GCC versions should be parsed correctly): - AssertEqual [4, 9, 1], ale#handlers#gcc#ParseGCCVersion([ - \ 'g++ (GCC) 4.9.1 20140922 (Red Hat 4.9.1-10)', - \]) - AssertEqual [4, 8, 4], ale#handlers#gcc#ParseGCCVersion([ - \ 'gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4', - \ 'Copyright (C) 2013 Free Software Foundation, Inc.', - \ 'This is free software; see the source for copying conditions. There is NO', - \ 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.', - \]) - Execute(The GCC handler shouldn't complain about #pragma once for headers): silent file! test.h diff --git a/test/test_flow_command.vader b/test/test_flow_command.vader index 32ceb57..49546e9 100644 --- a/test/test_flow_command.vader +++ b/test/test_flow_command.vader @@ -5,6 +5,7 @@ Before: After: call ale#test#RestoreDirectory() call ale#linter#Reset() + call ale#semver#ResetVersionCache() Execute(flow should return a command to run if a .flowconfig file exists): call ale#test#SetFilename('flow/a/sub/dummy') diff --git a/test/test_semver_utils.vader b/test/test_semver_utils.vader index 9730b74..30e9e81 100644 --- a/test/test_semver_utils.vader +++ b/test/test_semver_utils.vader @@ -1,16 +1,50 @@ -Execute(ParseSemver should return the correct results): - " We should be able to parse the semver string from flake8 - AssertEqual [3, 0, 4], ale#semver#Parse('3.0.4 (mccabe: 0.5.2, pyflakes: 1.2.3, pycodestyle: 2.0.0) CPython 2.7.12 on Linux') +After: + call ale#semver#ResetVersionCache() -Execute(GreaterOrEqual should compare triples correctly): - Assert ale#semver#GreaterOrEqual([3, 0, 4], [3, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 0, 0], [3, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 0, 0], [2, 0, 0]) - Assert ale#semver#GreaterOrEqual([3, 1, 0], [3, 1, 0]) - Assert ale#semver#GreaterOrEqual([3, 2, 0], [3, 1, 0]) - Assert ale#semver#GreaterOrEqual([3, 2, 2], [3, 1, 6]) - Assert ale#semver#GreaterOrEqual([3, 2, 5], [3, 2, 5]) - Assert ale#semver#GreaterOrEqual([3, 2, 6], [3, 2, 5]) - Assert !ale#semver#GreaterOrEqual([2, 9, 1], [3, 0, 0]) - Assert !ale#semver#GreaterOrEqual([3, 2, 3], [3, 3, 3]) - Assert !ale#semver#GreaterOrEqual([3, 3, 2], [3, 3, 3]) +Execute(GetVersion should return the version from the lines of output): + " We should be able to parse the semver string from flake8 + AssertEqual [3, 0, 4], ale#semver#GetVersion('dummy', [ + \ '3.0.4 (mccabe: 0.5.2, pyflakes: 1.2.3, pycodestyle: 2.0.0) CPython 2.7.12 on Linux', + \ '1.2.3', + \]) + +Execute(GetVersion should return an empty list when no vesrion can be found): + AssertEqual [], ale#semver#GetVersion('dummy', ['x']) + AssertEqual [], ale#semver#GetVersion('dummy', []) + +Execute(GetVersion should cache the version): + AssertEqual [], ale#semver#GetVersion('dummy', []) + AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', ['Version 3.4.7']) + AssertEqual [3, 4, 7], ale#semver#GetVersion('dummy', []) + +Execute(HasVersion should return 1 when the version has been cached): + call ale#semver#GetVersion('dummy', []) + AssertEqual 0, ale#semver#HasVersion('dummy') + call ale#semver#GetVersion('dummy', ['3.4.7']) + AssertEqual 1, ale#semver#HasVersion('dummy') + +Execute(GTE should compare triples correctly): + Assert ale#semver#GTE([3, 0, 4], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 0], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 0], [2, 0, 0]) + Assert ale#semver#GTE([3, 1, 0], [3, 1, 0]) + Assert ale#semver#GTE([3, 2, 0], [3, 1, 0]) + Assert ale#semver#GTE([3, 2, 2], [3, 1, 6]) + Assert ale#semver#GTE([3, 2, 5], [3, 2, 5]) + Assert ale#semver#GTE([3, 2, 6], [3, 2, 5]) + Assert !ale#semver#GTE([2, 9, 1], [3, 0, 0]) + Assert !ale#semver#GTE([3, 2, 3], [3, 3, 3]) + Assert !ale#semver#GTE([3, 3, 2], [3, 3, 3]) + +Execute(GTE should compare pairs correctly): + Assert ale#semver#GTE([3, 0], [3, 0, 0]) + Assert ale#semver#GTE([3, 0], [3, 0]) + Assert ale#semver#GTE([3, 1], [3, 0]) + Assert ale#semver#GTE([3, 1], [3, 0, 0]) + Assert ale#semver#GTE([3, 0, 1], [3, 0]) + Assert !ale#semver#GTE([3, 0], [3, 0, 1]) + Assert !ale#semver#GTE([3, 0], [3, 1]) + Assert !ale#semver#GTE([2, 9, 11], [3, 0]) + +Execute(GTE should permit the LHS to be an empty List): + Assert !ale#semver#GTE([], [0, 0, 0]) From 8bc44ed585db4c704f4c93feab6477f32d8aebe6 Mon Sep 17 00:00:00 2001 From: Jeff Willette Date: Fri, 10 Nov 2017 18:37:23 +0900 Subject: [PATCH 08/23] Added support for linting of proto files (#1098) * Added support for linting of proto files * Added function to get the proper protoc command --- ale_linters/proto/protoc_gen_lint.vim | 19 +++++++++++++++ doc/ale-proto.txt | 24 +++++++++++++++++++ doc/ale.txt | 2 ++ .../test_proto_command_callback.vader | 16 +++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 ale_linters/proto/protoc_gen_lint.vim create mode 100644 doc/ale-proto.txt create mode 100644 test/command_callback/test_proto_command_callback.vader diff --git a/ale_linters/proto/protoc_gen_lint.vim b/ale_linters/proto/protoc_gen_lint.vim new file mode 100644 index 0000000..9d5ceac --- /dev/null +++ b/ale_linters/proto/protoc_gen_lint.vim @@ -0,0 +1,19 @@ +" Author: Jeff Willette +" Description: run the protoc-gen-lint plugin for the protoc binary + +function! ale_linters#proto#protoc_gen_lint#GetCommand(buffer) abort + let l:dirname = expand('#' . a:buffer . ':p:h') + + return 'protoc' + \ . ' -I ' . ale#Escape(l:dirname) + \ . ' --lint_out=. ' . '%s' +endfunction + +call ale#linter#Define('proto', { +\ 'name': 'protoc-gen-lint', +\ 'lint_file': 1, +\ 'output_stream': 'stderr', +\ 'executable': 'protoc', +\ 'command_callback': 'ale_linters#proto#protoc_gen_lint#GetCommand', +\ 'callback': 'ale#handlers#unix#HandleAsError', +\}) diff --git a/doc/ale-proto.txt b/doc/ale-proto.txt new file mode 100644 index 0000000..6a25638 --- /dev/null +++ b/doc/ale-proto.txt @@ -0,0 +1,24 @@ +=============================================================================== +ALE Proto Integration *ale-proto-options* + + +=============================================================================== +Integration Information + +Linting of `.proto` files requires that the `protoc` binary is installed in the +system path and that the `protoc-gen-lint` plugin for the `protoc` binary is also +installed. + +To enable `.proto` file linting, update |g:ale_linters| as appropriate: +> + " Enable linter for .proto files + let g:ale_linters = {'proto': ['protoc-gen-lint']} +< +=============================================================================== +protoc-gen-lint *ale-proto-protoc-gen-lint* + + The linter is a plugin for the `protoc` binary. As long as the binary resides + in the system path, `protoc` will find it. + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index ffe7ac7..9401662 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -135,6 +135,8 @@ CONTENTS *ale-contents* phpstan.............................|ale-php-phpstan| pod...................................|ale-pod-options| write-good..........................|ale-pod-write-good| + proto.................................|ale-proto-options| + protoc-gen-lint.....................|ale-proto-protoc-gen-lint| pug...................................|ale-pug-options| puglint.............................|ale-pug-puglint| puppet................................|ale-puppet-options| diff --git a/test/command_callback/test_proto_command_callback.vader b/test/command_callback/test_proto_command_callback.vader new file mode 100644 index 0000000..2730bb8 --- /dev/null +++ b/test/command_callback/test_proto_command_callback.vader @@ -0,0 +1,16 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('test.proto') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The default command should be correct): + + AssertEqual + \ 'protoc' . ' -I ' . ale#Escape(getcwd()) . ' --lint_out=. ' . '%s', + \ ale_linters#proto#protoc_gen_lint#GetCommand(bufnr('')) + From 4952e2f143d87270292a56fb305fcba633d3d561 Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 10 Nov 2017 09:43:42 +0000 Subject: [PATCH 09/23] #1098 Add protoc-gen-lint to the list of supported tools --- README.md | 1 + doc/ale.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 7c6488c..8decebf 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ formatting. | Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) | | PHP | [hack](http://hacklang.org/), [hackfmt](https://github.com/facebook/flow/tree/master/hack/hackfmt), [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer) | | Pod | [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | +| proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) | | Pug | [pug-lint](https://github.com/pugjs/pug-lint) | | Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | | Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | diff --git a/doc/ale.txt b/doc/ale.txt index 9401662..5533277 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -312,6 +312,7 @@ Notes: * Perl: `perl -c`, `perl-critic` * PHP: `hack`, `hackfmt`, `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf` * Pod: `proselint`, `write-good` +* proto: `protoc-gen-lint` * Pug: `pug-lint` * Puppet: `puppet`, `puppet-lint` * Python: `autopep8`, `flake8`, `isort`, `mypy`, `pycodestyle`, `pyls`, `pylint`!!, `yapf` From 52b60249979ead96df696e82d9ed5ec57db04f0e Mon Sep 17 00:00:00 2001 From: Florian Beeres Date: Sat, 11 Nov 2017 13:07:08 +0100 Subject: [PATCH 10/23] Handle flow extra errors (#946) Show more information for Flow errors with :ALEDetail --- ale_linters/javascript/flow.vim | 54 +++++++++- test/handler/test_flow_handler.vader | 156 ++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 5 deletions(-) mode change 100644 => 100755 ale_linters/javascript/flow.vim diff --git a/ale_linters/javascript/flow.vim b/ale_linters/javascript/flow.vim old mode 100644 new mode 100755 index 6d51628..8dc930c --- a/ale_linters/javascript/flow.vim +++ b/ale_linters/javascript/flow.vim @@ -1,6 +1,9 @@ " Author: Zach Perrault -- @zperrault " Description: FlowType checking for JavaScript files +" Flow extra errors +" Author: Florian Beeres + call ale#Set('javascript_flow_executable', 'flow') call ale#Set('javascript_flow_use_global', 0) @@ -53,6 +56,44 @@ function! s:GetJSONLines(lines) abort return a:lines[l:start_index :] endfunction +function! s:ExtraErrorMsg(current, new) abort + let l:newMsg = '' + + if a:current is# '' + " extra messages appear to already have a : + let l:newMsg = a:new + else + let l:newMsg = a:current . ' ' . a:new + endif + + return l:newMsg +endfunction + + +function! s:GetDetails(error) abort + let l:detail = '' + + for l:extra_error in a:error.extra + + if has_key(l:extra_error, 'message') + for l:extra_message in l:extra_error.message + let l:detail = s:ExtraErrorMsg(l:detail, l:extra_message.descr) + endfor + endif + + if has_key(l:extra_error, 'children') + for l:child in l:extra_error.children + for l:child_message in l:child.message + let l:detail = l:detail . ' ' . l:child_message.descr + endfor + endfor + endif + + endfor + + return l:detail +endfunction + function! ale_linters#javascript#flow#Handle(buffer, lines) abort let l:str = join(s:GetJSONLines(a:lines), '') @@ -91,12 +132,19 @@ function! ale_linters#javascript#flow#Handle(buffer, lines) abort let l:text = l:text . ' See also: ' . l:error.operation.descr endif - call add(l:output, { + let l:errorToAdd = { \ 'lnum': l:line, \ 'col': l:col, \ 'text': l:text, - \ 'type': l:error.level is# 'error' ? 'E' : 'W', - \}) + \ 'type': has_key(l:error, 'level') && l:error.level is# 'error' ? 'E' : 'W', + \} + + if has_key(l:error, 'extra') + let l:errorToAdd.detail = s:GetDetails(l:error) + endif + + call add(l:output, l:errorToAdd) + endfor return l:output diff --git a/test/handler/test_flow_handler.vader b/test/handler/test_flow_handler.vader index 47efc30..3a575a0 100644 --- a/test/handler/test_flow_handler.vader +++ b/test/handler/test_flow_handler.vader @@ -243,7 +243,7 @@ Execute(The flow handler should fetch the correct location for the currently ope \ 'lnum': 6, \ 'col': 3, \ 'type': 'E', - \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`' + \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`', \ } \] @@ -347,7 +347,159 @@ Execute(The flow handler should handle relative paths): \ 'lnum': 6, \ 'col': 3, \ 'type': 'E', - \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`' + \ 'text': 'property `bar`: Property not found in props of React element `Foo` See also: React element `Foo`', + \ } + \] + + AssertEqual g:expected, g:actual + +Execute(The flow handler should handle extra errors): + silent! noautocmd file /Users/rav/Projects/vim-ale-flow/index.js + + let g:flow_output = { + \ "flowVersion": "0.54.0", + \ "errors": [{ + \ "extra": [{ + \ "message": [{ + \ "context": v:null, + \ "descr": "Property \`setVector\` is incompatible:", + \ "type": "Blame ", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }], + \ "children": [{ + \ "message": [{ + \ "context": "setVector = \{2\}", + \ "descr": "number ", + \ "type": "Blame ", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile ", + \ "start": { + \ "line": 90, + \ "column": 30, + \ "offset": 2296 + \ }, + \ "end": { + \ "line": 90, + \ "column": 30, + \ "offset": 2297 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 90, + \ "endline": 90, + \ "start": 30, + \ "end": 30 + \ }, { + \ "context": v:null, + \ "descr": "This type is incompatible with ", + \ "type": "Comment ", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }, { + \ "context": "setVector: VectorType => void,", + \ "descr": "function type ", + \ "type": "Blame ", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile", + \ "start": { + \ "line": 9, + \ "column": 14, + \ "offset": 252 + \ }, + \ "end": { + \ "line": 9, + \ "column": 31, + \ "offset": 270 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 9, + \ "endline": 9, + \ "start": 14, + \ "end": 31 + \ }] + \ }] + \ }], + \ "kind": "infer", + \ "level": "error", + \ "suppressions": [], + \ "message": [{ + \ "context": " < New ", + \ "descr": "props of React element `New`", + \ "type": "Blame", + \ "loc": { + \ "source": "vim-ale-flow/foo.js", + \ "type": "SourceFile", + \ "start": { + \ "line": 89, + \ "column": 17, + \ "offset": 2262 + \ }, + \ "end": { + \ "line": 94, + \ "column": 18, + \ "offset": 2488 + \ } + \ }, + \ "path": "", + \ "line": 89, + \ "endline": 94, + \ "start": 17, + \ "end": 18 + \ }, { + \ "context": v:null, + \ "descr": "This type is incompatible with", + \ "type": "Comment", + \ "path": "", + \ "line": 0, + \ "endline": 0, + \ "start": 1, + \ "end": 0 + \ }, { + \ "context": "class New extends React.Component < NewProps,NewState > {", + \ "descr": "object type", + \ "type": "Blame", + \ "loc": { + \ "source": expand('%:p'), + \ "type": "SourceFile", + \ "start": { + \ "line": 20, + \ "column": 35, + \ "offset": 489 + \ }, + \ "end": { + \ "line": 20, + \ "column": 42, + \ "offset": 497 + \ } + \ }, + \ "path": expand('%:p'), + \ "line": 20, + \ "endline": 20, + \ "start": 35, + \ "end": 42 + \ }] + \ }], + \ "passed": v:false + \} + + let g:actual = ale_linters#javascript#flow#Handle(bufnr(''), [json_encode(g:flow_output)]) + let g:expected = [ + \ { + \ 'lnum': 20, + \ 'col': 35, + \ 'type': 'E', + \ 'text': 'props of React element `New`: This type is incompatible with object type', + \ 'detail': 'Property `setVector` is incompatible: number This type is incompatible with function type ', \ } \] From 6c60ca24c13872c8e2e1e56154eac56e8555452c Mon Sep 17 00:00:00 2001 From: jnduli Date: Sat, 11 Nov 2017 15:10:17 +0300 Subject: [PATCH 11/23] Add rstcheck linter to check for errors in restructured text (#1090) --- README.md | 2 +- ale_linters/rst/rstcheck.vim | 37 +++++++++++++++++++ doc/ale.txt | 2 +- test/handler/test_rstcheck_lint_handler.vader | 33 +++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 ale_linters/rst/rstcheck.vim create mode 100644 test/handler/test_rstcheck_lint_handler.vader diff --git a/README.md b/README.md index 8decebf..285efaa 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ formatting. | Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | | R | [lintr](https://github.com/jimhester/lintr) | | ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-reason-merlin` for configuration instructions, [refmt](https://github.com/reasonml/reason-cli) | -| reStructuredText | [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | +| reStructuredText | [proselint](http://proselint.com/), [rstcheck](https://github.com/myint/rstcheck), [write-good](https://github.com/btford/write-good) | | RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) (disabled by default; see `:help ale-integration-spec`) | | Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org) | | Rust | cargo !! (see `:help ale-integration-rust` for configuration instructions), [rls](https://github.com/rust-lang-nursery/rls), [rustc](https://www.rust-lang.org/), [rustfmt](https://github.com/rust-lang-nursery/rustfmt) | diff --git a/ale_linters/rst/rstcheck.vim b/ale_linters/rst/rstcheck.vim new file mode 100644 index 0000000..b660627 --- /dev/null +++ b/ale_linters/rst/rstcheck.vim @@ -0,0 +1,37 @@ +" Author: John Nduli https://github.com/jnduli +" Description: Rstcheck for reStructuredText files +" + +function! ale_linters#rst#rstcheck#Handle(buffer, lines) abort + " matches: 'bad_rst.rst:1: (SEVERE/4) Title overline & underline + " mismatch.' + let l:pattern = '\v^(.+):(\d*): \(([a-zA-Z]*)/\d*\) (.+)$' + let l:dir = expand('#' . a:buffer . ':p:h') + let l:output = [] + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), + \ 'lnum': l:match[2] + 0, + \ 'col': 0, + \ 'type': l:match[3] is# 'SEVERE' ? 'E' : 'W', + \ 'text': l:match[4], + \}) + endfor + + return l:output +endfunction + +function! ale_linters#rst#rstcheck#GetCommand(buffer) abort + return ale#path#BufferCdString(a:buffer) + \ . 'rstcheck' + \ . ' %t' +endfunction + + +call ale#linter#Define('rst', { +\ 'name': 'rstcheck', +\ 'executable': 'rstcheck', +\ 'command_callback': 'ale_linters#rst#rstcheck#GetCommand', +\ 'callback': 'ale_linters#rst#rstcheck#Handle', +\ 'output_stream': 'both', +\}) diff --git a/doc/ale.txt b/doc/ale.txt index 5533277..a684d70 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -318,7 +318,7 @@ Notes: * Python: `autopep8`, `flake8`, `isort`, `mypy`, `pycodestyle`, `pyls`, `pylint`!!, `yapf` * R: `lintr` * ReasonML: `merlin`, `refmt` -* reStructuredText: `proselint`, `write-good` +* reStructuredText: `proselint`, `rstcheck`, `write-good` * RPM spec: `rpmlint` * Ruby: `brakeman`, `rails_best_practices`!!, `reek`, `rubocop`, `ruby` * Rust: `cargo`!!, `rls`, `rustc` (see |ale-integration-rust|), `rustfmt` diff --git a/test/handler/test_rstcheck_lint_handler.vader b/test/handler/test_rstcheck_lint_handler.vader new file mode 100644 index 0000000..64cb587 --- /dev/null +++ b/test/handler/test_rstcheck_lint_handler.vader @@ -0,0 +1,33 @@ +Before: + runtime ale_linters/rstcheck/rstcheck.vim + +Execute(Warning and error messages should be handled correctly): + AssertEqual + \ [ + \ { + \ 'filename': ale#path#Winify(expand('%:p:h') . '/bad_python.rst'), + \ 'lnum': 7, + \ 'col': 0, + \ 'type': 'W', + \ 'text': '(python) unexpected EOF while parsing', + \ }, + \ { + \ 'filename': ale#path#Winify(expand('%:p:h') . '/bad_cpp.rst'), + \ 'lnum': 9, + \ 'col': 0, + \ 'type': 'W', + \ 'text': '(cpp) error: ''x'' was not declared in this scope', + \ }, + \ { + \ 'filename': ale#path#Winify(expand('%:p:h') . '/bad_rst.rst'), + \ 'lnum': 1, + \ 'col': 0, + \ 'type': 'E', + \ 'text': 'Title overline & underline mismatch.', + \ }, + \ ], + \ ale_linters#rst#rstcheck#Handle(1, [ + \ 'bad_python.rst:7: (ERROR/3) (python) unexpected EOF while parsing', + \ 'bad_cpp.rst:9: (ERROR/3) (cpp) error: ''x'' was not declared in this scope', + \ 'bad_rst.rst:1: (SEVERE/4) Title overline & underline mismatch.', + \]) From 8cd1ccff844bb8a23a616c09cf55a9f40018e41f Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 11 Nov 2017 13:44:05 +0000 Subject: [PATCH 12/23] #1095 Apply all patterns for g:ale_pattern_options, instead of just the first match --- autoload/ale/pattern_options.vim | 40 +++++++++++------- autoload/ale/toggle.vim | 5 +-- doc/ale.txt | 14 ++++--- test/test_autocmd_commands.vader | 25 ++++++++---- test/test_pattern_options.vader | 70 ++++++++++++++++++++++++++------ 5 files changed, 111 insertions(+), 43 deletions(-) diff --git a/autoload/ale/pattern_options.vim b/autoload/ale/pattern_options.vim index a603c98..a55a27f 100644 --- a/autoload/ale/pattern_options.vim +++ b/autoload/ale/pattern_options.vim @@ -1,22 +1,34 @@ " Author: w0rp " Description: Set options in files based on regex patterns. -function! ale#pattern_options#SetOptions() abort - let l:filename = expand('%:p') " no-custom-checks - let l:options = {} +function! s:CmpPatterns(left_item, right_item) abort + if a:left_item[0] < a:right_item[0] + return -1 + endif - for l:pattern in keys(g:ale_pattern_options) + if a:left_item[0] > a:right_item[0] + return 1 + endif + + return 0 +endfunction + +function! ale#pattern_options#SetOptions(buffer) abort + if !g:ale_pattern_options_enabled || empty(g:ale_pattern_options) + return + endif + + let l:filename = expand('#' . a:buffer . ':p') + + " The patterns are sorted, so they are applied consistently. + for [l:pattern, l:options] in sort( + \ items(g:ale_pattern_options), + \ function('s:CmpPatterns') + \) if match(l:filename, l:pattern) >= 0 - let l:options = g:ale_pattern_options[l:pattern] - break - endif - endfor - - for l:key in keys(l:options) - if l:key[:0] is# '&' - call setbufvar(bufnr(''), l:key, l:options[l:key]) - else - let b:[l:key] = l:options[l:key] + for [l:key, l:value] in items(l:options) + call setbufvar(a:buffer, l:key, l:value) + endfor endif endfor endfunction diff --git a/autoload/ale/toggle.vim b/autoload/ale/toggle.vim index 6809edd..aa6d113 100644 --- a/autoload/ale/toggle.vim +++ b/autoload/ale/toggle.vim @@ -4,9 +4,7 @@ function! ale#toggle#InitAuGroups() abort augroup ALEPatternOptionsGroup autocmd! - if g:ale_enabled && g:ale_pattern_options_enabled - autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions() - endif + autocmd BufEnter,BufRead * call ale#pattern_options#SetOptions(str2nr(expand(''))) augroup END augroup ALERunOnTextChangedGroup @@ -71,7 +69,6 @@ function! ale#toggle#InitAuGroups() abort augroup END if !g:ale_enabled - augroup! ALEPatternOptionsGroup augroup! ALERunOnTextChangedGroup augroup! ALERunOnEnterGroup augroup! ALERunOnInsertLeave diff --git a/doc/ale.txt b/doc/ale.txt index a684d70..6d2e2c6 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1009,11 +1009,14 @@ g:ale_pattern_options *g:ale_pattern_options* only `eslint` for checking those files by setting `b:ale_linters`. Filenames are matched with |match()|, and patterns depend on the |magic| - setting, unless prefixed with the special escape sequences like `'\v'`, etc. - - The patterns can match any part of a filename. The absolute path of the + setting, unless prefixed with the special escape sequences like `'\v'`, + etc.The patterns can match any part of a filename. The absolute path of the filename will be used for matching, taken from `expand('%:p')`. + The options for every match for the filename will be applied, with the + pattern keys sorted in alphabetical order. Options for `'zebra'` will + override the options for `'alpha'` for a filename `alpha-zebra`. + g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* @@ -1021,8 +1024,9 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled* Default: `!empty(g:ale_pattern_options)` This option can be used for turning the behaviour of setting - |g:ale_pattern_options| on or off. By default, setting a single key - for |g:ale_pattern_options| will turn this option on. + |g:ale_pattern_options| on or off. By default, setting a single key for + |g:ale_pattern_options| will turn this option on, as long as the setting is + configured before ALE is loaded. g:ale_set_balloons *g:ale_set_balloons* diff --git a/test/test_autocmd_commands.vader b/test/test_autocmd_commands.vader index 88504a9..e7e9e86 100644 --- a/test/test_autocmd_commands.vader +++ b/test/test_autocmd_commands.vader @@ -108,17 +108,28 @@ Execute (g:ale_lint_on_insert_leave = 0 should bind no events): AssertEqual [], CheckAutocmd('ALERunOnInsertLeave') -Execute (g:ale_pattern_options_enabled = 0 should bind no events): - let g:ale_pattern_options_enabled = 0 - - AssertEqual [], CheckAutocmd('ALEPatternOptionsGroup') - Execute (g:ale_pattern_options_enabled = 1 should bind BufReadPost and BufEnter): let g:ale_pattern_options_enabled = 1 AssertEqual [ - \ 'BufEnter * call ale#pattern_options#SetOptions()', - \ 'BufReadPost * call ale#pattern_options#SetOptions()', + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \], CheckAutocmd('ALEPatternOptionsGroup') + +Execute (g:ale_pattern_options_enabled = 0 should still bind events): + let g:ale_pattern_options_enabled = 0 + + AssertEqual [ + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \], CheckAutocmd('ALEPatternOptionsGroup') + +Execute (g:ale_enabled = 0 should still bind pattern events): + let g:ale_enabled = 0 + + AssertEqual [ + \ 'BufEnter * call ale#pattern_options#SetOptions(str2nr(expand('''')))', + \ 'BufReadPost * call ale#pattern_options#SetOptions(str2nr(expand('''')))', \], CheckAutocmd('ALEPatternOptionsGroup') Execute (g:ale_lint_on_enter = 0 should bind only the BufEnter event): diff --git a/test/test_pattern_options.vader b/test/test_pattern_options.vader index 164e5aa..0ad4415 100644 --- a/test/test_pattern_options.vader +++ b/test/test_pattern_options.vader @@ -3,30 +3,74 @@ Before: Save g:ale_pattern_options_enabled Save &filetype + let g:ale_pattern_options_enabled = 1 + let g:ale_pattern_options = {} + + let b:ale_enabled = 0 + let b:some_option = 0 + + call ale#test#SetDirectory('/testplugin/test') + After: Restore unlet! b:ale_enabled unlet! b:some_option + call ale#test#RestoreDirectory() + +Execute(The pattern options function should work when there are no patterns): + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + Execute(Buffer variables should be set when filename patterns match): - let g:ale_pattern_options = {'baz.*\.js': { - \ 'ale_enabled': 1, - \ 'some_option': 347, - \ '&filetype': 'pattern_option_set_filetype', - \}} + let g:ale_pattern_options = { + \ 'baz.*\.js': { + \ 'ale_enabled': 1, + \ 'some_option': 347, + \ '&filetype': 'pattern_option_set_filetype', + \ }, + \} - silent! file foobar.js + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) - call ale#pattern_options#SetOptions() + AssertEqual 0, b:ale_enabled + AssertEqual 0, b:some_option - Assert !exists('b:ale_enabled') - Assert !exists('b:some_option') - - silent! file bazboz.js - - call ale#pattern_options#SetOptions() + call ale#test#SetFilename('bazboz.js') + call ale#pattern_options#SetOptions(bufnr('')) AssertEqual 1, b:ale_enabled AssertEqual 347, b:some_option AssertEqual 'pattern_option_set_filetype', &filetype + +Execute(Multiple pattern matches should be applied): + let g:ale_pattern_options = { + \ 'foo': { + \ 'some_option': 666, + \ }, + \ 'bar': { + \ 'ale_enabled': 1, + \ 'some_option': 123, + \ }, + \ 'notmatched': { + \ 'some_option': 489, + \ 'ale_enabled': 0, + \ }, + \} + + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 1, b:ale_enabled + AssertEqual 666, b:some_option + +Execute(Patterns should not be applied when the setting is disabled): + let g:ale_pattern_options_enabled = 0 + let g:ale_pattern_options = {'foo': {'some_option': 666}} + + call ale#test#SetFilename('foobar.js') + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 0, b:some_option From 555743a2baf7ca9cb00ccef91979c2e422cac49f Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 11 Nov 2017 14:26:54 +0000 Subject: [PATCH 13/23] #1095 Cache the sorting of patterns for g:ale_pattern_options --- autoload/ale/pattern_options.vim | 20 +++++++++++++++----- test/test_pattern_options.vader | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/autoload/ale/pattern_options.vim b/autoload/ale/pattern_options.vim index a55a27f..e58b8cf 100644 --- a/autoload/ale/pattern_options.vim +++ b/autoload/ale/pattern_options.vim @@ -1,6 +1,10 @@ " Author: w0rp " Description: Set options in files based on regex patterns. +" These variables are used to cache the sorting of patterns below. +let s:last_pattern_options = {} +let s:sorted_items = [] + function! s:CmpPatterns(left_item, right_item) abort if a:left_item[0] < a:right_item[0] return -1 @@ -18,13 +22,19 @@ function! ale#pattern_options#SetOptions(buffer) abort return endif + " The items will only be sorted whenever the patterns change. + if g:ale_pattern_options != s:last_pattern_options + let s:last_pattern_options = deepcopy(g:ale_pattern_options) + " The patterns are sorted, so they are applied consistently. + let s:sorted_items = sort( + \ items(g:ale_pattern_options), + \ function('s:CmpPatterns') + \) + endif + let l:filename = expand('#' . a:buffer . ':p') - " The patterns are sorted, so they are applied consistently. - for [l:pattern, l:options] in sort( - \ items(g:ale_pattern_options), - \ function('s:CmpPatterns') - \) + for [l:pattern, l:options] in s:sorted_items if match(l:filename, l:pattern) >= 0 for [l:key, l:value] in items(l:options) call setbufvar(a:buffer, l:key, l:value) diff --git a/test/test_pattern_options.vader b/test/test_pattern_options.vader index 0ad4415..0e26eaa 100644 --- a/test/test_pattern_options.vader +++ b/test/test_pattern_options.vader @@ -74,3 +74,19 @@ Execute(Patterns should not be applied when the setting is disabled): call ale#pattern_options#SetOptions(bufnr('')) AssertEqual 0, b:some_option + +" This test is important for making sure we update the sorted items. +Execute(Patterns should be applied after the Dictionary changes): + call ale#test#SetFilename('foobar.js') + + let g:ale_pattern_options = {} + + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 0, b:some_option + + let g:ale_pattern_options['foo'] = {'some_option': 666} + + call ale#pattern_options#SetOptions(bufnr('')) + + AssertEqual 666, b:some_option From 8e0d1f57c6f568a6845aa11a57add84a8f771b68 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 11 Nov 2017 14:27:53 +0000 Subject: [PATCH 14/23] Fix a typo --- doc/ale.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ale.txt b/doc/ale.txt index 6d2e2c6..c78ba3b 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -884,7 +884,7 @@ g:ale_linter_aliases *g:ale_linter_aliases* `let g:ale_linter_aliases = {'html': ['html', 'javascript', 'css']}` Note that `html` itself was included as an alias. That is because aliases - will override the original linters for the aliased filetepe. + will override the original linters for the aliased filetype. Linter aliases can be configured in each buffer with buffer-local variables. ALE will first look for aliases for filetypes in the `b:ale_linter_aliases` From 39107a48b99607fdebd1708cfaf0f115768c4899 Mon Sep 17 00:00:00 2001 From: Michael Jungo Date: Sat, 11 Nov 2017 19:27:41 +0100 Subject: [PATCH 15/23] Add ocaml-language-server for OCaml and ReasonML --- README.md | 4 ++-- ale_linters/ocaml/ols.vim | 14 ++++++++++++++ ale_linters/reason/ols.vim | 14 ++++++++++++++ autoload/ale/handlers/ols.vim | 25 +++++++++++++++++++++++++ doc/ale-ocaml.txt | 22 ++++++++++++++++++++++ doc/ale-reasonml.txt | 23 +++++++++++++++++++++++ doc/ale.txt | 6 ++++-- 7 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 ale_linters/ocaml/ols.vim create mode 100644 ale_linters/reason/ols.vim create mode 100644 autoload/ale/handlers/ols.vim diff --git a/README.md b/README.md index 285efaa..24f3deb 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ formatting. | nroff | [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good)| | Objective-C | [clang](http://clang.llvm.org/) | | Objective-C++ | [clang](http://clang.llvm.org/) | -| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions | +| OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-ocaml-merlin` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server) | | Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) | | PHP | [hack](http://hacklang.org/), [hackfmt](https://github.com/facebook/flow/tree/master/hack/hackfmt), [langserver](https://github.com/felixfbecker/php-language-server), [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions, [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan), [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer) | | Pod | [proselint](http://proselint.com/), [write-good](https://github.com/btford/write-good) | @@ -130,7 +130,7 @@ formatting. | Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | | Python | [autopep8](https://github.com/hhatto/autopep8), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | | R | [lintr](https://github.com/jimhester/lintr) | -| ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-reason-merlin` for configuration instructions, [refmt](https://github.com/reasonml/reason-cli) | +| ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-reason-merlin` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [refmt](https://github.com/reasonml/reason-cli) | | reStructuredText | [proselint](http://proselint.com/), [rstcheck](https://github.com/myint/rstcheck), [write-good](https://github.com/btford/write-good) | | RPM spec | [rpmlint](https://github.com/rpm-software-management/rpmlint) (disabled by default; see `:help ale-integration-spec`) | | Ruby | [brakeman](http://brakemanscanner.org/) !!, [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) !!, [reek](https://github.com/troessner/reek), [rubocop](https://github.com/bbatsov/rubocop), [ruby](https://www.ruby-lang.org) | diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim new file mode 100644 index 0000000..c0255a6 --- /dev/null +++ b/ale_linters/ocaml/ols.vim @@ -0,0 +1,14 @@ +" Author: Michael Jungo +" Description: A language server for OCaml + +call ale#Set('ocaml_ols_executable', 'ocaml-language-server') +call ale#Set('ocaml_ols_use_global', 0) + +call ale#linter#Define('ocaml', { +\ 'name': 'ols', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale#handlers#ols#GetExecutable', +\ 'command_callback': 'ale#handlers#ols#GetCommand', +\ 'language_callback': 'ale#handlers#ols#GetLanguage', +\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot', +\}) diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim new file mode 100644 index 0000000..b2cd5f7 --- /dev/null +++ b/ale_linters/reason/ols.vim @@ -0,0 +1,14 @@ +" Author: Michael Jungo +" Description: A language server for Reason + +call ale#Set('reason_ols_executable', 'ocaml-language-server') +call ale#Set('reason_ols_use_global', 0) + +call ale#linter#Define('reason', { +\ 'name': 'ols', +\ 'lsp': 'stdio', +\ 'executable_callback': 'ale#handlers#ols#GetExecutable', +\ 'command_callback': 'ale#handlers#ols#GetCommand', +\ 'language_callback': 'ale#handlers#ols#GetLanguage', +\ 'project_root_callback': 'ale#handlers#ols#GetProjectRoot', +\}) diff --git a/autoload/ale/handlers/ols.vim b/autoload/ale/handlers/ols.vim new file mode 100644 index 0000000..1dda7f9 --- /dev/null +++ b/autoload/ale/handlers/ols.vim @@ -0,0 +1,25 @@ +" Author: Michael Jungo +" Description: Handlers for the OCaml language server + +function! ale#handlers#ols#GetExecutable(buffer) abort + let l:ols_setting = ale#handlers#ols#GetLanguage(a:buffer) . '_ols' + return ale#node#FindExecutable(a:buffer, l:ols_setting, [ + \ 'node_modules/.bin/ocaml-language-server', + \]) +endfunction + +function! ale#handlers#ols#GetCommand(buffer) abort + let l:executable = ale#handlers#ols#GetExecutable(a:buffer) + + return ale#node#Executable(a:buffer, l:executable) . ' --stdio' +endfunction + +function! ale#handlers#ols#GetLanguage(buffer) abort + return getbufvar(a:buffer, '&filetype') +endfunction + +function! ale#handlers#ols#GetProjectRoot(buffer) abort + let l:merlin_file = ale#path#FindNearestFile(a:buffer, '.merlin') + + return !empty(l:merlin_file) ? fnamemodify(l:merlin_file, ':h') : '' +endfunction diff --git a/doc/ale-ocaml.txt b/doc/ale-ocaml.txt index 093d911..cfa365a 100644 --- a/doc/ale-ocaml.txt +++ b/doc/ale-ocaml.txt @@ -10,6 +10,28 @@ merlin *ale-ocaml-merlin* detailed instructions (https://github.com/the-lambda-church/merlin/wiki/vim-from-scratch). +=============================================================================== +ols *ale-ocaml-ols* + + The `ocaml-language-server` is the engine that powers OCaml and ReasonML + editor support using the Language Server Protocol. See the installation + instructions: + https://github.com/freebroccolo/ocaml-language-server#installation + +g:ale_ocaml_ols_executable *g:ale_ocaml_ols_executable* + *b:ale_ocaml_ols_executable* + Type: |String| + Default: `'ocaml-language-server'` + + This variable can be set to change the executable path for `ols`. + +g:ale_ocaml_ols_use_global *g:ale_ocaml_ols_use_global* + *b:ale_ocaml_ols_use_global* + Type: |String| + Default: `0` + + This variable can be set to `1` to always use the globally installed + executable. See also |ale-integrations-local-executables|. =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-reasonml.txt b/doc/ale-reasonml.txt index d533d85..36ddd75 100644 --- a/doc/ale-reasonml.txt +++ b/doc/ale-reasonml.txt @@ -10,6 +10,29 @@ merlin *ale-reasonml-merlin* detailed instructions (https://github.com/the-lambda-church/merlin/wiki/vim-from-scratch). +=============================================================================== +ols *ale-reasonml-ols* + + The `ocaml-language-server` is the engine that powers OCaml and ReasonML + editor support using the Language Server Protocol. See the installation + instructions: + https://github.com/freebroccolo/ocaml-language-server#installation + +g:ale_reason_ols_executable *g:ale_reason_ols_executable* + *b:ale_reason_ols_executable* + Type: |String| + Default: `'ocaml-language-server'` + + This variable can be set to change the executable path for `ols`. + +g:ale_reason_ols_use_global *g:ale_reason_ols_use_global* + *b:ale_reason_ols_use_global* + Type: |String| + Default: `0` + + This variable can be set to `1` to always use the globally installed + executable. See also |ale-integrations-local-executables|. + =============================================================================== refmt *ale-reasonml-refmt* diff --git a/doc/ale.txt b/doc/ale.txt index c78ba3b..83a724f 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -121,6 +121,7 @@ CONTENTS *ale-contents* clang...............................|ale-objcpp-clang| ocaml.................................|ale-ocaml-options| merlin..............................|ale-ocaml-merlin| + ols.................................|ale-ocaml-ols| perl..................................|ale-perl-options| perl................................|ale-perl-perl| perlcritic..........................|ale-perl-perlcritic| @@ -154,6 +155,7 @@ CONTENTS *ale-contents* lintr...............................|ale-r-lintr| reasonml..............................|ale-reasonml-options| merlin..............................|ale-reasonml-merlin| + ols.................................|ale-reasonml-ols| refmt...............................|ale-reasonml-refmt| restructuredtext......................|ale-restructuredtext-options| write-good..........................|ale-restructuredtext-write-good| @@ -308,7 +310,7 @@ Notes: * nroff: `proselint`, `write-good` * Objective-C: `clang` * Objective-C++: `clang` -* OCaml: `merlin` (see |ale-ocaml-merlin|) +* OCaml: `merlin` (see |ale-ocaml-merlin|), `ols` * Perl: `perl -c`, `perl-critic` * PHP: `hack`, `hackfmt`, `langserver`, `phan`, `php -l`, `phpcs`, `phpmd`, `phpstan`, `phpcbf` * Pod: `proselint`, `write-good` @@ -317,7 +319,7 @@ Notes: * Puppet: `puppet`, `puppet-lint` * Python: `autopep8`, `flake8`, `isort`, `mypy`, `pycodestyle`, `pyls`, `pylint`!!, `yapf` * R: `lintr` -* ReasonML: `merlin`, `refmt` +* ReasonML: `merlin`, `ols`, `refmt` * reStructuredText: `proselint`, `rstcheck`, `write-good` * RPM spec: `rpmlint` * Ruby: `brakeman`, `rails_best_practices`!!, `reek`, `rubocop`, `ruby` From 5df6ce6bb82e6a042eff644d77c71537e375114a Mon Sep 17 00:00:00 2001 From: Michael Jungo Date: Sat, 11 Nov 2017 19:28:24 +0100 Subject: [PATCH 16/23] Remove id from LSP notifications --- autoload/ale/lsp.vim | 3 +-- test/lsp/test_lsp_connections.vader | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index b6c890c..126d6c1 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -83,9 +83,8 @@ function! ale#lsp#CreateMessageData(message) abort let l:is_notification = a:message[0] let l:obj = { - \ 'id': v:null, - \ 'jsonrpc': '2.0', \ 'method': a:message[1], + \ 'jsonrpc': '2.0', \} if !l:is_notification diff --git a/test/lsp/test_lsp_connections.vader b/test/lsp/test_lsp_connections.vader index 5549b1f..8651d80 100644 --- a/test/lsp/test_lsp_connections.vader +++ b/test/lsp/test_lsp_connections.vader @@ -26,7 +26,7 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 1, \ "Content-Length: 79\r\n\r\n" - \ . '{"id": 1, "jsonrpc": "2.0", "method": "someMethod", "params": {"foo": "barÜ"}}', + \ . '{"method": "someMethod", "jsonrpc": "2.0", "id": 1, "params": {"foo": "barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) " Check again to ensure that we use the next ID. @@ -34,7 +34,7 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 2, \ "Content-Length: 79\r\n\r\n" - \ . '{"id": 2, "jsonrpc": "2.0", "method": "someMethod", "params": {"foo": "barÜ"}}', + \ . '{"method": "someMethod", "jsonrpc": "2.0", "id": 2, "params": {"foo": "barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) else @@ -42,14 +42,14 @@ Execute(ale#lsp#CreateMessageData() should create an appropriate message): \ [ \ 1, \ "Content-Length: 71\r\n\r\n" - \ . '{"id":1,"jsonrpc":"2.0","method":"someMethod","params":{"foo":"barÜ"}}', + \ . '{"method":"someMethod","jsonrpc":"2.0","id":1,"params":{"foo":"barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) AssertEqual \ [ \ 2, \ "Content-Length: 71\r\n\r\n" - \ . '{"id":2,"jsonrpc":"2.0","method":"someMethod","params":{"foo":"barÜ"}}', + \ . '{"method":"someMethod","jsonrpc":"2.0","id":2,"params":{"foo":"barÜ"}}', \ ], \ ale#lsp#CreateMessageData([0, 'someMethod', {'foo': 'barÜ'}]) endif @@ -60,7 +60,7 @@ Execute(ale#lsp#CreateMessageData() should create messages without params): \ [ \ 1, \ "Content-Length: 56\r\n\r\n" - \ . '{"id": 1, "jsonrpc": "2.0", "method": "someOtherMethod"}', + \ . '{"method": "someOtherMethod", "jsonrpc": "2.0", "id": 1}', \ ], \ ale#lsp#CreateMessageData([0, 'someOtherMethod']) else @@ -68,7 +68,7 @@ Execute(ale#lsp#CreateMessageData() should create messages without params): \ [ \ 1, \ "Content-Length: 51\r\n\r\n" - \ . '{"id":1,"jsonrpc":"2.0","method":"someOtherMethod"}', + \ . '{"method":"someOtherMethod","jsonrpc":"2.0","id":1}', \ ], \ ale#lsp#CreateMessageData([0, 'someOtherMethod']) endif @@ -78,30 +78,30 @@ Execute(ale#lsp#CreateMessageData() should create notifications): AssertEqual \ [ \ 0, - \ "Content-Length: 60\r\n\r\n" - \ . '{"id": null, "jsonrpc": "2.0", "method": "someNotification"}', + \ "Content-Length: 48\r\n\r\n" + \ . '{"method": "someNotification", "jsonrpc": "2.0"}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification']) AssertEqual \ [ \ 0, - \ "Content-Length: 86\r\n\r\n" - \ . '{"id": null, "jsonrpc": "2.0", "method": "someNotification", "params": {"foo": "bar"}}', + \ "Content-Length: 74\r\n\r\n" + \ . '{"method": "someNotification", "jsonrpc": "2.0", "params": {"foo": "bar"}}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification', {'foo': 'bar'}]) else AssertEqual \ [ \ 0, - \ "Content-Length: 55\r\n\r\n" - \ . '{"id":null,"jsonrpc":"2.0","method":"someNotification"}', + \ "Content-Length: 45\r\n\r\n" + \ . '{"method":"someNotification","jsonrpc":"2.0"}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification']) AssertEqual \ [ \ 0, - \ "Content-Length: 78\r\n\r\n" - \ . '{"id":null,"jsonrpc":"2.0","method":"someNotification","params":{"foo":"bar"}}', + \ "Content-Length: 68\r\n\r\n" + \ . '{"method":"someNotification","jsonrpc":"2.0","params":{"foo":"bar"}}', \ ], \ ale#lsp#CreateMessageData([1, 'someNotification', {'foo': 'bar'}]) endif From 365d023d0e5094b474b91d2ad72244ec5a13a08c Mon Sep 17 00:00:00 2001 From: Eddie Lebow Date: Sat, 11 Nov 2017 15:15:19 -0500 Subject: [PATCH 17/23] perlcritic: all issues are warnings Perlcritic is a style checker, not a syntax validator. This change was originally proposed by @RsrchBoy in https://github.com/w0rp/ale/pull/784. --- ale_linters/perl/perlcritic.vim | 1 + test/handler/test_perlcritic_handler.vader | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 test/handler/test_perlcritic_handler.vader diff --git a/ale_linters/perl/perlcritic.vim b/ale_linters/perl/perlcritic.vim index df2f8b2..24f7eb8 100644 --- a/ale_linters/perl/perlcritic.vim +++ b/ale_linters/perl/perlcritic.vim @@ -61,6 +61,7 @@ function! ale_linters#perl#perlcritic#Handle(buffer, lines) abort \ 'lnum': l:match[1], \ 'col': l:match[2], \ 'text': l:match[3], + \ 'type': 'W' \}) endfor diff --git a/test/handler/test_perlcritic_handler.vader b/test/handler/test_perlcritic_handler.vader new file mode 100644 index 0000000..f00b07d --- /dev/null +++ b/test/handler/test_perlcritic_handler.vader @@ -0,0 +1,20 @@ +Before: + runtime ale_linters/perl/perlcritic.vim + +After: + call ale#linter#Reset() + +Execute(The Perl::Critic handler should create all issues as warnings): + AssertEqual + \ [ + \ { + \ 'lnum': '21', + \ 'col': '17', + \ 'text': 'Regular expression without "/m" flag', + \ 'type': 'W', + \ } + \ ], + \ ale_linters#perl#perlcritic#Handle(99, [ + \ '21:17 Regular expression without "/m" flag' + \ ]) + From 099df0af522fddda09b50fd9ffe1f66cab310607 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 11 Nov 2017 23:04:08 +0000 Subject: [PATCH 18/23] #1108 Support selecting fixers with Lists --- autoload/ale/fix.vim | 27 +++++++++++++++++---------- doc/ale.txt | 19 +++++++++++++++++-- test/test_ale_fix.vader | 9 +++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index a57ad19..5a42b74 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -332,18 +332,25 @@ function! s:RunFixer(options) abort endfunction function! s:GetCallbacks() abort - let l:fixers = ale#Var(bufnr(''), 'fixers') - let l:callback_list = [] + if type(get(b:, 'ale_fixers')) is type([]) + " Lists can be used for buffer-local variables only + let l:callback_list = b:ale_fixers + else + " buffer and global options can use dictionaries mapping filetypes to + " callbacks to run. + let l:fixers = ale#Var(bufnr(''), 'fixers') + let l:callback_list = [] - for l:sub_type in split(&filetype, '\.') - let l:sub_type_callacks = get(l:fixers, l:sub_type, []) + for l:sub_type in split(&filetype, '\.') + let l:sub_type_callacks = get(l:fixers, l:sub_type, []) - if type(l:sub_type_callacks) == type('') - call add(l:callback_list, l:sub_type_callacks) - else - call extend(l:callback_list, l:sub_type_callacks) - endif - endfor + if type(l:sub_type_callacks) == type('') + call add(l:callback_list, l:sub_type_callacks) + else + call extend(l:callback_list, l:sub_type_callacks) + endif + endfor + endif if empty(l:callback_list) return [] diff --git a/doc/ale.txt b/doc/ale.txt index 83a724f..b070414 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -492,6 +492,18 @@ upon some lines immediately, then run `eslint` from the ALE registry, and then call a lambda function which will remove every single line comment from the file. +For buffer-local settings, such as in |g:ale_pattern_options| or in ftplugin +files, a |List| may be used for configuring the fixers instead. +> + " Same as the above, only a List can be used instead of a Dictionary. + let b:ale_fixers = [ + \ 'DoSomething', + \ 'eslint', + \ {buffer, lines -> filter(lines, 'v:val !=~ ''^\s*//''')}, + \] + + ALEFix +< For convenience, a plug mapping is defined for |ALEFix|, so you can set up a keybind easily for fixing files. > @@ -696,6 +708,8 @@ g:ale_fixers *g:ale_fixers* See |ale-fix| for more information. This variable can be overridden with variables in each buffer. + `b:ale_fixers` can be set to a |List| of callbacks instead, which can be + more convenient. g:ale_fix_on_save *g:ale_fix_on_save* @@ -1001,14 +1015,15 @@ g:ale_pattern_options *g:ale_pattern_options* buffer variables. This option can be set to automatically configure different settings for different files. For example: > + " Use just ESLint for linting and fixing files which end in '.foo.js' let g:ale_pattern_options = { \ '\.foo\.js$': { \ 'ale_linters': {'javascript': ['eslint']}, + \ 'ale_fixers: ['eslint'], \ }, \} < - The above example will match any filename ending in `.foo.js`, and use - only `eslint` for checking those files by setting `b:ale_linters`. + See |b:ale_linters| and |b:ale_fixers| for information for those options. Filenames are matched with |match()|, and patterns depend on the |magic| setting, unless prefixed with the special escape sequences like `'\v'`, diff --git a/test/test_ale_fix.vader b/test/test_ale_fix.vader index ffe3d93..ac6427a 100644 --- a/test/test_ale_fix.vader +++ b/test/test_ale_fix.vader @@ -330,6 +330,15 @@ Expect(There should be only two lines): a b +Execute(ALEFix should allow Lists to be used for buffer-local fixer settings): + let g:ale_fixers.testft = ['AddCarets', 'AddDollars'] + let b:ale_fixers = ['RemoveLastLine'] + ALEFix + +Expect(There should be only two lines): + a + b + Given testft (A file with three lines): a b From b98387d0fa9452a70f383ac19deb4d109ddf38ec Mon Sep 17 00:00:00 2001 From: w0rp Date: Sat, 11 Nov 2017 23:55:04 +0000 Subject: [PATCH 19/23] #1108 Support using Lists and 'all' for b:ale_linters --- autoload/ale/linter.vim | 18 +++++++++++++----- doc/ale.txt | 16 +++++++++++++++- test/test_linter_retrieval.vader | 24 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 269b092..00ab916 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -289,11 +289,19 @@ function! ale#linter#ResolveFiletype(original_filetype) abort endfunction function! s:GetLinterNames(original_filetype) abort - for l:dict in [ - \ get(b:, 'ale_linters', {}), - \ g:ale_linters, - \ s:default_ale_linters, - \] + let l:buffer_ale_linters = get(b:, 'ale_linters', {}) + + " b:ale_linters can be set to 'all' + if l:buffer_ale_linters is# 'all' + return 'all' + endif + + " b:ale_linters can be set to a List. + if type(l:buffer_ale_linters) is type([]) + return l:buffer_ale_linters + endif + + for l:dict in [l:buffer_ale_linters, g:ale_linters, s:default_ale_linters] if has_key(l:dict, a:original_filetype) return l:dict[a:original_filetype] endif diff --git a/doc/ale.txt b/doc/ale.txt index b070414..6d6a449 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -946,6 +946,20 @@ g:ale_linters *g:ale_linters* will first look for linters for filetypes in the `b:ale_linters` variable, then `g:ale_linters`, and then a default Dictionary. + `b:ale_linters` can be set to a List, or the string `'all'`. When linters + for two different filetypes share the same name, the first linter loaded + will be used. Any ambiguity can be resolved by using a Dictionary specifying + which linter to run for which filetype instead. > + + " Use ESLint for the buffer if the filetype includes 'javascript'. + let b:ale_linters = {'javascript': ['eslint'], 'html': ['tidy']} + " Use a List for the same setting. This will work in most cases. + let b:ale_linters = ['eslint', 'tidy'] + " Disable all linters for the buffer. + let b:ale_linters = [] + " Explicitly enable all available linters for the filetype. + let b:ale_linters = 'all' +< g:ale_max_buffer_history_size *g:ale_max_buffer_history_size* @@ -1018,7 +1032,7 @@ g:ale_pattern_options *g:ale_pattern_options* " Use just ESLint for linting and fixing files which end in '.foo.js' let g:ale_pattern_options = { \ '\.foo\.js$': { - \ 'ale_linters': {'javascript': ['eslint']}, + \ 'ale_linters': ['eslint'], \ 'ale_fixers: ['eslint'], \ }, \} diff --git a/test/test_linter_retrieval.vader b/test/test_linter_retrieval.vader index 1a1e258..265738f 100644 --- a/test/test_linter_retrieval.vader +++ b/test/test_linter_retrieval.vader @@ -42,6 +42,30 @@ Execute (You should be able to select linters with a buffer option): AssertEqual [g:testlinter1], ale#linter#Get('testft') +Execute (b:ale_linters should work when set to a List): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1', 'testlinter2']} + let b:ale_linters = ['testlinter1'] + + AssertEqual [g:testlinter1], ale#linter#Get('testft') + +Execute (b:ale_linters should disable all linters when set to an empty List): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1', 'testlinter2']} + let b:ale_linters = [] + + AssertEqual [], ale#linter#Get('testft') + +Execute (b:ale_linters should enable all available linters when set to 'all'): + call ale#linter#Define('testft', g:testlinter1) + call ale#linter#Define('testft', g:testlinter2) + let g:ale_linters = {'testft': ['testlinter1']} + let b:ale_linters = 'all' + + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft') + Execute (Buffer settings shouldn't completely replace global settings): call ale#linter#Define('testft', g:testlinter1) call ale#linter#Define('testft', g:testlinter2) From d20e3bc71cd835f6708e78c5efea2f6d826d7e4d Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 12 Nov 2017 00:11:50 +0000 Subject: [PATCH 20/23] #1108 Support setting b:ale_linter_aliases to a List --- autoload/ale/linter.vim | 9 ++++++++- doc/ale.txt | 6 ++++++ test/test_linter_retrieval.vader | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 00ab916..c6667d9 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -261,12 +261,19 @@ function! ale#linter#GetAll(filetypes) abort endfunction function! s:GetAliasedFiletype(original_filetype) abort + let l:buffer_aliases = get(b:, 'ale_linter_aliases', {}) + + " b:ale_linter_aliases can be set to a List. + if type(l:buffer_aliases) is type([]) + return l:buffer_aliases + endif + " Check for aliased filetypes first in a buffer variable, " then the global variable, " then in the default mapping, " otherwise use the original filetype. for l:dict in [ - \ get(b:, 'ale_linter_aliases', {}), + \ l:buffer_aliases, \ g:ale_linter_aliases, \ s:default_ale_linter_aliases, \] diff --git a/doc/ale.txt b/doc/ale.txt index 6d6a449..41e9661 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -906,6 +906,12 @@ g:ale_linter_aliases *g:ale_linter_aliases* ALE will first look for aliases for filetypes in the `b:ale_linter_aliases` variable, then `g:ale_linter_aliases`, and then a default Dictionary. + `b:ale_linter_aliases` can be set to a |List|, to tell ALE to load the + linters for specific filetypes for a given buffer. > + + let b:ale_linter_aliases = ['html', 'javascript', 'css'] +< + No linters will be loaded when the buffer's filetype is empty. g:ale_linters *g:ale_linters* *b:ale_linters* diff --git a/test/test_linter_retrieval.vader b/test/test_linter_retrieval.vader index 265738f..5d5b582 100644 --- a/test/test_linter_retrieval.vader +++ b/test/test_linter_retrieval.vader @@ -126,6 +126,21 @@ Execute (The local alias option shouldn't completely replace the global one): " global Dictionary. let b:ale_linter_aliases = {'testft3': ['testft1']} +Execute (Lists should be accepted for local aliases): + call ale#linter#Define('testft1', g:testlinter1) + call ale#linter#Define('testft2', g:testlinter2) + let g:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} + " We should load the testft2 linters for this buffer, with no duplicates. + let b:ale_linter_aliases = ['testft2'] + + AssertEqual [g:testlinter2], ale#linter#Get('anything.else') + +Execute (Buffer-local overrides for aliases should be used): + call ale#linter#Define('testft1', g:testlinter1) + call ale#linter#Define('testft2', g:testlinter2) + let g:ale_linter_aliases = {'testft1': ['testft2']} + let b:ale_linter_aliases = {'testft1': ['testft1', 'testft2']} + AssertEqual [g:testlinter1, g:testlinter2], ale#linter#Get('testft1') Execute (Linters should be loaded from disk appropriately): From 3aff1df9615ccc95bb91910f82d43947dd835f28 Mon Sep 17 00:00:00 2001 From: Michael Jungo Date: Sun, 12 Nov 2017 02:06:28 +0100 Subject: [PATCH 21/23] Add tests for ocaml-language-server callbacks --- test/command_callback/ols_paths/.merlin | 0 .../node_modules/.bin/ocaml-language-server | 0 .../test_ocaml_ols_callbacks.vader | 54 +++++++++++++++++++ .../test_reason_ols_callbacks.vader | 54 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 test/command_callback/ols_paths/.merlin create mode 100644 test/command_callback/ols_paths/node_modules/.bin/ocaml-language-server create mode 100644 test/command_callback/test_ocaml_ols_callbacks.vader create mode 100644 test/command_callback/test_reason_ols_callbacks.vader diff --git a/test/command_callback/ols_paths/.merlin b/test/command_callback/ols_paths/.merlin new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/ols_paths/node_modules/.bin/ocaml-language-server b/test/command_callback/ols_paths/node_modules/.bin/ocaml-language-server new file mode 100644 index 0000000..e69de29 diff --git a/test/command_callback/test_ocaml_ols_callbacks.vader b/test/command_callback/test_ocaml_ols_callbacks.vader new file mode 100644 index 0000000..2c44dbc --- /dev/null +++ b/test/command_callback/test_ocaml_ols_callbacks.vader @@ -0,0 +1,54 @@ +Before: + Save &filetype + Save g:ale_ocaml_ols_executable + Save g:ale_ocaml_ols_use_global + + let &filetype = 'ocaml' + unlet! g:ale_ocaml_ols_executable + unlet! g:ale_ocaml_ols_use_global + + runtime ale_linters/ocaml/ols.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The language string should be correct): + AssertEqual 'ocaml', ale#handlers#ols#GetLanguage(bufnr('')) + +Execute(The default executable should be correct): + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('ocaml-language-server') . ' --stdio', + \ ale#handlers#ols#GetCommand(bufnr('')) + +Execute(The project root should be detected correctly): + AssertEqual '', ale#handlers#ols#GetProjectRoot(bufnr('')) + + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual + \ ale#path#Winify(g:dir . '/ols_paths'), + \ ale#handlers#ols#GetProjectRoot(bufnr('')) + +Execute(The local executable should be used when available): + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual + \ ale#path#Winify(g:dir . '/ols_paths/node_modules/.bin/ocaml-language-server'), + \ ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The gloabl executable should always be used when use_global is set): + let g:ale_ocaml_ols_use_global = 1 + call ale#test#SetFilename('ols_paths/file.ml') + + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The executable should be configurable): + let g:ale_ocaml_ols_executable = 'foobar' + + AssertEqual 'foobar', ale#handlers#ols#GetExecutable(bufnr('')) diff --git a/test/command_callback/test_reason_ols_callbacks.vader b/test/command_callback/test_reason_ols_callbacks.vader new file mode 100644 index 0000000..ffe403f --- /dev/null +++ b/test/command_callback/test_reason_ols_callbacks.vader @@ -0,0 +1,54 @@ +Before: + Save &filetype + Save g:ale_reason_ols_executable + Save g:ale_reason_ols_use_global + + let &filetype = 'reason' + unlet! g:ale_reason_ols_executable + unlet! g:ale_reason_ols_use_global + + runtime ale_linters/reason/ols.vim + + call ale#test#SetDirectory('/testplugin/test/command_callback') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The language string should be correct): + AssertEqual 'reason', ale#handlers#ols#GetLanguage(bufnr('')) + +Execute(The default executable should be correct): + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + AssertEqual + \ ale#Escape('ocaml-language-server') . ' --stdio', + \ ale#handlers#ols#GetCommand(bufnr('')) + +Execute(The project root should be detected correctly): + AssertEqual '', ale#handlers#ols#GetProjectRoot(bufnr('')) + + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual + \ ale#path#Winify(g:dir . '/ols_paths'), + \ ale#handlers#ols#GetProjectRoot(bufnr('')) + +Execute(The local executable should be used when available): + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual + \ ale#path#Winify(g:dir . '/ols_paths/node_modules/.bin/ocaml-language-server'), + \ ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The gloabl executable should always be used when use_global is set): + let g:ale_reason_ols_use_global = 1 + call ale#test#SetFilename('ols_paths/file.re') + + AssertEqual 'ocaml-language-server', ale#handlers#ols#GetExecutable(bufnr('')) + +Execute(The executable should be configurable): + let g:ale_reason_ols_executable = 'foobar' + + AssertEqual 'foobar', ale#handlers#ols#GetExecutable(bufnr('')) From cd5da50531d55c003de495d8b151c60c5dbf26eb Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 12 Nov 2017 11:25:24 +0000 Subject: [PATCH 22/23] Add tests for the command and executable callbacks, and make them use local node_modulse esxecutables like other linters --- ale_linters/less/lessc.vim | 25 ++++-- doc/ale-less.txt | 18 +++- doc/ale.txt | 2 +- .../lessc_paths/node_modules/.bin/lessc | 0 .../test_lessc_command_callback.vader | 82 +++++++++++++++++++ 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100755 test/command_callback/lessc_paths/node_modules/.bin/lessc create mode 100644 test/command_callback/test_lessc_command_callback.vader diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index 76b7f13..8b7985a 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -1,12 +1,25 @@ -" Author: zanona +" Author: zanona , w0rp " Description: This file adds support for checking Less code with lessc. +call ale#Set('less_lessc_executable', 'lessc') call ale#Set('less_lessc_options', '') +call ale#Set('less_lessc_use_global', 0) + +function! ale_linters#less#lessc#GetExecutable(buffer) abort + return ale#node#FindExecutable(a:buffer, 'less_lessc', [ + \ 'node_modules/.bin/lessc', + \]) +endfunction function! ale_linters#less#lessc#GetCommand(buffer) abort - return 'lessc' - \ . ' --no-color --lint --include-path=' . expand('%:p:h') - \ . ' ' . ale#Var(a:buffer, 'less_lessc_options') + let l:executable = ale_linters#less#lessc#GetExecutable(a:buffer) + let l:dir = expand('#' . a:buffer . ':p:h') + let l:options = ale#Var(a:buffer, 'less_lessc_options') + + return ale#Escape(l:executable) + \ . ' --no-color --lint' + \ . ' --include-path=' . ale#Escape(l:dir) + \ . (!empty(l:options) ? ' ' . l:options : '') \ . ' -' endfunction @@ -29,8 +42,8 @@ endfunction call ale#linter#Define('less', { \ 'name': 'lessc', -\ 'executable': 'lessc', -\ 'output_stream': 'stderr', +\ 'executable_callback': 'ale_linters#less#lessc#GetExecutable', \ 'command_callback': 'ale_linters#less#lessc#GetCommand', \ 'callback': 'ale_linters#less#lessc#Handle', +\ 'output_stream': 'stderr', \}) diff --git a/doc/ale-less.txt b/doc/ale-less.txt index cac9c9a..a372afe 100644 --- a/doc/ale-less.txt +++ b/doc/ale-less.txt @@ -5,6 +5,14 @@ ALE Less Integration *ale-less-options* =============================================================================== lessc *ale-less-lessc* +g:ale_less_lessc_executable *g:ale_less_lessc_executable* + *b:ale_less_lessc_executable* + Type: |String| + Default: `'lessc'` + + See |ale-integrations-local-executables| + + g:ale_less_lessc_options *g:ale_less_lessc_options* *b:ale_less_lessc_options* Type: |String| @@ -13,6 +21,14 @@ g:ale_less_lessc_options *g:ale_less_lessc_options* This variable can be set to pass additional options to lessc. +g:ale_less_lessc_use_global *g:ale_less_lessc_use_global* + *b:ale_less_lessc_use_global* + Type: |String| + Default: `0` + + See |ale-integrations-local-executables| + + =============================================================================== prettier *ale-less-prettier* @@ -20,4 +36,4 @@ See |ale-javascript-prettier| for information about the available options. =============================================================================== - + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index 41e9661..ff38cbb 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -105,7 +105,7 @@ CONTENTS *ale-contents* latex.................................|ale-latex-options| write-good..........................|ale-latex-write-good| less..................................|ale-less-options| - lessc...............................|ale-less-lessc-options| + lessc...............................|ale-less-lessc| prettier............................|ale-less-prettier| llvm..................................|ale-llvm-options| llc.................................|ale-llvm-llc| diff --git a/test/command_callback/lessc_paths/node_modules/.bin/lessc b/test/command_callback/lessc_paths/node_modules/.bin/lessc new file mode 100755 index 0000000..e69de29 diff --git a/test/command_callback/test_lessc_command_callback.vader b/test/command_callback/test_lessc_command_callback.vader new file mode 100644 index 0000000..785c38c --- /dev/null +++ b/test/command_callback/test_lessc_command_callback.vader @@ -0,0 +1,82 @@ +Before: + Save g:ale_less_lessc_executable + Save g:ale_less_lessc_use_global + Save g:ale_less_lessc_options + + unlet! b:executable + + unlet! g:ale_less_lessc_executable + unlet! g:ale_less_lessc_use_global + unlet! g:ale_less_lessc_options + + call ale#test#SetDirectory('/testplugin/test/command_callback') + call ale#test#SetFilename('testfile.less') + + runtime ale_linters/less/lessc.vim + +After: + Restore + + unlet! b:executable + unlet! b:ale_less_lessc_executable + unlet! b:ale_less_lessc_use_global + unlet! b:ale_less_lessc_options + + call ale#test#SetFilename('test.txt') + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(node_modules directories should be discovered): + call ale#test#SetFilename('lessc_paths/nested/testfile.less') + + let b:executable = ale#path#Winify( + \ g:dir + \ . '/lessc_paths/node_modules/.bin/lessc' + \) + + AssertEqual + \ b:executable, + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape(b:executable) + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Winify(g:dir . '/lessc_paths/nested')) + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) + +Execute(The global override should work): + let b:ale_less_lessc_executable = 'foobar' + let b:ale_less_lessc_use_global = 1 + + call ale#test#SetFilename('lessc_paths/nested/testfile.less') + + AssertEqual + \ 'foobar', + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('foobar') + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Winify(g:dir . '/lessc_paths/nested')) + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) + +Execute(Extra options should be configurable): + let b:ale_less_lessc_options = '--whatever' + + AssertEqual + \ 'lessc', + \ ale_linters#less#lessc#GetExecutable(bufnr('')) + + AssertEqual + \ ale#Escape('lessc') + \ . ' --no-color --lint' + \ . ' --include-path=' + \ . ale#Escape(ale#path#Winify(g:dir)) + \ . ' --whatever' + \ . ' -', + \ ale_linters#less#lessc#GetCommand(bufnr('')) From 7edcb2210b2b60d5eaf81381e5d9443369576c28 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 12 Nov 2017 11:35:01 +0000 Subject: [PATCH 23/23] Show problems from other files for lessc --- ale_linters/less/lessc.vim | 11 ++++- test/handler/test_lessc_handler.vader | 69 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test/handler/test_lessc_handler.vader diff --git a/ale_linters/less/lessc.vim b/ale_linters/less/lessc.vim index 8b7985a..108679d 100755 --- a/ale_linters/less/lessc.vim +++ b/ale_linters/less/lessc.vim @@ -24,17 +24,24 @@ function! ale_linters#less#lessc#GetCommand(buffer) abort endfunction function! ale_linters#less#lessc#Handle(buffer, lines) abort + let l:dir = expand('#' . a:buffer . ':p:h') " Matches patterns like the following: let l:pattern = '^\(\w\+\): \(.\{-}\) in \(.\{-}\) on line \(\d\+\), column \(\d\+\):$' let l:output = [] for l:match in ale#util#GetMatches(a:lines, l:pattern) - call add(l:output, { + let l:item = { \ 'lnum': l:match[4] + 0, \ 'col': l:match[5] + 0, \ 'text': l:match[2], \ 'type': 'E', - \}) + \} + + if l:match[3] isnot# '-' + let l:item.filename = ale#path#GetAbsPath(l:dir, l:match[3]) + endif + + call add(l:output, l:item) endfor return l:output diff --git a/test/handler/test_lessc_handler.vader b/test/handler/test_lessc_handler.vader new file mode 100644 index 0000000..530c582 --- /dev/null +++ b/test/handler/test_lessc_handler.vader @@ -0,0 +1,69 @@ +Before: + call ale#test#SetDirectory('/testplugin/test/handler') + call ale#test#SetFilename('testfile.less') + + runtime ale_linters/less/lessc.vim + +After: + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(The lessc handler should handle errors for the current file correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in - on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \]) + +Execute(The lessc handler should handle errors for other files in the same directory correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Winify(g:dir . '/imported.less') + \ }, + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Winify(g:dir . '/imported.less') + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in imported.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \ 'ParseError: Unrecognised input. Possibly missing something in ./imported.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \]) + +Execute(The lessc handler should handle errors for files in directories above correctly): + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 1, + \ 'type': 'E', + \ 'text': 'Unrecognised input. Possibly missing something', + \ 'filename': ale#path#Winify(g:dir . '/../imported2.less') + \ }, + \ ], + \ ale_linters#less#lessc#Handle(bufnr(''), [ + \ 'ParseError: Unrecognised input. Possibly missing something in ../imported2.less on line 2, column 1:', + \ '1 vwewww', + \ '2 ', + \])