Skip to content

Exit code #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Easily configure your custom formatter if not in the defaults:
ignore_error -- boolean: when has lsp error ignore format
find -- string: format if the file is found in the lsp root dir
env -- table: environment variables passed to cmd (key value pair)
check_exit_code -- function: ignore format if return false

--special
fn -- function: if fn is set other field will not take effect
Expand Down Expand Up @@ -121,6 +122,8 @@ Consult the [builtin tools](https://github.com/nvimdev/guard.nvim/tree/main/lua%
#### Linters

- [clang-tidy](https://clang.llvm.org/extra/clang-tidy/)
- [hadolint](https://github.com/hadolint/hadolint)
- [luacheck](https://github.com/lunarmodules/luacheck)
- [pylint](https://github.com/PyCQA/pylint)
- [rubocop](https://github.com/rubocop/rubocop)
- [shellcheck](https://github.com/koalaman/shellcheck)
Expand Down
3 changes: 2 additions & 1 deletion doc/guard.nvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ Easily configure your custom formatter if not in the defaults:
ignore_error -- boolean: when has lsp error ignore format
find -- string: format if the file is found in the lsp root dir
env -- table: environment variables passed to cmd (key value pair)

check_exit_code -- function: ignore format if return false

--special
fn -- function: if fn is set other field will not take effect
}
Expand Down
106 changes: 105 additions & 1 deletion lua/guard/lint.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ local function do_lint(buf)
lint.lines = prev_lines
local data = spawn(lint)
if #data > 0 then
results = lint.output_fmt(data, buf)
results = lint.parse(data, buf)
end
end

Expand Down Expand Up @@ -80,7 +80,111 @@ local function diag_fmt(buf, lnum, col, message, severity, source)
}
end

local severities = {
error = 1,
warning = 2,
info = 3,
style = 4,
}

local from_opts = {
offset = 1,
source = nil,
severities = severities,
}

local json_opts = {
get_diagnostics = function(...)
return vim.json.decode(...)
end,
attributes = {
lnum = 'line',
col = 'column',
message = 'message',
code = 'code',
severity = 'severity',
},
lines = nil,
}

local function from_json(opts)
opts = vim.tbl_deep_extend('force', from_opts, opts or {})
opts = vim.tbl_deep_extend('force', json_opts, opts)

return function(result, buf)
local diags, offences = {}, {}

if opts.lines then
vim.tbl_map(function(line)
offences[#offences + 1] = opts.get_diagnostics(line)
end, vim.split(result, '\n', { trimempty = true }))
else
offences = opts.get_diagnostics(result)
end

vim.tbl_map(function(mes)
diags[#diags + 1] = diag_fmt(
buf,
tonumber(mes[opts.attributes.lnum] - opts.offset),
tonumber(mes[opts.attributes.col] - opts.offset),
('%s [%s]'):format(mes[opts.attributes.message], mes[opts.attributes.code]),
opts.severities[mes[opts.attributes.severity]],
opts.source
)
end, offences or {})

return diags
end
end

local regex_opts = {
regex = nil,
groups = { 'lnum', 'col', 'severity', 'code', 'message' },
}

local function from_regex(opts)
opts = vim.tbl_deep_extend('force', from_opts, opts or {})
opts = vim.tbl_deep_extend('force', regex_opts, opts)

return function(result, buf)
local diags, offences = {}, {}

local lines = vim.split(result, '\n', { trimempty = true })

for _, line in ipairs(lines) do
local offence = {}

local matches = { line:match(opts.regex) }

-- regex matched
if #matches == #opts.groups then
for i = 1, #opts.groups do
offence[opts.groups[i]] = matches[i]
end

offences[#offences + 1] = offence
end
end

vim.tbl_map(function(mes)
diags[#diags + 1] = diag_fmt(
buf,
tonumber(mes.lnum) - opts.offset,
tonumber(mes.col) - opts.offset,
('%s [%s]'):format(mes.message, mes.code),
opts.severities[mes.severity],
opts.source
)
end, offences)

return diags
end
end

return {
register_lint = register_lint,
diag_fmt = diag_fmt,
from_json = from_json,
from_regex = from_regex,
severities = severities,
}
14 changes: 13 additions & 1 deletion lua/guard/spawn.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ local function on_failed(msg)
end)
end

local function exit_code_valid(check_exit_code, exit_code)
if check_exit_code then
return check_exit_code(exit_code)
end
return exit_code == 0
end

--TODO: replace by vim.system when neovim 0.10 released
local function spawn(opt)
assert(opt, 'missing opt param')
Expand Down Expand Up @@ -66,7 +73,12 @@ local function spawn(opt)
end
end)

coroutine.resume(co, table.concat(chunks))
if exit_code_valid(opt.check_exit_code, exit_code) then
coroutine.resume(co, table.concat(chunks))
else -- fail silently
coroutine.resume(co)
return
end
end)

if not handle then
Expand Down
48 changes: 11 additions & 37 deletions lua/guard/tools/linter/clang-tidy.lua
Original file line number Diff line number Diff line change
@@ -1,42 +1,16 @@
local diag_fmt = require('guard.lint').diag_fmt
local lint = require('guard.lint')

return {
cmd = 'clang-tidy',
args = { '--quiet' },
stdin = false,
output_fmt = function(result, buf)
local map = {
'error',
'warning',
'information',
'hint',
'note',
}

local messages = vim.split(result, '\n')
local diags = {}
vim.tbl_map(function(mes)
local message
local severity
for idx, t in ipairs(map) do
local _, p = mes:find(t)
if p then
message = mes:sub(p + 2, #mes)
severity = idx
local pos = mes:match([[(%d+:%d+)]])
local lnum, col = unpack(vim.split(pos, ':'))
diags[#diags + 1] = diag_fmt(
buf,
tonumber(lnum) - 1,
tonumber(col),
message,
severity > 4 and 4 or severity,
'clang-tidy'
)
end
end
end, messages)

return diags
end,
parse = lint.from_regex({
source = 'clang-tidy',
regex = ':(%d+):(%d+):%s+(%w+):%s+(.-)%s+%[(.-)%]',
groups = { 'lnum', 'col', 'severity', 'message', 'code' },
severities = {
information = lint.severities.info,
hint = lint.severities.info,
note = lint.severities.style,
},
}),
}
11 changes: 11 additions & 0 deletions lua/guard/tools/linter/hadolint.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
return {
cmd = 'hadolint',
args = { '--no-fail', '--format=json' },
stdin = true,
parse = require('guard.lint').from_json({
attributes = {
severity = 'level',
},
source = 'hadolint',
}),
}
16 changes: 16 additions & 0 deletions lua/guard/tools/linter/luacheck.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local lint = require('guard.lint')

return {
cmd = 'luacheck',
args = { '--formatter', 'plain', '--codes', '-', '--filename' },
fname = true,
stdin = true,
parse = lint.from_regex({
regex = '(%d+):(%d+):%s%((%a)(%w+)%) (.+)',
severities = {
E = lint.severities.error,
W = lint.severities.warning,
},
source = 'luacheck',
}),
}
40 changes: 17 additions & 23 deletions lua/guard/tools/linter/pylint.lua
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
local diag_fmt = require('guard.lint').diag_fmt
local lint = require('guard.lint')

return {
cmd = 'pylint',
args = { '--from-stdin' },
args = { '--from-stdin', '--output-format', 'json' },
stdin = true,
output_fmt = function(result, buf)
local output = vim.split(result, '\n')
local patterns = {
'E%d+',
'W%d+',
'C%d+',
}
local diags = {}
for _, line in ipairs(output) do
for i, pattern in ipairs(patterns) do
if line:find(pattern) then
local pos = line:match('py:(%d+:%d+)')
local lnum, col = unpack(vim.split(pos, ':'))
local mes = line:match('%d:%s(.*)')
diags[#diags + 1] = diag_fmt(buf, tonumber(lnum) - 1, tonumber(col), mes, i, 'pylint')
end
end
end

return diags
end,
parse = lint.from_json({
attributes = {
severity = 'type',
code = 'symbol',
},
severities = {
-- https://pylint.readthedocs.io/en/stable/user_guide/usage/output.html
convention = lint.severities.info,
refactor = lint.severities.info,
informational = lint.severities.info,
fatal = lint.severities.error,
},
source = 'pylint',
}),
}
48 changes: 17 additions & 31 deletions lua/guard/tools/linter/rubocop.lua
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
local diag_fmt = require('guard.lint').diag_fmt
local lint = require('guard.lint')

return {
cmd = 'bundle',
args = { 'exec', 'rubocop', '--format', 'json', '--force-exclusion', '--stdin' },
stdin = true,
output_fmt = function(result, buf)
local severities = {
info = 3,
convention = 3,
refactor = 4,
warning = 2,
error = 1,
fatal = 1,
}

local offenses = vim.json.decode(result).files[1].offenses
local diags = {}

if #offenses < 1 then
return {}
end

vim.tbl_map(function(mes)
diags[#diags + 1] = diag_fmt(
buf,
tonumber(mes.location.line) - 1,
tonumber(mes.location.column) - 1,
mes.message .. ' [' .. mes.cop_name .. ']',
severities[mes.severity] or 4,
'rubocop'
)
end, offenses)

return diags
end,
parse = lint.from_json({
get_diagnostics = function(...)
return vim.json.decode(...).files[1].offenses
end,
attributes = {
lnum = 'location.line',
col = 'location.column',
code = 'cop_name',
},
severities = {
convention = lint.severities.info,
refactor = lint.severities.style,
fatal = lint.severities.error,
},
}),
}
Loading