折叠方法可通过 'foldmethod' 选项设置。
将 'foldmethod' 切换为非 "manual" 值时,先删除所有已存在的折叠,然后依据指定方
法创建新折叠。切换为 "manual" 时,则不会删除现有折叠。此设计方便先通过自动方式
定义折叠,然后进行手动微调。
有 6 种方法来选定折叠:
manual 手动定义/调整折叠
indent 通过缩进量定义折叠级别
expr 通过表达式定义折叠
syntax 通过语法高亮定义折叠
diff 基于比较结果,对未更改文本进行折叠
marker 通过文本标志定义折叠
手 动 fold-manual
使用命令手动定义折叠区域。脚本中也可用此法,自行解析文本并定义折叠。
折叠级别仅由嵌套层数定义。要提升指定行范围的折叠级别,可在当前折叠内部,定义包
含此范围的新折叠。
放弃文件后,手工折叠会丢失。要保存折叠,可用 :mkview 命令。之后可使用
:loadview 恢复。
缩 进 fold-indent
通过行缩进量自动定义折叠。
折叠级别的计算方法是用行缩进量除以 'shiftwidth',向下取整。一段拥有相同或更高
级别的连续行构成一个折叠。级别更高的连续行构成嵌套折叠。
最大嵌套层数由 'foldnestmax' 选项控制。
计算折叠级别时,部分行会被忽略,并继承其前后相邻行的折叠级别的较小值。这包括空
行或空白行,也包括以 'foldignore' 中字符开头的行。'foldignore' 检查前会跳过前
导空白。缺省值适用于 C 语言,用 "#" 忽略预处理行 (在老式 C 风格中不缩进)。
如果需要更复杂的忽略规则,请使用 "expr" 方法。可在 'foldexpr' 中使用
indent() 函数获取行缩进量。
表 达 式 fold-expr
与 "indent" 类似,本方法也通过折叠级别自动定义折叠。每行分别计算 'foldexpr' 表
达式值获取其折叠级别。 示例:
对所有以制表符开始的连续行创建折叠:
:set foldexpr=getline(v:lnum)[0]==\"\\t\"
为空白行分隔的段落创建折叠:
:set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1
同上,另一种写法:
:set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1
注意 必须用反斜杠转义 ":set" 命令中的特殊字符 (包括空格,反斜杠,双引号等,参
见 option-backslash )。
使用不带参数的函数调用更有效率:
:set foldexpr=MyFoldLevel()
该函数必须使用 v:lnum 访问当前行号。见 expr-option-function 。
计算表达式时,已满足以下条件:
- 已设置当前行所在的当前缓冲区和当前窗口
- v:lnum 已被设为当前行号
foldexpr 的计算结果按以下规则决定缩进级别:
值 含义
0 本行不折叠
1, 2, .. 指定本行折叠级别
-1 折叠级别未定义,使用前后相邻行的折叠级别的较小值。
"=" 折叠级别继承上行的计算结果 (译者注: 不能简单理解为沿用
上行级别,如上行使用 "<2",则上行级别为 2,而本行会据
上行要求,级别推断为 1)。
"a1", "a2", .. 上行折叠级别加上 (add) 指定数值作为本行折叠级别
"s1", "s2", .. 上行折叠级别减去 (subtract) 指定数值作为下行折叠级别
"<1", "<2", .. 指示指定级别的折叠在本行结束
">1", ">2", .. 指示指定级别的折叠从本行开始
"="、"s" 和 "a" 诸值计算开销更大,请见 fold-expr-slow 。
并非必须用 ">1" ("<1") 标志折叠的始止。本行折叠级别高于 (低于) 上行时,折叠会
自动开始 (结束) (译者注: 反之不真。即使两行折叠级别相同,本行也可以开始新的同
级别的折叠)。
表达式不能产生副作用。包括不能修改缓冲区文本,光标位置,搜索模式,选项等。如过
程中必须临时修改,必须谨慎保存原值并事后恢复。
表达式中出错或计算结果无法识别时,Vim 不会报错,但会将折叠级别自动设为 0。需要
调试时,可将 'debug' 选项设为 "msg" 来查看错误消息。
表达式以 s: 或 <SID> 开始时,会用脚本 ID ( local-function ) 替代这些前缀。例
如:
set foldexpr=s:MyFoldExpr()
set foldexpr=<SID>SomeFoldExpr()
"a1" 和 "s1" 用于折叠多行 C 注释的示例。包含 "/*" 的行返回 "a1",开始折叠,包
含 "*/" 的行则返回 "s1",结束折叠:
if match(thisline, '/\*') >= 0
return 'a1'
elseif match(thisline, '\*/') >= 0
return 's1'
else
return '='
endif
不过,此方法不适用于单行注释,也不适用于内含注释标识的字符串等。
在表达式中,可用 foldlevel() 根据上一折叠级别来计算当前折叠级别。但要注意,
如果指定行的级别未知,该函数返回 -1。另外,返回的是行开头所处级别,但折叠可能
在行内结束。
如果折叠未及时更新,可用 zx 或 zX 强制刷新。
尽 量 减 少 计 算 开 销 fold-expr-slow
本方法因为计算开销较高,尤其在初次计算所有行的折叠级别时,会让系统变卡。在此之
后的每次改动,Vim 只会重新计算受影响的行 (其他行则重用已知折叠级别)。
因而,折叠表达式应尽量减少一行的计算所依赖的行数: 例如,最好避免使用 "=","a"
和 "s" 作为返回值,因为它们需要 Vim 向后回溯,计算之前行的折叠级别,直到找到独
立的折叠级别为止。
如果这很难,次优解是在缓冲区局部变量里缓存折叠级别 ( b:foldlevels ),仅在文本
修改 ( b:changedtick 变化) 时,重新计算:
vim9script
def MyFoldFunc(): number
if b:lasttick == b:changedtick
return b:foldlevels[v:lnum - 1]
endif
b:lasttick = b:changedtick
b:foldlevels = []
# compute foldlevels ...
return b:foldlevels[v:lnum - 1]
enddef
set foldexpr=s:MyFoldFunc()
上例通过预编译不带参数 (仍然必须使用 v:lnum ) 的 Vim9 脚本函数获取额外加速。
见 expr-option-function 。
语 法 fold-syntax
通过带有 "fold" 参数的语法项定义折叠。 :syn-fold
折叠级别仅由嵌套层数定义。最大嵌套层数由 'foldnestmax' 控制。
务必正确配置语法同步。否则折叠效果可能与显示高亮不一致。使用跨行匹配模式时尤需
谨慎。如有疑问,可使用强制同步:
:syn sync fromstart
比 较 fold-diff
根据比较结果,自动折叠未改动 (更准确说法是不靠近改动区域) 的文本。
仅适用于开启 'diff' 选项且显示差异文本的窗口。不然,会将整个缓冲区视作一个大折
叠。
可通过 'diffopt' 设置指定上下文行数。即折叠与变更行之间,不被折叠的行数。例如
要设定 8 行上下文:
:set diffopt=filler,context:8
默认上下文为 6 行。
同时打开 'scrollbind' 选项时,Vim 会尽量在其他比较窗口中保持相同的折叠状态,确
保显示内容同步。
标 志 fold-marker
通过文本中的特定标志,定义折叠开始和结束位置。可精确定义折叠范围。这种方法可安
心删除和复制折叠,无需担心错误包含无关行。'foldtext' 选项的缺省设置会显示已折
叠行在折叠标志前的文本。方便为折叠命名 (见 foldtext() )。
标志可直接指定折叠级别,也可通过开始/结束标志配对来间接指定。直接指定级别更简
单,无须添加结束标志,也不会出现标志不配对的问题。如:
/* 全局变量 {{{1 */
int varA, varB;
/* 函数 {{{1 */
/* funcA() {{{2 */
void funcA() {}
/* funcB() {{{2 */
void funcB() {}
{{{ }}}
折叠从 "{{{" 标志开始。其后数值表示折叠级别。实际行为依当前级别和标志指定级别
的大小关系而定:
1. 级别相同时,结束上一个折叠,开始同级别的新折叠。
2. 标志指定级别更高时,开始新的嵌套折叠。
3. 标志指定级别更低时,结束所有高于或等于当前级别的折叠,开始指定级别新折叠。
数值指定折叠级别。0 值无效 (会被忽略)。可用 "}}}" 加数值表示结束对应级别的折
叠。下行的折叠级别会自动减一。注意 Vim 不会回溯实际开始标志以决定结束后的折叠
级别 (因为开销太大)。例如:
{{{1
当前折叠级别为 1
{{{3
当前折叠级别为 3
}}}3
当前折叠级别为 2
也可用配对的 "{{{" 和 "}}}" 标志定义折叠。每次使用 "{{{" 会使折叠级别加 1,而
每次使用 "}}}" 会使折叠级别减 1。注意保持标志配对!如:
{{{
当前折叠级别为 1
{{{
当前折叠级别为 2
}}}
当前折叠级别为 1
可混合使用带数值和无数值标志。一种实用的方式是,用带数值的标志定义大区域折叠,
而在函数内部用无数值标志。例如,可用 1 级折叠划分文件中的大章节 ("结构定义"、
"局部变量" 和 "函数")。用 2 级折叠包围每个结构定义和函数体。而在函数内部使用无
数值标志。在函数中修改拆分折叠时,无须重新编号标志。
标志可通过 'foldmarker' 选项设置。建议保留缺省值 "{{{,}}}"。这样文件可在不同
Vim 用户间通用。仅当文件有特殊需求时 (如包含来自其他编辑器的折叠标志,或缺省折
叠标志与当前文件语法有冲突等) 才需要修改。
fold-create-marker
可通过 zf 命令创建标志折叠。Vim 会在行范围的首行和末行的行尾自动插入由
'foldmarker' 指定的开始和结束标志。'commentstring' 非空时,会用注释标记包围折
叠标志。
以下情况下无法正常工作:
- 该行 (首行或末行) 已包含带级别的标志。Vim 无法判断如何进行。
- 附近折叠使用了带级别的标志,会因此产生冲突。
- 该行位于注释中,'commentstring' 非空,且嵌套注释非法。例如,在 C 语言的注释
内部加入 /* {{{ */ 会破坏现有注释。解决方法是在该注释外部 (之前或之后) 添加
标志,或手工添加标志。
通用建议是,已使用带级别标志时,不建议再使用 Vim 命令创建标志。
fold-delete-marker
可通过 zd 命令删除标志折叠。Vim 会自动在折叠的开始和结束处根据 'foldmarker'
搜索并删除开始和结束标志。如果标志周围文本匹配 'commentstring', 也自动删除这些
注释标记。
以下情况下无法正常工作:
- 该行包含多个标志,其中一个带级别编号。Vim 只会删除首个标志,无法保存结果是否
能真正删除折叠。
- 标志包含级别编号,且同时开始或结束多个折叠。
所有的折叠命令均以 "z" 开头。提示: 从侧面看,"z" 像一张叠起来的纸。
创 建 和 删 除 折 叠
zf E350
zf{motion} 或
{Visual}zf 折叠创建操作符。
仅当 'foldmethod' 为 "manual" 或 "marker" 时有效。
用 "manual" 方式新建的折叠缺省关闭。
本命令会自动打开 'foldenable'。
参考 fold-create-marker 。
zF
zF 为 [count] 行创建折叠。用法同 zf 。
:{range}fo[ld] :fold :fo
为 {range} 范围内的行创建折叠。用法同 zf 。
zd E351
zd 删除 (delete) 光标所在的单层折叠。光标位于已折叠行时,则删除该
折叠。删除折叠内部的嵌套折叠上移一层。在可视模式下,删除选区覆
盖 (包括部分覆盖) 的所有单层折叠。
小心: 这很容易删除超出预期的折叠,而且手动折叠删除后无法撤销。
仅当 'foldmethod' 为 "manual" 或 "marker" 时有效。
参考 fold-delete-marker 。
zD
zD 递归删除 (Delete) 光标所在折叠,包括嵌套折叠。在可视模式下,删
除选区覆盖 (包括部分覆盖) 的所有折叠,包括嵌套折叠。
仅当 'foldmethod' 为 "manual" 或 "marker" 时有效。
参考 fold-delete-marker 。
zE E352
zE 清空 (Eliminate) 窗口所有折叠。
仅当 'foldmethod' 为 "manual" 或 "marker" 时有效。
参考 fold-delete-marker 。
打 开 和 关 闭 折 叠
行数少于 'foldminlines' 的折叠总是会显示为展开状态。要注意以下命令的描述对小折
叠未必适用。
zo
zo 展开 (open) 光标所在折叠一层。给定计数时,展开相应层数;
在可视模式下,展开选区覆盖的所有折叠一层。
zO
zO 递归展开 (Open) 光标所在折叠,包括嵌套折叠。不包含光标的折叠不
受影响。在可视模式下,展开选区覆盖 (包括部分覆盖) 的所有折叠,
包括嵌套折叠。
zc
zc 关闭 (close) 光标所在折叠一层。给定计数时,关闭相应层数;
在可视模式下,关闭选区覆盖的所有折叠一层。
本命令会自动打开 'foldenable'。
zC
zC 递归关闭 (Close) 光标所在折叠,包括嵌套折叠。不包含光标的折叠
不受影响。在可视模式下,关闭选区覆盖 (包括部分覆盖) 的所有折
叠,包括嵌套折叠。
本命令会自动打开 'foldenable'。
za
za 小结: 切换光标所在折叠状态。
光标位于已关闭折叠时,展开一层。位于嵌套折叠时,可能需要用
"za" 数次才能完全展开。给定计数时,打开相应层数的已关闭折叠。
光标位于展开折叠时,关闭,且自动打开 'foldenable'。仅关闭单层
折叠,再次使用 "za" 会展开折叠。给定计数时,关闭相应层数的折叠
(和重复 "za" 同样多次的效果不同)。
zA
zA 位于已关闭折叠时,递归展开折叠。
位于展开折叠时,递归关闭折叠,且自动打开 'foldenable'。
zv
zv 查看 (view) 光标所在行: 仅展开必要折叠,保证光标行完整可见。
zx
zx 更新折叠: 撤销对折叠的手动展开和关闭: 重新应用 'foldlevel'。
然后执行 zv : 查看光标所在行。
同时强制重算折叠。适用于 'foldexpr' 未对缓冲区变动及时更新折叠
的异常场景。
zX
zX 撤销对折叠的手动展开和关闭: 重新应用 'foldlevel'。
和 zx 类似,也同时强制重算折叠,
zm
zm 折起更多 (more): 使 'foldlevel' 减少 v:count1 ,但不能低于零。
本命令会自动打开 'foldenable'。
zM
zM 关闭所有折叠: 将 'foldlevel' 设为 0。
本命令会自动打开 'foldenable'。
zr
zr 减少 (reduce) 折叠: 使 'foldlevel' 增加 v:count1 。
zR
zR 展开所有的折叠。将 'foldlevel' 设为当前最高嵌套级别。
:foldo :foldopen
:{range}foldo[pen][!]
展开 {range} 范围内的所有折叠。带 [!] 时,递归展开所有折叠,方
便查看 {range} 内的完整文本。省略 [!] 时,打开折叠一层。
:foldc :foldclose
:{range}foldc[lose][!]
关闭 {range} 范围内的所有折叠。带 [!] 时,递归关闭所有折叠。方
便隐藏 {range} 内的所有文本。省略 [!] 时,关闭折叠一层。
zn
zn 不 (none) 折叠: 复位 'foldenable'。所有折叠全部展开。
zN
zN 正常 (normal) 折叠: 打开 'foldenable'。还原原有折叠状态。
zi
zi 翻转 (invert) 'foldenable' 值,切换折叠功能启用状态。
在 折 叠 间 移 动
[z
[z 跳转到当前打开折叠的起始位置。如果已在起始位置,则跳转到包含当
前折叠的上一层折叠的起始位置。如果没有外层折叠,本命令会失败。
给定计数时,重复执行 [count] 次。
]z
]z 跳转到当前打开折叠的结束位置。如果已在结束位置,则跳转到包含当
前折叠的上一层折叠结束位置。如果没有外层折叠,本命令会失败。
给定计数时,重复执行 [count] 次。
zj
zj 向下跳转到下一个折叠的开始位置。已关闭折叠也算作一个折叠。
给定计数时,重复执行 [count] 次。
本命令可在 operator 后使用。
zk
zk 向上跳转到上一个折叠的结束位置。已关闭折叠也算作一个折叠。
给定计数时,重复执行 [count] 次。
本命令可在 operator 后使用。
在 折 叠 上 执 行 命 令
:[range]foldd[oopen] {cmd} :foldd :folddo :folddoopen
对非关闭折叠内的所有行执行 {cmd}。
给定 [range] 时,仅在指定范围内执行。
执行时,光标会先自动定位到待处理行。
和 :global 命令的执行机制类似: 首先标记出不在关闭折叠内的所
有行。然后对所有标记行执行 {cmd}。因此,即使 {cmd} 修改了文本
的折叠状态,也不会影响命令执行范围 (当然,被删除行例外)。
例如:
:folddoopen s/end/loop_end/ge
使用标志位 "e" 可在 "end" 不匹配时避免报错。
:[range]folddoc[losed] {cmd} :folddoc :folddoclosed
对已关闭折叠内的所有行执行 {cmd}。
余同 :folddoopen 命令。
颜 色 fold-colors
已关闭折叠的颜色由 Folded 高亮组 hl-Folded 决定。折叠栏的颜色的设
定由 FolderColumn 高亮组 hl-FoldColumn 决定。
:highlight Folded guibg=grey guifg=blue
:highlight FoldColumn guibg=darkgrey guifg=white
折 叠 级 别 fold-foldlevel
'foldlevel' 为数值选项,代表折叠级别: 级别越大则展开的折叠更多。
'foldlevel' 为 0 时,所有折叠都被关闭。
'foldlevel' 为正数时,关闭部分折叠。
'foldlevel' 很大时,展开所有折叠。
'foldlevel' 被修改后效果立即生效。之后仍可以手动展开和关闭折叠。
增加时,高于新级别 (译者注: 应为旧级别) 的折叠会展开。不会关闭手工展开的折叠。
减小时,高于新级别的折叠被关闭。不会展开手工关闭的折叠。
折 叠 文 本 fold-foldtext
'foldtext' 是字符串选项,指定用于替代已关闭折叠的显示文本的表达式。例如:
:set foldtext=v:folddashes.substitute(getline(v:foldstart),'/\\*\\\|\\*/\\\|{{{\\d\\=','','g')
效果是显示折叠第一行,并移除 "/*"、"*/" 和 "{{{" 等标记。注意 必须用反斜杠转义
":set" 命令中的特殊字符。使用函数定义处理逻辑,在选项中调用函数会简便很多:
:set foldtext=MyFoldText()
:function MyFoldText()
: let line = getline(v:foldstart)
: let sub = substitute(line, '/\*\|\*/\|{{{\d\=', '', 'g')
: return v:folddashes .. sub
:endfunction
使用不带参数的函数调用更有效率,见 expr-option-function 。
模式行中设置时会在沙盘 sandbox 里计算 'foldtext'。当前窗口是显示该行的窗口。
在上次设置本选项的脚本上下文下执行表达式。
表达式中出错时不会报错,需要调试时,可将 'debug' 选项设为 "throw"。
选项缺省值是 foldtext() 。能为大多数类型的折叠生成合适的文本。如果不满意缺省
效果,可自定义 'foldtext' 表达式。其中可使用以下 Vim 变量:
v:foldstart 折叠首行的行号
v:foldend 折叠末行的行号
v:folddashes 表示折叠级别的连字符序列
v:foldlevel 折叠级别
返回结果里的制表符会被替换为空格,而不可显示字符会被替换为可显示形式 <xx>。
超长结果文本会被截短以适配窗口而不会回绕。而多余空间会用 'fillchars' 里的
"fold" 字符来填充。
表达式以 s: 或 <SID> 开始时,会用脚本 ID ( local-function ) 替代这些前缀。例
如:
set foldtext=s:MyFoldText()
set foldtext=<SID>SomeFoldText()
注意 必须用反斜杠转义 ":set" 命令中的特殊字符 (包括空格,反斜杠,双引号等,参
见 option-backslash )。
折 叠 栏 fold-foldcolumn
'foldcolumn' 是数值选项,设置窗口左侧折叠指示列的宽度。为 0 时关闭折叠栏。常用
值为 4 或 5。最小可用值为 2,不过设为 1 仍可提供少量信息 (译者注: 为美观起见,
折叠栏最右侧缺省会显示一列空白,但设为 1 时关闭空白列)。最大值为 12。
打开折叠在折叠栏里会占用一列,在顶行显示 '-',后续行显示 '|' 延伸。折叠结束时
该列也会结束。嵌套折叠会占用外层折叠列的右侧列。
已关闭折叠会显示 '+'。
可用 'fillchars' 选项调整上述字符 ("foldopen", "foldsep", "foldclose")。
窄折叠栏容不下所有嵌套折叠时,会显示数位 (超过 9 会显示 '>') 代表嵌套级别,替
代 '|'。可用 'fillchars' 选项的 "foldinner" 字符替代级别显示 (译者注: 窄折叠栏
也会根据 'foldcolumn' 智能选择最左列对应的起始折叠级别)。
在折叠栏点击鼠标,可展开和关闭折叠:
- 点击 '+' 会展开该行所在已关闭折叠。
- 在其他非空字符上点击,会关闭该行所在打开折叠。
其 他 选 项
'foldenable' 'fen': 复位时所有折叠保持展开。
'foldexpr' 'fde': 用于 "expr" 表达式折叠的表达式。
'foldignore' 'fdi': 用于 "indent" 缩进折叠的需要忽略的行首字符。
'foldmarker' 'fmr': 用于 "marker" 标志折叠的标志符号。
'foldmethod' 'fdm': 当前折叠方法。
'foldminlines' 'fml': 折叠最小显示行数,少于此行数的小折叠永远不会关闭。
'foldnestmax' 'fdn': 用于 "indent" 缩进和 "syntax" 语法折叠的最大嵌套层数。
'foldopen' 'fdo': 限定可以自动展开已关闭折叠的跳转命令。
'foldclose' 'fcl': 指定何时自动关闭非光标所在的折叠。
上下移动光标和滚动文本经过已关闭折叠时,光标会定位到折叠块的首行。光标已位于折
叠行时,会移动到下一个未折叠行,或下一个已关闭折叠。
光标位于折叠行时,总会落在第一列。标尺会显示确切光标位置。但折叠状态下无法直观
对应文本位置。
许多移动命令会将整个折叠块视作单行空行。例如按 w 命令会在折叠首列停留一次。
在已关闭折叠上开始搜索时,不会匹配当前折叠内的文本。正向搜索默认从折叠末尾开
始,而反向搜索则从折叠开头开始。
在插入模式下,光标行永远不会被折叠遮挡。保障输入内容正常显示。
使用编辑操作符时,已关闭折叠被当作单一对象处理。执行 dl 会删除光标所在的整个
已关闭折叠。
作用于缓冲区行的 Ex 命令会自动调整作用范围,以包含完整折叠。例如,当光标位于已
关闭折叠时执行命令:
:s/foo/bar/g
会在整个折叠里用 "bar" 替代 "foo"。
:folddoopen 和 :folddoclosed 不受此规则影响。
注意 :source 等部分 Ex 命令,仅在使用双行限定符 [range] 时才会进行范围调整。
重新打开过往编辑的缓冲区时,会沿用上次的折叠设置。恢复手动创建的折叠,也复原在
所有方法下创建折叠的手动展开或关闭状态。优先读取当前窗口中该缓冲区的历史配置
(如有),否则,调取该缓冲区最近一次编辑窗口的配置。
vim:tw=78:ts=8:noet:ft=help:norl: