diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | ale_linters/python/vulture.vim | 84 | ||||
-rw-r--r-- | doc/ale-python.txt | 30 | ||||
-rw-r--r-- | doc/ale.txt | 3 | ||||
-rw-r--r-- | test/handler/test_vulture_handler.vader | 92 |
5 files changed, 209 insertions, 2 deletions
@@ -159,7 +159,7 @@ formatting. | proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) | | Pug | [pug-lint](https://github.com/pugjs/pug-lint) | | Puppet | [languageserver](https://github.com/lingua-pupuli/puppet-editor-services), [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | -| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](http://github.com/landscapeio/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [yapf](https://github.com/google/yapf) | +| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](http://github.com/landscapeio/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture), [yapf](https://github.com/google/yapf) | | QML | [qmlfmt](https://github.com/jesperhh/qmlfmt), [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint) | | R | [lintr](https://github.com/jimhester/lintr) | | ReasonML | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-reasonml-ols` for configuration instructions, [ols](https://github.com/freebroccolo/ocaml-language-server), [refmt](https://github.com/reasonml/reason-cli) | diff --git a/ale_linters/python/vulture.vim b/ale_linters/python/vulture.vim new file mode 100644 index 00000000..bff7d623 --- /dev/null +++ b/ale_linters/python/vulture.vim @@ -0,0 +1,84 @@ +" Author: Yauheni Kirylau <actionless.loveless@gmail.com> +" Description: vulture linting for python files + +call ale#Set('python_vulture_executable', 'vulture') +call ale#Set('python_vulture_options', '') +call ale#Set('python_vulture_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_vulture_change_directory', 1) + + +" The directory to change to before running vulture +function! s:GetDir(buffer) abort + let l:project_root = ale#python#FindProjectRoot(a:buffer) + + return !empty(l:project_root) + \ ? l:project_root + \ : expand('#' . a:buffer . ':p:h') +endfunction + + +function! ale_linters#python#vulture#GetExecutable(buffer) abort + return ale#python#FindExecutable(a:buffer, 'python_vulture', ['vulture']) +endfunction + + +function! ale_linters#python#vulture#GetCommand(buffer) abort + let l:change_dir = ale#Var(a:buffer, 'python_vulture_change_directory') + \ ? ale#path#CdString(s:GetDir(a:buffer)) + \ : '' + + let l:executable = ale_linters#python#vulture#GetExecutable(a:buffer) + + let l:exec_args = l:executable =~? 'pipenv$' + \ ? ' run vulture' + \ : '' + + let l:lint_dest = ale#Var(a:buffer, 'python_vulture_change_directory') + \ ? ' .' + \ : ' %s' + + return l:change_dir + \ . ale#Escape(l:executable) . l:exec_args + \ . ' ' + \ . ale#Var(a:buffer, 'python_vulture_options') + \ . l:lint_dest +endfunction + + +function! ale_linters#python#vulture#Handle(buffer, lines) abort + for l:line in a:lines[:10] + if match(l:line, '^Traceback') >= 0 + return [{ + \ 'lnum': 1, + \ 'text': 'An exception was thrown. See :ALEDetail', + \ 'detail': join(a:lines, "\n"), + \}] + endif + endfor + + " Matches patterns line the following: + let l:pattern = '\v^([a-zA-Z]?:?[^:]+):(\d+): (.*)$' + let l:output = [] + let l:dir = s:GetDir(a:buffer) + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + let l:abspath = ale#path#GetAbsPath(l:dir, l:match[1]) + let l:item = { + \ 'filename': l:abspath, + \ 'lnum': l:match[2] + 0, + \ 'text': l:match[3], + \ 'type': 'W', + \} + call add(l:output, l:item) + endfor + + return l:output +endfunction + + +call ale#linter#Define('python', { +\ 'name': 'vulture', +\ 'executable_callback': 'ale_linters#python#vulture#GetExecutable', +\ 'command_callback': 'ale_linters#python#vulture#GetCommand', +\ 'callback': 'ale_linters#python#vulture#Handle', +\}) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index e24ef1aa..093ea758 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -398,6 +398,36 @@ g:ale_python_pyre_use_global *g:ale_python_pyre_use_global* =============================================================================== +vulture *ale-python-vulture* + +g:ale_python_vulture_change_directory *g:ale_python_vulture_change_directory* + *b:ale_python_vulture_change_directory* + Type: |Number| + Default: `1` + + If set to `1`, ALE will switch to the directory the Python file being + checked with `vulture` is in before checking it and check the whole project + directory instead of checking only the file opened in the current buffer. + This helps `vulture` to know the context and avoid false-negative results. + + +g:ale_python_vulture_executable *g:ale_python_vulture_executable* + *b:ale_python_vulture_executable* + Type: |String| + Default: `'vulture'` + + See |ale-integrations-local-executables| + + +g:ale_python_vulture_use_global *g:ale_python_vulture_use_global* + *b:ale_python_vulture_use_global* + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + + +=============================================================================== yapf *ale-python-yapf* g:ale_python_yapf_executable *g:ale_python_yapf_executable* diff --git a/doc/ale.txt b/doc/ale.txt index fdb2d0a6..24f57424 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -207,6 +207,7 @@ CONTENTS *ale-contents* pylint..............................|ale-python-pylint| pyls................................|ale-python-pyls| pyre................................|ale-python-pyre| + vulture.............................|ale-python-vulture| yapf................................|ale-python-yapf| qml...................................|ale-qml-options| qmlfmt..............................|ale-qml-qmlfmt| @@ -399,7 +400,7 @@ Notes: * proto: `protoc-gen-lint` * Pug: `pug-lint` * Puppet: `languageserver`, `puppet`, `puppet-lint` -* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pyre`, `pylint`!!, `yapf` +* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pyls`, `pyre`, `pylint`!!, `vulture`, `yapf` * QML: `qmlfmt`, `qmllint` * R: `lintr` * ReasonML: `merlin`, `ols`, `refmt` diff --git a/test/handler/test_vulture_handler.vader b/test/handler/test_vulture_handler.vader new file mode 100644 index 00000000..c6bd7643 --- /dev/null +++ b/test/handler/test_vulture_handler.vader @@ -0,0 +1,92 @@ +Before: + runtime ale_linters/python/vulture.vim + + call ale#test#SetDirectory('/testplugin/test/handler') + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + + silent file something_else.py + +Execute(Basic vulture check with relative path in result should be handled): + call ale#test#SetFilename('something_else.py') + AssertEqual + \ [ + \ { + \ 'lnum': 34, + \ 'text': 'unused variable ''foo'' (60% confidence)', + \ 'type': 'W', + \ 'filename': ale#path#Simplify(g:dir . '/something_else.py'), + \ }, + \ ], + \ ale_linters#python#vulture#Handle(bufnr(''), [ + \ './something_else.py:34: unused variable ''foo'' (60% confidence)', + \ ]) + +Execute(Basic vulture check with absolute path in result should be handled): + call ale#test#SetFilename('something_else.py') + AssertEqual + \ [ + \ { + \ 'lnum': 34, + \ 'text': 'unused variable ''foo'' (60% confidence)', + \ 'type': 'W', + \ 'filename': ale#path#Simplify(g:dir . '/something_else.py'), + \ }, + \ ], + \ ale_linters#python#vulture#Handle(bufnr(''), [ + \ ale#path#Simplify(g:dir . '/something_else.py') . ':34: unused variable ''foo'' (60% confidence)', + \ ]) + +Execute(Vulture check for two files should be handled): + call ale#test#SetFilename('something_else.py') + AssertEqual + \ [ + \ { + \ 'lnum': 34, + \ 'text': 'unused variable ''foo'' (60% confidence)', + \ 'type': 'W', + \ 'filename': ale#path#Simplify(g:dir . '/something_else.py'), + \ }, + \ { + \ 'lnum': 12, + \ 'text': 'unused variable ''bar'' (60% confidence)', + \ 'type': 'W', + \ 'filename': ale#path#Simplify(g:dir . '/second_one.py'), + \ }, + \ ], + \ ale_linters#python#vulture#Handle(bufnr(''), [ + \ './something_else.py:34: unused variable ''foo'' (60% confidence)', + \ './second_one.py:12: unused variable ''bar'' (60% confidence)', + \ ]) + + +Execute(Vulture exception should be handled): + call ale#test#SetFilename('something_else.py') + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'text': 'An exception was thrown. See :ALEDetail', + \ 'detail': join([ + \ 'Traceback (most recent call last):', + \ ' File "/usr/lib/python3.6/site-packages/vulture/__init__.py", line 13, in <module>', + \ ' from .core import stuff', + \ 'BaddestException: Everything gone wrong', + \ ], "\n"), + \ } + \ ], + \ ale_linters#python#vulture#Handle(bufnr(''), [ + \ 'Traceback (most recent call last):', + \ ' File "/usr/lib/python3.6/site-packages/vulture/__init__.py", line 13, in <module>', + \ ' from .core import stuff', + \ 'BaddestException: Everything gone wrong', + \ ]) + +Execute(The vulture handler should handle empty output): + AssertEqual + \ [], + \ ale_linters#python#vulture#Handle(bufnr(''), []) |