From 69fcd51c8f8e0ba41b957e9c1ff0df0f086b7135 Mon Sep 17 00:00:00 2001 From: Thomas Strobel Date: Sat, 27 Sep 2014 11:45:02 +0200 Subject: [PATCH 1/4] Add infrastruture: vimProfile VimProfile allows to wrap vim with different configurations. A configuration is composed by enabling and customizing pre-defined modules. A module is a set of vim plugins and vimrc statements that are tightly linked. Configurations are hierarchical and can inherit from each other. The base of each hierarchy of configurations is a distingued default configuration. Enabled vim configurations appear as normal package in nix, and have to be installed accordingly. The vimProfiles are declared either for system wide installations in /etc/nixos/configuration.nix, or for each user in ~/.nixuser/configuration.nix. An example for a vim configuration, here the content of ~/.nixuser/configuration.nix: { config, pkgs, ... }: with pkgs; { programs.vim = { enable = true; leader = " "; general.enable = true; statusline.enable = true; profiles = { # Haskell configuration hvim = { enable = true; exec = { package = pkgs.vim_configurable; bin = "bin/vim"; }; search.enable = true; # leader can be overwritten independently for each module search.leader = "|"; text.enable = true; sidebar.enable = true; # overwrite default key binding sidebar.toggleUndo = "uu"; completion.enable = true; haskell.enable = true; }; # graphical Haskell configuration hqvim = { enable = true; # set the name to call the wrapped vim name = "Hvim"; parent = "hvim"; exec = { package = pkgs.qvim; bin = "bin/qvim"; }; # just as an example that inherited modules can also be disabled search.enable = false; }; # same configuration as for default, but with qvim qvim = { # don't make that profile installable enable = false; exec = { package = pkgs.qvim; bin = "bin/qvim"; }; # if no name attribute is given, the attribute name # of the configuration is taken: "qvim" }; # alias declaration for qvim gvim = { enable = true; # inherit from a configuration by its attribute name # you can inherit from a disabled configuration as well parent = "qvim"; # add statement to the generated vimrc; it is inserted # before any module statement vimrc = '' " Leader key timeout set tm=2000 ''; }; }; }; } The declaration above defines the following packages in nix: "vimProfile.hvim", "vimProfile.hqvim" and "vimProfile.gvim". When the default profile is enabled, it is automatically installed with the package "vim". An un-wrapped vim package is always available with "vimPlain". ------------------- Internals of the implementation: A new file "nixos/modules/nixuser-module-list.nix" is added. The file lists modules that are made available both for system configuration and for user configuration. The file is imported by "nixos/modules/module-list.nix" and is used in "pkgs/top-level/all-packages.nix". A new user specific configuration file is introduced. It is located in "~/.nixuser/configuration.nix". The idea is to have a declarative approach for defining the user environment at some point. The configuration file acts as the input of a module evaluation system, similar to "/etc/nixos/configuration.nix" for the NixOS configuration. The configuration modules provided with vimProfile reduce to "nixpkgs.config" attributes, which are added to the configuration of "~/.nixpkgs/config.nix". --- nixos/modules/module-list.nix | 1 + nixos/modules/nixuser-module-list.nix | 4 + nixos/modules/programs/vim/completion.nix | 76 ++++ nixos/modules/programs/vim/default.nix | 352 ++++++++++++++++++ nixos/modules/programs/vim/general.nix | 77 ++++ nixos/modules/programs/vim/haskell.nix | 259 +++++++++++++ nixos/modules/programs/vim/search.nix | 179 +++++++++ nixos/modules/programs/vim/sidebar.nix | 62 +++ nixos/modules/programs/vim/statusline.nix | 22 ++ nixos/modules/programs/vim/text.nix | 90 +++++ pkgs/applications/editors/vim/vimProfiles.nix | 106 ++++++ pkgs/misc/vim-plugins/default.nix | 7 +- pkgs/top-level/all-packages.nix | 60 ++- 13 files changed, 1289 insertions(+), 6 deletions(-) create mode 100644 nixos/modules/nixuser-module-list.nix create mode 100644 nixos/modules/programs/vim/completion.nix create mode 100644 nixos/modules/programs/vim/default.nix create mode 100644 nixos/modules/programs/vim/general.nix create mode 100644 nixos/modules/programs/vim/haskell.nix create mode 100644 nixos/modules/programs/vim/search.nix create mode 100644 nixos/modules/programs/vim/sidebar.nix create mode 100644 nixos/modules/programs/vim/statusline.nix create mode 100644 nixos/modules/programs/vim/text.nix create mode 100644 pkgs/applications/editors/vim/vimProfiles.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 46a343460715a..aa727b30a89aa 100755 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -384,3 +384,4 @@ ./virtualisation/virtualbox-guest.nix #./virtualisation/xen-dom0.nix ] +++ import ./nixuser-module-list.nix diff --git a/nixos/modules/nixuser-module-list.nix b/nixos/modules/nixuser-module-list.nix new file mode 100644 index 0000000000000..92be641b453a9 --- /dev/null +++ b/nixos/modules/nixuser-module-list.nix @@ -0,0 +1,4 @@ +[ + # modules that are used in nixos and nixuser + ./programs/vim/default.nix +] diff --git a/nixos/modules/programs/vim/completion.nix b/nixos/modules/programs/vim/completion.nix new file mode 100644 index 0000000000000..986b8b2fdc883 --- /dev/null +++ b/nixos/modules/programs/vim/completion.nix @@ -0,0 +1,76 @@ +{pkgs,...}: +{ + ###### interface + options = { + forward = { + default = ""; + description = "Keymap to trigger completion, and to cycle in forward direction."; + }; + + backward = { + default = ""; + description = "Keymap to cycle in backward direction."; + }; + + literal = { + default = ""; + description = "Keymap to insert a literal TAB."; + }; + + listSnippets = { + default = ""; + description = "Keymap to list all snippets of UltiSnip."; + }; + + force = { + default = ""; + description = "Keymap to invoce semantic completion of YouCompleteMe."; + }; + }; + + + ###### implementation + vimrc = cfg: if !(isNull cfg.forward) || !(isNull cfg.backward) + then '' + " trigger supertab mapping + let g:SuperTabMappingForward = '${cfg.forward}' + let g:SuperTabMappingBackward = '${cfg.backward}' + + " tune supertab + let g:SuperTabRetainCompletionDuration = 'completion' + let g:SuperTabNoCompleteBefore = ['\t'] + let g:SuperTabNoCompleteAfter = ['^', '\s', '\t'] + let g:SuperTabClosePreviewOnPopupClose = 1 + " literal tab + '' + + (if (isNull cfg.literal) then "" else "let g:SuperTabMappingTabLiteral = '${cfg.literal}'") + + '' + " enable next-longest matching support + let g:SuperTabLongestEnhanced = 1 + + " couple supertab to YCM + let g:SuperTabDefaultCompletionType = '' + let g:ycm_key_list_select_completion = [''] + let g:ycm_key_list_previous_completion = [''] + let g:ycm_key_invoke_completion = '${if isNull cfg.force then "" else cfg.force}' + + " match ultisnips + let g:UltiSnipsExpandTrigger = '${cfg.forward}' + let g:UltiSnipsJumpForwardTrigger = '${cfg.forward}' + let g:UltiSnipsJumpBackwardTrigger = '${cfg.backward}' + + " config YCM + let g:ycm_seed_identifiers_with_syntax = 1 + " ctags needs to be called with --fields=+l, and tagsfiles come from tagfiles() + let g:ycm_collect_identifiers_from_tags_files = 1 + let g:ycm_autoclose_preview_window_after_completion = 1 + let g:ycm_autoclose_preview_window_after_insertion = 1 + '' + + (if (isNull cfg.listSnippets) then "" else "let g:UltiSnipsListSnippets = '${cfg.listSnippets}'") + else ""; + + plugins = [ "supertab" "YouCompleteMe" "ultisnips" ]; + + paths = [ pkgs.python ]; +} + diff --git a/nixos/modules/programs/vim/default.nix b/nixos/modules/programs/vim/default.nix new file mode 100644 index 0000000000000..6730a6d92be78 --- /dev/null +++ b/nixos/modules/programs/vim/default.nix @@ -0,0 +1,352 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + modules = { + general = import ./general.nix {inherit pkgs;}; + search = import ./search.nix {inherit pkgs;}; + completion = import ./completion.nix {inherit pkgs;}; + text = import ./text.nix {inherit pkgs;}; + sidebar = import ./sidebar.nix {inherit pkgs;}; + statusline = import ./statusline.nix {inherit pkgs;}; + haskell = import ./haskell.nix {inherit pkgs;}; + }; +in +let + # + # options + # + optionsHelpers = rec { + + /* + * The configuration is structured into profiles. + * Each profile can activate individual modules and specify their settings. + * One profile is marked as the default profile. + * A non-default profile is created by specifying only the overwrites of the default profile. + */ + + + # option flags that are added into each module + moduleDefaults = dp: + { enable = mkOption { + type = if dp then types.bool else types.nullOr types.bool; + default = if dp then false else null; + description = "Enable/Disable flag for the module."; + }; + leader = mkOption { + type = types.nullOr types.string; + default = null; + description = ''Prefix key for all key bindings of the module. + If null, fall back to the leader defined for the profile.''; + }; + vimrc = mkOption { + type = if dp then types.string else types.nullOr types.string; + default = if dp then "" else null; + description = "Configuration to add into vimrc."; + }; + }; + + + # Convert the stubs of a module definition into full options definitions. + # Also, add the default attributes which are present in each module. + /* m = modules definition + * dp = true if module definition is created for the default profile + */ + moduleDefinitions = m: dp: + let + /* check for reserved attributes; pass through return on success + * n = attribute to be tested + * r = value that is returned on success + */ + reservedModuleAttrs = attrNames (moduleDefaults false); + #reservedModuleAttrs = [ "enable" "leader" "vimrc" ]; + assertReserved = n: r: assert !(elem n reservedModuleAttrs); r; + # attribute set to add to the module stub + stubExtension = + let + # in the default profile module options are strings + defaultType = {type = types.string;}; + # in all other profiles module options default to null; + # null means that the value from the default profile is used + profileType = {type = types.nullOr types.string; default = null;}; + in + if dp then defaultType else profileType; + in + mapAttrs (mod_name: stub: mkOption ((assertReserved mod_name stub) // stubExtension)) m.options; + + + # create the combined config for a module + # m = module declaration, that is set of option stubs + # dp = true if module options are composed for the default profile + composeOptions = m: dp: (moduleDefinitions m dp) // (moduleDefaults dp); + + + + # option flags that are added into each profile + # dp = true for default profile + profileDefaults = dp: + let + typesPrefix = if dp then id else types.nullOr; + extension = if dp then { profiles = mkOption { + type = types.attrsOf (types.submodule {options = makeProfile false;}); + default = {}; # TODO: is this the best place to add default profiles? + description = "Vim profile configurations."; + }; } + else { parent = mkOption { + type = types.nullOr types.string; + default = null; + description = "Parent profile to base configuration on; null for default profile."; + }; }; + in + { plugins = mkOption { + type = typesPrefix (types.listOf types.string); + default = if dp then [] else null; + description = "Vim plugins to include."; + }; + paths = mkOption { + type = typesPrefix (types.listOf types.package); + default = if dp then [] else null; + description = "Pathes to make accessible for vim."; + }; + leader = mkOption { + type = typesPrefix types.string; + default = if dp then "\\\\\\\\" else null; + description = "Default leader for that profile."; + }; + vimrc = mkOption { + type = typesPrefix types.string; + default = if dp then "" else null; + description = "Configuration to add into vimrc."; + }; + exec = mkOption { + type = typesPrefix types.attrs; + default = if dp then {package=pkgs.vimPlain; bin="bin/vim";} else null; + description = "Vim derivation to base profile on"; + options = { + package = mkOption { + type = types.package; + default = pkgs.vimPlain; + description = "Vim package"; + }; + bin = mkOption { + type = types.string; + default = "bin/vim"; + description = "Vim binary"; + }; + }; + }; + name = mkOption { + type = typesPrefix types.string; + default = if dp then "vim" else null; + description = ''Name of the executable that calls vim with the profile. + In a profile other than the default profile, if null, then + the name derives automatically.''; + }; + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the creation of the vim profile."; + }; + } // extension; + + + + + + # create options of a profile + makeProfile = dp: + let + # check for reserved module names; these attribute names have special purposes + assertModNames = + let + reservedModuleNames = attrNames ((profileDefaults true) // (profileDefaults false)); + #reservedModuleNames = [ "plugins" "enable" "paths" "vimrc" "profiles" "leader" "exec" "name" "parent" ]; + in + mods: assert !(any id ( map (mod_name: elem mod_name reservedModuleNames) (attrNames mods))); mods; + in + # turn the stubs of all modules into mkOptions, and add the profile specific mkOptions + mapAttrs (_: m: composeOptions m dp) (assertModNames modules) // profileDefaults dp; + + }; + + + + + + # + # configuration + # + configHelpers = rec { + + # merge configs, where p_cfg is overwriting d_cfg + # d_cfg = default config + # p_cfg = profile config + mergeConfig = d_cfg: p_cfg: + let + profileChain = + let + chain = list: + let + head = elemAt list 0; + in + if isNull head.parent then list else chain ((singleton (getAttr head.parent d_cfg.profiles)) ++ list); + in + chain [p_cfg]; + # filter out options in profiles that should fall back to the default profile, + # that leaves only the options which are overwriting the default profile + filterOverwrites = cfg: + let + filteredModules = + let + moduleOverwrites = mod_name: mod_cfg: {${mod_name} = (filterAttrs (n: v: !(isNull v)) mod_cfg);}; + in fold (a: b: a // b) {} (map (mod_name: moduleOverwrites mod_name (getAttr mod_name cfg)) (attrNames modules)); + in filterAttrs (n: v: n=="name" || n=="enable" || n=="parent" || !(isNull v)) (cfg // filteredModules); + in + # pt_cfg: parent config + # o_cfg: overwrite config + foldl (pt_cfg: o_cfg: recursiveUpdate pt_cfg (filterOverwrites o_cfg)) d_cfg profileChain; + + + # evaluate the plugin configuration and extract the vimrc content, the plugins and the paths + evaluateConfig = cfg: + let + # get modules which are enabled + enabledModules = filterAttrs (n: _: (getAttr n cfg).enable) modules; + + # helper function + uniqList' = list: uniqList {inputList=list;}; + wrapString = s: if elem s ["" "\n"] then [] else [s]; + + # update the options of the module (m: v:) with the values from the configuration cfg + moduleCfgs = m: v: + let + # configuration that corresponds to the module m + m_cfg = getAttr m cfg; + # option delarations of the module m + m_options = v.options; + in + # transform an attribute of "" to null + mapAttrs (a: _: let v=getAttr a m_cfg; in if v=="" then null else v) m_options; + + setLeaderCmd = leader: ["let mapleader=\"${leader}\""]; + + leader = m: v: + let + # configuration that corresponds to the module m + m_cfg = getAttr m cfg; + in + setLeaderCmd (if isNull m_cfg.leader then cfg.leader else m_cfg.leader); + + commentLine = name: ["\n\">>> nix configuration module '${name}'"]; + in + { + # vimrc content of the profile + # INFO: the content of profile.vimrc goes first! + # In contrast, the content of profile.module.vimrc goes after the generated content of each module. + vimrc = + let + # INFO: wrap text sniplets into lists so that no unnecessary \n are added + sniplets = m: v: flatten ((commentLine m) ++ (leader m v) ++ (wrapString (v.vimrc (moduleCfgs m v))) ++ (wrapString (getAttr m cfg).vimrc)); + in concatStringsSep "\n" ((setLeaderCmd cfg.leader) ++ (wrapString cfg.vimrc) ++ (flatten (mapAttrsFlatten sniplets enabledModules))); + # get a list of all plugins that are enabled for the profile + plugins = uniqList' (cfg.plugins ++ (flatten (mapAttrsFlatten (_:v: v.plugins) enabledModules))); + # get a list of all paths that have to be included in the profile + paths = uniqList' (cfg.paths ++ (flatten (mapAttrsFlatten (_:v: v.paths) enabledModules))); + }; + + + # write content into a file in the nix store + createVimDir = vimExcePackage: profile_name: vimrcContent: plugins: + let + pluginPackages = map (pluginName: getAttr pluginName pkgs.vimPlugins) plugins; + rtps = + let + toPluginPaths = pluginDeriv: "${pluginDeriv}/share/vim-plugins/${pluginDeriv.path}/"; + in + concatStringsSep " " (map toPluginPaths pluginPackages); + in + pkgs.stdenv.mkDerivation rec { + name = if isNull profile_name then "vimrc" else "vimrc-${profile_name}"; + buildInputs = pluginPackages; + phases = ["installPhase"]; + installPhase = '' + mkdir $out + ln -s ${vimExcePackage}/share/vim/* $out/. + rm -f $out/vimrc + echo "set nocompatible" > $out/vimrc + echo "" >> $out/vimrc + for f in ${rtps}; do echo "set rtp^=$f" >> $out/vimrc; done + for f in ${rtps}; do if [ -d $f/after ]; then echo "set rtp+=$f/after" >> $out/vimrc; fi; done + echo "" >> $out/vimrc + cat >> $out/vimrc <\ ,trail:-,extends:>,precedes:<,nbsp:+ + endif + + " Configure backspace so it acts as it should act + set backspace=eol,start,indent + set whichwrap+=<,>,h,l + + " Don't redraw while executing macros (good performance config) + set lazyredraw + + + " Return to last edit position when opening files (You want this!) + augroup last_edit + autocmd! + autocmd BufReadPost * + \ if line("'\"") > 0 && line("'\"") <= line("$") | + \ exe "normal! g\`\"" | + \ endif + augroup END + " Remember info about open buffers on close + set viminfo^=% + + '' + (if isNull cfg.spell then "" else '' + " Toggle and untoggle spell checking + map ${cfg.spell} :setlocal spell! + ''); + + plugins = []; + + paths = []; +} + diff --git a/nixos/modules/programs/vim/haskell.nix b/nixos/modules/programs/vim/haskell.nix new file mode 100644 index 0000000000000..165c9d974788f --- /dev/null +++ b/nixos/modules/programs/vim/haskell.nix @@ -0,0 +1,259 @@ +{pkgs,...}: +{ + ###### interface + options = { + getType = { + default = "ht"; + description = "Keymap for getting Haskell type under cursor."; + }; + + insertType = { + default = "hi"; + description = "Keymap to query and to insert Haskell type under cursor."; + }; + + check = { + default = "hc"; + description = "Keymap to trigger syntax checking."; + }; + + lint = { + default = "hl"; + description = "Keymap to trigger code linting."; + }; + + error = { + default = "he"; + description = "Keymap to open window with errors and warnings."; + }; + + clear = { + default = ""; + description = "Keymap to clear and close all preview or helper windows."; + }; + + hoogle = { + default = "hh"; + description = "Keymap to hoogle word under cursor."; + }; + + hoogleInfo = { + default = "ho"; + description = "Keymap to hoogle infos for word under cursor."; + }; + + hooglePrompt = { + default = "hH"; + description = "Keymap to get hoogle prompt."; + }; + + hoogleInfoPrompt = { + default = "hO"; + description = "Keymap to get hoogle info prompt."; + }; + + pointfree = { + default = "h."; + description = "Keymap to convert selection into pointfree style."; + }; + + pointful = { + default = "h>"; + description = "Keymap to convert selection into pointful style."; + }; + + }; + + ###### implementation + + vimrc = cfg: '' + filetype on + filetype plugin on + filetype indent on + setfiletype haskell + + + " vim2hs: Pretty unicode haskell symbols + let g:haskell_conceal_wide = 1 + let g:haskell_conceal_enumerations = 1 + " alterantive to vim2hs would be hasksyn + + " Enable some tabular presets for Haskell + let g:haskell_tabular = 1 + + + " integrate necoghc into completion; should work with YouCompleteMe; necoghc requires Cabal, which is in ghc, in paths below + setlocal omnifunc=necoghc#omnifunc + let g:ycm_semantic_triggers = {'haskell' : ['.']} + " Show types in completion suggestions + let g:necoghc_enable_detailed_browse = 1 + + '' + (if isNull cfg.getType then "" else '' + " Type of expression under cursor + nmap ${cfg.getType} :HdevtoolsType + '') + (if isNull cfg.insertType then "" else '' + " Insert type of expression under cursor + nmap ${cfg.insertType} :GhcModTypeInsert + + '') + (if (isNull cfg.check) && (isNull cfg.lint) then "" else '' + let g:syntastic_mode_map = { 'mode': 'passive', 'passive_filetypes': ['haskell'] } + let g:syntastic_enable_signs = 1 + let g:syntastic_aggregate_errors = 1 + let g:syntastic_id_checkers = 0 + let g:syntastic_sort_aggregated_errors = 0 + let g:syntastic_haskell_checkers = [ 'hdevtools', 'hlint' ] + "let g:syntastic_ignore_files = [] + + '') + (if isNull cfg.check then "" else '' + " GHC errors and warnings + nmap ${cfg.check} :SyntasticCheck hdevtools + '') + (if isNull cfg.lint then "" else '' + " Haskell Lint + nmap ${cfg.lint} :SyntasticCheck hlint + '') + (if (isNull cfg.check) && (isNull cfg.lint) && (isNull cfg.error) then "" else '' + " show syntastic error window + nmap ${cfg.error} :Errors + + '') + (if isNull cfg.hoogle then "" else '' + " Hoogle the word under the cursor + nnoremap ${cfg.hoogle} :Hoogle + + '') + (if isNull cfg.hoogleInfo then "" else '' + " Hoogle for detailed documentation (e.g. "Functor") + nnoremap ${cfg.hoogleInfo} :HoogleInfo + + '') + (if isNull cfg.hooglePrompt then "" else '' + " Hoogle and prompt for input + nnoremap ${cfg.hooglePrompt} :Hoogle + + '') + (if isNull cfg.hoogleInfoPrompt then "" else '' + " Hoogle for detailed documentation and prompt for input + nnoremap ${cfg.hoogleInfoPrompt} :HoogleInfo + + '') + (if (isNull cfg.check) && (isNull cfg.lint) && (isNull cfg.hoogle) && (isNull cfg.hoogleInfo) + && (isNull cfg.hooglePrompt) && (isNull cfg.hoogleInfoPrompt) && (isNull cfg.clear) then "" else '' + " clear/reset + map ${cfg.clear} :noh:GhcModTypeClear:SyntasticReset:HdevtoolsClear:HoogleClose + + + '') + (if isNull cfg.pointfree then "" else '' + " pointfree + function! Pointfree() + call setline('.', split(system('pointfree '.shellescape(join(getline(a:firstline, a:lastline), "\n"))), "\n")) + endfunction + vnoremap ${cfg.pointfree} :call Pointfree() + + '') + (if isNull cfg.pointful then "" else '' + " pointful + function! Pointful() + call setline('.', split(system('pointful '.shellescape(join(getline(a:firstline, a:lastline), "\n"))), "\n")) + endfunction + vnoremap ${cfg.pointful} :call Pointful() + '') + '' + + let g:ycm_server_use_vim_stdout = 1 + let g:ycm_server_log_level = 'debug' + + function! TestVimproc() + let l:ghcmod = vimproc#system(['ghc-mod','version']) + let l:m = matchlist(l:ghcmod, 'version \(\d\+\)\.\(\d\+\)\.\(\d\+\)') + let s:ghc_mod_version = l:m[1 : 3] + call map(s:ghc_mod_version, 'str2nr(v:val)') + return s:ghc_mod_version + endfunction + + + + " configure cscope for use with Haskell + set cscopeprg=hscope + + " add the database pointed to by environment variable + " suppress 'duplicate connection' error + set nocscopeverbose + if !empty("$NIX_HASKELL_HSCOPE") + cs add $NIX_HASKELL_HSCOPE + " else add any cscope database in current directory + elseif filereadable("hscope.out") + cs add hscope.out + endif + set cscopeverbose + + " add haskell tags file to by environment variable + if !empty("$NIX_HASKELL_HTAGS") + set tags=$NIX_HASKELL_HTAGS + " else add haskell tags in the current directory + elseif filereadable("htags") + set tags=htags + endif + + + " Tagbar settings + let g:tagbar_type_haskell = { + \ 'ctagsbin' : 'lushtags', + \ 'ctagsargs' : '--ignore-parse-error --', + \ 'kinds' : [ + \ 'm:module:0', + \ 'e:exports:1', + \ 'i:imports:1', + \ 't:declarations:0', + \ 'd:declarations:1', + \ 'n:declarations:1', + \ 'f:functions:0', + \ 'c:constructors:0' + \ ], + \ 'sro' : '.', + \ 'kind2scope' : { + \ 'd' : 'data', + \ 'n' : 'newtype', + \ 'c' : 'constructor', + \ 't' : 'type' + \ }, + \ 'scope2kind' : { + \ 'data' : 'd', + \ 'newtype' : 'n', + \ 'constructor' : 'c', + \ 'type' : 't' + \ } + \ } + + + "let g:tagbar_type_haskell = { + " \ 'ctagsbin' : 'hasktags', + " \ 'ctagsargs' : '-x -c -o-', + " \ 'kinds' : [ + " \ 'm:modules:0:1', + " \ 'd:data: 0:1', + " \ 'd_gadt: data gadt:0:1', + " \ 't:type names:0:1', + " \ 'nt:new types:0:1', + " \ 'c:classes:0:1', + " \ 'cons:constructors:1:1', + " \ 'c_gadt:constructor gadt:1:1', + " \ 'c_a:constructor accessors:1:1', + " \ 'ft:function types:1:1', + " \ 'fi:function implementations:0:1', + " \ 'o:others:0:1' + " \ ], + " \ 'sro' : '.', + " \ 'kind2scope' : { + " \ 'm' : 'module', + " \ 'c' : 'class', + " \ 'd' : 'data', + " \ 't' : 'type' + " \ }, + " \ 'scope2kind' : { + " \ 'module' : 'm', + " \ 'class' : 'c', + " \ 'data' : 'd', + " \ 'type' : 't' + " \ } + "\ } + ''; + + plugins = [ "hdevtools" "ghcmod" "vimproc" "syntastic" "hoogle" "stylishHaskell" "necoGhc" "vim2hs" "hasksyn" "haskellConceal" ]; + + paths = with pkgs; [ haskellPackages.ghc haskellPackages.hdevtools haskellPackages.ghcMod haskellPackages.stylishHaskell + haskellPackages.hlint haskellPackages.pointfree haskellPackages.pointful haskellPackages.hoogle + haskellPackages.lushtags coreutils haskellPackages.c2hs ]; +} + diff --git a/nixos/modules/programs/vim/search.nix b/nixos/modules/programs/vim/search.nix new file mode 100644 index 0000000000000..16d6310257bcf --- /dev/null +++ b/nixos/modules/programs/vim/search.nix @@ -0,0 +1,179 @@ +{pkgs,...}: +{ + ###### interface + options = { + symbol = { + default = "ss"; + description = "Find all references to the token under cursor."; + }; + + global = { + default = "sg"; + description = "Find global definition(s) of the token under cursor."; + }; + + calls = { + default = "sc"; + description = "Find all calls to the function name under cursor."; + }; + + text = { + default = "st"; + description = "Find all instances of the text under cursor."; + }; + + egrep = { + default = "se"; + description = "Egrep search for the word under cursor."; + }; + + file = { + default = "sf"; + description = "Open the filename under cursor."; + }; + + includes = { + default = "si"; + description = "Find files that include the filename under cursor."; + }; + + called = { + default = "sd"; + description = "Find functions that function under cursor calls."; + }; + + fuzzy = { + default = "s"; + description = "Fuzzy find files."; + }; + + selectionForward = { + default = "*"; + description = "Visual mode forward search for the current selection."; + }; + + selectionBackward = { + default = "#"; + description = "Visual mode backward search for the current selection."; + }; + }; + + + ###### implementation + + vimrc = cfg: '' + " Turn on the WiLd menu + set wildmenu + " Tab-complete files up to longest unambiguous prefix + set wildmode=list:longest,full + + " Ignore compiled files + set wildignore=*.o,*~,*.pyc,.git\*,.hg\*,.svn\* + + " Ignore case when searching + set ignorecase + + " When searching try to be smart about cases + set smartcase + + " Highlight search results + set hlsearch + + " Makes search act like search in modern browsers + set incsearch + + " Show matching brackets when text indicator is over them + set showmatch + " How many tenths of a second to blink when matching brackets + set mat=2 + + + '' + (if isNull cfg.fuzzy then "" else '' + " Fuzzy find files + nnoremap ${cfg.fuzzy} :CtrlP + '') + '' + let g:ctrlp_max_files=0 + + " For regular expressions turn magic on + set magic + + + + '' + (if !((isNull cfg.selectionForward) || (isNull cfg.selectionBackward)) then "" else '' + " Visual mode pressing * or # searches for the current selection + " From an idea by Michael Naumann + + function! VisualSelection(direction, extra_filter) range + let l:saved_reg = @" + execute "normal! vgvy" + + let l:pattern = escape(@", '\\/.*$^~[]') + let l:pattern = substitute(l:pattern, "\n$", "", "") + + if a:direction == 'b' + execute "normal ?" . l:pattern . "^M" + elseif a:direction == 'gv' + call CmdLine("vimgrep " . '/'. l:pattern . '/' . ' **/*.' . a:extra_filter) + elseif a:direction == 'replace' + call CmdLine("%s" . '/'. l:pattern . '/') + elseif a:direction == 'f' + execute "normal /" . l:pattern . "^M" + endif + + let @/ = l:pattern + let @" = l:saved_reg + endfunction + '') + '' + + '' + (if isNull cfg.selectionForward then "" else '' + " Forward search for current selection + vnoremap ${cfg.selectionForward} :call VisualSelection('f', "") + '') + (if isNull cfg.selectionBackward then "" else '' + " Forward search for current selection + vnoremap ${cfg.selectionBackward} :call VisualSelection('b', "") + '') + '' + + + + " General CScope and CTags search settings + " detailed, e.g. language specific settings are defined in the corresponding Nix configured modules + + " use both cscope and ctag + set cscopetag + " search ctags first + set cscopetagorder=1 + set csverb + + '' + (if isNull cfg.symbol then "" else '' + " Cscope/Ctags search for symbol or reference + nnoremap ${cfg.symbol} :cs find s =expand("") + '') + (if isNull cfg.global then "" else '' + " Cscope/Ctags search for global definition + nnoremap ${cfg.global} :cs find g =expand("") + '') + (if isNull cfg.calls then "" else '' + " Cscope/Ctags search for calls to function name + nnoremap ${cfg.calls} :cs find c =expand("") + '') + (if isNull cfg.text then "" else '' + " Cscope/Ctags search for instances of text + nnoremap ${cfg.text} :cs find t =expand("") + '') + (if isNull cfg.egrep then "" else '' + " Cscope/Ctags egrep search for text + nnoremap ${cfg.egrep} :cs find e =expand("") + '') + (if isNull cfg.file then "" else '' + " Cscope/Ctags open file + nnoremap ${cfg.file} :cs find f =expand("") + '') + (if isNull cfg.includes then "" else '' + " Cscope/Ctags find files that include filename + nnoremap ${cfg.includes} :cs find i ^=expand("")$ + '') + (if isNull cfg.called then "" else '' + " Cscope/Ctags search for functions called by function + nnoremap ${cfg.called} :cs find d =expand("") + ''); + + plugins = [ "ctrlp" ]; + + paths = []; +} + + + diff --git a/nixos/modules/programs/vim/sidebar.nix b/nixos/modules/programs/vim/sidebar.nix new file mode 100644 index 0000000000000..edc583d6171a5 --- /dev/null +++ b/nixos/modules/programs/vim/sidebar.nix @@ -0,0 +1,62 @@ +{pkgs, ...}: +{ + ###### interface + options = { + toggleUndo = { + default = "tu"; + description = "Keymap to toggle undo sidebar."; + }; + + toggleTag = { + default = "tt"; + description = "Keymap to toggle tags sidebar."; + }; + + toggleFile = { + default = "tf"; + description = "Keymap to toggle file tree sidebar."; + }; + }; + + + ###### implementation + vimrc = cfg: + '' + " Add a bit extra margin to the left + set foldcolumn=1 + + '' + (if isNull cfg.toggleUndo then "" else '' + " Show undo tree + nmap ${cfg.toggleUndo} :GundoToggle + + '') + (if isNull cfg.toggleTag then "" else '' + " Show tags + map ${cfg.toggleTag} :TagbarToggle + '') + '' + + " NERDTree + " Close nerdtree after a file is selected + let NERDTreeQuitOnOpen = 1 + + '' + (if isNull cfg.toggleFile then "" else '' + function! IsNERDTreeOpen() + return exists("t:NERDTreeBufName") && (bufwinnr(t:NERDTreeBufName) != -1) + endfunction + + function! ToggleFindNerd() + if IsNERDTreeOpen() + exec ':NERDTreeToggle' + else + exec ':NERDTreeFind' + endif + endfunction + + " If nerd tree is closed, find current file, if open, close it + nmap ${cfg.toggleFile} :call ToggleFindNerd() + ''); + + plugins = [ "nerdtree" "tagbar" "gundo" ]; + + paths = []; +} + diff --git a/nixos/modules/programs/vim/statusline.nix b/nixos/modules/programs/vim/statusline.nix new file mode 100644 index 0000000000000..302d21777465d --- /dev/null +++ b/nixos/modules/programs/vim/statusline.nix @@ -0,0 +1,22 @@ +{pkgs, ...}: +{ + ###### interface + options = { + }; + + + ###### implementation + vimrc = cfg: + '' + " Always show the status line + set laststatus=2 + + " Height of the command bar + set cmdheight=1 + ''; + + plugins = [ "airline" ]; + + paths = []; +} + diff --git a/nixos/modules/programs/vim/text.nix b/nixos/modules/programs/vim/text.nix new file mode 100644 index 0000000000000..d7e9f7ef24216 --- /dev/null +++ b/nixos/modules/programs/vim/text.nix @@ -0,0 +1,90 @@ +{pkgs, ...}: +{ + ###### interface + options = { + alignEqual = { + default = "a="; + description = "Keymap to align on equal sign."; + }; + + alignComma = { + default = "a,"; + description = "Keymap to align on comma."; + }; + + alignBar = { + default = "a"; + description = "Keymap to align on '|'."; + }; + + alignPrompt = { + default = "ap"; + description = "Prompt for alignment key, and then align on that key."; + }; + + deleteWS = { + default = "dw"; + description = "Keymap to delete trailing whitespace."; + }; + }; + + + ###### implementation + vimrc = cfg: + '' + " Use spaces instead of tabs + set expandtab + + " Be smart when using tabs ;) + set smarttab + + " 1 tab == 2 spaces + set shiftwidth=2 + set tabstop=2 + + " Linebreak on 500 characters + set lbr + set tw=500 + + " Auto indent + set ai + " Smart indent + set si + " Wrap lines + set wrap + + + " Stop Align plugin from forcing its mappings on us + let g:loaded_AlignMapsPlugin=1 + '' + (if isNull cfg.alignEqual then "" else '' + " Align on equal signs + map ${cfg.alignEqual} :Align = + '') + (if isNull cfg.alignComma then "" else '' + " Align on commas + map ${cfg.alignComma} :Align , + '') + (if isNull cfg.alignBar then "" else '' + " Align on bars + map ${cfg.alignBar} :Align + '') + (if isNull cfg.alignPrompt then "" else '' + " Prompt for align character + map ${cfg.alignPrompt} :Align + '') + '' + + '' + (if isNull cfg.deleteWS then "" else '' + " Delete trailing white space + func! DeleteTrailingWS() + exe "normal mz" + %s/\s\+$//ge + exe "normal \`z" + endfunc + + map ${cfg.deleteWS} :call DeleteTrailingWS + ''); + + plugins = [ "tabular" "align" "commentary" ]; + + paths = []; +} + + + diff --git a/pkgs/applications/editors/vim/vimProfiles.nix b/pkgs/applications/editors/vim/vimProfiles.nix new file mode 100644 index 0000000000000..1f1075ff22c80 --- /dev/null +++ b/pkgs/applications/editors/vim/vimProfiles.nix @@ -0,0 +1,106 @@ +{ stdenv, lib, pkgs, makeWrapper, vimProfiles ? null, vimDefault ? null }: + +let + mkProfile = profile_name: profile_config: + pkgs.stdenv.mkDerivation rec { + name = if isNull profile_name + then "vim-default" + else "vimProfile-${profile_name}"; + version = pkgs.vimPlain.version; + phases = ["buildPhase" "installPhase"]; + + buildInputs = [makeWrapper profile_config.buildInputs]; + buildPhase = lib.concatStringsSep "\n" ([ ''mkdir "bin"'' ] ++ [ profile_config.buildCommand ]); + installPhase = '' + cp -r . $out + rm -f $out/env-vars + ''; + preferLocalBuild = true; + meta = { + description = '' + VimProfile allows to wrap vim with different configurations. + A configuration is composed by enabling and customizing pre-defined modules. + A module is a set of vim plugins and vimrc statements that are tightly linked. + Configurations are hierarchical and can inherit from each other. + The base of each hierarchy of configurations is a distingued default configuration. + Enabled vim configurations appear as normal package in nix, and have to be + installed accordingly. + + The vimProfiles are declared either for system wide installations in + /etc/nixos/configuration.nix, or for each user in ~/.nixuser/configuration.nix. + + An example for a vim configuration, here the content of ~/.nixuser/configuration.nix: + + { config, pkgs, ... }: + with pkgs; + { + programs.vim = + { enable = true; + leader = " "; + general.enable = true; + statusline.enable = true; + profiles = + { # Haskell configuration + hvim = + { enable = true; + exec = { package = pkgs.vim_configurable; bin = "bin/vim"; }; + search.enable = true; + # leader can be overwritten independently for each module + search.leader = "|"; + text.enable = true; + sidebar.enable = true; + # overwrite default key binding + sidebar.toggleUndo = "uu"; + completion.enable = true; + haskell.enable = true; + }; + # graphical Haskell configuration + hqvim = + { enable = true; + # set the name to call the wrapped vim + name = "Hvim"; + parent = "hvim"; + exec = { package = pkgs.qvim; bin = "bin/qvim"; }; + # just as an example that inherited modules can also be disabled + search.enable = false; + }; + # same configuration as for default, but with qvim + qvim = + { # don't make that profile installable + enable = false; + exec = { package = pkgs.qvim; bin = "bin/qvim"; }; + # if no name attribute is given, the attribute name + # of the configuration is taken: "qvim" + }; + # alias declaration for qvim + gvim = + { enable = true; + # inherit from a configuration by its attribute name + # you can inherit from a disabled configuration as well + parent = "qvim"; + # add statement to the generated vimrc; it is inserted + # before any module statement + vimrc = "" # replace "" with two single quotes + " Leader key timeout + set tm=2000 + ""; + }; + }; + }; + } + + The declaration above defines the following packages in nix: + "vimProfile.hvim", "vimProfile.hqvim" and "vimProfile.gvim". + When the default profile is enabled, it is automatically installed with + the package "vim". + An un-wrapped vim package is always available with "vimPlain". + ''; + maintainers = [ stdenv.lib.maintainers.tstrobel ]; + platforms = stdenv.lib.platforms.unix; + }; + }; +in + if isNull vimDefault then + lib.mapAttrs mkProfile vimProfiles + else + mkProfile null vimDefault diff --git a/pkgs/misc/vim-plugins/default.nix b/pkgs/misc/vim-plugins/default.nix index 8dd17c074a7af..7177f10e3a231 100644 --- a/pkgs/misc/vim-plugins/default.nix +++ b/pkgs/misc/vim-plugins/default.nix @@ -141,6 +141,8 @@ in rec # TODO: support llvm based C completion, See README of git repository installPhase = ":"; + path = "YouCompleteMe"; + meta = { description = "fastest non utf-8 aware word and C completion engine for Vim"; homepage = http://github.com/Valloric/YouCompleteMe; @@ -323,7 +325,7 @@ in rec }; vimproc = simpleDerivation rec { - version = "7788b5f934bc7460c1e9134b51fe5690b21de83c"; + version = "21a79bf4edca3ae97555df3fc729d208c7e19b9c"; name = "vimproc-${version}"; meta = with stdenv.lib; { @@ -338,7 +340,7 @@ in rec src = fetchgit { url = "https://github.com/Shougo/vimproc.vim.git"; rev = "${version}"; - sha256 = "0ahmnzccf5rv8rwg7b6pfgxh8pcmq955aznjv64slyh0mjqmh6jl"; + sha256 = "16mlrhmd1hq4rgg7bl9gajhb4nmn1x8jxfaxfwiy2bm1phgljgq0"; }; buildInputs = [ which ]; @@ -573,6 +575,7 @@ in rec rev = "0c4e94281e57c475752e799adc261f7d5e4ab124"; sha256 = "f6a085f7b8198747fae3fff0bc38e4d030e5c97aaeb84958fbf96fa658bbe862"; }; + patches = [ (fetchurl { url = "https://github.com/eagletmt/ghcmod-vim/pull/57.diff"; md5 = "cafbb9f725afbba26b52b6c3344ee89a"; }) ]; path = "ghcmod"; }; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 9f6aedd4e3932..f9b0f28eb6a12 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -28,12 +28,17 @@ # ~/.nixpkgs/config.nix. config ? null +, # Allow a nixuser configuration attribute set to be passed in as an + # argument. Otherwise, it's read from $NIXUSER_CONFIG or + # ~/.nixuser/configuration.nix. + nixuserConfig ? null + , crossSystem ? null , platform ? null }: -let config_ = config; platform_ = platform; in # rename the function arguments +let config_ = config; nixuserConfig_ = nixuserConfig; platform_ = platform; in # rename the function arguments let @@ -42,7 +47,7 @@ let # The contents of the configuration file found at $NIXPKGS_CONFIG or # $HOME/.nixpkgs/config.nix. # for NIXOS (nixos-rebuild): use nixpkgs.config option - config = + nixpkgsConfig = let toPath = builtins.toPath; getEnv = x: if builtins ? getEnv then builtins.getEnv x else ""; @@ -67,6 +72,42 @@ let then configExpr { inherit pkgs; } else configExpr; + + # Merge the content found in the configuration file found at $NIXUSER_CONFIG or + # $HOME/.nixuser/configuration.nix with nixpkgsConfig. + # TODO: Think about: for NIXOS (nixos-rebuild): use nixpkgs..config option + config = + let + toPath = builtins.toPath; + getEnv = x: if builtins ? getEnv then builtins.getEnv x else ""; + pathExists = name: + builtins ? pathExists && builtins.pathExists (toPath name); + + configFile = getEnv "NIXUSER_CONFIG"; + homeDir = getEnv "HOME"; + configFile2 = homeDir + "/.nixuser/configuration.nix"; + + configuration = + if nixuserConfig_ != null && pathExists nixuserConfig_ then [(toPath nixuserConfig_)] + else if configFile != "" && pathExists configFile then [(toPath configFile)] + else if homeDir != "" && pathExists configFile2 then [(toPath configFile2)] + else []; + + nixpkgs_options_base = [ ../../nixos/modules/misc/nixpkgs.nix ]; + nixuser_modules = import ../../nixos/modules/nixuser-module-list.nix; + + nixpkgsConfig_ = [{nixpkgs.config = nixpkgsConfig;}]; + in + if configuration == [] + then nixpkgsConfig + else + (lib.evalModules { + modules = configuration ++ nixpkgs_options_base ++ nixuser_modules ++ nixpkgsConfig_; + args = {inherit pkgs;}; + check = true; + }).config.nixpkgs.config; + + # Allow setting the platform in the config file. Otherwise, let's use a reasonable default (pc) platformAuto = let @@ -531,6 +572,8 @@ let grc = callPackage ../tools/misc/grc { }; + nixuser = callPackage ../tools/package-management/nixuser { }; + otool = callPackage ../os-specific/darwin/otool { }; pass = callPackage ../tools/security/pass { @@ -3143,6 +3186,9 @@ let # Import Haskell infrastructure. + # NOTE: If there would be an opposite for recurseIntoAttrs like e.g. dontListAttrs, + # then haskell should be prefixed with it. + # haskell can not be installed directly. haskell = let pkgs_ = pkgs // { gmp = gmp.override { withStatic = true; }; }; callPackage = newScope pkgs_; newScope = extra: lib.callPackageWith (pkgs_ // pkgs_.xorg // extra); @@ -10450,7 +10496,11 @@ let flup = pythonPackages.flup; }; - vim = callPackage ../applications/editors/vim { }; + vimPlain = callPackage ../applications/editors/vim { }; + + vim = if isNull config.vim.profile + then vimPlain + else callPackage ../applications/editors/vim/vimProfiles.nix { vimProfiles=null; vimDefault = config.vim.profile; }; macvim = callPackage ../applications/editors/vim/macvim.nix { }; @@ -10488,6 +10538,8 @@ let flags = [ "python" "X11" ]; # only flag "X11" by now }); + vimProfiles = callPackage ../applications/editors/vim/vimProfiles.nix { vimProfiles = config.vimProfiles; }; + vimpc = callPackage ../applications/audio/vimpc { }; virtviewer = callPackage ../applications/virtualization/virt-viewer { @@ -12122,7 +12174,7 @@ let viewnior = callPackage ../applications/graphics/viewnior { }; - vimPlugins = recurseIntoAttrs (callPackage ../misc/vim-plugins { }); + vimPlugins = recurseIntoAttrs (callPackage ../misc/vim-plugins { vim=vimPlain; }); vimprobable2 = callPackage ../applications/networking/browsers/vimprobable2 { webkit = webkitgtk2; From 61cd168db07b9621d0140abfe6a890681425a014 Mon Sep 17 00:00:00 2001 From: Thomas Strobel Date: Thu, 9 Oct 2014 10:48:01 +0200 Subject: [PATCH 2/4] Fix: Completely remove temporary package definition. --- pkgs/top-level/all-packages.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index f9b0f28eb6a12..316ae7afef0db 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -572,8 +572,6 @@ let grc = callPackage ../tools/misc/grc { }; - nixuser = callPackage ../tools/package-management/nixuser { }; - otool = callPackage ../os-specific/darwin/otool { }; pass = callPackage ../tools/security/pass { From 669c77143bb6c4517ca68c32f29769ce1e36b024 Mon Sep 17 00:00:00 2001 From: Thomas Strobel Date: Thu, 9 Oct 2014 11:15:58 +0200 Subject: [PATCH 3/4] Fix: "~/.nixuser/configuration.nix" no longer has to exist. --- pkgs/top-level/all-packages.nix | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 316ae7afef0db..76960d9c2abc7 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -98,14 +98,11 @@ let nixpkgsConfig_ = [{nixpkgs.config = nixpkgsConfig;}]; in - if configuration == [] - then nixpkgsConfig - else - (lib.evalModules { - modules = configuration ++ nixpkgs_options_base ++ nixuser_modules ++ nixpkgsConfig_; - args = {inherit pkgs;}; - check = true; - }).config.nixpkgs.config; + (lib.evalModules { + modules = configuration ++ nixpkgs_options_base ++ nixuser_modules ++ nixpkgsConfig_; + args = {inherit pkgs;}; + check = true; + }).config.nixpkgs.config; # Allow setting the platform in the config file. Otherwise, let's use a reasonable default (pc) From 6b5a3294ce175fca6a4dded5869c13467d1b1175 Mon Sep 17 00:00:00 2001 From: Thomas Strobel Date: Thu, 9 Oct 2014 12:39:51 +0200 Subject: [PATCH 4/4] Add default profiles. --- nixos/modules/programs/vim/default.nix | 43 +++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/nixos/modules/programs/vim/default.nix b/nixos/modules/programs/vim/default.nix index 6730a6d92be78..17ed14a26581a 100644 --- a/nixos/modules/programs/vim/default.nix +++ b/nixos/modules/programs/vim/default.nix @@ -12,6 +12,29 @@ let statusline = import ./statusline.nix {inherit pkgs;}; haskell = import ./haskell.nix {inherit pkgs;}; }; + defaultConfig = { + #TODO: will overwrite all vim configurations :), so warn people about it first! enable = true; + leader = " "; + general.enable = true; + statusline.enable = true; + profiles = + { hvim = + { enable = true; + exec = { package = pkgs.vim_configurable; bin = "bin/vim"; }; + search.enable = true; + text.enable = true; + sidebar.enable = true; + completion.enable = true; + haskell.enable = true; + }; + hqvim = + { enable = true; + name = "Hvim"; + parent = "hvim"; + exec = { package = pkgs.qvim; bin = "bin/qvim"; }; + }; + }; + }; in let # @@ -257,7 +280,7 @@ let # write content into a file in the nix store - createVimDir = vimExcePackage: profile_name: vimrcContent: plugins: + createVimDir = vimExecPackage: profile_name: vimrcContent: plugins: let pluginPackages = map (pluginName: getAttr pluginName pkgs.vimPlugins) plugins; rtps = @@ -272,7 +295,7 @@ let phases = ["installPhase"]; installPhase = '' mkdir $out - ln -s ${vimExcePackage}/share/vim/* $out/. + ln -s ${vimExecPackage}/share/vim/* $out/. rm -f $out/vimrc echo "set nocompatible" > $out/vimrc echo "" >> $out/vimrc @@ -344,9 +367,19 @@ in defaultCfg = (filterAttrs (n: v: n != "profile") config.programs.vim); enabledProfiles = filterAttrs (n: v: v.enable) config.programs.vim.profiles; - profilesConfig = mapAttrs (p_name: p_cfg: configHelpers.defineWrapper p_name (configHelpers.mergeConfig defaultCfg p_cfg)) enabledProfiles; - defaultConfig = if defaultCfg.enable then (configHelpers.defineWrapper null defaultCfg) else null; + vimProfilesConfig = mapAttrs (p_name: p_cfg: configHelpers.defineWrapper p_name (configHelpers.mergeConfig defaultCfg p_cfg)) enabledProfiles; + vimDefaultConfig = if defaultCfg.enable then (configHelpers.defineWrapper null defaultCfg) else null; + + # reduce the priority of the default configuration; + vimProfileOptionDefaults = + let + default = mapAttrs (n: value: mkDefault value) defaultConfig; + profiles = (mapAttrsRecursive (path: value: if ((length path) <= 2) then mkDefault value else value) defaultConfig.profiles); + in + if (hasAttr "profiles" defaultConfig) then + default // { profiles = (mkDefault profiles); } + else default; in - {nixpkgs.config.vim.profile = defaultConfig; nixpkgs.config.vimProfiles = profilesConfig;}; + {nixpkgs.config.vim.profile = vimDefaultConfig; nixpkgs.config.vimProfiles = vimProfilesConfig; programs.vim = vimProfileOptionDefaults;}; }