Vim 的折叠功能怎么用?
打开一个上千行的配置文件或源码时,满屏文本让人无从下手。Vim 的折叠功能可以把逻辑块收成一行,让代码结构一目了然——但很多人只停留在 zc/zo 的程度,不知道 Vim 其实提供了六种折叠方式,各有适用场景。
六种折叠方式
Vim 通过 foldmethod 选项决定折叠规则,一共有六种:
manual — 手动折叠。用 zf 配合移动命令圈选范围来创建折叠,最灵活但退出后丢失(除非持久化)。适合临时阅读不熟悉的文件。
indent — 按缩进折叠。缩进越深,折叠层级越高,Vim 用 shiftwidth 的值把缩进空格数折算成折叠级别。Python、YAML 这类缩进敏感的语言用这个最省心:
vimset foldmethod=indent
expr — 表达式折叠。通过 foldexpr 指定一个 Vim 表达式,对每一行求值返回折叠级别。灵活性最强,写法也最复杂。一个常见用法——按空行分段落折叠:
vimset foldmethod=expr set foldexpr=getline(v:lnum)=~'^\s*$'?'<1':1
返回值的含义:正整数表示折叠级别,>N 表示 N 级折叠从此行开始,<N 表示 N 级折叠到此行结束,= 继承上一行级别,a1/s1 分别在上一行基础上加/减一级。为了性能,建议把逻辑封装成函数:
vimset foldexpr=MyFoldLevel() function MyFoldLevel() let line = getline(v:lnum) if line =~# '^\s*$' return '<1' else return 1 endif endfunction
syntax — 语法折叠。依赖语法高亮文件中定义的 fold 区域,不需要额外配置,前提是当前文件类型的语法文件支持折叠。大部分主流语言开箱即用:
vimset foldmethod=syntax
diff — 差异折叠。只在 diff 模式下生效,自动把未修改的连续行折叠起来,只展示差异部分。用 vimdiff 比较文件时自动启用,无需手动设置。
marker — 标记折叠。通过文本中的标记符号定义折叠边界,默认是 {{{ 和 }}}。标记会写入文件内容,所以退出后依然存在,且支持撤销/重做:
vimset foldmethod=marker " 代码中写: " 函数开始 {{{ function! Example() " ... endfunction " }}}
不同文件类型可以用对应的注释格式:Python 用 # {{{,HTML 用 <!-- {{{ -->,C 用 /* {{{ */。
折叠操作的快捷键
掌握创建、删除、打开、关闭四个维度就够了:
创建折叠:
zf+ 移动命令:创建折叠。zf3j把当前行及下方三行折起来,zf%折叠配对的括号块,zfa}折叠当前大括号内的内容。zf+ 可视选择:在 Visual 模式下选中后按zf。
删除折叠:
zd:删除光标处的一个折叠(只删折叠结构,不删内容)。zD:递归删除光标处所有嵌套折叠。zE:删除当前窗口所有折叠。
打开/关闭折叠:
zo:打开当前折叠。zc:关闭当前折叠。za:切换开关(最常用)。zO/zC:递归打开/关闭所有嵌套层。zR:打开所有折叠(全局)。zM:关闭所有折叠(全局)。
在折叠行上按回车或双击也可以打开折叠,但快捷键更高效。
嵌套折叠
折叠可以层层嵌套。一个函数内部有 if 块,if 块内部有循环,每层都可以独立折叠。嵌套深度由 foldnestmax 控制,默认没有上限:
vimset foldnestmax=3
超过最大嵌套层数的折叠会被合并到允许的最深层级。对于结构复杂的代码,适当限制嵌套层级能避免过度折叠导致结构不清晰。
折叠相关的显示选项
foldcolumn — 在窗口左侧显示折叠指示列。设置为 0 隐藏,最大 12,建议设为 2 或 3:
vimset foldcolumn=2
折叠列里用 - 表示折叠打开的行,+ 表示折叠关闭的位置,| 表示折叠层级的延续。有了折叠列,鼠标点击也可以操作折叠。
foldlevel — 控制初始折叠深度。设为 0 打开文件时全部折叠,设为 99 等于全部展开。设一个中间值,打开文件就能看到结构骨架:
vimset foldlevel=2
配合 foldlevelstart 可以单独控制打开文件时的初始折叠级别而不影响后续操作。
foldminlines — 折叠最少显示行数。如果一个折叠内容不足指定行数,就不允许折叠它。避免把两三行的小块也折起来:
vimset foldminlines=5
折叠持久化
manual 模式的折叠退出 Vim 就没了。要持久化,在 vimrc 中加:
vimaugroup FoldPersist autocmd! autocmd BufWinLeave * mkview autocmd BufWinEnter * silent loadview augroup END
mkview 保存当前窗口的折叠状态、光标位置等信息,loadview 恢复。视图文件默认存在 ~/.vim/view/ 目录下。
marker 模式天然持久,因为标记写在文件内容里,但会污染文件,协作项目慎用。indent、expr、syntax 这三种是按规则实时计算的,不需要额外持久化——重新打开文件,折叠会自动重建。
大文件折叠的性能
折叠是有代价的。foldmethod=expr 和 foldmethod=syntax 需要对每一行求值或语法解析,文件上万行时可能出现明显的卡顿,尤其是滚动和插入时频繁重算。
几个应对方法:
- 大文件优先用
indent,计算量最小。 - 把
foldexpr的逻辑封装成函数,Vim 对编译过的函数调用比直接求值快。 - 用
foldnestmax限制嵌套层数,减少计算深度。 - 只读查看时开启折叠,编辑时临时切回 manual:
set foldmethod=manual。 - Vim 8+ 可以用
foldmethod=expr配合async插件异步计算,但原生的折叠本身是同步的。
如果你经常处理大文件,建议在 vimrc 里按文件类型设置不同的折叠策略,而不是一刀切。
推荐的 vimrc 折叠配置
把上面这些选项组合起来,一个实用折中的配置:
vim" 默认使用缩进折叠 set foldmethod=indent set foldlevel=2 set foldcolumn=2 set foldminlines=3 set foldnestmax=6 " 按文件类型覆盖 autocmd FileType vim setlocal foldmethod=marker autocmd FileType python setlocal foldmethod=indent autocmd FileType json,yaml setlocal foldmethod=syntax " 持久化 manual 折叠 augroup FoldPersist autocmd! autocmd BufWinLeave * mkview autocmd BufWinEnter * silent loadview augroup END
折叠不是花哨的功能——它解决的是真实问题:在有限屏幕里看清代码结构。花十分钟配好折叠策略,之后每次打开文件都能直接看到骨架而不是一片文字墙。