From 8584d0880228c3e1fc31b689a8bd6b0d8be7efdb Mon Sep 17 00:00:00 2001 From: LeMarsu Date: Mon, 27 May 2024 01:04:24 +0200 Subject: [PATCH] feat: check inputs of functions with yants --- flake.lock | 36 +++- flake.nix | 6 +- lib.nix | 477 -------------------------------------------- lib/default.nix | 18 ++ lib/deps.nix | 113 +++++++++++ lib/lua.nix | 14 ++ lib/mkNeovimPkg.nix | 44 ++++ lib/types.nix | 95 +++++++++ 8 files changed, 323 insertions(+), 480 deletions(-) delete mode 100644 lib.nix create mode 100644 lib/default.nix create mode 100644 lib/deps.nix create mode 100644 lib/lua.nix create mode 100644 lib/mkNeovimPkg.nix create mode 100644 lib/types.nix diff --git a/flake.lock b/flake.lock index 0c404a7..4bdb884 100644 --- a/flake.lock +++ b/flake.lock @@ -149,12 +149,28 @@ "type": "github" } }, + "nixpkgs_3": { + "locked": { + "lastModified": 1660438583, + "narHash": "sha256-rJUTYxFKlWUJI3njAwEc1pKAVooAViZGJvsgqfh/q/E=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "bbd8f7cd87d0b29294ef3072ffdbd61d60f05da4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, "root": { "inputs": { "alejandra": "alejandra", "nil": "nil", "nixpkgs": "nixpkgs_2", - "utils": "utils" + "utils": "utils", + "yants": "yants" } }, "rust-analyzer-src": { @@ -247,6 +263,24 @@ "repo": "flake-utils-plus", "type": "github" } + }, + "yants": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1686863218, + "narHash": "sha256-kooxYm3/3ornWtVBNHM3Zh020gACUyFX2G0VQXnB+mk=", + "owner": "divnix", + "repo": "yants", + "rev": "8f0da0dba57149676aa4817ec0c880fbde7a648d", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "yants", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index cde7fff..55cae4e 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,7 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; utils.url = "github:gytis-ivaskevicius/flake-utils-plus/v1.4.0"; nil.url = "github:oxalica/nil"; + yants.url = "github:divnix/yants"; alejandra = { url = "github:kamadorueda/alejandra/3.0.0"; inputs.nixpkgs.follows = "nixpkgs"; @@ -13,9 +14,10 @@ outputs = { self, + alejandra, nixpkgs, utils, - alejandra, + yants, ... } @ inputs: let versionFile = builtins.replaceStrings ["\n"] [""] (builtins.readFile ./VERSION); @@ -36,6 +38,6 @@ }; }; - lib = import ./lib.nix {inherit version;}; + lib = import ./lib {inherit version yants;}; }; } diff --git a/lib.nix b/lib.nix deleted file mode 100644 index 4363a8d..0000000 --- a/lib.nix +++ /dev/null @@ -1,477 +0,0 @@ -{version}: { - mkNeovimPkg = { - pkgs, - package ? pkgs.neovim-unwrapped, - namePrefix ? "", - nameSuffix ? "", - dependencies ? [], - dependenciesExtraArgs ? {}, - runtime ? {}, - ... - }: let - inherit (builtins) isPath; - inherit (pkgs) lib vimUtils; - callPackage = lib.callPackageWith (pkgs // dependenciesExtraArgs); - inherit (lib.lists) concatMap filter foldl' map optional reverseList; - inherit (lib.attrsets) attrNames optionalAttrs; - inherit (lib.strings) concatStringsSep fileContents hasSuffix removePrefix removeSuffix replaceStrings; - inherit (lib.sources) sourceByRegex; - # inherit (lib.debug) traceIf traceSeq traceVal traceValSeq traceValFn; - - remotePluginToNeovimPlugin = p: - vimUtils.buildVimPlugin rec { - inherit (p) src name; - pname = name; - }; - - defaultPlugin = { - enabled = true; - init = null; - config = null; - }; - - withPluginDefaults = dep: defaultPlugin // dep; - normalizePlugin = dep: let - p = - if ! dep ? plugin - then {plugin = dep;} - else let - inherit (dep) plugin; - in - if attrNames plugin == ["name" "src"] - then {plugin = remotePluginToNeovimPlugin plugin;} - else dep; - in - withPluginDefaults p; - normalizeOrImportPlugin = dep: - if isPath dep - then normalizePlugins (callPackage dep {}) - else [(normalizePlugin dep)]; - normalizePlugins = concatMap normalizeOrImportPlugin; - - wrapLuaInFunction = section: lua: '' - -- begin ${section} - (function() - ${removeSuffix "\n" lua} - end)(); - -- end ${section} - ''; - - getLua = type: p: let - pluginName = - if p.plugin ? name - then p.plugin.name - else baseNameOf p.plugin; - - content = p.${type}; - - textContent = - if isPath content - then fileContents content - else content; - in - optional (! isNull content) - (wrapLuaInFunction "${type} for ${pluginName}" textContent); - - getAllLua = type: - concatStringsSep "\n" - (concatMap (getLua type) plugins); - - sloth-flake.plugin = vimUtils.buildVimPlugin { - inherit version; - pname = "sloth-flake"; - src = ./lua/sloth-flake; - buildPhase = '' - dir=lua/sloth-flake - mkdir -p $dir - mv init.lua $dir - - cat <<'LUA' > $dir/initialize.lua - return function () - ${getAllLua "init"} - end - LUA - - cat <<'LUA' > $dir/config.lua - return function() - ${getAllLua "config"} - end - LUA - ''; - }; - - runtimePlugin.plugin = vimUtils.buildVimPlugin ({ - inherit (runtime) src; - } - // (optionalAttrs (isNull runtime.version) { - name = "runtime"; - }) - // (optionalAttrs (! isNull runtime.version) { - inherit (runtime) version; - pname = "runtime"; - })); - - plugins = normalizePlugins (dependencies ++ [runtimePlugin sloth-flake]); - - extractPlugin = map (p: p.plugin); - - customRC = let - rc = - if runtime ? init - then runtime.init - else ""; - in - if isPath rc - then lib.fileContents rc - else rc; - - neovimConfig = - pkgs.neovimUtils.makeNeovimConfig { - inherit customRC; - plugins = extractPlugin plugins; - } - // { - luaRcContent = customRC; - }; - pkg = pkgs.wrapNeovimUnstable package (removeAttrs neovimConfig ["manifestRc" "neovimRcContent"]); - # TODO nameSuffix is buggy :'( - name = "${namePrefix}${pkg.name}${nameSuffix}"; - in - pkg // {inherit name;}; - - sourcesWith = path: paths: let - samePath = a: let a' = builtins.toString a; in b: a' == builtins.toString b; - isRoot = samePath "/"; - isInPath = path: subPath: - if isRoot subPath - then false - else (samePath path subPath) || (isInPath path (builtins.dirOf subPath)); - filter = src: _type: builtins.any (includePath: isInPath includePath src) paths; - in - builtins.path { - inherit path filter; - }; - - mkNeovimModule = { - pluginsDir ? null, - attrName ? "sloth-flake", - self, - }: { - config, - lib, - pkgs, - ... - }: let - cfg = config.${attrName}; - inherit (builtins) baseNameOf isPath; - inherit (lib) mkEnableOption mkIf mkOption types; - # inherit (lib.debug) traceIf traceSeq traceVal traceValSeq traceValFn; - inherit (lib.attrsets) attrNames optionalAttrs; - inherit (lib.lists) concatMap filter foldl' map optional reverseList; - inherit (lib.strings) concatStringsSep fileContents hasSuffix removePrefix removeSuffix replaceStrings; - - hm-file-type = import ./hm-file-type.nix { - inherit (config.home) homeDirectory; - inherit lib pkgs; - }; - inherit (hm-file-type) fileTypeSubmodule; - - verbatimSubmodule = types.submodule { - options = { - path = mkOption { - description = "path to copy from. Must be included in the flake folder."; - type = types.path; - }; - - dest = mkOption { - description = "dest into `.config/nvim`."; - type = types.str; - }; - }; - }; - - remotePluginConfig = types.addCheck (types.submodule { - options = { - name = mkOption { - type = types.str; - }; - - src = mkOption { - type = types.path; - }; - }; - }) (mod: attrNames mod == ["name" "src"]); - - pluginWithConfigType = types.submodule { - options = { - enabled = - mkEnableOption "enabled" - // { - description = '' - Whether this plugin should be enabled. This option allows specific - plugins to be disabled. - ''; - default = true; - }; - - init = mkOption { - type = types.nullOr (fileTypeSubmodule "${attrName}.plugins._.init" "{var}`xdg.configHome/nvim`" "nvim"); - description = "Script to init this plugin. Run before plugin load."; - default = null; - }; - - config = mkOption { - type = types.nullOr (fileTypeSubmodule "${attrName}.plugins._.config" "{var}`xdg.configHome/nvim`" "nvim"); - description = "Script to configure this plugin. Run after plugin load."; - default = null; - }; - - main = mkOption { - type = with types; nullOr str; - description = "Name of the main module to load."; - default = null; - }; - - ## Lazy options - - lazy = mkOption { - type = types.bool; - description = "Should this plugin be load lazily ?"; - default = false; - }; - - events = mkOption { - type = with types; listOf str; - description = "List of events on which the plugin should be loaded"; - default = []; - }; - - commands = mkOption { - type = with types; listOf str; - description = "List of commands on which the plugin should be loaded"; - default = []; - }; - - filetypes = mkOption { - type = with types; listOf str; - description = "List of filetypes on which the plugin should be loaded"; - default = []; - }; - - keys = mkOption { - type = with types; listOf str; - description = "List of keystrokes on which the plugin should be loaded"; - default = []; - }; - - priority = mkOption { - type = with types; listOf str; - description = '' - Priority of the module. Influence the order of loading plugins. - Highest values get loaded before. - ''; - default = []; - }; - - dependencies = mkOption { - # Should we accept strings? - # type = with types; listOf (either strings package); - type = with types; listOf package; - description = '' - Give the list of packages that should be loaded before the current one. - ''; - }; - - plugin = mkOption { - type = with types; oneOf [path remotePluginConfig package]; - description = "The actual vim plugin package to load"; - }; - }; - }; - - hasNixSuffix = hasSuffix ".nix"; - pluginNixFiles = - if isNull pluginsDir - then [] - else filter hasNixSuffix (lib.fileset.toList pluginsDir); - - pathToNeovimPlugin = src: let - normalizeName = replaceStrings ["."] ["-"]; - in - pkgs.vimUtils.buildVimPlugin rec { - inherit src; - pname = normalizeName (baseNameOf src); - name = pname; - }; - - remotePluginToNeovimPlugin = p: - pkgs.vimUtils.buildVimPlugin rec { - inherit (p) src name; - pname = name; - }; - - mkPlugin = plugin: - if plugin ? plugin - then let - p = plugin.plugin; - in - if isPath p - then pathToNeovimPlugin p - else if attrNames p == ["name" "src"] - then remotePluginToNeovimPlugin p - else p - else plugin; - in { - # imports = map wrapImport pluginNixFiles; - imports = pluginNixFiles; - - options.${attrName} = { - enable = mkEnableOption "${attrName} module"; - plugins = mkOption { - description = "List all plugins to load"; - type = with types; listOf (oneOf [package pluginWithConfigType]); - }; - - includesVerbatim = mkOption { - description = "Includes files as is in final .config/nvim."; - type = with types; listOf (either path verbatimSubmodule); - default = []; - }; - - defaultConfig = { - enable = mkOption { - description = "generate default configuration"; - type = types.bool; - default = true; - }; - }; - }; - - config = let - defaultPlugin = { - enabled = true; - init = null; - config = null; - }; - wrapIfNeeded = p: - if p ? plugin - then p - else {plugin = p;}; - normalizedPlugins = map (p: defaultPlugin // (wrapIfNeeded p)) cfg.plugins; - - getText = submodule: - if ! isNull submodule.text - then submodule.text - else fileContents submodule.source; - - wrapLuaInFunction = section: lua: '' - -- begin ${section} - (function() - ${removeSuffix "\n" lua} - end)(); - -- end ${section} - ''; - - pluginName = p: - if p.plugin ? name - then p.plugin.name - else baseNameOf p.plugin; - - getInitText = p: - optional (!(isNull p.init)) - (wrapLuaInFunction "init for ${pluginName p}" (getText p.init)); - - getConfigText = p: - optional (!(isNull p.config)) - (wrapLuaInFunction "config for ${pluginName p}" (getText p.config)); - - initLua = - concatStringsSep "\n" - (concatMap getInitText normalizedPlugins); - - configLua = - concatStringsSep "\n" - (concatMap getConfigText normalizedPlugins); - - pathToString = filePath: let - keyName = key: { - inherit key; - name = baseNameOf key; - }; - list = map (n: n.name) (builtins.genericClosure { - startSet = [(keyName filePath)]; - operator = item: let - parent = dirOf item.key; - in [(keyName parent)]; - }); - in - concatStringsSep "/" (reverseList list); - normalizeVerbatim = def: - if def ? path && def ? dest - then def - else if ! isPath def - then abort "Not a path nor a verbatim" - else let - fileStr = pathToString def; - root = pathToString self.outPath; - in { - path = def; - dest = removePrefix (root + "/") fileStr; - }; - - normalizedVerbatim = map normalizeVerbatim cfg.includesVerbatim; - - verbatimFiles = - foldl' - (memo: verbatim: - memo - // { - "nvim/${verbatim.dest}" = { - source = verbatim.path; - recursive = true; - }; - }) {} - normalizedVerbatim; - - slothFlakeFiles = let - prefix = "nvim/lua/sloth-flake"; - in { - ${prefix} = { - source = ./lua/sloth-flake; - recursive = true; - }; - "${prefix}/initialize.lua".text = '' - return function () - ${initLua} - end - ''; - "${prefix}/config.lua".text = '' - return function () - ${configLua} - end - ''; - }; - - defaultConfig = optionalAttrs cfg.defaultConfig.enable { - "nvim/init.lua".source = ./lua/default_init.lua; - }; - in - mkIf cfg.enable { - programs.neovim = { - enable = true; - vimAlias = true; - viAlias = true; - defaultEditor = true; - withPython3 = true; - withNodeJs = true; - - plugins = map mkPlugin cfg.plugins; - }; - - xdg.configFile = - verbatimFiles - // slothFlakeFiles - // defaultConfig; - }; - }; -} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..9c21e8f --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,18 @@ +{version, yants}: let + types = import ./types.nix {inherit yants;}; +in { + mkNeovimPkg = import ./mkNeovimPkg.nix {inherit version types;}; + + sourcesWith = path: paths: let + samePath = a: let a' = builtins.toString a; in b: a' == builtins.toString b; + isRoot = samePath "/"; + isInPath = path: subPath: + if isRoot subPath + then false + else (samePath path subPath) || (isInPath path (builtins.dirOf subPath)); + filter = src: _type: builtins.any (includePath: isInPath includePath src) paths; + in + builtins.path { + inherit path filter; + }; +} diff --git a/lib/deps.nix b/lib/deps.nix new file mode 100644 index 0000000..c924bcb --- /dev/null +++ b/lib/deps.nix @@ -0,0 +1,113 @@ +{ + pkgs, + lib, + vimUtils, + dependenciesExtraArgs, + types, + ... +}: let + inherit (builtins) isPath; + inherit (lib.attrsets) attrNames optionalAttrs; + inherit (lib.lists) concatMap optional; + inherit (lib.strings) concatStringsSep fileContents; + lua = callPackage ./lua.nix {}; + + callPackage = lib.callPackageWith (pkgs // dependenciesExtraArgs); + + defaultPlugin = { + enabled = true; + init = null; + config = null; + }; + + remotePluginToNeovimPlugin = p: + vimUtils.buildVimPlugin rec { + inherit (p) src name; + pname = name; + }; + + withPluginDefaults = dep: defaultPlugin // dep; + normalizePlugin = d: let + dep = types.dependency d; + p = + if ! dep ? plugin + then {plugin = dep;} + else let + inherit (dep) plugin; + in + if attrNames plugin == ["name" "src"] + then {plugin = remotePluginToNeovimPlugin plugin;} + else dep; + in + withPluginDefaults p; + + normalizeOrImportPlugin = dep: + if isPath dep + then normalizePlugins (callPackage dep {}) + else [(normalizePlugin dep)]; + normalizePlugins = concatMap normalizeOrImportPlugin; + + mkRuntimePlugin = { + src, + version, + ... + }: + vimUtils.buildVimPlugin ({ + inherit src; + } + // (optionalAttrs (isNull version) { + name = "runtime"; + }) + // (optionalAttrs (! isNull version) { + inherit version; + pname = "runtime"; + })); + + mkSlothFlakePlugin = version: plugins: let + getLua = type: p: let + content = p.${type}; + textContent = textOrContent content; + pluginName = + if p.plugin ? name + then p.plugin.name + else baseNameOf p.plugin; + in + optional (! isNull content) + (lua.wrapSelfInvokingFunction { + section = "${type} for ${pluginName}"; + lua = textContent; + }); + + getAllLua = type: + concatStringsSep "\n" + (concatMap (getLua type) plugins); + in + vimUtils.buildVimPlugin { + inherit version; + pname = "sloth-flake"; + src = ../lua/sloth-flake; + buildPhase = '' + dir=lua/sloth-flake + mkdir -p $dir + mv init.lua $dir + + cat <<'LUA' > $dir/initialize.lua + ${lua.wrapReturnFunction (getAllLua "init")} + LUA + + cat <<'LUA' > $dir/config.lua + ${lua.wrapReturnFunction (getAllLua "config")} + LUA + ''; + }; + + textOrContent = content: + if isPath content + then fileContents content + else content; +in { + inherit normalizePlugins; + inherit mkSlothFlakePlugin; + inherit mkRuntimePlugin; + inherit textOrContent; +} diff --git a/lib/lua.nix b/lib/lua.nix new file mode 100644 index 0000000..f79ae17 --- /dev/null +++ b/lib/lua.nix @@ -0,0 +1,14 @@ +{lib, ...}: let + inherit (lib.strings) removeSuffix; +in rec { + wrapFunction = content: "function()\n${content}\nend"; + wrapReturnFunction = content: "return ${wrapFunction content}"; + wrapSelfInvokingFunction = { + section, + lua, + }: '' + -- begin ${section} + (${wrapFunction (removeSuffix "\n" lua)})(); + -- end ${section} + ''; +} diff --git a/lib/mkNeovimPkg.nix b/lib/mkNeovimPkg.nix new file mode 100644 index 0000000..b895248 --- /dev/null +++ b/lib/mkNeovimPkg.nix @@ -0,0 +1,44 @@ +{ + version, + types, +}: { + pkgs, + package ? pkgs.neovim-unwrapped, + namePrefix ? "", + nameSuffix ? "", + dependencies ? [], + dependenciesExtraArgs ? {}, + runtime ? {}, + ... +} @ config: let + inherit (builtins) map; + inherit (pkgs) callPackage; + # inherit (lib.lists) concatMap filter foldl' map optional reverseList; + # inherit (lib.attrsets) attrNames optionalAttrs; + # inherit (lib.strings) concatStringsSep fileContents hasSuffix removePrefix removeSuffix replaceStrings; + # inherit (lib.debug) traceIf traceSeq traceVal traceValSeq traceValFn; + + deps = callPackage ./deps.nix {inherit dependenciesExtraArgs types;}; + + sloth-flake.plugin = deps.mkSlothFlakePlugin version plugins; + runtimePlugin.plugin = deps.mkRuntimePlugin runtime; + plugins = deps.normalizePlugins (dependencies ++ [runtimePlugin sloth-flake]); + + extractPlugin = map (p: p.plugin); + + customRC = let + rc = ({init ? "", ...}: init) runtime; + in + deps.textOrContent rc; + + neovimConfig = + pkgs.neovimUtils.makeNeovimConfig { + inherit customRC; + plugins = extractPlugin plugins; + } + // {luaRcContent = customRC;}; + pkg = pkgs.wrapNeovimUnstable package (removeAttrs neovimConfig ["manifestRc" "neovimRcContent"]); + # TODO nameSuffix is buggy :'( + name = "${namePrefix}${pkg.name}${nameSuffix}"; +in + builtins.seq (types.mkNeovimPkgOptions config) pkg // {inherit name;} diff --git a/lib/types.nix b/lib/types.nix new file mode 100644 index 0000000..50bdbbb --- /dev/null +++ b/lib/types.nix @@ -0,0 +1,95 @@ +{yants, ...}: rec { + # The runtime object + runtimeType = with yants; + struct "runtime" { + # The version of the runtime + version = option string; + + # The init configuration file + init = option (either path string); + + # The content of the runtime directory + src = any; + }; + + # As simple remote plugin definition + basicPluginType = with yants; + struct "basicPlugin" { + # The name of your plugin. + name = string; + # The sources of your plugin + # TODO What is the type of a source ? + src = any; + }; + + # The plugin type of dependencies + pluginType = with yants; + # let stringList = list string in + struct "plugin" { + # Whether this plugin should be enabled. This option allows specific + # plugins to be disabled. + # enable = option bool; + + # The init configuration of your plugin. + # This should be called before loading your plugin. + init = option (either path string); + + # The configuration of your plugin. + # This should be called after loading your plugin. + config = option (either path string); + + # Ensure thoses plugins are loaded before the current one + plugin = either drv basicPluginType; + + # Ensure thoses plugins are loaded before the current one + dependencies = option (list drv); + + # Should this plugin be load lazily ? + # lazy = option bool; + + # List of events on which the plugin should be loaded + # events = option stringList; + + # List of commands on which the plugin should be loaded + # commands = option stringList; + + # List of filetypes on which the plugin should be loaded + # filetypes = option stringList; + + # List of keystrokes on which the plugin should be loaded + # keys = option stringList; + + # Priority of the module. Influence the order of loading plugins. + # Highest values get loaded before. + # priority = option int; + }; + + # A dependency. + # TODO Complete doc + dependency = with yants; eitherN [path drv pluginType]; + + mkNeovimPkgOptions = with yants; + struct "mkNeovimPkgOptions" { + # The configuration of mkNeovimPkg + pkgs = attrs any; + + # The neovim package to wrap with your conifguration. + # Default is pkgs.neovim-unwrapped + package = option drv; + + # The prefix to add to the name of the package + namePrefix = option string; + + # The suffix to add to the name of the package + nameSuffix = option string; + + # An array of dependencies. + dependencies = list dependency; + + # Extra argument to pass to dependencies files + dependenciesExtraArgs = attrs any; + + # Runtime configuration + runtime = runtimeType; + }; +}