From :help 'foldexpr'
:
It is evaluated for each line to obtain its fold level
The foldexpr
is evaluated, so it needs to be VimL code; there is no mention of "special syntax" or the like. The result of this evaluation controls what Vim considers a fold or not.
Possible values are
0 the line is not in a fold
1, 2, .. the line is in a fold with this level
"<1", "<2", .. a fold with this level ends at this line
">1", ">2", .. a fold with this level starts at this line
This is not the full list; just the ones used in the examples in your question. See :help fold-expr
for the full list.
First
The first one is fairly simple once we add some spaces and remove the backslashes we need to get this working in a :set
command:
getline(v:lnum)[0] == "\t"
getline(v:lnum)
gets the entire line.
[0]
gets the first character of that
- and
== "\t"
checks if that is a tab character.
- VimL doesn't have "true" or "false", it just uses "0" for false, and "1" for true. So if this line starts with a tab, it's folded at foldlevel 1. If it doesn't, it's not in a fold (0).
If you would expand this to count the number of tabs you would have indentation-based folding (at least, when expandtab
isn't enabled).
Third
The third one is really not that much more complicated as the first one; as with the first example, we first want to make it more readable:
getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
- We get the entire line with
getline(v:lnum)
- We match that as a regexp with
=~
to '^\s*$'
; ^
anchors to the start, \s
means any whitespace character, *
means repeat the previous zero or more times, and $
anchors to the end. So this regexp matches (returns true) for blank lines or lines with only whitespace.
getline(v:lnum + 1)
gets the next line.
- We match this to
\S
, which matches any non-whitespace character anywhere on this line.
- If these 2 conditions are true, we evaluate to
<1
, otherwise, 1
. This is done with the "ternary" if
known from C and some other languages: condition ? return_if_true : return_if_false
.
<1
means a fold ends on this line, and 1
means foldlevel one.
So, If we end a fold if the line is blank and the next line is not blank. Otherwise, we're at foldlevel 1. Or, as :h fold-expr
says it:
This will make a fold out of paragraphs separated by blank lines
Fourth
The fourth behaves the same as the third one, but does it in a slightly different way. Expanded, it's:
getline(v:lnum - 1) =~ '^\s*$' && getline(v:lnum) =~ '\S' ? '>1' : 1
If the previous line is a blank line, and the current line is a non-blank line, we start a fold on this line (>1
), if not, we're setting the foldlevel to 1.
Afterword
So the logic on all 3 examples is really quite simple. Most of the difficulty comes in the lack of spaces and some of the backslash usage.
I suspect that calling a function has some overhead, and since this is evaluated for every line you want to have a decent performance. I don't know how great the difference is on modern machines though, and would recommend that you use a function (as in the 2nd example) unless you have performance problems. Remember The Knuth: "premature optimisation is the root of all evil".
This question is also on StackOverflow, which has a slightly different answer. But mine is of course better ;-)