neovim: make the wrapper more evolvable

Now that we have structured attributes enabled, it's easier than ever to
access the wrapper config from itself. Let's expose the 'plugins'
derivations instead of the final 'packpathDir' with which one can't do much.

TLDR: the neovim wrapper accepts a list of neovim derivations in
`plugins` instead of the symlinkJoin of plugins in `packpathDir`.

With exposed plugins, one could tweak the current wrapper with more
plugins, e.g. neovim.withPlugins([fugitive]).withPlugins([plenary]) .
we could also add a boolean to autoadd the plugins passthru.initLua,
better handle the dependencies (runtime programs, python deps).
This commit is contained in:
Matthieu C. 2024-09-08 17:27:26 +02:00 committed by Gaétan Lepage
parent 11cf80ae32
commit 12dafac23c
2 changed files with 98 additions and 104 deletions

View File

@ -13,6 +13,30 @@
let let
inherit (vimUtils) toVimPlugin; 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: /* returns everything needed for the caller to wrap its own neovim:
- the generated content of the future init.vim - the generated content of the future init.vim
- the arguments to wrap neovim with - the arguments to wrap neovim with
@ -23,102 +47,18 @@ let
Indeed, note that wrapping with `-u init.vim` has sideeffects like .nvimrc wont be loaded Indeed, note that wrapping with `-u init.vim` has sideeffects like .nvimrc wont be loaded
anymore, $MYVIMRC wont be set etc anymore, $MYVIMRC wont be set etc
*/ */
makeNeovimConfig = makeNeovimConfig = {
{ withPython3 ? true customRC ? ""
/* the function you would have passed to python3.withPackages */
, extraPython3Packages ? (_: [ ])
, withNodeJs ? false
, withRuby ? true
/* the function you would have passed to lua.withPackages */ /* the function you would have passed to lua.withPackages */
, extraLuaPackages ? (_: [ ]) , extraLuaPackages ? (_: [ ])
, ...}@attrs: let
# 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; luaEnv = neovim-unwrapped.lua.withPackages extraLuaPackages;
in attrs // {
# as expected by packdir neovimRcContent = customRC;
packpathDirs.myNeovimPackages = myVimPackage; wrapperArgs = lib.optionals (luaEnv != null) [
## 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) [
"--prefix" "LUA_PATH" ";" (neovim-unwrapped.lua.pkgs.luaLib.genLuaPathAbsStr luaEnv) "--prefix" "LUA_PATH" ";" (neovim-unwrapped.lua.pkgs.luaLib.genLuaPathAbsStr luaEnv)
"--prefix" "LUA_CPATH" ";" (neovim-unwrapped.lua.pkgs.luaLib.genLuaCPathAbsStr 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;
}; };
@ -198,6 +138,9 @@ let
in in
lib.concatStringsSep ";" hostProviderLua; 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 { buildNeovimPlugin = callPackage ./build-neovim-plugin.nix {
inherit (vimUtils) toVimPlugin; inherit (vimUtils) toVimPlugin;
inherit lua; inherit lua;
@ -275,6 +218,7 @@ in
inherit legacyWrapper; inherit legacyWrapper;
inherit grammarToPlugin; inherit grammarToPlugin;
inherit packDir; inherit packDir;
inherit normalizePlugins normalizedPluginsToVimPackage;
inherit buildNeovimPlugin; inherit buildNeovimPlugin;
buildNeovimPluginFrom2Nix = lib.warn "buildNeovimPluginFrom2Nix was renamed to buildNeovimPlugin" buildNeovimPlugin; buildNeovimPluginFrom2Nix = lib.warn "buildNeovimPluginFrom2Nix was renamed to buildNeovimPlugin" buildNeovimPlugin;

View File

@ -1,4 +1,7 @@
{ stdenv, symlinkJoin, lib, makeWrapper { stdenv, symlinkJoin, lib, makeWrapper
, bundlerEnv
, ruby
, nodejs
, writeText , writeText
, nodePackages , nodePackages
, python3 , python3
@ -6,6 +9,7 @@
, neovimUtils , neovimUtils
, perl , perl
, lndir , lndir
, vimUtils
}: }:
neovim-unwrapped: neovim-unwrapped:
@ -18,10 +22,11 @@ let
extraName ? "" extraName ? ""
# should contain all args but the binary. Can be either a string or list # should contain all args but the binary. Can be either a string or list
, wrapperArgs ? [] , wrapperArgs ? []
# a limited RC script used only to generate the manifest for remote plugins
, manifestRc ? null
, withPython2 ? false , withPython2 ? false
, withPython3 ? true, python3Env ? python3 , withPython3 ? true
/* the function you would have passed to python3.withPackages */
, extraPython3Packages ? (_: [ ])
, withNodeJs ? false , withNodeJs ? false
, withPerl ? false , withPerl ? false
, rubyEnv ? null , rubyEnv ? null
@ -39,23 +44,67 @@ let
, neovimRcContent ? null , neovimRcContent ? null
# lua code to put into the generated init.lua file # lua code to put into the generated init.lua file
, luaRcContent ? "" , luaRcContent ? ""
# entry to load in packpath # DEPRECATED: entry to load in packpath
, packpathDirs # 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 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: stdenv.mkDerivation (finalAttrs:
let 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; finalPackdir = neovimUtils.packDir packpathDirs;
rcContent = '' rcContent = ''
${luaRcContent} ${luaRcContent}
'' + lib.optionalString (!isNull neovimRcContent) '' '' + lib.optionalString (neovimRcContent' != null) ''
vim.cmd.source "${writeText "init.vim" neovimRcContent}" 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; wrapperArgsStr = if lib.isString wrapperArgs then wrapperArgs else lib.escapeShellArgs wrapperArgs;
generatedWrapperArgs = generatedWrapperArgs =
@ -94,6 +143,7 @@ let
in { in {
name = "${pname}-${version}${extraName}"; name = "${pname}-${version}${extraName}";
inherit pname version; inherit pname version;
inherit plugins;
__structuredAttrs = true; __structuredAttrs = true;
dontUnpack = true; dontUnpack = true;
@ -193,7 +243,7 @@ let
passthru = { passthru = {
inherit providerLuaRc packpathDirs; inherit providerLuaRc packpathDirs;
unwrapped = neovim-unwrapped; unwrapped = neovim-unwrapped;
initRc = neovimRcContent; initRc = neovimRcContent';
tests = callPackage ./tests { tests = callPackage ./tests {
}; };