From 75d241342532e5acf34caff28a7a9e5cda688a32 Mon Sep 17 00:00:00 2001 From: "Reza J. Bavaghoush" Date: Mon, 16 May 2022 14:14:11 +0200 Subject: [PATCH] add rego support (#4199) * add opa fmt fixer for rego files * add opa linter * add basic tests for linter and fixer * add cspell to the docs --- ale_linters/rego/cspell.vim | 4 ++ ale_linters/rego/opacheck.vim | 56 +++++++++++++++++++ autoload/ale/fix/registry.vim | 5 ++ autoload/ale/fixers/opafmt.vim | 15 +++++ doc/ale-rego.txt | 50 +++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 4 ++ doc/ale.txt | 4 ++ supported-tools.md | 4 ++ test/fixers/test_opa_fmt_fixer_callback.vader | 33 +++++++++++ test/linter/test_rego_opacheck.vader | 16 ++++++ 10 files changed, 191 insertions(+) create mode 100644 ale_linters/rego/cspell.vim create mode 100644 ale_linters/rego/opacheck.vim create mode 100644 autoload/ale/fixers/opafmt.vim create mode 100644 doc/ale-rego.txt create mode 100644 test/fixers/test_opa_fmt_fixer_callback.vader create mode 100644 test/linter/test_rego_opacheck.vader diff --git a/ale_linters/rego/cspell.vim b/ale_linters/rego/cspell.vim new file mode 100644 index 00000000..a54a5379 --- /dev/null +++ b/ale_linters/rego/cspell.vim @@ -0,0 +1,4 @@ +scriptencoding utf-8 +" Description: cspell support for rego files. + +call ale#handlers#cspell#DefineLinter('rego') diff --git a/ale_linters/rego/opacheck.vim b/ale_linters/rego/opacheck.vim new file mode 100644 index 00000000..77d8c93a --- /dev/null +++ b/ale_linters/rego/opacheck.vim @@ -0,0 +1,56 @@ +" Description: opa check for rego files + +call ale#Set('rego_opacheck_executable', 'opa') +call ale#Set('rego_opacheck_options', '') + +function! ale_linters#rego#opacheck#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'rego_opacheck_executable') +endfunction + +function! ale_linters#rego#opacheck#GetCommand(buffer) abort + let l:options = ale#Var(a:buffer, 'rego_opacheck_options') + + return ale#Escape(ale_linters#rego#opacheck#GetExecutable(a:buffer)) + \ . ' check %s --format json ' + \ . (!empty(l:options) ? ' ' . l:options : '') +endfunction + +function! ale_linters#rego#opacheck#Handle(buffer, lines) abort + let l:output = [] + + let l:errors = ale#util#FuzzyJSONDecode(a:lines, {'errors': []}) + let l:dir = expand('#' . a:buffer . ':p:h') + let l:file = expand('#' . a:buffer . ':p') + + for l:error in l:errors['errors'] + if has_key(l:error, 'location') + call add(l:output, { + \ 'filename': ale#path#GetAbsPath(l:dir, l:error['location']['file']), + \ 'lnum': l:error['location']['row'], + \ 'col': l:error['location']['col'], + \ 'text': l:error['message'], + \ 'code': l:error['code'], + \ 'type': 'E', + \}) + else + call add(l:output, { + \ 'filename': l:file, + \ 'lnum': 0, + \ 'col': 0, + \ 'text': l:error['message'], + \ 'code': l:error['code'], + \ 'type': 'E', + \}) + endif + endfor + + return l:output +endfunction + +call ale#linter#Define('rego', { +\ 'name': 'opacheck', +\ 'output_stream': 'both', +\ 'executable': function('ale_linters#rego#opacheck#GetExecutable'), +\ 'command': function('ale_linters#rego#opacheck#GetCommand'), +\ 'callback': 'ale_linters#rego#opacheck#Handle', +\}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 6ac617d8..6932ad9d 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -526,6 +526,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['pascal'], \ 'description': 'Fix Pascal files with ptop.', \ }, +\ 'opafmt': { +\ 'function': 'ale#fixers#opafmt#Fix', +\ 'suggested_filetypes': ['rego'], +\ 'description': 'Fix rego files with opa fmt.', +\ }, \ 'vfmt': { \ 'function': 'ale#fixers#vfmt#Fix', \ 'suggested_filetypes': ['v'], diff --git a/autoload/ale/fixers/opafmt.vim b/autoload/ale/fixers/opafmt.vim new file mode 100644 index 00000000..a0999b70 --- /dev/null +++ b/autoload/ale/fixers/opafmt.vim @@ -0,0 +1,15 @@ +" Description: Fixer for rego files + +call ale#Set('opa_fmt_executable', 'opa') +call ale#Set('opa_fmt_options', '') + +function! ale#fixers#opafmt#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'opa_fmt_executable') + let l:options = ale#Var(a:buffer, 'opa_fmt_options') + + return { + \ 'command': ale#Escape(l:executable) + \ . ' fmt' + \ . (empty(l:options) ? '' : ' ' . l:options) + \} +endfunction diff --git a/doc/ale-rego.txt b/doc/ale-rego.txt new file mode 100644 index 00000000..9a39dbf0 --- /dev/null +++ b/doc/ale-rego.txt @@ -0,0 +1,50 @@ +=============================================================================== +ALE Rego Integration *ale-rego-options* + + +=============================================================================== +cspell *ale-rego-cspell* + +See |ale-cspell-options| + + +=============================================================================== +opacheck *ale-rego-opa-check* + +g:ale_rego_opacheck_executable *g:rego_opacheck_executable* + *b:rego_opacheck_executable* + + Type: |String| + Default: `'opa'` + + This variable can be changed to use a different executable for opa. + + +g:rego_opacheck_options *g:rego_opacheck_options* + *b:rego_opacheck_options* + Type: |String| + Default: `''` + + This variable can be changed to pass custom CLI flags to opa check. + + +=============================================================================== +opafmt *ale-rego-opa-fmt-fixer* + +g:ale_opa_fmt_executable *g:ale_opa_fmt_executable* + *b:ale_opa_fmt_executable* + + Type: |String| + Default: `'opa'` + + This variable can be changed to use a different executable for opa. + + +g:ale_opa_fmt_options *g:ale_opa_fmt_options* + *b:ale_opa_fmt_options* + Type: |String| + Default: `''` + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 07d71826..e4c021c3 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -489,6 +489,10 @@ Notes: * `ols` * `reason-language-server` * `refmt` +* Rego + * `cspell` + * `opacheck` + * `opafmt` * reStructuredText * `alex` * `cspell` diff --git a/doc/ale.txt b/doc/ale.txt index 63145438..5430affb 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3128,6 +3128,10 @@ documented in additional help files. ols...................................|ale-reasonml-ols| reason-language-server................|ale-reasonml-language-server| refmt.................................|ale-reasonml-refmt| + rego....................................|ale-rego-options| + cspell................................|ale-rego-cspell| + opacheck..............................|ale-rego-opa-check| + opafmt................................|ale-rego-opa-fmt-fixer| restructuredtext........................|ale-restructuredtext-options| cspell................................|ale-restructuredtext-cspell| textlint..............................|ale-restructuredtext-textlint| diff --git a/supported-tools.md b/supported-tools.md index 94dbe4fc..a79d2294 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -498,6 +498,10 @@ formatting. * [ols](https://github.com/freebroccolo/ocaml-language-server) * [reason-language-server](https://github.com/jaredly/reason-language-server) * [refmt](https://github.com/reasonml/reason-cli) +* Rego + * [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell) + * [opacheck](https://www.openpolicyagent.org/docs/latest/cli/#opa-check) + * [opafmt](https://www.openpolicyagent.org/docs/latest/cli/#opa-fmt) * reStructuredText * [alex](https://github.com/get-alex/alex) * [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell) diff --git a/test/fixers/test_opa_fmt_fixer_callback.vader b/test/fixers/test_opa_fmt_fixer_callback.vader new file mode 100644 index 00000000..3b112b2e --- /dev/null +++ b/test/fixers/test_opa_fmt_fixer_callback.vader @@ -0,0 +1,33 @@ +Before: + Save g:ale_opa_fmt_executable + Save g:ale_opa_fmt_options + + " Use an invalid global executable, so we don't match it. + let g:ale_opa_fmt_executable = 'xxxinvalid' + let g:ale_opa_fmt_options = '' + + call ale#test#SetDirectory('/testplugin/test/fixers') + +After: + Restore + + call ale#test#RestoreDirectory() + +Execute(The opa fmt callback should return the correct default values): + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') . ' fmt', + \ }, + \ ale#fixers#opafmt#Fix(bufnr('')) + +Execute(The opa fmt callback should include custom options): + let g:ale_opa_fmt_options = "--list" + + AssertEqual + \ { + \ 'command': ale#Escape('xxxinvalid') + \ . ' fmt' + \ . ' ' . g:ale_opa_fmt_options + \ }, + \ ale#fixers#opafmt#Fix(bufnr('')) diff --git a/test/linter/test_rego_opacheck.vader b/test/linter/test_rego_opacheck.vader new file mode 100644 index 00000000..886a9339 --- /dev/null +++ b/test/linter/test_rego_opacheck.vader @@ -0,0 +1,16 @@ + +" Based upon :help ale-development +Before: + call ale#assert#SetUpLinterTest('rego', 'opacheck') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'opa', + \ ale#Escape('opa') . ' check %s --format json ' + +Execute(The default command should be overriden): + let b:ale_rego_opacheck_executable = '/bin/other/opa' + AssertLinter '/bin/other/opa', + \ ale#Escape('/bin/other/opa') . ' check %s --format json '