feat(clangd): add clang-tidy linter for clang-analyzer checks
This commit is contained in:
@@ -164,6 +164,30 @@ function M.setup()
|
|||||||
},
|
},
|
||||||
single_file_support = true,
|
single_file_support = true,
|
||||||
on_attach = M.with_defaults("clangd", function(_, bufnr)
|
on_attach = M.with_defaults("clangd", function(_, bufnr)
|
||||||
|
linter.add(bufnr, {
|
||||||
|
cmd = {
|
||||||
|
"clang-tidy",
|
||||||
|
"-p=build",
|
||||||
|
"--quiet",
|
||||||
|
"--checks=-*,clang-analyzer-*",
|
||||||
|
"%file%",
|
||||||
|
},
|
||||||
|
events = { "BufWritePost" },
|
||||||
|
clear_events = { "TextChanged", "TextChangedI" },
|
||||||
|
stdin = false,
|
||||||
|
stdout = true,
|
||||||
|
pattern = "^.+:(%d+):(%d+): (%w+): (.*) %[(.*)%]$",
|
||||||
|
groups = { "lnum", "col", "severity", "message", "code" },
|
||||||
|
source = "clang-tidy",
|
||||||
|
severity_map = {
|
||||||
|
error = vim.diagnostic.severity.ERROR,
|
||||||
|
warning = vim.diagnostic.severity.WARN,
|
||||||
|
note = vim.diagnostic.severity.HINT,
|
||||||
|
},
|
||||||
|
zero_idx_col = true,
|
||||||
|
zero_idx_lnum = true,
|
||||||
|
ignore_stderr = true,
|
||||||
|
})
|
||||||
keymap.set(bufnr, {
|
keymap.set(bufnr, {
|
||||||
{
|
{
|
||||||
mode = "n",
|
mode = "n",
|
||||||
|
|||||||
+65
-4
@@ -1,5 +1,5 @@
|
|||||||
local util = require("ow.util")
|
|
||||||
local log = require("ow.log")
|
local log = require("ow.log")
|
||||||
|
local util = require("ow.util")
|
||||||
|
|
||||||
---@class Linter
|
---@class Linter
|
||||||
---@field namespace number
|
---@field namespace number
|
||||||
@@ -36,21 +36,42 @@ M.__index = M
|
|||||||
---@field deprecated? string[]
|
---@field deprecated? string[]
|
||||||
|
|
||||||
---@class LinterConfig
|
---@class LinterConfig
|
||||||
---@field cmd string[] Command to run. The following keywords get replaces by the specified values:
|
--- Command to run. The following keywords get replaces by the specified values:
|
||||||
--- * %file% - path to the current file
|
--- * %file% - path to the current file
|
||||||
--- * %filename% - name of the current file
|
--- * %filename% - name of the current file
|
||||||
|
---@field cmd string[]
|
||||||
|
--- Events that trigger the linter (default: TextChanged, TextChangedI)
|
||||||
|
---@field events? string[]
|
||||||
|
--- Events that clear diagnostics
|
||||||
|
---@field clear_events? string[]
|
||||||
|
--- Pass buffer content via stdin (default: false)
|
||||||
---@field stdin? boolean
|
---@field stdin? boolean
|
||||||
|
--- Read diagnostics from stdout (default: false)
|
||||||
---@field stdout? boolean
|
---@field stdout? boolean
|
||||||
|
--- Read diagnostics from stderr (default: false)
|
||||||
---@field stderr? boolean
|
---@field stderr? boolean
|
||||||
|
--- Regex pattern to parse diagnostic lines (required if not using json)
|
||||||
---@field pattern? string
|
---@field pattern? string
|
||||||
|
--- Named capture groups for pattern matching (required if not using json)
|
||||||
---@field groups? Group[]
|
---@field groups? Group[]
|
||||||
|
--- Map severity strings to vim diagnostic levels
|
||||||
---@field severity_map? table<string, vim.diagnostic.Severity>
|
---@field severity_map? table<string, vim.diagnostic.Severity>
|
||||||
|
--- Source name for diagnostics (default: command name)
|
||||||
---@field source? string
|
---@field source? string
|
||||||
|
--- Debounce delay in ms (default: 100)
|
||||||
---@field debounce? number
|
---@field debounce? number
|
||||||
|
--- Configuration for JSON output parsing
|
||||||
---@field json? JsonConfig
|
---@field json? JsonConfig
|
||||||
|
--- Map diagnostic codes to tags (unnecessary/deprecated)
|
||||||
---@field tags? DiagnosticTagMap
|
---@field tags? DiagnosticTagMap
|
||||||
|
--- Line numbers are 0-indexed (default: false, 1-indexed)
|
||||||
---@field zero_idx_lnum? boolean
|
---@field zero_idx_lnum? boolean
|
||||||
|
--- Column numbers are 0-indexed (default: false, 1-indexed)
|
||||||
---@field zero_idx_col? boolean
|
---@field zero_idx_col? boolean
|
||||||
|
--- Don't log stderr as errors (default: false)
|
||||||
|
---@field ignore_stderr? boolean
|
||||||
|
--- Post-process diagnostics
|
||||||
|
---@field hook? fun(self: Linter, diagnostics: vim.Diagnostic[])
|
||||||
M.config = {}
|
M.config = {}
|
||||||
|
|
||||||
-- Extract a value from a JSON object using a path
|
-- Extract a value from a JSON object using a path
|
||||||
@@ -227,6 +248,22 @@ function M.validate(config)
|
|||||||
end,
|
end,
|
||||||
"list of strings",
|
"list of strings",
|
||||||
},
|
},
|
||||||
|
events = {
|
||||||
|
config.events,
|
||||||
|
function(t)
|
||||||
|
return util.is_list(t, "string")
|
||||||
|
end,
|
||||||
|
true,
|
||||||
|
"list of strings",
|
||||||
|
},
|
||||||
|
clear_events = {
|
||||||
|
config.clear_events,
|
||||||
|
function(t)
|
||||||
|
return util.is_list(t, "string")
|
||||||
|
end,
|
||||||
|
true,
|
||||||
|
"list of strings",
|
||||||
|
},
|
||||||
stdin = { config.stdin, "boolean", true },
|
stdin = { config.stdin, "boolean", true },
|
||||||
stdout = { config.stdout, "boolean", true },
|
stdout = { config.stdout, "boolean", true },
|
||||||
stderr = { config.stderr, "boolean", true },
|
stderr = { config.stderr, "boolean", true },
|
||||||
@@ -251,6 +288,9 @@ function M.validate(config)
|
|||||||
source = { config.source, "string", true },
|
source = { config.source, "string", true },
|
||||||
json = { config.json, "table", true },
|
json = { config.json, "table", true },
|
||||||
tags = { config.tags, "table", true },
|
tags = { config.tags, "table", true },
|
||||||
|
zero_idx_lnum = { config.zero_idx_lnum, "boolean", true },
|
||||||
|
zero_idx_col = { config.zero_idx_col, "boolean", true },
|
||||||
|
ignore_stderr = { config.ignore_stderr, "boolean", true },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -299,7 +339,11 @@ function M:run()
|
|||||||
|
|
||||||
if self.config.stderr then
|
if self.config.stderr then
|
||||||
output = out.stderr or ""
|
output = out.stderr or ""
|
||||||
elseif out.stderr and out.stderr ~= "" then
|
elseif
|
||||||
|
not self.config.ignore_stderr
|
||||||
|
and out.stderr
|
||||||
|
and out.stderr ~= ""
|
||||||
|
then
|
||||||
log.error(out.stderr)
|
log.error(out.stderr)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -317,6 +361,9 @@ function M:run()
|
|||||||
|
|
||||||
local diagnostics = self:process_json_output(json)
|
local diagnostics = self:process_json_output(json)
|
||||||
if diagnostics then
|
if diagnostics then
|
||||||
|
if self.config.hook then
|
||||||
|
self.config.hook(self, diagnostics)
|
||||||
|
end
|
||||||
vim.diagnostic.set(self.namespace, self.bufnr, diagnostics)
|
vim.diagnostic.set(self.namespace, self.bufnr, diagnostics)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -347,6 +394,9 @@ function M:run()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.config.hook then
|
||||||
|
self.config.hook(self, diagnostics)
|
||||||
|
end
|
||||||
vim.diagnostic.set(self.namespace, self.bufnr, diagnostics)
|
vim.diagnostic.set(self.namespace, self.bufnr, diagnostics)
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
@@ -367,6 +417,7 @@ function M.add(bufnr, config)
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.debounce = config.debounce or 100
|
config.debounce = config.debounce or 100
|
||||||
|
config.events = config.events or { "TextChanged", "TextChangedI" }
|
||||||
|
|
||||||
local linter = {
|
local linter = {
|
||||||
namespace = vim.api.nvim_create_namespace("ow.lsp.linter"),
|
namespace = vim.api.nvim_create_namespace("ow.lsp.linter"),
|
||||||
@@ -386,13 +437,23 @@ function M.add(bufnr, config)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
|
vim.api.nvim_create_autocmd(config.events, {
|
||||||
buffer = linter.bufnr,
|
buffer = linter.bufnr,
|
||||||
callback = util.debounce(function()
|
callback = util.debounce(function()
|
||||||
linter:run()
|
linter:run()
|
||||||
end, linter.config.debounce),
|
end, linter.config.debounce),
|
||||||
group = linter.augroup,
|
group = linter.augroup,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if config.clear_events then
|
||||||
|
vim.api.nvim_create_autocmd(config.clear_events, {
|
||||||
|
buffer = linter.bufnr,
|
||||||
|
callback = function()
|
||||||
|
vim.diagnostic.reset(linter.namespace, linter.bufnr)
|
||||||
|
end,
|
||||||
|
group = linter.augroup,
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
Reference in New Issue
Block a user