at v192 13 kB view raw
1{stdenv, vim, vimPlugins, vim_configurable, buildEnv, writeText, writeScriptBin 2, nix-prefetch-scripts }: 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 vimrcConfig.vam.knownPlugins = pkgs.vimPlugins; # optional 21 vimrcConfig.vam.pluginDictionaries = [ 22 # load always 23 { name = "youcompleteme"; } 24 { names = ["youcompleteme" "foo"]; } 25 26 # only load when opening a .php file 27 { name = "phpCompletion"; ft_regex = "^php\$"; } 28 { name = "phpCompletion"; filename_regex = "^.php\$"; } 29 30 # provide plugin which can be loaded manually: 31 { name = "phpCompletion"; tag = "lazy"; } 32 33 # full ducomentation at github.com/MarcWeber/vim-addon-manager 34 ]; 35 36 # there is a pathogen implementation as well, but its startup is slower and [VAM] has more feature 37 # vimrcConfig.pathogen.knownPlugins = vimPlugins; # optional 38 # vimrcConfig.pathogen.pluginNames = ["vim-addon-nix"]; 39 }; 40 41WHAT IS A VIM PLUGIN? 42===================== 43Typical plugin files: 44 45 plugin/P1.vim 46 autoload/P1.vim 47 ftplugin/xyz.vim 48 doc/plugin-documentation.txt (traditional documentation) 49 README(.md) (nowadays thanks to github) 50 51 52Vim offers the :h rtp setting which works for most plugins. Thus adding 53this to your .vimrc should make most plugins work: 54 55 set rtp+=~/.nix-profile/share/vim-plugins/youcompleteme 56 " or for p in ["youcompleteme"] | exec 'set rtp+=~/.nix-profile/share/vim-plugins/'.p | endfor 57 58which is what the [VAM]/pathogen solutions above basically do. 59 60Learn about about plugin Vim plugin mm managers at 61http://vim-wiki.mawercer.de/wiki/topic/vim%20plugin%20managment.html. 62 63The documentation can be accessed by Vim's :help command if it was tagged. 64See vimHelpTags sample code below. 65 66CONTRIBUTING AND CUSTOMIZING 67============================ 68The example file pkgs/misc/vim-plugins/default.nix provides both: 69* manually mantained plugins 70* plugins created by VAM's nix#ExportPluginsForNix implementation 71 72I highly recommend to lookup vim plugin attribute names at the [vim-pi] project 73 which is a database containing all plugins from 74vim.org and quite a lot of found at github and similar sources. vim-pi's documented purpose 75is to associate vim.org script ids to human readable names so that dependencies 76can be describe easily. 77 78How to find a name? 79 * http://vam.mawercer.de/ or VAM's 80 * grep vim-pi 81 * use VAM's completion or :AddonsInfo command 82 83It might happen than a plugin is not known by vim-pi yet. We encourage you to 84contribute to vim-pi so that plugins can be updated automatically. 85 86 87CREATING DERVITATIONS AUTOMATICALLY BY PLUGIN NAME 88================================================== 89Most convenient is to use a ~/.vim-scripts file putting a plugin name into each line 90as documented by [VAM]'s README.md 91It is the same format you pass to vimrcConfig.vam.pluginDictionaries from the 92usage example above. 93 94Then 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 104Then ":source %" it. 105 106nix#ExportPluginsForNix is provided by github.com/JagaJaga/vim-addon-vim2nix 107 108A buffer will open containing the plugin derivation lines as well list 109fitting the vimrcConfig.vam.pluginDictionaries option. 110 111Thus the most simple usage would be: 112 113 vim_with_plugins = 114 let vim = vim_configurable; 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 132vim_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 138 139let 140 inherit (stdenv) lib; 141 142 findDependenciesRecursively = {knownPlugins, names}: 143 144 let depsOf = name: (builtins.getAttr name knownPlugins).dependencies or []; 145 146 recurseNames = path: names: lib.concatMap (name: recurse ([name]++path)) names; 147 148 recurse = path: 149 let name = builtins.head path; 150 in if builtins.elem name (builtins.tail path) 151 then throw "recursive vim dependencies" 152 else [name] ++ recurseNames path (depsOf name); 153 154 in lib.uniqList { inputList = recurseNames [] names; }; 155 156 vimrcFile = { 157 vam ? null, 158 pathogen ? null, 159 customRC ? "" 160 }: 161 162 let 163 /* pathogen mostly can set &rtp at startup time. Its used very commonly. 164 */ 165 pathogenImpl = lib.optionalString (pathogen != null) 166 (let 167 knownPlugins = pathogen.knownPlugins or vimPlugins; 168 169 plugins = map (name: knownPlugins.${name}) (findDependenciesRecursively { inherit knownPlugins; names = pathogen.pluginNames; }); 170 171 pluginsEnv = buildEnv { 172 name = "pathogen-plugin-env"; 173 paths = map (x: "${x}/${vimPlugins.rtpPath}") plugins; 174 }; 175 in 176 '' 177 let &rtp.=(empty(&rtp)?"":',')."${vimPlugins.pathogen.rtp}" 178 execute pathogen#infect('${pluginsEnv}/{}') 179 ''); 180 181 /* 182 vim-addon-manager = VAM 183 184 * maps names to plugin location 185 186 * manipulates &rtp at startup time 187 or when Vim has been running for a while 188 189 * can activate plugins laziy (eg when loading a specific filetype) 190 191 * knows about vim plugin dependencies (addon-info.json files) 192 193 * still is minimalistic (only loads one file), the "check out" code it also 194 has only gets loaded when a plugin is requested which is not found on disk 195 yet 196 197 */ 198 vamImpl = lib.optionalString (vam != null) 199 (let 200 knownPlugins = vam.knownPlugins or vimPlugins; 201 202 toNames = x: 203 if builtins.isString x then [x] 204 else (lib.optional (x ? name) x.name) 205 ++ (x.names or []); 206 207 names = findDependenciesRecursively { inherit knownPlugins; names = lib.concatMap toNames vam.pluginDictionaries; }; 208 209 # Vim almost reads JSON, so eventually JSON support should be added to Nix 210 # TODO: proper quoting 211 toNix = x: 212 if (builtins.isString x) then "'${x}'" 213 else if builtins.isAttrs x && builtins ? out then toNix "${x}" # a derivation 214 else if builtins.isAttrs x then "{${lib.concatStringsSep ", " (lib.mapAttrsToList (n: v: "${toNix n}: ${toNix v}") x)}}" 215 else if builtins.isList x then "[${lib.concatMapStringsSep ", " toNix x}]" 216 else throw "turning ${lib.showVal x} into a VimL thing not implemented yet"; 217 218 in assert builtins.hasAttr "vim-addon-manager" knownPlugins; 219 '' 220 let g:nix_plugin_locations = {} 221 ${lib.concatMapStrings (name: '' 222 let g:nix_plugin_locations['${name}'] = "${knownPlugins.${name}.rtp}" 223 '') names} 224 let g:nix_plugin_locations['vim-addon-manager'] = "${knownPlugins."vim-addon-manager".rtp}" 225 226 let g:vim_addon_manager = {} 227 228 if exists('g:nix_plugin_locations') 229 " nix managed config 230 231 " override default function making VAM aware of plugin locations: 232 fun! NixPluginLocation(name) 233 let path = get(g:nix_plugin_locations, a:name, "") 234 return path == "" ? vam#DefaultPluginDirFromName(a:name) : path 235 endfun 236 let g:vim_addon_manager.plugin_dir_by_name = 'NixPluginLocation' 237 " tell Vim about VAM: 238 let &rtp.=(empty(&rtp)?"":','). g:nix_plugin_locations['vim-addon-manager'] 239 else 240 " standalone config 241 242 let &rtp.=(empty(&rtp)?"":',').c.plugin_root_dir.'/vim-addon-manager' 243 if !isdirectory(c.plugin_root_dir.'/vim-addon-manager/autoload') 244 " checkout VAM 245 execute '!git clone --depth=1 git://github.com/MarcWeber/vim-addon-manager ' 246 \ shellescape(c.plugin_root_dir.'/vim-addon-manager', 1) 247 endif 248 endif 249 250 " tell vam about which plugins to load when: 251 let l = [] 252 ${lib.concatMapStrings (p: "call add(l, ${toNix p})\n") vam.pluginDictionaries} 253 call vam#Scripts(l, {}) 254 ''); 255 256 # somebody else could provide these implementations 257 vundleImpl = ""; 258 259 neobundleImpl = ""; 260 261 262 in writeText "vimrc" '' 263 " minimal setup, generated by NIX 264 set nocompatible 265 filetype indent plugin on | syn on 266 267 ${vamImpl} 268 ${pathogenImpl} 269 ${vundleImpl} 270 ${neobundleImpl} 271 272 ${customRC} 273 ''; 274 275in 276 277rec { 278 inherit vimrcFile; 279 280 # shell script with custom name passing [-u vimrc] [-U gvimrc] to vim 281 vimWithRC = {vimExecutable, name ? null, vimrcFile ? null, gvimrcFile ? null}: 282 let rcOption = o: file: stdenv.lib.optionalString (file != null) "-${o} ${file}"; 283 in writeScriptBin (if name == null then "vim" else name) '' 284 #!/bin/sh 285 exec ${vimExecutable} ${rcOption "u" vimrcFile} ${rcOption "U" gvimrcFile} "$@" 286 ''; 287 288 # add a customize option to a vim derivation 289 makeCustomizable = vim: vim // { 290 customize = {name, vimrcConfig}: vimWithRC { 291 vimExecutable = "${vim}/bin/vim"; 292 inherit name; 293 vimrcFile = vimrcFile vimrcConfig; 294 }; 295 }; 296 297 pluginnames2Nix = {name, namefiles} : vim_configurable.customize { 298 inherit name; 299 vimrcConfig.vam.knownPlugins = vimPlugins; 300 vimrcConfig.vam.pluginDictionaries = ["vim-addon-vim2nix"]; # Using fork until patch is accepted by upstream 301 vimrcConfig.customRC = '' 302 " Yes - this is impure and will create the cache file and checkout vim-pi 303 " into ~/.vim/vim-addons 304 let g:vim_addon_manager.plugin_root_dir = "/tmp/vim2nix-".$USER 305 if !isdirectory(g:vim_addon_manager.plugin_root_dir) 306 call mkdir(g:vim_addon_manager.plugin_root_dir) 307 else 308 echom repeat("=", 80) 309 echom "WARNING: reusing cache directory :".g:vim_addon_manager.plugin_root_dir 310 echom repeat("=", 80) 311 endif 312 let opts = {} 313 let opts.nix_prefetch_git = "${nix-prefetch-scripts}/bin/nix-prefetch-git" 314 let opts.nix_prefetch_hg = "${nix-prefetch-scripts}/bin/nix-prefetch-hg" 315 let opts.cache_file = g:vim_addon_manager.plugin_root_dir.'/cache' 316 let opts.plugin_dictionaries = [] 317 ${lib.concatMapStrings (file: "let opts.plugin_dictionaries += map(readfile(\"${file}\"), 'eval(v:val)')\n") namefiles } 318 319 " uncomment for debugging failures 320 " let opts.try_catch = 0 321 322 " add more files 323 " let opts.plugin_dictionaries += map(.. other file ) 324 call nix#ExportPluginsForNix(opts) 325 ''; 326 }; 327 328 rtpPath = "share/vim-plugins"; 329 330 vimHelpTags = '' 331 vimHelpTags(){ 332 if [ -d "$1/doc" ]; then 333 ${vim}/bin/vim -N -u NONE -i NONE -n -e -s -c "helptags $1/doc" +quit! 334 fi 335 } 336 ''; 337 338 addRtp = path: derivation: 339 derivation // { rtp = "${derivation}/${path}"; }; 340 341 buildVimPlugin = a@{ 342 name, 343 namePrefix ? "vimplugin-", 344 src, 345 unpackPhase ? "", 346 configurePhase ? "", 347 buildPhase ? "", 348 path ? (builtins.parseDrvName name).name, 349 addonInfo ? null, 350 ... 351 }: 352 addRtp "${rtpPath}/${path}" (stdenv.mkDerivation (a // { 353 name = namePrefix + name; 354 355 inherit unpackPhase configurePhase buildPhase addonInfo; 356 357 installPhase = '' 358 target=$out/${rtpPath}/${path} 359 mkdir -p $out/${rtpPath} 360 cp -r . $target 361 ${vimHelpTags} 362 vimHelpTags $target 363 if [ -n "$addonInfo" ]; then 364 echo "$addonInfo" > $target/addon-info.json 365 fi 366 ''; 367 })); 368 369 buildVimPluginFrom2Nix = a: buildVimPlugin ({ 370 buildPhase = ":"; 371 configurePhase =":"; 372 } // a); 373 374 # test cases: 375 test_vim_with_vim_addon_nix_using_vam = vim_configurable.customize { 376 name = "vim-with-vim-addon-nix-using-vam"; 377 vimrcConfig.vam.pluginDictionaries = [{name = "vim-addon-nix"; }]; 378 }; 379 380 test_vim_with_vim_addon_nix_using_pathogen = vim_configurable.customize { 381 name = "vim-with-vim-addon-nix-using-pathogen"; 382 vimrcConfig.pathogen.pluginNames = [ "vim-addon-nix" ]; 383 }; 384 385}