diff --git a/pkgs/applications/editors/neovim/utils.nix b/pkgs/applications/editors/neovim/utils.nix index 9f0aa86de48c..5f63f2e60df8 100644 --- a/pkgs/applications/editors/neovim/utils.nix +++ b/pkgs/applications/editors/neovim/utils.nix @@ -13,6 +13,30 @@ let inherit (vimUtils) toVimPlugin; + /* transform all plugins into an attrset + { optional = bool; plugin = package; } + */ + normalizePlugins = plugins: + let + defaultPlugin = { + plugin = null; + config = null; + optional = false; + }; + in + map (x: defaultPlugin // (if (x ? plugin) then x else { plugin = x; })) plugins; + + + /* accepts a list of normalized plugins and convert themn + */ + normalizedPluginsToVimPackage = normalizedPlugins: + let + pluginsPartitioned = lib.partition (x: x.optional == true) normalizedPlugins; + in { + start = map (x: x.plugin) pluginsPartitioned.wrong; + opt = map (x: x.plugin) pluginsPartitioned.right; + }; + /* returns everything needed for the caller to wrap its own neovim: - the generated content of the future init.vim - the arguments to wrap neovim with @@ -23,103 +47,19 @@ let Indeed, note that wrapping with `-u init.vim` has sideeffects like .nvimrc wont be loaded anymore, $MYVIMRC wont be set etc */ - makeNeovimConfig = - { withPython3 ? true - /* the function you would have passed to python3.withPackages */ - , extraPython3Packages ? (_: [ ]) - , withNodeJs ? false - , withRuby ? true - /* the function you would have passed to lua.withPackages */ - , extraLuaPackages ? (_: [ ]) - - # expects a list of plugin configuration - # expects { plugin=far-vim; config = "let g:far#source='rg'"; optional = false; } - , plugins ? [] - # custom viml config appended after plugin-specific config - , customRC ? "" - - # for forward compability, when adding new environments, haskell etc. - , ... - }@args: - let - rubyEnv = bundlerEnv { - name = "neovim-ruby-env"; - gemdir = ./ruby_provider; - postBuild = '' - ln -sf ${ruby}/bin/* $out/bin - ''; - }; - - # transform all plugins into an attrset - # { optional = bool; plugin = package; } - pluginsNormalized = let - defaultPlugin = { - plugin = null; - config = null; - optional = false; - }; - in - map (x: defaultPlugin // (if (x ? plugin) then x else { plugin = x; })) plugins; - - pluginRC = lib.foldl (acc: p: if p.config != null then acc ++ [p.config] else acc) [] pluginsNormalized; - - pluginsPartitioned = lib.partition (x: x.optional == true) pluginsNormalized; - requiredPlugins = vimUtils.requiredPluginsForPackage myVimPackage; - getDeps = attrname: map (plugin: plugin.${attrname} or (_: [ ])); - myVimPackage = { - start = map (x: x.plugin) pluginsPartitioned.wrong; - opt = map (x: x.plugin) pluginsPartitioned.right; - }; - - pluginPython3Packages = getDeps "python3Dependencies" requiredPlugins; - python3Env = python3Packages.python.withPackages (ps: - [ ps.pynvim ] - ++ (extraPython3Packages ps) - ++ (lib.concatMap (f: f ps) pluginPython3Packages)); - - luaEnv = neovim-unwrapped.lua.withPackages extraLuaPackages; - - # as expected by packdir - packpathDirs.myNeovimPackages = myVimPackage; - ## Here we calculate all of the arguments to the 1st call of `makeWrapper` - # We start with the executable itself NOTE we call this variable "initial" - # because if configure != {} we need to call makeWrapper twice, in order to - # avoid double wrapping, see comment near finalMakeWrapperArgs - makeWrapperArgs = - let - binPath = lib.makeBinPath (lib.optionals withRuby [ rubyEnv ] ++ lib.optionals withNodeJs [ nodejs ]); - in - [ - "--inherit-argv0" - ] ++ lib.optionals withRuby [ - "--set" "GEM_HOME" "${rubyEnv}/${rubyEnv.ruby.gemPath}" - ] ++ lib.optionals (binPath != "") [ - "--suffix" "PATH" ":" binPath - ] ++ lib.optionals (luaEnv != null) [ + makeNeovimConfig = { + customRC ? "" + /* the function you would have passed to lua.withPackages */ + , extraLuaPackages ? (_: [ ]) + , ...}@attrs: let + luaEnv = neovim-unwrapped.lua.withPackages extraLuaPackages; + in attrs // { + neovimRcContent = customRC; + wrapperArgs = lib.optionals (luaEnv != null) [ "--prefix" "LUA_PATH" ";" (neovim-unwrapped.lua.pkgs.luaLib.genLuaPathAbsStr luaEnv) "--prefix" "LUA_CPATH" ";" (neovim-unwrapped.lua.pkgs.luaLib.genLuaCPathAbsStr luaEnv) - ]; - - manifestRc = vimUtils.vimrcContent { customRC = ""; }; - # we call vimrcContent without 'packages' to avoid the init.vim generation - neovimRcContent = vimUtils.vimrcContent { - beforePlugins = ""; - customRC = lib.concatStringsSep "\n" (pluginRC ++ [customRC]); - packages = null; - }; - in - - builtins.removeAttrs args ["plugins"] // { - wrapperArgs = makeWrapperArgs; - inherit packpathDirs; - inherit neovimRcContent; - inherit manifestRc; - inherit python3Env; - inherit luaEnv; - inherit withNodeJs; - } // lib.optionalAttrs withRuby { - inherit rubyEnv; - }; + ]; + }; # to keep backwards compatibility for people using neovim.override @@ -198,6 +138,9 @@ let in lib.concatStringsSep ";" hostProviderLua; + /* Converts a lua package into a neovim plugin. + Does so by installing the lua package with a flat hierarchy of folders + */ buildNeovimPlugin = callPackage ./build-neovim-plugin.nix { inherit (vimUtils) toVimPlugin; inherit lua; @@ -275,6 +218,7 @@ in inherit legacyWrapper; inherit grammarToPlugin; inherit packDir; + inherit normalizePlugins normalizedPluginsToVimPackage; inherit buildNeovimPlugin; buildNeovimPluginFrom2Nix = lib.warn "buildNeovimPluginFrom2Nix was renamed to buildNeovimPlugin" buildNeovimPlugin; diff --git a/pkgs/applications/editors/neovim/wrapper.nix b/pkgs/applications/editors/neovim/wrapper.nix index 8bcd1b5ebbd6..164146814262 100644 --- a/pkgs/applications/editors/neovim/wrapper.nix +++ b/pkgs/applications/editors/neovim/wrapper.nix @@ -1,4 +1,7 @@ { stdenv, symlinkJoin, lib, makeWrapper +, bundlerEnv +, ruby +, nodejs , writeText , nodePackages , python3 @@ -6,6 +9,7 @@ , neovimUtils , perl , lndir +, vimUtils }: neovim-unwrapped: @@ -18,10 +22,11 @@ let extraName ? "" # should contain all args but the binary. Can be either a string or list , wrapperArgs ? [] - # a limited RC script used only to generate the manifest for remote plugins - , manifestRc ? null , withPython2 ? false - , withPython3 ? true, python3Env ? python3 + , withPython3 ? true + /* the function you would have passed to python3.withPackages */ + , extraPython3Packages ? (_: [ ]) + , withNodeJs ? false , withPerl ? false , rubyEnv ? null @@ -39,23 +44,67 @@ let , neovimRcContent ? null # lua code to put into the generated init.lua file , luaRcContent ? "" - # entry to load in packpath - , packpathDirs + # DEPRECATED: entry to load in packpath + # use 'plugins' instead + , packpathDirs ? null # not used anymore + + # a list of neovim plugin derivations, for instance + # plugins = [ + # { plugin=far-vim; config = "let g:far#source='rg'"; optional = false; } + # ] + , plugins ? [] , ... - }: + }@attrs: assert withPython2 -> throw "Python2 support has been removed from the neovim wrapper, please remove withPython2 and python2Env."; + assert packpathDirs != null -> throw "packpathdirs is not used anymore: pass a list of neovim plugin derivations in 'plugins' instead."; + stdenv.mkDerivation (finalAttrs: let + pluginsNormalized = neovimUtils.normalizePlugins plugins; + + myVimPackage = neovimUtils.normalizedPluginsToVimPackage pluginsNormalized; + + rubyEnv = bundlerEnv { + name = "neovim-ruby-env"; + gemdir = ./ruby_provider; + postBuild = '' + ln -sf ${ruby}/bin/* $out/bin + ''; + }; + + pluginRC = lib.foldl (acc: p: if p.config != null then acc ++ [p.config] else acc) [] pluginsNormalized; + + # a limited RC script used only to generate the manifest for remote plugins + manifestRc = vimUtils.vimrcContent { customRC = ""; }; + # we call vimrcContent without 'packages' to avoid the init.vim generation + neovimRcContent' = vimUtils.vimrcContent { + beforePlugins = ""; + customRC = lib.concatStringsSep "\n" (pluginRC ++ [neovimRcContent]); + packages = null; + }; finalPackdir = neovimUtils.packDir packpathDirs; rcContent = '' ${luaRcContent} - '' + lib.optionalString (!isNull neovimRcContent) '' - vim.cmd.source "${writeText "init.vim" neovimRcContent}" + '' + lib.optionalString (neovimRcContent' != null) '' + vim.cmd.source "${writeText "init.vim" neovimRcContent'}" ''; + getDeps = attrname: map (plugin: plugin.${attrname} or (_: [ ])); + + requiredPlugins = vimUtils.requiredPluginsForPackage myVimPackage; + pluginPython3Packages = getDeps "python3Dependencies" requiredPlugins; + + python3Env = lib.warnIf (attrs ? python3Env) "Pass your python packages via the `extraPython3Packages`, e.g., `extraPython3Packages = ps: [ ps.pandas ]`" + python3.pkgs.python.withPackages (ps: + [ ps.pynvim ] + ++ (extraPython3Packages ps) + ++ (lib.concatMap (f: f ps) pluginPython3Packages)); + + packpathDirs.myNeovimPackages = myVimPackage; + wrapperArgsStr = if lib.isString wrapperArgs then wrapperArgs else lib.escapeShellArgs wrapperArgs; generatedWrapperArgs = @@ -94,6 +143,7 @@ let in { name = "${pname}-${version}${extraName}"; inherit pname version; + inherit plugins; __structuredAttrs = true; dontUnpack = true; @@ -193,7 +243,7 @@ let passthru = { inherit providerLuaRc packpathDirs; unwrapped = neovim-unwrapped; - initRc = neovimRcContent; + initRc = neovimRcContent'; tests = callPackage ./tests { };