Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# tests available at pkgs/test/vim 2{ lib, stdenv, vim, vimPlugins, buildEnv, writeText 3, runCommand, makeWrapper 4, python3 5, callPackage, makeSetupHook 6, linkFarm 7}: 8 9/* 10 11USAGE EXAMPLE 12============= 13 14Install Vim like this eg using nixos option environment.systemPackages which will provide 15vim-with-plugins in PATH: 16 17 vim-full.customize { 18 name = "vim-with-plugins"; # optional 19 20 # add custom .vimrc lines like this: 21 vimrcConfig.customRC = '' 22 set hidden 23 ''; 24 25 # store your plugins in Vim packages 26 vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; { 27 # loaded on launch 28 start = [ youcompleteme fugitive ]; 29 # manually loadable by calling `:packadd $plugin-name` 30 opt = [ phpCompletion elm-vim ]; 31 # To automatically load a plugin when opening a filetype, add vimrc lines like: 32 # autocmd FileType php :packadd phpCompletion 33 }; 34 }; 35 36WHAT IS A VIM PLUGIN? 37===================== 38Typical plugin files: 39 40 plugin/P1.vim 41 autoload/P1.vim 42 ftplugin/xyz.vim 43 doc/plugin-documentation.txt (traditional documentation) 44 README(.md) (nowadays thanks to github) 45 46 47Vim offers the :h rtp setting which works for most plugins. Thus adding 48this to your .vimrc should make most plugins work: 49 50 set rtp+=~/.nix-profile/share/vim-plugins/youcompleteme 51 " or for p in ["youcompleteme"] | exec 'set rtp+=~/.nix-profile/share/vim-plugins/'.p | endfor 52 53Learn about about plugin Vim plugin mm managers at 54http://vim-wiki.mawercer.de/wiki/topic/vim%20plugin%20managment.html. 55 56The documentation can be accessed by Vim's :help command if it was tagged. 57See vimHelpTags sample code below. 58 59CONTRIBUTING AND CUSTOMIZING 60============================ 61The example file pkgs/applications/editors/vim/plugins/default.nix provides 62both: 63* manually mantained plugins 64* plugins created by VAM's nix#ExportPluginsForNix implementation 65 66I highly recommend to lookup vim plugin attribute names at the [vim-pi] project 67 which is a database containing all plugins from 68vim.org and quite a lot of found at github and similar sources. vim-pi's documented purpose 69is to associate vim.org script ids to human readable names so that dependencies 70can be describe easily. 71 72How to find a name? 73 * http://vam.mawercer.de/ or VAM's 74 * grep vim-pi 75 * use VAM's completion or :AddonsInfo command 76 77It might happen than a plugin is not known by vim-pi yet. We encourage you to 78contribute to vim-pi so that plugins can be updated automatically. 79 80 81CREATING DERIVATIONS AUTOMATICALLY BY PLUGIN NAME 82================================================== 83Most convenient is to use a ~/.vim-scripts file putting a plugin name into each line 84as documented by [VAM]'s README.md 85It is the same format you pass to vimrcConfig.vam.pluginDictionaries from the 86usage example above. 87 88Then create a temp vim file and insert: 89 90 let opts = {} 91 let opts.path_to_nixpkgs = '/etc/nixos/nixpkgs' 92 let opts.cache_file = '/tmp/export-vim-plugin-for-nix-cache-file' 93 let opts.plugin_dictionaries = map(readfile("vim-plugins"), 'eval(v:val)') 94 " add more files 95 " let opts.plugin_dictionaries += map(.. other file ) 96 call nix#ExportPluginsForNix(opts) 97 98Then ":source %" it. 99 100nix#ExportPluginsForNix is provided by ./vim2nix 101 102A buffer will open containing the plugin derivation lines as well list 103fitting the vimrcConfig.vam.pluginDictionaries option. 104 105Thus the most simple usage would be: 106 107 vim_with_plugins = 108 let vim = vim-full; 109 inherit (vimUtil.override {inherit vim}) rtpPath addRtp buildVimPlugin vimHelpTags; 110 vimPlugins = [ 111 # the derivation list from the buffer created by nix#ExportPluginsForNix 112 # don't set which will default to pkgs.vimPlugins 113 ]; 114 in vim.customize { 115 name = "vim-with-plugins"; 116 117 vimrcConfig.customRC = '' .. ''; 118 119 vimrcConfig.vam.knownPlugins = vimPlugins; 120 vimrcConfig.vam.pluginDictionaries = [ 121 # the plugin list form ~/.vim-scripts turned into nix format added to 122 # the buffer created by the nix#ExportPluginsForNix 123 ]; 124 } 125 126vim_with_plugins can be installed like any other application within Nix. 127 128[VAM] https://github.com/MarcWeber/vim-addon-manager 129[vim-pi] https://bitbucket.org/vimcommunity/vim-pi 130*/ 131 132 133let 134 inherit lib; 135 136 # make sure a plugin is a derivation and its dependencies are derivations. If 137 # plugin already is a derivation, this is a no-op. If it is a string, it is 138 # looked up in knownPlugins. 139 pluginToDrv = knownPlugins: plugin: 140 let 141 drv = 142 if builtins.isString plugin then 143 # make sure `pname` is set to that we are able to convert the derivation 144 # back to a string. 145 ( knownPlugins.${plugin} // { pname = plugin; }) 146 else 147 plugin; 148 in 149 # make sure all the dependencies of the plugin are also derivations 150 drv // { dependencies = map (pluginToDrv knownPlugins) (drv.dependencies or []); }; 151 152 # transitive closure of plugin dependencies (plugin needs to be a derivation) 153 transitiveClosure = plugin: 154 [ plugin ] ++ ( 155 lib.unique (builtins.concatLists (map transitiveClosure plugin.dependencies or [])) 156 ); 157 158 findDependenciesRecursively = plugins: lib.concatMap transitiveClosure plugins; 159 160 vamDictToNames = x: 161 if builtins.isString x then [x] 162 else (lib.optional (x ? name) x.name) 163 ++ (x.names or []); 164 165 rtpPath = "."; 166 167 vimFarm = prefix: name: drvs: 168 let mkEntryFromDrv = drv: { name = "${prefix}/${lib.getName drv}"; path = drv; }; 169 in linkFarm name (map mkEntryFromDrv drvs); 170 171 /* Generates a packpath folder as expected by vim 172 Example: 173 packDir (myVimPackage.{ start = [ vimPlugins.vim-fugitive ]; opt = [] }) 174 => "/nix/store/xxxxx-pack-dir" 175 */ 176 packDir = packages: 177 let 178 packageLinks = packageName: {start ? [], opt ? []}: 179 let 180 # `nativeImpl` expects packages to be derivations, not strings (as 181 # opposed to older implementations that have to maintain backwards 182 # compatibility). Therefore we don't need to deal with "knownPlugins" 183 # and can simply pass `null`. 184 depsOfOptionalPlugins = lib.subtractLists opt (findDependenciesRecursively opt); 185 startWithDeps = findDependenciesRecursively start; 186 allPlugins = lib.unique (startWithDeps ++ depsOfOptionalPlugins); 187 allPython3Dependencies = ps: 188 lib.flatten (builtins.map (plugin: (plugin.python3Dependencies or (_: [])) ps) allPlugins); 189 python3Env = python3.withPackages allPython3Dependencies; 190 191 packdirStart = vimFarm "pack/${packageName}/start" "packdir-start" allPlugins; 192 packdirOpt = vimFarm "pack/${packageName}/opt" "packdir-opt" opt; 193 # Assemble all python3 dependencies into a single `site-packages` to avoid doing recursive dependency collection 194 # for each plugin. 195 # This directory is only for python import search path, and will not slow down the startup time. 196 # see :help python3-directory for more details 197 python3link = runCommand "vim-python3-deps" {} '' 198 mkdir -p $out/pack/${packageName}/start/__python3_dependencies 199 ln -s ${python3Env}/${python3Env.sitePackages} $out/pack/${packageName}/start/__python3_dependencies/python3 200 ''; 201 in 202 [ packdirStart packdirOpt ] ++ lib.optional (allPython3Dependencies python3.pkgs != []) python3link; 203 in 204 buildEnv { 205 name = "vim-pack-dir"; 206 paths = (lib.flatten (lib.mapAttrsToList packageLinks packages)); 207 }; 208 209 nativeImpl = packages: 210 '' 211 set packpath^=${packDir packages} 212 set runtimepath^=${packDir packages} 213 ''; 214 215 /* Generates a vimrc string 216 217 packages is an attrset with {name: { start = [ vim derivations ]; opt = [ vim derivations ]; } 218 Example: 219 vimrcContent { 220 221 packages = { home-manager = { start = [vimPlugins.vim-fugitive]; opt = [];}; 222 beforePlugins = ''; 223 customRC = ''let mapleader = " "''; 224 225 }; 226 */ 227 vimrcContent = { 228 packages ? null, 229 vam ? null, # deprecated 230 pathogen ? null, # deprecated 231 plug ? null, 232 beforePlugins ? '' 233 " configuration generated by NIX 234 set nocompatible 235 '', 236 customRC ? null 237 }: 238 239 let 240 /* vim-plug is an extremely popular vim plugin manager. 241 */ 242 plugImpl = 243 '' 244 source ${vimPlugins.vim-plug}/plug.vim 245 silent! call plug#begin('/dev/null') 246 247 '' + (lib.concatMapStringsSep "\n" (pkg: "Plug '${pkg}'") plug.plugins) + '' 248 249 call plug#end() 250 ''; 251 252 # vim-addon-manager = VAM (deprecated) 253 vamImpl = 254 let 255 knownPlugins = vam.knownPlugins or vimPlugins; 256 257 # plugins specified by the user 258 specifiedPlugins = map (pluginToDrv knownPlugins) (lib.concatMap vamDictToNames vam.pluginDictionaries); 259 # plugins with dependencies 260 plugins = findDependenciesRecursively specifiedPlugins; 261 vamPackages.vam = { 262 start = plugins; 263 }; 264 in 265 nativeImpl vamPackages; 266 267 entries = [ 268 beforePlugins 269 ] 270 ++ lib.optional (vam != null) (lib.warn "'vam' attribute is deprecated. Use 'packages' instead in your vim configuration" vamImpl) 271 ++ lib.optional (packages != null && packages != []) (nativeImpl packages) 272 ++ lib.optional (pathogen != null) (throw "pathogen is now unsupported, replace `pathogen = {}` with `packages.home = { start = []; }`") 273 ++ lib.optional (plug != null) plugImpl 274 ++ [ customRC ]; 275 276 in 277 lib.concatStringsSep "\n" (lib.filter (x: x != null && x != "") entries); 278 279 vimrcFile = settings: writeText "vimrc" (vimrcContent settings); 280 281in 282 283rec { 284 inherit vimrcFile; 285 inherit vimrcContent; 286 inherit packDir; 287 288 makeCustomizable = let 289 mkVimrcFile = vimrcFile; # avoid conflict with argument name 290 in vim: vim // { 291 # Returns a customized vim that uses the specified vimrc configuration. 292 customize = 293 { # The name of the derivation. 294 name ? "vim" 295 , # A shell word used to specify the names of the customized executables. 296 # The shell variable $exe can be used to refer to the wrapped executable's name. 297 # Examples: "my-$exe", "$exe-with-plugins", "\${exe/vim/v1m}" 298 executableName ? 299 if lib.hasInfix "vim" name then 300 lib.replaceStrings [ "vim" ] [ "$exe" ] name 301 else 302 "\${exe/vim/${lib.escapeShellArg name}}" 303 , # A custom vimrc configuration, treated as an argument to vimrcContent (see the documentation in this file). 304 vimrcConfig ? null 305 , # A custom vimrc file. 306 vimrcFile ? null 307 , # A custom gvimrc file. 308 gvimrcFile ? null 309 , # If set to true, return the *vim wrappers only. 310 # If set to false, overlay the wrappers on top of the original vim derivation. 311 # This ensures that things like man pages and .desktop files are available. 312 standalone ? name != "vim" && wrapManual != true 313 314 , # deprecated arguments (TODO: remove eventually) 315 wrapManual ? null, wrapGui ? null, vimExecutableName ? null, gvimExecutableName ? null, 316 }: 317 lib.warnIf (wrapManual != null) '' 318 vim.customize: wrapManual is deprecated: the manual is now included by default if `name == "vim"`. 319 ${if wrapManual == true && name != "vim" then "Set `standalone = false` to include the manual." 320 else lib.optionalString (wrapManual == false && name == "vim") "Set `standalone = true` to get the *vim wrappers only." 321 }'' 322 lib.warnIf (wrapGui != null) 323 "vim.customize: wrapGui is deprecated: gvim is now automatically included if present" 324 lib.throwIfNot (vimExecutableName == null && gvimExecutableName == null) 325 "vim.customize: (g)vimExecutableName is deprecated: use executableName instead (see source code for examples)" 326 (let 327 vimrc = 328 if vimrcFile != null then vimrcFile 329 else if vimrcConfig != null then mkVimrcFile vimrcConfig 330 else throw "at least one of vimrcConfig and vimrcFile must be specified"; 331 bin = runCommand "${name}-bin" { nativeBuildInputs = [ makeWrapper ]; } '' 332 vimrc=${lib.escapeShellArg vimrc} 333 gvimrc=${lib.optionalString (gvimrcFile != null) (lib.escapeShellArg gvimrcFile)} 334 335 mkdir -p "$out/bin" 336 for exe in ${ 337 if standalone then "{,g,r,rg,e}vim {,g}vimdiff vi" 338 else "{,g,r,rg,e}{vim,view} {,g}vimdiff ex vi" 339 }; do 340 if [[ -e ${vim}/bin/$exe ]]; then 341 dest="$out/bin/${executableName}" 342 if [[ -e $dest ]]; then 343 echo "ambiguous executableName: ''${dest##*/} already exists" 344 continue 345 fi 346 makeWrapper ${vim}/bin/"$exe" "$dest" \ 347 --add-flags "-u ''${vimrc@Q} ''${gvimrc:+-U ''${gvimrc@Q}}" 348 fi 349 done 350 ''; 351 in if standalone then bin else 352 buildEnv { 353 inherit name; 354 paths = [ (lib.lowPrio vim) bin ]; 355 }); 356 357 override = f: makeCustomizable (vim.override f); 358 overrideAttrs = f: makeCustomizable (vim.overrideAttrs f); 359 }; 360 361 vimWithRC = throw "vimWithRC was removed, please use vim.customize instead"; 362 363 vimGenDocHook = callPackage ({ vim }: 364 makeSetupHook { 365 name = "vim-gen-doc-hook"; 366 propagatedBuildInputs = [ vim ]; 367 substitutions = { 368 vimBinary = "${vim}/bin/vim"; 369 inherit rtpPath; 370 }; 371 } ./vim-gen-doc-hook.sh) {}; 372 373 vimCommandCheckHook = callPackage ({ neovim-unwrapped }: 374 makeSetupHook { 375 name = "vim-command-check-hook"; 376 propagatedBuildInputs = [ neovim-unwrapped ]; 377 substitutions = { 378 vimBinary = "${neovim-unwrapped}/bin/nvim"; 379 inherit rtpPath; 380 }; 381 } ./vim-command-check-hook.sh) {}; 382 383 neovimRequireCheckHook = callPackage ({ neovim-unwrapped }: 384 makeSetupHook { 385 name = "neovim-require-check-hook"; 386 propagatedBuildInputs = [ neovim-unwrapped ]; 387 substitutions = { 388 nvimBinary = "${neovim-unwrapped}/bin/nvim"; 389 inherit rtpPath; 390 }; 391 } ./neovim-require-check-hook.sh) {}; 392 393 inherit (import ./build-vim-plugin.nix { 394 inherit lib stdenv rtpPath toVimPlugin; 395 }) buildVimPlugin buildVimPluginFrom2Nix; 396 397 398 # used to figure out which python dependencies etc. neovim needs 399 requiredPlugins = { 400 packages ? {}, 401 plug ? null, ... 402 }: 403 let 404 nativePluginsConfigs = lib.attrsets.attrValues packages; 405 nonNativePlugins = (lib.optionals (plug != null) plug.plugins); 406 nativePlugins = lib.concatMap (requiredPluginsForPackage) nativePluginsConfigs; 407 in 408 nativePlugins ++ nonNativePlugins; 409 410 411 # figures out which python dependencies etc. is needed for one vim package 412 requiredPluginsForPackage = { start ? [], opt ? []}: 413 start ++ opt; 414 415 toVimPlugin = drv: 416 drv.overrideAttrs(oldAttrs: { 417 # dont move the "doc" folder since vim expects it 418 forceShare = [ "man" "info" ]; 419 420 nativeBuildInputs = oldAttrs.nativeBuildInputs or [] 421 ++ lib.optionals (stdenv.hostPlatform == stdenv.buildPlatform) [ 422 vimCommandCheckHook vimGenDocHook 423 # many neovim plugins keep using buildVimPlugin 424 neovimRequireCheckHook 425 ]; 426 427 passthru = (oldAttrs.passthru or {}) // { 428 vimPlugin = true; 429 }; 430 }); 431}