Skip to content

Commit

Permalink
Add support for lpeg patterns and match functions to from_pattern
Browse files Browse the repository at this point in the history
An alternative and more general approach to #625
  • Loading branch information
mfussenegger committed Aug 16, 2024
1 parent e9b481a commit 40df9fe
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 12 deletions.
39 changes: 29 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,26 @@ The function takes two arguments: `errorformat` and `skeleton` (optional).

### from_pattern

Creates a parser function from a pattern.

```lua
parser = require('lint.parser').from_pattern(pattern, groups, severity_map, defaults, opts)
```

The function allows to parse the linter's output using a Lua regular expression pattern.
### 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.


- pattern: The regular expression pattern applied on each line of the output
- groups: The groups specified by the pattern
### groups

The groups specify the result format of the pattern.
Available groups:

- `lnum`
Expand All @@ -305,7 +316,11 @@ local pattern = '[^:]+:(%d+):(%d+):(%w+):(.+)'
local groups = { 'lnum', 'col', 'code', 'message' }
```

- severity: A mapping from severity codes to diagnostic codes
The captures in the pattern correspond to the group at the same position.

### severity

A mapping from severity codes to diagnostic codes

``` lua
default_severity = {
Expand All @@ -316,18 +331,22 @@ default_severity = {
}
```

- defaults: The defaults diagnostic values
### defaults

The defaults diagnostic values

```lua
defaults = {["source"] = "mylint-name"}
```

- opts: Additional options
### 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.
- `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
Expand Down
23 changes: 21 additions & 2 deletions lua/lint/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ local normalize = (vim.fs ~= nil and vim.fs.normalize ~= nil)

--- Parse a linter's output using a Lua pattern
---
---@param pattern string
---@param pattern string|vim.lpeg.Pattern|fun(line: string):string[]
---@param groups string[]
---@param severity_map? table<string, vim.diagnostic.Severity>
---@param defaults? table
Expand All @@ -55,10 +55,29 @@ function M.from_pattern(pattern, groups, severity_map, defaults, opts)
defaults = defaults or {}
severity_map = severity_map or {}
opts = opts or {}

local type_ = type(pattern)
local matchline
if type_ == "string" then
matchline = function(line)
return { line:match(pattern) }
end
elseif type_ == "function" then
matchline = pattern
else
matchline = function(line)
return { pattern:match(line) }
end
end


-- Like vim.diagnostic.match but also checks if a `file` group matches the buffer path
-- Some linters produce diagnostics for the full project and this should only produce buffer diagnostics
local match = function(linter_cwd, buffer_path, line)
local matches = { line:match(pattern) }
local ok, matches = pcall(matchline, line)
if not ok then
error(string.format("pattern match failed on line: %s with error: %q", line, matches))
end
if not next(matches) then
return nil
end
Expand Down
54 changes: 54 additions & 0 deletions tests/parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,58 @@ bar:209:14 Bigger mistake
}
assert.are.same(expected, result)
end)

it("supports lpeg pattern", function()
if not vim.re then
return
end
local pattern = vim.re.compile("{[0-9]+} ':' { (.*) }")
local groups = { 'lnum', 'message' }
local parser = require('lint.parser').from_pattern(pattern, groups)
local output = [[
10:Big mistake
14:Bigger mistake
]]
local result = parser(output, 0)
local expected = {
{
message = 'Big mistake',
lnum = 9,
end_lnum = 9,
col = 0,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
},
{
message = 'Bigger mistake',
lnum = 13,
col = 0,
end_lnum = 13,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
},
}
assert.are.same(expected, result)
assert.are.same({}, parser("no-match", 0))
end)

it("supports match function", function()
local pattern = function(_)
return { 10, "hello" }
end
local groups = { 'lnum', 'message' }
local parser = require('lint.parser').from_pattern(pattern, groups)
local result = parser("foo", 0)
local expected = {
{
message = "hello",
lnum = 9,
col = 0,
end_lnum = 9,
end_col = 0,
severity = vim.diagnostic.severity.ERROR
},
}
assert.are.same(expected, result)
end)
end)

0 comments on commit 40df9fe

Please sign in to comment.