如何写一个fold-expr?


10

我已经阅读了有关fold-expr(:h fold-expr)的帮助页面,但没有解释表达式中使用的语法。

有四个示例:

  1. :set foldexpr=getline(v:lnum)[0]==\"\\t\"
  2. :set foldexpr=MyFoldLevel(v:lnum)
  3. :set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1
  4. :set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1

我知道这v:lnum是需要缩进级别的行,表达式2是对函数的调用。

表达式1、3和4呢?有人可以向我解释一下吗?


我的理解是该表达式应返回一个数字,并且该数字将用于确定给定行将在哪一级折叠。0不折叠,1是最外面的折叠,2是嵌套在1级折叠内部的折叠,依此类推
tommcdo

Answers:


12

来自:help 'foldexpr'

对每条线进行评估以获取其折叠级别

foldexpr评价,所以它需要VimL代码; 没有提及“特殊语法”等。评估的结果控制了Vim认为是折叠还是不折叠。

可能的值为

  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

不是完整列表;只是问题示例中使用的那些。请参阅:help foldexpr完整列表。


第一

第一个相当简单,一旦我们添加了一些空格并删除了反斜杠,就需要在:set命令中使它正常工作:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) 得到整条线。
  2. [0] 得到那个的第一个字符
  3. == "\t"检查它是否为制表符。
  4. VimL没有“ true”或“ false”,它仅将“ 0”表示为false,将“ 1”表示为true。因此,如果此行以制表符开头,则以foldlevel 1折叠。如果不是,则不在fold(0)中。

如果您将其扩展为计算选项卡的数量,则将具有基于缩进的折叠(至少在expandtab未启用时)。


第三

第三个确实没有第一个复杂。与第一个示例一样,我们首先要使其更具可读性:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. 我们得到了整条线 getline(v:lnum)
  2. 我们匹配与一个正则表达式=~'^\s*$'; ^锚定到开头,\s表示任何空白字符,*表示重复前一个零次或多次,然后$锚定到末尾。因此,此正则表达式与空白行或仅包含空格的行匹配(返回true)。
  3. getline(v:lnum + 1)获取下一行。
  4. 我们将\S其匹配到,这匹配此行上任何地方的任何非空白字符。
  5. 如果这两个条件都成立,我们求值为<1,否则为1。这是通过ifC和其他一些语言中已知的“三元”来完成的 condition ? return_if_true : return_if_false
  6. <1表示折线在此行结束,并且1表示折线级别1。

因此,如果我们在一行是空白而下一行不是空白的情况下结束折叠。否则,我们将处于折叠级别1。或者说::h foldexpr

这将使用空白行分隔的段落折叠起来


第四

第四者的行为与第三者相同,但方式略有不同。扩展为:

getline(v:lnum - 1) =~ '^\s*$' && getline(v:lnum) =~ '\S' ? '>1' : 1

如果一行是空白行,而当前行是非空白行,则在该行(>1)上开始折叠,如果不是,则将折叠级别设置为1。


后记

因此,所有3个示例的逻辑都非常简单。大多数困难来自缺少空格和一些反斜杠的使用。

我怀疑调用函数会产生一些开销,并且因为要评估每条要获得良好性能的行,因此会对其进行评估。我不知道现代机器上的区别有多大,除非您有性能问题,否则建议您使用一个函数(如第二个示例中所示)。记住The Knuth:“过早的优化是万恶之源”

这个问题也在StackOverflow上,答案略有不同。但是我的当然更好;-)


3

您实际上是在问这些表达式中的其他元素是什么,可以通过:help依次调用它们中的任何一个来找到它们:

v:lnum: the line being evaluated
getline(): get the line of text for a line number
==: equals
=~: matches
<cond>?<if-true>:<if-false>: evaluates to <if-true> if <cond> is true, else to <if-false>

我在下面按部分细分了这些表达式,以帮助说明它们的含义:

1将以制表符开头的所有行返回1,其他行返回0:

v:lnum                      the current line number
getline(v:lnum)             the text of the current line
getline(v:lnum)[0]          the first character of the current line
getline(v:lnum)[0]==\"\\t\" the first char of the current line is 'tab'

3段落后面的空白行的折痕:

 getline(v:lnum)=~'^\\s*$'                                       current line is only spaces
                              getline(v:lnum+1)=~'\\S'           next line has non-space
(getline(v:lnum)=~'^\\s*$' && getline(v:lnum+1)=~'\\S') ? '<1'   if both of these: <1
                                                              :1 otherwise: 1
(getline(v:lnum)=~'^\\s*$' && getline(v:lnum+1)=~'\\S') ? '<1':1

4从段落开始的空白行开始折叠:

(getline(v:lnum-1)=~'^\\s*$'                                     previous line only spaces
                                getline(v:lnum)=~'\\S'           this line has non-space
(getline(v:lnum-1)=~'^\\s*$' && getline(v:lnum)=~'\\S') ? '>1'   if both of these: >1
                                                              :1 otherwise: 1
(getline(v:lnum-1)=~'^\\s*$' && getline(v:lnum)=~'\\S') ? '>1':1 

的含义<1>1等是对这些表达式如下:help fold-expr


1

不小心将我的答案发表为评论,并提早提交。该死的手机。

我的理解是该表达式应返回一个数字,并且该数字将用于确定给定行将在哪一级折叠。0不折叠,1是最外面的折叠,2是嵌套在1级折叠内部的折叠,依此类推。

示例中的表达式看起来好像它们的计算结果为true或false。VimScript没有正确的布尔类型,因此它实际上是1或0,它们是有效的折叠级别。

您可以使用VimScript编写自己的表达式,就像返回1或0一样简单,或更复杂的是,允许嵌套折叠。


仅使用数字会起作用,但是值得注意的是foldexpr可以求值其他特殊值,例如=,a1,s1,> 1,<1,-1
Matt Boehm 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.