Like Vim, Neovim comes with a bunch of preconfigured “compilers” (i.e., general settings for makeprg
and errorformat
), yet it is possible to define additional compilers when you’re not relying on a language server or, as in my case, when you want to use a specific linter via makeprg
. Indeed, when it does not make sense to use a build system, it might be interesting to consider a linter as an alternative for Vim’s make
feature. The results of invoking :make
are displayed in the quickfix window, which I particularly like. Likewise, the formatprg
option allows one to define a fixer for gq
‘ing on a selection. We can also define a mapping to fix the whole buffer. This way, we can have linting and fixing facilities without relying on external plugins, like null-ls, ALE or efm-langserver.
Usually, I stand by the already defined compilers, but I added two specifications, one for pytest and the other for shellcheck. The following is for pytest, and it has been proposed by Phelipe:1
if exists('current_compiler')
finish
endif
let current_compiler = 'pytest'
if exists(':CompilerSet') != 2 " older Vim always used :setlocal
command -nargs=* CompilerSet setlocal <args>
endif
CompilerSet makeprg=pytest\ --tb=short\ -vv\ $*
CompilerSet errorformat=
\%EE\ \ \ \ \ File\ \"%f\"\\,\ line\ %l,
\%CE\ \ \ %p^,
\%ZE\ \ \ %[%^\ ]%\\@=%m,
\%Afile\ %f\\,\ line\ %l,
\%+ZE\ %mnot\ found,
\%CE\ %.%#,
\%-G_%\\+\ ERROR%.%#\ _%\\+,
\%A_%\\+\ %o\ _%\\+,
\%C%f:%l:\ in\ %o,
\%ZE\ %\\{3}%m,
\%EImportError%.%#\'%f\'\.,
\%C%.%#,
\%+G%[=]%\\+\ %*\\d\ passed%.%#,
\%-G%[%^E]%.%#,
\%-G
function! FixColumnNumber()
if b:current_compiler !=? 'pytest'
return
endif
let qflist = getqflist()
for i in qflist
let i.col = i.col + 1
endfor
call setqflist(qflist)
endfunction
augroup FixPytestQuickFix
au!
autocmd QuickFixCmdPost <buffer> call FixColumnNumber()
augroup END
For shellcheck, I have the following in my compiler
directory:
CompilerSet makeprg=shellcheck\ -f\ gcc
CompilerSet errorformat=%f:%l:%c:\ %trror:\ %m\ [SC%n],
\%f:%l:%c:\ %tarning:\ %m\ [SC%n],
\%f:%l:%c:\ %tote:\ %m\ [SC%n],
\%-G%.%#
In both cases above, the tricky part is to manage the errorformat
. Once you have defined your custom compiler, you can declare it in your filetype file in after/ftplugin
and optionally defined an appropriate makeprg
command. For instance, in the case of shellcheck I use the following:
compiler shellcheck
setlocal makeprg=shellcheck\ -f\ gcc\ %
nmap <buffer> <silent> g= :!shfmt -i 2 -ln posix -sr -ci -s -w %<cr>:redraw!<cr>
The last line define a mapping to format the whole buffer using shfmt. I use g=
for vim.lsp.buf.format()
when a language server is available too. Of course I realize that the ALE or null-ls plugins handle all of that in a more elegant and efficient way, especially since it is asynchroneous contrary to the approach presented above and you are limited to one linter unless you write a shell script to gather several commands,2 but that’s my take for exploiting Neovim’s builtins.
I summarized sone of the options I have in my vimrc folder in the following table.
Filetype | LSP | Linter | Fixer |
c | clangd | built-in | built-in |
python | pyright | built-in | black, isort |
racket | racket-langserver | built-in | built-in |
haskell | hls | built-in | built-in |
purescript | purescriptls | built-in | built-in |
sh | not used | shellcheck | shfmt |
javascript | not used | quick-lint-js | prettier |
♪ Greg Abate • Sunshower