Convert Figma logo to code with AI

mfussenegger logonvim-lint

An asynchronous linter plugin for Neovim complementary to the built-in Language Server Protocol support.

2,321
241
2,321
35

Top Related Projects

13,706

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.

2,675

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

13,706

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" },
  },
})
2,675

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 Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

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:

ToolLinter name
Set via makeprgcompiler
actionlintactionlint
alexalex
amebaameba
ansible-lintansible_lint
banditbandit
bashbash
bean-checkbean_check
biomejsbiomejs
blocklintblocklint
buf_lintbuf_lint
buildifierbuildifier
cfn-lintcfn_lint
cfn_nagcfn_nag
checkmakecheckmake
checkpatch.plcheckpatch
checkstylecheckstyle
chktexchktex
clang-tidyclangtidy
clazyclazy
clippyclippy
clj-kondoclj-kondo
cmakelintcmakelint
codespellcodespell
commitlintcommitlint
cppcheckcppcheck
cpplintcpplint
credocredo
cspellcspell
cuecue
curlylintcurlylint
dashdash
deadnixdeadnix
denodeno
dmypydmypy
DirectX Shader Compilerdxc
djlintdjlint
dotenv-linterdotenv_linter
editorconfig-checkereditorconfig-checker
erb-linterb_lint
ESLinteslint
eslint_deslint_d
eugeneeugene
fennelfennel
fishfish
Flake8flake8
flawfinderflawfinder
fortitudefortitude
gawkgawk
gdlint (gdtoolkit)gdlint
GHDLghdl
gitlintgitlint
glslcglslc
Golangci-lintgolangcilint
hadolinthadolint
hledgerhledger
hlinthlint
htmlhinthtmlhint
HTML Tidytidy
Inkoinko
janetjanet
jokerjoker
jshintjshint
jsonlintjsonlint
kshksh
ktlintktlint
lachecklacheck
Languagetoollanguagetool
luacluac
luacheckluacheck
markdownlintmarkdownlint
markdownlint-cli2markdownlint-cli2
markuplintmarkuplint
mlintmlint
Mypymypy
Nagelfarnagelfar
Nixnix
npm-groovy-lintnpm-groovy-lint
oelint-advoelint-adv
opa_checkopa_check
oxlintoxlint
perlcriticperlcritic
perlimportsperlimports
phpcsphpcs
phpinsightsphpinsights
phpmdphpmd
phpphp
phpstanphpstan
ponycpony
prisma-lintprisma-lint
proselintproselint
protolintprotolint
psalmpsalm
puppet-lintpuppet-lint
pycodestylepycodestyle
pydocstylepydocstyle
Pylintpylint
pyproject-flake8pflake8
quick-lint-jsquick-lint-js
redoclyredolcy
regalregal
Reviverevive
rflintrflint
robocoprobocop
rpmlintrpmlint
RPMrpmspec
rstcheckrstcheck
rstlintrstlint
RuboCoprubocop
Rubyruby
Ruffruff
salt-lintsaltlint
Seleneselene
ShellCheckshellcheck
slangslang
Snakemakesnakemake
snyksnyk_iac
Solhintsolhint
Spectralspectral
sphinx-lintsphinx-lint
sqlfluffsqlfluff
sqruffsqruff
standardjsstandardjs
StandardRBstandardrb
statix checkstatix
stylelintstylelint
svlintsvlint
SwiftLintswiftlint
systemd-analyzesystemd-analyze
systemdlintsystemdlint
tflinttflint
tfsectfsec
tlinttlint
trivytrivy
ts-standardts-standard
twig-cs-fixertwig-cs-fixer
typostypos
Valavala_lint
Valevale
Verilatorverilator
vintvint
VSGvsg
vulturevulture
wokewoke
write-goodwrite_good
yamllintyamllint
yqyq
zizmorzizmor
zshzsh

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 to lnum. Defaults to 0
  • end_lnum_offset: Added to end_lnum. Defaults to 0
  • end_col_offset: offset added to end_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/