Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at 21.05 473 lines 17 kB view raw
1# tests available at pkgs/test/vim 2{ lib, stdenv, vim, vimPlugins, vim_configurable, buildEnv, writeText, writeScriptBin 3, nix-prefetch-hg, nix-prefetch-git 4, fetchFromGitHub, runtimeShell 5}: 6 7/* 8 9USAGE EXAMPLE 10============= 11 12Install Vim like this eg using nixos option environment.systemPackages which will provide 13vim-with-plugins in PATH: 14 15 vim_configurable.customize { 16 name = "vim-with-plugins"; 17 18 # add custom .vimrc lines like this: 19 vimrcConfig.customRC = '' 20 set hidden 21 ''; 22 23 # store your plugins in Vim packages 24 vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; { 25 # loaded on launch 26 start = [ youcompleteme fugitive ]; 27 # manually loadable by calling `:packadd $plugin-name` 28 opt = [ phpCompletion elm-vim ]; 29 # To automatically load a plugin when opening a filetype, add vimrc lines like: 30 # autocmd FileType php :packadd phpCompletion 31 }; 32 33 # plugins can also be managed by VAM 34 vimrcConfig.vam.knownPlugins = pkgs.vimPlugins; # optional 35 vimrcConfig.vam.pluginDictionaries = [ 36 # load always 37 { name = "youcompleteme"; } 38 { names = ["youcompleteme" "foo"]; } 39 40 # only load when opening a .php file 41 { name = "phpCompletion"; ft_regex = "^php\$"; } 42 { name = "phpCompletion"; filename_regex = "^.php\$"; } 43 44 # provide plugin which can be loaded manually: 45 { name = "phpCompletion"; tag = "lazy"; } 46 47 # full documentation at github.com/MarcWeber/vim-addon-manager 48 ]; 49 50 # there is a pathogen implementation as well, but its startup is slower and [VAM] has more feature 51 # vimrcConfig.pathogen.knownPlugins = vimPlugins; # optional 52 # vimrcConfig.pathogen.pluginNames = ["vim-addon-nix"]; 53 }; 54 55WHAT IS A VIM PLUGIN? 56===================== 57Typical plugin files: 58 59 plugin/P1.vim 60 autoload/P1.vim 61 ftplugin/xyz.vim 62 doc/plugin-documentation.txt (traditional documentation) 63 README(.md) (nowadays thanks to github) 64 65 66Vim offers the :h rtp setting which works for most plugins. Thus adding 67this to your .vimrc should make most plugins work: 68 69 set rtp+=~/.nix-profile/share/vim-plugins/youcompleteme 70 " or for p in ["youcompleteme"] | exec 'set rtp+=~/.nix-profile/share/vim-plugins/'.p | endfor 71 72which is what the [VAM]/pathogen solutions above basically do. 73 74Learn about about plugin Vim plugin mm managers at 75http://vim-wiki.mawercer.de/wiki/topic/vim%20plugin%20managment.html. 76 77The documentation can be accessed by Vim's :help command if it was tagged. 78See vimHelpTags sample code below. 79 80CONTRIBUTING AND CUSTOMIZING 81============================ 82The example file pkgs/misc/vim-plugins/default.nix provides both: 83* manually mantained plugins 84* plugins created by VAM's nix#ExportPluginsForNix implementation 85 86I highly recommend to lookup vim plugin attribute names at the [vim-pi] project 87 which is a database containing all plugins from 88vim.org and quite a lot of found at github and similar sources. vim-pi's documented purpose 89is to associate vim.org script ids to human readable names so that dependencies 90can be describe easily. 91 92How to find a name? 93 * http://vam.mawercer.de/ or VAM's 94 * grep vim-pi 95 * use VAM's completion or :AddonsInfo command 96 97It might happen than a plugin is not known by vim-pi yet. We encourage you to 98contribute to vim-pi so that plugins can be updated automatically. 99 100 101CREATING DERVITATIONS AUTOMATICALLY BY PLUGIN NAME 102================================================== 103Most convenient is to use a ~/.vim-scripts file putting a plugin name into each line 104as documented by [VAM]'s README.md 105It is the same format you pass to vimrcConfig.vam.pluginDictionaries from the 106usage example above. 107 108Then create a temp vim file and insert: 109 110 let opts = {} 111 let opts.path_to_nixpkgs = '/etc/nixos/nixpkgs' 112 let opts.cache_file = '/tmp/export-vim-plugin-for-nix-cache-file' 113 let opts.plugin_dictionaries = map(readfile("vim-plugins"), 'eval(v:val)') 114 " add more files 115 " let opts.plugin_dictionaries += map(.. other file ) 116 call nix#ExportPluginsForNix(opts) 117 118Then ":source %" it. 119 120nix#ExportPluginsForNix is provided by ./vim2nix 121 122A buffer will open containing the plugin derivation lines as well list 123fitting the vimrcConfig.vam.pluginDictionaries option. 124 125Thus the most simple usage would be: 126 127 vim_with_plugins = 128 let vim = vim_configurable; 129 inherit (vimUtil.override {inherit vim}) rtpPath addRtp buildVimPlugin vimHelpTags; 130 vimPlugins = [ 131 # the derivation list from the buffer created by nix#ExportPluginsForNix 132 # don't set which will default to pkgs.vimPlugins 133 ]; 134 in vim.customize { 135 name = "vim-with-plugins"; 136 137 vimrcConfig.customRC = '' .. ''; 138 139 vimrcConfig.vam.knownPlugins = vimPlugins; 140 vimrcConfig.vam.pluginDictionaries = [ 141 # the plugin list form ~/.vim-scripts turned into nix format added to 142 # the buffer created by the nix#ExportPluginsForNix 143 ]; 144 } 145 146vim_with_plugins can be installed like any other application within Nix. 147 148[VAM] https://github.com/MarcWeber/vim-addon-manager 149[vim-pi] https://bitbucket.org/vimcommunity/vim-pi 150*/ 151 152 153let 154 inherit lib; 155 156 # make sure a plugin is a derivation and its dependencies are derivations. If 157 # plugin already is a derivation, this is a no-op. If it is a string, it is 158 # looked up in knownPlugins. 159 pluginToDrv = knownPlugins: plugin: 160 let 161 drv = 162 if builtins.isString plugin then 163 # make sure `pname` is set to that we are able to convert the derivation 164 # back to a string. 165 ( knownPlugins.${plugin} // { pname = plugin; }) 166 else 167 plugin; 168 in 169 # make sure all the dependencies of the plugin are also derivations 170 drv // { dependencies = map (pluginToDrv knownPlugins) (drv.dependencies or []); }; 171 172 # transitive closure of plugin dependencies (plugin needs to be a derivation) 173 transitiveClosure = plugin: 174 [ plugin ] ++ ( 175 lib.unique (builtins.concatLists (map transitiveClosure plugin.dependencies or [])) 176 ); 177 178 findDependenciesRecursively = plugins: lib.concatMap transitiveClosure plugins; 179 180 vamDictToNames = x: 181 if builtins.isString x then [x] 182 else (lib.optional (x ? name) x.name) 183 ++ (x.names or []); 184 185 rtpPath = "share/vim-plugins"; 186 187 nativeImpl = packages: lib.optionalString (packages != null) 188 (let 189 link = (packageName: dir: pluginPath: "ln -sf ${pluginPath}/share/vim-plugins/* $out/pack/${packageName}/${dir}"); 190 packageLinks = (packageName: {start ? [], opt ? []}: 191 let 192 # `nativeImpl` expects packages to be derivations, not strings (as 193 # opposed to older implementations that have to maintain backwards 194 # compatibility). Therefore we don't need to deal with "knownPlugins" 195 # and can simply pass `null`. 196 depsOfOptionalPlugins = lib.subtractLists opt (findDependenciesRecursively opt); 197 startWithDeps = findDependenciesRecursively start; 198 in 199 [ "mkdir -p $out/pack/${packageName}/start" ] 200 # To avoid confusion, even dependencies of optional plugins are added 201 # to `start` (except if they are explicitly listed as optional plugins). 202 ++ (builtins.map (link packageName "start") (lib.unique (startWithDeps ++ depsOfOptionalPlugins))) 203 ++ ["mkdir -p $out/pack/${packageName}/opt"] 204 ++ (builtins.map (link packageName "opt") opt) 205 ); 206 packDir = (packages: 207 stdenv.mkDerivation { 208 name = "vim-pack-dir"; 209 src = ./.; 210 installPhase = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList packageLinks packages)); 211 preferLocalBuild = true; 212 } 213 ); 214 in 215 '' 216 set packpath^=${packDir packages} 217 set runtimepath^=${packDir packages} 218 ''); 219 220 vimrcContent = { 221 packages ? null, 222 vam ? null, 223 pathogen ? null, 224 plug ? null, 225 beforePlugins ? '' 226 " configuration generated by NIX 227 set nocompatible 228 '', 229 customRC ? null 230 }: 231 232 let 233 /* pathogen mostly can set &rtp at startup time. Its used very commonly. 234 */ 235 pathogenImpl = lib.optionalString (pathogen != null) 236 (let 237 knownPlugins = pathogen.knownPlugins or vimPlugins; 238 239 plugins = findDependenciesRecursively (map (pluginToDrv knownPlugins) pathogen.pluginNames); 240 241 pluginsEnv = buildEnv { 242 name = "pathogen-plugin-env"; 243 paths = map (x: "${x}/${rtpPath}") plugins; 244 }; 245 in 246 '' 247 let &rtp.=(empty(&rtp)?"":',')."${vimPlugins.pathogen.rtp}" 248 execute pathogen#infect('${pluginsEnv}/{}') 249 250 filetype indent plugin on | syn on 251 ''); 252 253 /* vim-plug is an extremely popular vim plugin manager. 254 */ 255 plugImpl = lib.optionalString (plug != null) 256 ('' 257 source ${vimPlugins.vim-plug.rtp}/plug.vim 258 call plug#begin('/dev/null') 259 260 '' + (lib.concatMapStringsSep "\n" (pkg: "Plug '${pkg.rtp}'") plug.plugins) + '' 261 262 call plug#end() 263 ''); 264 265 /* 266 vim-addon-manager = VAM 267 268 * maps names to plugin location 269 270 * manipulates &rtp at startup time 271 or when Vim has been running for a while 272 273 * can activate plugins laziy (eg when loading a specific filetype) 274 275 * knows about vim plugin dependencies (addon-info.json files) 276 277 * still is minimalistic (only loads one file), the "check out" code it also 278 has only gets loaded when a plugin is requested which is not found on disk 279 yet 280 281 */ 282 vamImpl = lib.optionalString (vam != null) 283 (let 284 knownPlugins = vam.knownPlugins or vimPlugins; 285 286 # plugins specified by the user 287 specifiedPlugins = map (pluginToDrv knownPlugins) (lib.concatMap vamDictToNames vam.pluginDictionaries); 288 # plugins with dependencies 289 plugins = findDependenciesRecursively specifiedPlugins; 290 291 # Convert scalars, lists, and attrs, to VimL equivalents 292 toVimL = x: 293 if builtins.isString x then "'${lib.replaceStrings [ "\n" "'" ] [ "\n\\ " "''" ] x}'" 294 else if builtins.isAttrs x && builtins ? out then toVimL x # a derivation 295 else if builtins.isAttrs x then "{${lib.concatStringsSep ", " (lib.mapAttrsToList (n: v: "${toVimL n}: ${toVimL v}") x)}}" 296 else if builtins.isList x then "[${lib.concatMapStringsSep ", " toVimL x}]" 297 else if builtins.isInt x || builtins.isFloat x then builtins.toString x 298 else if builtins.isBool x then (if x then "1" else "0") 299 else throw "turning ${lib.generators.toPretty {} x} into a VimL thing not implemented yet"; 300 301 in assert builtins.hasAttr "vim-addon-manager" knownPlugins; 302 '' 303 filetype indent plugin on | syn on 304 305 let g:nix_plugin_locations = {} 306 ${lib.concatMapStrings (plugin: '' 307 let g:nix_plugin_locations['${plugin.pname}'] = "${plugin.rtp}" 308 '') plugins} 309 let g:nix_plugin_locations['vim-addon-manager'] = "${knownPlugins.vim-addon-manager.rtp}" 310 311 let g:vim_addon_manager = {} 312 313 if exists('g:nix_plugin_locations') 314 " nix managed config 315 316 " override default function making VAM aware of plugin locations: 317 fun! NixPluginLocation(name) 318 let path = get(g:nix_plugin_locations, a:name, "") 319 return path == "" ? vam#DefaultPluginDirFromName(a:name) : path 320 endfun 321 let g:vim_addon_manager.plugin_dir_by_name = 'NixPluginLocation' 322 " tell Vim about VAM: 323 let &rtp.=(empty(&rtp)?"":','). g:nix_plugin_locations['vim-addon-manager'] 324 else 325 " standalone config 326 327 let &rtp.=(empty(&rtp)?"":',').c.plugin_root_dir.'/vim-addon-manager' 328 if !isdirectory(c.plugin_root_dir.'/vim-addon-manager/autoload') 329 " checkout VAM 330 execute '!git clone --depth=1 https://github.com/MarcWeber/vim-addon-manager ' 331 \ shellescape(c.plugin_root_dir.'/vim-addon-manager', 1) 332 endif 333 endif 334 335 " tell vam which plugins to load, and when: 336 let l = [] 337 ${lib.concatMapStrings (p: "call add(l, ${toVimL p})\n") vam.pluginDictionaries} 338 call vam#Scripts(l, {}) 339 ''); 340 341 entries = [ 342 beforePlugins 343 vamImpl pathogenImpl plugImpl 344 (nativeImpl packages) 345 customRC 346 ]; 347 348 in 349 lib.concatStringsSep "\n" (lib.filter (x: x != null && x != "") entries); 350 351 vimrcFile = settings: writeText "vimrc" (vimrcContent settings); 352 353in 354 355rec { 356 inherit vimrcFile; 357 inherit vimrcContent; 358 359 # shell script with custom name passing [-u vimrc] [-U gvimrc] to vim 360 vimWithRC = { 361 vimExecutable, 362 gvimExecutable, 363 vimManPages, 364 wrapManual, 365 wrapGui, 366 name ? "vim", 367 vimrcFile ? null, 368 gvimrcFile ? null, 369 vimExecutableName, 370 gvimExecutableName, 371 }: 372 let 373 rcOption = o: file: lib.optionalString (file != null) "-${o} ${file}"; 374 vimWrapperScript = writeScriptBin vimExecutableName '' 375 #!${runtimeShell} 376 exec ${vimExecutable} ${rcOption "u" vimrcFile} ${rcOption "U" gvimrcFile} "$@" 377 ''; 378 gvimWrapperScript = writeScriptBin gvimExecutableName '' 379 #!${stdenv.shell} 380 exec ${gvimExecutable} ${rcOption "u" vimrcFile} ${rcOption "U" gvimrcFile} "$@" 381 ''; 382 in 383 buildEnv { 384 inherit name; 385 paths = [ 386 vimWrapperScript 387 ] ++ lib.optional wrapGui gvimWrapperScript 388 ++ lib.optional wrapManual vimManPages 389 ; 390 }; 391 392 # add a customize option to a vim derivation 393 makeCustomizable = vim: vim // { 394 customize = { 395 name, 396 vimrcConfig, 397 wrapManual ? true, 398 wrapGui ? false, 399 vimExecutableName ? name, 400 gvimExecutableName ? (lib.concatStrings [ "g" name ]), 401 }: vimWithRC { 402 vimExecutable = "${vim}/bin/vim"; 403 gvimExecutable = "${vim}/bin/gvim"; 404 inherit name wrapManual wrapGui vimExecutableName gvimExecutableName; 405 vimrcFile = vimrcFile vimrcConfig; 406 vimManPages = buildEnv { 407 name = "vim-doc"; 408 paths = [ vim ]; 409 pathsToLink = [ "/share/man" ]; 410 }; 411 }; 412 413 override = f: makeCustomizable (vim.override f); 414 overrideAttrs = f: makeCustomizable (vim.overrideAttrs f); 415 }; 416 417 pluginnames2Nix = {name, namefiles} : vim_configurable.customize { 418 inherit name; 419 vimrcConfig.vam.knownPlugins = vimPlugins; 420 vimrcConfig.vam.pluginDictionaries = ["vim2nix"]; 421 vimrcConfig.customRC = '' 422 " Yes - this is impure and will create the cache file and checkout vim-pi 423 " into ~/.vim/vim-addons 424 let g:vim_addon_manager.plugin_root_dir = "/tmp/vim2nix-".$USER 425 if !isdirectory(g:vim_addon_manager.plugin_root_dir) 426 call mkdir(g:vim_addon_manager.plugin_root_dir) 427 else 428 echom repeat("=", 80) 429 echom "WARNING: reusing cache directory :".g:vim_addon_manager.plugin_root_dir 430 echom repeat("=", 80) 431 endif 432 let opts = {} 433 let opts.nix_prefetch_git = "${nix-prefetch-git}/bin/nix-prefetch-git" 434 let opts.nix_prefetch_hg = "${nix-prefetch-hg}/bin/nix-prefetch-hg" 435 let opts.cache_file = g:vim_addon_manager.plugin_root_dir.'/cache' 436 let opts.plugin_dictionaries = [] 437 ${lib.concatMapStrings (file: "let opts.plugin_dictionaries += map(readfile(\"${file}\"), 'eval(v:val)')\n") namefiles } 438 439 " uncomment for debugging failures 440 " let opts.try_catch = 0 441 442 " add more files 443 " let opts.plugin_dictionaries += map(.. other file ) 444 call nix#ExportPluginsForNix(opts) 445 ''; 446 }; 447 448 inherit (import ./build-vim-plugin.nix { inherit lib stdenv rtpPath vim; }) buildVimPlugin buildVimPluginFrom2Nix; 449 450 # used to figure out which python dependencies etc. neovim needs 451 requiredPlugins = { 452 packages ? {}, 453 givenKnownPlugins ? null, 454 vam ? null, 455 pathogen ? null, 456 plug ? null, ... 457 }: 458 let 459 # This is probably overcomplicated, but I don't understand this well enough to know what's necessary. 460 knownPlugins = if givenKnownPlugins != null then givenKnownPlugins else 461 if vam != null && vam ? knownPlugins then vam.knownPlugins else 462 if pathogen != null && pathogen ? knownPlugins then pathogen.knownPlugins else 463 vimPlugins; 464 pathogenPlugins = findDependenciesRecursively (map (pluginToDrv knownPlugins) pathogen.pluginNames); 465 vamPlugins = findDependenciesRecursively (map (pluginToDrv knownPlugins) (lib.concatMap vamDictToNames vam.pluginDictionaries)); 466 nonNativePlugins = (lib.optionals (pathogen != null) pathogenPlugins) 467 ++ (lib.optionals (vam != null) vamPlugins) 468 ++ (lib.optionals (plug != null) plug.plugins); 469 nativePluginsConfigs = lib.attrsets.attrValues packages; 470 nativePlugins = lib.concatMap ({start?[], opt?[], knownPlugins?vimPlugins}: start++opt) nativePluginsConfigs; 471 in 472 nativePlugins ++ nonNativePlugins; 473}