nix-community.nixvim/lib/options.nix
2025-01-04 00:15:16 +01:00

407 lines
14 KiB
Nix

{ lib }:
let
inherit (lib) types;
removed = lib.mapAttrs (name: msg: throw "${name} is removed. ${msg}") {
# Removed 2024-09-05
mkPackageOption = "Use `lib.mkPackageOption` instead.";
mkPluginPackageOption = "Use `lib.mkPackageOption` instead.";
};
# Render a plugin default string
pluginDefaultText =
{
# plugin default: any value or literal expression
pluginDefault,
# nix option default value, used if `defaultText` is missing
default ? null,
# nix option default string or literal expression
defaultText ? lib.options.renderOptionValue default // {
__lang = "nix";
},
...
}:
let
# Only add `__lang` if `pluginDefault` is not already a literal type
pluginDefaultText =
if pluginDefault ? _type && pluginDefault ? text then
pluginDefault
else
lib.options.renderOptionValue pluginDefault // { __lang = "nix"; };
# Format text using markdown code block or inline code
# Handle `v` being a literalExpression or literalMD type
toMD =
v:
let
value = lib.options.renderOptionValue v;
multiline = lib.hasInfix "\n" value.text;
lang = value.__lang or ""; # `__lang` is added internally when parsed in argument defaults
in
if value._type == "literalMD" then
if multiline then "\n${value.text}" else " ${value.text}"
else if multiline then
"\n```${lang}\n${value.text}\n```"
else
" `${value.text}`";
in
lib.literalMD ''
${toMD defaultText}
_Plugin default:_${toMD pluginDefaultText}
'';
# Convert args into normal `mkOption`-style arguments, i.e. merge `pluginDefault` into `defaultText`.
#
# - `defaultText` is only set if `args` contains `pluginDefault`.
# - `pluginDefault` is removed from the resulting args.
# - All other args are untouched.
processNixvimArgs =
args:
(removeAttrs args [ "pluginDefault" ])
// (lib.optionalAttrs (args ? pluginDefault) { defaultText = pluginDefaultText args; });
in
rec {
inherit pluginDefaultText;
# Creates an option with a nullable type that defaults to null.
mkNullOrOption' =
{
type,
default ? null,
...
}@args:
lib.mkOption (
(processNixvimArgs args)
// {
type = lib.types.nullOr type;
inherit default;
}
);
mkNullOrOption = type: description: mkNullOrOption' { inherit type description; };
mkCompositeOption' =
{ options, ... }@args:
mkNullOrOption' (
(lib.filterAttrs (n: _: n != "options") args) // { type = types.submodule { inherit options; }; }
);
mkCompositeOption = description: options: mkCompositeOption' { inherit description options; };
mkNullOrStr' = args: mkNullOrOption' (args // { type = with types; maybeRaw str; });
mkNullOrStr = description: mkNullOrStr' { inherit description; };
mkNullOrLua' = args: mkNullOrOption' (args // { type = types.strLua; });
mkNullOrLua = description: mkNullOrLua' { inherit description; };
mkNullOrLuaFn' = args: mkNullOrOption' (args // { type = types.strLuaFn; });
mkNullOrLuaFn = description: mkNullOrLua' { inherit description; };
mkNullOrStrLuaOr' =
{ type, ... }@args: mkNullOrOption' (args // { type = with types; either strLua type; });
mkNullOrStrLuaOr = type: description: mkNullOrStrLuaOr' { inherit type description; };
mkNullOrStrLuaFnOr' =
{ type, ... }@args: mkNullOrOption' (args // { type = with types; either strLuaFn type; });
mkNullOrStrLuaFnOr = type: description: mkNullOrStrLuaFnOr' { inherit type description; };
defaultNullOpts =
let
# Ensures that default is null and defaultText is not set
processDefaultNullArgs =
args:
assert
args ? default
-> abort "defaultNullOpts: unexpected argument `default`. Did you mean `pluginDefault`?";
assert
args ? defaultText
-> abort "defaultNullOpts: unexpected argument `defaultText`. Did you mean `pluginDefault`?";
args // { default = null; };
in
rec {
# TODO: removed 2024-06-14; remove stub 2024-09-01
mkDesc = abort "mkDesc has been removed. Use the `pluginDefault` argument or `lib.nixvim.pluginDefaultText`.";
mkNullable' = args: mkNullOrOption' (processDefaultNullArgs args);
mkNullable =
type: pluginDefault: description:
mkNullable' { inherit type pluginDefault description; };
mkNullableWithRaw' = { type, ... }@args: mkNullable' (args // { type = types.maybeRaw type; });
mkNullableWithRaw =
type: pluginDefault: description:
mkNullableWithRaw' { inherit type pluginDefault description; };
mkStrLuaOr' = args: mkNullOrStrLuaOr' (processDefaultNullArgs args);
mkStrLuaOr =
type: pluginDefault: description:
mkStrLuaOr' { inherit type pluginDefault description; };
mkStrLuaFnOr' = args: mkNullOrStrLuaFnOr' (processDefaultNullArgs args);
mkStrLuaFnOr =
type: pluginDefault: description:
mkStrLuaFnOr' { inherit type pluginDefault description; };
mkLua' = args: mkNullOrLua' (processDefaultNullArgs args);
mkLua = pluginDefault: description: mkLua' { inherit pluginDefault description; };
mkLuaFn' = args: mkNullOrLuaFn' (processDefaultNullArgs args);
mkLuaFn = pluginDefault: description: mkLuaFn' { inherit pluginDefault description; };
mkRaw' =
args:
mkNullable' (
args
// {
type = types.rawLua;
}
// lib.optionalAttrs (args ? pluginDefault) {
pluginDefault =
if args.pluginDefault == null then null else lib.nixvim.literalLua args.pluginDefault;
}
// lib.optionalAttrs (args ? example) {
example =
if builtins.isString args.example then lib.nixvim.literalLua args.example else args.example;
}
);
mkRaw = pluginDefault: description: mkRaw' { inherit pluginDefault description; };
mkNum' = args: mkNullableWithRaw' (args // { type = types.number; });
mkNum = pluginDefault: description: mkNum' { inherit pluginDefault description; };
mkFloat' = args: mkNullableWithRaw' (args // { type = types.float; });
mkFloat = pluginDefault: description: mkFloat' { inherit pluginDefault description; };
mkProportion' = args: mkNullableWithRaw' (args // { type = types.numbers.between 0.0 1.0; });
mkProportion = pluginDefault: description: mkProportion' { inherit pluginDefault description; };
mkInt' = args: mkNullableWithRaw' (args // { type = types.int; });
mkInt = pluginDefault: description: mkNum' { inherit pluginDefault description; };
# Positive: >0
mkPositiveInt' = args: mkNullableWithRaw' (args // { type = types.ints.positive; });
mkPositiveInt = pluginDefault: description: mkPositiveInt' { inherit pluginDefault description; };
# Unsigned: >=0
mkUnsignedInt' = args: mkNullableWithRaw' (args // { type = types.ints.unsigned; });
mkUnsignedInt = pluginDefault: description: mkUnsignedInt' { inherit pluginDefault description; };
mkFlagInt = pluginDefault: description: mkFlagInt' { inherit pluginDefault description; };
mkFlagInt' = args: mkNullableWithRaw' (args // { type = types.intFlag; });
mkBool' = args: mkNullableWithRaw' (args // { type = types.bool; });
mkBool = pluginDefault: description: mkBool' { inherit pluginDefault description; };
mkStr' = args: mkNullableWithRaw' (args // { type = types.str; });
mkStr = pluginDefault: description: mkStr' { inherit pluginDefault description; };
mkAttributeSet' = args: mkNullableWithRaw' (args // { type = types.attrs; });
mkAttributeSet = pluginDefault: description: mkAttributeSet' { inherit pluginDefault description; };
mkListOf' =
{ type, ... }@args: mkNullableWithRaw' (args // { type = with types; listOf (maybeRaw type); });
mkListOf =
type: pluginDefault: description:
mkListOf' { inherit type pluginDefault description; };
mkAttrsOf' =
{ type, ... }@args: mkNullableWithRaw' (args // { type = with types; attrsOf (maybeRaw type); });
mkAttrsOf =
type: pluginDefault: description:
mkAttrsOf' { inherit type pluginDefault description; };
mkEnum' =
{ values, ... }@args:
let
showInline = lib.generators.toPretty { multiline = false; };
# Check `v` is either null, one of `values`, or a literal type
assertIsValid =
v:
v == null
|| lib.elem v values
|| (v ? _type && v ? text)
|| (v ? __raw && lib.isString v.__raw)
|| throw "Default value ${showInline v} is not valid for enum ${showInline values}.";
in
# Ensure `values` is a list and `pluginDefault` is valid if present
assert lib.isList values;
assert args ? pluginDefault -> assertIsValid args.pluginDefault;
mkNullableWithRaw' (removeAttrs args [ "values" ] // { type = types.enum values; });
mkEnum =
values: pluginDefault: description:
mkEnum' { inherit values pluginDefault description; };
mkEnumFirstDefault =
values: description:
mkEnum' {
inherit values description;
pluginDefault = lib.head values;
};
mkBorder' =
{
name,
description ? "",
...
}@args:
mkNullableWithRaw' (
(lib.filterAttrs (n: v: n != "name") args)
// {
type = types.border;
description = lib.concatStringsSep "\n" (
(lib.optional (description != "") description)
++ [
"Defines the border to use for ${name}."
"Accepts same border values as `nvim_open_win()`. See `:help nvim_open_win()` for more info."
]
);
}
);
mkBorder =
pluginDefault: name: description:
mkBorder' { inherit pluginDefault name description; };
mkSeverity' =
args:
mkNullOrOption' (
args
// {
type =
with types;
either ints.unsigned (enum [
"error"
"warn"
"info"
"hint"
]);
apply = lib.mapNullable (
value:
if lib.isInt value then
value
else
lib.nixvim.mkRaw "vim.diagnostic.severity.${lib.strings.toUpper value}"
);
}
);
mkSeverity = pluginDefault: description: mkSeverity' { inherit pluginDefault description; };
mkLogLevel' =
args:
mkNullOrOption' (
args
// {
type = with types; either ints.unsigned logLevel;
apply = lib.mapNullable (
value:
if lib.isInt value then value else lib.nixvim.mkRaw "vim.log.levels.${lib.strings.toUpper value}"
);
}
);
mkLogLevel = pluginDefault: description: mkLogLevel' { inherit pluginDefault description; };
mkHighlight' =
{
description ? "Highlight settings.",
...
}@args:
mkNullable' (
args
// {
type = types.highlight;
inherit description;
}
);
# FIXME `name` argument is ignored
# TODO deprecate in favor of `mkHighlight'`?
mkHighlight =
pluginDefault: name: description:
mkHighlight' (
{
inherit pluginDefault;
}
// (lib.optionalAttrs (description != null && description != "") { inherit description; })
);
};
mkSettingsOption =
{
options ? { },
description,
example ? null,
}:
lib.mkOption {
type =
with types;
submodule {
freeformType = attrsOf anything;
inherit options;
};
default = { };
inherit description;
example =
if example == null then
{
foo_bar = 42;
hostname = "localhost:8080";
callback.__raw = ''
function()
print('nixvim')
end
'';
}
else
example;
};
mkLazyLoadOption =
name:
lib.mkOption {
description = ''
Lazy-load settings for ${name}.
> [!WARNING]
> This is an experimental option and may not work as expected with all plugins.
> The API may change without notice.
> Please report any issues you encounter.
'';
default = { };
type = types.submodule (
{ config, ... }:
{
options = {
enable = lib.mkOption {
default = lib.any (x: x != null) (builtins.attrValues config.settings);
defaultText = lib.literalMD ''
`true` when `settings` has a non-null attribute
'';
description = ''
lazy-loading for ${name}
'';
};
settings = lib.nixvim.mkSettingsOption {
description = ''
Lazy provider configuration settings.
Check your lazy loading provider's documentation on settings to configure.
'';
example = {
cmd = "Neotest";
keys = [
{
__unkeyed-1 = "<leader>nt";
__unkeyed-3 = "<CMD>Neotest summary<CR>";
desc = "Summary toggle";
}
];
};
};
};
}
);
};
mkAutoLoadOption =
cfg: name:
lib.mkOption {
description = ''
Whether to automatically load ${name} when neovim starts.
'';
type = types.bool;
default = !(cfg.lazyLoad.enable or false);
defaultText =
if cfg ? lazyLoad then lib.literalMD "`false` when lazy-loading is enabled." else true;
example = false;
};
}
// removed