An asynchronous linter plugin for Neovim (>= 0.5) complementary to the built-in Language Server Protocol support.
It uses the same API to report diagnostics as the language server client
built-in to neovim would do. Any customizations you did for
vim.lsp.diagnostic
apply for this plugin as well.
With ale we already got an asynchronous linter, why write yet another one?
Because ale is a full blown linter including a language server client with its own commands and functions.
nvim-lint
is for cases where you use the language server protocol client
built into neovim for 90% of the cases, but you want something to fill the
remaining gaps for languages where there is no good language server
implementation or where the diagnostics reporting of the language server is
inadequate and a better standalone linter exists.
- Requires Neovim >= 0.5
nvim-lint
is a plugin. Install it like any other Neovim plugin.- If using vim-plug:
Plug 'mfussenegger/nvim-lint'
- If using packer.nvim:
use 'mfussenegger/nvim-lint'
- If using vim-plug:
Configure the linters you want to run per filetype. For example:
require('lint').linters_by_ft = {
markdown = {'vale',}
}
Then setup a autocmd to trigger linting. For example:
au BufWritePost <buffer> lua require('lint').try_lint()
Some linters require a file to be saved to disk, others support linting stdin
input. For such linters you could also define a more aggressive autocmd, for
example on the InsertLeave
or TextChanged
events.
There is a generic linter called compiler
that uses the makeprg
and
errorformat
options of the current buffer.
Other dedicated linters that are built-in are:
Tool | Linter name |
---|---|
Set via makeprg |
compiler |
Languagetool | languagetool |
Vale | vale |
ShellCheck | shellcheck |
Mypy | mypy |
HTML Tidy | tidy |
Flake8 | mypy |
Revive | revive |
Pylint | pylint |
Golangci-lint | golangcilint |
Ruby | ruby |
Inko | inko |
cppcheck | cppcheck |
clang-tidy | clangtidy |
codespell | codespell |
luacheck | luacheck |
chktex | chktex |
vint | vint |
You can register custom linters by adding them to the linters
table, but
please consider contributing a linter if it is missing.
require('lint').linters.your_linter_name = {
cmd = 'linter_cmd',
stdin = true -- or false if it doesn't support content input via stdin. In that case the filename is automatically added to the arguments.
args = {}, -- list of arguments. Can contain functions with zero arguments that will be evaluated once the linter is used.
stream = nil, -- ('stdout' | 'stderr') configure the stream to which the linter outputs the linting result.
ignore_exitcode = false, -- set this to true if the linter exits with a code != 0 and that's considered normal.
parser = your_parse_function
}
Instead of declaring the linter as a table, you can also declare it as a function which returns the linter table in case you want to dynamically generate some of the properties.
your_parse_function
can be a function which takes two arguments:
output
bufnr
The output
is the output generated by the linter command.
The function must return a list of diagnostics as specified in the language server protocol.
You can generate a parse function from a Lua pattern or from an errorformat
using the function in the lint.parser
module:
parser = require('lint.parser').from_errorformat(errorformat)
The function takes a single argument which is the errorformat
.
parser = require('lint.parser').from_pattern(pattern, skeleton)
The function takes two arguments. The first is a lua pattern that must match four groups in order: line number, offset, code and message.
The second argument is a skeleton used to create each diagnostic. This can be
used to provide default values - for example to set a severity
.
Diagnostic interface description
export interface Diagnostic {
/**
* The range at which the message applies.
*/
range: Range;
/**
* The diagnostic's severity. Can be omitted. If omitted it is up to the
* client to interpret diagnostics as error, warning, info or hint.
*/
severity?: DiagnosticSeverity;
/**
* The diagnostic's code, which might appear in the user interface.
*/
code?: integer | string;
/**
* An optional property to describe the error code.
*
* @since 3.16.0
*/
codeDescription?: CodeDescription;
/**
* A human-readable string describing the source of this
* diagnostic, e.g. 'typescript' or 'super lint'.
*/
source?: string;
/**
* The diagnostic's message.
*/
message: string;
/**
* Additional metadata about the diagnostic.
*
* @since 3.15.0
*/
tags?: DiagnosticTag[];
/**
* An array of related diagnostic information, e.g. when symbol-names within
* a scope collide all definitions can be marked via this property.
*/
relatedInformation?: DiagnosticRelatedInformation[];
/**
* A data entry field that is preserved between a
* `textDocument/publishDiagnostics` notification and
* `textDocument/codeAction` request.
*
* @since 3.16.0
*/
data?: unknown;
}