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