diff --git a/plugins/utils/todo-comments.nix b/plugins/utils/todo-comments.nix index b95e6e4a..e5c61aa0 100644 --- a/plugins/utils/todo-comments.nix +++ b/plugins/utils/todo-comments.nix @@ -1,22 +1,35 @@ { lib, - helpers, config, pkgs, ... }: with lib; let - cfg = config.plugins.todo-comments; + inherit (lib.nixvim) + defaultNullOpts + keymaps + mkPackageOption + mkCompositeOption + transitionType + ; + types = lib.nixvim.nixvimTypes; - commands = { + keymapsActions = { todoQuickFix = "TodoQuickFix"; todoLocList = "TodoLocList"; todoTrouble = "TodoTrouble"; todoTelescope = "TodoTelescope"; }; in -{ +lib.nixvim.neovim-plugin.mkNeovimPlugin config { + name = "todo-comments"; + originalName = "todo-comments.nvim"; + defaultPackage = pkgs.vimPlugins.todo-comments-nvim; + + maintainers = [ lib.maintainers.khaneliman ]; + + # TODO: Added 2023-11-06, remove after 24.11 imports = [ (mkRemovedOptionModule [ "plugins" @@ -24,267 +37,416 @@ in "keymapsSilent" ] "Use `plugins.todo-comments.keymaps..options.silent`.") ]; - options = { - plugins.todo-comments = helpers.neovim-plugin.extraOptionsOptions // { - enable = mkEnableOption "todo-comments"; - package = helpers.mkPluginPackageOption "todo-comments" pkgs.vimPlugins.todo-comments-nvim; + # TODO: Added 2024-08-16, remove after 24.11 + deprecateExtraOptions = true; + optionsRenamedToSettings = [ + "signs" + "signPriority" + "keywords" + [ + "guiStyle" + "bg" + ] + [ + "guiStyle" + "fg" + ] + "mergeKeywords" + [ + "highlight" + "multiline" + ] + [ + "highlight" + "multilinePattern" + ] + [ + "highlight" + "multilineContext" + ] + [ + "highlight" + "before" + ] + [ + "highlight" + "keyword" + ] + [ + "highlight" + "after" + ] + [ + "highlight" + "pattern" + ] + [ + "highlight" + "commentsOnly" + ] + [ + "highlight" + "maxLineLen" + ] + [ + "highlight" + "exclude" + ] + [ + "colors" + "error" + ] + [ + "colors" + "warning" + ] + [ + "colors" + "info" + ] + [ + "colors" + "hint" + ] + [ + "colors" + "default" + ] + [ + "colors" + "test" + ] + [ + "search" + "command" + ] + [ + "search" + "args" + ] + [ + "search" + "pattern" + ] + ]; - ripgrepPackage = helpers.mkPackageOption { - default = pkgs.ripgrep; - description = "Which package (if any) to be added for file search support in todo-comments."; - }; + settingsOptions = { + signs = defaultNullOpts.mkBool true "Show icons in the signs column."; - signs = helpers.defaultNullOpts.mkBool true "Show icons in the signs column."; + sign_priority = defaultNullOpts.mkInt 8 "Sign priority."; - signPriority = helpers.defaultNullOpts.mkInt 8 "Sign priority."; + keywords = + defaultNullOpts.mkAttrsOf + ( + with types; + submodule { + freeformType = attrsOf anything; + options = { + icon = defaultNullOpts.mkStr null '' + Icon used for the sign, and in search results. + ''; - keywords = - helpers.mkNullOrOption - (types.attrsOf ( - types.submodule { - options = { - icon = helpers.mkNullOrOption types.str '' - Icon used for the sign, and in search results. - ''; + color = defaultNullOpts.mkStr null '' + Can be a hex color, or a named color. + ''; - color = helpers.mkNullOrOption types.str '' - Can be a hex color, or a named color. - ''; + alt = defaultNullOpts.mkListOf types.str null '' + A set of other keywords that all map to this FIX keywords. + ''; - alt = helpers.mkNullOrOption (types.listOf types.str) '' - A set of other keywords that all map to this FIX keywords. - ''; - - signs = helpers.mkNullOrOption types.bool '' - Configure signs for some keywords individually. - ''; - }; - } - )) - '' - Configurations for keywords to be recognized as todo comments. - - Default: - ```nix - { - FIX = { - icon = " "; # Icon used for the sign, and in search results. - color = "error"; # Can be a hex color, or a named color. - alt = [ "FIXME" "BUG" "FIXIT" "ISSUE" ]; # A set of other keywords that all map to this FIX keywords. - }; - TODO = { icon = " "; color = "info"; }; - HACK = { icon = " "; color = "warning"; }; - WARN = { icon = " "; color = "warning"; alt = [ "WARNING" "XXX" ]; }; - PERF = { icon = " "; alt = [ "OPTIM" "PERFORMANCE" "OPTIMIZE" ]; }; - NOTE = { icon = " "; color = "hint"; alt = [ "INFO" ]; }; - TEST = { icon = "⏲ "; color = "test"; alt = [ "TESTING" "PASSED" "FAILED" ]; }; + signs = defaultNullOpts.mkBool null '' + Configure signs for some keywords individually. + ''; }; - ``` + } + ) + { + FIX = { + icon = " "; + color = "error"; + alt = [ + "FIXME" + "BUG" + "FIXIT" + "ISSUE" + ]; + }; + TODO = { + icon = " "; + color = "info"; + }; + HACK = { + icon = " "; + color = "warning"; + }; + WARN = { + icon = " "; + color = "warning"; + alt = [ + "WARNING" + "XXX" + ]; + }; + PERF = { + icon = " "; + alt = [ + "OPTIM" + "PERFORMANCE" + "OPTIMIZE" + ]; + }; + NOTE = { + icon = " "; + color = "hint"; + alt = [ "INFO" ]; + }; + TEST = { + icon = "⏲ "; + color = "test"; + alt = [ + "TESTING" + "PASSED" + "FAILED" + ]; + }; + } + '' + Configurations for keywords to be recognized as todo comments. + ''; + + gui_style = { + fg = defaultNullOpts.mkStr "NONE" '' + The gui style to use for the fg highlight group. + ''; + + bg = defaultNullOpts.mkStr "BOLD" '' + The gui style to use for the bg highlight group. + ''; + }; + + merge_keywords = defaultNullOpts.mkBool true '' + When true, custom keywords will be merged with the default. + ''; + + highlight = { + multiline = defaultNullOpts.mkBool true '' + Enable multiline todo comments. + ''; + + multiline_pattern = defaultNullOpts.mkStr "^." '' + Lua pattern to match the next multiline from the start of the + matched keyword. + ''; + + multiline_context = defaultNullOpts.mkInt 10 '' + Extra lines that will be re-evaluated when changing a line. + ''; + + before = + defaultNullOpts.mkEnumFirstDefault + [ + "" + "fg" + "bg" + ] + '' + Whether to apply the before highlight to the foreground or background. ''; - guiStyle = { - fg = helpers.defaultNullOpts.mkStr "NONE" '' - The gui style to use for the fg highlight group. - ''; + keyword = + defaultNullOpts.mkEnumFirstDefault + [ + "wide" + "fg" + "bg" + "wide_bg" + "wide_fg" + "" + ] + '' + How highlighting is applied to the keyword. - bg = helpers.defaultNullOpts.mkStr "BOLD" '' - The gui style to use for the bg highlight group. + `wide` and `wide_bg` are the same as `bg`, but will also highlight + surrounding characters, `wide_fg` acts accordingly but with `fg`. + ''; + + after = + defaultNullOpts.mkEnumFirstDefault + [ + "fg" + "bg" + "" + ] + '' + Whether to apply the after highlight to the foreground or background. + ''; + + pattern = defaultNullOpts.mkNullable' { + type = with types; either str (listOf str); + pluginDefault = ".*<(KEYWORDS)\\s*:"; + description = '' + Pattern or list of patterns, used for highlighting (vim regex). ''; }; - mergeKeywords = helpers.defaultNullOpts.mkBool true '' - When true, custom keywords will be merged with the default + comments_only = defaultNullOpts.mkBool true '' + Uses treesitter to match keywords in comments only. ''; - highlight = { - multiline = helpers.defaultNullOpts.mkBool true '' - Enable multiline todo comments. - ''; + max_line_len = defaultNullOpts.mkInt 400 '' + Ignore lines longer than this. + ''; - multilinePattern = helpers.defaultNullOpts.mkStr "^." '' - Lua pattern to match the next multiline from the start of the - matched keyword. - ''; + exclude = defaultNullOpts.mkListOf types.str [ ] '' + List of file types to exclude highlighting. + ''; + }; - multilineContext = helpers.defaultNullOpts.mkInt 10 '' - Extra lines that will be re-evaluated when changing a line. - ''; - - before = helpers.defaultNullOpts.mkStr "" '' - "fg" or "bg" or empty. - ''; - - keyword = helpers.defaultNullOpts.mkStr "wide" '' - "fg", "bg", "wide", "wide_bg", "wide_fg" or empty. - (wide and wide_bg is the same as bg, but will also highlight - surrounding characters, wide_fg acts accordingly but with fg). - ''; - - after = helpers.defaultNullOpts.mkStr "fg" '' - "fg" or "bg" or empty. - ''; - - pattern = - helpers.defaultNullOpts.mkNullable (with types; either str (listOf str)) ".*<(KEYWORDS)\\s*:" - '' - Pattern or list of patterns, used for highlighting (vim regex) - - Note: the provided pattern will be embedded as such: `[[PATTERN]]`. - ''; - - commentsOnly = helpers.defaultNullOpts.mkBool true '' - Uses treesitter to match keywords in comments only. - ''; - - maxLineLen = helpers.defaultNullOpts.mkInt 400 '' - Ignore lines longer than this. - ''; - - exclude = helpers.mkNullOrOption (types.listOf types.str) '' - List of file types to exclude highlighting. - ''; - }; - - colors = helpers.mkNullOrOption (types.attrsOf (types.listOf types.str)) '' - List of named colors where we try to extract the guifg from the list - of highlight groups or use the hex color if hl not found as a fallback. - - Default: - ```nix + colors = + defaultNullOpts.mkAttrsOf (types.listOf types.str) { - error = [ "DiagnosticError" "ErrorMsg" "#DC2626" ]; - warning = [ "DiagnosticWarn" "WarningMsg" "#FBBF24" ]; - info = [ "DiagnosticInfo" "#2563EB" ]; - hint = [ "DiagnosticHint" "#10B981" ]; - default = [ "Identifier" "#7C3AED" ]; - test = [ "Identifier" "#FF00FF" ]; - }; - ``` - ''; + error = [ + "DiagnosticError" + "ErrorMsg" + "#DC2626" + ]; + warning = [ + "DiagnosticWarn" + "WarningMsg" + "#FBBF24" + ]; + info = [ + "DiagnosticInfo" + "#2563EB" + ]; + hint = [ + "DiagnosticHint" + "#10B981" + ]; + default = [ + "Identifier" + "#7C3AED" + ]; + test = [ + "Identifier" + "#FF00FF" + ]; + } + '' + List of named colors where we try to extract the guifg from the list + of highlight groups or use the hex color if hl not found as a fallback. + ''; - search = { - command = helpers.defaultNullOpts.mkStr "rg" "Command to use for searching for keywords."; + search = { + command = defaultNullOpts.mkStr "rg" "Command to use for searching for keywords."; - args = helpers.mkNullOrOption (types.listOf types.str) '' - Arguments to use for the search command in list form. - - Default: - ```nix + args = + defaultNullOpts.mkListOf types.str [ "--color=never" "--no-heading" "--with-filename" "--line-number" "--column" - ]; - ``` - ''; + ] + '' + Arguments to use for the search command in list form. + ''; - pattern = helpers.defaultNullOpts.mkStr "\\b(KEYWORDS):" '' - Regex that will be used to match keywords. - Don't replace the (KEYWORDS) placeholder. - - Note: the provided pattern will be embedded as such: `[[PATTERN]]`. - ''; - }; - - keymaps = - let - mkKeymapOption = - optionName: funcName: - helpers.mkCompositeOption "Keymap settings for the `:${funcName}` function." { - key = mkOption { - type = types.str; - description = "Key for the `${funcName}` function."; - }; - - cwd = mkOption { - type = types.nullOr types.str; - description = "Specify the directory to search for comments"; - default = null; - example = "~/projects/foobar"; - }; - - keywords = mkOption { - type = types.nullOr types.str; - description = '' - Comma separated list of keywords to filter results by. - Keywords are case-sensitive. - ''; - default = null; - example = "TODO,FIX"; - }; - - options = helpers.keymaps.mapConfigOptions; - }; - in - mapAttrs mkKeymapOption commands; + pattern = defaultNullOpts.mkStr "\\b(KEYWORDS):" '' + Regex that will be used to match keywords. + Don't replace the (KEYWORDS) placeholder. + ''; }; }; - config = - let - setupOptions = { - inherit (cfg) signs; - sign_priority = cfg.signPriority; - inherit (cfg) keywords; - gui_style = cfg.guiStyle; - merge_keywords = cfg.mergeKeywords; - highlight = { - inherit (cfg.highlight) - multiline - before - keyword - after - exclude - ; - pattern = helpers.ifNonNull' cfg.highlight.pattern (helpers.mkRaw "[[${cfg.highlight.pattern}]]"); - multiline_pattern = cfg.highlight.multilinePattern; - multiline_context = cfg.highlight.multilineContext; - comments_only = cfg.highlight.commentsOnly; - max_line_len = cfg.highlight.maxLineLen; - }; - inherit (cfg) colors; - search = { - inherit (cfg.search) command args; - pattern = helpers.ifNonNull' cfg.search.pattern ( - if isList cfg.search.pattern then - (map helpers.mkRaw cfg.search.pattern) - else - helpers.mkRaw "[[${cfg.search.pattern}]]" - ); - }; - } // cfg.extraOptions; - in - mkIf cfg.enable { - extraPlugins = [ cfg.package ]; - extraPackages = [ cfg.ripgrepPackage ]; - extraConfigLua = '' - require("todo-comments").setup${helpers.toLuaObject setupOptions} - ''; - - keymaps = flatten ( - mapAttrsToList ( - optionName: funcName: - let - keymap = cfg.keymaps.${optionName}; - - cwd = optionalString (keymap.cwd != null) " cwd=${keymap.cwd}"; - keywords = optionalString (keymap.keywords != null) " keywords=${keymap.keywords}"; - in - optional (keymap != null) { - mode = "n"; - inherit (keymap) key options; - action = ":${funcName}${cwd}${keywords}"; - } - ) commands - ); - - # Automatically enable plugins if keymaps have been set - plugins = mkMerge [ - (mkIf (cfg.keymaps.todoTrouble != null) { trouble.enable = true; }) - (mkIf (cfg.keymaps.todoTelescope != null) { telescope.enable = true; }) + settingsExample = { + highlight = { + pattern = [ + ".*<(KEYWORDS)\s*:" + ".*<(KEYWORDS)\s*" ]; }; + }; + + extraOptions = { + keymaps = + let + mkKeymapOption = + optionName: funcName: + mkCompositeOption "Keymap settings for the `:${funcName}` function." { + key = mkOption { + type = types.str; + default = null; + description = "Key for the `${funcName}` function."; + }; + + cwd = mkOption { + type = types.nullOr types.str; + description = "Specify the directory to search for comments"; + default = null; + example = "~/projects/foobar"; + }; + + keywords = mkOption { + type = with types; transitionType str (splitString ",") (nullOr (listOf str)); + description = '' + Comma separated list of keywords to filter results by. + Keywords are case-sensitive. + ''; + default = null; + example = "TODO,FIX"; + }; + + options = keymaps.mapConfigOptions; + }; + in + mapAttrs mkKeymapOption keymapsActions; + + ripgrepPackage = mkPackageOption { + name = "ripgrep"; + default = pkgs.ripgrep; + }; + }; + + extraConfig = cfg: { + assertions = [ + { + assertion = cfg.keymaps.todoTelescope.key or null != null -> config.plugins.telescope.enable; + message = '' + Nixvim(plugins.todo-comments): You have enabled todo-comment's `telescope` integration. + However, you have not enabled the `telescope` plugin itself (`plugins.telescope.enable = true`). + ''; + } + { + assertion = cfg.keymaps.todoTrouble.key or null != null -> config.plugins.trouble.enable; + message = '' + Nixvim(plugins.todo-comments): You have enabled todo-comment's `trouble` integration. + However, you have not enabled the `trouble` plugin itself (`plugins.trouble.enable = true`). + ''; + } + ]; + + extraPackages = [ cfg.ripgrepPackage ]; + + keymaps = lib.pipe cfg.keymaps [ + (filterAttrs (n: v: v != null)) + (mapAttrsToList ( + name: keymap: { + inherit (keymap) key options; + mode = "n"; + action = + let + cmd = keymapsActions.${name}; + cwd = optionalString (keymap.cwd != null) " cwd=${keymap.cwd}"; + keywords = optionalString ( + keymap.keywords != null && keymap.keywords != [ ] + ) " keywords=${concatStringsSep "," keymap.keywords}"; + in + "${cmd}${cwd}${keywords}"; + } + )) + ]; + }; } diff --git a/tests/test-sources/plugins/utils/todo-comments.nix b/tests/test-sources/plugins/utils/todo-comments.nix index 2f9eed0b..12b1ab98 100644 --- a/tests/test-sources/plugins/utils/todo-comments.nix +++ b/tests/test-sources/plugins/utils/todo-comments.nix @@ -7,164 +7,182 @@ plugins.todo-comments = { enable = true; - signs = true; - signPriority = 8; + settings = { + signs = true; + sign_priority = 8; - keywords = { - FIX = { - icon = " "; - color = "error"; - alt = [ - "FIXME" - "BUG" - "FIXIT" - "ISSUE" + keywords = { + FIX = { + icon = " "; + color = "error"; + alt = [ + "FIXME" + "BUG" + "FIXIT" + "ISSUE" + ]; + signs = false; + }; + TODO = { + icon = " "; + color = "info"; + }; + HACK = { + icon = " "; + color = "warning"; + }; + WARN = { + icon = " "; + color = "warning"; + alt = [ + "WARNING" + "XXX" + ]; + }; + PERF = { + icon = " "; + alt = [ + "OPTIM" + "PERFORMANCE" + "OPTIMIZE" + ]; + }; + NOTE = { + icon = " "; + color = "hint"; + alt = [ "INFO" ]; + }; + TEST = { + icon = "⏲ "; + color = "test"; + alt = [ + "TESTING" + "PASSED" + "FAILED" + ]; + }; + }; + + gui_style = { + fg = "NONE"; + bg = "BOLD"; + }; + + merge_keywords = true; + + highlight = { + multiline = true; + multiline_pattern = "^."; + multiline_context = 10; + before = ""; + keyword = "wide"; + after = "fg"; + pattern = ''.*<(KEYWORDS)\s*:''; + comments_only = true; + max_line_len = 400; + exclude = [ ]; + }; + + colors = { + error = [ + "DiagnosticError" + "ErrorMsg" + "#DC2626" ]; - signs = false; - }; - TODO = { - icon = " "; - color = "info"; - }; - HACK = { - icon = " "; - color = "warning"; - }; - WARN = { - icon = " "; - color = "warning"; - alt = [ - "WARNING" - "XXX" + warning = [ + "DiagnosticWarn" + "WarningMsg" + "#FBBF24" + ]; + info = [ + "DiagnosticInfo" + "#2563EB" + ]; + hint = [ + "DiagnosticHint" + "#10B981" + ]; + default = [ + "Identifier" + "#7C3AED" + ]; + test = [ + "Identifier" + "#FF00FF" ]; }; - PERF = { - icon = " "; - alt = [ - "OPTIM" - "PERFORMANCE" - "OPTIMIZE" + + search = { + command = "rg"; + args = [ + "--color=never" + "--no-heading" + "--with-filename" + "--line-number" + "--column" ]; - }; - NOTE = { - icon = " "; - color = "hint"; - alt = [ "INFO" ]; - }; - TEST = { - icon = "⏲ "; - color = "test"; - alt = [ - "TESTING" - "PASSED" - "FAILED" - ]; - }; - }; - - guiStyle = { - fg = "NONE"; - bg = "BOLD"; - }; - - mergeKeywords = true; - - highlight = { - multiline = true; - multilinePattern = "^."; - multilineContext = 10; - before = ""; - keyword = "wide"; - after = "fg"; - pattern = ''.*<(KEYWORDS)\s*:''; - commentsOnly = true; - maxLineLen = 400; - exclude = [ ]; - }; - - colors = { - error = [ - "DiagnosticError" - "ErrorMsg" - "#DC2626" - ]; - warning = [ - "DiagnosticWarn" - "WarningMsg" - "#FBBF24" - ]; - info = [ - "DiagnosticInfo" - "#2563EB" - ]; - hint = [ - "DiagnosticHint" - "#10B981" - ]; - default = [ - "Identifier" - "#7C3AED" - ]; - test = [ - "Identifier" - "#FF00FF" - ]; - }; - - search = { - command = "rg"; - args = [ - "--color=never" - "--no-heading" - "--with-filename" - "--line-number" - "--column" - ]; - pattern = ''\b(KEYWORDS):''; - }; - - keymaps = { - todoQuickFix.key = ""; - todoLocList = { - key = ""; - cwd = "~/projects/foobar"; - keywords = "TODO,FIX"; - options.silent = true; - }; - todoTrouble = { - key = ""; - keywords = "TODO,FIX"; - }; - todoTelescope = { - key = ""; - cwd = "~/projects/foobar"; + pattern = ''\b(KEYWORDS):''; }; }; }; }; - keymapsOptions = { - plugins.todo-comments = { - enable = true; + keymaps-options = { + plugins = { + trouble.enable = true; + telescope.enable = true; - keymaps = { - todoTrouble = { - key = ""; - keywords = "TODO,FIX"; - options = { - desc = "Description for todoTrouble"; - silent = true; + todo-comments = { + enable = true; + + keymaps = { + todoQuickFix.key = ""; + todoLocList = { + key = ""; + cwd = "~/projects/foobar"; + keywords = [ + "TODO" + "FIX" + ]; + options.silent = true; + }; + todoTrouble = { + key = ""; + keywords = [ + "TODO" + "FIX" + ]; + options = { + desc = "Description for todoTrouble"; + silent = true; + }; + }; + todoTelescope = { + key = ""; + cwd = "~/projects/foobar"; }; }; }; }; }; - withoutRipgrep = { + without-ripgrep = { plugins.todo-comments = { enable = true; ripgrepPackage = null; }; }; + + highlight-pattern-list = { + plugins.todo-comments = { + enable = true; + + settings = { + highlight = { + pattern = [ ".*<(KEYWORDS)\s*:" ]; + }; + }; + }; + }; }