From cad8f8e68338221e157adb1fffbe8eb16c430fe0 Mon Sep 17 00:00:00 2001 From: Yining Date: Tue, 6 Dec 2022 23:26:09 +1100 Subject: add: support for refurb as a Python linter (#4379) this commit adds refurb as a Python linter, together with some tests and documentation. it should fix issue: #4362 refurb repo: https://github.com/dosisod/refurb --- ale_linters/python/refurb.vim | 73 +++++++++++++++++++ doc/ale-python.txt | 61 ++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/linter/test_refurb.vader | 85 ++++++++++++++++++++++ .../python/with_virtualenv/env/Scripts/refurb.exe | 0 .../python/with_virtualenv/env/bin/refurb | 0 8 files changed, 222 insertions(+) create mode 100644 ale_linters/python/refurb.vim create mode 100644 test/linter/test_refurb.vader create mode 100755 test/test-files/python/with_virtualenv/env/Scripts/refurb.exe create mode 100755 test/test-files/python/with_virtualenv/env/bin/refurb diff --git a/ale_linters/python/refurb.vim b/ale_linters/python/refurb.vim new file mode 100644 index 00000000..1ae77b77 --- /dev/null +++ b/ale_linters/python/refurb.vim @@ -0,0 +1,73 @@ +" Author: Yining +" Description: refurb as linter for python files + +call ale#Set('python_refurb_executable', 'refurb') +call ale#Set('python_refurb_options', '') +call ale#Set('python_refurb_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_refurb_change_directory', 1) +call ale#Set('python_refurb_auto_pipenv', 0) +call ale#Set('python_refurb_auto_poetry', 0) + +function! ale_linters#python#refurb#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_refurb_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + + if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_refurb_auto_poetry')) + \ && ale#python#PoetryPresent(a:buffer) + return 'poetry' + endif + + return ale#python#FindExecutable(a:buffer, 'python_refurb', ['refurb']) +endfunction + +function! ale_linters#python#refurb#GetCwd(buffer) abort + if ale#Var(a:buffer, 'python_refurb_change_directory') + " Run from project root if found, else from buffer dir. + let l:project_root = ale#python#FindProjectRoot(a:buffer) + + return !empty(l:project_root) ? l:project_root : '%s:h' + endif + + return '' +endfunction + +function! ale_linters#python#refurb#GetCommand(buffer) abort + let l:executable = ale_linters#python#refurb#GetExecutable(a:buffer) + let l:exec_args = l:executable =~? 'pipenv\|poetry$' + \ ? ' run refurb' + \ : '' + + return ale#Escape(l:executable) . l:exec_args + \ . ale#Pad(ale#Var(a:buffer, 'python_refurb_options')) + \ . ' %s' +endfunction + +function! ale_linters#python#refurb#Handle(buffer, lines) abort + "Example: path/to/file.py:3:17 [FURB109]: Replace `in [x, y, z]` with `in (x, y, z)` + let l:pattern = '\v^[a-zA-Z]?:?[^:]+:(\d+):(\d+)?:?\s*\[FURB(\d+)\]:\s*(.+)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[1] + 0, + \ 'col': l:match[2] + 0, + \ 'code': l:match[3] + 0, + \ 'text': l:match[4], + \ 'type': 'W', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('python', { +\ 'name': 'refurb', +\ 'executable': function('ale_linters#python#refurb#GetExecutable'), +\ 'cwd': function('ale_linters#python#refurb#GetCwd'), +\ 'command': function('ale_linters#python#refurb#GetCommand'), +\ 'callback': 'ale_linters#python#refurb#Handle', +\ 'output_stream': 'both', +\ 'read_buffer': 0, +\}) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index 6badcff3..07f54db8 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -1085,6 +1085,67 @@ g:ale_python_pyright_config *g:ale_python_pyright_config* \} < +=============================================================================== +refurb *ale-python-refurb* + +g:ale_python_refurb_change_directory *g:ale_python_refurb_change_directory* + *b:ale_python_refurb_change_directory* + Type: |Number| + Default: `1` + + If set to `1`, `refurb` will be run from a detected project root, per + |ale-python-root|. if set to `0` or no project root detected, + `refurb` will be run from the buffer's directory. + + +g:ale_python_refurb_executable *g:ale_python_refurb_executable* + *b:ale_python_refurb_executable* + Type: |String| + Default: `'refurb'` + + See |ale-integrations-local-executables| + + Set this to `'pipenv'` to invoke `'pipenv` `run` `refurb'`. + Set this to `'poetry'` to invoke `'poetry` `run` `refurb'`. + + +g:ale_python_refurb_options *g:ale_python_refurb_options* + *b:ale_python_refurb_options* + Type: |String| + Default: `''` + + This variable can be changed to add command-line arguments to the refurb + invocation. + + For example, to select/enable and/or disable some error codes, + you may want to set > + let g:ale_python_refurb_options = '--ignore 100' +g:ale_python_refurb_use_global *g:ale_python_refurb_use_global* + *b:ale_python_refurb_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + + +g:ale_python_refurb_auto_pipenv *g:ale_python_refurb_auto_pipenv* + *b:ale_python_refurb_auto_pipenv* + Type: |Number| + Default: `0` + + Detect whether the file is inside a pipenv, and set the executable to `pipenv` + if true. This is overridden by a manually-set executable. + + +g:ale_python_refurb_auto_poetry *g:ale_python_refurb_auto_poetry* + *b:ale_python_refurb_auto_poetry* + Type: |Number| + Default: `0` + + Detect whether the file is inside a poetry, and set the executable to `poetry` + if true. This is overridden by a manually-set executable. + + =============================================================================== reorder-python-imports *ale-python-reorder_python_imports* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 2477fe5a..4edc10c0 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -490,6 +490,7 @@ Notes: * `pylsp` * `pyre` * `pyright` + * `refurb` * `reorder-python-imports` * ruff * `unimport` diff --git a/doc/ale.txt b/doc/ale.txt index 9752ce6f..88593eb3 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3168,6 +3168,7 @@ documented in additional help files. pylsp.................................|ale-python-pylsp| pyre..................................|ale-python-pyre| pyright...............................|ale-python-pyright| + refurb................................|ale-python-refurb| reorder-python-imports................|ale-python-reorder_python_imports| ruff..................................|ale-python-ruff| unimport..............................|ale-python-unimport| diff --git a/supported-tools.md b/supported-tools.md index 6fca98f9..d6ea92b4 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -499,6 +499,7 @@ formatting. * [pylsp](https://github.com/python-lsp/python-lsp-server) :warning: * [pyre](https://github.com/facebook/pyre-check) :warning: * [pyright](https://github.com/microsoft/pyright) + * [refurb](https://github.com/dosisod/refurb) :floppy_disk: * [reorder-python-imports](https://github.com/asottile/reorder_python_imports) * [ruff](https://github.com/charliermarsh/ruff) * [unimport](https://github.com/hakancelik96/unimport) diff --git a/test/linter/test_refurb.vader b/test/linter/test_refurb.vader new file mode 100644 index 00000000..a1f066eb --- /dev/null +++ b/test/linter/test_refurb.vader @@ -0,0 +1,85 @@ +Before: + Save g:ale_python_auto_pipenv + Save g:ale_python_auto_poetry + + let g:ale_python_auto_pipenv = 0 + let g:ale_python_auto_poetry = 0 + + call ale#assert#SetUpLinterTest('python', 'refurb') + + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + unlet! b:bin_dir + unlet! b:executable + + call ale#assert#TearDownLinterTest() + +Execute(The refurb callbacks should return the correct default values): + AssertLinterCwd expand('%:p:h') + AssertLinter 'refurb', ale#Escape('refurb') . ' %s' + +Execute(The option for disabling changing directories should work): + let g:ale_python_refurb_change_directory = 0 + + AssertLinterCwd '' + AssertLinter 'refurb', ale#Escape('refurb') . ' %s' + +Execute(The refurb executable should be configurable, and escaped properly): + let g:ale_python_refurb_executable = 'executable with spaces' + + AssertLinter 'executable with spaces', ale#Escape('executable with spaces') . ' %s' + +Execute(The refurb command callback should let you set options): + let g:ale_python_refurb_options = '--some-flag' + AssertLinter 'refurb', ale#Escape('refurb') . ' --some-flag %s' + + let g:ale_python_refurb_options = '--some-option value' + AssertLinter 'refurb', ale#Escape('refurb') . ' --some-option value %s' + +Execute(The refurb callbacks shouldn't detect virtualenv directories where they don't exist): + call ale#test#SetFilename('../test-files/python/no_virtualenv/subdir/foo/bar.py') + + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/no_virtualenv/subdir') + AssertLinter 'refurb', ale#Escape('refurb') . ' %s' + +Execute(The refurb callbacks should detect virtualenv directories): + call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') + let b:executable = ale#path#Simplify( + \ g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/refurb' + \) + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir') + AssertLinter b:executable, ale#Escape(b:executable) . ' %s' + +Execute(You should able able to use the global refurb instead): + call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') + let g:ale_python_refurb_use_global = 1 + + AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir') + AssertLinter 'refurb', ale#Escape('refurb') . ' %s' + +Execute(Setting executable to 'pipenv' appends 'run refurb'): + let g:ale_python_refurb_executable = 'path/to/pipenv' + let g:ale_python_refurb_use_global = 1 + + AssertLinter 'path/to/pipenv', ale#Escape('path/to/pipenv') . ' run refurb %s' + +Execute(Pipenv is detected when python_refurb_auto_pipenv is set): + let g:ale_python_refurb_auto_pipenv = 1 + call ale#test#SetFilename('../test-files/python/pipenv/whatever.py') + + AssertLinterCwd expand('%:p:h') + AssertLinter 'pipenv', ale#Escape('pipenv') . ' run refurb %s' + +Execute(Setting executable to 'poetry' appends 'run refurb'): + let g:ale_python_refurb_executable = 'path/to/poetry' + let g:ale_python_refurb_use_global = 1 + + AssertLinter 'path/to/poetry', ale#Escape('path/to/poetry') . ' run refurb %s' + +Execute(poetry is detected when python_refurb_auto_poetry is set): + let g:ale_python_refurb_auto_poetry = 1 + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + AssertLinterCwd expand('%:p:h') + AssertLinter 'poetry', ale#Escape('poetry') . ' run refurb %s' diff --git a/test/test-files/python/with_virtualenv/env/Scripts/refurb.exe b/test/test-files/python/with_virtualenv/env/Scripts/refurb.exe new file mode 100755 index 00000000..e69de29b diff --git a/test/test-files/python/with_virtualenv/env/bin/refurb b/test/test-files/python/with_virtualenv/env/bin/refurb new file mode 100755 index 00000000..e69de29b -- cgit v1.2.3