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