{ lib, config, pkgs, ... }: let cfg = config.programs._neovim; pluginDir = let extraPlugins = cfg.plugins.packages // { start = (cfg.plugins.packages.start or []) ++ lib.optionals (cfg.plugins.lsp.enable && cfg.plugins.lsp.lspconfig.enable) [ cfg.plugins.lsp.lspconfig.pluginPackage ] ++ lib.optionals cfg.plugins.treesitter.enable [ cfg.plugins.treesitter.pluginPackage ]; }; in pkgs.linkFarm "nvim-site" (lib.concatLists (lib.mapAttrsToList (stage: plugins: map (plugin: { name = "share/nvim/site/pack/plugins/${stage}/${plugin.name}"; path = plugin; }) plugins ) extraPlugins)); extraPackages = pkgs.symlinkJoin { name = "nvim-extra-packages"; paths = cfg.extraPackages ++ lib.optionals cfg.plugins.lsp.enable cfg.plugins.lsp.languageServers.packages; }; parserDir = let allTSParserPackages = builtins.filter lib.isDerivation (builtins.attrValues cfg.plugins.treesitter.parsers.installAllFromPackageSet); in pkgs.symlinkJoin { name = "nvim-treesitter-parsers"; paths = if cfg.plugins.treesitter.parsers.installAll then allTSParserPackages else cfg.plugins.treesitter.parsers.packages; }; finalPackage = pkgs.symlinkJoin { name = "neovim-not-wrapped"; paths = [ cfg.package ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/nvim \ --prefix PATH : ${extraPackages} \ --prefix XDG_DATA_DIRS : ${pluginDir}/share \ '' + lib.optionalString cfg.plugins.treesitter.enable '' --add-flags '--cmd "set rtp^=${parserDir}"' \ --add-flags '--cmd "set rtp^=${cfg.plugins.treesitter.pluginPackage}/runtime"' \ '' + '' # blank line here to terminate multiline command string '' + lib.optionalString cfg.viAlias '' ln -s $out/bin/nvim $out/bin/vi '' + lib.optionalString cfg.vimAlias '' ln -s $out/bin/nvim $out/bin/vim ''; }; in { options.programs._neovim = { enable = lib.mkEnableOption "install and configure neovim"; package = lib.mkPackageOption pkgs "neovim" {}; viAlias = lib.mkEnableOption "alias vi => nvim"; vimAlias = lib.mkEnableOption "alias vim => nvim"; plugins = { lsp = { enable = lib.mkEnableOption "install defined language servers into nvim path"; lspconfig = { enable = lib.mkEnableOption "install plugin `lspconfig.pluginPackage`"; pluginPackage = lib.mkPackageOption pkgs.vimPlugins "nvim-lspconfig" {}; }; languageServers.packages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; description = "language server packages to add to nvim path when lsp is enabled"; }; }; treesitter = { enable = lib.mkEnableOption "install treesitter and parsers as nvim plugins"; pluginPackage = lib.mkPackageOption pkgs.vimPlugins "nvim-treesitter" {}; parsers.installAll = lib.mkEnableOption "install all available parsers. overrides `parsers.packages`"; parsers.installAllFromPackageSet = lib.mkOption { type = lib.types.attrsOf lib.types.anything; default = pkgs.vimPlugins.nvim-treesitter-parsers; defaultText = lib.literalExpression "pkgs.vimPlugins.nvim-treesitter-parsers"; description = "parent package from which to install all treesitter parsers"; }; parsers.packages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; description = "treesitter parsers to install as plugins"; }; }; packages = lib.mkOption { type = lib.types.attrsOf (lib.types.listOf lib.types.package); default = [ ]; example = { start = with pkgs.vimPlugins; [ nvim-colorizer-lua ]; }; description = "extra plugins to be made available to nvim via packpath, organized by stage"; }; }; extraPackages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; description = "extra packages to add to nvim path"; }; }; config = lib.mkIf cfg.enable { # NOTE: Don't use programs.neovim since that will build neovim from source home.packages = [ finalPackage ]; }; }