Install linters for Python code in vim with ALE - lmmx/devnotes GitHub Wiki
Motivation
I had previously tried to use linters in vim without success,
before finding "ALE" a tool with a "fixer" feature which lints on save,
much like Go does (for imports, similar to Python's isort
).
About ALE
From its README:
ALE offers support for fixing code with command line tools in a non-blocking manner with the :ALEFix feature, supporting tools in many languages, like
prettier
,eslint
,autopep8
, and more.
- There are a ton of these: recently I've used
flake8-bugbear
andpandas-vet
. - My standard set of linters that themselves modify code is:
isort
(reorder imports),black
(enforce standard code style), andautoflake8
(remove unused imports)
It also claims to be efficient
One of ALE's general missions is that you won't pay for the features that you don't use.
The work is done in the background upon opening new buffers or editing files.
Options are documented in the vim help file
:help ale-options
for global options:help ale-integration-options
for specific linters
Linters vs. fixers
In ALE's terminology, linters find errors at certain lines (think mypy
and flake8
),
and fixers reformat the code following some rule (think isort
and black
).
In my case I'm primarily interested in using fixers (YMMV!).
When you save a file, edits should be made if your fixers are set up properly:
the fixers run on save, the linters are run during editing. Running :ALEInfo
will list
your available/enabled/ignored linters (but only "suggested" fixers).
Requirements
ALE requires vim 8+ (mine is 8.1 at time of writing, in 2022)
To install plugins for Vim I use Pathogen (see the README for other plugin managers).
The requirement is one line in .vimrc
, which should already be there if you've used it before.
"ensure you have the line
execute pathogen#infect()
in your~/.vimrc
file"
Installing ALE with Pathogen
cd ~/.vim/bundle
git clone https://github.com/dense-analysis/ale.git
Installing 'fixers' (linters that reformat code) with ALE
- Append to
.vimrc
to automatically 'fix' files using the specified 'fixers' when you save them, tone down visual distraction when using linters, and only use explicitly specified linters (thus this will disable all linters while editing without further configuration):
" Set this variable to 1 to fix files when you save them.
let g:ale_fix_on_save = 1
" Tone down the visual distraction of the 'sign gutter' from any linters
highlight clear ALEErrorSign
highlight clear ALEWarningSign
" Disable highlighting on code from any linters
let g:ale_set_highlights = 0
" Only run linters named in ale_linters settings.
let g:ale_linters_explicit = 1
- Then define a List in an ftplugin file at
~/.vim/ftplugin/python.vim
, (aftermkdir -p ~/.vim/ftplugin
in my case), similar to the suggestion here [but with the standard line length]:
" Fix files with isort, and then black.
let b:ale_fixers = {'python': ['isort', 'black']}
let g:ale_python_isort_options = '--profile black -l 88'
:bulb: Note: typically you'll find the defaults to use here in
pyproject.toml
(e.g.) or.pre-commit-config.yaml
(e.g.)
You 'fix' files with the linters installed in this way by calling ALEFix
if you set the
ale_fix_on_save
option to 0
(off).
I would personally want to avoid running fixers that delete code, such as autoflake8
,
as otherwise it'd be impossible to save code in intermediate states, and this could manifest subtly
and create 'rework' (YMMV!)
Using 'linters' (linters that indicate problems in code) with ALE
The :ALEInfo
status shows the available 'linters': by default there are many.
Current Filetype: python
Available Linters: ['bandit', 'cspell', 'flake8', 'flakehell', 'jedils', 'mypy', 'prospector',
'pycodestyle', 'pydocstyle', 'pyflakes', 'pylama', 'pylint', 'pylsp', 'pyre', 'pyright', 'unimport',
'vulture']
Enabled Linters: ['flake8', 'mypy', 'pylint', 'pyright']
Ignored Linters: []
The chaotic default is to run all of the available linters, all of the time. This is obviously detrimental to focus due to false positives.
To only run specified linters while editing, set:
" Only run linters named in ale_linters settings.
let g:ale_linters_explicit = 1
Tips and further configuration explanation
More fixers
The :ALEInfo
command lists some more suggested fixers (familiar to users of pre-commit
):
Suggested Fixers:
'add_blank_lines_for_python_control_statements' - Add blank lines before control statements.
'autoflake' - Fix flake issues with autoflake.
'autoimport' - Fix import issues with autoimport.
'autopep8' - Fix PEP8 issues with autopep8.
'black' - Fix PEP8 issues with black.
'isort' - Sort Python imports with isort.
'pyflyby' - Tidy Python imports with pyflyby.
'remove_trailing_lines' - Remove all blank lines at the end of a file.
'reorder-python-imports' - Sort Python imports with reorder-python-imports.
'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.
'yapf' - Fix Python files with yapf.
Linter command execution
If you scroll down to the bottom of the :ALEInfo
output you see how the linters run,
with arguments to /bin/bash -c
:
Command History:
(finished - exit code 127) ['/bin/bash', '-c', '''pylint'' --version']
<<<OUTPUT STARTS>>>
/bin/bash: pylint: command not found
<<<OUTPUT ENDS>>>
(executable check - success) flake8
(finished - exit code 0) ['/bin/bash', '-c', 'cd ''/home/louis/dev/testing/ale'' && ''flake8'' --version']
<<<OUTPUT STARTS>>>
4.0.1 (mccabe: 0.6.1, pycodestyle: 2.8.0, pyflakes: 2.4.0) CPython 3.9.7 on Linux
...
Hiding linter output
I recommend only using explicitly enabled linters. If you choose to use them all, and want to hide a
few by name, you put them in a list ale_linters_ignore
. Note: for linters not fixers.
let g:ale_linters_ignore = ["flake8", "mypy", "pylint", "pyright"]
Quietening ALE
ALE does not attempt to 'fade into the background' of development, but instead commandeers the vim UI as a linting readout. If I am using a linter, I'd prefer it to simply "do its job and get out of your way".
By default, ALE uses the strongest visual cues available to direct attention. As an initial user this is off-putting (I came for automatic rewriting, not to recreate an IDE).
Each to their own, but below I outline how to disable this behaviour in ALE.
Freezing the sign gutter
The default behaviour for ALE is to introduce a "sign gutter" along the left hand side of your code, that shifts the entire code file along -- quite visually disruptive.
The relevant part of the docs
here
show these are the 'sign gutter' containing ale_sign_error
(default: '>>'
)
and ale_sign_warning
(default: '--'
). These cannot be set to the empty string.
You can keep the gutter open at all times with
let g:ale_sign_column_always = 1
Turning off gutter background highlighting
The sign gutter uses red background highlighting for errors and orange for warnings,
which can be turned off with the following in ~/.vimrc
:
" Set this in your vimrc file to disable sign gutter highlighting
highlight clear ALEErrorSign
highlight clear ALEWarningSign
This leaves just the grey gutter background and black background where there is a sign/warning.
Turning off code background highlighting
The code itself is also marked at the position of errors, not just in the gutter beside it.
To turn this off:
" Set this in your vimrc file to disable highlighting
let g:ale_set_highlights = 0
Hiding the gutter
As far as I can tell, the only way to hide the sign gutter entirely is to turn off the 'linters' (note: not the 'fixers').
You can achieve this by activating the ale_linters_explicit
flag (see above)
and not setting any linters in ale_linters
.
Ideas to implement
Leaving the gutter open if any linters are in use
A nice solution to bring in linters deliberately might be to activate them for a particular buffer or to uncomment them at a particular time. In this case, I think personally I'd prefer to leave the gutter always open.
I'd also like to know if/how to jump between highlighted points when using linters in this way.
For flake8, it could be more trouble than it's worth compared to manually reviewing them, or it could be useful to use a combination of manual bulk review and stepping through them in the editor itself.
For flake8, I recommend a config to reduce false positives of --max-line-length=88 --extend-ignore=E203,E501,