From 655f0070cd2ce575f81092d1faac739fd116b515 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 26 Mar 2021 03:38:57 -0300 Subject: [PATCH] Add support for V: "v" (compiler) and "vfmt" fixer. (#3622) * v: add "v fmt" fixer. * v: add "v" (build) linter. * v: fix vlint complaints and add documentation. * v: add tests. * v: use ale#Pad(). --- ale_linters/v/v.vim | 82 ++++++++++++++++++++++ autoload/ale/fix/registry.vim | 5 ++ autoload/ale/fixers/vfmt.vim | 13 ++++ autoload/ale/linter.vim | 1 + doc/ale-supported-languages-and-tools.txt | 3 + doc/ale-v.txt | 45 ++++++++++++ doc/ale.txt | 4 ++ supported-tools.md | 3 + test/fixers/test_vfmt_fixer_callback.vader | 44 ++++++++++++ test/handler/test_v_handler.vader | 54 ++++++++++++++ test/linter/test_v_command_callback.vader | 25 +++++++ test/v_files/testfile.v | 0 12 files changed, 279 insertions(+) create mode 100644 ale_linters/v/v.vim create mode 100644 autoload/ale/fixers/vfmt.vim create mode 100644 doc/ale-v.txt create mode 100644 test/fixers/test_vfmt_fixer_callback.vader create mode 100644 test/handler/test_v_handler.vader create mode 100644 test/linter/test_v_command_callback.vader create mode 100644 test/v_files/testfile.v diff --git a/ale_linters/v/v.vim b/ale_linters/v/v.vim new file mode 100644 index 00000000..afa98c56 --- /dev/null +++ b/ale_linters/v/v.vim @@ -0,0 +1,82 @@ +" Author: fiatjaf +" Description: v build for V files + +call ale#Set('v_v_executable', 'v') +call ale#Set('v_v_options', '') + +function! ale_linters#v#v#GetCommand(buffer) abort + let l:options = ale#Var(a:buffer, 'v_v_options') + + " Run v in local directory with relative path + let l:command = ale#Var(a:buffer, 'v_v_executable') + \ . ale#Pad(l:options) + \ . ' .' . ' -o /tmp/vim-ale-v' + + return l:command +endfunction + +function! ale_linters#v#v#Handler(buffer, lines) abort + let l:dir = expand('#' . a:buffer . ':p:h') + let l:output = [] + + " Matches patterns like the following: + " + " ./const.v:4:3: warning: const names cannot contain uppercase letters, use snake_case instead + " 2 | + " 3 | const ( + " 4 | BUTTON_TEXT = 'OK' + " | ~~~~~~~~~~~ + " 5 | ) + " ./main.v:4:8: warning: module 'os' is imported but never used + " 2 | + " 3 | import ui + " 4 | import os + " | ~~ + " 5 | + " 6 | const ( + " ./main.v:20:10: error: undefined ident: `win_widt` + " 18 | mut app := &App{} + " 19 | app.window = ui.window({ + " 20 | width: win_widt + " | ~~~~~~~~ + " 21 | height: win_height + " 22 | title: 'Counter' + let l:current = {} + + for l:line in a:lines + " matches basic error description + let l:match = matchlist(l:line, + \ '\([^:]\+\):\([^:]\+\):\([^:]\+\): \([^:]\+\): \(.*\)') + + if !empty(l:match) + let l:current = { + \ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]), + \ 'lnum': l:match[2] + 0, + \ 'col': l:match[3] + 0, + \ 'text': l:match[5], + \ 'type': l:match[4] is# 'error' ? 'E' : 'W', + \} + call add(l:output, l:current) + continue + endif + + " try to get information about the ending column + let l:tildematch = matchstr(l:line, '\~\+') + + if !empty(l:tildematch) + let l:current['end_col'] = l:current['col'] + len(l:tildematch) + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('v', { +\ 'name': 'v', +\ 'aliases': [], +\ 'executable': {b -> ale#Var(b, 'v_v_executable')}, +\ 'command': function('ale_linters#v#v#GetCommand'), +\ 'output_stream': 'stderr', +\ 'callback': 'ale_linters#v#v#Handler', +\ 'lint_file': 1, +\}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 632bc890..0cf866dc 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -440,6 +440,11 @@ let s:default_registry = { \ 'function': 'ale#fixers#ptop#Fix', \ 'suggested_filetypes': ['pascal'], \ 'description': 'Fix Pascal files with ptop.', +\ }, +\ 'vfmt': { +\ 'function': 'ale#fixers#vfmt#Fix', +\ 'suggested_filetypes': ['v'], +\ 'description': 'A formatter for V source code.', \ } \} diff --git a/autoload/ale/fixers/vfmt.vim b/autoload/ale/fixers/vfmt.vim new file mode 100644 index 00000000..2e780318 --- /dev/null +++ b/autoload/ale/fixers/vfmt.vim @@ -0,0 +1,13 @@ +" Author: fiatjaf +" Description: Integration of `v fmt` with ALE. + +call ale#Set('v_vfmt_options', '') + +function! ale#fixers#vfmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'v_v_executable') + let l:options = ale#Var(a:buffer, 'v_vfmt_options') + + return { + \ 'command': ale#Escape(l:executable) . ' fmt' . ale#Pad(l:options) + \} +endfunction diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index d7b6a1ac..d26b9443 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -53,6 +53,7 @@ let s:default_ale_linters = { \ 'text': [], \ 'vue': ['eslint', 'vls'], \ 'zsh': ['shell'], +\ 'v': ['v'], \} " Testing/debugging helper to unload all linters. diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index fe19ae63..e0e0a6a3 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -534,6 +534,9 @@ Notes: * `tslint` * `tsserver` * `typecheck` +* V + * `v` + * `vfmt` * VALA * `uncrustify` * `vala_lint`!! diff --git a/doc/ale-v.txt b/doc/ale-v.txt new file mode 100644 index 00000000..8c641447 --- /dev/null +++ b/doc/ale-v.txt @@ -0,0 +1,45 @@ +=============================================================================== +ALE V Integration *ale-v-options* + + +=============================================================================== +Integration Information + +`v` is V's build tool. `vfmt` (called as `v fmt` from the same +executable that does the builds) is the autoformatter/fixer. + +g:ale_v_v_executable *g:ale_v_v_executable* + *b:ale_v_v_executable* + + Type: |String| + Default: `'v'` + + The executable that will be run for the `v` linter and the `vfmt` fixer. + + +=============================================================================== +v *ale-v-v* + +g:ale_v_v_options *g:ale_v_v_options* + *b:ale_v_v_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the v linter. + They are injected directly after "v .". + + +=============================================================================== +vfmt *ale-v-vfmt* + +g:ale_v_vfmt_options *g:ale_v_vfmt_options* + *b:ale_v_vfmt_options* + Type: |String| + Default: `''` + + This variable can be set to pass additional options to the vfmt fixer. + They are injected directly after "v fmt". + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index 81162348..84d4b096 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1593,6 +1593,7 @@ g:ale_linters *g:ale_linters* \ 'text': [], \ 'vue': ['eslint', 'vls'], \ 'zsh': ['shell'], + \ 'v': ['v'], \} < This option can be used to enable only a particular set of linters for a @@ -3051,6 +3052,9 @@ documented in additional help files. tslint................................|ale-typescript-tslint| tsserver..............................|ale-typescript-tsserver| xo....................................|ale-typescript-xo| + v.......................................|ale-v-options| + v.....................................|ale-v-v| + vfmt..................................|ale-v-vfmt| vala....................................|ale-vala-options| uncrustify............................|ale-vala-uncrustify| verilog/systemverilog...................|ale-verilog-options| diff --git a/supported-tools.md b/supported-tools.md index 4a557538..da0222d1 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -543,6 +543,9 @@ formatting. * [tslint](https://github.com/palantir/tslint) * [tsserver](https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29) * typecheck +* V + * [v](https://github.com/vlang/v/) + * [vfmt](https://github.com/vlang/v/) * VALA * [uncrustify](https://github.com/uncrustify/uncrustify) * [vala_lint](https://github.com/vala-lang/vala-lint) :floppy_disk: diff --git a/test/fixers/test_vfmt_fixer_callback.vader b/test/fixers/test_vfmt_fixer_callback.vader new file mode 100644 index 00000000..cbab1189 --- /dev/null +++ b/test/fixers/test_vfmt_fixer_callback.vader @@ -0,0 +1,44 @@ +Before: + Save g:ale_v_v_executable + Save g:ale_v_vfmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_v_v_executable = 'xxxinvalid' + let g:ale_v_vfmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The vfmt callback should return the correct default values): + call ale#test#SetFilename('../v_files/testfile.v') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') . ' fmt', + \ }, + \ ale#fixers#vfmt#Fix(bufnr('')) + +Execute(The vfmt callback should include custom vfmt options): + let g:ale_v_vfmt_options = "-r '(a) -> a'" + + call ale#test#SetFilename('../v_files/testfile.v') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' fmt ' . g:ale_v_vfmt_options, + \ }, + \ ale#fixers#vfmt#Fix(bufnr('')) + +Execute(The vfmt callback should support Go environment variables): + call ale#test#SetFilename('../v_files/testfile.v') + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') . ' fmt', + \ }, + \ ale#fixers#vfmt#Fix(bufnr('')) diff --git a/test/handler/test_v_handler.vader b/test/handler/test_v_handler.vader new file mode 100644 index 00000000..4d6e3d9b --- /dev/null +++ b/test/handler/test_v_handler.vader @@ -0,0 +1,54 @@ +Before: + runtime ale_linters/v/v.vim + +After: + call ale#linter#Reset() + +Execute (The v handler should correctly parse error messages): + AssertEqual + \ [{ + \ 'lnum': 4, + \ 'col': 3, + \ 'filename': ale#path#GetAbsPath(expand('%:p:h'), 'const ants.v'), + \ 'type': 'W', + \ 'end_col': 14, + \ 'text': 'const names cannot contain uppercase letters, use snake_case instead' + \ }, + \ { + \ 'lnum': 4, + \ 'col': 8, + \ 'filename': ale#path#GetAbsPath(expand('%:p:h'), 'main.v'), + \ 'type': 'W', + \ 'end_col': 10, + \ 'text': 'module "os" is imported but never used' + \ }, + \ { + \ 'lnum': 20, + \ 'col': 10, + \ 'filename': ale#path#GetAbsPath(expand('%:p:h'), 'main.v'), + \ 'type': 'E', + \ 'end_col': 18, + \ 'text': 'undefined ident: `win_widt`' + \ }], + \ ale_linters#v#v#Handler('', [ + \ './const ants.v:4:3: warning: const names cannot contain uppercase letters, use snake_case instead', + \ ' 2 |', + \ ' 3 | const (', + \ ' 4 | BUTTON_TEXT = "OK"', + \ ' | ~~~~~~~~~~~', + \ ' 5 | )', + \ './main.v:4:8: warning: module "os" is imported but never used', + \ ' 2 |', + \ ' 3 | import ui', + \ ' 4 | import os', + \ ' | ~~', + \ ' 5 |', + \ ' 6 | const (', + \ './main.v:20:10: error: undefined ident: `win_widt`', + \ ' 18 | mut app := &App{}', + \ ' 19 | app.window = ui.window({', + \ ' 20 | width: win_widt', + \ ' | ~~~~~~~~', + \ ' 21 | height: win_height', + \ ' 22 | title: "Counter"', + \ ]) diff --git a/test/linter/test_v_command_callback.vader b/test/linter/test_v_command_callback.vader new file mode 100644 index 00000000..17f24ad7 --- /dev/null +++ b/test/linter/test_v_command_callback.vader @@ -0,0 +1,25 @@ +Before: + Save g:ale_v_v_executable + + call ale#assert#SetUpLinterTest('v', 'v') + + GivenCommandOutput ['/foo/bar', '/foo/baz'] + +After: + Restore + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'v', 'v . -o /tmp/vim-ale-v' + +Execute(Extra options should be supported): + let g:ale_v_v_options = '--foo-bar' + + AssertLinter 'v', 'v --foo-bar . -o /tmp/vim-ale-v' + + let g:ale_v_vbuild_options = '' + +Execute(The executable should be configurable): + let g:ale_v_v_executable = 'foobar' + + AssertLinter 'foobar', 'foobar . -o /tmp/vim-ale-v' diff --git a/test/v_files/testfile.v b/test/v_files/testfile.v new file mode 100644 index 00000000..e69de29b