5月27日 14:48

Vim 退出后如何恢复上次的工作状态?

每次关闭 Vim 再重新打开,窗口布局没了,文件列表清空,折叠消失了——这种"从零开始"的体验让人抓狂。Vim 内置的会话(session)和视图(view)功能,专门解决这个问题。

用 :mksession 保存完整工作环境

:mksession 把当前 Vim 的窗口布局、标签页、缓冲区列表、折叠状态、当前目录等信息序列化成一个 Vim 脚本文件:

vim
" 保存到当前目录的 Session.vim :mksession " 指定路径 :mksession ~/sessions/project-a.vim " 文件已存在时强制覆盖 :mksession! ~/sessions/project-a.vim

会话文件本质上是一段 Vim 脚本,可以直接打开查看。里面记录了 tabnewsplitedit 等命令,Vim 逐行执行就能还原你的编辑环境。

用 :source 或 vim -S 恢复会话

恢复会话有两种方式:

vim
" 在 Vim 内部加载 :source Session.vim
bash
# 启动时直接加载 vim -S Session.vim # 等价写法 vim -S ~/sessions/project-a.vim

vim -S 是最常用的方式,可以把加载会话的命令写进项目目录的 Makefile 或 shell alias 里,一条命令就能回到上次的工作状态。

sessionoptions 控制保存范围

不是所有东西都需要保存到会话里。sessionoptions(缩写 ssop)决定了 :mksession 写入哪些内容:

vim
" 查看当前设置 :set sessionoptions? " 典型配置 :set sessionoptions=buffers,curdir,folds,help,tabpages,winsize,terminal

常用选项说明:

  • buffers — 缓冲区列表(包括隐藏缓冲区)
  • tabpages — 所有标签页,去掉则只保存当前标签页
  • winsize — 窗口大小比例
  • folds — 手动折叠信息
  • curdir — 当前工作目录
  • terminal — 终端窗口

实际使用中经常需要微调。比如你不希望会话保存终端窗口,因为重新打开时原来的 shell 进程已经不存在了:

vim
:set sessionoptions-=terminal

不想保存空白窗口:

vim
:set sessionoptions-=blank

:mkview 和 :loadview 保存单个窗口的状态

会话保存的是全局状态,但有时你只想保存某个窗口的折叠、滚动位置和本地选项。这时用视图:

vim
" 保存当前窗口的视图 :mkview " 加载当前窗口的视图 :loadview

视图和会话的区别在于作用域——视图只管当前窗口,会话管整个 Vim 实例。视图默认保存在 viewdir 目录下,文件名由缓冲区路径编码而来:

vim
" 查看 viewdir 位置 :set viewdir? " 自定义 viewdir :set viewdir=~/.vim/views

一个常见用法是在 vimrc 里自动保存和恢复视图:

vim
autocmd BufWinLeave *.py mkview autocmd BufWinEnter *.py loadview

这样每次切换 Python 文件时,之前的折叠和滚动位置都能自动恢复。

会话与标签页、窗口、缓冲区的关系

理解这三者的关系有助于用好会话:

  • 缓冲区(buffer):文件在内存中的实例,会话保存的是缓冲区列表,不是文件内容
  • 窗口(window):缓冲区的视口,一个缓冲区可以出现在多个窗口中,会话保存窗口的布局和尺寸
  • 标签页(tabpage):窗口的容器,每个标签页有自己的窗口布局,会话保存所有标签页(如果 sessionoptions 包含 tabpages

关键点:会话恢复时,Vim 会先加载缓冲区列表,然后按照保存的布局重建窗口和标签页。如果文件已经被移动或删除,对应的窗口会变成空缓冲区。

项目级会话管理

给不同项目维护独立的会话文件是最实用的做法。在项目根目录保存一个 Session.vim,启动时用 vim -S 加载:

bash
# 在项目目录下 cd ~/projects/my-app vim -S

更规范的做法是把会话文件放在统一目录,按项目名区分:

bash
mkdir -p ~/.vim/sessions vim -S ~/.vim/sessions/my-app.vim

也可以在 vimrc 里根据当前目录自动选择会话:

vim
let g:session_dir = expand('~/.vim/sessions/') let g:session_file = g:session_dir . substitute(getcwd(), '/', '_', 'g') . '.vim'

自动保存和恢复会话

手动执行 :mksession 容易忘记。用自动命令在退出 Vim 时自动保存:

vim
" 退出时自动保存会话 autocmd VimLeave * if v:this_session != '' | execute 'mksession!' v:this_session | endif

v:this_session 变量保存着当前会话文件的路径。如果还没加载过会话,这个变量为空字符串,此时不执行保存,避免在随意打开文件时产生多余的 Session.vim。

配合启动时自动加载:

vim
" 启动时加载项目会话(无参数时) autocmd VimEnter * if argc() == 0 && filereadable(g:session_file) | execute 'source' g:session_file | endif

argc() == 0 确保只有直接输入 vim 不带文件参数时才加载会话,避免和 vim file.txt 这种用法冲突。

tpope/vim-obsession 插件

手动管理会话的自动命令虽然能工作,但需要处理各种边界情况(比如打开多个 Vim 实例、会话文件冲突)。tpope 的 vim-obsession 插件把这些细节都封装好了:

vim
" 安装后,在项目目录执行 :Obsess ~/.vim/sessions/my-app.vim " 停止自动保存 :Obsess!

启动 Obsession 后,它会持续跟踪当前会话的变化,在合适时机自动更新会话文件。退出 Vim 时自动保存,不需要额外配置。

和手动方案相比,vim-obsession 的优势在于:

  • 不会在未启动 Obsession 的情况下覆盖已有会话
  • 正确处理多实例场景
  • 和 vim-fugitive 等插件配合良好

如果你经常在多个项目之间切换,vim-obsession 基本上是必装的。

viminfo 和会话的分工

会话保存的是"你在看什么"——窗口、标签页、缓冲区布局。viminfo 保存的是"你做过什么"——命令历史、搜索历史、寄存器内容、标记位置。

vim
" 保存 viminfo :wviminfo ~/.vim/viminfo " 加载 viminfo :rviminfo ~/.vim/viminfo

完整的恢复需要两者配合:会话还原布局,viminfo 还原操作历史。Vim 默认在退出时自动写入 viminfo,所以通常只需要手动管理会话部分即可。


Vim 的会话机制把"恢复工作环境"这件事从手动操作变成了可自动化的流程。从最基础的 :mksession / :source 开始,配合 sessionoptions 调整保存范围,再用自动命令或 vim-obsession 实现无人值守的保存恢复,这个渐进式的路径覆盖了从偶尔用到每天依赖的全部场景。

标签:Vim