diff --git a/lib/helpers.nix b/lib/helpers.nix new file mode 100644 index 00000000..94d4ab08 --- /dev/null +++ b/lib/helpers.nix @@ -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; +} diff --git a/lib/option-warnings.nix b/lib/option-warnings.nix new file mode 100644 index 00000000..68bb5666 --- /dev/null +++ b/lib/option-warnings.nix @@ -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 = { }; + # }; +} diff --git a/modules/warnings.nix b/modules/warnings.nix new file mode 100644 index 00000000..27905187 --- /dev/null +++ b/modules/warnings.nix @@ -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 = []; + }; + }; +} diff --git a/plugins/bufferlines/bufferline.nix b/plugins/bufferlines/bufferline.nix index 1d5f0596..e8371197 100644 --- a/plugins/bufferlines/bufferline.nix +++ b/plugins/bufferlines/bufferline.nix @@ -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; diff --git a/plugins/helpers.nix b/plugins/helpers.nix index f976cfe9..a6d36262 100644 --- a/plugins/helpers.nix +++ b/plugins/helpers.nix @@ -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; -} diff --git a/plugins/utils/nvim-tree.nix b/plugins/utils/nvim-tree.nix index a75106a1..bf4fb9ac 100644 --- a/plugins/utils/nvim-tree.nix +++ b/plugins/utils/nvim-tree.nix @@ -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; diff --git a/wrappers/_shared.nix b/wrappers/_shared.nix new file mode 100644 index 00000000..6ebe609f --- /dev/null +++ b/wrappers/_shared.nix @@ -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; }; + }; +} diff --git a/wrappers/darwin.nix b/wrappers/darwin.nix index 75365711..0267a40b 100644 --- a/wrappers/darwin.nix +++ b/wrappers/darwin.nix @@ -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; + } + ]; } diff --git a/wrappers/hm.nix b/wrappers/hm.nix index e10b63ca..971c255a 100644 --- a/wrappers/hm.nix +++ b/wrappers/hm.nix @@ -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; + }) ]); } diff --git a/wrappers/nixos.nix b/wrappers/nixos.nix index f0474eea..66eadbcb 100644 --- a/wrappers/nixos.nix +++ b/wrappers/nixos.nix @@ -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; + }) ]); }