{ config, lib, pkgs, ... }: with lib; let cfg = config.my.programs.nvim; toLua = value: with builtins; if value == null then "nil" else if isBool value then boolToString value else if isInt value || isFloat value then toString value else if isString value then string value else if isAttrs value then attrs value else if isList value then list value else abort "should never happen (value = ${value})"; string = str: ''"${toString str}"''; attrs = set: let toKeyword = name: value: "${name} = ${toLua value}"; keywords = concatStringsSep ", " (mapAttrsToList toKeyword set); in "{ " + keywords + " }"; listContent = values: concatStringsSep ", " (map toLua values); list = values: "{ " + listContent values + " }"; luaList = values: "{" + (concatStringsSep ", " values) + "}"; keybinding = { key, cmd, func, mode, desc }: let cmdString = if cmd != null then toLua cmd else if func != null then func else abort "Either cmd or function must be set"; descString = optionalString (desc != null) "desc = ${toLua desc},"; in ''{ ${toLua key}, ${cmdString}, mode = ${toLua mode}, ${descString} }''; lazySpecFromPlugin = { cmd , conf , dependencies , enabled , event , ft , init , keys , lazy , opts , plugin , priority }: luaList ([ "dir = ${string plugin}" "name = ${toLua (getName plugin)}" ] ++ (optional (opts != null) "opts = ${toLua opts}") ++ (optional (lazy != null) "lazy = ${toLua lazy}") ++ (optional (!enabled) "enabled = ${toLua enabled}") ++ (optional (dependencies != [ ]) "dependencies = ${luaList (map lazySpecFromPlugin dependencies)}") ++ (optional (init != null) "init = function(plugin)\n${init}\nend") ++ (optional (conf != null) "config = function(plugin, opts)\n${conf}\nend") ++ (optional (keys != [ ]) "keys = ${luaList (map keybinding keys)}") ++ (optional (event != [ ]) "event = ${toLua event}") ++ (optional (cmd != [ ]) "cmd = ${toLua cmd}") ++ (optional (ft != [ ]) "ft = ${toLua ft}") ++ (optional (priority != null) "priority = ${toLua priority}") ); lazySpecs = luaList (map lazySpecFromPlugin cfg.plugins); lazy = /* lua */ '' require("lazy").setup(${lazySpecs}) ''; initLua = let text = lib.concatLines [ (builtins.readFile ./options.lua) lazy ]; in pkgs.runCommand "init.lua" { inherit text; } '' touch $out echo -n "$text" > $out ${getExe pkgs.stylua} $out ''; in { imports = lib.my.listModulesRec ./plugins; options.my.programs.nvim = { enable = mkEnableOption "nvim"; plugins = mkOption { default = [ ]; description = '' List of plugins with config. ''; type = with types; listOf ( let sub = submodule { options = { conf = mkOption { type = nullOr str; default = null; description = '' Lua function to be executed when the plugin is loaded. ''; }; opts = mkOption { type = let valueType = nullOr (oneOf [ str bool int float (listOf valueType) (attrsOf valueType) ]) // { description = "Lua value"; }; in nullOr (attrsOf valueType); default = null; description = '' Lua table to be passed to te plugin config function. ''; }; dependencies = mkOption { type = listOf sub; default = [ ]; description = '' List of plugins this plugin depends on. ''; }; init = mkOption { type = nullOr str; default = null; description = '' Lua code to be executed when the plugin is initialized. ''; }; event = mkOption { type = listOf str; default = [ ]; description = '' Event to load the plugin on. ''; }; lazy = mkOption { type = nullOr bool; default = null; description = '' Whether to load the plugin lazily. ''; }; plugin = mkOption { type = package; description = '' The plugin package. ''; }; enabled = mkOption { type = bool; default = true; description = '' Whether to enable the plugin. ''; }; cmd = mkOption { type = listOf str; default = [ ]; description = '' Command to load the plugin. ''; }; ft = mkOption { type = listOf str; default = [ ]; description = '' Filetype to load the plugin on. ''; }; priority = mkOption { type = nullOr int; default = null; description = '' Priority to load the plugin. ''; }; keys = mkOption { default = [ ]; description = '' List of keybindings. ''; type = listOf (submodule { options = { key = mkOption { type = str; description = '' Key to bind. ''; }; cmd = mkOption { type = nullOr str; default = null; description = '' Command to execute. ''; }; func = mkOption { type = nullOr str; default = null; description = '' Function to execute. ''; }; mode = mkOption { type = listOf str; default = [ "n" ]; description = '' Mode to bind the key in. ''; }; desc = mkOption { type = nullOr str; default = null; description = '' Description of the keybinding. ''; }; }; }); }; }; }; in sub ); }; }; config = mkIf cfg.enable { home-manager.users.moritz = { home.packages = with pkgs; [ ( if config.my.programs.hyprland.enable then neovide-hyprland else neovide ) ]; xdg.configFile."nvim/init.lua".source = initLua; programs.neovim = { enable = true; package = pkgs.neovim-nightly; vimAlias = true; vimdiffAlias = true; withNodeJs = true; withPython3 = true; extraPython3Packages = ps: let plugins = map (getAttr "plugin") cfg.plugins; depAttrName = "python3Dependencies"; filtered = filter (hasAttr depAttrName) plugins; funcs = map (getAttr depAttrName) filtered; in foldl (list: f: list ++ (f ps)) [ ] funcs; extraPackages = with pkgs; [ alejandra black deadnix isort jq nil nixpkgs-fmt nodePackages.bash-language-server rustfmt shellcheck shfmt statix stylua sumneko-lua-language-server taplo typst typst-lsp yamlfmt ]; plugins = [ pkgs.vimPlugins.lazy-nvim ]; }; }; }; }