From c0fab2c7e8ef46763eb8eb341c23df29fd313dbf Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Mon, 17 Jun 2024 16:08:58 +0100 Subject: [PATCH] plugins/flash: switch to mkNeovimPlugin --- plugins/utils/flash.nix | 1061 +++++++++++--------- tests/test-sources/plugins/utils/flash.nix | 350 ++++--- 2 files changed, 767 insertions(+), 644 deletions(-) diff --git a/plugins/utils/flash.nix b/plugins/utils/flash.nix index e96056cea..6831d0b78 100644 --- a/plugins/utils/flash.nix +++ b/plugins/utils/flash.nix @@ -6,536 +6,669 @@ ... }: with lib; -let - cfg = config.plugins.flash; -in -{ - options.plugins.flash = +helpers.neovim-plugin.mkNeovimPlugin config { + name = "flash"; + originalName = "flash.nvim"; + defaultPackage = pkgs.vimPlugins.flash-nvim; + + maintainers = with maintainers; [ + traxys + MattSturgeon + ]; + + # TODO: Added 2024-06-17; remove 2024-09-17 + optionsRenamedToSettings = let - configOpts = { - labels = helpers.defaultNullOpts.mkStr "asdfghjklqwertyuiopzxcvbnm" '' - Labels appear next to the matches, allowing you to quickly jump to any location. Labels are - guaranteed not to exist as a continuation of the search pattern. - ''; - + # Combine base option paths with nested mode option paths + merged = flatten [ + opts + (mapAttrsToList (mode: extras: map (opt: "${mode}.${opt}") (opts ++ extras)) modes) + ]; + + # Option paths that should be renamed + opts = [ + "labels" + "search.multiWindow" + "search.forward" + "search.wrap" + "search.mode" + "search.incremental" + "search.exclude" + "search.trigger" + "search.maxLength" + "jump.jumplist" + "jump.pos" + "jump.history" + "jump.register" + "jump.nohlsearch" + "jump.autojump" + "jump.inclusive" + "jump.offset" + "label.uppercase" + "label.exclude" + "label.current" + "label.after" + "label.before" + "label.style" + "label.reuse" + "label.distance" + "label.minPatternLength" + "label.rainbow.enabled" + "label.rainbow.shade" + "label.format" + "highlight.backdrop" + "highlight.matches" + "highlight.priority" + "highlight.group.match" + "highlight.group.current" + "highlight.group.backdrop" + "highlight.group.label" + "action" + "pattern" + "continue" + "config" + "prompt.enabled" + "prompt.prefix" + "prompt.winConfig.relative" + "prompt.winConfig.width" + "prompt.winConfig.height" + "prompt.winConfig.row" + "prompt.winConfig.col" + "prompt.winConfig.zindex" + "remoteOp.restore" + "remoteOp.motion" + ]; + + # "Modes" that should also be renamed + # `opts` will get added to the attr value + modes = { + search = [ "enabled" ]; + char = [ + "enabled" + "autohide" + "jumpLabels" + "multiLine" + "keys" + "charActions" + ]; + treesitter = [ ]; + treesitterSearch = [ ]; + remote = [ ]; + }; + in + map (splitString ".") merged; + + imports = [ + # TODO: check automatic search config still works + (mkRemovedOptionModule [ + "plugins" + "flash" + "search" + "automatic" + ] "You can configure `plugins.flash.settings.search.search.*` directly.") + ]; + + settingsOptions = + let + # Default values used for the top-level settings options, + # also used as secondary defaults for mode options. + configDefaults = { + labels = "asdfghjklqwertyuiopzxcvbnm"; search = { - automatic = mkOption { - type = types.bool; - default = false; - description = '' - Automatically set the values according to context. Same as passing `search = {}` in lua - ''; + multi_window = true; + forward = true; + wrap = true; + mode = "exact"; + incremental = false; + exclude = [ + "notify" + "cmp_menu" + "noice" + "flash_prompt" + (helpers.mkRaw '' + function(win) + -- exclude non-focusable windows + return not vim.api.nvim_win_get_config(win).focusable + end + '') + ]; + trigger = ""; + max_length = false; + }; + jump = { + jumplist = true; + pos = "start"; + history = false; + register = false; + nohlsearch = false; + autojump = false; + inclusive = null; + offset = null; + }; + label = { + uppercase = true; + exclude = ""; + current = true; + after = true; + before = false; + style = "overlay"; + reuse = "lowercase"; + distance = true; + min_pattern_length = 0; + rainbow = { + enabled = false; + shade = 5; }; + format = '' + function(opts) + return { { opts.match.label, opts.hl_group } } + end + ''; + }; + highlight = { + backdrop = true; + matches = true; + priority = 5000; + groups = { + match = "FlashMatch"; + current = "FlashCurrent"; + backdrop = "FlashBackdrop"; + label = "FlashLabel"; + }; + }; + action = null; + pattern = ""; + continue = false; + config = null; + prompt = { + enabled = true; + prefix = [ + [ + "⚡" + "FlashPromptIcon" + ] + ]; + win_config = { + relative = "editor"; + width = 1; + height = 1; + row = -1; + col = 0; + zindex = 1000; + }; + }; + remote_op = { + restore = false; + motion = false; + }; + }; - multiWindow = helpers.defaultNullOpts.mkBool true "search/jump in all windows"; + # Shared config options, used by the top-level settings, as well as modes. + # Each usage has different default values. + configOpts = + defaults': + let + defaults = recursiveUpdate configDefaults defaults'; + in + { + labels = helpers.defaultNullOpts.mkStr defaults.labels '' + Labels appear next to the matches, allowing you to quickly jump to any location. Labels are + guaranteed not to exist as a continuation of the search pattern. + ''; - forward = helpers.defaultNullOpts.mkBool true "search direction"; + search = { + multi_window = helpers.defaultNullOpts.mkBool defaults.search.multi_window '' + Search/jump in all windows + ''; - wrap = helpers.defaultNullOpts.mkBool true '' - when `false`, find only matches in the given direction - ''; + forward = helpers.defaultNullOpts.mkBool defaults.search.forward '' + Search direction + ''; + + wrap = helpers.defaultNullOpts.mkBool defaults.search.wrap '' + Continue searching after reaching the start/end of the file. + When `false`, find only matches in the given direction + ''; - mode = - helpers.defaultNullOpts.mkNullable - ( - with types; - either (enum [ + mode = + helpers.defaultNullOpts.mkEnum + [ "exact" "search" "fuzzy" - ]) helpers.nixvimTypes.rawLua - ) - ''"exact"'' - '' - - exact: exact match - - search: regular search - - fuzzy: fuzzy search - - fun(str): custom search function that returns a pattern + ] + defaults.search.mode + '' + Each mode will take `ignorecase` and `smartcase` into account. + - `exact`: exact match + - `search`: regular search + - `fuzzy`: fuzzy search + - `__raw` fun(str): custom search function that returns a pattern + For example, to only match at the beginning of a word: - function(str) - return "\\<" .. str - end - ''; + ```lua + function(str) + return "\\<" .. str + end + ``` + ''; + + incremental = helpers.defaultNullOpts.mkBool defaults.search.incremental '' + Behave like `incsearch`. + ''; + + exclude = helpers.defaultNullOpts.mkListOf types.str defaults.search.exclude '' + Excluded filetypes and custom window filters. + ''; - incremental = helpers.defaultNullOpts.mkBool false "behave like `incsearch`"; + trigger = helpers.defaultNullOpts.mkStr defaults.search.trigger '' + Optional trigger character that needs to be typed before a jump label can be used. + It's NOT recommended to set this, unless you know what you're doing. + ''; + + max_length = + helpers.defaultNullOpts.mkNullable (with types; either (enum [ false ]) int) + defaults.search.max_length + '' + Max pattern length. If the pattern length is equal to this labels will no longer be skipped. + When it exceeds this length it will either end in a jump or terminate the search. + ''; + }; + + jump = { + jumplist = helpers.defaultNullOpts.mkBool defaults.jump.jumplist '' + Save location in the jumplist. + ''; - exclude = - helpers.defaultNullOpts.mkNullable (with types; listOf (either str helpers.nixvimTypes.rawLua)) - '' + pos = + helpers.defaultNullOpts.mkEnum [ - "notify" - "cmp_menu" - "noice" - "flash_prompt" - ( - helpers.mkRaw - ''' - function(win) - return not vim.api.nvim_win_get_config(win).focusable - end - ''' - ) + "start" + "end" + "range" ] - '' - '' - Excluded filetypes and custom window filters - ''; + defaults.jump.pos + '' + Jump position + ''; - trigger = helpers.defaultNullOpts.mkStr "" '' - Optional trigger character that needs to be typed before a jump label can be used. - It's NOT recommended to set this, unless you know what you're doing - ''; + history = helpers.defaultNullOpts.mkBool defaults.jump.history '' + Add pattern to search history. + ''; - maxLength = - helpers.defaultNullOpts.mkNullable (with types; either (enum [ false ]) types.int) "false" - '' - max pattern length. If the pattern length is equal to this labels will no longer be - skipped. When it exceeds this length it will either end in a jump or terminate the search - ''; - }; + register = helpers.defaultNullOpts.mkBool defaults.jump.register '' + Add pattern to search register. + ''; - jump = { - jumplist = helpers.defaultNullOpts.mkBool true "save location in the jumplist"; + nohlsearch = helpers.defaultNullOpts.mkBool defaults.jump.nohlsearch '' + Clear highlight after jump + ''; - pos = helpers.defaultNullOpts.mkEnumFirstDefault [ - "start" - "end" - "range" - ] "jump position"; + autojump = helpers.defaultNullOpts.mkBool defaults.jump.autojump '' + Automatically jump when there is only one match + ''; - history = helpers.defaultNullOpts.mkBool false "add pattern to search history"; + inclusive = helpers.defaultNullOpts.mkBool defaults.jump.inclusive '' + You can force inclusive/exclusive jumps by setting the `inclusive` option. By default it + will be automatically set based on the mode. + ''; - register = helpers.defaultNullOpts.mkBool false "add pattern to search register"; + offset = helpers.defaultNullOpts.mkInt defaults.jump.offset '' + jump position offset. Not used for range jumps. + 0: default + 1: when pos == "end" and pos < current position + ''; + }; - nohlsearch = helpers.defaultNullOpts.mkBool false "clear highlight after jump"; + label = { + uppercase = helpers.defaultNullOpts.mkBool defaults.label.uppercase '' + Allow uppercase labels. + ''; - autojump = helpers.defaultNullOpts.mkBool false '' - automatically jump when there is only one match - ''; + exclude = helpers.defaultNullOpts.mkStr defaults.label.exclude '' + add any labels with the correct case here, that you want to exclude + ''; - inclusive = helpers.mkNullOrOption types.bool '' - You can force inclusive/exclusive jumps by setting the `inclusive` option. By default it - will be automatically set based on the mode. - ''; + current = helpers.defaultNullOpts.mkBool true '' + Add a label for the first match in the current window. + You can always jump to the first match with `` + ''; - offset = helpers.mkNullOrOption types.int '' - jump position offset. Not used for range jumps. - 0: default - 1: when pos == "end" and pos < current position - ''; - }; + after = + helpers.defaultNullOpts.mkNullableWithRaw (with types; either bool (listOf int)) + defaults.label.after + '' + Show the label after the match + ''; - label = { - uppercase = helpers.defaultNullOpts.mkBool true "allow uppercase labels"; + before = + helpers.defaultNullOpts.mkNullableWithRaw (with types; either bool (listOf int)) + defaults.label.before + '' + Show the label before the match + ''; - exclude = helpers.defaultNullOpts.mkStr "" '' - add any labels with the correct case here, that you want to exclude - ''; + style = + helpers.defaultNullOpts.mkEnum + [ + "eol" + "overlay" + "right_align" + "inline" + ] + defaults.label.style + '' + position of the label extmark + ''; - current = helpers.defaultNullOpts.mkBool true '' - add a label for the first match in the current window. - you can always jump to the first match with `` - ''; + reuse = + helpers.defaultNullOpts.mkEnum + [ + "lowercase" + "all" + "none" + ] + defaults.label.reuse + '' + flash tries to re-use labels that were already assigned to a position, + when typing more characters. By default only lower-case labels are re-used. + ''; - after = helpers.defaultNullOpts.mkNullable (with types; either bool (listOf int)) "true" '' - show the label after the match - ''; + distance = helpers.defaultNullOpts.mkBool defaults.label.distance '' + for the current window, label targets closer to the cursor first + ''; - before = helpers.defaultNullOpts.mkNullable (with types; either bool (listOf int)) "false" '' - show the label before the match - ''; + min_pattern_length = helpers.defaultNullOpts.mkInt defaults.label.min_pattern_length '' + minimum pattrn length to show labels + Ignored for custom labelers. + ''; - style = - helpers.defaultNullOpts.mkEnum - [ - "eol" - "overlay" - "right_align" - "inline" - ] - "overlay" - '' - position of the label extmark + rainbow = { + enabled = helpers.defaultNullOpts.mkBool defaults.label.rainbow.enabled '' + Enable this to use rainbow colors to highlight labels + Can be useful for visualizing Treesitter ranges. ''; - reuse = - helpers.defaultNullOpts.mkEnumFirstDefault - [ - "lowercase" - "all" - "none" - ] - '' - flash tries to re-use labels that were already assigned to a position, - when typing more characters. By default only lower-case labels are re-used. - ''; + shade = helpers.defaultNullOpts.mkNullable (types.ints.between 1 9) defaults.label.rainbow.shade ""; + }; - distance = helpers.defaultNullOpts.mkBool true '' - for the current window, label targets closer to the cursor first - ''; + format = helpers.defaultNullOpts.mkLuaFn defaults.label.format '' + With `format`, you can change how the label is rendered. + Should return a list of `[text, highlight]` tuples. - minPatternLength = helpers.defaultNullOpts.mkInt 0 '' - minimum pattern length to show labels - Ignored for custom labelers. - ''; - - rainbow = { - enabled = helpers.defaultNullOpts.mkBool false '' - Enable this to use rainbow colors to highlight labels - Can be useful for visualizing Treesitter ranges. + @class Flash.Format + @field state Flash.State + @field match Flash.Match + @field hl_group string + @field after boolean + @type fun(opts:Flash.Format): string[][] ''; - - shade = helpers.defaultNullOpts.mkNullable (types.ints.between 1 9) "5" ""; }; - format = - helpers.defaultNullOpts.mkLuaFn - '' - format = function(opts) - return { { opts.match.label, opts.hl_group } } - end - '' - '' - With `format`, you can change how the label is rendered. - Should return a list of `[text, highlight]` tuples. - @class Flash.Format - @field state Flash.State - @field match Flash.Match - @field hl_group string - @field after boolean - @type fun(opts:Flash.Format): string[][] - ''; - }; - - highlight = { - backdrop = helpers.defaultNullOpts.mkBool true "show a backdrop with hl FlashBackdrop"; + highlight = { + backdrop = helpers.defaultNullOpts.mkBool defaults.highlight.backdrop '' + Show a backdrop with hl FlashBackdrop. + ''; - matches = helpers.defaultNullOpts.mkBool true "Highlight the search matches"; + matches = helpers.defaultNullOpts.mkBool defaults.highlight.matches '' + Highlight the search matches. + ''; - priority = helpers.defaultNullOpts.mkPositiveInt 5000 "extmark priority"; + priority = helpers.defaultNullOpts.mkPositiveInt defaults.highlight.priority '' + Extmark priority. + ''; - groups = builtins.mapAttrs (_: default: helpers.defaultNullOpts.mkStr default "") { - match = "FlashMatch"; - current = "FlashCurrent"; - backdrop = "FlashBackdrop"; - label = "FlashLabel"; + groups = mapAttrs (name: helpers.defaultNullOpts.mkStr defaults.highlight.groups.${name}) { + # opt = description + match = "FlashMatch"; + current = "FlashCurrent"; + backdrop = "FlashBackdrop"; + label = "FlashLabel"; + }; }; - }; - - action = helpers.defaultNullOpts.mkLuaFn "nil" '' - action to perform when picking a label. - defaults to the jumping logic depending on the mode. - @type fun(match:Flash.Match, state:Flash.State) - ''; - pattern = helpers.defaultNullOpts.mkStr "" "initial pattern to use when opening flash"; + action = helpers.defaultNullOpts.mkLuaFn defaults.action '' + action to perform when picking a label. + defaults to the jumping logic depending on the mode. - continue = helpers.defaultNullOpts.mkBool false '' - When `true`, flash will try to continue the last search - ''; + @type fun(match:Flash.Match, state:Flash.State) + ''; - config = helpers.defaultNullOpts.mkLuaFn "nil" '' - Set config to a function to dynamically change the config - @type fun(opts:Flash.Config) - ''; + pattern = helpers.defaultNullOpts.mkStr defaults.pattern '' + Initial pattern to use when opening flash. + ''; - prompt = { - enabled = helpers.defaultNullOpts.mkBool true '' - options for the floating window that shows the prompt, for regular jumps + continue = helpers.defaultNullOpts.mkBool defaults.continue '' + When `true`, flash will try to continue the last search. ''; - # Not sure what is the type... - prefix = helpers.defaultNullOpts.mkNullable (types.listOf types.anything) '' - [ ["⚡" "FlashPromptIcon"] ] - '' ""; - winConfig = helpers.defaultNullOpts.mkNullable (types.attrsOf types.anything) '' - { - relative = "editor"; - width = 1; - height = 1; - row = -1; - col = 0; - zindex = 1000; - } - '' "See nvim_open_win for more details"; - }; + config = helpers.defaultNullOpts.mkLuaFn defaults.config '' + Set config to a function to dynamically change the config. - remoteOp = { - restore = helpers.defaultNullOpts.mkBool false '' - restore window views and cursor position after doing a remote operation + @type fun(opts:Flash.Config) ''; - motion = helpers.defaultNullOpts.mkBool false '' - For `jump.pos = "range"`, this setting is ignored. - - `true`: always enter a new motion when doing a remote operation - - `false`: use the window's cursor position and jump target - - `nil`: act as `true` for remote windows, `false` for the current window - ''; - }; - }; - in - helpers.neovim-plugin.extraOptionsOptions - // { - enable = mkEnableOption "flash.nvim"; + prompt = { + enabled = helpers.defaultNullOpts.mkBool defaults.prompt.enabled '' + Options for the floating window that shows the prompt, for regular jumps. + ''; - package = helpers.mkPluginPackageOption "flash.nvim" pkgs.vimPlugins.flash-nvim; + # Not sure what is the type... + # Think it's listOf (maybeRaw (listOf (maybeRaw str))) + prefix = helpers.defaultNullOpts.mkListOf types.anything defaults.prompt.prefix ""; + win_config = helpers.defaultNullOpts.mkAttrsOf types.anything defaults.prompt.win_config '' + See `:h nvim_open_win` for more details. + ''; + }; - modes = - let - mkModeConfig = - extra: default: desc: - helpers.defaultNullOpts.mkNullable (types.submodule { - options = configOpts // extra; - }) default desc; - in + remote_op = { + restore = helpers.defaultNullOpts.mkBool defaults.remote_op.restore '' + Restore window views and cursor position after doing a remote operation. + ''; + + motion = helpers.defaultNullOpts.mkBool defaults.remote_op.motion '' + For `jump.pos = "range"`, this setting is ignored. + - `true`: always enter a new motion when doing a remote operation + - `false`: use the window's cursor position and jump target + - `nil`: act as `true` for remote windows, `false` for the current window + ''; + }; + }; + + # The `mode`s have all the same options as top-level settings, + # but with different defaults and some additional options. + mkModeConfig = { - search = - mkModeConfig - { - enabled = helpers.defaultNullOpts.mkBool true '' - when `true`, flash will be activated during regular search by default. - You can always toggle when searching with `require("flash").toggle()` - ''; - } - '' - { - enabled = true; - highlight = { backdrop = false; }; - jump = { history = true; register = true; nohlsearch = true; }; - /* - forward will be automatically set to the search direction - mode is always set to 'search' - incremental is set to 'true' when 'incsearch' is enabled - */ - search.automatic = true; + defaults ? { }, + options ? { }, + ... + }@args: + # FIXME: use mkNullableWithRaw when #1618 is fixed + helpers.defaultNullOpts.mkNullable' ( + (removeAttrs args [ + "options" + "defaults" + ]) + // { + type = + with types; + submodule { + freeformType = attrsOf anything; + options = recursiveUpdate (configOpts defaults) options; + }; + } + ); + in + (configOpts { }) + // { + modes = { + search = mkModeConfig rec { + description = '' + Options used when flash is activated through a regular search, + e.g. with `/` or `?`. + ''; + defaults = { + enabled = false; + highlight = { + backdrop = false; + }; + jump = { + history = true; + register = true; + nohlsearch = true; + }; + search = { + forward.__raw = "vim.fn.getcmdtype() == '/'"; + mode = "search"; + incremental.__raw = "vim.go.incsearch"; + }; + }; + options = { + enabled = helpers.defaultNullOpts.mkBool defaults.enabled '' + When `true`, flash will be activated during regular search by default. + You can always toggle when searching with `require("flash").toggle()` + ''; + }; + }; + char = mkModeConfig rec { + description = '' + Options used when flash is activated through + `f`, `F`, `t`, `T`, `;` and `,` motions. + ''; + defaults = { + enabled = true; + config = '' + -- dynamic configuration for ftFT motions + function(opts) + -- autohide flash when in operator-pending mode + opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y") + + -- disable jump labels when not enabled, when using a count, + -- or when recording/executing registers + opts.jump_labels = opts.jump_labels + and vim.v.count == 0 + and vim.fn.reg_executing() == "" + and vim.fn.reg_recording() == "" + + -- Show jump labels only in operator-pending mode + -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + end + ''; + autohide = false; + jump_labels = false; + multi_line = true; + label = { + exclude = "hjkliardc"; + }; + # by default all keymaps are enabled, but you can disable some of them; + # by removing them from the list. + # If you rather use another key, you can map them + # to something else, e.g., { [";"] = "L", [","] = H } + keys = helpers.listToUnkeyedAttrs (lib.stringToCharacters "fFtT;,"); + #-@alias Flash.CharActions table + # The direction for `prev` and `next` is determined by the motion. + # `left` and `right` are always left and right. + char_actions = '' + function(motion) + return { + [";"] = "next", -- set to `right` to always go right + [","] = "prev", -- set to `left` to always go left + -- clever-f style + [motion:lower()] = "next", + [motion:upper()] = "prev", + -- jump2d style: same case goes next, opposite case goes prev + -- [motion] = "next", + -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", } - '' - '' - options used when flash is activated through a regular search with `/` or `?` - ''; - char = - mkModeConfig - { - enabled = helpers.defaultNullOpts.mkBool true ""; + end + ''; + search.wrap = false; + highlight.backdrop = true; + jump.register = false; + }; + options = { + enabled = helpers.defaultNullOpts.mkBool defaults.enabled ""; - autohide = helpers.defaultNullOpts.mkBool false '' - hide after jump when not using jump labels - ''; + autohide = helpers.defaultNullOpts.mkBool defaults.autohide '' + Hide after jump when not using jump labels. + ''; - jumpLabels = helpers.defaultNullOpts.mkBool false "show jump labels"; + jump_labels = helpers.defaultNullOpts.mkBool defaults.jump_labels '' + Show jump labels. + ''; - multiLine = helpers.defaultNullOpts.mkBool true '' - set to `false` to use the current line only - ''; + multi_line = helpers.defaultNullOpts.mkBool defaults.multi_line '' + Set to `false` to use the current line only. + ''; - keys = - helpers.defaultNullOpts.mkNullable (types.attrsOf types.str) - '' - helpers.listToUnkeyedAttrs [ "f" "F" "t" "T" ";" "," ] - '' - '' - by default all keymaps are enabled, but you can disable some of them, - by removing them from the list. - If you rather use another key, you can map them - to something else, e.g., `{ ";" = "L"; "," = "H"; }` - ''; - - charActions = - helpers.defaultNullOpts.mkLuaFn - '' - function(motion) - return { - [";"] = "next", -- set to right to always go right - [","] = "prev", -- set to left to always go left - -- clever-f style - [motion:lower()] = "next", - [motion:upper()] = "prev", - -- jump2d style: same case goes next, opposite case goes prev - -- [motion] = "next", - -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", - } - end - '' - '' - The direction for `prev` and `next` is determined by the motion. - `left` and `right` are always left and right. - ''; - } - '' - { - enabled = true; - /* dynamic configuration for ftFT motions */ - config = ''' - function(opts) - -- autohide flash when in operator-pending mode - opts.autohide = vim.fn.mode(true):find("no") and vim.v.operator == "y" - - -- disable jump labels when not enabled, when using a count, - -- or when recording/executing registers - opts.jump_labels = opts.jump_labels - and vim.v.count == 0 - and vim.fn.reg_executing() == "" - and vim.fn.reg_recording() == "" - - -- Show jump labels only in operator-pending mode - -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") - end - '''; - autohide = false; - jumpLabels = false; - multiLine = false; - label = { exclude = "hjkliardc"; }; - keys = helpers.listToUnkeyedAttrs [ "f" "F" "t" "T" ";" "," ]; - charActions = ''' - function(motion) - return { - [";"] = "next", -- set to right to always go right - [","] = "prev", -- set to left to always go left - -- clever-f style - [motion:lower()] = "next", - [motion:upper()] = "prev", - -- jump2d style: same case goes next, opposite case goes prev - -- [motion] = "next", - -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", - } - end - '''; - search = { wrap = false; }; - highlight = { backdrop = true; }; - jump = { register = false; }; - } - '' - "options used when flash is activated through a regular search with `/` or `?`"; - treesitter = - mkModeConfig { } - '' - { - labels = "abcdefghijklmnopqrstuvwxyz"; - jump = { pos = "range"; }; - search = { incremental = false; }; - label = { before = true; after = true; style = "inline"; }; - highlight = { backdrop = false; matches = false; }; - }helpers.ifNonNull' - '' - '' - options used for treesitter selections `require("flash").treesitter()` - ''; - treesitterSearch = mkModeConfig { } '' - { - jump = { pos = "range"; }; - search = { multiWindow = true; wrap = true; incremental = false; }; - remoteOp = { restore = true }; - label = { before = true; after = true; style = "inline"; }; - } - '' ""; - remote = mkModeConfig { } '' - { remoteOp = { restore = true; motion = true; }; } - '' "options used for remote flash"; - }; - } - // configOpts; + keys = helpers.defaultNullOpts.mkAttrsOf' { + type = types.str; + pluginDefault = defaults.keys; + description = '' + By default all keymaps are enabled, but you can disable some of them, + by removing them from the list. - config = - let - mkGlobalConfig = c: { - inherit (c) labels; - search = - if c.search.automatic then - helpers.emptyTable - else - { - multi_window = c.search.multiWindow; - inherit (c.search) - forward - wrap - mode - incremental - exclude - trigger - ; - max_length = c.search.maxLength; + If you rather use another key, you can map them to something else. + ''; + example = { + ";" = "L"; + "," = "H"; + }; }; - jump = { - inherit (c.jump) - jumplist - pos - history - register - nohlsearch - autojump - inclusive - offset - ; - }; - label = { - inherit (c.label) - uppercase - exclude - current - after - before - style - reuse - distance - ; - min_pattern_length = c.label.minPatternLength; - rainbow = { - inherit (c.label.rainbow) enabled shade; + + char_actions = helpers.defaultNullOpts.mkLuaFn defaults.char_actions '' + The direction for `prev` and `next` is determined by the motion. + `left` and `right` are always left and right. + ''; }; - inherit (c.label) format; }; - highlight = { - inherit (c.highlight) - backdrop - matches - priority - groups - ; + treesitter = mkModeConfig { + description = '' + Options used for treesitter selections in `require("flash").treesitter()`. + ''; + defaults = { + labels = "abcdefghijklmnopqrstuvwxyz"; + jump.pos = "range"; + search.incremental = false; + label = { + before = true; + after = true; + style = "inline"; + }; + highlight = { + backdrop = false; + matches = false; + }; + }; }; - inherit (c) - action - pattern - continue - config - ; - prompt = { - inherit (c.prompt) enabled prefix; - win_config = c.prompt.winConfig; + treesitter_search = mkModeConfig { + description = '' + Options used when flash is activated through `require("flash").treesitter_search()`. + ''; + defaults = { + jump.pos = "range"; + search = { + multi_window = true; + wrap = true; + incremental = false; + }; + remote_op.restore = true; + label = { + before = true; + after = true; + style = "inline"; + }; + }; }; - remote_op = { - inherit (c.remoteOp) restore motion; + remote = mkModeConfig { + description = "Options used for remote flash."; + defaults = { + remote_op = { + restore = true; + motion = true; + }; + }; }; }; - - options = - (mkGlobalConfig cfg) - // { - modes = - let - mkModeConfig = c: extra: helpers.ifNonNull' c ((mkGlobalConfig c) // (extra c)); - in - { - search = mkModeConfig cfg.modes.search (c: { - inherit (c) enabled; - }); - char = mkModeConfig cfg.modes.char (c: { - inherit (c) enabled autohide; - jump_labels = c.jumpLabels; - multi_line = c.multiLine; - inherit (c) keys charActions; - }); - treesitter = mkModeConfig cfg.modes.treesitter (c: { }); - treesitter_search = mkModeConfig cfg.modes.treesitterSearch (c: { }); - remote = mkModeConfig cfg.modes.remote (c: { }); - }; - } - // cfg.extraOptions; - in - mkIf cfg.enable { - extraPlugins = [ cfg.package ]; - - extraConfigLua = '' - require('flash').setup(${helpers.toLuaObject options}) - ''; }; } diff --git a/tests/test-sources/plugins/utils/flash.nix b/tests/test-sources/plugins/utils/flash.nix index 1d8e44870..ecf6668cc 100644 --- a/tests/test-sources/plugins/utils/flash.nix +++ b/tests/test-sources/plugins/utils/flash.nix @@ -8,205 +8,195 @@ plugins.flash = { enable = true; - labels = "asdfghjklqwertyuiopzxcvbnm"; - search = { - multiWindow = true; - forward = true; - wrap = true; - mode = "exact"; - incremental = false; - exclude = [ - "notify" - "cmp_menu" - "noice" - "flash_prompt" - { - __raw = '' + settings = { + labels = "asdfghjklqwertyuiopzxcvbnm"; + search = { + multi_window = true; + forward = true; + wrap = true; + mode = "exact"; + incremental = false; + exclude = [ + "notify" + "cmp_menu" + "noice" + "flash_prompt" + (helpers.mkRaw '' function(win) -- exclude non-focusable windows return not vim.api.nvim_win_get_config(win).focusable end - ''; - } - ]; - trigger = ""; - maxLength = false; - }; - jump = { - jumplist = true; - pos = "start"; - history = false; - register = false; - nohlsearch = false; - autojump = false; - inclusive = null; - offset = null; - }; - label = { - uppercase = true; - exclude = ""; - current = true; - after = true; - before = false; - style = "overlay"; - reuse = "lowercase"; - distance = true; - minPatternLength = 0; - rainbow = { - enabled = false; - shade = 5; - }; - format = '' - function(opts) - return { { opts.match.label, opts.hl_group } } - end - ''; - }; - highlight = { - backdrop = true; - matches = true; - priority = 5000; - groups = { - match = "FlashMatch"; - current = "FlashCurrent"; - backdrop = "FlashBackdrop"; - label = "FlashLabel"; + '') + ]; + trigger = ""; + max_length = false; }; - }; - action = null; - pattern = ""; - continue = false; - config = null; - prompt = { - enabled = true; - prefix = [ - [ - "⚡" - "FlashPromptIcon" - ] - ]; - winConfig = { - relative = "editor"; - width = 1; - height = 1; - row = -1; - col = 0; - zindex = 1000; + jump = { + jumplist = true; + pos = "start"; + history = false; + register = false; + nohlsearch = false; + autojump = false; + inclusive = null; + offset = null; }; - }; - remoteOp = { - restore = false; - motion = false; - }; - modes = { - search = { - enabled = true; - highlight = { - backdrop = false; - }; - jump = { - history = true; - register = true; - nohlsearch = true; + label = { + uppercase = true; + exclude = ""; + current = true; + after = true; + before = false; + style = "overlay"; + reuse = "lowercase"; + distance = true; + min_pattern_length = 0; + rainbow = { + enabled = false; + shade = 5; }; - search.automatic = true; - }; - char = { - enabled = true; - config = '' + format = '' function(opts) - -- autohide flash when in operator-pending mode - opts.autohide = vim.fn.mode(true):find("no") and vim.v.operator == "y" - - -- disable jump labels when not enabled, when using a count, - -- or when recording/executing registers - opts.jump_labels = opts.jump_labels - and vim.v.count == 0 - and vim.fn.reg_executing() == "" - and vim.fn.reg_recording() == "" - - -- Show jump labels only in operator-pending mode - -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + return { { opts.match.label, opts.hl_group } } end ''; - autohide = false; - jumpLabels = false; - multiLine = true; - label = { - exclude = "hjkliardc"; + }; + highlight = { + backdrop = true; + matches = true; + priority = 5000; + groups = { + match = "FlashMatch"; + current = "FlashCurrent"; + backdrop = "FlashBackdrop"; + label = "FlashLabel"; }; - keys = helpers.listToUnkeyedAttrs [ - "f" - "F" - "t" - "T" - ";" - "," + }; + action = null; + pattern = ""; + continue = false; + config = null; + prompt = { + enabled = true; + prefix = [ + [ + "⚡" + "FlashPromptIcon" + ] ]; - charActions = '' - function(motion) - return { - [";"] = "next", -- set to `right` to always go right - [","] = "prev", -- set to `left` to always go left - -- clever-f style - [motion:lower()] = "next", - [motion:upper()] = "prev", - -- jump2d style: same case goes next, opposite case goes prev - -- [motion] = "next", - -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", - } - end - ''; - search = { - wrap = false; - }; - highlight = { - backdrop = true; - }; - jump = { - register = false; + win_config = { + relative = "editor"; + width = 1; + height = 1; + row = -1; + col = 0; + zindex = 1000; }; }; - treesitter = { - labels = "abcdefghijklmnopqrstuvwxyz"; - jump = { - pos = "range"; - }; - search = { - incremental = false; - }; - label = { - before = true; - after = true; - style = "inline"; - }; - highlight = { - backdrop = false; - matches = false; - }; + remote_op = { + restore = false; + motion = false; }; - treesitterSearch = { - jump = { - pos = "range"; - }; + modes = { search = { - multiWindow = true; - wrap = true; - incremental = false; + enabled = false; + highlight = { + backdrop = false; + }; + jump = { + history = true; + register = true; + nohlsearch = true; + }; + search = { + forward.__raw = "vim.fn.getcmdtype() == '/'"; + mode = "search"; + incremental.__raw = "vim.go.incsearch"; + }; }; - remoteOp = { - restore = true; + char = { + enabled = true; + config = '' + function(opts) + -- autohide flash when in operator-pending mode + opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y") + + -- disable jump labels when not enabled, when using a count, + -- or when recording/executing registers + opts.jump_labels = opts.jump_labels + and vim.v.count == 0 + and vim.fn.reg_executing() == "" + and vim.fn.reg_recording() == "" + + -- Show jump labels only in operator-pending mode + -- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o") + end + ''; + autohide = false; + jump_labels = false; + multi_line = true; + label = { + exclude = "hjkliardc"; + }; + keys = { + __unkeyed-0 = "f"; + __unkeyed-1 = "F"; + __unkeyed-2 = "t"; + __unkeyed-3 = "T"; + __unkeyed-4 = ";"; + __unkeyed-5 = ","; + }; + char_actions = '' + function(motion) + return { + [";"] = "next", -- set to `right` to always go right + [","] = "prev", -- set to `left` to always go left + -- clever-f style + [motion:lower()] = "next", + [motion:upper()] = "prev", + -- jump2d style: same case goes next, opposite case goes prev + -- [motion] = "next", + -- [motion:match("%l") and motion:upper() or motion:lower()] = "prev", + } + end + ''; + search.wrap = false; + highlight.backdrop = true; + jump.register = false; }; - label = { - before = true; - after = true; - style = "inline"; + treesitter = { + labels = "abcdefghijklmnopqrstuvwxyz"; + jump.pos = "range"; + search.incremental = false; + label = { + before = true; + after = true; + style = "inline"; + }; + highlight = { + backdrop = false; + matches = false; + }; }; - }; - remote = { - remoteOp = { - restore = true; - motion = true; + treesitter_search = { + jump.pos = "range"; + search = { + multi_window = true; + wrap = true; + incremental = false; + }; + remote_op.restore = true; + label = { + before = true; + after = true; + style = "inline"; + }; + }; + remote = { + remote_op = { + restore = true; + motion = true; + }; }; }; };