-- Stolen from LazyVim https://github.com/LazyVim/LazyVim/tree/f086bcde253c29be9a2b9c90b413a516f5d5a3b2/lua/lazyvim/plugins return { "mfussenegger/nvim-lint", event = { "VeryLazy", "BufWritePost", "BufReadPost", "InsertLeave" }, opts = { -- Event to trigger linters events = { "BufWritePost", "BufReadPost", "InsertLeave", "CursorHold", "CursorHoldI" }, linters_by_ft = { -- Builtin: https://github.com/mfussenegger/nvim-lint/tree/master/lua/lint/linters markdown = { "markdownlint" }, -- lua = { "luacheck" }, -- TODO revisit this was not working last time typescript = { "biomejs" }, typescriptreact = { "biomejs" }, javascript = { "biomejs" }, javascriptreact = { "biomejs" }, -- Use the "*" filetype to run linters on all filetypes. -- ['*'] = { 'global linter' }, -- Use the "_" filetype to run linters on filetypes that don't have other linters configured. -- ['_'] = { 'fallback linter' }, -- ["*"] = { "typos" }, }, -- LazyVim extension to easily override linter options -- or add custom linters. ---@type table -- -- Options: -- 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 -- -- 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. linters = { luacheck = { args = { "--globals", "vim", "--globals", "NIX", "--globals", "U", "--max_line_length", "240", "--max_code_line_length", "240", "--max_string_line_length", "240", "--max_comment_line_length", "240", "--formatter", "plain", "--codes", "--ranges", "-", }, }, -- TODO add v vet? for v-lang? -- -- Example of using selene only when a selene.toml file is present -- selene = { -- -- `condition` is another LazyVim extension that allows you to -- -- dynamically enable/disable linters based on the context. -- condition = function(ctx) -- return vim.fs.find({ "selene.toml" }, { path = ctx.filename, upward = true })[1] -- end, -- }, }, }, config = function(_, opts) local M = {} local lint = require("lint") for name, linter in pairs(opts.linters) do if type(linter) == "table" and type(lint.linters[name]) == "table" then lint.linters[name] = vim.tbl_deep_extend("force", lint.linters[name], linter) else lint.linters[name] = linter end end lint.linters_by_ft = opts.linters_by_ft function M.debounce(ms, fn) local timer = vim.uv.new_timer() return function(...) local argv = { ... } timer:start(ms, 0, function() timer:stop() vim.schedule_wrap(fn)(unpack(argv)) end) end end function M.lint() -- Use nvim-lint's logic first: -- * checks if linters exist for the full filetype first -- * otherwise will split filetype by "." and add all those linters -- * this differs from conform.nvim which only uses the first filetype that has a formatter local names = lint._resolve_linter_by_ft(vim.bo.filetype) -- Create a copy of the names table to avoid modifying the original. names = vim.list_extend({}, names) -- Add fallback linters. if #names == 0 then vim.list_extend(names, lint.linters_by_ft["_"] or {}) end -- Add global linters. vim.list_extend(names, lint.linters_by_ft["*"] or {}) -- Filter out linters that don't exist or don't match the condition. local ctx = { filename = vim.api.nvim_buf_get_name(0) } ctx.dirname = vim.fn.fnamemodify(ctx.filename, ":h") names = vim.tbl_filter(function(name) local linter = lint.linters[name] if not linter then LazyVim.warn("Linter not found: " .. name, { title = "nvim-lint" }) end return linter and not (type(linter) == "table" and linter.condition and not linter.condition(ctx)) end, names) -- Run linters. if #names > 0 then lint.try_lint(names) end end vim.api.nvim_create_autocmd(opts.events, { group = vim.api.nvim_create_augroup("nvim-lint", { clear = true }), callback = M.debounce(100, M.lint), }) end, }