Skip to content

Linting

Blak uses mfussenegger/nvim-lint to run standalone linters — anything that isn’t already an LSP. It fires on the events in lint.events (default: BufWritePost, BufReadPost, InsertLeave).

Spec: lua/blak/plugins/formatting.lua (formatting and linting share a file).

Defaults

lint = {
events = { "BufWritePost", "BufReadPost", "InsertLeave" },
linters_by_ft = {},
}

No linters are configured in core. Linters arrive via language extras:

ExtraAdds
lang.typescripteslint_d for js/ts/jsx/tsx
lang.typescript-tsgoeslint_d for js/ts/jsx/tsx
lang.pythonruff for python
lang.gogolangcilint for go
lang.markdownmarkdownlint for markdown

lang.python-pro uses Ruff’s native LSP diagnostics and code actions instead of adding a separate nvim-lint entry, so it avoids duplicate Ruff diagnostics.

Adding a linter

-- ~/.config/blak/lua/blak/user.lua
return {
lint = {
linters_by_ft = {
sh = { "shellcheck" },
dockerfile = { "hadolint" },
},
},
mason = {
ensure_installed = { "shellcheck", "hadolint" },
},
}

Or wrap it in an extra so it’s reversible.

Tuning when it runs

return {
lint = {
-- Only on write, not on every InsertLeave or BufReadPost
events = { "BufWritePost" },
},
}

Customizing a linter

nvim-lint exposes each linter as require("lint").linters.<name>. Tweak in user.lua:

vim.schedule(function()
require("lint").linters.shellcheck.args = {
"--severity=warning",
"--shell=bash",
"-",
}
end)

Silent on missing tools

If a linter isn’t installed (shellcheck missing from $PATH, say), nvim-lint stays silent — no spam in your messages. The diagnostic just won’t appear. Run :BlakToolsInstall after adding a Mason-installable linter, or install it yourself.

Inspecting

:lua print(vim.inspect(require("lint").linters_by_ft))
:lua require("lint").try_lint() " trigger now
:lua require("lint").get_running() " what's running