nix-community.nixvim/lib/deprecation.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

191 lines
6 KiB
Nix
Raw Normal View History

{ lib }:
rec {
# Get a (sub)option by walking the path,
# checking for submodules along the way
getOptionRecursive =
opt: prefix: optionPath:
if optionPath == [ ] then
opt
2024-08-30 14:49:20 -05:00
else if lib.isOption opt then
getOptionRecursive (opt.type.getSubOptions prefix) prefix optionPath
else
let
2024-08-30 14:49:20 -05:00
name = lib.head optionPath;
opt' = lib.getAttr name opt;
prefix' = prefix ++ [ name ];
2024-08-30 14:49:20 -05:00
optionPath' = lib.drop 1 optionPath;
in
getOptionRecursive opt' prefix' optionPath';
# Like mkRemovedOptionModule, but has support for nested sub-options
# and uses warnings instead of assertions.
mkDeprecatedSubOptionModule =
optionPath: replacementInstructions:
{ options, ... }:
{
2024-08-30 14:49:20 -05:00
options = lib.setAttrByPath optionPath (
lib.mkOption {
# When (e.g.) `mkAttrs` is used on a submodule, this option will be evaluated.
# Therefore we have to apply _something_ (null) when there's no definition.
apply =
v:
let
# Avoid "option used but not defined" errors
res = builtins.tryEval v;
in
if res.success then res.value else null;
visible = false;
}
);
config.warnings =
let
opt = getOptionRecursive options [ ] optionPath;
in
2024-08-30 14:49:20 -05:00
lib.optional opt.isDefined ''
The option definition `${lib.showOption optionPath}' in ${lib.showFiles opt.files} is deprecated.
${replacementInstructions}
'';
};
/*
Returns a function that maps
[
"someOption"
["fooBar" "someSubOption"]
{ old = "someOtherOption"; new = ["foo_bar" "some_other_option"]}
]
to
[
(lib.mkRenamedOptionModule
(oldPath ++ ["someOption"])
(newPath ++ ["some_option"])
)
(lib.mkRenamedOptionModule
(oldPath ++ ["fooBar" "someSubOption"])
(newPath ++ ["foo_bar" "some_sub_option"])
)
(lib.mkRenamedOptionModule
(oldPath ++ ["someOtherOption"])
(newPath ++ ["foo_bar" "some_other_option"])
)
]
*/
mkSettingsRenamedOptionModules =
oldPrefix: newPrefix:
map (
spec:
let
finalSpec =
if lib.isAttrs spec then
lib.mapAttrs (_: lib.toList) spec
else
{
old = lib.toList spec;
new = map lib.nixvim.toSnakeCase finalSpec.old;
};
in
lib.mkRenamedOptionModule (oldPrefix ++ finalSpec.old) (newPrefix ++ finalSpec.new)
);
# A clone of types.coercedTo, but it prints a warning when oldType is used.
transitionType =
oldType: coerceFn: newType:
assert lib.assertMsg (
oldType.getSubModules == null
) "transitionType: oldType must not have submodules (its a ${oldType.description})";
lib.mkOptionType rec {
name = "transitionType";
inherit (newType) description;
check = x: (oldType.check x && newType.check (coerceFn x)) || newType.check x;
merge =
opt: defs:
let
coerceVal =
val:
if oldType.check val then
lib.warn ''
Passing a ${oldType.description} for `${lib.showOption opt}' is deprecated, use ${newType.description} instead. Definitions: ${lib.options.showDefs defs}
'' (coerceFn val)
else
val;
in
newType.merge opt (map (def: def // { value = coerceVal def.value; }) defs);
inherit (newType) emptyValue;
inherit (newType) getSubOptions;
inherit (newType) getSubModules;
substSubModules = m: transitionType oldType coerceFn (newType.substSubModules m);
typeMerge = t1: t2: null;
functor = (lib.types.defaultFunctor name) // {
wrapped = newType;
};
nestedTypes.coercedType = oldType;
nestedTypes.finalType = newType;
};
2025-04-06 17:17:51 +02:00
mkRemovedPackageOptionModule =
{
plugin,
packageName,
oldPackageName ? packageName,
}:
{ config, options, ... }:
2025-04-07 17:22:34 +02:00
let
cfg = config.dependencies.${packageName};
opt = options.dependencies.${packageName};
oldOptPath = builtins.concatMap lib.toList [
"plugins"
plugin
"${oldPackageName}Package"
];
oldOpt = lib.getAttrFromPath oldOptPath options;
# We can't use `oldOpt.value` because that will use our `apply`, so we merge the definitions ourselves:
oldDef = lib.modules.mergeDefinitions oldOpt.loc oldOpt.type oldOpt.definitionsWithLocations;
# Conceptually similar to `mkDerivedConfig`, but uses our manually merged definition
configFromOldDef =
{
predicate ? _: true,
apply ? lib.id,
}:
let
inherit (oldDef) mergedValue isDefined;
inherit (oldDef.defsFinal') highestPrio;
condition = isDefined && predicate mergedValue;
value = apply mergedValue;
in
lib.mkIf condition (lib.mkOverride highestPrio value);
2025-04-07 17:22:34 +02:00
in
{
options = lib.setAttrByPath oldOptPath (
lib.mkOption {
type = with lib.types; nullOr package;
description = "Alias of `${opt.enable}` and `${opt.package}`.";
visible = false;
apply =
let
value = if cfg.enable then cfg.package else null;
use = builtins.trace "Obsolete option `${oldOpt}' is used. It was replaced by `${opt.enable}' and `${opt.package}'.";
in
_: use value;
}
);
config = lib.mkIf oldOpt.isDefined {
warnings = [
"The option `${oldOpt}' defined in ${lib.showFiles oldOpt.files} has been replaced by `${opt.enable}' and `${opt.package}'."
];
dependencies.${packageName} = {
enable = configFromOldDef {
apply = pkg: pkg != null;
};
package = configFromOldDef {
predicate = pkg: pkg != null;
};
};
};
};
}