TransWikia.com

Is there a best practice to fold a vimrc file

Vi and Vim Asked by statox on December 20, 2020

I recently realized that my vimrc is now more than 400 lines long (which IMO is too much I’ll try to reduce that) and to make it easier to navigate, read and edit it I decided to investigate the concept of folding in vim (which I wasn’t familiar with).

  • I tried to set the folding method to indent but I didn’t like the result (It was too messy mostly because a great part of my vimrc isn’t really indented).
  • I also tried to set foldmethod to expr and syntax but I wasn’t able to fold anything properly.
  • Here using diff as folding method doesn’t seem relevant. (Or if it is I didn’t understand how to use it)
  • So for now I’m using the marker method which doesn’t totally satisfy me because of the "{{{ and "}}} markers which I found “noisy” in the file.

So I’d like to know if there are best practices or common guidelines about properly folding a vimrc.

Note 1: As we all know SO isn’t a forum and isn’t made to collect personal opinions and that’s not what I’m looking for: of course I guess some people has their preferences but I’d like to know why using markers (for example) improves the readability more than using indent.

Note 2: Also my main goal is to make my vimrc as clear as possible so if other best practices exists to create a nice vimrc I’m curious about it.

Edit 1: I should have precised that my vimrc is already subdivided in sections (and sometimes subsection) the main ones being

  • general options
  • plugins (containing a subsection for each plugin and its configuration)
  • mappings
  • navigation (also containing subsection)
  • color
  • etc…

And it’s this structure which made me thought of folding: I feel that being able to output only the section I’m interested in at a certain point is something pretty convenient.

Edit 2: Answer mentioning subdivisions of the vimrc in several files are valid, but as a personal preference I’d rather use folding because I think it is easier to maintain only one file in the git repo containing my dotfiles. That is only a personal preference and I’m aware that is it possible to also use this approach but I’d prefer to use folding.

8 Answers

Expanding the idea of @Peter Rincker and @go2null. If you do not want to set the folding options in Vim modeline. You can use the following autocmd to set the folding method and folding expression.

augroup vim_folding
    autocmd!
    autocmd FileType vim set foldmethod=expr foldlevel=0
    " note that double quote in foldexpr has to be escaped with backslash
    autocmd FileType vim set foldexpr=getline(v:lnum)=~'^"#'?'>'.(matchend(getline(v:lnum),'"#*')-1):'='
augroup END

I made small modifications to make the original answer to make it work as regular vim command (no need to escape colon, but double quote needs to be escaped).

If you do not like the long foldexpr string, we can define a function for that:

function! VimFolds(lnum)
    let s:cur_line = getline(a:lnum)

    if s:cur_line =~ '^"#'
        return '>' . (matchend(s:cur_line, '"#*')-1)
    else
        return '='
    endif

endfunction

Then replace the autocmd line about foldexpr to

autocmd FileType vim set foldexpr=VimFolds(v:lnum)

Answered by jdhao on December 20, 2020

Inspired by @PeterRincker's answer, I crafted the following to use ATX style headers. Add it to the end of your .vimrc

"# Folding

" Fold with ATX style headers - "# is H1, "## is H2, and so on
" vim:fdm=expr:fdl=0
" vim:fde=getline(v:lnum)=~'^"#'?'>'.(matchend(getline(v:lnum),'"#*')-1):'='

Answered by go2null on December 20, 2020

It is a good idea to first define your own categories in your .vimrc (like a list with sublists and subsublists) and add all your plugins/settings/functions to the respective categories. Combined with customized folding this can work great:

Example

The example above shows possible categories that I find helpful to structure my .vimrc. It uses the following customized fold settings:

""""""""""""""""""""""""
"  THIS IS A CATEGORY  "
""""""""""""""""""""""""
"" Autofolding .vimrc
" see http://vimcasts.org/episodes/writing-a-custom-fold-expression/
""" defines a foldlevel for each line of code
function! VimFolds(lnum)
  let s:thisline = getline(a:lnum)
  if match(s:thisline, '^"" ') >= 0
    return '>2'
  endif
  if match(s:thisline, '^""" ') >= 0
    return '>3'
  endif
  let s:two_following_lines = 0
  if line(a:lnum) + 2 <= line('$')
    let s:line_1_after = getline(a:lnum+1)
    let s:line_2_after = getline(a:lnum+2)
    let s:two_following_lines = 1
  endif
  if !s:two_following_lines
      return '='
    endif
  else
    if (match(s:thisline, '^"""""') >= 0) &&
        (match(s:line_1_after, '^"  ') >= 0) &&
        (match(s:line_2_after, '^""""') >= 0)
      return '>1'
    else
      return '='
    endif
  endif
endfunction

""" defines a foldtext
function! VimFoldText()
  " handle special case of normal comment first
  let s:info = '('.string(v:foldend-v:foldstart).' l)'
  if v:foldlevel == 1
    let s:line = ' ◇ '.getline(v:foldstart+1)[3:-2]
  elseif v:foldlevel == 2
    let s:line = '   ●  '.getline(v:foldstart)[3:]
  elseif v:foldlevel == 3
    let s:line = '     ▪ '.getline(v:foldstart)[4:]
  endif
  if strwidth(s:line) > 80 - len(s:info) - 3
    return s:line[:79-len(s:info)-3+len(s:line)-strwidth(s:line)].'...'.s:info
  else
    return s:line.repeat(' ', 80 - strwidth(s:line) - len(s:info)).s:info
  endif
endfunction

""" set foldsettings automatically for vim files
augroup fold_vimrc
  autocmd!
  autocmd FileType vim 
                    setlocal foldmethod=expr |
                    setlocal foldexpr=VimFolds(v:lnum) |
                    setlocal foldtext=VimFoldText() |
     "               set foldcolumn=2 foldminlines=2
augroup END

To define your own categories and subcategories use the following syntax:

""""""""""""""
"  Category  "
""""""""""""""
"" Subcategory
""" Subsubcategory
" Just a comment, gets ignored no matter where

The top level category can be created really easy if you use vim-snippets (e.g. with UltiSnips): Just expand the box or bbox snippet provided by vim-snippets (write box or bbox and press the expand trigger).

To toggle folds open and closed even faster by pressing space twice:

let mapleader = "<space>"
" Toggle folds
nnoremap <silent> <leader><Space> @=(foldlevel('.')?'za':"<Space>")<CR>
vnoremap <leader><space> zf

That way you have an well structured .vimrc which can be navigated easily.

Answered by cbaumhardt on December 20, 2020

If you have large functions like me, you can use this to fold your functions:

fun! MyFoldLevel(linenum)
    if ! exists('w:nextline')
        let w:nextline = 0
        let w:insideafun = 0
    endif

    if w:nextline == 1
        let w:nextline = 0
        let w:insideafun = 0
    endif

    let l:line = getline(a:linenum)

    if l:line =~# '^[[:space:]]*fun'
        let w:insideafun = 1
        return '>1'
    elseif l:line =~# '^[[:space:]]*endf'
        let w:nextline = 1
        return '<1'
    endif

    if w:insideafun == 1
        return 1
    else
        return 0
    endif
endfun

And add this modeline to your vimrc:

" vim:fde=MyFoldLevel(v:lnum):fdm=expr:

Answered by MichalH on December 20, 2020

I have the following modelines at the bottom of my vimrc which I copied from godlygeek, the author of tabular:

"" vim:fdm=expr:fdl=0
"" vim:fde=getline(v:lnum)=~'^""'?'>'.(matchend(getline(v:lnum),'""*')-2):'='

This will make any line starting with 2+ "'s a fold. The more "'s the deeper the fold. This allows you to sub divide sections if you need.

Update: As noted in a comment below the second modeline now causes an error in Vim. Here's a note from the patch that introduced the change:

Patch 8.1.1366

Problem: Using expressions in a modeline is unsafe.

Solution: Disallow using expressions in a modeline, unless the 'modelineexpr' option is set.

And, no, you can't set 'modelineexpr' from the modeline! You could try setting 'fde' as above in an autocmd (e.g. autocmd Filetype vim ...). I (B Layer) didn't test this but I did test it manually. You need to escape the "s.

set fde=getline(v:lnum)=~'^""'?'>'.(matchend(getline(v:lnum),'""*')-2):'='

Answered by Peter Rincker on December 20, 2020

You could say that "best practice" is preeminently a matter of opinion, :) but there are two approaches that (1) make obvious sense, and (2) can be applied to all config files, not only Vim's: folding by logical sections and subsections (or even deeper, if you feel brave), and splitting your config into several smaller files and :source-ing them.

I personally prefer folding because it makes things easier to access, while still giving me some hierarchy to pick up from. Folding functions and autocmds at innermost levels is also a good idea, since these make "natural" logical units. marker folding makes most sense for all this, because logical hierarchies are not necessarily reflected into indent levels, or into syntax highlighting. I also increase foldcolumn, which gives me a visual hint of where I am:

# vim: filetype=vim foldmethod=marker foldlevel=0 foldcolumn=3

On a side note, this foldtext function (a modification of a similar function by Drew Neil, IIRC) makes more sense to me than the default:

function! MyFoldText()
    let line = getline(v:foldstart)

    let nucolwidth = &foldcolumn + &number * &numberwidth
    let windowwidth = winwidth(0) - nucolwidth - 3
    let foldedlinecount = v:foldend - v:foldstart

    " expand tabs into spaces
    let chunks = split(line, "t", 1)
    let line = join(map(chunks[:-2], 'v:val . repeat(" ", &tabstop - strwidth(v:val) % &tabstop)'), '') . chunks[-1]

    let line = strpart(line, 0, windowwidth - 2 - len(foldedlinecount))
    let fillcharcount = windowwidth - len(line) - len(foldedlinecount) - 1
    return line . '...' . repeat(' ', fillcharcount) . foldedlinecount . ' '
endfunction
set foldtext=MyFoldText()

With the other approach, split files, the main problems are finding things, and switching from one file to another. A very nice way to address both is to use a plugin such as CtrlSF, CtrlP, or similar. But you're probably already using one of those anyway.

Answered by lcd047 on December 20, 2020

I use my primary vimrc as a link to several other categorized files, sourcing each as it goes along, e.g. Vim options in one file, plugin settings in another.

"--- Vim Options
source ~/.vim/config/vim_options.vim

"--- Here Be Functions!
" (need to be sourced before stuff that uses 'em)
runtime! functions/*.vim

"--- Key Mapping
source ~/.vim/config/key_mapping.vim

"--- Folding
source ~/.vim/config/folding.vim

"--- Autocmds
source ~/.vim/config/autocmds.vim

"--- We Are Plugged In!
source ~/.vim/config/plugins.vim

" vim: ft=vim fdm=marker

As a more direct answer to OP question, I do use the marker method, but off to the right side with spacing, and around more categories than individual, for the most part. I do each plugin separately though.

Answered by Cometsong on December 20, 2020

Basic best practises:

  • Divide into sections:

    • Plugins
    • Settings
    • Rebinds
  • Comment each section/rebind

  • (backup your .vimrc or _vimrc on Github)

Just my personal preference. Maybe not so much help though.

Answered by Gustav Blomqvist on December 20, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP