# Vim everywhere

## 前言

自从用上 vscode 以后，vim 逐渐成为了一个插件，似乎消失了，我也不知道我还算不算 vim 用户了。最近重新关注起来，那个被我丢在角落的 vim 社区已经有了远超我想象的进步了。 当初因为插件性能问题，我开始转向了 vscode 。后来 neovim 诞生了，支持多窗口，内置了 lsp 支持，这使得 neovim 的  LSP 性能并不再是问题了。而且 neovim 内置了 Lua 插件支持，不仅性能大大提升，而且语言能力强大非常多。另外，还有了 [LazyVim](https://www.lazyvim.org/) 这样开箱即用的解决方案，内置了非常多优秀的插件，也节省了配置的时间。 研究了一段时间，终端的 neovim 功能已经非常强大了，文本搜索、LSP、文件搜索，session 功能，完全不输 VSCode，并且有了新的 [which key](https://github.com/folke/which-key.nvim) 插件，对应新手，已经不需要记忆快捷键了。总体感觉 vscode 不再香了，**vim 依旧是最酷的编辑器**。 这里重新整理一下 vim 的一些我感觉好用之处，对我自己来说也是一次温习过程。

## 基本思想

在 vim 的世界里，**文本编辑最核心的是修改，而不是输入**。这一点和大部分编辑器都不一样，vim 默认的是 normal 模式，这个模型下，用户可以快速找到文本，进行修改、选中、移动光标、删除。在 normal 模式下，我们所有的键都是快捷键，这也是 vim 能够摆脱鼠标的原因。 在 normal 模式下，除了 ctrl + key 和普通 key 作为快捷键之外，vim 还扩展了一个 leader key ，一般是 space 空格键，这个键可以多一个组合的方式。结合 which key ，我们也不需要记忆绑定了。这是 vim 高效的基础，可以有无法的操作组合。

## 几个阶段

首先，简单介绍一下我个人使用 vim 的几个阶段。

### 初识

最开始，是大学刚毕业之前，买了一个上网本，那种性能非常差但非常便宜的电脑，这种电脑能用的编辑器也就 vim 了。作为初学者，学习 vim 最好的方式就是使用 vim 自带的工具，vimtutor ，命令行输入一下 vimtutor 就可以开始练习了。 此外，vim 自带的文档，也是非常好的，`:help`就可以了。

### 狂热

**熟悉以后，vim 成为了我的主要编辑器**，有一段时间喜欢参与网上的编辑器争论，还专门写了[两篇文章](https://www.yuque.com/eward/old/bwxz8p)介绍 vim 。那时候试图用 vim 做一切，包括写日记、文章。 但 vim 要跟上 IDE 的功能还是很难的，搞一堆插件，最终 vim 配置越来越复杂，需要记的命令也越来越多，vim 本身也越来越慢。

### vim mode

再后来，我开始使用上了 vscode ，加上 vim 插件（这个插件是实现了 vim 的基本功能）。\*\*从此以后，很少用 vim \*\*，只在偶尔处理大批量文本的情况用用。 到了最后这个阶段，我慢慢的适应了 vim 作为插件的形式运行在 vscode 中。**vim 我也只留下最简单，最原始的用法**。折腾 vim 插件太繁琐了，很多功能现代编辑器要强大得多。但通过 vim 插件，**可以把 vim 的编辑的优势保留（不依赖鼠标操作），两者结合是最完美的了**。 vim 我虽然不用了，但 vim 的模式会永远存在于各种环境中。

## 快速移动

vim 的 normal 模式下，你只能输入命令，可以非常方便的用来移动光标位置，最著名的就是h/j/k/l 移动了。当然这种也只适合小规模移动，但这种方法成了一种标准，可以应用与各种地方。比如，ctrl + w + (hjkl) 表示移动到不同的窗口。通过 [vimium](https://github.com/philc/vimium) ，我们可以通过 H L 在 chrome 窗口左右切换，这样浏览器访问也可以大大减少鼠标的使用。 在 normal 模式下，所有的键都可以是快捷键，所以，vim 也逐渐发展出来一套快速移动的模式。除了上下左右，还有一些常用的移动

| 操作  | 功能  |
|-----|-----|
| <ctrl> + u / d | 向上、下滚动半页屏幕 |
| w / e / b | 向前移动一个 word, e 移动到 word end 位置，b 回退一个 word |
| $ / 0 | 移动到行头尾为止 |
| gg / G | 移动到最开头结尾 |
| H / M / L | 光标移动到屏幕的 top / middle / bottom 位置 |

这些快捷键是有点多，但都可以通过 vim 内置的 :help M 这类的指令找到相关的说明。 虽然记住这些很麻烦，但其实也不算多，每天用用自然熟悉了。**这些快捷键逐渐成为了一种标准**，比如我们使用终端工具，less more 之类的，也是这些快捷键操作来移动光标。

### 窗口移动

有一组命令，可以在不同窗口切换，都是 ctrl + w 开头

* ctrl + w +  (hjkl) : 上下左右切换
* ctrl + w + (sv) : split window (horizontally or vertically), 开启一个分屏
* ctrl + w + q: 退出当前窗口

这些命令可以看似组合非常多，但是有规律的，hjkl 是一种位置的标准，q 也是退出的常用命令。

## 搜索模式

前面的快速移动，都是一些通用的移动，但有时候我们是有目标的移动，这时候就需要使用到了搜索模式，这也是很多鼠标需要用到的场景。 搜索模式需要两个操作步骤，第一步选择一个关键词，第二步移动。当然，vim 的搜索优势只是在同一个文件范围，如果跨文件，vscode 的搜索更方便。 我用的比较多的是，搜索光标下的 word ，通过 `*` 进入搜索模式，然后通过 `n/N`往后、前移动。这种方式实际上，大部分是查询一个变量的引用，我们可以通过 gd/gr 这种方式更快捷，gd 意思就是 goto declaration , gr 查找引用。 另外一种方式，就是通过 `/`，然后输入正则表达式进行搜索。还有一个叫 [easymotion](https://github.com/easymotion/vim-easymotion) 的移动，简单说来就是通过一个快捷键进入搜索模式，但是高亮的所有 word 的开头，举个例子，一开始代码这样的  ![image.png](https://iling.fun/api/files.get?sig=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1cGxvYWRzL2MyMzFmODM0LTY3ZGQtNGJjZi1iM2RmLWYxMTQ2NGUxM2Y0MS8wNmQxZTg4ZS0xMDdiLTQwZjItOTYyMi0yNzRjNjRjMTMxZDIvaW1hZ2UucG5nIiwidHlwZSI6ImF0dGFjaG1lbnQiLCJpYXQiOjE3OTA0Mzg0MDAsImV4cCI6MTc5MDUyNDgwMH0.tm32KuCkjjY2VCmVrT0lvCpVfmvKDMCvgovu2sWtJLM) 输入 `,,w`，然后变成了下图，接下来我就可以在任意位置跳动，比如输入 a 就到了`this.renderMattermost`位置。最新的 neovim 插件 [flit.vim](https://github.com/ggandor/flit.nvim) ，直接通过 f F 激活进入 easymotion 搜索模式，这更简单了。  ![image.png](https://iling.fun/api/files.get?sig=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1cGxvYWRzL2MyMzFmODM0LTY3ZGQtNGJjZi1iM2RmLWYxMTQ2NGUxM2Y0MS8xMjBiOTVjZS01MjM0LTQ0YmMtYjZjMi0xODhmOWFmNWE0MDgvaW1hZ2UucG5nIiwidHlwZSI6ImF0dGFjaG1lbnQiLCJpYXQiOjE3OTA0Mzg0MDAsImV4cCI6MTc5MDUyNDgwMH0.52oar1aPsUyBK2kGGIMy9lDUJuyD5lhbOMh328YIkaw) 类似操作的还有 [vimium](https://github.com/philc/vimium) ，通过 [vimium](https://github.com/philc/vimium) ，页面输入 f ，所有可点击的链接都有一个字母标识，输入字母，即可跳转 url ，这实际上并不比鼠标点击慢，而且少了切换过程。  ![image.png](https://iling.fun/api/files.get?sig=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1cGxvYWRzL2MyMzFmODM0LTY3ZGQtNGJjZi1iM2RmLWYxMTQ2NGUxM2Y0MS9hZWVlNTc3ZS05MDJkLTQ0ZWMtODhhOC1mNDNhNmJkZjRmODIvaW1hZ2UucG5nIiwidHlwZSI6ImF0dGFjaG1lbnQiLCJpYXQiOjE3OTA0Mzg0MDAsImV4cCI6MTc5MDUyNDgwMH0.l2ewvxjWgXcytfqogzAiZFAsap6A8gYb6JiP83Rwlk0) mac 操作系统也可以如此操作，hammerspoon 有一个插件，可以给每个窗口一个字母标识，然后通过一个快捷键可以跳转到任意窗口。

## text object

text object 是 vim 里面最有特色的功能了。text object 就是定义一段文本对象的，比如 aw 表示 word ，( 表示括号包裹的一个对象。在 vim 里面移动或者修改，有点类似于描述一件事情，做一件事情，就有一个操作的对象，这个对象就是 text object 。 比如，我想删除一个单词，就是 delete word daw ，也可以加数字 d3aw ，删除三个 word 。组合行为一般分为两段

* d v y 这是几个基础的操作， d 删除，v 选中，v 表示进入选择模式，y 复制
* a i 这是范围的修饰，a 英文的 a ，i 表示 inner ，aw 和 iw 的区别就是 aw 会删除包括单词后面的空格，i 只是单词本身。a" 和 i" 的区别类似，a" 删除引号里面的内容，同时包括引号本身，i" 只是 inner ，只删除引号里面的内容
* 最后就是文本对象符号了，有很多，下面有一个 table 列出来了 |  | a/i + w / p / s | a /i  + ( " ' [ { < `| a / i + t | | --- | --- | --- | --- | | d | 删除一个 word, paragraph, sentence | 删除 '` [ 之类符号 里面的内容， i 是 inner ，不删除符号，a 删除符号本身 | dat / dit 删除一个 html tag | | v | 选中一个 word | vi( 选择符号包裹的内容 | 选择一个 html tag | | ds (delete surround) | 无 | 删除符号 | 删除一个 tag | | cs |  | 修改符号，"hello" + csa"'  = 'hello' | <div>hello</div> + cst" => "hello" 也可以 cstt 修改 tag 标签 |

在 neovim 里面，b 表示最近的一个 block，包括`( [ {`三个了。还有了 q (quote)表示 \`" ' \`\`，和 LSP 结合，有了 F(Function) 、c(Class) 这类的文本对象了。

### vim-surround

s(urround) 这个是一个插件提供的 object ，叫做 [vim-surround](https://github.com/tpope/vim-surround) ，这个在写代码的过程中使用非常多。这个使用的场景非常多，比如

* **快速添加一个 tag**，一个函数返回一个元素<Comp1>，这时候上面加一个组件<Comp2>，这时候代码变成了 <Comp1><Comp2>，通过 surround 可以快速添加一个 <></> 之类的包裹（选中组件，输入 S ，输入标签）
* **标签修改**，cstt 然后输入新的标签名称，或者把一个字符串转换为一个组件，cs"<p> 这样就可以了
* 删除标签，dst

除了可以操作一个文本对象之外，还可以对象之间移动。这些移动包括以下操作 {} 往前往后移动一个 paragraph 。[]  ][ 找到最近的一个 { 。% 在 ( " { < 符号收尾移动。这些都是比较常见的移动方式。

## 其他

### 复制

通常我们通过 y 执行复制操作，p 粘贴。但 vim 其实还有一个 register 的概念，我们可以的复制的东西，会依次写入到 0-9 标记的 register 中。然后通 `"0p`可以粘贴指定 register 的内容，这比系统一个剪切板稍微好点。通过 `:di`可以查看 register 里面的内容，粘贴一次以后，可以通过 `.`重放，这也是比较常用的。

### redo and undo

u / <ctrl> + r：u(ndo) 撤销操作，ctrl + r  redo 撤销 u 操作。vim 还支持 Undo Persistence ，关闭文件后还能撤回，有点类似 git 功能了。 类似的通过 **ctrl + o / i 两个操作符，可以在历史访问位置之间快速调整**，ctrl + o 回到上次位置， i 反之。

### 输入模式

本文中，所有的快捷键都是正常模式命令，这里单独说一下输入模式下的快捷键。 在输入模型下，也有一些通用的快捷键。因为 vim 的 normal 比较强大，所以我在输入模型下最多的操作是退出 insert 模式。一般是 esc ，此外还有 ctrl + [ / c / o ，退出有稍微的不同，**我一般用 ctrl + [ 比较多**，esc 太远了。

#### 快捷键

输入模型下的快捷键，很多都是系统级别通用的，和 vim 关系并不大。 ctrl + f / b , 光标向前向后移动，ctrl +  h 删除一个字母，ctrl a / e 移动到行头尾位置。这些是 emac 快捷键，在 vim 中也部分可用，但一般没必要，vim 移动用正常模式更快，这些我一般在其他输入场景使用。 此外，还有一组自动补全相关的快捷键，ctrl + n / p ，应用非常广泛（浏览器地址栏、终端、vscode 默认快捷键，Jetbrains），如果有输入提示，表示在提示选项之间前后切换，否则表示行 next 和 previous 。这一组是 vim 的快捷键和 emac 的组合，搜索提示是 vim 的，上下换行是 emac 的。 vim 特有的是 ctrl + w 删除一个 word 。

### marco 宏

在 vim 中，我们可以通过 q + 任意一个符号（mark）开始录制一个宏，然后后面的操作会被录制，完成以后通过 q 完成录制，这样就完成了录制过程，通过 @ + mark 来重放。这种场景在一些数据处理操作非常方便，比如把一个 csv 转换成一个 js 对象，我们完成第一行，然后通过 100@a 就可以完成后续 100 行的替换了。

## 总结

这里所说的，大部分是 vim 基础功能，其中有两个插件，vim-surround 和 [easymotion](https://github.com/easymotion/vim-easymotion) ，这些都是加强 vim 基础功能的插件，在 Vscode 的 vim 插件默认支持的。 虽然 vim 可以很快，但是也还是有代价的，需要记住很多快捷键。neovim 加 which key ，可以减少记忆的负担，但这些适合组合的快捷键，很多基础的操作是不需要组合的，还有需要记忆的。 现在越来越多的 ai 辅助工具，比如 copilot ，要比 vim 快得多，也不许要太过执着于 vim 。vim 最大的价值在于他提供了一套快捷的文本操作模式，并且这种模式，可以应用于任意场景。因为 vim 如此受欢迎，所以这套快捷操作模式，对于任意编辑器来说都是必须品。对于开发者来说，快捷键使用，认准 vim 就够了。

---

**Documents**

- [基于实践谈谈软件架构设计](https://iling.fun/s/blog/doc/5z65lqo5a6e6le16lci6lci6l2v5lu25p625p6e6k66k6h-tsGrQnNSNu)
- [rspack 使用体验过程](https://iling.fun/s/blog/doc/rspack-u8S4Mtjkfz)
- [E2E test with Playwright](https://iling.fun/s/blog/doc/e2e-test-with-playwright-JLFKAvWQ0U)
- [Vim everywhere](https://iling.fun/s/blog/doc/vim-everywhere-v6WZyLzGA1)
- [滴答 FaaS 研发平台小结](https://iling.fun/s/blog/doc/faas-XQxhEO3e5r)
- [Cod 服务编排之路](https://iling.fun/s/blog/doc/cod-0CFsOfiX20)
- [Cod 起源和用武之地](https://iling.fun/s/blog/doc/cod-1aAA2rVJZY)
- [2019 年度总结](https://iling.fun/s/blog/doc/2019-RhbTD2SiCn)
- [凤蝶区块历史及其问题排查记录](https://iling.fun/s/blog/doc/5yek6j225yy65z2x5y6g5yy5yk5yw26zeu6aky5o6s5pl6k6w5b2v-WoaaJ9QHpq)
- [如何用单元测试](https://iling.fun/s/blog/doc/5aac5l2v55so5y2v5ywd5rwl6kv-3zwG1cJvTn)
- [小钱袋 Node 应用开发经验总结](https://iling.fun/s/blog/doc/node-QpFfYCuoE0)
- [简单 JS 算数表达式求值算法的实现探索](https://iling.fun/s/blog/doc/js-FXcWkpW2vw)
- [凤蝶可视化编辑实现原理](https://iling.fun/s/blog/doc/5yek6j225yv6keg5yyw57yw6l6r5a6e546w5y6f55cg-ZsBj0CS1ev)
- [凤蝶服务端渲染经验总结二](https://iling.fun/s/blog/doc/5yek6j225pyn5yqh56uv5riy5pt57up6aqm5oc757ut5lqm-a4Kd59d7Td)
- [凤蝶服务端渲染经验总结](https://iling.fun/s/blog/doc/5yek6j225pyn5yqh56uv5riy5pt57up6aqm5oc757ut-GBhTESymbn)
- [Node.js 中 sourcemap 使用问题总结](https://iling.fun/s/blog/doc/nodejs-sourcemap-duex3wDhWt)
- [如何实现一个 mvvm 组件](https://iling.fun/s/blog/doc/mvvm-8TXAVSXe77)
- [webpack 热加载原理探索](https://iling.fun/s/blog/doc/webpack-7Wjn9z1fUB)
- [再谈 vim](https://iling.fun/s/blog/doc/vim-UBf5u57ko7)
- [How to realize velocity template interpreters](https://iling.fun/s/blog/doc/how-to-realize-velocity-template-interpreters-4s1KEznjYR)
- [joycss](https://iling.fun/s/blog/doc/joycss-bGvBvDGF7k)