diff --git a/pkgs/applications/editors/neovim/tests/default.nix b/pkgs/applications/editors/neovim/tests/default.nix index 14d682e14e431..a5443ab4896cc 100644 --- a/pkgs/applications/editors/neovim/tests/default.nix +++ b/pkgs/applications/editors/neovim/tests/default.nix @@ -101,8 +101,18 @@ in ### neovim tests ################## nvim_with_plugins = wrapNeovim2 "-with-plugins" nvimConfNix; + nvim_singlelines = wrapNeovim2 "-single-lines" nvimConfSingleLines; + + # test that passthru.initRc hasn't changed + passthruInitRc = runTest nvim_singlelines '' + INITRC=${pkgs.writeTextFile { name = "initrc"; text = nvim_singlelines.passthru.initRc; }} + assertFileContent \ + $INITRC \ + "${./init-single-lines.vim}" + ''; - singlelinesconfig = runTest (wrapNeovim2 "-single-lines" nvimConfSingleLines) '' + # test single line concatenation + singlelinesconfig = runTest nvim_singlelines '' assertFileContains \ "$luarcGeneric" \ "vim.cmd.source \"/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-init.vim" @@ -127,6 +137,7 @@ in viAlias = true; }; + # test it still works with vim-plug nvim_with_plug = neovim.override { extraName = "-with-plug"; configure.packages.plugins = with pkgs.vimPlugins; { diff --git a/pkgs/applications/editors/neovim/utils.nix b/pkgs/applications/editors/neovim/utils.nix index 9f0aa86de48ca..5f63f2e60df8a 100644 --- a/pkgs/applications/editors/neovim/utils.nix +++ b/pkgs/applications/editors/neovim/utils.nix @@ -13,46 +13,11 @@ let inherit (vimUtils) toVimPlugin; - /* 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 - The caller is responsible for writing the init.vim and adding it to the wrapped - arguments (["-u" writeText "init.vim" GENERATEDRC)]). - This makes it possible to write the config anywhere: on a per-project basis - .nvimrc or in $XDG_CONFIG_HOME/nvim/init.vim to avoid sideeffects. - 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 + /* transform all plugins into an attrset + { optional = bool; plugin = package; } + */ + normalizePlugins = plugins: + let defaultPlugin = { plugin = null; config = null; @@ -61,65 +26,40 @@ let 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; + /* 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; }; - 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) [ + /* 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 + The caller is responsible for writing the init.vim and adding it to the wrapped + arguments (["-u" writeText "init.vim" GENERATEDRC)]). + This makes it possible to write the config anywhere: on a per-project basis + .nvimrc or in $XDG_CONFIG_HOME/nvim/init.vim to avoid sideeffects. + Indeed, note that wrapping with `-u init.vim` has sideeffects like .nvimrc wont be loaded + anymore, $MYVIMRC wont be set etc + */ + 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 8bcd1b5ebbd66..164146814262d 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 { };