mirror of
https://github.com/nix-community/nixvim.git
synced 2025-06-21 16:39:00 +02:00
465 lines
16 KiB
Nix
465 lines
16 KiB
Nix
{
|
|
lib,
|
|
helpers,
|
|
config,
|
|
pkgs,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.plugins.flash;
|
|
in {
|
|
options.plugins.flash = 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.
|
|
'';
|
|
|
|
search = {
|
|
automatic = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Automatically set the values according to context. Same as passing `search = {}` in lua
|
|
'';
|
|
};
|
|
|
|
multiWindow = helpers.defaultNullOpts.mkBool true "search/jump in all windows";
|
|
|
|
forward = helpers.defaultNullOpts.mkBool true "search direction";
|
|
|
|
wrap = helpers.defaultNullOpts.mkBool true ''
|
|
when `false`, find only matches in the given direction
|
|
'';
|
|
|
|
mode =
|
|
helpers.defaultNullOpts.mkNullable
|
|
(with types; either (enum ["exact" "search" "fuzzy"]) helpers.rawType) ''"exact"'' ''
|
|
- exact: exact match
|
|
- search: regular search
|
|
- fuzzy: fuzzy search
|
|
- fun(str): custom search function that returns a pattern
|
|
For example, to only match at the begining of a word:
|
|
function(str)
|
|
return "\\<" .. str
|
|
end
|
|
'';
|
|
|
|
incremental = helpers.defaultNullOpts.mkBool false "behave like `incsearch`";
|
|
|
|
exclude =
|
|
helpers.defaultNullOpts.mkNullable (with types; listOf (either str helpers.rawType)) ''
|
|
[
|
|
"notify"
|
|
"cmp_menu"
|
|
"noice"
|
|
"flash_prompt"
|
|
(
|
|
helpers.mkRaw
|
|
'''
|
|
function(win)
|
|
return not vim.api.nvim_win_get_config(win).focusable
|
|
end
|
|
'''
|
|
)
|
|
]
|
|
'' ''
|
|
Excluded filetypes and custom window filters
|
|
'';
|
|
|
|
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
|
|
'';
|
|
|
|
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
|
|
'';
|
|
};
|
|
|
|
jump = {
|
|
jumplist = helpers.defaultNullOpts.mkBool true "save location in the jumplist";
|
|
|
|
pos = helpers.defaultNullOpts.mkEnumFirstDefault ["start" "end" "range"] "jump position";
|
|
|
|
history = helpers.defaultNullOpts.mkBool false "add pattern to search history";
|
|
|
|
register = helpers.defaultNullOpts.mkBool false "add pattern to search register";
|
|
|
|
nohlsearch = helpers.defaultNullOpts.mkBool false "clear highlight after jump";
|
|
|
|
autojump = helpers.defaultNullOpts.mkBool false ''
|
|
automatically jump when there is only one match
|
|
'';
|
|
|
|
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.
|
|
'';
|
|
|
|
offset = helpers.mkNullOrOption types.int ''
|
|
jump position offset. Not used for range jumps.
|
|
0: default
|
|
1: when pos == "end" and pos < current position
|
|
'';
|
|
};
|
|
|
|
label = {
|
|
uppercase = helpers.defaultNullOpts.mkBool true "allow upercase labels";
|
|
|
|
exclude = helpers.defaultNullOpts.mkStr "" ''
|
|
add any labels with the correct case here, that you want to exclude
|
|
'';
|
|
|
|
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 `<CR>`
|
|
'';
|
|
|
|
after = helpers.defaultNullOpts.mkNullable (with types; either bool (listOf int)) "true" ''
|
|
show the label after the match
|
|
'';
|
|
|
|
before = helpers.defaultNullOpts.mkNullable (with types; either bool (listOf int)) "false" ''
|
|
show the label before the match
|
|
'';
|
|
|
|
style = helpers.defaultNullOpts.mkEnum ["eol" "overlay" "right_align" "inline"] "overlay" ''
|
|
position of the label extmark
|
|
'';
|
|
|
|
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.
|
|
'';
|
|
|
|
distance = helpers.defaultNullOpts.mkBool true ''
|
|
for the current window, label targets closer to the cursor first
|
|
'';
|
|
|
|
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.
|
|
'';
|
|
|
|
shade = helpers.defaultNullOpts.mkNullable (types.ints.between 1 9) "5" "";
|
|
};
|
|
|
|
format =
|
|
helpers.defaultNullOpts.mkStr ''
|
|
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";
|
|
|
|
matches = helpers.defaultNullOpts.mkBool true "Highlight the search matches";
|
|
|
|
priority = helpers.defaultNullOpts.mkPositiveInt 5000 "extmark priority";
|
|
|
|
groups = builtins.mapAttrs (_: default: helpers.defaultNullOpts.mkStr default "") {
|
|
match = "FlashMatch";
|
|
current = "FlashCurrent";
|
|
backdrop = "FlashBackdrop";
|
|
label = "FlashLabel";
|
|
};
|
|
};
|
|
|
|
action = helpers.mkNullOrOption types.str ''
|
|
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";
|
|
|
|
continue = helpers.defaultNullOpts.mkBool false ''
|
|
When `true`, flash will try to continue the last search
|
|
'';
|
|
|
|
config = helpers.mkNullOrOption types.str ''
|
|
Set config to a function to dynamically change the config
|
|
@type fun(opts:Flash.Config)
|
|
'';
|
|
|
|
prompt = {
|
|
enabled = helpers.defaultNullOpts.mkBool true ''
|
|
options for the floating window that shows the prompt, for regular jumps
|
|
'';
|
|
|
|
# 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";
|
|
};
|
|
|
|
remoteOp = {
|
|
restore = helpers.defaultNullOpts.mkBool false ''
|
|
restore window views and cursor position after doing a remote operation
|
|
'';
|
|
|
|
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.extraOptionsOptions
|
|
// {
|
|
enable = mkEnableOption "flash.nvim";
|
|
|
|
package = helpers.mkPackageOption "flash.nvim" pkgs.vimPlugins.flash-nvim;
|
|
|
|
modes = let
|
|
mkModeConfig = extra: default: desc:
|
|
helpers.defaultNullOpts.mkNullable (types.submodule {
|
|
options = configOpts // extra;
|
|
})
|
|
default
|
|
desc;
|
|
in {
|
|
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;
|
|
}
|
|
'' ''
|
|
options used when flash is activated through a regular search with `/` or `?`
|
|
'';
|
|
char =
|
|
mkModeConfig {
|
|
enabled = helpers.defaultNullOpts.mkBool true "";
|
|
|
|
autohide = helpers.defaultNullOpts.mkBool false ''
|
|
hide after jump when not using jump labels
|
|
'';
|
|
|
|
jumpLabels = helpers.defaultNullOpts.mkBool false "show jump labels";
|
|
|
|
multiLine = helpers.defaultNullOpts.mkBool true ''
|
|
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.mkStr ''
|
|
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;
|
|
|
|
config = let
|
|
mkRawIfNonNull = v: helpers.ifNonNull' v (helpers.mkRaw v);
|
|
|
|
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;
|
|
};
|
|
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;
|
|
};
|
|
format = mkRawIfNonNull c.label.format;
|
|
};
|
|
highlight = {
|
|
inherit (c.highlight) backdrop matches priority groups;
|
|
};
|
|
action = mkRawIfNonNull c.action;
|
|
inherit (c) pattern continue;
|
|
config = mkRawIfNonNull c.config;
|
|
prompt = {
|
|
inherit (c.prompt) enabled prefix;
|
|
win_config = c.prompt.winConfig;
|
|
};
|
|
remote_op = {
|
|
inherit (c.remoteOp) restore motion;
|
|
};
|
|
};
|
|
|
|
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;
|
|
char_actions = mkRawIfNonNull c.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})
|
|
'';
|
|
};
|
|
}
|