From 8beb1967fda06d4b940991b6f8b4ad0c6e2309b9 Mon Sep 17 00:00:00 2001 From: Chris Grieser <73286100+chrisgrieser@users.noreply.github.com> Date: Sun, 17 Nov 2024 02:57:42 +0100 Subject: [PATCH] feat(ignoreRule): support comments disabling multiple rules (#2) --- README.md | 15 +++++++++-- lua/rulebook/config.lua | 3 +++ lua/rulebook/data/add-ignore-rule-comment.lua | 15 ++++++++++- lua/rulebook/diagnostic-actions.lua | 26 +++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7509406..ef0718f 100644 --- a/README.md +++ b/README.md @@ -152,12 +152,23 @@ require("rulebook").setup = ({ shellcheck = { comment = "# shellcheck disable=%s", location = "prevLine", + multiRuleIgnore = true, + multiRuleSeparator = ",", }, -- ... (full list of sources with builtin support can be found in the README) yourCustomSource = { -- exact, case-sensitive source-name - comment = "// disabling-comment %s", -- `%s` will be replaced with rule-id - location = "prevLine", -- "prevLine"|"sameLine"|"encloseLine" + -- `%s` will be replaced with rule-id + comment = "// disabling-comment %s", + + ---@type "prevLine"|"sameLine"|"encloseLine" + location = "sameLine", + + --if whether multiple rules can be ignored with one comment, defaults to `false` + multiRuleIgnore = true, + + -- separator for multiple rule-ids, defaults to ", " + multiRuleSeparator = ",", } -- if location is "encloseLine", needs to be a list of two strings diff --git a/lua/rulebook/config.lua b/lua/rulebook/config.lua index 43e3be6..4338d32 100644 --- a/lua/rulebook/config.lua +++ b/lua/rulebook/config.lua @@ -17,6 +17,9 @@ local function validateIgnoreComment(config, commentKeyName) elseif location ~= "encloseLine" and comType ~= "string" and comType ~= "function" then errorMsg = ("`%s` requires `%s` to be a string or function"):format(location, commentKeyName) end + if location == "encloseLine" and config.multiRuleIgnore then + errorMsg = "`encloseLine` does not support `multiRuleIgnore`." + end return errorMsg end diff --git a/lua/rulebook/data/add-ignore-rule-comment.lua b/lua/rulebook/data/add-ignore-rule-comment.lua index 2e309e2..37f922f 100644 --- a/lua/rulebook/data/add-ignore-rule-comment.lua +++ b/lua/rulebook/data/add-ignore-rule-comment.lua @@ -3,6 +3,8 @@ ---@field location "prevLine"|"sameLine"|"encloseLine" "encloseLine" is a list with two strings, one to be inserted before and one after ---@field docs string used for auto-generated docs ---@field doesNotUseCodes? boolean the linter does not use codes/rule-ids +---@field multiRuleIgnore? boolean supports multiple rule ignores, assuming codes are separated by commas +---@field multiRuleSeparator? string defaults to ", " ---INFO the key must be named like the exact, case-sensitive `diagnostic.source` -------------------------------------------------------------------------------- @@ -13,16 +15,20 @@ M = { comment = "# shellcheck disable=%s", location = "prevLine", docs = "https://www.shellcheck.net/wiki/Ignore", + multiRuleIgnore = true, + multiRuleSeparator = ",", -- with space throws this parsing error: https://www.shellcheck.net/wiki/SC1073 }, selene = { comment = "-- selene: allow(%s)", location = "prevLine", docs = "https://kampfkarren.github.io/selene/usage/filtering.html#allowingdenying-lints-for-an-entire-file", + multiRuleIgnore = true, }, ["Lua Diagnostics."] = { -- Lua LSP comment = "---@diagnostic disable-line: %s", location = "sameLine", -- prevLine already available via code action docs = "https://luals.github.io/wiki/annotations/#diagnostic", + multiRuleIgnore = true, }, yamllint = { comment = "# yamllint disable-line rule:%s", @@ -33,6 +39,7 @@ M = { comment = "/* stylelint-disable-next-line %s */", location = "prevLine", docs = "https://stylelint.io/user-guide/ignore-code/", + multiRuleIgnore = true, }, LTeX = { comment = { "", "" }, @@ -48,21 +55,25 @@ M = { comment = "# pylint: disable=%s", location = "sameLine", docs = "https://pylint.readthedocs.io/en/latest/user_guide/messages/message_control.html", + multiRuleIgnore = true, }, Pyright = { comment = "# pyright: ignore [%s]", location = "sameLine", docs = "https://microsoft.github.io/pyright/#/comments", + multiRuleIgnore = true, }, Ruff = { comment = "# noqa: %s", location = "sameLine", docs = "https://docs.astral.sh/ruff/linter/#error-suppression", + multiRuleIgnore = true, }, eslint = { comment = "// eslint-disable-next-line %s", location = "prevLine", docs = "https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1", + multiRuleIgnore = true, }, biome = { -- biome works for css and js, so the comment syntax is dependent on the filetype @@ -71,7 +82,8 @@ M = { return vim.bo.commentstring:format(ignoreText) end, location = "prevLine", - docs = "https://biomejs.dev/linter/#ignoring-code", + docs = "https://biomejs.dev/linter/#ignore-code", + multiRuleIgnore = false, -- apparently not supported }, typescript = { comment = "// @ts-expect-error", -- https://typescript-eslint.io/rules/prefer-ts-expect-error/ @@ -95,6 +107,7 @@ M = { comment = "// NOLINT(%s)", location = "sameLine", docs = "https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics", + multiRuleIgnore = true, }, alex = { comment = "", diff --git a/lua/rulebook/diagnostic-actions.lua b/lua/rulebook/diagnostic-actions.lua index 0796fd3..5d1f55a 100644 --- a/lua/rulebook/diagnostic-actions.lua +++ b/lua/rulebook/diagnostic-actions.lua @@ -77,9 +77,25 @@ function actions.ignoreRule(diag) local ignoreComment = sourceConf.comment if type(ignoreComment) == "function" then ignoreComment = ignoreComment(diag) end + -- used with `str.match`, this pattern will return the already ignored code(s) + local existingRulePattern = vim.pesc(ignoreComment[1] or ignoreComment) + :gsub("%%%%s", "([%%w,-_]+)") .. "%s*$" + ----------------------------------------------------------------------------- if sourceConf.location == "prevLine" then ---@cast ignoreComment string + + if sourceConf.multiRuleIgnore then + local prevLine = vim.api.nvim_buf_get_lines(0, prevLn - 1, prevLn, false)[1] + local oldCode = prevLine:match(existingRulePattern) + if oldCode then + local sep = sourceConf.multiRuleSeparator or ", " + diag.code = oldCode .. sep .. diag.code + vim.api.nvim_buf_set_lines(0, prevLn - 1, prevLn, false, {}) -- = deletes previous line + prevLn = prevLn - 1 -- account for the deleted line + end + end + local comment = indent .. ignoreComment:format(diag.code) vim.api.nvim_buf_set_lines(0, prevLn, prevLn, false, { comment }) ----------------------------------------------------------------------------- @@ -87,6 +103,16 @@ function actions.ignoreRule(diag) ---@cast ignoreComment string local curLine = vim.api.nvim_get_current_line():gsub("%s+$", "") + if sourceConf.multiRuleIgnore then + local oldCode = curLine:match(existingRulePattern) + if oldCode then + diag.code = oldCode .. ", " .. diag.code + curLine = curLine + :gsub(existingRulePattern, "") -- remove old ignore comment + :gsub("%s+$", "") + end + end + local comment = ignoreComment:format(diag.code) local extraSpace = vim.bo.filetype == "python" and " " or "" -- formatters expect an extra space vim.api.nvim_set_current_line(curLine .. " " .. extraSpace .. comment)