Vim Tips Wiki
Advertisement
Tip 1168 Printable Monobook Previous Next

created March 13, 2006 · complexity advanced · author Paul Donohue · version 6.0


I like to organize my notes/outlines/etc in a hierarchical tree format. I wanted to be able to hide branches of the hierarchy that I'm not currently working on, but I also wanted to be able to store everything in a plain text file with minimal special formatting to implement the hiding. So, I decided to use Vim's folding features for this.

Folding based on indentation is the easiest and most intuitive way to hide sections of a file with minimal special formatting.

However, I wanted a section of lines with the same indent to fold into a heading with less indent - the default behavior is to fold into the first line with the same indent:

Unfolded text:

heading A
line 1
line 2
line 3
heading B

Default Behavior (folded based on indent):

heading A
line 1
heading B

What I wanted when folded:

heading A
heading B

So, I put this in my vimrc to implement folding:

setlocal foldmethod=expr
setlocal foldexpr=(getline(v:lnum)=~'^$')?-1:((indent(v:lnum)<indent(v:lnum+1))?('>'.indent(v:lnum+1)):indent(v:lnum))
set foldtext=getline(v:foldstart)
set fillchars=fold:\ "(there's a space after that \)
highlight Folded ctermfg=DarkGreen ctermbg=Black

This will create folds based on a single-space indent (I wanted to be able to build a hierarchy many many levels deep, and using a tab for the fold indents very quickly pushed my text off the window, but I didn't want to mess with the tabstop). Blank lines are folded based on the surrounding indentation (they are not counted as a 0 indent line).

I quickly got annoyed by the default key mappings for folding/unfolding sections, so I remapped Shift-Left/Shift-Right to close/open:

nnoremap ^[[1;2C zo
inoremap ^[[1;2C <C-O>zo
nnoremap ^[[1;2D zc
inoremap ^[[1;2D <C-O>zc
" Shift-Up Shift-Down (incase Shift is held while browsing folds)
nmap ^[[1;2A <Up>
imap ^[[1;2A <Up>
nmap ^[[1;2B <Down>
imap ^[[1;2B <Down>

But, I found that the default mappings for Alt-(Arrows) and CTRL-(Arrows) sometimes caused Vim to do strange stuff, and I would occasionally hit those by accident, so I remapped those as well:

" modified arrow keys do bad things by default
" (<S-Up>, <M-Up>, <C-Up>, etc don't seem to work for me in mappings, so I had to type CTRL-V then the key sequence to
" get the escape codes you see below. Unfortunately, since the escape codes begin with an ^] (Esc) character, this
" means that 'timeoutlen' now applies to <Esc>, making <Esc> respond slower)
" Ctrl-(Up, Down, Left, Right)
noremap ^[[1;5A <Nop>
noremap! ^[[1;5A <Nop>
noremap ^[[1;5B <Nop>
noremap! ^[[1;5B <Nop>
noremap ^[[1;5D <Nop>
noremap! ^[[1;5D <Nop>
noremap ^[[1;5C <Nop>
noremap! ^[[1;5C <Nop>
" Alt-(Up, Down, Left, Right)
noremap ^[[1;3A <Nop>
noremap! ^[[1;3A <Nop>
noremap ^[[1;3B <Nop>
noremap! ^[[1;3B <Nop>
noremap ^[[1;3D <Nop>
noremap! ^[[1;3D <Nop>
noremap ^[[1;3C <Nop>
noremap! ^[[1;3C <Nop>

References

Comments

An alternative approach is "reverse indent outlining" as described at http://jfi.uchicago.edu/~tten/Vim_editor/ .


Using the fold method 'indent' is easier. With these mappings you can open or close folds easily:

inoremap <F9> <C-O>za
nnoremap <F9> za
onoremap <F9> <C-C>za

TVO is neat, but it seems to have slightly different goals in mind. The biggest difference is that it doesn't allow folding a text block within a text block. For example, with my script, you can do:

-headline
 text block 1a
 text block 1b
- text block 1c
 text block 2a
 text block 2b
 text block 1d
 text block 1e
So, when text block 2 is folded, you see:
-headline
 text block 1a
 text block 1b
+ text block 1c
 text block 1d
 text block 1e
And when text block 1 is folded, you see:
+headline

So, basically, an arbitrary line in a text block can become a headline, and another text block can be folded inside it. This isn't possible in TVO.

TVO is also limited to folds 10 levels deep, it doesn't fold text into the headline (by default - the docs seem to suggest there is an option to fold the text into the headline, but I can't seem to find it), it determines the fold level using Tabs (as I mentioned before, Tabs pushed my text off the screen way too fast, and I'd like to do folding without mucking with the tabstop), and it requires that a | be placed in front of each text block (not a big deal, but I like not having any special characters besides spaces to implement the folding, especially since TVO's use of the | is what makes it impossible to put a text block within a text block). So, TVO just isn't quite right for my purposes.


Take a look at:

They talk about a better way to do the key mappings I originally mentioned without making 'timeoutlen' apply to the <Esc> character.

I also have an outline-to-html converter which generates HTML with JavaScript so that you get a web page which can actually fold the text interactively in the page like you can fold it in Vim. (The HTML converter in TVO simply generates static HTML using

    and
  • tags) I can post that somewhere if anyone is interested.

    Based on the suggestions in the links I listed in my last message, perhaps this is a better way to do the key mappings:

    " Vim's termcap options for a bunch of modified keys are wrong (at least in xterm)
    " so, Vim does unexpected things when these key combinations are hit - the lines below should correct the problem
    " (type CTRL-V then the keys to get the actual key code escape sequences below)
    set <S-Up>=^[[1;2A
    set <S-Down>=^[[1;2B
    set <S-Right>=^[[1;2C
    set <S-Left>=^[[1;2D
    set <C-Right>=^[[1;5C
    set <C-Left>=^[[1;5D
    set <S-Home>=^[[1;2H
    set <S-End>=^[[1;2F
    set <C-Home>=^[[1;5H
    set <C-End>=^[[1;5F
    " For some reason, Vim won't let you set <C-Up> or <C-Down>, <C-PageUp> or <C-PageDown>, or <M-*> - so we'll make Vim think we're hitting other unused keys instead
    " I would like to map a lot of other modified keys as well (a lot of them do unexpected and usually bad things), but there's only so many unused keys to be commandeered
    " CTRL-Up, CTRL-Down
    set <F13>=^[[1;5A
    set <F14>=^[[1;5B
    " CTRL-PageUp, CTRL-PageDown
    set <F15>=^[[5;5~
    set <F16>=^[[6;5~
    " ALT-Home, ALT-End
    set <F17>=^[[1;3H
    set <F18>=^[[1;3F
    
    " Fold-related Mappings
    " open current fold
    nnoremap <S-Right> zo
    inoremap <S-Right> <C-O>zo
    " close current fold
    nnoremap <S-Left> zc
    inoremap <S-Left> <C-O>zc
    " incase Shift is held while browsing folds
    nmap <S-Up> <Up>
    imap <S-Up> <Up>
    nmap <S-Down> <Down>
    imap <S-Down> <Down>
    " map CTRL-(arrow keys) to <Nop>, since I don't like the expected Vim behavior even when the termcap entries are correct
    noremap <C-Right> <Nop>
    noremap! <C-Right> <Nop>
    noremap <C-Left> <Nop>
    noremap! <C-Left> <Nop>
    noremap <F13> <Nop>
    noremap! <F13> <Nop>
    noremap <F14> <Nop>
    noremap! <F14> <Nop>
    

Advertisement