nvim-lint
An asynchronous linter plugin for Neovim complementary to the built-in Language Server Protocol support.
Top Related Projects
Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
Quickstart configs for Nvim LSP
Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
Asynchronous linting and make framework for Neovim/Vim
plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice.
Quick Overview
The nvim-lint
project is a Neovim plugin that provides a unified interface for running various linters and formatters within the Neovim editor. It aims to simplify the configuration and usage of linting and formatting tools, making it easier to maintain code quality and consistency.
Pros
- Unified Interface:
nvim-lint
provides a single, consistent interface for running various linting and formatting tools, simplifying the configuration and usage of these tools. - Extensible: The plugin supports a wide range of linters and formatters, and it can be easily extended to support additional tools.
- Asynchronous Execution:
nvim-lint
runs linters and formatters asynchronously, allowing for a more responsive and efficient editing experience. - Customizable: The plugin offers a high degree of customization, allowing users to configure the behavior of linters and formatters to suit their needs.
Cons
- Dependency on Neovim: The
nvim-lint
plugin is specifically designed for the Neovim editor and may not be compatible with other text editors. - Limited Linter/Formatter Support: While the plugin supports a wide range of tools, it may not cover all the linters and formatters that a user might require.
- Potential Performance Issues: Depending on the number of linters and formatters configured, the plugin may impact the overall performance of the Neovim editor.
- Learning Curve: Configuring and using
nvim-lint
may have a steeper learning curve compared to using individual linting and formatting tools.
Code Examples
N/A (This is not a code library)
Getting Started
N/A (This is not a code library)
Competitor Comparisons
Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
Pros of dense-analysis/ale
- Supports a wide range of programming languages and linters, making it a versatile choice for developers working with various codebases.
- Provides a comprehensive set of features, including automatic fixing, code formatting, and integration with various editor plugins.
- Actively maintained with regular updates and a large community of contributors.
Cons of dense-analysis/ale
- Can be more complex to configure and set up, especially for users who prefer a more lightweight solution.
- May have a higher resource footprint compared to mfussenegger/nvim-lint, which could be a concern for users with limited system resources.
- The extensive feature set may be overkill for users who only require basic linting functionality.
Code Comparison
Here's a brief comparison of the configuration for both projects:
mfussenegger/nvim-lint:
require('nvim-lint').setup({
run_on_save = true,
filetype = {
lua = { 'luacheck' },
python = { 'flake8' },
-- Add more filetypes and linters as needed
},
})
dense-analysis/ale:
let g:ale_linters = {
\ 'lua': ['luacheck'],
\ 'python': ['flake8'],
\ -- Add more filetypes and linters as needed
\}
let g:ale_fix_on_save = 1
As you can see, the configuration for both projects is relatively straightforward, with the main difference being the syntax (Lua vs. Vimscript) and the specific options available.
Quickstart configs for Nvim LSP
Pros of neovim/nvim-lspconfig
- Provides a comprehensive configuration for the Neovim LSP (Language Server Protocol) client, making it easier to set up and manage multiple language servers.
- Offers a wide range of built-in configurations for various programming languages and tools, reducing the need for custom setup.
- Integrates well with other Neovim plugins, allowing for a seamless development experience.
Cons of neovim/nvim-lspconfig
- May have a steeper learning curve for users who are new to the Neovim ecosystem or the LSP concept.
- Requires more configuration and setup compared to a more opinionated plugin like nvim-lint.
- May not provide as much flexibility and customization options as a more lightweight plugin.
Code Comparison
neovim/nvim-lspconfig:
require("lspconfig").pyright.setup {
on_attach = on_attach,
flags = {
debounce_text_changes = 150,
},
settings = {
python = {
analysis = {
typeCheckingMode = "off",
diagnosticMode = "workspace",
},
},
},
}
mfussenegger/nvim-lint:
require("lint").linters_by_ft = {
python = {"flake8", "mypy"},
javascript = {"eslint"},
typescript = {"eslint"},
}
require("lint").run_on_save = true
Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
Pros of null-ls.nvim
- Supports a wide range of linters, formatters, and other tools out of the box, making it a versatile choice for various development needs.
- Provides a flexible and extensible architecture, allowing users to easily configure and customize the behavior of the linters and formatters.
- Integrates well with other Neovim plugins, such as Telescope and Lualine, providing a seamless user experience.
Cons of null-ls.nvim
- The configuration can be more complex compared to nvim-lint, as it requires more setup and customization to get the desired behavior.
- The documentation, while comprehensive, may be less beginner-friendly than nvim-lint's straightforward approach.
- Depending on the specific tools used, the performance impact may be higher than nvim-lint's more lightweight approach.
Code Comparison
null-ls.nvim:
local null_ls = require("null-ls")
null_ls.setup({
sources = {
null_ls.builtins.formatting.prettier,
null_ls.builtins.diagnostics.eslint,
},
})
nvim-lint:
require("nvim-lint").setup({
run_on_save = true,
filetype = {
javascript = { "eslint" },
python = { "flake8" },
},
})
Asynchronous linting and make framework for Neovim/Vim
Pros of Neomake
- Neomake supports a wide range of programming languages and linters, making it a versatile choice for developers.
- The plugin provides a comprehensive set of configuration options, allowing users to customize the linting process to their needs.
- Neomake has a large and active community, with regular updates and bug fixes.
Cons of Neomake
- Neomake can be more complex to set up and configure compared to nvim-lint, especially for users who are new to Neovim.
- The plugin can be resource-intensive, particularly on larger projects, which may impact performance.
Code Comparison
Neomake configuration:
" Neomake configuration
let g:neomake_python_enabled_makers = ['flake8', 'mypy']
let g:neomake_javascript_enabled_makers = ['eslint']
nvim-lint configuration:
-- nvim-lint configuration
require("lint").linters_by_ft = {
python = {"flake8", "mypy"},
javascript = {"eslint"}
}
Both configurations set up linters for Python (flake8 and mypy) and JavaScript (eslint). The main difference is the syntax, with Neomake using Vimscript and nvim-lint using Lua.
plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice.
Pros of plenary.nvim
- plenary.nvim provides a wide range of utility functions and modules that can be used across various Neovim plugins, making it a valuable tool for plugin developers.
- The library is well-documented and actively maintained, ensuring a reliable and consistent experience for users.
- plenary.nvim is designed to be modular, allowing users to selectively include only the functionality they need, reducing the overall plugin footprint.
Cons of plenary.nvim
- While plenary.nvim is a powerful library, it may be overkill for smaller plugins that only require a few basic utility functions.
- The library's broad scope and feature set can make it challenging for new users to navigate and understand the full extent of its capabilities.
- Depending on the specific use case, the overhead of including plenary.nvim may outweigh the benefits for some plugin developers.
Code Comparison
nvim-lint:
local lint = require("lint")
lint.linters_by_ft = {
lua = {"luacheck"},
python = {"flake8"},
-- Add more linters as needed
}
lint.linters.luacheck = {
cmd = "luacheck",
args = {"--formatter", "plain", "--codes", "--filename", "%:p"},
stream = "both",
ignore_exitcode = true,
debounce = 100,
}
plenary.nvim:
local Job = require("plenary.job")
Job:new({
command = "luacheck",
args = {"--formatter", "plain", "--codes", "--filename", "%:p"},
on_exit = function(j, return_val)
-- Handle the linting result
end,
}):sync()
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
nvim-lint
An asynchronous linter plugin for Neovim (>= 0.9.5) complementary to the built-in Language Server Protocol support.
Motivation & Goals
With ale we already got an asynchronous linter, why write yet another one?
Because ale also includes its own language server client.
nvim-lint
instead has a more narrow scope: It spawns linters, parses their
output, and reports the results via the vim.diagnostic
module.
nvim-lint
complements the built-in language server client for languages where
there are no language servers, or where standalone linters provide better
results.
Installation
- Requires Neovim >= 0.9.5
nvim-lint
is a regular plugin and can be installed via the:h packages
mechanism or via a plugin manager.
For example:
git clone \
https://github.com/mfussenegger/nvim-lint.git
~/.config/nvim/pack/plugins/start/nvim-lint
- If using vim-plug:
Plug 'mfussenegger/nvim-lint'
- If using packer.nvim:
use 'mfussenegger/nvim-lint'
Usage
Configure the linters you want to run per file type. For example:
require('lint').linters_by_ft = {
markdown = {'vale'},
}
To get the filetype
of a buffer you can run := vim.bo.filetype
.
The filetype
can also be a compound filetype
. For example, if you have a buffer
with a filetype
like yaml.ghaction
, you can use either ghaction
, yaml
or
the full yaml.ghaction
as key in the linters_by_ft
table and the linter
will be picked up in that buffer. This is useful for linters like
actionlint in combination with vim.filetype
patterns like
[".*/.github/workflows/.*%.yml"] = "yaml.ghaction",
Then setup a autocmd
to trigger linting. For example:
au BufWritePost * lua require('lint').try_lint()
or with Lua auto commands:
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
callback = function()
-- try_lint without arguments runs the linters defined in `linters_by_ft`
-- for the current filetype
require("lint").try_lint()
-- You can call `try_lint` with a linter name or a list of names to always
-- run specific linters, independent of the `linters_by_ft` configuration
require("lint").try_lint("cspell")
end,
})
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.
If you want to customize how the diagnostics are displayed, read :help vim.diagnostic.config
.
Available Linters
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:
Custom Linters
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.
append_fname = true, -- Automatically append the file name to `args` if `stdin = false` (default: true)
args = {}, -- list of arguments. Can contain functions with zero arguments that will be evaluated once the linter is used.
stream = nil, -- ('stdout' | 'stderr' | 'both') 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.
env = nil, -- custom environment table to use with the external process. Note that this replaces the *entire* environment, it is not additive.
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 three arguments:
output
bufnr
linter_cwd
The output
is the output generated by the linter command.
The function must return a list of diagnostics as specified in :help diagnostic-structure
.
You can override the environment that the linting process runs in by setting
the env
key, e.g.
env = { ["FOO"] = "bar" }
Note that this completely overrides the environment, it does not add new
environment variables. The one exception is that the PATH
variable will be
preserved if it is not explicitly set.
You can generate a parse function from a Lua pattern or from an errorformat
using the function in the lint.parser
module:
from_errorformat
parser = require('lint.parser').from_errorformat(errorformat)
The function takes two arguments: errorformat
and skeleton
(optional).
from_pattern
Creates a parser function from a pattern.
parser = require('lint.parser').from_pattern(pattern, groups, severity_map, defaults, opts)
pattern
The function allows to parse the linter's output using a pattern which can be either:
- A Lua pattern. See
:help lua-patterns
. - A LPEG pattern object. See
:help vim.lpeg
. - A function (
fun(line: string):string[]
). It takes one parameter - a line from the linter output and must return a string array with the matches. The array should be empty if there was no match.
groups
The groups specify the result format of the pattern. Available groups:
lnum
end_lnum
col
end_col
message
file
severity
code
The order of the groups must match the order of the captures within the pattern. An example:
local pattern = '[^:]+:(%d+):(%d+):(%w+):(.+)'
local groups = { 'lnum', 'col', 'code', 'message' }
The captures in the pattern correspond to the group at the same position.
severity
A mapping from severity codes to diagnostic codes
default_severity = {
['error'] = vim.diagnostic.severity.ERROR,
['warning'] = vim.diagnostic.severity.WARN,
['information'] = vim.diagnostic.severity.INFO,
['hint'] = vim.diagnostic.severity.HINT,
}
defaults
The defaults diagnostic values
defaults = {["source"] = "mylint-name"}
opts
Additional options
lnum_offset
: Added tolnum
. Defaults to 0end_lnum_offset
: Added toend_lnum
. Defaults to 0end_col_offset
: offset added toend_col
. Defaults to-1
, assuming that the end-column position is exclusive.
Customize built-in linters
You can import a linter and modify its properties. An example:
local phpcs = require('lint').linters.phpcs
phpcs.args = {
'-q',
-- <- Add a new parameter here
'--report=json',
'-'
}
You can also post-process the diagnostics produced by a linter by wrapping it.
For example, to change the severity of all diagnostics created by cspell
:
local lint = require("lint")
lint.linters.cspell = require("lint.util").wrap(lint.linters.cspell, function(diagnostic)
diagnostic.severity = vim.diagnostic.severity.HINT
return diagnostic
end)
Display configuration
See :help vim.diagnostic.config
.
If you want to have different settings per linter, you can get the namespace
for a linter via require("lint").get_namespace("linter_name")
. An example:
local ns = require("lint").get_namespace("my_linter_name")
vim.diagnostic.config({ virtual_text = true }, ns)
Get the current running linters for your buffer
You can see which linters are running with require("lint").get_running()
.
To include the running linters in the status line you could format them like this:
local lint_progress = function()
local linters = require("lint").get_running()
if #linters == 0 then
return "ó°¦"
end
return "ó±¶ " .. table.concat(linters, ", ")
end
Alternatives
Development â¢ï¸
Run tests
Running tests requires busted.
See neorocks or Using Neovim as Lua interpreter with Luarocks for installation instructions.
busted tests/
Top Related Projects
Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
Quickstart configs for Nvim LSP
Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
Asynchronous linting and make framework for Neovim/Vim
plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot