anvil-editor: init at 0.4
[NixPkgs.git] / pkgs / applications / editors / neovim / wrapper.nix
blobb51e7f08a0bfaea43127d0b9bd4795b3c83a475d
1 { stdenv, symlinkJoin, lib, makeWrapper
2 , bundlerEnv
3 , ruby
4 , nodejs
5 , writeText
6 , nodePackages
7 , python3
8 , callPackage
9 , neovimUtils
10 , perl
11 , lndir
12 , vimUtils
15 neovim-unwrapped:
17 let
18   # inherit interpreter from neovim
19   lua = neovim-unwrapped.lua;
21   wrapper = {
22       extraName ? ""
23     # should contain all args but the binary. Can be either a string or list
24     , wrapperArgs ? []
25     , withPython2 ? false
26     , withPython3 ? true
27     /* the function you would have passed to python3.withPackages */
28     , extraPython3Packages ? (_: [ ])
30     , withNodeJs ? false
31     , withPerl ? false
32     , withRuby ? true
34     # wether to create symlinks in $out/bin/vi(m) -> $out/bin/nvim
35     , vimAlias ? false
36     , viAlias ? false
38     # additional argument not generated by makeNeovimConfig
39     # it will append "-u <customRc>" to the wrapped arguments
40     # set to false if you want to control where to save the generated config
41     # (e.g., in ~/.config/init.vim or project/.nvimrc)
42     , wrapRc ? true
43     # vimL code that should be sourced as part of the generated init.lua file
44     , neovimRcContent ? null
45     # lua code to put into the generated init.lua file
46     , luaRcContent ? ""
47     # DEPRECATED: entry to load in packpath
48     # use 'plugins' instead
49     , packpathDirs ? null # not used anymore
51     # a list of neovim plugin derivations, for instance
52     #  plugins = [
53     # { plugin=far-vim; config = "let g:far#source='rg'"; optional = false; }
54     # ]
55     , plugins ? []
56     , ...
57   }@attrs:
58   assert withPython2 -> throw "Python2 support has been removed from the neovim wrapper, please remove withPython2 and python2Env.";
60   assert packpathDirs != null -> throw "packpathdirs is not used anymore: pass a list of neovim plugin derivations in 'plugins' instead.";
62   stdenv.mkDerivation (finalAttrs:
63   let
64     pluginsNormalized = neovimUtils.normalizePlugins finalAttrs.plugins;
66     myVimPackage = neovimUtils.normalizedPluginsToVimPackage pluginsNormalized;
68     rubyEnv = bundlerEnv {
69       name = "neovim-ruby-env";
70       gemdir = ./ruby_provider;
71       postBuild = ''
72         ln -sf ${ruby}/bin/* $out/bin
73       '';
74     };
76     pluginRC = lib.foldl (acc: p: if p.config != null then acc ++ [p.config] else acc) []  pluginsNormalized;
78     # a limited RC script used only to generate the manifest for remote plugins
79     manifestRc = vimUtils.vimrcContent { customRC = ""; };
80     # we call vimrcContent without 'packages' to avoid the init.vim generation
81     neovimRcContent' = vimUtils.vimrcContent {
82       beforePlugins = "";
83       customRC = lib.concatStringsSep "\n" (pluginRC ++ lib.optional (neovimRcContent != null) neovimRcContent);
84       packages = null;
85     };
87     packpathDirs.myNeovimPackages = myVimPackage;
88     finalPackdir = neovimUtils.packDir packpathDirs;
90     rcContent = ''
91       ${luaRcContent}
92     '' + lib.optionalString (neovimRcContent' != null) ''
93       vim.cmd.source "${writeText "init.vim" neovimRcContent'}"
94     '';
96     getDeps = attrname: map (plugin: plugin.${attrname} or (_: [ ]));
98     requiredPlugins = vimUtils.requiredPluginsForPackage myVimPackage;
99     pluginPython3Packages = getDeps "python3Dependencies" requiredPlugins;
101     python3Env = lib.warnIf (attrs ? python3Env) "Pass your python packages via the `extraPython3Packages`, e.g., `extraPython3Packages = ps: [ ps.pandas ]`"
102       python3.pkgs.python.withPackages (ps:
103       [ ps.pynvim ]
104       ++ (extraPython3Packages ps)
105       ++ (lib.concatMap (f: f ps) pluginPython3Packages));
108     wrapperArgsStr = if lib.isString wrapperArgs then wrapperArgs else lib.escapeShellArgs wrapperArgs;
110     generatedWrapperArgs = let
111       binPath = lib.makeBinPath (lib.optional finalAttrs.withRuby rubyEnv ++ lib.optional finalAttrs.withNodeJs nodejs);
112     in
114       # vim accepts a limited number of commands so we join them all
115           [
116             "--add-flags" ''--cmd "lua ${providerLuaRc}"''
117           ]
118           ++ lib.optionals (packpathDirs.myNeovimPackages.start != [] || packpathDirs.myNeovimPackages.opt != []) [
119             "--add-flags" ''--cmd "set packpath^=${finalPackdir}"''
120             "--add-flags" ''--cmd "set rtp^=${finalPackdir}"''
121           ]
122           ++ lib.optionals finalAttrs.withRuby [
123             "--set" "GEM_HOME" "${rubyEnv}/${rubyEnv.ruby.gemPath}"
124           ] ++ lib.optionals (binPath != "") [
125             "--suffix" "PATH" ":" binPath
126           ]
127           ;
129     providerLuaRc = neovimUtils.generateProviderRc {
130       inherit (finalAttrs) withPython3 withNodeJs withPerl withRuby;
131     };
133     # If configure != {}, we can't generate the rplugin.vim file with e.g
134     # NVIM_SYSTEM_RPLUGIN_MANIFEST *and* NVIM_RPLUGIN_MANIFEST env vars set in
135     # the wrapper. That's why only when configure != {} (tested both here and
136     # when postBuild is evaluated), we call makeWrapper once to generate a
137     # wrapper with most arguments we need, excluding those that cause problems to
138     # generate rplugin.vim, but still required for the final wrapper.
139     finalMakeWrapperArgs =
140       [ "${neovim-unwrapped}/bin/nvim" "${placeholder "out"}/bin/nvim" ]
141       ++ [ "--set" "NVIM_SYSTEM_RPLUGIN_MANIFEST" "${placeholder "out"}/rplugin.vim" ]
142       ++ lib.optionals finalAttrs.wrapRc [ "--add-flags" "-u ${writeText "init.lua" rcContent}" ]
143       ++ finalAttrs.generatedWrapperArgs
144       ;
146     perlEnv = perl.withPackages (p: [ p.NeovimExt p.Appcpanminus ]);
148     pname = "neovim";
149     version = lib.getVersion neovim-unwrapped;
150   in {
151       name = "${pname}-${version}${extraName}";
152       inherit pname version;
153       inherit plugins;
155       __structuredAttrs = true;
156       dontUnpack = true;
157       inherit viAlias vimAlias withNodeJs withPython3 withPerl withRuby;
158       inherit wrapRc providerLuaRc packpathDirs;
159       inherit python3Env rubyEnv;
160       inherit wrapperArgs generatedWrapperArgs;
161       luaRcContent = rcContent;
162       # Remove the symlinks created by symlinkJoin which we need to perform
163       # extra actions upon
164       postBuild = lib.optionalString stdenv.hostPlatform.isLinux ''
165         rm $out/share/applications/nvim.desktop
166         substitute ${neovim-unwrapped}/share/applications/nvim.desktop $out/share/applications/nvim.desktop \
167           --replace-warn 'Name=Neovim' 'Name=Neovim wrapper'
168       ''
169       + lib.optionalString finalAttrs.withPython3 ''
170         makeWrapper ${python3Env.interpreter} $out/bin/nvim-python3 --unset PYTHONPATH --unset PYTHONSAFEPATH
171       ''
172       + lib.optionalString (finalAttrs.withRuby) ''
173         ln -s ${finalAttrs.rubyEnv}/bin/neovim-ruby-host $out/bin/nvim-ruby
174       ''
175       + lib.optionalString finalAttrs.withNodeJs ''
176         ln -s ${nodePackages.neovim}/bin/neovim-node-host $out/bin/nvim-node
177       ''
178       + lib.optionalString finalAttrs.withPerl ''
179         ln -s ${perlEnv}/bin/perl $out/bin/nvim-perl
180       ''
181       + lib.optionalString finalAttrs.vimAlias ''
182         ln -s $out/bin/nvim $out/bin/vim
183       ''
184       + lib.optionalString finalAttrs.viAlias ''
185         ln -s $out/bin/nvim $out/bin/vi
186       ''
187       + lib.optionalString (manifestRc != null) (let
188         manifestWrapperArgs =
189           [ "${neovim-unwrapped}/bin/nvim" "${placeholder "out"}/bin/nvim-wrapper" ] ++ finalAttrs.generatedWrapperArgs;
190       in ''
191         echo "Generating remote plugin manifest"
192         export NVIM_RPLUGIN_MANIFEST=$out/rplugin.vim
193         makeWrapper ${lib.escapeShellArgs manifestWrapperArgs} ${wrapperArgsStr}
195         # Some plugins assume that the home directory is accessible for
196         # initializing caches, temporary files, etc. Even if the plugin isn't
197         # actively used, it may throw an error as soon as Neovim is launched
198         # (e.g., inside an autoload script), causing manifest generation to
199         # fail. Therefore, let's create a fake home directory before generating
200         # the manifest, just to satisfy the needs of these plugins.
201         #
202         # See https://github.com/Yggdroot/LeaderF/blob/v1.21/autoload/lfMru.vim#L10
203         # for an example of this behavior.
204         export HOME="$(mktemp -d)"
205         # Launch neovim with a vimrc file containing only the generated plugin
206         # code. Pass various flags to disable temp file generation
207         # (swap/viminfo) and redirect errors to stderr.
208         # Only display the log on error since it will contain a few normally
209         # irrelevant messages.
210         if ! $out/bin/nvim-wrapper \
211           -u ${writeText "manifest.vim" manifestRc} \
212           -i NONE -n \
213           -V1rplugins.log \
214           +UpdateRemotePlugins +quit! > outfile 2>&1; then
215           cat outfile
216           echo -e "\nGenerating rplugin.vim failed!"
217           exit 1
218         fi
219         rm "${placeholder "out"}/bin/nvim-wrapper"
220       '')
221       + ''
222         rm $out/bin/nvim
223         touch $out/rplugin.vim
225         echo "Looking for lua dependencies..."
226         source ${lua}/nix-support/utils.sh
228         _addToLuaPath "${finalPackdir}"
230         echo "LUA_PATH towards the end of packdir: $LUA_PATH"
232         makeWrapper ${lib.escapeShellArgs finalMakeWrapperArgs} ${wrapperArgsStr} \
233             --prefix LUA_PATH ';' "$LUA_PATH" \
234             --prefix LUA_CPATH ';' "$LUA_CPATH"
235       '';
237     buildPhase = ''
238       runHook preBuild
239       mkdir -p $out
240       for i in ${neovim-unwrapped}; do
241         lndir -silent $i $out
242       done
243       runHook postBuild
244     '';
246     preferLocalBuild = true;
248     nativeBuildInputs = [ makeWrapper lndir ];
250     # A Vim "package", see ':h packages'
251     vimPackage = myVimPackage;
253     checkPhase = ''
254       runHook preCheck
256       $out/bin/nvim -i NONE -e +quitall!
257       runHook postCheck
258       '';
260     passthru = {
261       inherit providerLuaRc packpathDirs;
262       unwrapped = neovim-unwrapped;
263       initRc = neovimRcContent';
265       tests = callPackage ./tests {
266       };
267     };
269     meta = {
270       inherit (neovim-unwrapped.meta)
271         description
272         longDescription
273         homepage
274         mainProgram
275         license
276         maintainers
277         platforms;
279       # To prevent builds on hydra
280       hydraPlatforms = [];
281       # prefer wrapper over the package
282       priority = (neovim-unwrapped.meta.priority or lib.meta.defaultPriority) - 1;
283     };
284   });
286   lib.makeOverridable wrapper