mfussenegger / nvim-lint

An asynchronous linter plugin for Neovim complementary to the built-in Language Server Protocol support.
GNU General Public License v3.0
1.88k stars 198 forks source link

feature request: from_patterns #625

Closed Kamilcuk closed 3 weeks ago

Kamilcuk commented 1 month ago

There are situations where tools output several patterns. This is internally handled separately:

https://github.com/mfussenegger/nvim-lint/blob/efc6fc83f0772283e064c53a8f9fb5645bde0bc0/lua/lint/linters/ruby.lua#L8

I propose to add a "from_patterns" function, that will take several patterns, and deprecated from_pattern to reduce API.

parser = require('lint.parser').from_patterns({
 {
    pattern1,
    groups1,
    nil,
    { ['severity'] = vim.diagnostic.severity.WARN, ['source'] = 'ruby' }
 },
 {
    pattern2,
    groups2,
    nil,
    { ['severity'] = vim.diagnostic.severity.ERROR, ['source'] = 'ruby' }
  },
},

Thanks, nvim-lint is amazing, I am using it and it is great.

Kamilcuk commented 1 month ago

After a bit of tinkering I was able to do the following design:


---@class LinterSpec
---@field [1] string
---@field pattern? string
---@field [2] string[]
---@field groups? string[]
---@field [3] table<string, vim.diagnostic.Severity>
---@field severity_map? table<string, vim.diagnostic.Severity>
---@field [4] table
---@field defaults? table
---@field [5] {col_offset?: integer, end_col_offset?: integer, lnum_offset?: integer, end_lnum_offset?: integer}
---@field opts? {col_offset?: integer, end_col_offset?: integer, lnum_offset?: integer, end_lnum_offset?: integer}
---@field col_offset? integer
---@field end_col_offset? integer
---@field lnum_offset? integer
---@field end_lnum_offset? integer

---@class LintersSpec
---@field defaults? table

---@param patterns LinterSpec[]|LintersSpec
local function from_patterns(patterns)
  return function(output, bufnr)
    local diagnostics = {}
    for _, pattern in ipairs(patterns) do
      local args = {
        pattern.pattern or pattern[1],
        pattern.groups or pattern[2],
        pattern.severity_map or pattern[3],
        pattern.defaults or pattern[4] or patterns.defaults,
        pattern.opts or pattern[5] or {
          col_offset = pattern.col_offset,
          end_col_offset = pattern.end_col_offset,
          lnum_offset = pattern.lnum_offset,
          end_lnum_offset = pattern.end_lnum_offset,
        },
      }
      local result = require("lint.parser").from_pattern(unpack(args))(output, bufnr)
      for _, diagnostic in ipairs(result) do
        table.insert(diagnostics, diagnostic)
      end
    end
    return diagnostics
  end
end

-- Add nomad as a linter for hcl files.
require("lint").linters.nomad = {
  name = "nomad",
  cmd = "nomad",
  stdin = false,
  append_fname = true,
  args = { "job", "validate" },
  stream = "both",
  ignore_exitcode = false,
  parser = from_patterns {
    defaults = { severity = vim.diagnostic.severity.ERROR },
    {
      "[^:]+:(%d+): (.+)",
      { "lnum", "message" },
    },
    {
      "[^:]+:(%d+),(%d+)-(%d+): (.+)",
      { "lnum", "col", "col_end", "message" },
    },
  },
}
require("lint").linters_by_ft = {
  hcl = { "nomad" },
}
mfussenegger commented 1 month ago

I'd rather not add yet another variant given that errorformat already supports multiple patterns and as far as I can tell it's rare for linters to output several patterns.

Can the output format of nomad be configured? What other linters would need this?

mfussenegger commented 3 weeks ago

With https://github.com/mfussenegger/nvim-lint/pull/635 you should be able to use something like this instead:

local pattern = function(line)
  local lnum, message = line:match("[^:]+:(%d+): (.+)")
  if lnum then
    return {lnum, 0, 0, message}
  else
    return {line:match("[^:]+:(%d+),(%d+)-(%d+): (.+)")}
  end
end
local groups = {"lnum", "col", "end_col", "message"}
return {
  cmd = "nomad",
  args = {"job", "validate"},
  ignore_exitcode = true,
  stdin = false,
  stream = "stderr",
  parser = require("lint.parser").from_pattern(pattern, groups)
}