diff --git a/flakeModule.nix b/flakeModule.nix index 8f87321..2f6fd01 100644 --- a/flakeModule.nix +++ b/flakeModule.nix @@ -14,19 +14,35 @@ ... }: let cfg = config.sloth; - sLib = (cfg).lib; + sLib = cfg.lib; - packageModule = types.submodule { - options.modules = mkOption { - description = "modules used to create your neovim package"; - type = with types; listOf attrs; - default = []; + packageModule = types.submodule ({config, ...}: { + options = { + module = mkOption { + description = "module used to create your neovim package"; + type = types.deferredModule; + }; + + modules = mkOption { + description = "modules used to create your neovim package"; + type = with types; listOf attrs; + default = [config.module]; + }; + + specialArgs = mkOption { + description = "specialArgs to follow to your modules"; + type = types.attrs; + default = {}; + }; }; - }; + }); - buildPackage = { name, value }: { + buildPackage = { + name, + value, + }: { inherit name; - value = sLib.mkNeovimPkg {inherit (value) modules;}; + value = sLib.mkNeovimPkg {inherit (value) modules specialArgs;}; }; packagesList = map buildPackage (attrsToList cfg.packages); in { @@ -35,7 +51,7 @@ type = types.attrs; description = "sloth’s lib"; default = topConfig.flake.mkLib {inherit pkgs;}; - defaultText = "mkLib { inherit pkgs; }"; + defaultText = "mkLib {inherit pkgs;}"; }; packages = mkOption { diff --git a/lib/default.nix b/lib/default.nix index 598d998..8c0461f 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -15,8 +15,8 @@ in { flake = { lib = { mkNeovimPkg = import ./mkNeovimPkg.nix {inherit version types;}; - mkPluginsFromInputs = import ./mkPluginsFromInputs.nix; + mkPluginsFromInputs = import ./oldMkPluginsFromInputs.nix; }; - mkLib = import ./lib.nix; + mkLib = import ./lib.nix {inherit version;}; }; } diff --git a/lib/init.nix b/lib/init.nix new file mode 100644 index 0000000..ba4886f --- /dev/null +++ b/lib/init.nix @@ -0,0 +1,51 @@ +{pkgs, ...}: let + inherit (builtins) isAttrs isPath isString readFile; + inherit (pkgs.lib) fix mkOption types; +in + fix (self: { + module = types.submodule { + options = { + init = mkOption { + type = with types; nullOr (either path str); + default = null; + description = '' + Lua code to call before plugins loaded + ''; + }; + + postInit = mkOption { + type = with types; nullOr (either path str); + default = null; + description = '' + Lua code called after init but before import + ''; + }; + + config = mkOption { + type = with types; nullOr (either path str); + default = null; + description = '' + Lua code called after all plugins are loaded + ''; + }; + }; + }; + + option = mkOption { + default = null; + description = '' + init.lua configuration + ''; + type = with types; nullOr (oneOf [path str self.module]); + example = ./init.lua; + }; + + mkCustomLuaRc = init: + if isString init + then init + else if isPath init + then readFile init + else if isAttrs init + then "print('not implmented yet')" + else null; + }) diff --git a/lib/lib.nix b/lib/lib.nix index 31b3634..ce0a14b 100644 --- a/lib/lib.nix +++ b/lib/lib.nix @@ -1,181 +1,82 @@ -{pkgs, ...}: let - inherit (pkgs) lib; - inherit (lib) concatMap evalModules fix literalExample mkEnableOption mkOption mkPackageOption optional types; +{version, ...}: {pkgs, ...}: let + inherit (builtins) concatLists; + inherit (pkgs) bash lib; + inherit + (lib) + callPackageWith + concatMap + evalModules + fix + flip + literalExample + mkEnableOption + mkOption + mkPackageOption + optional + optionalString + types + ; - initModule = types.submodule { - options = { - init = mkOption { - type = with types; nullOr (either path str); - default = null; - description = '' - Lua code to call before plugins loaded - ''; - }; + callModule = callPackageWith {inherit pkgs callModule;}; - postInit = mkOption { - type = with types; nullOr (either path str); - default = null; - description = '' - Lua code called after init but before import - ''; - }; - - config = mkOption { - type = with types; nullOr (either path str); - default = null; - description = '' - Lua code called after all plugins are loaded - ''; - }; - }; - }; - - pluginModule = types.submodule { - options = { - init = mkOption { - type = with types; nullOr (either path str); - default = null; - description = '' - The init configuration of your plugin. - This will be called before loading your plugin. - ''; - }; - - config = mkOption { - type = with types; nullOr (either path str); - default = null; - description = '' - The configuration of your plugin. - This will be called after loading your plugin. - ''; - }; - - plugin = mkOption { - # TODO Type should allow `basicPluginType` - type = with types; nullOr package; - default = null; - description = '' - Ensure thoses plugins are loaded before the current one - ''; - }; - - dependencies = mkOption { - type = with types; listOf package; - default = []; - description = '' - Ensure thoses plugins are loaded before the current one - ''; - }; - - extraLuaPackages = mkOption { - type = with types; listOf package; - default = []; - description = '' - Ensure those packages are available - ''; - }; - - lazy = mkEnableOption "loading lazily the plugin"; - - events = mkOption { - # TODO See eventType - type = with types; listOf str; - default = []; - description = '' - List of events on which the plugin should be loaded - ''; - }; - - cmd = mkOption { - type = with types; listOf str; - default = []; - description = '' - List of commands on which the plugin should be loaded - ''; - }; - - ft = mkOption { - type = with types; listOf str; - default = []; - description = '' - List of filetypes on which the plugin should be loaded - ''; - }; - - keymaps = mkOption { - # TODO See keymapType - type = with types; listOf str; - default = []; - description = '' - List of keystrokes on which the plugin should be loaded - ''; - }; - - # priority = mkOption { - # type = types.int; - # default = []; - # description = '' - # Priority of the module. Influence the order of loading plugins. - # Highest values get loaded before. - # ''; - # }; - }; - }; - - runtimeModule = types.submodule { - options = { - version = mkOption { - type = with types; nullOr str; - description = "Optional version of your runtime"; - default = null; - example = "2025.10.16"; - }; - - src = mkOption { - type = with types; either path attrs; - description = "Files to include in your runtime"; - example = ./my-runtime; - }; - - extraOptions = mkOption { - type = types.attrs; - description = "Extra options to pass to `vimUtils.buildVimPlugin`"; - default = {}; - example.nvimRequireCheck = ["my-module.my-submodule"]; - }; - }; - }; - - convertPkgToPlugin = pkg: { - plugin = pkg; + modules = { + init = callModule ./init.nix {}; + plugin = callModule ./plugin {}; + runtime = callModule ./runtime.nix {}; + sloth = callModule ./sloth.nix {}; }; extraLuaPackagesType = - (with types; functionTo package) + (with types; functionTo (listOf package)) // { merge = loc: defs: let - fnList = concatMap (def: def.value) defs; + fnList = map (def: def.value) defs; concatPackages = ps: fn: fn ps; in ps: concatMap (concatPackages ps) fnList; }; + mkDiffAlias = name: + (flip optionalString) '' + cat < $out/bin/${name} + #!${bash}/bin/bash + exec $out/bin/nvim -d "\''${@}" + SH + chmod 555 $out/bin/${name} + ''; + defaultModule = { config, lib, ... - }: { + }: let + pkg = pkgs.wrapNeovimUnstable config.package config.neovimOptions; + + customLuaRC = modules.init.mkCustomLuaRc config.init; + + plugins = concatLists [ + (map modules.plugin.extract config.plugins) + (optional (! isNull config.runtime) (modules.runtime.mkPlugin config.runtime)) + [(modules.sloth.mkPlugin version config.plugins)] + ]; + + neovimOptions = pkgs.neovimUtils.makeNeovimConfig { + inherit (config) extraLuaPackages viAlias vimAlias; + inherit customLuaRC plugins; + wrapRc = customLuaRC != null; + # inherit customRC; + }; + + neovimPackage = pkg.overrideAttrs (final: super: { + postBuild = + super.postBuild + + (mkDiffAlias "vimdiff" config.vimdiffAlias) + + (mkDiffAlias "nvimdiff" config.nvimdiffAlias); + }); + in { options = { package = mkPackageOption pkgs "neovim-unwrapped" {}; - plugins = mkOption { - description = '' - List of plugins to enable in this installation - ''; - type = with types; listOf (coercedTo package convertPkgToPlugin pluginModule); - default = []; - }; - # Will probably be not needed # dependenciesExtraArgs = mkOption { # default = {}; @@ -192,23 +93,9 @@ default = _: []; }; - runtime = mkOption { - type = types.nullOr runtimeModule; - default = null; - description = '' - Your runtime submodule. You can configure what files should be - indluded in your runtime - ''; - }; - - init = mkOption { - default = null; - description = '' - init.lua configuration - ''; - type = with types; nullOr (oneOf [path str initModule]); - example = ./init.lua; - }; + init = modules.init.option; + runtime = modules.runtime.option; + plugins = modules.plugin.option; viAlias = mkEnableOption "creation on `vi` alias"; vimAlias = mkEnableOption "creation on `vim` alias"; @@ -218,25 +105,21 @@ neovimOptions = mkOption { type = types.attrs; description = "The resulting configuration passed to `pkgs.wrapNeovimUnstable`"; - default = pkgs.neovimUtils.makeNeovimConfig { - # inherit customRC; - # plugins = extractPlugins plugins; - # extraLuaPackages = ps: - # (extractLuaPackagesFn plugins ps) ++ (extraLuaPackages ps); - }; + default = neovimOptions; }; neovimPackage = mkOption { type = types.package; description = "The neovim package generated from your configuration."; # defaultText = lib.literalExpression "pkgs.hello"; - default = pkgs.wrapNeovimUnstable config.package config.neovimOptions; + default = neovimPackage; }; }; }; in fix (sLib: { inherit defaultModule; + mkPluginsFromInputs = import ./mkPluginsFromInputs.nix {inherit pkgs;}; evalSlothModules = { modules ? [], diff --git a/lib/mkPluginsFromInputs.nix b/lib/mkPluginsFromInputs.nix index df450aa..546bbfc 100644 --- a/lib/mkPluginsFromInputs.nix +++ b/lib/mkPluginsFromInputs.nix @@ -1,11 +1,11 @@ -{ - pkgs, +{pkgs, ...}: { inputs, predicate ? pkgs.lib.strings.hasPrefix "plugin-", nameMap ? builtins.substring 7 (-1), buildVimPlugin ? pkgs.vimUtils.buildVimPlugin, }: let inherit (builtins) attrNames filter foldl' mapAttrs; + names = filter predicate (attrNames inputs); mkPlugin = m: k: let name = nameMap k; diff --git a/lib/oldMkPluginsFromInputs.nix b/lib/oldMkPluginsFromInputs.nix new file mode 100644 index 0000000..df450aa --- /dev/null +++ b/lib/oldMkPluginsFromInputs.nix @@ -0,0 +1,20 @@ +{ + pkgs, + inputs, + predicate ? pkgs.lib.strings.hasPrefix "plugin-", + nameMap ? builtins.substring 7 (-1), + buildVimPlugin ? pkgs.vimUtils.buildVimPlugin, +}: let + inherit (builtins) attrNames filter foldl' mapAttrs; + names = filter predicate (attrNames inputs); + mkPlugin = m: k: let + name = nameMap k; + pluginDef = { + inherit name; + src = inputs.${k}; + }; + in + m // {${name} = pluginDef;}; + plugins = foldl' mkPlugin {} names; +in + mapAttrs (_: buildVimPlugin) plugins diff --git a/lib/plugin/default.nix b/lib/plugin/default.nix new file mode 100644 index 0000000..268ba2b --- /dev/null +++ b/lib/plugin/default.nix @@ -0,0 +1,136 @@ +{ + pkgs, + callModule, + ... +}: let + inherit (pkgs.lib) fix literalExample mkOption types; + + modules = { + keymap = callModule ./keymap.nix {}; + event = callModule ./event.nix {}; + }; + + coercePkgToPlugin = pkg: { + plugin = pkg; + }; + + mkROBoolOption = default: description: + mkOption { + type = types.bool; + inherit default; + readOnly = true; + description = '' + Wether this plugin has ${description}. + ''; + }; +in + fix (self: { + module = types.submodule ({config, ...}: { + options = { + init = mkOption { + type = with types; nullOr (either path str); + default = null; + description = '' + The init configuration of your plugin. + This will be called before loading your plugin. + ''; + }; + + config = mkOption { + type = with types; nullOr (either path str); + default = null; + description = '' + The configuration of your plugin. + This will be called after loading your plugin. + ''; + }; + + optional = mkOption { + type = types.bool; + default = config.lazy || config.init != null; + example = true; + }; + + plugin = mkOption { + # TODO Type should allow `basicPluginType` + type = with types; nullOr package; + default = null; + description = '' + Ensure thoses plugins are loaded before the current one + ''; + }; + + dependencies = mkOption { + type = with types; listOf package; + default = []; + description = '' + Ensure thoses plugins are loaded before the current one + ''; + }; + + extraLuaPackages = mkOption { + type = with types; functionTo (listOf package); + example = literalExample "p: [p.nvim-nio]"; + defaultText = literalExample "_: []"; + default = _: []; + description = '' + Ensure those packages are available + ''; + }; + + cmd = mkOption { + type = with types; listOf str; + default = []; + description = '' + List of commands on which the plugin should be loaded + ''; + }; + + ft = mkOption { + type = with types; listOf str; + default = []; + description = '' + List of filetypes on which the plugin should be loaded + ''; + }; + + keymaps = modules.keymap.option; + events = modules.event.option; + + hasCommands = mkROBoolOption (config.cmd != []) "declared commands"; + hasFileTypes = mkROBoolOption (config.ft != []) "file types"; + hasKeymaps = mkROBoolOption (config.keymaps != []) "keymaps"; + hasEvents = mkROBoolOption (config.events != []) "events"; + + lazy = mkOption { + type = types.bool; + default = with config; hasCommands || hasFileTypes || hasEvents || hasKeymaps; + example = true; + description = '' + Whether to enable loading lazily the plugin. + ''; + }; + + # priority = mkOption { + # type = types.int; + # default = []; + # description = '' + # Priority of the module. Influence the order of loading plugins. + # Highest values get loaded before. + # ''; + # }; + }; + }); + + option = mkOption { + description = '' + List of plugins to enable in this installation + ''; + type = with types; listOf (coercedTo package coercePkgToPlugin self.module); + default = []; + }; + + extract = plugin: { + inherit (plugin) plugin optional; + }; + }) diff --git a/lib/plugin/event.nix b/lib/plugin/event.nix new file mode 100644 index 0000000..85ca2ac --- /dev/null +++ b/lib/plugin/event.nix @@ -0,0 +1,60 @@ +{pkgs, ...}: let + inherit (builtins) elemAt isList isString mapAttrs match ; + inherit (pkgs.lib) fix mkOption splitString types ; + + wrapArray = value: + if isList value + then value + else [value]; + + hasMatch = pattern: str: isList (match pattern str); + coerceToEvent = event: let + part = elemAt (splitString " " event); + value = + if ! isString event + then event + else if ! hasMatch ".* .*" event + then {name = event;} + else { + name = part 0; + pattern = part 1; + }; + in + mapAttrs (_: wrapArray) value; + coerceToListOfEvent = events: + if isList events + then map coerceToEvent events + else [(coerceToEvent events)]; + + typeOrListOf = t: with types; coercedTo t (e: [e]) (listOf t); +in + fix (self: { + module = types.submodule { + options = { + name = mkOption { + type = typeOrListOf types.str; + default = "n"; + example = "v"; + description = '' + The name of the event + ''; + }; + + pattern = mkOption { + type = typeOrListOf types.str; + example = ""; + description = '' + The pattern of the event + ''; + }; + }; + }; + + option = mkOption { + type = with types; coercedTo (either str (listOf str)) coerceToListOfEvent (listOf self.module); + default = []; + description = '' + List of events on which the plugin should be loaded + ''; + }; + }) diff --git a/lib/plugin/keymap.nix b/lib/plugin/keymap.nix new file mode 100644 index 0000000..326e248 --- /dev/null +++ b/lib/plugin/keymap.nix @@ -0,0 +1,42 @@ +{pkgs, ...}: let + inherit (pkgs.lib) fix mkOption types; + + coerceStrToKeymap = mapping: { + inherit mapping; + mode = ["n"]; + }; + + typeOrListOf = t: with types; coercedTo t (e: [e]) (listOf t); + + modeType = types.enum ["n" "v" "s" "i" "o" "x"]; +in + fix (self: { + module = types.submodule { + options = { + mode = mkOption { + type = typeOrListOf modeType; + default = "n"; + example = "v"; + description = '' + The mode of the keymap. + ''; + }; + + mapping = mkOption { + type = typeOrListOf types.str; + example = ""; + description = '' + The actual keymap + ''; + }; + }; + }; + + option = mkOption { + type = with types; listOf (coercedTo str coerceStrToKeymap self.module); + default = []; + description = '' + List of keystrokes on which the plugin should be loaded + ''; + }; + }) diff --git a/lib/runtime.nix b/lib/runtime.nix new file mode 100644 index 0000000..b1c34b9 --- /dev/null +++ b/lib/runtime.nix @@ -0,0 +1,53 @@ +{pkgs, ...}: let + inherit (pkgs) vimUtils; + inherit (pkgs.lib) fix mkOption types; +in fix (self: { + module = types.submodule { + options = { + src = mkOption { + type = with types; either path attrs; + description = "Files to include in your runtime"; + example = ./my-runtime; + }; + + version = mkOption { + type = with types; nullOr str; + description = "Optional version of your runtime"; + default = null; + example = "2025.10.16"; + }; + + extraOptions = mkOption { + type = with types; attrsOf anything; + description = "Extra options to pass to `vimUtils.buildVimPlugin`"; + default = {}; + example.nvimRequireCheck = ["my-module.my-submodule"]; + }; + }; + }; + + option = mkOption { + type = types.nullOr self.module; + default = null; + description = '' + Your runtime submodule. You can configure what files should be + indluded in your runtime + ''; + }; + + mkPlugin = opts: let + inherit (opts) src version extraOptions; + in + vimUtils.buildVimPlugin ( + extraOptions + // {inherit src;} + // ( + if isNull version + then {name = "runtime";} + else { + pname = "runtime"; + inherit version; + } + ) + ); +}) diff --git a/lib/sloth.nix b/lib/sloth.nix new file mode 100644 index 0000000..9be5f7b --- /dev/null +++ b/lib/sloth.nix @@ -0,0 +1,80 @@ +{pkgs, ...}: let + inherit (builtins) foldl' isPath; + inherit (pkgs) vimUtils; + inherit (pkgs.lib) fileContents fix optionalAttrs; + + fs = pkgs.lib.fileset; + + lua = import ./lua.nix {}; + + versionLua = version: with lua; nix2lua (return (lambda (return version))); + pluginsLuaDef = plugins: + with lua; nix2lua (return (foldl' pluginLuaDef {} plugins)); + + textOrContent = content: + if isPath content + then fileContents content + else content; + + pluginLuaDef = memo: plugin: let + mkTypeFn = type: let + content = textOrContent plugin.${type}; + in + optionalAttrs (! isNull plugin.${type}) { + ${type} = with lua; lambda (raw content); + }; + pluginName = plugin: + if plugin ? pname + then plugin.pname + else plugin.name; + name = pluginName plugin.plugin; + in + memo + // { + ${name} = + { + name = pluginName plugin.plugin; + dependencies = map pluginName plugin.dependencies; + } + // (mkTypeFn "init") + // (mkTypeFn "config") + // (optionalAttrs plugin.lazy { + lazy = true; + }) + // (optionalAttrs plugin.hasCommands { + inherit (plugin) cmd; + }) + // (optionalAttrs plugin.hasFileTypes { + inherit (plugin) ft; + }) + // (optionalAttrs plugin.hasEvents { + inherit (plugin) events; + }) + // (optionalAttrs plugin.hasKeymaps { + inherit (plugin) keymaps; + }); + }; +in + fix (self: { + mkPlugin = version: plugins: + vimUtils.buildVimPlugin { + inherit version; + pname = "sloth-flake"; + src = fs.toSource { + root = ../.; + fileset = ../lua/sloth-flake; + }; + nvimRequireCheck = "sloth-flake"; + buildPhase = '' + dir=lua/sloth-flake + + cat <<'LUA' > $dir/dependencies.lua + ${pluginsLuaDef plugins} + LUA + + cat <<'LUA' > $dir/version.lua + ${versionLua version} + LUA + ''; + }; + })