vim:术语和配置
站内链接:
- vim 笔记(1)-命令-基本命令
- vim 笔记(2)-命令-ex 命令
- vim 笔记(3)-命令-map 映射
- vim 笔记(4)-命令-输入模式
- vim 笔记(5)-配置-通用配置和术语
- vim 笔记(6)-配置-neovim
What is vim?
发展历史
vim(vi[Improved]), 一种跨平台的文本文件编辑工具, 继承 UNIX 系统的 vi 编辑器, 在 Linux/Mac/Windows 系统上都支持. 关于 vim 的入门等文章, 网络上有非常之多, 例如: 简明 VIM 练级攻略.
vim 的发展历史脉络大概如下:ed-->ex-->vi-->vim
,其发展历史反映了其作为开源项目的持续进化,以及一个活跃社区对它的支持和贡献。
- ed: 文本编辑器, 一次仅仅能编辑一行
- ex: 文本编辑器, 相比 ed, 增加了一些人性化的设置, 相当于 vim -E 启动
- vi: 模式编辑器, visual(ex 命令: visual), 不同的模式下有不同的工作方式
- vim: vi Improved
- other: vi 的其他派生物, nvi(BSD 系统), vim, Elvis, Vigor.
其中 vim 自身也在不断地进化,从而赢得越来越多的用户,从 vim4.0 到现在的 vim9.0 前前后后总共结果了 20 多年的时光。
帮助文档
- 获取帮助信息或者使用 vimtutor 来练习基本的 vim 命令
- 帮助信息: help tutor
- 启动: vimtutor
- 中文文档,目前 vim 的中文文档据说已经完全托管到vimdoc上,安装步骤如下:
1 | # 安装 |
Learning of vim
- 学习之道
- 普通思路:发现问题-搜索并查找资料-找到更好的解决方式
- 高级思路:在普通思路的基础上,了解细节和原理,从而更好的理解普通思路,并且提出相应的优化方法
- 扩展思路:根据已有的技术和原理,找出相似问题的解决办法,参与技术和原理的制定
- 文档为重
一个官方文档抵得上多篇博客文章介绍, 不一定需要英文文档, 下载安装中文文档, 一旦有任何不懂的命令, 使用 help 查询即可.
文件
filetype
用于指示正在编辑的文件的类型,这是非常重要的,特别是对于自定义 vim 命令或者编写 vim 插件的时候,了解文件类型就能决定启用哪些语法高亮、缩进规则和其他特定于文件类型的插件或设置。例如:
- 对于 python 文件,Vim 会启用 Python 的语法高亮和适当的缩进规则
- 对于 html 文件,则启用 HTML 的语法高亮和相关设置
从上面就可以简单的看出 filetype 的作用,特别是结合 autocmd 可以根据具体的文件类型设置自定义的命令。那么,filetype 有哪几种设置方式呢?
- 自动检测,此时设置
filetype on
即可开启,其主要启用文件类型检测, 当然如果希望关闭则可设置filetype off
- 手动检测,在 ex 命令中输入
:set filetype=python
手动指定文件的具体类型
filetype 还是通过在配置中开启插件的自动加载、缩进的自动加载,他们的命令分别是:
- filetype on/off:文件类型检测的开启和关闭
- filetype plugin on/off:根据文件类型加载特定插件的开启和关闭
- filetype indent on/off:根据文件类型进行自动缩进的开启和关闭
fileformat
决定了 Vim 如何识别和处理文件中的行结束符,根据不同的操作系统(unix/Dos/旧 mac)他们的换行标准是不一样的
- dos:
<CR><LF>
- unix/linux:
<LF>
- 旧 mac 系统:
<CR>
另外,可以通过 ex 命令手动设置文件的行结束符
1 | set ffs " 列出支持的文件格式 |
注意,line Endings
不一致会导致文件编辑的时候出现乱码问题,文件打开出现^M
问题,比如用 dos 去读取 unix 文件,即试图 从'^J'(LF)
中识别'^M^J'
(即 CRLF),所以发生错误,所以在日常开发或者多平台协作的时候需要设置统一的 fileformat
- 项目文档开发中明确指出,现在一般都是 Unix/Linux 基准的 LF 标准
- 通过项目目录下的.editorconfig 文件保证不同编辑器和 IDE 之间的统一
- 通过 git 配置进行自动切换,另外在 CI/CD 时增加检查脚本,一旦检查不通过则拒绝 MR/PR(merge request, pull request)
其中通过 git 配置换行符的自动转换命令如下:
1 | # Unix/Linux系统中, 设置 Git 检出时不转换,提交时转换为 LF: |
如果发生^M
乱码问题之后,在 vim 中怎么处理或解决呢?
1 | %s/\r//g |
文件编码
fileencoding
和 encoding
是两个与字符编码相关的设置,它们分别用于控制文件的编码方式和 Vim 内部处理字符的编码方式,理解他们的功能和区别对于处理多语言文本还是比较重要的。
- 文件编码
fileencoding
设置指定了 Vim 保存文件
(注意,其关注于保存)时使用的字符编码,一旦编辑并保存文件时,vim 会根据该值来决定如何将文件中的字符编码为相应的字节序列,例如 utf8、GBK 等。下面是文件编码的设置命令:
1 | " 设置当前文件编码 |
注意,如果未设置 fileencoding,默认使用 encoding 的值替代当前文件的字符编码。
- 内部编码
encoding
设置决定了 Vim 内部处理字符数据的方式,其影响 Vim 缓冲区中文本的表示、Vim 如何解释键盘输入和显示字符。通过设置encoding
可以调整 vim 识别数据的编码方式,其命令为::set encoding=utf8
。
注意,fileencoding 和 encoding 两者的区别
- fileencoding:用于当前缓冲区的字符编码,在保存的时候会基于该值作为文件的编码方式
- encoding:影响的是文本的显示,不影响写入文件时的编码,这点非常重要
autocmd
定义和格式
在 filetype 章节,我们提到 fieltype 和 autocmd 的结合使用,下面就是一个简单的使用示例:
1 | " 对html或者htmldjango类型进行额外的配置 |
那么,autocmd 是什么呢?autocmd 自动命令是 vim 中非常强大的功能,允许你根据不同的事件自动执行一系列命令,这些命令可以是文件类型的更改、读取或保存文件、进入或离开窗口。字如其意,autocmd 可以使 Vim 自动化执行特定任务。通过前端的场景解释,每一个 autocmd 都是一个事件回调函数,在某些动作触发事件之后就会自动执行(slient)对应的命令。
- 通用的基本 autocmd 命令格式
1 | au[tocmd] [group] {event} {pattern} [nested] {command} |
其中命令的说明如下:
- group,可选项,自动命令组名,若是指定组名,则此 autocmd 自动加入该组,组让你更容易地管理和清除相关的自动命令
- event,触发事件列表,用逗号隔开,表示当这些事件触发的时候执行此命令
- pattern,文件模式列表,通常伴随通配符,目标文件类型,表示当文件名或类型与给定的模式匹配时 + 事件触发的时候执行该命令
- nested,允许自动命令的嵌套使用
- command,欲被执行的命令
- autocmd 删除命令格式
当然,有 autocmd 添加命令,自然伴随着 autocmd 删除命令,其命令格式如下:
1 | " 删除所有和{event},{pat}关联的自动命令,之后加入新的{cmd}到{group}中 |
- 列出命令
1 | " 列出关联的命令,不添加命令 |
- 嵌套命令
通过nested
选项允许一个自动命令触发其他自动命令,当然,默认情况下是不支持嵌套的,其一般用于如下场景:
- 自动命令需要执行某些可能会触发其他自动命令的操作时,例如加载或保存文件、改变缓冲区等
- 自动命令链需要串联多个操作,并且后一个操作依赖前一个操作的结果时
例如,一个是在打开任何 .txt
文件时自动设置文本宽度为 80,另一个是在设置文本宽度时自动打开拼写检查,此时就需要设置 nested
1 | " 当文本宽度被设置时,开启拼写检查 |
匹配规则
那么,autocmd 的匹配规则是怎么样的呢?其有两种类型的匹配规则:基于路径的文件名匹配、文件类型匹配
- 文件名
*
:匹配任意数量的任意字符,不包括目录分隔符,例如*.html
**
:匹配任意数量的任意字符,包括目录分隔符,通常用于匹配子目录中的文件,例如:**/*.html
表示当前目录下所有子孙目录的 html 文件?
:匹配任意单个字符,不包括目录分隔符
1 | autocdmd BufNewFile *.txt ex-write |
- 文件类型,这里就需要配合 filetype 来使用,例如
1 | autocmd FileType python setlocal shiftwidth=4 tabstop=4 expandtab |
当然,一般 autocmd 都是通过组合匹配来完成命令的触发,比如多个事件、多个文件模式都放在一个命令中,他们是或
的关系,例如:
1 | " 在打开md或者markdown文件的时候 |
命令分组
前面已经讲解过 autocmd 可以通过 group 在逻辑上放在某个分组里面,实际上通过autogroup
可以在定义的时候就物理意义上进行命令的分组,这样更加的清晰
1 | " 命令 |
忽略事件
有忽略命令自然就存在忽略事件的操作,实际上可以通过全局设置 eventignore 指定 Vim 应该忽略的事件列表,其命令说明如下:
1 | " 事件列表 |
那么,其使用场景有哪些呢?
- 编辑大型文件:编辑大型文件时,某些事件(如语法高亮更新)可能会导致性能下降,则可以临时的关闭语法高亮事件
- 批处理或自动任务:不需要触发某些事件的自动命令,则可以通过 eventignore 来忽略
- 调试
手动命令
doautocmd 用于手动触发一个或多个自动命令。它强制执行为特定事件定义的自动命令,而不需要等待事件自然发生。
1 | doautocmd [nomodeline] [group] {event} [fname] |
例如,你的 vim 配置中已经存在一个打开 JavaScript 文件时设置特定的缩进规则,但是你现在随手打开一个 filetype 并非 javascript 的文件并进行类型更改,那么在不重新打开文件的前提下触发这条缩进规则,那么就应该执行如下操作:
1 | :set filetype=javascript |
现场保存
保存方式
使用 session 和 viminfo,可以帮助用户保存和恢复编辑环境,保证当前环境,以便下次启动时进行现场恢复操作,类似 Mysql 的数据库迁移,记录各种命令。虽然这两者的目的类似,即在用户关闭 vim 后能够恢复到之前的工作状态,但它们保存信息和使用方式有一些不同。下面是这两者的使用场景:
- session: 项目切换(每个项目保存一个 session)、工作状态保持(所有打开文件和他们的布局)、编辑环境恢复
- viminfo:命令历史共享、寄存器内容持久化(即使重启 vim 也保持)、标记持久化、搜索历史维护
在场景的使用上,session 更加侧重于保存和恢复整个工作环境的状态(文件和布局),viminfo 用于保存和恢复 Vim 的历史记录和状态(命令历史、寄存器等会话间共享的数据),下面简单介绍下它们各自的使用方式和基本原理。
session
当保存一个 session 时,Vim 会将当前环境的状态写入到一个 Session 文件(例如 Session.vim)中,这个文件中保存的实际上一系列的 Vim 命令,当该文件被 vim 加载的时候会重放这些命令以重建当时的编辑环境,这点同binlog 主从复制原理的原理类似,通过重放 sql 命令来实现数据的复制和备份。
- 创建和保存会话
1 | " a. 保存当前vim会话,默认保存到当前工作目录下Session.vim文件 |
最终生成的 Session.vim 是一个有几百行(安装的插件越多生效的插件越多,行数越多)命令的文件,其中包含了很多内置命令,文件的头部如下
1 | let SessionLoad = 1 |
- 加载会话
在推出或者离开工作编辑环境之后,需要重新加载之前的界面或窗口,则需要加载会话命令完成窗口的载入
1 | " a. 通过ex命令加载会话文件 |
此后,之前的编辑环境会自动加载,包括切割界面等等。
- 更新会话,通过
:mksession!
可以更新会话中的内容为最新的,确保每次离开工作环境之前都执行一遍该命令 - 设置会话保存选项,通过
sessionoptions
控制保存在会话中的内容,例如是否保存窗口布局等等,注意,这是白名单选项设置
1 | " a. 当前目录和会话目录设置 |
viminfo
每次推出和重新打开一个 vim 窗口的时候都伴随着 viminfo(~/.viminfo
)信息的重新写入和重新加载,这里保存的信息会在多个会话之间共享,包含命令历史、搜索历史、寄存器内容等,这个一般不做过多的了解即可。
折叠
folding 可以折叠和展开一部分代码和文本,从而能够聚焦于文件的某一部分,并从整体的视角查看代码或文本,折叠有两个术语或分类
- foldlevel,决定了打开文件时折叠的最深层级(阈值),只有级别高于这个阈值的折叠才会被打开,而低于或等于这个阈值的折叠会被关闭
- foldmethod,设置折叠的方式
其中 foldmethod 的可选值如下:
- manual(手动):用户手动创建折叠。后续可使用
zf
命令创建折叠,zf20j
表示创建从当前行开始向下 20 行的折叠 - indent(缩进):根据缩进级别自动创建折叠。具有相同缩进级别的连续行被视为一个折叠区域,在 python 文件中经常使用
- expr(表达式):使用 Vim 脚本表达式来定义折叠,允许基于复杂逻辑创建折叠。通过 foldexpr 来设置折叠的逻辑,例如
:set foldexpr=getline(v:lnum)=~'^\\s*if'
表示以 if 语句开始的折叠 - syntax(语法):根据语法高亮来创建折叠,利用语法文件中定义的规则来识别折叠区域。
- diff:在 diff 模式下,用于隐藏未更改的行
- marker(标记):根据文件中的特定标记来创建折叠,用户可以在文件中插入特定的字符串来标记折叠的开始和结束,例如
:set foldmarker=//{{,//}}
定义开始和结束的标记,后续就可以使用`//