mirror of
https://github.com/nix-community/nixvim.git
synced 2025-06-20 16:15:43 +02:00
modules/keymaps: refactor + new syntax
This commit is contained in:
parent
382973b627
commit
574fb73258
6 changed files with 716 additions and 521 deletions
70
README.md
70
README.md
|
@ -321,18 +321,23 @@ If you are using `makeNixvimWithModule`, then options is treated as options for
|
|||
|
||||
## Key mappings
|
||||
It is fully possible to define key mappings from within NixVim. This is done
|
||||
using the `maps` attribute:
|
||||
using the `keymaps` attribute:
|
||||
|
||||
```nix
|
||||
{
|
||||
programs.nixvim = {
|
||||
maps = {
|
||||
normalVisualOp.";" = ":";
|
||||
normal."<leader>m" = {
|
||||
silent = true;
|
||||
action = "<cmd>make<CR>";
|
||||
keymaps = [
|
||||
{
|
||||
key = ";";
|
||||
action = ":";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
key = "<leader>m";
|
||||
options.silent = true;
|
||||
action = "<cmd>!make<CR>";
|
||||
};
|
||||
};
|
||||
];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -344,34 +349,35 @@ noremap ; :
|
|||
nnoremap <leader>m <silent> <cmd>make<CR>
|
||||
```
|
||||
|
||||
This table describes all modes for the `maps` option:
|
||||
This table describes all modes for the `keymaps` option.
|
||||
You can provide several mode to a single mapping by using a list of strings.
|
||||
|
||||
| NixVim | NeoVim |
|
||||
|----------------|--------------------------------------------------|
|
||||
| normal | Normal mode |
|
||||
| insert | Insert mode |
|
||||
| visual | Visual and Select mode |
|
||||
| select | Select mode |
|
||||
| terminal | Terminal mode |
|
||||
| normalVisualOp | Normal, visual, select and operator-pending mode |
|
||||
| visualOnly | Visual mode only, without select |
|
||||
| operator | Operator-pending mode |
|
||||
| insertCommand | Insert and command-line mode |
|
||||
| lang | Insert, command-line and lang-arg mode |
|
||||
| command | Command-line mode |
|
||||
| Short | Description |
|
||||
|-------|--------------------------------------------------|
|
||||
| `"n"` | Normal mode |
|
||||
| `"i"` | Insert mode |
|
||||
| `"v"` | Visual and Select mode |
|
||||
| `"s"` | Select mode |
|
||||
| `"t"` | Terminal mode |
|
||||
| `"" ` | Normal, visual, select and operator-pending mode |
|
||||
| `"x"` | Visual mode only, without select |
|
||||
| `"o"` | Operator-pending mode |
|
||||
| `"!"` | Insert and command-line mode |
|
||||
| `"l"` | Insert, command-line and lang-arg mode |
|
||||
| `"c"` | Command-line mode |
|
||||
|
||||
The map options can be set to either a string, containing just the action,
|
||||
or to a set describing additional options:
|
||||
Each keymap can specify the following settings in the `options` attrs.
|
||||
|
||||
| NixVim | Default | VimScript |
|
||||
|---------|---------|------------------------------------------|
|
||||
| silent | false | `<silent>` |
|
||||
| nowait | false | `<silent>` |
|
||||
| script | false | `<script>` |
|
||||
| expr | false | `<expr>` |
|
||||
| unique | false | `<unique>` |
|
||||
| noremap | true | Use the 'noremap' variant of the mapping |
|
||||
| action | N/A | Action to execute |
|
||||
| NixVim | Default | VimScript |
|
||||
|---------|---------|---------------------------------------------------|
|
||||
| silent | false | `<silent>` |
|
||||
| nowait | false | `<silent>` |
|
||||
| script | false | `<script>` |
|
||||
| expr | false | `<expr>` |
|
||||
| unique | false | `<unique>` |
|
||||
| noremap | true | Use the 'noremap' variant of the mapping |
|
||||
| remap | false | Make the mapping recursive (inverses `noremap`) |
|
||||
| desc | "" | A description of this keymap |
|
||||
|
||||
## Globals
|
||||
Sometimes you might want to define a global variable, for example to set the
|
||||
|
|
111
example.nix
111
example.nix
|
@ -5,74 +5,81 @@
|
|||
# when compared to just installing NeoVim.
|
||||
enable = true;
|
||||
|
||||
maps.normal = {
|
||||
keymaps = [
|
||||
# Equivalent to nnoremap ; :
|
||||
";" = ":";
|
||||
{
|
||||
key = ";";
|
||||
action = ":";
|
||||
}
|
||||
|
||||
# Equivalent to nmap <silent> <buffer> <leader>gg <cmd>Man<CR>
|
||||
"<leader>gg" = {
|
||||
silent = true;
|
||||
remap = false;
|
||||
{
|
||||
key = "<leader>gg";
|
||||
action = "<cmd>Man<CR>";
|
||||
# Etc...
|
||||
};
|
||||
options = {
|
||||
silent = true;
|
||||
remap = false;
|
||||
};
|
||||
}
|
||||
# Etc...
|
||||
];
|
||||
|
||||
# We can set the leader key:
|
||||
leader = ",";
|
||||
# We can set the leader key:
|
||||
leader = ",";
|
||||
|
||||
# We can create maps for every mode!
|
||||
# There is .normal, .insert, .visual, .operator, etc!
|
||||
# We can create maps for every mode!
|
||||
# There is .normal, .insert, .visual, .operator, etc!
|
||||
|
||||
# We can also set options:
|
||||
options = {
|
||||
tabstop = 4;
|
||||
shiftwidth = 4;
|
||||
expandtab = false;
|
||||
# We can also set options:
|
||||
options = {
|
||||
tabstop = 4;
|
||||
shiftwidth = 4;
|
||||
expandtab = false;
|
||||
|
||||
mouse = "a";
|
||||
mouse = "a";
|
||||
|
||||
# etc...
|
||||
};
|
||||
# etc...
|
||||
};
|
||||
|
||||
# Of course, we can still use comfy vimscript:
|
||||
extraConfigVim = builtins.readFile ./init.vim;
|
||||
# Or lua!
|
||||
extraConfigLua = builtins.readFile ./init.lua;
|
||||
# Of course, we can still use comfy vimscript:
|
||||
extraConfigVim = builtins.readFile ./init.vim;
|
||||
# Or lua!
|
||||
extraConfigLua = builtins.readFile ./init.lua;
|
||||
|
||||
# One of the big advantages of NixVim is how it provides modules for
|
||||
# popular vim plugins
|
||||
# Enabling a plugin this way skips all the boring configuration that
|
||||
# some plugins tend to require.
|
||||
plugins = {
|
||||
lightline = {
|
||||
enable = true;
|
||||
# One of the big advantages of NixVim is how it provides modules for
|
||||
# popular vim plugins
|
||||
# Enabling a plugin this way skips all the boring configuration that
|
||||
# some plugins tend to require.
|
||||
plugins = {
|
||||
lightline = {
|
||||
enable = true;
|
||||
|
||||
# This is optional - it will default to your enabled colorscheme
|
||||
colorscheme = "wombat";
|
||||
# This is optional - it will default to your enabled colorscheme
|
||||
colorscheme = "wombat";
|
||||
|
||||
# This is one of lightline's example configurations
|
||||
active = {
|
||||
left = [
|
||||
["mode" "paste"]
|
||||
["redaonly" "filename" "modified" "helloworld"]
|
||||
];
|
||||
};
|
||||
|
||||
component = {
|
||||
helloworld = "Hello, world!";
|
||||
};
|
||||
# This is one of lightline's example configurations
|
||||
active = {
|
||||
left = [
|
||||
["mode" "paste"]
|
||||
["redaonly" "filename" "modified" "helloworld"]
|
||||
];
|
||||
};
|
||||
|
||||
# Of course, there are a lot more plugins available.
|
||||
# You can find an up-to-date list here:
|
||||
# https://nixvim.pta2002.com/plugins
|
||||
component = {
|
||||
helloworld = "Hello, world!";
|
||||
};
|
||||
};
|
||||
|
||||
# There is a separate namespace for colorschemes:
|
||||
colorschemes.gruvbox.enable = true;
|
||||
|
||||
# What about plugins not available as a module?
|
||||
# Use extraPlugins:
|
||||
extraPlugins = with pkgs.vimPlugins; [vim-toml];
|
||||
# Of course, there are a lot more plugins available.
|
||||
# You can find an up-to-date list here:
|
||||
# https://nixvim.pta2002.com/plugins
|
||||
};
|
||||
|
||||
# There is a separate namespace for colorschemes:
|
||||
colorschemes.gruvbox.enable = true;
|
||||
|
||||
# What about plugins not available as a module?
|
||||
# Use extraPlugins:
|
||||
extraPlugins = with pkgs.vimPlugins; [vim-toml];
|
||||
};
|
||||
}
|
||||
|
|
548
lib/helpers.nix
548
lib/helpers.nix
|
@ -1,330 +1,286 @@
|
|||
{lib, ...}:
|
||||
with lib; rec {
|
||||
# vim dictionaries are, in theory, compatible with JSON
|
||||
toVimDict = args:
|
||||
toJSON
|
||||
(lib.filterAttrs (n: v: v != null) args);
|
||||
with lib;
|
||||
(import ./keymap-helpers.nix {inherit lib;})
|
||||
// rec {
|
||||
# vim dictionaries are, in theory, compatible with JSON
|
||||
toVimDict = args:
|
||||
toJSON
|
||||
(lib.filterAttrs (n: v: v != null) args);
|
||||
|
||||
# Black functional magic that converts a bunch of different Nix types to their
|
||||
# lua equivalents!
|
||||
toLuaObject = args:
|
||||
if builtins.isAttrs args
|
||||
then
|
||||
if hasAttr "__raw" args
|
||||
then args.__raw
|
||||
else if hasAttr "__empty" args
|
||||
then "{ }"
|
||||
else
|
||||
"{"
|
||||
+ (concatStringsSep ","
|
||||
(mapAttrsToList
|
||||
(n: v:
|
||||
if head (stringToCharacters n) == "@"
|
||||
then toLuaObject v
|
||||
else if n == "__emptyString"
|
||||
then "[''] = " + (toLuaObject v)
|
||||
else "[${toLuaObject n}] = " + (toLuaObject v))
|
||||
(filterAttrs
|
||||
(
|
||||
n: v:
|
||||
v != null && (toLuaObject v != "{}")
|
||||
)
|
||||
args)))
|
||||
+ "}"
|
||||
else if builtins.isList args
|
||||
then "{" + concatMapStringsSep "," toLuaObject args + "}"
|
||||
else if builtins.isString args
|
||||
then
|
||||
# This should be enough!
|
||||
builtins.toJSON args
|
||||
else if builtins.isPath args
|
||||
then builtins.toJSON (toString args)
|
||||
else if builtins.isBool args
|
||||
then "${boolToString args}"
|
||||
else if builtins.isFloat args
|
||||
then "${toString args}"
|
||||
else if builtins.isInt args
|
||||
then "${toString args}"
|
||||
else if (args == null)
|
||||
then "nil"
|
||||
else "";
|
||||
# Black functional magic that converts a bunch of different Nix types to their
|
||||
# lua equivalents!
|
||||
toLuaObject = args:
|
||||
if builtins.isAttrs args
|
||||
then
|
||||
if hasAttr "__raw" args
|
||||
then args.__raw
|
||||
else if hasAttr "__empty" args
|
||||
then "{ }"
|
||||
else
|
||||
"{"
|
||||
+ (concatStringsSep ","
|
||||
(mapAttrsToList
|
||||
(n: v:
|
||||
if head (stringToCharacters n) == "@"
|
||||
then toLuaObject v
|
||||
else if n == "__emptyString"
|
||||
then "[''] = " + (toLuaObject v)
|
||||
else "[${toLuaObject n}] = " + (toLuaObject v))
|
||||
(filterAttrs
|
||||
(
|
||||
n: v:
|
||||
v != null && (toLuaObject v != "{}")
|
||||
)
|
||||
args)))
|
||||
+ "}"
|
||||
else if builtins.isList args
|
||||
then "{" + concatMapStringsSep "," toLuaObject args + "}"
|
||||
else if builtins.isString args
|
||||
then
|
||||
# This should be enough!
|
||||
builtins.toJSON args
|
||||
else if builtins.isPath args
|
||||
then builtins.toJSON (toString args)
|
||||
else if builtins.isBool args
|
||||
then "${boolToString args}"
|
||||
else if builtins.isFloat args
|
||||
then "${toString args}"
|
||||
else if builtins.isInt args
|
||||
then "${toString args}"
|
||||
else if (args == null)
|
||||
then "nil"
|
||||
else "";
|
||||
|
||||
emptyTable = {"__empty" = null;};
|
||||
emptyTable = {"__empty" = null;};
|
||||
|
||||
highlightType = with lib.types;
|
||||
submodule {
|
||||
# Adds flexibility for other keys
|
||||
freeformType = types.attrs;
|
||||
highlightType = with lib.types;
|
||||
submodule {
|
||||
# Adds flexibility for other keys
|
||||
freeformType = types.attrs;
|
||||
|
||||
# :help nvim_set_hl()
|
||||
options = {
|
||||
fg = mkNullOrOption str "Color for the foreground (color name or '#RRGGBB').";
|
||||
bg = mkNullOrOption str "Color for the background (color name or '#RRGGBB').";
|
||||
sp = mkNullOrOption str "Special color (color name or '#RRGGBB').";
|
||||
blend = mkNullOrOption (numbers.between 0 100) "Integer between 0 and 100.";
|
||||
bold = mkNullOrOption bool "";
|
||||
standout = mkNullOrOption bool "";
|
||||
underline = mkNullOrOption bool "";
|
||||
undercurl = mkNullOrOption bool "";
|
||||
underdouble = mkNullOrOption bool "";
|
||||
underdotted = mkNullOrOption bool "";
|
||||
underdashed = mkNullOrOption bool "";
|
||||
strikethrough = mkNullOrOption bool "";
|
||||
italic = mkNullOrOption bool "";
|
||||
reverse = mkNullOrOption bool "";
|
||||
nocombine = mkNullOrOption bool "";
|
||||
link = mkNullOrOption str "Name of another highlight group to link to.";
|
||||
default = mkNullOrOption bool "Don't override existing definition.";
|
||||
ctermfg = mkNullOrOption str "Sets foreground of cterm color.";
|
||||
ctermbg = mkNullOrOption str "Sets background of cterm color.";
|
||||
cterm = mkNullOrOption attrs ''
|
||||
cterm attribute map, like |highlight-args|.
|
||||
If not set, cterm attributes will match those from the attribute map documented above.
|
||||
'';
|
||||
# :help nvim_set_hl()
|
||||
options = {
|
||||
fg = mkNullOrOption str "Color for the foreground (color name or '#RRGGBB').";
|
||||
bg = mkNullOrOption str "Color for the background (color name or '#RRGGBB').";
|
||||
sp = mkNullOrOption str "Special color (color name or '#RRGGBB').";
|
||||
blend = mkNullOrOption (numbers.between 0 100) "Integer between 0 and 100.";
|
||||
bold = mkNullOrOption bool "";
|
||||
standout = mkNullOrOption bool "";
|
||||
underline = mkNullOrOption bool "";
|
||||
undercurl = mkNullOrOption bool "";
|
||||
underdouble = mkNullOrOption bool "";
|
||||
underdotted = mkNullOrOption bool "";
|
||||
underdashed = mkNullOrOption bool "";
|
||||
strikethrough = mkNullOrOption bool "";
|
||||
italic = mkNullOrOption bool "";
|
||||
reverse = mkNullOrOption bool "";
|
||||
nocombine = mkNullOrOption bool "";
|
||||
link = mkNullOrOption str "Name of another highlight group to link to.";
|
||||
default = mkNullOrOption bool "Don't override existing definition.";
|
||||
ctermfg = mkNullOrOption str "Sets foreground of cterm color.";
|
||||
ctermbg = mkNullOrOption str "Sets background of cterm color.";
|
||||
cterm = mkNullOrOption attrs ''
|
||||
cterm attribute map, like |highlight-args|.
|
||||
If not set, cterm attributes will match those from the attribute map documented above.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Given an attrs of key mappings (for a single mode), applies the defaults to each one of them.
|
||||
#
|
||||
# Example:
|
||||
# mkModeMaps { silent = true; } {
|
||||
# Y = "y$";
|
||||
# "<C-c>" = { action = ":b#<CR>"; silent = false; };
|
||||
# };
|
||||
#
|
||||
# would give:
|
||||
# {
|
||||
# Y = {
|
||||
# action = "y$";
|
||||
# silent = true;
|
||||
# };
|
||||
# "<C-c>" = {
|
||||
# action = ":b#<CR>";
|
||||
# silent = false;
|
||||
# };
|
||||
# };
|
||||
mkModeMaps = defaults:
|
||||
mapAttrs
|
||||
(
|
||||
shortcut: action: let
|
||||
actionAttrs =
|
||||
if isString action
|
||||
then {inherit action;}
|
||||
else action;
|
||||
in
|
||||
defaults // actionAttrs
|
||||
);
|
||||
# Creates an option with a nullable type that defaults to null.
|
||||
mkNullOrOption = type: desc:
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr type;
|
||||
default = null;
|
||||
description = desc;
|
||||
};
|
||||
|
||||
# Applies some default mapping options to a set of mappings
|
||||
#
|
||||
# Example:
|
||||
# maps = mkMaps { silent = true; expr = true; } {
|
||||
# normal = {
|
||||
# ...
|
||||
# };
|
||||
# visual = {
|
||||
# ...
|
||||
# };
|
||||
# }
|
||||
mkMaps = defaults:
|
||||
mapAttrs
|
||||
(name: modeMaps: (mkModeMaps defaults modeMaps));
|
||||
mkIfNonNull' = x: y: (mkIf (x != null) y);
|
||||
|
||||
# Creates an option with a nullable type that defaults to null.
|
||||
mkNullOrOption = type: desc:
|
||||
lib.mkOption {
|
||||
type = lib.types.nullOr type;
|
||||
default = null;
|
||||
description = desc;
|
||||
};
|
||||
mkIfNonNull = x: (mkIfNonNull' x x);
|
||||
|
||||
mkIfNonNull' = x: y: (mkIf (x != null) y);
|
||||
ifNonNull' = x: y:
|
||||
if (x == null)
|
||||
then null
|
||||
else y;
|
||||
|
||||
mkIfNonNull = x: (mkIfNonNull' x x);
|
||||
mkCompositeOption = desc: options:
|
||||
mkNullOrOption (types.submodule {inherit options;}) desc;
|
||||
|
||||
ifNonNull' = x: y:
|
||||
if (x == null)
|
||||
then null
|
||||
else y;
|
||||
defaultNullOpts = rec {
|
||||
mkNullable = type: default: desc:
|
||||
mkNullOrOption type (
|
||||
let
|
||||
defaultDesc = "default: `${default}`";
|
||||
in
|
||||
if desc == ""
|
||||
then defaultDesc
|
||||
else ''
|
||||
${desc}
|
||||
|
||||
mkCompositeOption = desc: options:
|
||||
mkNullOrOption (types.submodule {inherit options;}) desc;
|
||||
${defaultDesc}
|
||||
''
|
||||
);
|
||||
|
||||
defaultNullOpts = rec {
|
||||
mkNullable = type: default: desc:
|
||||
mkNullOrOption type (
|
||||
let
|
||||
defaultDesc = "default: `${default}`";
|
||||
mkNum = default: mkNullable lib.types.number (toString default);
|
||||
mkInt = default: mkNullable lib.types.int (toString default);
|
||||
# Positive: >0
|
||||
mkPositiveInt = default: mkNullable lib.types.ints.positive (toString default);
|
||||
# Unsigned: >=0
|
||||
mkUnsignedInt = default: mkNullable lib.types.ints.unsigned (toString default);
|
||||
mkBool = default:
|
||||
mkNullable lib.types.bool (
|
||||
if default
|
||||
then "true"
|
||||
else "false"
|
||||
);
|
||||
mkStr = default: mkNullable lib.types.str ''${builtins.toString default}'';
|
||||
mkAttributeSet = default: mkNullable lib.types.attrs ''${default}'';
|
||||
mkEnum = enum: default: mkNullable (lib.types.enum enum) ''"${default}"'';
|
||||
mkEnumFirstDefault = enum: mkEnum enum (head enum);
|
||||
mkBorder = default: name: desc:
|
||||
mkNullable
|
||||
(
|
||||
with lib.types;
|
||||
oneOf [
|
||||
str
|
||||
(listOf str)
|
||||
(listOf (listOf str))
|
||||
]
|
||||
)
|
||||
default
|
||||
(let
|
||||
defaultDesc = ''
|
||||
Defines the border to use for ${name}.
|
||||
Accepts same border values as `nvim_open_win()`. See `:help nvim_open_win()` for more info.
|
||||
'';
|
||||
in
|
||||
if desc == ""
|
||||
then defaultDesc
|
||||
else ''
|
||||
${desc}
|
||||
|
||||
${defaultDesc}
|
||||
''
|
||||
);
|
||||
'');
|
||||
|
||||
mkNum = default: mkNullable lib.types.number (toString default);
|
||||
mkInt = default: mkNullable lib.types.int (toString default);
|
||||
# Positive: >0
|
||||
mkPositiveInt = default: mkNullable lib.types.ints.positive (toString default);
|
||||
# Unsigned: >=0
|
||||
mkUnsignedInt = default: mkNullable lib.types.ints.unsigned (toString default);
|
||||
mkBool = default:
|
||||
mkNullable lib.types.bool (
|
||||
if default
|
||||
then "true"
|
||||
else "false"
|
||||
);
|
||||
mkStr = default: mkNullable lib.types.str ''${builtins.toString default}'';
|
||||
mkAttributeSet = default: mkNullable lib.types.attrs ''${default}'';
|
||||
mkEnum = enum: default: mkNullable (lib.types.enum enum) ''"${default}"'';
|
||||
mkEnumFirstDefault = enum: mkEnum enum (head enum);
|
||||
mkBorder = default: name: desc:
|
||||
mkNullable
|
||||
(
|
||||
with lib.types;
|
||||
oneOf [
|
||||
str
|
||||
(listOf str)
|
||||
(listOf (listOf str))
|
||||
]
|
||||
)
|
||||
default
|
||||
(let
|
||||
defaultDesc = ''
|
||||
Defines the border to use for ${name}.
|
||||
Accepts same border values as `nvim_open_win()`. See `:help nvim_open_win()` for more info.
|
||||
'';
|
||||
in
|
||||
if desc == ""
|
||||
then defaultDesc
|
||||
else ''
|
||||
${desc}
|
||||
${defaultDesc}
|
||||
'');
|
||||
|
||||
mkHighlight = default: name: desc:
|
||||
mkNullable
|
||||
highlightType
|
||||
default
|
||||
(
|
||||
if desc == ""
|
||||
then "Highlight settings."
|
||||
else desc
|
||||
);
|
||||
};
|
||||
|
||||
mkPackageOption = name: default:
|
||||
mkOption {
|
||||
type = types.package;
|
||||
inherit default;
|
||||
description = "Plugin to use for ${name}";
|
||||
mkHighlight = default: name: desc:
|
||||
mkNullable
|
||||
highlightType
|
||||
default
|
||||
(
|
||||
if desc == ""
|
||||
then "Highlight settings."
|
||||
else desc
|
||||
);
|
||||
};
|
||||
|
||||
mkPlugin = {
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
name,
|
||||
description,
|
||||
package ? null,
|
||||
extraPlugins ? [],
|
||||
extraPackages ? [],
|
||||
options ? {},
|
||||
globalPrefix ? "",
|
||||
...
|
||||
}: let
|
||||
cfg = config.plugins.${name};
|
||||
# TODO support nested options!
|
||||
pluginOptions = mapAttrs (k: v: v.option) options;
|
||||
globals =
|
||||
mapAttrs'
|
||||
(name: opt: {
|
||||
name = globalPrefix + opt.global;
|
||||
value =
|
||||
if cfg.${name} != null
|
||||
then opt.value cfg.${name}
|
||||
else null;
|
||||
})
|
||||
options;
|
||||
# does this evaluate package?
|
||||
packageOption =
|
||||
if package == null
|
||||
then {}
|
||||
else {
|
||||
package = mkPackageOption name package;
|
||||
mkPackageOption = name: default:
|
||||
mkOption {
|
||||
type = types.package;
|
||||
inherit default;
|
||||
description = "Plugin to use for ${name}";
|
||||
};
|
||||
in {
|
||||
options.plugins.${name} =
|
||||
{
|
||||
enable = mkEnableOption description;
|
||||
}
|
||||
// packageOption
|
||||
// pluginOptions;
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
inherit extraPackages globals;
|
||||
# does this evaluate package? it would not be desired to evaluate pacakge if we use another package.
|
||||
extraPlugins = extraPlugins ++ optional (package != null) cfg.package;
|
||||
};
|
||||
};
|
||||
mkPlugin = {
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
name,
|
||||
description,
|
||||
package ? null,
|
||||
extraPlugins ? [],
|
||||
extraPackages ? [],
|
||||
options ? {},
|
||||
globalPrefix ? "",
|
||||
...
|
||||
}: let
|
||||
cfg = config.plugins.${name};
|
||||
# TODO support nested options!
|
||||
pluginOptions = mapAttrs (k: v: v.option) options;
|
||||
globals =
|
||||
mapAttrs'
|
||||
(name: opt: {
|
||||
name = globalPrefix + opt.global;
|
||||
value =
|
||||
if cfg.${name} != null
|
||||
then opt.value cfg.${name}
|
||||
else null;
|
||||
})
|
||||
options;
|
||||
# does this evaluate package?
|
||||
packageOption =
|
||||
if package == null
|
||||
then {}
|
||||
else {
|
||||
package = mkPackageOption name package;
|
||||
};
|
||||
in {
|
||||
options.plugins.${name} =
|
||||
{
|
||||
enable = mkEnableOption description;
|
||||
}
|
||||
// packageOption
|
||||
// pluginOptions;
|
||||
|
||||
globalVal = val:
|
||||
if builtins.isBool val
|
||||
then
|
||||
(
|
||||
if !val
|
||||
then 0
|
||||
else 1
|
||||
)
|
||||
else val;
|
||||
|
||||
mkDefaultOpt = {
|
||||
type,
|
||||
global,
|
||||
description ? null,
|
||||
example ? null,
|
||||
default ? null,
|
||||
value ? v: (globalVal v),
|
||||
...
|
||||
}: {
|
||||
option = mkOption {
|
||||
type = types.nullOr type;
|
||||
inherit default description example;
|
||||
config = mkIf cfg.enable {
|
||||
inherit extraPackages globals;
|
||||
# does this evaluate package? it would not be desired to evaluate pacakge if we use another package.
|
||||
extraPlugins = extraPlugins ++ optional (package != null) cfg.package;
|
||||
};
|
||||
};
|
||||
|
||||
inherit value global;
|
||||
};
|
||||
globalVal = val:
|
||||
if builtins.isBool val
|
||||
then
|
||||
(
|
||||
if !val
|
||||
then 0
|
||||
else 1
|
||||
)
|
||||
else val;
|
||||
|
||||
extraOptionsOptions = {
|
||||
extraOptions = mkOption {
|
||||
default = {};
|
||||
type = types.attrs;
|
||||
description = ''
|
||||
These attributes will be added to the table parameter for the setup function.
|
||||
(Can override other attributes set by nixvim)
|
||||
'';
|
||||
mkDefaultOpt = {
|
||||
type,
|
||||
global,
|
||||
description ? null,
|
||||
example ? null,
|
||||
default ? null,
|
||||
value ? v: (globalVal v),
|
||||
...
|
||||
}: {
|
||||
option = mkOption {
|
||||
type = types.nullOr type;
|
||||
inherit default description example;
|
||||
};
|
||||
|
||||
inherit value global;
|
||||
};
|
||||
};
|
||||
|
||||
mkRaw = r: {__raw = r;};
|
||||
extraOptionsOptions = {
|
||||
extraOptions = mkOption {
|
||||
default = {};
|
||||
type = types.attrs;
|
||||
description = ''
|
||||
These attributes will be added to the table parameter for the setup function.
|
||||
(Can override other attributes set by nixvim)
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
wrapDo = string: ''
|
||||
do
|
||||
${string}
|
||||
end
|
||||
'';
|
||||
mkRaw = r: {__raw = r;};
|
||||
|
||||
rawType = mkOptionType {
|
||||
name = "rawType";
|
||||
description = "raw lua code";
|
||||
descriptionClass = "noun";
|
||||
merge = mergeEqualOption;
|
||||
check = isRawType;
|
||||
};
|
||||
wrapDo = string: ''
|
||||
do
|
||||
${string}
|
||||
end
|
||||
'';
|
||||
|
||||
isRawType = v: lib.isAttrs v && lib.hasAttr "__raw" v && lib.isString v.__raw;
|
||||
}
|
||||
rawType = mkOptionType {
|
||||
name = "rawType";
|
||||
description = "raw lua code";
|
||||
descriptionClass = "noun";
|
||||
merge = mergeEqualOption;
|
||||
check = isRawType;
|
||||
};
|
||||
|
||||
isRawType = v: lib.isAttrs v && lib.hasAttr "__raw" v && lib.isString v.__raw;
|
||||
}
|
||||
|
|
64
lib/keymap-helpers.nix
Normal file
64
lib/keymap-helpers.nix
Normal file
|
@ -0,0 +1,64 @@
|
|||
{lib, ...}:
|
||||
with lib; rec {
|
||||
# Correctly merge two attrs (partially) representing a mapping.
|
||||
mergeKeymap = defaults: keymap: let
|
||||
# First, merge the `options` attrs of both options.
|
||||
mergedOpts = (defaults.options or {}) // (keymap.options or {});
|
||||
in
|
||||
# Then, merge the root attrs together and add the previously merged `options` attrs.
|
||||
(defaults // keymap) // {options = mergedOpts;};
|
||||
|
||||
# Given an attrs of key mappings (for a single mode), applies the defaults to each one of them.
|
||||
#
|
||||
# Example:
|
||||
# mkModeMaps { silent = true; } {
|
||||
# Y = "y$";
|
||||
# "<C-c>" = { action = ":b#<CR>"; silent = false; };
|
||||
# };
|
||||
#
|
||||
# would give:
|
||||
# {
|
||||
# Y = {
|
||||
# action = "y$";
|
||||
# silent = true;
|
||||
# };
|
||||
# "<C-c>" = {
|
||||
# action = ":b#<CR>";
|
||||
# silent = false;
|
||||
# };
|
||||
# };
|
||||
mkModeMaps = defaults:
|
||||
mapAttrs
|
||||
(
|
||||
key: action: let
|
||||
actionAttrs =
|
||||
if isString action
|
||||
then {inherit action;}
|
||||
else action;
|
||||
in
|
||||
defaults // actionAttrs
|
||||
);
|
||||
|
||||
# Applies some default mapping options to a set of mappings
|
||||
#
|
||||
# Example:
|
||||
# maps = mkMaps { silent = true; expr = true; } {
|
||||
# normal = {
|
||||
# ...
|
||||
# };
|
||||
# visual = {
|
||||
# ...
|
||||
# };
|
||||
# }
|
||||
mkMaps = defaults:
|
||||
mapAttrs
|
||||
(
|
||||
name: modeMaps:
|
||||
mkModeMaps defaults modeMaps
|
||||
);
|
||||
|
||||
# TODO deprecate `mkMaps` and `mkModeMaps` and leave only this one
|
||||
mkKeymaps = defaults:
|
||||
map
|
||||
(mergeKeymap defaults);
|
||||
}
|
|
@ -41,149 +41,244 @@ with lib; let
|
|||
"A textual description of this keybind, to be shown in which-key, if you have it.";
|
||||
};
|
||||
|
||||
# Generates maps for a lua config
|
||||
genMaps = mode: maps: let
|
||||
/*
|
||||
Take a user-defined action (string or attrs) and return the following attribute set:
|
||||
{
|
||||
action = (string) the actual action to map to this key
|
||||
config = (attrs) the configuration options for this mapping (noremap, silent...)
|
||||
}
|
||||
|
||||
- If the action is a string:
|
||||
{
|
||||
action = action;
|
||||
config = {};
|
||||
}
|
||||
|
||||
- If the action is an attrs:
|
||||
{
|
||||
action = action;
|
||||
config = {
|
||||
inherit (action) <values of the config options that have been explicitly set by the user>
|
||||
};
|
||||
}
|
||||
*/
|
||||
normalizeAction = action:
|
||||
if isString action
|
||||
# Case 1: action is a string
|
||||
then {
|
||||
inherit action;
|
||||
config = helpers.emptyTable;
|
||||
}
|
||||
else
|
||||
# Case 2: action is an attrs
|
||||
let
|
||||
# Extract the values of the config options that have been explicitly set by the user
|
||||
config =
|
||||
filterAttrs (n: v: v != null)
|
||||
(getAttrs (attrNames mapConfigOptions) action);
|
||||
in {
|
||||
config =
|
||||
if config == {}
|
||||
then helpers.emptyTable
|
||||
else config;
|
||||
action =
|
||||
if action.lua
|
||||
then helpers.mkRaw action.action
|
||||
else action.action;
|
||||
};
|
||||
in
|
||||
builtins.attrValues (builtins.mapAttrs
|
||||
(key: action: let
|
||||
normalizedAction = normalizeAction action;
|
||||
in {
|
||||
inherit (normalizedAction) action config;
|
||||
inherit key mode;
|
||||
})
|
||||
maps);
|
||||
|
||||
mapOption = types.oneOf [
|
||||
types.str
|
||||
(types.submodule {
|
||||
options =
|
||||
mapConfigOptions
|
||||
// {
|
||||
action =
|
||||
if config.plugins.which-key.enable
|
||||
then helpers.mkNullOrOption types.str "The action to execute"
|
||||
else
|
||||
mkOption {
|
||||
type = types.str;
|
||||
description = "The action to execute.";
|
||||
};
|
||||
|
||||
lua = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If true, `action` is considered to be lua code.
|
||||
Thus, it will not be wrapped in `""`.
|
||||
'';
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
mapOptions = mode:
|
||||
mkOption {
|
||||
description = "Mappings for ${mode} mode";
|
||||
type = types.attrsOf mapOption;
|
||||
default = {};
|
||||
modes = {
|
||||
normal.short = "n";
|
||||
insert.short = "i";
|
||||
visual = {
|
||||
desc = "visual and select";
|
||||
short = "v";
|
||||
};
|
||||
visualOnly = {
|
||||
desc = "visual only";
|
||||
short = "x";
|
||||
};
|
||||
select.short = "s";
|
||||
terminal.short = "t";
|
||||
normalVisualOp = {
|
||||
desc = "normal, visual, select and operator-pending (same as plain 'map')";
|
||||
short = "";
|
||||
};
|
||||
operator.short = "o";
|
||||
lang = {
|
||||
desc = "normal, visual, select and operator-pending (same as plain 'map')";
|
||||
short = "l";
|
||||
};
|
||||
insertCommand = {
|
||||
desc = "insert and command-line";
|
||||
short = "!";
|
||||
};
|
||||
command.short = "c";
|
||||
};
|
||||
|
||||
mkMapOptionSubmodule = {
|
||||
defaultMode ? "",
|
||||
withKeyOpt ? true,
|
||||
flatConfig ? false,
|
||||
}:
|
||||
with types;
|
||||
either
|
||||
str
|
||||
(types.submodule {
|
||||
options =
|
||||
(
|
||||
if withKeyOpt
|
||||
then {
|
||||
key = mkOption {
|
||||
type = types.str;
|
||||
description = "The key to map.";
|
||||
example = "<C-m>";
|
||||
};
|
||||
}
|
||||
else {}
|
||||
)
|
||||
// {
|
||||
mode = mkOption {
|
||||
type = let
|
||||
modeEnum =
|
||||
enum
|
||||
# ["" "n" "v" ...]
|
||||
(
|
||||
map
|
||||
(
|
||||
{short, ...}: short
|
||||
)
|
||||
(attrValues modes)
|
||||
);
|
||||
in
|
||||
either modeEnum (listOf modeEnum);
|
||||
description = ''
|
||||
One or several modes.
|
||||
Use the short-names (`"n"`, `"v"`, ...).
|
||||
See `:h map-modes` to learn more.
|
||||
'';
|
||||
default = defaultMode;
|
||||
example = ["n" "v"];
|
||||
};
|
||||
|
||||
action =
|
||||
if config.plugins.which-key.enable
|
||||
then helpers.mkNullOrOption types.str "The action to execute"
|
||||
else
|
||||
mkOption {
|
||||
type = types.str;
|
||||
description = "The action to execute.";
|
||||
};
|
||||
|
||||
lua = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If true, `action` is considered to be lua code.
|
||||
Thus, it will not be wrapped in `""`.
|
||||
'';
|
||||
default = false;
|
||||
};
|
||||
}
|
||||
// (
|
||||
if flatConfig
|
||||
then mapConfigOptions
|
||||
else {
|
||||
options = mapConfigOptions;
|
||||
}
|
||||
);
|
||||
});
|
||||
in {
|
||||
options = {
|
||||
maps = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
normal = mapOptions "normal";
|
||||
insert = mapOptions "insert";
|
||||
select = mapOptions "select";
|
||||
visual = mapOptions "visual and select";
|
||||
terminal = mapOptions "terminal";
|
||||
normalVisualOp = mapOptions "normal, visual, select and operator-pending (same as plain 'map')";
|
||||
maps =
|
||||
mapAttrs
|
||||
(
|
||||
modeName: modeProps: let
|
||||
desc = modeProps.desc or modeName;
|
||||
in
|
||||
mkOption {
|
||||
description = "Mappings for ${desc} mode";
|
||||
type = with types;
|
||||
attrsOf
|
||||
(
|
||||
either
|
||||
str
|
||||
(
|
||||
mkMapOptionSubmodule
|
||||
{
|
||||
defaultMode = modeProps.short;
|
||||
withKeyOpt = false;
|
||||
flatConfig = true;
|
||||
}
|
||||
)
|
||||
);
|
||||
default = {};
|
||||
}
|
||||
)
|
||||
modes;
|
||||
|
||||
visualOnly = mapOptions "visual only";
|
||||
operator = mapOptions "operator-pending";
|
||||
insertCommand = mapOptions "insert and command-line";
|
||||
lang = mapOptions "insert, command-line and lang-arg";
|
||||
command = mapOptions "command-line";
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
description = ''
|
||||
Custom keybindings for any mode.
|
||||
|
||||
For plain maps (e.g. just 'map' or 'remap') use maps.normalVisualOp.
|
||||
'';
|
||||
|
||||
example = ''
|
||||
maps = {
|
||||
normalVisualOp.";" = ":"; # Same as noremap ; :
|
||||
normal."<leader>m" = {
|
||||
silent = true;
|
||||
action = "<cmd>make<CR>";
|
||||
}; # Same as nnoremap <leader>m <silent> <cmd>make<CR>
|
||||
};
|
||||
'';
|
||||
keymaps = mkOption {
|
||||
type = types.listOf (mkMapOptionSubmodule {});
|
||||
default = [];
|
||||
example = [
|
||||
{
|
||||
key = "<C-m>";
|
||||
action = "<cmd>make<CR>";
|
||||
options.silent = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
mappings =
|
||||
(genMaps "" config.maps.normalVisualOp)
|
||||
++ (genMaps "n" config.maps.normal)
|
||||
++ (genMaps "i" config.maps.insert)
|
||||
++ (genMaps "v" config.maps.visual)
|
||||
++ (genMaps "x" config.maps.visualOnly)
|
||||
++ (genMaps "s" config.maps.select)
|
||||
++ (genMaps "t" config.maps.terminal)
|
||||
++ (genMaps "o" config.maps.operator)
|
||||
++ (genMaps "l" config.maps.lang)
|
||||
++ (genMaps "!" config.maps.insertCommand)
|
||||
++ (genMaps "c" config.maps.command);
|
||||
in {
|
||||
extraConfigLua =
|
||||
config = {
|
||||
warnings =
|
||||
optional
|
||||
(
|
||||
any
|
||||
(modeMaps: modeMaps != {})
|
||||
(attrValues config.maps)
|
||||
)
|
||||
''
|
||||
The `maps` option will be deprecated in the near future.
|
||||
Please, use the new `keymaps` option which works as follows:
|
||||
|
||||
keymaps = [
|
||||
{
|
||||
# Default mode is "" which means normal-visual-op
|
||||
key = "<C-m>";
|
||||
action = ":!make<CR>";
|
||||
}
|
||||
{
|
||||
# Mode can be a string or a list of strings
|
||||
mode = "n";
|
||||
key = "<leader>p";
|
||||
action = "require('my-plugin').do_stuff";
|
||||
lua = true;
|
||||
# Note that all of the mapping options are now under the `options` attrs
|
||||
options = {
|
||||
silent = true;
|
||||
desc = "My plugin does stuff";
|
||||
};
|
||||
}
|
||||
];
|
||||
'';
|
||||
|
||||
extraConfigLua = let
|
||||
modeMapsAsList =
|
||||
flatten
|
||||
(
|
||||
mapAttrsToList
|
||||
(
|
||||
modeOptionName: modeProps:
|
||||
mapAttrsToList
|
||||
(
|
||||
key: action:
|
||||
(
|
||||
if isString action
|
||||
then {
|
||||
mode = modeProps.short;
|
||||
inherit action;
|
||||
lua = false;
|
||||
options = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
inherit
|
||||
(action)
|
||||
action
|
||||
lua
|
||||
mode
|
||||
;
|
||||
}
|
||||
// {
|
||||
options =
|
||||
getAttrs
|
||||
(attrNames mapConfigOptions)
|
||||
action;
|
||||
}
|
||||
)
|
||||
// {inherit key;}
|
||||
)
|
||||
config.maps.${modeOptionName}
|
||||
)
|
||||
modes
|
||||
);
|
||||
|
||||
mappings = let
|
||||
normalizeMapping = keyMapping: {
|
||||
inherit
|
||||
(keyMapping)
|
||||
mode
|
||||
key
|
||||
;
|
||||
|
||||
action =
|
||||
if keyMapping.lua
|
||||
then helpers.mkRaw keyMapping.action
|
||||
else keyMapping.action;
|
||||
|
||||
options =
|
||||
if keyMapping.options == {}
|
||||
then helpers.emptyTable
|
||||
else keyMapping.options;
|
||||
};
|
||||
in
|
||||
map normalizeMapping
|
||||
(config.keymaps ++ modeMapsAsList);
|
||||
in
|
||||
optionalString (mappings != [])
|
||||
(
|
||||
if config.plugins.which-key.enable
|
||||
|
@ -193,9 +288,9 @@ in {
|
|||
local __nixvim_binds = ${helpers.toLuaObject mappings}
|
||||
for i, map in ipairs(__nixvim_binds) do
|
||||
if not map.action then
|
||||
require("which-key").register({[map.key] = {name = map.config.desc }})
|
||||
require("which-key").register({[map.key] = {name = map.options.desc }})
|
||||
else
|
||||
vim.keymap.set(map.mode, map.key, map.action, map.config)
|
||||
vim.keymap.set(map.mode, map.key, map.action, map.options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -206,7 +301,7 @@ in {
|
|||
do
|
||||
local __nixvim_binds = ${helpers.toLuaObject mappings}
|
||||
for i, map in ipairs(__nixvim_binds) do
|
||||
vim.keymap.set(map.mode, map.key, map.action, map.config)
|
||||
vim.keymap.set(map.mode, map.key, map.action, map.options)
|
||||
end
|
||||
end
|
||||
-- }}}
|
||||
|
|
|
@ -1,5 +1,72 @@
|
|||
{
|
||||
example = {
|
||||
{helpers, ...}: {
|
||||
legacy = {
|
||||
maps.normal."," = "<cmd>echo \"test\"<cr>";
|
||||
};
|
||||
|
||||
legacy-mkMaps = {
|
||||
maps = helpers.mkMaps {silent = true;} {
|
||||
normal."," = "<cmd>echo \"test\"<cr>";
|
||||
visual = {
|
||||
"<C-a>" = {
|
||||
action = "function() print('toto') end";
|
||||
lua = true;
|
||||
silent = false;
|
||||
};
|
||||
"<C-z>" = {
|
||||
action = "bar";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
legacy-mkModeMaps = {
|
||||
maps.normal = helpers.mkModeMaps {silent = true;} {
|
||||
"," = "<cmd>echo \"test\"<cr>";
|
||||
"<C-a>" = {
|
||||
action = "function() print('toto') end";
|
||||
lua = true;
|
||||
silent = false;
|
||||
};
|
||||
"<leader>b" = {
|
||||
action = "bar";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
example = {
|
||||
keymaps = [
|
||||
{
|
||||
key = ",";
|
||||
action = "<cmd>echo \"test\"<cr>";
|
||||
mode = ["n" "s"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkMaps = {
|
||||
keymaps =
|
||||
helpers.mkKeymaps
|
||||
{
|
||||
mode = "x";
|
||||
options.silent = true;
|
||||
}
|
||||
[
|
||||
{
|
||||
mode = "n";
|
||||
key = ",";
|
||||
action = "<cmd>echo \"test\"<cr>";
|
||||
}
|
||||
{
|
||||
key = "<C-a>";
|
||||
action = "function() print('toto') end";
|
||||
lua = true;
|
||||
options.silent = false;
|
||||
}
|
||||
{
|
||||
mode = ["n" "v"];
|
||||
key = "<C-z>";
|
||||
action = "bar";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue