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