在上一个章节里,我们用Vim的indent折叠来给我的Potion代码添加了一些方便的但是不完美的折叠方式。
打开factorial.pn文件,然后使用zM来折叠所有的代码。现在你的文件应该看起来和下面相似:
factorial = (n):
+--- 5 lines: total = 1
10 times (i)
+-- 4 lines: i string print
打开第一个折叠区,文件看起来如下所示:
factorial = (n):
total = 1
n to 1 (i):
+-- 2 lines: # Multiply the running total.
total.
10 times (i):
+-- 4 lines: i string print
这样看起来也不错,不过我个人更加喜欢用一个代码块的第一行来折叠它所有的内容。这个章节里我们会写一些自定义的折叠代码,我们完成后,上面的代码就会折叠成下面的样子:
factorial = (n):
total = 1
+--- 3 lines: n to 1 (i):
total.
+-- 5 lines:10 times (i):
这样代码看起来就更加紧凑了,也就更加容易阅读了。如果你更加喜欢indent方法的折叠的话,也是可以的。不过,还是要学习本章的内容来实践一下Vim的折叠表达式。
知道vim是怎么来“考虑”折叠的,对于编写自定义代码折叠是很有作用的。下面是对这个的简单介绍:
文件的每一行文本都对应一个折叠等级。这个等级要么是0要么是一个正整数。
折叠等级为0的行不会被折叠
相邻的文本,如果等级相同的话,会被折叠在一起
如果一个等级为X的折叠是关闭的话,它后面所有的折叠等级大于或者等于X的文本行都会被折叠,直到遇到折叠等级大于X的文本行为止。
下面我们用一个简单的例子来说明这个。打开一个vim窗口,把下面的文本粘贴进去:
a
b
c
d
e
f
g
通过下面的命令来打开indent折叠:
:setlocal foldmethod = indent
试试这个折叠方式,看看它是怎么进行折叠的。
现在通过下面的命令来看看第一行的折叠等级:
:echom foldlevel(1)
Vim会输出0。现在再看看第二行的折叠等级:
:echom foldlevel(2)
Vim会输出1.在看看第三行:
:echom foldlevel(3)
Vim会输出1。这就意味着第二和第三行都是等级为1的折叠。
下面显示了每一行的折叠等级:
a 0
b 1
c 1
d 2
e 2
f 1
g 0
再次阅读前面的内容。打开和关闭每个折叠,看看对应的折叠等级,确保你明白了折叠的原理。
一旦你对于每行文本的折叠等级对于创建不同的折叠的作用,继续阅读下一章节。
在我们开始写代码之前,我们先对我们的折叠定义一些规则。
首先,缩进一致的文本要一起折叠,同时前一行文本要和它们放在一个折叠里,所以,类似于下面的文本:
hello = (name):
'Hello, ' print
name print.
会被折叠成:
+-- 3 lines: hello = (name)
空行和它下面一行的等级保持一致,所以文本后面的空行都不会被包含进来。也就意味着下面的文本:
hello = (name):
'Hello, ' print
name print.
hello('Steve')
会被折叠成:
+-- 3 lines: hello =():
hello('Steve')
而不是:
+-- 4 lines: hello=()
hello('Steve')
这些规则都是个人的喜好,不过现在这些规则是我们要实现的折叠方式。
在编写自定义折叠代码之前,我们先打开两个split。一个打开ftplugin/potion/folding.vim文件,一个打开我们的样本文件factorial.pn。
在之前的章节里,我们都是通过打开再关闭的方式来使得我们的folding.vim文件生效,但是实际上还有一种更加简单的方式。
记得之前讲过,无论什么一个缓存里的文件的filetype被设置成potion,所有在ftplugin/potion/里的文件都会被运行。这就意味着,你只需要在factorial.pn的split里运行:set ft=potion来让Vim重新加载折叠代码。
这样就比关闭再打开文件方便多了。唯一需要注意的就是你要保持folding.vim文件,否则没有保存的内容是不会生效的。
我们接下来要用Vim的expr folding 来让我们的代码折叠变得更加灵活。
我们还需要把folding.vim里的foldignore去掉,因为它只会在indent folding下生效。同样的,我们还需要告诉vim去使用expr folding,所以我们把folding.vim的内容改变成如下所示:
setlocal foldmethod=expr
setlocal foldexpr=GetPotionFold(v:lnum)
function! GetPotionFold(lnum)
return '0'
endfunction
第一行的代码是告诉vim使用expr folding。
第二行代码指定了Vim用来取得当前行的foldlevel的函数。当vim运行这段代码的时候,它会把想要知道foldlevel的文本的行号作为参数传给这个表达式。然后这个表达式会把用这个参数来调用一个自定义的函数。
最后,我们定义了一个简单的函数,它会对所有的文本行返回'0'。注意这里返回的是一个字符串,而不是一个整数,后面我们会讲到这个的。
接下来,保存folding.vim,然后通过在factorial.pn文件里运行:set ft=potion来重新加载折叠代码。这次,Vim不会做任何折叠,因为我们的函数对于所有的文本行都返回'0'。
我们首先来看看空行这个特殊的文本。修改GetPotionFold函数,使得它看起来如下所示:
function! GetPotionFold(lnum)
if getline(a:lnum) =~? '\v^\s*$'
return '-1'
endif
return '0'
endfunction
我们在代码里添加了一个if语句来处理空行,它是怎么工作的呢?
首先,我们使用getline(a:lnum)来去等当前行的内容。
我们把取得的内容和正则'\v^\s*$'进行匹配。记住\v会打开'very magic'模式。这个正则会匹配“从文本行开始到结束全部都是空格”的文本。
这里的比较用的是大小写敏感的比较符=~?。从技术的角度来讲,在批评空格的时候是没必要注意大小写的,但是我比较喜欢在比较字符串的时候显示写清楚。如果你喜欢的话,也可以用~=代替。
如果你需要复习Vim里正则表达式的使用的话,你可以重新阅读“基本正则表达式”那一章节,以及“Grep 操作符”的章节。
如果当前行包含非空格字符的话,就不会匹配成功,这样就返回‘0’。
如果当前行匹配成功的话(例如,它是一个空行或者只包含空格),我们就会返回字符串‘-1’。
前面我们讲过,一个文本行的foldlevel可以是0或者正整数,那么这里又是怎样的情况呢?
你自定义的折叠表达式可以直接返回文本行的折叠等级,也可以返回一个特殊的字符串来告诉vim来特殊处理。
‘-1’就是其中的一个特殊字符串。它告诉Vim当前行的折叠等级是未定义的。Vim会这样解释它“这一行文本的折叠等级和它前面一行以及后面一行中,折叠等级较低的一个保持一致”。
Vim可以把这些未定义的文本行链接起来,如果你有两个未定义折叠等级的文本行在一个等级为1的文本行前面的话,那么vim就会设置最后一个未定义的文本行的等级为1,然后倒数第二个未定义的文本行的等级为1,然后第一个为1.