Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pkgs/applications/editors/neovim/tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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; {
Expand Down
132 changes: 38 additions & 94 deletions pkgs/applications/editors/neovim/utils.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this removed? Could you add a deprecation stub that throws an appropriate error message for any publicly exposed functions/etc that have been removed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my mistake. This is not removed. Is anything else removed without a throw?

{ 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 ? (_: [ ])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the new expected way of handling adding extraLuaPackages to a config? I didn't see this argument moved to the wrapper like extraPython3Packages was.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are absolutely correct. I would like to avoid adding them to the wrapper as extraLuaPackages was a workaround to specify plugin dependencies. Lua dependencies are now loaded correctly in the wrapper via shell hooks. A user could call the wrapper directly adding its own wrapper args to add externmal lua dependencies or convert a lua package with buildNeovimPlugin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you could trigger the nnixvim job again, hopefully it is now fixed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, kicking off another build there


# 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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
68 changes: 59 additions & 9 deletions pkgs/applications/editors/neovim/wrapper.nix
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{ stdenv, symlinkJoin, lib, makeWrapper
, bundlerEnv
, ruby
, nodejs
, writeText
, nodePackages
, python3
, callPackage
, neovimUtils
, perl
, lndir
, vimUtils
}:

neovim-unwrapped:
Expand All @@ -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
Expand All @@ -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;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to also have a runtimeInputs or dependencies argument that prepends to PATH via the wrapper?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally. This will be unblocked by this PR. I prefer it to be done as a follow up though since neovim is a touchy derivation: you quickly know when you break something xD and this has the potential to break configs already (at least for people using the "private/unstable" APIs)


generatedWrapperArgs =
Expand Down Expand Up @@ -94,6 +143,7 @@ let
in {
name = "${pname}-${version}${extraName}";
inherit pname version;
inherit plugins;

__structuredAttrs = true;
dontUnpack = true;
Expand Down Expand Up @@ -193,7 +243,7 @@ let
passthru = {
inherit providerLuaRc packpathDirs;
unwrapped = neovim-unwrapped;
initRc = neovimRcContent;
initRc = neovimRcContent';

tests = callPackage ./tests {
};
Expand Down