diff --git a/modules/top-level/default.nix b/modules/top-level/default.nix index b45a59af..4cd8862f 100644 --- a/modules/top-level/default.nix +++ b/modules/top-level/default.nix @@ -9,6 +9,7 @@ ./files ./nixpkgs.nix ./output.nix + ./plugins ./readonly-renames.nix ./test.nix ]; diff --git a/modules/top-level/output.nix b/modules/top-level/output.nix index 8feb9d3d..204e1cd3 100644 --- a/modules/top-level/output.nix +++ b/modules/top-level/output.nix @@ -171,126 +171,6 @@ in config = let - # Plugin normalization - normalize = - p: - let - defaultPlugin = { - plugin = null; - config = null; - optional = false; - }; - in - defaultPlugin // (if p ? plugin then p else { plugin = p; }); - normalizePluginList = plugins: map normalize plugins; - - # Byte compiling of normalized plugin list - byteCompilePlugins = - plugins: - let - byteCompile = - p: - (builders.byteCompileLuaDrv p).overrideAttrs ( - prev: lib.optionalAttrs (prev ? dependencies) { dependencies = map byteCompile prev.dependencies; } - ); - in - map (p: p // { plugin = byteCompile p.plugin; }) plugins; - - # Normalized and optionally byte compiled plugin list - normalizedPlugins = - let - normalized = normalizePluginList config.extraPlugins; - in - if config.performance.byteCompileLua.enable && config.performance.byteCompileLua.plugins then - byteCompilePlugins normalized - else - normalized; - - # Plugin list extended with dependencies - allPlugins = - let - pluginWithItsDeps = - p: [ p ] ++ builtins.concatMap pluginWithItsDeps (normalizePluginList p.plugin.dependencies or [ ]); - in - lib.unique (builtins.concatMap pluginWithItsDeps normalizedPlugins); - - # Remove dependencies from all plugins in a list - removeDependencies = ps: map (p: p // { plugin = removeAttrs p.plugin [ "dependencies" ]; }) ps; - - # Separated start and opt plugins - partitionedOptStartPlugins = builtins.partition (p: p.optional) allPlugins; - startPlugins = partitionedOptStartPlugins.wrong; - # Remove opt plugin dependencies since they are already available in start plugins - optPlugins = removeDependencies partitionedOptStartPlugins.right; - - # Test if plugin shouldn't be included in plugin pack - isStandalone = - p: - builtins.elem p.plugin config.performance.combinePlugins.standalonePlugins - || builtins.elem (lib.getName p.plugin) config.performance.combinePlugins.standalonePlugins; - - # Separated standalone and combined start plugins - partitionedStandaloneStartPlugins = builtins.partition isStandalone startPlugins; - toCombinePlugins = partitionedStandaloneStartPlugins.wrong; - # Remove standalone plugin dependencies since they are already available in start plugins - standaloneStartPlugins = removeDependencies partitionedStandaloneStartPlugins.right; - - # Combine start plugins into a single pack - pluginPack = - let - # Every plugin has its own generated help tags (doc/tags) - # Remove them to avoid collisions, new help tags - # will be generate for the entire pack later on - overriddenPlugins = map ( - plugin: - plugin.plugin.overrideAttrs (prev: { - nativeBuildInputs = lib.remove pkgs.vimUtils.vimGenDocHook prev.nativeBuildInputs or [ ]; - configurePhase = '' - ${prev.configurePhase or ""} - rm -vf doc/tags''; - }) - ) toCombinePlugins; - - # Python3 dependencies - python3Dependencies = - let - deps = map (p: p.plugin.python3Dependencies or (_: [ ])) toCombinePlugins; - in - ps: builtins.concatMap (f: f ps) deps; - - # Combined plugin - combinedPlugin = pkgs.vimUtils.toVimPlugin ( - pkgs.buildEnv { - name = "plugin-pack"; - paths = overriddenPlugins; - inherit (config.performance.combinePlugins) pathsToLink; - # Remove empty directories and activate vimGenDocHook - postBuild = '' - find $out -type d -empty -delete - runHook preFixup - ''; - passthru = { - inherit python3Dependencies; - }; - } - ); - - # Combined plugin configs - combinedConfig = builtins.concatStringsSep "\n" ( - builtins.concatMap (x: lib.optional (x.config != null && x.config != "") x.config) toCombinePlugins - ); - in - normalize { - plugin = combinedPlugin; - config = combinedConfig; - }; - - # Combined plugins - combinedPlugins = [ pluginPack ] ++ standaloneStartPlugins ++ optPlugins; - - # Plugins to use in build.package - plugins = if config.performance.combinePlugins.enable then combinedPlugins else normalizedPlugins; - neovimConfig = pkgs.neovimUtils.makeNeovimConfig ( { inherit (config) @@ -304,7 +184,7 @@ in withPython3 ; # inherit customRC; - inherit plugins; + inherit (config.build) plugins; } # Necessary to make sure the runtime path is set properly in NixOS 22.05, # or more generally before the commit: @@ -312,7 +192,7 @@ in // optionalAttrs (lib.functionArgs pkgs.neovimUtils.makeNeovimConfig ? configure) { configure.packages = { nixvim = { - start = map (x: x.plugin) plugins; + start = map (x: x.plugin) config.build.plugins; opt = [ ]; }; }; diff --git a/modules/top-level/plugins/byte-compile-plugins.nix b/modules/top-level/plugins/byte-compile-plugins.nix new file mode 100644 index 00000000..d90d361f --- /dev/null +++ b/modules/top-level/plugins/byte-compile-plugins.nix @@ -0,0 +1,17 @@ +/* + Byte compiling of normalized plugin list + + Inputs: List of normalized plugins + Outputs: List of normalized (compiled) plugins +*/ +{ lib, pkgs }: +let + builders = lib.nixvim.builders.withPkgs pkgs; + + byteCompile = + p: + (builders.byteCompileLuaDrv p).overrideAttrs ( + prev: lib.optionalAttrs (prev ? dependencies) { dependencies = map byteCompile prev.dependencies; } + ); +in +map (p: p // { plugin = byteCompile p.plugin; }) diff --git a/modules/top-level/plugins/combine-plugins.nix b/modules/top-level/plugins/combine-plugins.nix new file mode 100644 index 00000000..956328c0 --- /dev/null +++ b/modules/top-level/plugins/combine-plugins.nix @@ -0,0 +1,50 @@ +{ + lib, + pkgs, + pathsToLink, + standalonePlugins, +}: +let + inherit (import ./utils.nix lib) + getAndNormalizeDeps + removeDeps + ; + mkPluginPack = import ./mk-plugin-pack.nix { inherit lib pkgs; }; + +in +/* + *combinePlugins* function + + Take a list of combined plugins, combine the relevant ones and return the resulting list of plugins +*/ +normalizedPlugins: +let + # Plugin list extended with dependencies + allPlugins = + let + pluginWithItsDeps = p: [ p ] ++ builtins.concatMap pluginWithItsDeps (getAndNormalizeDeps p); + in + lib.unique (builtins.concatMap pluginWithItsDeps normalizedPlugins); + + # Separated start and opt plugins + partitionedOptStartPlugins = builtins.partition (p: p.optional) allPlugins; + startPlugins = partitionedOptStartPlugins.wrong; + # Remove opt plugin dependencies since they are already available in start plugins + optPlugins = removeDeps partitionedOptStartPlugins.right; + + # Test if plugin shouldn't be included in plugin pack + isStandalone = + p: + builtins.elem p.plugin standalonePlugins || builtins.elem (lib.getName p.plugin) standalonePlugins; + + # Separated standalone and combined start plugins + partitionedStandaloneStartPlugins = builtins.partition isStandalone startPlugins; + pluginsToCombine = partitionedStandaloneStartPlugins.wrong; + # Remove standalone plugin dependencies since they are already available in start plugins + standaloneStartPlugins = removeDeps partitionedStandaloneStartPlugins.right; + + # Combine start plugins into a single pack + pluginPack = mkPluginPack { inherit pluginsToCombine pathsToLink; }; +in +# Combined plugins +[ pluginPack ] ++ standaloneStartPlugins ++ optPlugins diff --git a/modules/top-level/plugins/default.nix b/modules/top-level/plugins/default.nix new file mode 100644 index 00000000..33f28beb --- /dev/null +++ b/modules/top-level/plugins/default.nix @@ -0,0 +1,53 @@ +{ + lib, + config, + pkgs, + ... +}: +let + inherit (import ./utils.nix lib) + normalizedPluginType + normalizePlugins + ; + byteCompileCfg = config.performance.byteCompileLua; +in +{ + options = { + build.plugins = lib.mkOption { + visible = false; + internal = true; + readOnly = true; + type = lib.types.listOf normalizedPluginType; + description = '' + Final list of (normalized) plugins that will be passed to the wrapper. + + It notably implements: + - byte-compilation (performance.byteCompileLua) -> ./byte-compile-plugins.nix + - plugins combining (performance.combinePlugins) -> ./combine-plugins.nix + ''; + }; + }; + + config = { + build.plugins = + let + shouldCompilePlugins = byteCompileCfg.enable && byteCompileCfg.plugins; + byteCompilePlugins = import ./byte-compile-plugins.nix { inherit lib pkgs; }; + + shouldCombinePlugins = config.performance.combinePlugins.enable; + combinePlugins = import ./combine-plugins.nix { + inherit lib pkgs; + inherit (config.performance.combinePlugins) + standalonePlugins + pathsToLink + ; + }; + in + + lib.pipe config.extraPlugins ( + [ normalizePlugins ] + ++ lib.optionals shouldCompilePlugins [ byteCompilePlugins ] + ++ lib.optionals shouldCombinePlugins [ combinePlugins ] + ); + }; +} diff --git a/modules/top-level/plugins/mk-plugin-pack.nix b/modules/top-level/plugins/mk-plugin-pack.nix new file mode 100644 index 00000000..6bb7adc2 --- /dev/null +++ b/modules/top-level/plugins/mk-plugin-pack.nix @@ -0,0 +1,60 @@ +{ lib, pkgs }: +let + inherit (import ./utils.nix lib) normalizePlugin; +in +{ pluginsToCombine, pathsToLink }: +let + + overridePlugin = + plugin: + plugin.plugin.overrideAttrs (prev: { + nativeBuildInputs = lib.remove pkgs.vimUtils.vimGenDocHook prev.nativeBuildInputs or [ ]; + configurePhase = '' + ${prev.configurePhase or ""} + rm -vf doc/tags''; + }); + + # Every plugin has its own generated help tags (doc/tags) + # Remove them to avoid collisions, new help tags + # will be generate for the entire pack later on + overriddenPlugins = map overridePlugin pluginsToCombine; + + # Gather python 3 dependencies from every plugins + python3Dependencies = + ps: + lib.pipe pluginsToCombine [ + (builtins.catAttrs "plugin") + (builtins.catAttrs "python3Dependencies") + (builtins.concatMap (f: f ps)) + ]; + + # Combined plugin + combinedPlugin = pkgs.vimUtils.toVimPlugin ( + pkgs.buildEnv { + name = "plugin-pack"; + paths = overriddenPlugins; + inherit pathsToLink; + + # Remove empty directories and activate vimGenDocHook + # TODO: figure out why we are running the `preFixup` hook in `postBuild` + postBuild = '' + find $out -type d -empty -delete + runHook preFixup + ''; + passthru = { + inherit python3Dependencies; + }; + } + ); + + # Combined plugin configs + combinedConfig = lib.pipe pluginsToCombine [ + (builtins.catAttrs "config") + (builtins.filter (config: config != null && config != "")) + (builtins.concatStringsSep "\n") + ]; +in +normalizePlugin { + plugin = combinedPlugin; + config = combinedConfig; +} diff --git a/modules/top-level/plugins/utils.nix b/modules/top-level/plugins/utils.nix new file mode 100644 index 00000000..9afdb1fa --- /dev/null +++ b/modules/top-level/plugins/utils.nix @@ -0,0 +1,38 @@ +lib: +lib.fix (self: { + normalizedPluginType = lib.types.submodule { + options = { + plugin = lib.mkOption { + type = lib.types.package; + }; + + config = lib.mkOption { + type = with lib.types; nullOr str; + }; + + optional = lib.mkOption { + type = lib.types.bool; + }; + }; + }; + + # Normalize a plugin in a standard { plugin, config, optional } attrs + normalizePlugin = + p: + let + defaultPlugin = { + plugin = null; + config = null; + optional = false; + }; + in + defaultPlugin // (if p ? plugin then p else { plugin = p; }); + + # Normalize a list of plugins + normalizePlugins = builtins.map self.normalizePlugin; + + getAndNormalizeDeps = p: self.normalizePlugins (p.plugin.dependencies or [ ]); + + # Remove dependencies from all plugins in a list + removeDeps = map (p: p // { plugin = removeAttrs p.plugin [ "dependencies" ]; }); +})