core: show warnings for deprecated or changed options (#129)

* moved helpers to lib folder

* Created proxy file for helpers.nix

* wrappers: removed code duplication

* null-ls: fix wrong name of variable

* added warnings module

* Added assertions

* bufferline: deprecated option

* nvim-tree: renamed options

* Fixed mkRenamedOption

* Bufferline: added new options

* Fixed deprecated option

Co-authored-by: Pedro Alves <pta2002@pta2002.com>
This commit is contained in:
Alexander Nortung 2023-01-24 01:28:01 +00:00 committed by GitHub
parent 5fac9be0ab
commit 63c256dc3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 369 additions and 206 deletions

178
lib/helpers.nix Normal file
View file

@ -0,0 +1,178 @@
{ lib, ... }:
with lib;
rec {
# vim dictionaries are, in theory, compatible with JSON
toVimDict = args: toJSON
(lib.filterAttrs (n: v: !isNull v) 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
"{" + (concatStringsSep ","
(mapAttrsToList
(n: v:
if head (stringToCharacters n) == "@" then
toLuaObject v
else "[${toLuaObject n}] = " + (toLuaObject v))
(filterAttrs (n: v: !isNull v && 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 isNull args then
"nil"
else "";
# Generates maps for a lua config
genMaps = mode: maps:
let
normalized = builtins.mapAttrs
(key: action:
if builtins.isString action then
{
silent = false;
expr = false;
unique = false;
noremap = true;
script = false;
nowait = false;
action = action;
}
else action)
maps;
in
builtins.attrValues (builtins.mapAttrs
(key: action:
{
action = action.action;
config = lib.filterAttrs (_: v: v) {
inherit (action) silent expr unique noremap script nowait;
};
key = key;
mode = mode;
})
normalized);
# 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;
};
defaultNullOpts = rec {
mkNullable = type: default: desc: mkNullOrOption type (
let
defaultDesc = "default: `${default}`";
in
if desc == "" then defaultDesc else ''
${desc}
${defaultDesc}
''
);
mkInt = default: mkNullable lib.types.int (toString default);
mkBool = default: mkNullable lib.types.bool (toString default);
mkStr = default: mkNullable lib.types.str ''"${default}"'';
};
mkPlugin = { config, lib, ... }: { name
, description
, package ? null
, extraPlugins ? [ ]
, extraPackages ? [ ]
, options ? { }
, ...
}:
let
cfg = config.plugins.${name};
# TODO support nested options!
pluginOptions = mapAttrs (k: v: v.option) options;
globals = mapAttrs'
(name: opt: {
name = 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 = mkOption {
type = types.package;
default = package;
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;
};
};
globalVal = val:
if builtins.isBool val then
(if val == false 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;
default = default;
description = description;
example = example;
};
inherit value global;
};
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)
'';
};
};
mkRaw = r: { __raw = r; };
wrapDo = string: ''
do
${string}
end
'';
rawType = types.submodule {
options = {
__raw = mkOption {
type = types.str;
description = "raw lua code";
default = "";
};
};
};
isRawType = v: lib.isAttrs v && lib.hasAttr "__raw" v && lib.isString v.__raw;
}

81
lib/option-warnings.nix Normal file
View file

@ -0,0 +1,81 @@
{ lib, ... }:
with lib;
rec {
# This should be used instead of mkRemovedOptionModule, when the option still works,
# but is just deprecated and should be changed now and for the future
mkDeprecatedOption =
{ option # an array of the path to the option
, alternative ? null
, message ? null
, visible ? false
}:
{ options, config, ... }:
let
fromOpt = getAttrFromPath option options;
fromValue = getAttrFromPath option config;
fullMessage = "The option `${showOption option}` has been deprecated, but might still work." +
(optionalString (alternative != null) " You may want to use `${showOption alternative}` instead.") +
(optionalString (message != null) " Message: ${message}");
in
{
config = mkIf (fromOpt.isDefined && fromValue != fromOpt.default) {
warnings = [
("Nixvim: ${fullMessage}")
];
};
};
mkRenamedOption =
{ option
, newOption
, visible ? false
, warn ? true
, overrideDescription ? null
}:
{ options, ... }:
let
fromOpt = getAttrFromPath option options;
# toOf = attrByPath newOption
# (abort "Renaming error: option `${showOption newOption}` does not exist.");
toType = let opt = attrByPath newOption { } options; in
opt.type or (types.submodule { });
message = "`${showOption option}` has been renamed to `${showOption newOption}`, but can still be used for compatibility";
in
{
options = setAttrByPath option (mkOption
{
inherit visible;
description =
if overrideDescription == null
then message
else overrideDescription;
} // optionalAttrs (toType != null) {
type = toType;
});
config = mkMerge [
{
warnings = mkIf (warn && fromOpt.isDefined) [ "Nixvim: ${message}" ];
}
(mkAliasAndWrapDefinitions (setAttrByPath newOption) fromOpt)
];
};
mkAliasOption = option: newOption: mkRenamedOption {
inherit option newOption;
visible = true;
warn = false;
overrideDescription = "Alias of ${showOption newOption}";
};
# TODO:
# mkRemovedOption =
# { option
# , visible ? false
# }:
# { options, ... }:
# {
# options = { };
# config = { };
# };
}

17
modules/warnings.nix Normal file
View file

@ -0,0 +1,17 @@
{ lib, ... }:
with lib;
{
options = {
warnings = mkOption {
type = types.listOf types.str;
visible = false;
default = [];
};
assertions = mkOption {
type = types.listOf types.attrs; # Not sure what the correct type is here
visible = false;
default = [];
};
};
}

View file

@ -1,8 +1,9 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, ... }@args:
with lib;
let
cfg = config.plugins.bufferline;
helpers = import ../helpers.nix { inherit lib; };
optionWarnings = import ../../lib/option-warnings.nix args;
helpers = import ../helpers.nix args;
highlight = mkOption {
type = types.nullOr (types.submodule ({ ... }: {
@ -23,6 +24,13 @@ let
};
in
{
imports = [
(optionWarnings.mkDeprecatedOption {
option = [ "plugins" "bufferline" "indicatorIcon" ];
alternative = [ "plugins" "bufferline" "indicator" "icon" ];
})
];
options = {
plugins.bufferline = {
enable = mkEnableOption "bufferline";
@ -56,6 +64,7 @@ in
description = "Command or function run when middle clicking on a buffer.";
default = null;
};
# is deprecated, but might still work
indicatorIcon = mkOption {
type = types.nullOr types.str;
description = "The Icon shown as a indicator for buffer. Changing it is NOT recommended,
@ -157,6 +166,21 @@ in
type = types.nullOr (types.enum [ "id" "extension" "relative_directory" "directory" "tabs" ]);
default = null;
};
indicator = mkOption {
default = null;
type = types.nullOr (types.submodule ({ ... }: {
options = {
icon = mkOption {
type = types.nullOr types.str;
default = null;
};
style = mkOption {
type = types.nullOr (types.enum [ "icon" "underline" "none" ]);
default = null;
};
};
}));
};
highlights = mkOption {
default = null;
type = types.nullOr (types.submodule ({ ... }: {
@ -241,7 +265,12 @@ in
right_mouse_command = cfg.rightMouseCommand;
left_mouse_command = cfg.leftMouseCommand;
middle_mouse_command = cfg.middleMouseCommand;
# deprecated, but might still work
indicator_icon = cfg.indicatorIcon;
indicator = {
icon = cfg.indicator.icon;
style = cfg.indicator.style;
};
buffer_close_icon = cfg.bufferCloseIcon;
modified_icon = cfg.modifiedIcon;
close_icon = cfg.closeIcon;

View file

@ -1,164 +1,2 @@
{ lib, ... }:
with lib;
rec {
# vim dictionaries are, in theory, compatible with JSON
toVimDict = args: toJSON
(lib.filterAttrs (n: v: !isNull v) args);
args: import ../lib/helpers.nix 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
"{" + (concatStringsSep ","
(mapAttrsToList
(n: v: if head (stringToCharacters n) == "@" then
toLuaObject v
else "[${toLuaObject n}] = " + (toLuaObject v))
(filterAttrs (n: v: !isNull v && 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 isNull args then
"nil"
else "";
# Generates maps for a lua config
genMaps = mode: maps: let
normalized = builtins.mapAttrs (key: action:
if builtins.isString action then
{
silent = false;
expr = false;
unique = false;
noremap = true;
script = false;
nowait = false;
action = action;
}
else action) maps;
in builtins.attrValues (builtins.mapAttrs (key: action:
{
action = action.action;
config = lib.filterAttrs (_: v: v) {
inherit (action) silent expr unique noremap script nowait;
};
key = key;
mode = mode;
}) normalized);
# 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;
};
defaultNullOpts = rec {
mkNullable = type: default: desc: mkNullOrOption type (let
defaultDesc = "default: `${default}`";
in if desc == "" then defaultDesc else ''
${desc}
${defaultDesc}
'');
mkInt = default: mkNullable lib.types.int (toString default);
mkBool = default: mkNullable lib.types.bool (toString default);
mkStr = default: mkNullable lib.types.str ''"${default}"'';
};
mkPlugin = { config, lib, ... }: {
name,
description,
package ? null,
extraPlugins ? [],
extraPackages ? [],
options ? {},
...
}: let
cfg = config.plugins.${name};
# TODO support nested options!
pluginOptions = mapAttrs (k: v: v.option) options;
globals = mapAttrs' (name: opt: {
name = 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 = mkOption {
type = types.package;
default = package;
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;
};
};
globalVal = val: if builtins.isBool val then
(if val == false 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;
default = default;
description = description;
example = example;
};
inherit value global;
};
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)
'';
};
};
mkRaw = r: { __raw = r; };
wrapDo = string: ''
do
${string}
end
'';
rawType = types.submodule {
options = {
__raw = mkOption {
type = types.str;
description = "raw lua code";
default = "";
};
};
};
isRawType = v: lib.isAttrs v && lib.hasAttr "__raw" v && lib.isString v.__raw;
}

View file

@ -1,10 +1,23 @@
{ pkgs, config, lib, ... }:
{ pkgs, config, lib, ... }@args:
with lib;
let
cfg = config.plugins.nvim-tree;
helpers = import ../helpers.nix { lib = lib; };
optionWarnings = import ../../lib/option-warnings.nix args;
basePluginPath = [ "plugins" "nvim-tree" ];
in
{
imports = [
(optionWarnings.mkRenamedOption {
option = basePluginPath ++ [ "updateCwd" ];
newOption = basePluginPath ++ [ "syncRootWithCwd" ];
})
(optionWarnings.mkRenamedOption {
option = basePluginPath ++ [ "updateFocusedFile" "updatedCwd" ];
newOption = basePluginPath ++ [ "updateFocusedFile" "updateRoot" ];
})
];
options.plugins.nvim-tree = {
enable = mkEnableOption "nvim-tree";
@ -54,8 +67,7 @@ in
description = "Hijack cursor";
};
# TODO: change this to it's new definition sync_root_with_cwd
updateCwd = mkOption {
syncRootWithCwd = mkOption {
type = types.nullOr types.bool;
default = null;
};
@ -106,8 +118,7 @@ in
default = null;
};
# TODO: change this to it's new definition update_root
updateCwd = mkOption {
updateRoot = mkOption {
type = types.nullOr types.bool;
default = null;
};
@ -225,7 +236,7 @@ in
ignore_ft_on_setup = cfg.ignoreFtOnSetup;
open_on_tab = cfg.openOnTab;
hijack_cursor = cfg.hijackCursor;
update_cwd = cfg.updateCwd;
sync_root_with_cwd = cfg.syncRootWithCwd;
respect_buf_cwd = cfg.respectBufCwd;
update_to_buf_dir = {
enable = cfg.updateToBufDir.enable;
@ -234,7 +245,7 @@ in
diagnostics = cfg.diagnostics;
update_focused_file = {
enable = cfg.updateFocusedFile.enable;
update_cwd = cfg.updateFocusedFile.updateCwd;
update_root = cfg.updateFocusedFile.updateRoot;
ignore_list = cfg.updateFocusedFile.ignoreList;
};
system_open = cfg.systemOpen;

16
wrappers/_shared.nix Normal file
View file

@ -0,0 +1,16 @@
{ lib, ... }:
let
inherit (lib) mkEnableOption mkOption mkOptionType mkForce mkMerge mkIf types;
in
{
helpers = mkOption {
type = mkOptionType {
name = "helpers";
description = "Helpers that can be used when writing nixvim configs";
check = builtins.isAttrs;
};
description = "Use this option to access the helpers";
default = import ../plugins/helpers.nix { inherit (pkgs) lib; };
};
}

View file

@ -1,8 +1,9 @@
modules:
{ pkgs, config, lib, ... }:
{ pkgs, config, lib, ... }@args:
let
inherit (lib) mkEnableOption mkOption mkOptionType mkForce mkMerge mkIf types;
shared = import ./_shared.nix args;
cfg = config.programs.nixvim;
in
{
@ -10,21 +11,19 @@ in
programs.nixvim = mkOption {
type = types.submodule ((modules pkgs) ++ [{
options.enable = mkEnableOption "nixvim";
config.wrapRc = mkForce true;
config.wrapRc = mkForce true;
}]);
};
nixvim.helpers = mkOption {
type = mkOptionType {
name = "helpers";
description = "Helpers that can be used when writing nixvim configs";
check = builtins.isAttrs;
};
description = "Use this option to access the helpers";
default = import ../plugins/helpers.nix { inherit (pkgs) lib; };
};
nixvim.helpers = shared.helpers;
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.finalPackage ];
};
config = mkIf cfg.enable mkMerge [
{
environment.systemPackages = [ cfg.finalPackage ];
}
{
warnings = cfg.warnings;
assertions = cfg.assertions;
}
];
}

View file

@ -1,8 +1,9 @@
modules:
{ pkgs, config, lib, ... }:
{ pkgs, config, lib, ... }@args:
let
inherit (lib) mkEnableOption mkOption mkOptionType mkMerge mkIf types;
shared = import ./_shared.nix args;
cfg = config.programs.nixvim;
in
{
@ -12,15 +13,7 @@ in
options.enable = mkEnableOption "nixvim";
}]);
};
nixvim.helpers = mkOption {
type = mkOptionType {
name = "helpers";
description = "Helpers that can be used when writing nixvim configs";
check = builtins.isAttrs;
};
description = "Use this option to access the helpers";
default = import ../plugins/helpers.nix { inherit (pkgs) lib; };
};
nixvim.helpers = shared.helpers;
};
config = mkIf cfg.enable
@ -29,5 +22,9 @@ in
(mkIf (!cfg.wrapRc) {
xdg.configFile."nvim/init.lua".text = cfg.initContent;
})
({
warnings = cfg.warnings;
assertions = cfg.assertions;
})
]);
}

View file

@ -1,8 +1,9 @@
modules:
{ pkgs, config, lib, ... }:
{ pkgs, config, lib, ... }@args:
let
inherit (lib) mkEnableOption mkOption mkOptionType mkMerge mkIf types;
shared = import ./_shared.nix args;
cfg = config.programs.nixvim;
in
{
@ -12,15 +13,7 @@ in
options.enable = mkEnableOption "nixvim";
}]);
};
nixvim.helpers = mkOption {
type = mkOptionType {
name = "helpers";
description = "Helpers that can be used when writing nixvim configs";
check = builtins.isAttrs;
};
description = "Use this option to access the helpers";
default = import ../plugins/helpers.nix { inherit (pkgs) lib; };
};
nixvim.helpers = shared.helpers;
};
config = mkIf cfg.enable
@ -30,5 +23,9 @@ in
environment.etc."nvim/sysinit.lua".text = cfg.initContent;
environment.variables."VIM" = "/etc/nvim";
})
({
warnings = cfg.warnings;
assertions = cfg.assertions;
})
]);
}