Skip to content

Instantly share code, notes, and snippets.

@romainl
Last active April 13, 2024 03:30
Show Gist options
  • Star 110 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save romainl/ce55ce6fdc1659c5fbc0f4224fd6ad29 to your computer and use it in GitHub Desktop.
Save romainl/ce55ce6fdc1659c5fbc0f4224fd6ad29 to your computer and use it in GitHub Desktop.
Linting your code, the vanilla way

Linting your code, the vanilla way

You may want a linter plugin to lint your code in Vim but you probably don't need it. At least try the built-in way before jumping on the plugin bandwagon.

Defining makeprg

autocmd FileType <filetype> setlocal makeprg=<external command>

This autocommand tells Vim to use <external command> when invoking :make % in a <filetype> buffer. You can add as many similar lines as needed for other languages.

Example:

autocmd FileType python setlocal makeprg=pylint\ --output-format=parseable
autocmd FileType javascript setlocal makeprg=eslint\ --format\ compact

Defining errorformat (optional)

The whole point of defining makeprg is for Vim to parse the output of the linter and display eventual errors.

The most sensibly designed compilers/linters default to or can be told to use a simple, machine-readable, output formatting that looks more or less like this:

path/to/file:123:4 Error message

That format (and a few variants) is supported by default so there is no need to do anything but some compilers/linters are not as sensibly designed and you might have to handle silly formatting with ASCII arrows, multi-line messages, etc. In such a case, writing a custom errorformat is pretty much an obligation. See :help errorformat for the nitty-gritty.

"Compiler"

Chances are your linter is already natively supported via the "Compiler" feature, in which case you don't have to define either makeprg or errorformat. You simply need to call the :compiler command with the name of your linter as argument and you are set:

:compiler eslint

Of course, calling that command manually for every file you edit is not an exciting prospect but you can use an autocommand like the ones above to make it all automatic:

autocmd FileType python compiler pylint
autocmd FileType javascript compiler eslint

There are currently 96 supported "compilers" so it might be a good idea to :view $VIMRUNTIME/compiler (and consider adding it to the default distribution if it is not ;-)).

Automatic execution on :write

autocmd BufWritePost <pattern> silent make! <afile> | silent redraw!

This autocommand tells Vim to run :make on the current file matching <pattern> whenever you :write it. You can add patterns if you want that to happen with other filetypes.

Example:

autocmd BufWritePost *.py,*.js silent make! <afile> | silent redraw!

Automatic opening of the quickfix window

This autocommand tells Vim to open the quickfix window whenever a quickfix command is executed (like :make) AND there are valid errors to display.

autocmd QuickFixCmdPost [^l]* cwindow

My Vim-related gists.

augroup Linting
autocmd!
autocmd FileType python compiler pylint
autocmd BufWritePost *.py silent make! <afile> | silent redraw!
autocmd QuickFixCmdPost [^l]* cwindow
augroup END
@Konfekt
Copy link

Konfekt commented Apr 5, 2020

Thank you, fixed!

@KonnorRogers
Copy link

KonnorRogers commented May 7, 2020

I've been hitting issues with redraw! it doesn't consistently work.

It could be plugin related, I haven't had the chance to fully test it. However, when using edit I don't run into any issues.

Are there any downsides to using edit instead of redraw!

Example:

autocmd BufWritePost *.py silent make! % | edit

The other option I found was also:

set autoread

@romainl
Copy link
Author

romainl commented May 7, 2020

@ParamagicDev, I never thought about using edit in this context. That's pretty clever.

As for downsides I must admit I don't know. There are side-effects, that's for sure, but whether they are blockers or not is yours to decide, I guess.

Off the top of my head:

  • :edit triggers the whole filetype mechanism from ftdetect to ftplugin via custom autocommands in your vimrc, which may or may not be acceptable.
  • :edit also triggers the BufRead event and possibly others, which may or may not end up executing stuff you don't want.

:help 'autoread' is generally used in conjunction with :help :checktime to react on an external write, which may be relevant if your linter also does some fixing. See this other gist of mine for an example involving eslint --fix.

@romainl
Copy link
Author

romainl commented Jul 21, 2021

This needs an update with :help :compiler. (done)

@pbnj
Copy link

pbnj commented Sep 3, 2021

@lewis6991, the "interface" for managing signs is way too messy. Defining, placing, and removing a sign is not particularly hard but you'll have to manage them and sync them with the content of the quickfix list and/or the content of any location list. You really need some non-trivial scripting for getting it to work.

See :help sign-support.

This is how I do it:

function! QF_signs() abort
	" define the signs used by quickfix
	call sign_define('QFError',{'text':'>>','texthl':'NONE','linehl':'NONE'})
	" remove any existing signs
	call sign_unplace('*')
	" get the quickfix list
	let s:qfl = getqflist()
	" loop over quickfix list to place signs
	for item in s:qfl
		call sign_place(0, '', 'QFError', item.bufnr, {'lnum': item.lnum})
	endfor
endfunction

augroup quickfix
	autocmd!
	autocmd QuickFixCmdPost [^l]* cwindow | call QF_signs()
	autocmd QuickFixCmdPost l* lwindow | call QF_signs()
augroup END

It's not the most eloquent and there may be definitely are some buggy edge-cases, but it works Good Enough for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment