{stdenv, vim, vimPlugins, vim_configurable, buildEnv, writeText, writeScriptBin , nix-prefetch-hg, nix-prefetch-git }: /* USAGE EXAMPLE ============= Install Vim like this eg using nixos option environment.systemPackages which will provide vim-with-plugins in PATH: vim_configurable.customize { name = "vim-with-plugins"; # add custom .vimrc lines like this: vimrcConfig.customRC = '' set hidden ''; # store your plugins in Vim packages vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; { # loaded on launch start = [ youcompleteme fugitive ]; # manually loadable by calling `:packadd $plugin-name` opt = [ phpCompletion elm-vim ]; # To automatically load a plugin when opening a filetype, add vimrc lines like: # autocmd FileType php :packadd phpCompletion }; # plugins can also be managed by VAM vimrcConfig.vam.knownPlugins = pkgs.vimPlugins; # optional vimrcConfig.vam.pluginDictionaries = [ # load always { name = "youcompleteme"; } { names = ["youcompleteme" "foo"]; } # only load when opening a .php file { name = "phpCompletion"; ft_regex = "^php\$"; } { name = "phpCompletion"; filename_regex = "^.php\$"; } # provide plugin which can be loaded manually: { name = "phpCompletion"; tag = "lazy"; } # full documentation at github.com/MarcWeber/vim-addon-manager ]; # there is a pathogen implementation as well, but its startup is slower and [VAM] has more feature # vimrcConfig.pathogen.knownPlugins = vimPlugins; # optional # vimrcConfig.pathogen.pluginNames = ["vim-addon-nix"]; }; WHAT IS A VIM PLUGIN? ===================== Typical plugin files: plugin/P1.vim autoload/P1.vim ftplugin/xyz.vim doc/plugin-documentation.txt (traditional documentation) README(.md) (nowadays thanks to github) Vim offers the :h rtp setting which works for most plugins. Thus adding this to your .vimrc should make most plugins work: set rtp+=~/.nix-profile/share/vim-plugins/youcompleteme " or for p in ["youcompleteme"] | exec 'set rtp+=~/.nix-profile/share/vim-plugins/'.p | endfor which is what the [VAM]/pathogen solutions above basically do. Learn about about plugin Vim plugin mm managers at http://vim-wiki.mawercer.de/wiki/topic/vim%20plugin%20managment.html. The documentation can be accessed by Vim's :help command if it was tagged. See vimHelpTags sample code below. CONTRIBUTING AND CUSTOMIZING ============================ The example file pkgs/misc/vim-plugins/default.nix provides both: * manually mantained plugins * plugins created by VAM's nix#ExportPluginsForNix implementation I highly recommend to lookup vim plugin attribute names at the [vim-pi] project which is a database containing all plugins from vim.org and quite a lot of found at github and similar sources. vim-pi's documented purpose is to associate vim.org script ids to human readable names so that dependencies can be describe easily. How to find a name? * http://vam.mawercer.de/ or VAM's * grep vim-pi * use VAM's completion or :AddonsInfo command It might happen than a plugin is not known by vim-pi yet. We encourage you to contribute to vim-pi so that plugins can be updated automatically. CREATING DERVITATIONS AUTOMATICALLY BY PLUGIN NAME ================================================== Most convenient is to use a ~/.vim-scripts file putting a plugin name into each line as documented by [VAM]'s README.md It is the same format you pass to vimrcConfig.vam.pluginDictionaries from the usage example above. Then create a temp vim file and insert: let opts = {} let opts.path_to_nixpkgs = '/etc/nixos/nixpkgs' let opts.cache_file = '/tmp/export-vim-plugin-for-nix-cache-file' let opts.plugin_dictionaries = map(readfile("vim-plugins"), 'eval(v:val)') " add more files " let opts.plugin_dictionaries += map(.. other file ) call nix#ExportPluginsForNix(opts) Then ":source %" it. nix#ExportPluginsForNix is provided by ./vim2nix A buffer will open containing the plugin derivation lines as well list fitting the vimrcConfig.vam.pluginDictionaries option. Thus the most simple usage would be: vim_with_plugins = let vim = vim_configurable; inherit (vimUtil.override {inherit vim}) rtpPath addRtp buildVimPlugin vimHelpTags; vimPlugins = [ # the derivation list from the buffer created by nix#ExportPluginsForNix # don't set which will default to pkgs.vimPlugins ]; in vim.customize { name = "vim-with-plugins"; vimrcConfig.customRC = '' .. ''; vimrcConfig.vam.knownPlugins = vimPlugins; vimrcConfig.vam.pluginDictionaries = [ # the plugin list form ~/.vim-scripts turned into nix format added to # the buffer created by the nix#ExportPluginsForNix ]; } vim_with_plugins can be installed like any other application within Nix. [VAM] https://github.com/MarcWeber/vim-addon-manager [vim-pi] https://bitbucket.org/vimcommunity/vim-pi */ let inherit (stdenv) lib; toNames = x: if builtins.isString x then [x] else (lib.optional (x ? name) x.name) ++ (x.names or []); findDependenciesRecursively = {knownPlugins, names}: let depsOf = name: (builtins.getAttr name knownPlugins).dependencies or []; recurseNames = path: names: lib.concatMap (name: recurse ([name]++path)) names; recurse = path: let name = builtins.head path; in if builtins.elem name (builtins.tail path) then throw "recursive vim dependencies" else [name] ++ recurseNames path (depsOf name); in lib.uniqList { inputList = recurseNames [] names; }; vimrcFile = { packages ? null, vam ? null, pathogen ? null, plug ? null, customRC ? "" }: let /* pathogen mostly can set &rtp at startup time. Its used very commonly. */ pathogenImpl = lib.optionalString (pathogen != null) (let knownPlugins = pathogen.knownPlugins or vimPlugins; plugins = map (name: knownPlugins.${name}) (findDependenciesRecursively { inherit knownPlugins; names = pathogen.pluginNames; }); pluginsEnv = buildEnv { name = "pathogen-plugin-env"; paths = map (x: "${x}/${vimPlugins.rtpPath}") plugins; }; in '' let &rtp.=(empty(&rtp)?"":',')."${vimPlugins.pathogen.rtp}" execute pathogen#infect('${pluginsEnv}/{}') ''); /* vim-plug is an extremely popular vim plugin manager. */ plugImpl = lib.optionalString (plug != null) '' source ${vimPlugins.vim-plug.rtp}/plug.vim call plug#begin('/dev/null') '' + (lib.concatMapStringsSep "\n" (pkg: "Plug '${pkg.rtp}'") plug.plugins) + '' call plug#end() ''; /* vim-addon-manager = VAM * maps names to plugin location * manipulates &rtp at startup time or when Vim has been running for a while * can activate plugins laziy (eg when loading a specific filetype) * knows about vim plugin dependencies (addon-info.json files) * still is minimalistic (only loads one file), the "check out" code it also has only gets loaded when a plugin is requested which is not found on disk yet */ vamImpl = lib.optionalString (vam != null) (let knownPlugins = vam.knownPlugins or vimPlugins; names = findDependenciesRecursively { inherit knownPlugins; names = lib.concatMap toNames vam.pluginDictionaries; }; # Vim almost reads JSON, so eventually JSON support should be added to Nix # TODO: proper quoting toNix = x: if (builtins.isString x) then "'${x}'" else if builtins.isAttrs x && builtins ? out then toNix "${x}" # a derivation else if builtins.isAttrs x then "{${lib.concatStringsSep ", " (lib.mapAttrsToList (n: v: "${toNix n}: ${toNix v}") x)}}" else if builtins.isList x then "[${lib.concatMapStringsSep ", " toNix x}]" else throw "turning ${lib.generators.toPretty {} x} into a VimL thing not implemented yet"; in assert builtins.hasAttr "vim-addon-manager" knownPlugins; '' let g:nix_plugin_locations = {} ${lib.concatMapStrings (name: '' let g:nix_plugin_locations['${name}'] = "${knownPlugins.${name}.rtp}" '') names} let g:nix_plugin_locations['vim-addon-manager'] = "${knownPlugins."vim-addon-manager".rtp}" let g:vim_addon_manager = {} if exists('g:nix_plugin_locations') " nix managed config " override default function making VAM aware of plugin locations: fun! NixPluginLocation(name) let path = get(g:nix_plugin_locations, a:name, "") return path == "" ? vam#DefaultPluginDirFromName(a:name) : path endfun let g:vim_addon_manager.plugin_dir_by_name = 'NixPluginLocation' " tell Vim about VAM: let &rtp.=(empty(&rtp)?"":','). g:nix_plugin_locations['vim-addon-manager'] else " standalone config let &rtp.=(empty(&rtp)?"":',').c.plugin_root_dir.'/vim-addon-manager' if !isdirectory(c.plugin_root_dir.'/vim-addon-manager/autoload') " checkout VAM execute '!git clone --depth=1 https://github.com/MarcWeber/vim-addon-manager ' \ shellescape(c.plugin_root_dir.'/vim-addon-manager', 1) endif endif " tell vam about which plugins to load when: let l = [] ${lib.concatMapStrings (p: "call add(l, ${toNix p})\n") vam.pluginDictionaries} call vam#Scripts(l, {}) ''); nativeImpl = lib.optionalString (packages != null) (let link = (packageName: dir: pluginPath: "ln -sf ${pluginPath}/share/vim-plugins/* $out/pack/${packageName}/${dir}"); packageLinks = (packageName: {start ? [], opt ? []}: ["mkdir -p $out/pack/${packageName}/start"] ++ (builtins.map (link packageName "start") start) ++ ["mkdir -p $out/pack/${packageName}/opt"] ++ (builtins.map (link packageName "opt") opt) ); packDir = (packages: stdenv.mkDerivation rec { name = "vim-pack-dir"; src = ./.; installPhase = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList packageLinks packages)); preferLocalBuild = true; } ); in '' set packpath-=~/.vim/after set packpath+=${packDir packages} set packpath+=~/.vim/after ''); # somebody else could provide these implementations vundleImpl = ""; neobundleImpl = ""; in writeText "vimrc" '' " minimal setup, generated by NIX set nocompatible ${vamImpl} ${pathogenImpl} ${plugImpl} ${vundleImpl} ${neobundleImpl} ${nativeImpl} filetype indent plugin on | syn on ${customRC} ''; in rec { inherit vimrcFile; # shell script with custom name passing [-u vimrc] [-U gvimrc] to vim vimWithRC = {vimExecutable, name ? null, vimrcFile ? null, gvimrcFile ? null}: let rcOption = o: file: stdenv.lib.optionalString (file != null) "-${o} ${file}"; in writeScriptBin (if name == null then "vim" else name) '' #!/bin/sh exec ${vimExecutable} ${rcOption "u" vimrcFile} ${rcOption "U" gvimrcFile} "$@" ''; # add a customize option to a vim derivation makeCustomizable = vim: vim // { customize = { name, vimrcConfig }: vimWithRC { vimExecutable = "${vim}/bin/vim"; inherit name; vimrcFile = vimrcFile vimrcConfig; }; override = f: makeCustomizable (vim.override f); overrideAttrs = f: makeCustomizable (vim.overrideAttrs f); }; pluginnames2Nix = {name, namefiles} : vim_configurable.customize { inherit name; vimrcConfig.vam.knownPlugins = vimPlugins; vimrcConfig.vam.pluginDictionaries = ["vim2nix"]; vimrcConfig.customRC = '' " Yes - this is impure and will create the cache file and checkout vim-pi " into ~/.vim/vim-addons let g:vim_addon_manager.plugin_root_dir = "/tmp/vim2nix-".$USER if !isdirectory(g:vim_addon_manager.plugin_root_dir) call mkdir(g:vim_addon_manager.plugin_root_dir) else echom repeat("=", 80) echom "WARNING: reusing cache directory :".g:vim_addon_manager.plugin_root_dir echom repeat("=", 80) endif let opts = {} let opts.nix_prefetch_git = "${nix-prefetch-git}/bin/nix-prefetch-git" let opts.nix_prefetch_hg = "${nix-prefetch-hg}/bin/nix-prefetch-hg" let opts.cache_file = g:vim_addon_manager.plugin_root_dir.'/cache' let opts.plugin_dictionaries = [] ${lib.concatMapStrings (file: "let opts.plugin_dictionaries += map(readfile(\"${file}\"), 'eval(v:val)')\n") namefiles } " uncomment for debugging failures " let opts.try_catch = 0 " add more files " let opts.plugin_dictionaries += map(.. other file ) call nix#ExportPluginsForNix(opts) ''; }; rtpPath = "share/vim-plugins"; vimHelpTags = '' vimHelpTags(){ if [ -d "$1/doc" ]; then ${vim}/bin/vim -N -u NONE -i NONE -n -E -s -c "helptags $1/doc" +quit! || echo "docs to build failed" fi } ''; addRtp = path: attrs: derivation: derivation // { rtp = "${derivation}/${path}"; } // { overrideAttrs = f: buildVimPlugin (attrs // f attrs); }; buildVimPlugin = a@{ name, namePrefix ? "vimplugin-", src, unpackPhase ? "", configurePhase ? "", buildPhase ? "", preInstall ? "", postInstall ? "", path ? (builtins.parseDrvName name).name, addonInfo ? null, ... }: addRtp "${rtpPath}/${path}" a (stdenv.mkDerivation (a // { name = namePrefix + name; inherit unpackPhase configurePhase buildPhase addonInfo preInstall postInstall; installPhase = '' runHook preInstall target=$out/${rtpPath}/${path} mkdir -p $out/${rtpPath} cp -r . $target ${vimHelpTags} vimHelpTags $target if [ -n "$addonInfo" ]; then echo "$addonInfo" > $target/addon-info.json fi runHook postInstall ''; })); vim_with_vim2nix = vim_configurable.customize { name = "vim"; vimrcConfig.vam.pluginDictionaries = [ "vim-addon-vim2nix" ]; }; buildVimPluginFrom2Nix = a: buildVimPlugin ({ buildPhase = ":"; configurePhase =":"; } // a); requiredPlugins = { packages ? {}, givenKnownPlugins ? null, vam ? null, pathogen ? null, plug ? null, ... }: let # This is probably overcomplicated, but I don't understand this well enough to know what's necessary. knownPlugins = if givenKnownPlugins != null then givenKnownPlugins else if vam != null && vam ? knownPlugins then vam.knownPlugins else if pathogen != null && pathogen ? knownPlugins then pathogen.knownPlugins else vimPlugins; pathogenNames = map (name: knownPlugins.${name}) (findDependenciesRecursively { inherit knownPlugins; names = pathogen.pluginNames; }); vamNames = findDependenciesRecursively { inherit knownPlugins; names = lib.concatMap toNames vam.pluginDictionaries; }; names = (lib.optionals (pathogen != null) pathogenNames) ++ (lib.optionals (vam != null) vamNames); nonNativePlugins = map (name: knownPlugins.${name}) names ++ (lib.optionals (plug != null) plug.plugins); nativePluginsConfigs = lib.attrsets.attrValues packages; nativePlugins = lib.concatMap ({start?[], opt?[]}: start++opt) nativePluginsConfigs; in nativePlugins ++ nonNativePlugins; # test cases: test_vim_with_vim_addon_nix_using_vam = vim_configurable.customize { name = "vim-with-vim-addon-nix-using-vam"; vimrcConfig.vam.pluginDictionaries = [{name = "vim-addon-nix"; }]; }; test_vim_with_vim_addon_nix_using_pathogen = vim_configurable.customize { name = "vim-with-vim-addon-nix-using-pathogen"; vimrcConfig.pathogen.pluginNames = [ "vim-addon-nix" ]; }; test_vim_with_vim_addon_nix_using_plug = vim_configurable.customize { name = "vim-with-vim-addon-nix-using-plug"; vimrcConfig.plug.plugins = with vimPlugins; [ vim-addon-nix ]; }; test_vim_with_vim_addon_nix = vim_configurable.customize { name = "vim-with-vim-addon-nix"; vimrcConfig.packages.myVimPackage.start = with vimPlugins; [ vim-addon-nix ]; }; }