Featured image of post 我为什么使用 Neovim 作为主力开发工具

我为什么使用 Neovim 作为主力开发工具

Neovim 真的和大家说的一样神奇吗

如果你喜欢高强度的网上冲浪,你一定见过程序员们为了什么编辑器才是最好的而争论不休,而在这些争论当中,你也一定会见到有很多 vim 的拥趸,高呼 vim 才是编辑器之神。我相信很多人在看到这些对于 vim 神乎其神的描述后,会去亲自尝试使用这个编辑器,然后陷入深深的迷茫——这个东西到底怎么好用了?

事实上,我在第一次接触 vim 的时候,就是这种感觉。我不能理解,一个不怎么依赖鼠标、很多在现代编辑器中司空见惯的功能都要依靠一长串命令才能完成的老古董,到底怎么会让这群人如此喜爱。但当我为了更深入了解 vim 而强迫自己使用它进行开发时,我才逐渐感受到了这个编辑器的优秀之处。

但同时,我也意识到,这些我通过慢慢熟悉所感受到的优点,在我了解 vim 之初似乎没有看到有人提及。而那些大行其道的观点、它们所声称的 vim 的优点,在我看来也似乎并不是 vim 真正的优越之处。而当一个 vim 新手第一次尝试 vim 却没有感受到他们所阅读到的优点,也难免会感到失望和怀疑。这也是为什么现在,你一边能看到 vim 的用户给别人大力推荐 vim,一边能看到不用 vim 的人表示不能接受 vim、甚至称 vim 用户都在装X。

于是,我决定写这样一篇博客,来从我这个主力使用 neovim(可以把它理解为 vim 的一个分支)进行开发的人的视角,来谈谈我为什么要使用它进行开发、它能给我带来什么好处。

# 1 vim 真的能提高速度、提高效率吗?

我认为这个答案是否定的。很多描述 vim 优点的文章中都在反复强调 vim 的速度优势,但至少在我这段时间的使用中,我没有感受到 vim 显著的速度优势。

需要明确,我们是在写代码而不是在聊天,这也就意味着我们大多数时间是在思考而不是在劈里啪啦地敲键盘;而即使到了敲键盘的时候,其效率也要看我们的打字速度——vim 千好万好,也不会让我们打字的速度提高。vim 中当然有很多可以提高效率的功能,比如宏操作,但我认为这些便利的功能对于整体的工作效率提升是有限的,毕竟我们也不是时刻在使用这些功能。

某种角度来说,vim 和现代通用的编辑模式的区别,就类似于用手柄打游戏和用键鼠打游戏——如果你对 vim 有一定了解,你会发现 vim 移动光标的方式确实类似于手柄的定位,手柄通过方向和力度控制移动的方向和幅度,vim 通过方向和字符数 / 行数进行跳转。很多人会觉得手柄打游戏比键鼠打游戏舒服,但一定不会有人觉得手柄打游戏会比键鼠更快让我们通关。使用 vim 也是同理。

# 2 vim 的优点之一:统一开发环境的轻量解决方案

我大概从 2019 年 9 月份学校开设C语言课程开始接触编程。一开始接触的编程语言比较少,基本上一门语言用一个 IDE——MATLAB 就用自己的 IDE,写 C 用的是 DEV C++,写 Python 用的是 Spyder。但很快我就遇到了问题,那就是我电脑上安装的 IDE 越来越多:为了写前端安装了 VsCode,为了写 C# 和 Unity 安装了 VS,为了写 Java 安装了 IDEA,为了安卓开发安装了 Android Studio。然而,很多时候我在写代码的时候并不只是使用一种语言,为此就需要同时打开多个 IDE,这种时候我不但看着打开的一大堆标签头大,还因为这些工具吃掉了大量内存而无比焦虑。

显然,一个统一的开发环境,是解决这种问题的最好方案。它不但可以帮我精简空间,还可以让我统一操作习惯,不用一套编辑器记一套组合键——例如,VsCode 中的一些操作在 IDEA 中是需要用不同的方式实现的(比如,多光标操作),MATLAB 的注释快捷键和其他任何主流编辑器都不一样,等等。

vim 是能够满足这个要求的,只要通过合理的配置,vim 可以进行很多语言的开发。我可以在一个 neovim 实例中,同时打开 python、javascript、dart 等,并按照我自己所熟悉的工作流程书写代码。此时,我的快捷键是统一的,界面是统一的,甭提有多舒服了。除了某门实在是没有办法用 vim 开发的语言(就是你,MATLAB;不过这门语言差不多也只能用它自己的 IDE 开发了),我已经把我几乎所有的开发工作都放在了 neovim 上。

此时,会有人指出,很多主流编辑器都有 vim 插件。如果实在想用 vim,完全可以在 VsCode 或者其他的主流编辑器里开 vim 模式,而不是自己从头配置一个 vim。

然而,这样做并不是完美的。诚然,VsCode 和 IDEA 等工具提供了开盒即用的体验,在其中开 vim 模式不像原生的 vim 那样需要做很多配置,但这同样意味着这些工具有很多冗余的功能,而冗余的功能就会造成工具占用硬盘空间和运行时吃掉的内存的增加。此外,因为工具本身十分笨重,VsCode 这些编辑器在启动的时候相比于 vim 就会慢一些,然后再启动 vim 模式又要占用一些时间;虽然我们不会一整天都在开关编辑器,但启动的时候卡那一下子还是挺闹心的。

此外,这些工具提供的 vim 模式,毕竟不是原生的 vim。所以,这就意味着,你在一个工具的 vim 模式使用的某个功能,如果在另一个工具的 vim 模式里没有提供,那此时该如何配置这个功能,可能就会成为一个难题。此外,由于编辑器的 vim 模式不是原生的 vim,原生 vim 的一些配置方式在编辑器的 vim 模式中可能就不好用了。

所以,如果你是新手,想要用 vim 尝尝鲜而不是先被配置 vim 折腾的半死不活,可以试着用 VsCode 等提供的 vim模式,但如果真的要把 vim 作为主力开发工具,我的建议还是使用原生的 vim 或者 neovim。

# 3 vim 的优点之二:高度的自定义能力

高度的自定义能力,这才是我认为的 vim 最具有吸引力的地方。如果你不能理解为什么那么多人喜欢用原生的 vim,甚至连编辑器的 vim 插件都排斥,只需要想想,为什么那么多人只是玩着不打 mod 的 MineCraft 就能无比快乐,就会明白了。在 MineCraft 中,你可以自由地改造这个世界;在原生的 vim 和 neovim 中,你可以自由地改造这个编辑器,有什么不顺手的地方,改之。

# 3.1 自定义组合键

比如,我有时候需要打开的我的配置文件,但是 windows 下 neovim 配置文件的存放位置完整敲出来还是比较长的,所以我就绑定了一套组合键来快速打开配置文件。

注:以下用于绑定快捷键的函数是我自己二次封装的。

1
2
-- Select configuration file
map("n", "<leader>cfg", ":e ~\\AppData\\Local\\nvim\\", { noremap = true })

因为是从其他IDE转过来的,所以我会习惯用 Ctrl + Z 进行撤销操作,但这一操作在 vim 中会导致编辑器挂起,于是我干脆将这一组合键绑定为撤销操作。

1
2
3
4
5
6
-- Map ctrl-z to undo in normal / insert / visual modes
map("n", "<C-z>", "<Esc>:u<CR>", opt)
map("i", "<C-z>", "<Esc>:u<CR>", opt)
map("v", "<C-z>", "<Esc>:u<CR>", opt)
map("t", "<C-z>", "<Nop>", opt)
map("c", "<C-z>", "<Nop>", opt)

我习惯用 Alt + B 的组合键打开当前 HTML 文档,于是我在 neovim 中实现了一个类似的功能:

1
2
3
4
5
6
7
-- Run html files
RunHtmlFile = function ()
    if vim.bo.filetype == "html" then
        vim.cmd("!explorer %")
    end
end
map("n", "<A-b>", ":lua RunHtmlFile()<CR><Esc>", opt)

Neovim 中装了很多插件,但如果要执行插件提供的功能,可能需要输入长长的命令;又有时插件提供了快捷键,但我不太喜欢这些快捷键。这些情况下,改键同样是很好的解决办法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- UndoTree
map("n", "<leader>udt", ":UndotreeToggle<CR>", opt)

-- Markdown Preview
ToggleMarkdownPreview = function ()
    if vim.bo.filetype == "markdown" then
        vim.cmd("MarkdownPreviewToggle")
    end
end
map("n", "<leader>mdp", ":lua ToggleMarkdownPreview()<CR>", opt)

-- Hop
map("n", "<leader>hp", ":HopWord<CR>", opt)

这些只是 vim 自定义的冰山一角。事实上,你可以随意通过安装 / 卸载插件来添加或移除某些功能,想要修改某些默认的设置也只需要来到自己的配置文件中进行修改,你还可以通过 ftplugin 对特定文件进行特定设置。最主要的是,这一切设置都是你亲手一点一点创建出来的,你对它们无比熟悉,修改起来也就十分顺手。可以说,在使用 vim 的时候,你是真正掌控了这个编辑器。

# 3.2 宏操作

而除了改造编辑器,你还可以向它发号施令。且不提 vim 本身的 normal 模式和 command 模式就颇有指挥 vim 做事的感觉,你还可以通过宏操作对这些命令进行拓展。所谓宏操作,就是将一系列指令录制下来,以供我们后续反复调用。例如,假如我们有这样一个文本,我们想要在每行开头添加序号:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world

我们就可以按这样一系列按键。首先,gg 定位到首行,I 来到行首并进入 Insert 模式,输入 1. 添加第一个序号,再按 Esc 回到 Normal 模式。随后,我们就要对添加序号这个操作进行录制,我们按 qa 开始宏录制(此时会录制到寄存器 a 中)。第一步,我们按 l 将光标右移,y0 复制从光标到开头的内容(也就是 “1. “),然后按 j 来到下一行,0 来到行首,P 粘贴到光标前,2h 向左移动两个字符,Ctrl + a 对数字进行自增,按 f + <space> 定位到空格以回到录制宏前的最后一步。这样,我们就完成了一组宏操作,此时再按 q 结束录制。最后,我们通过 16@a 对剩下的 16 行重复这个宏操作,即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
1. Hello world
2. Hello world
3. Hello world
4. Hello world
5. Hello world
6. Hello world
7. Hello world
8. Hello world
9. Hello world
10. Hello world
11. Hello world
12. Hello world
13. Hello world
14. Hello world
15. Hello world
16. Hello world
17. Hello world
18. Hello world

如前文所说,我们并不是总在使用宏操作这种功能,宏操作对于我们整体的编码速度的影响也许是微乎其微的。但是,当我们真的需要这种重复性的工作的时候,宏操作还是会让我们的工作体验有很大的提升。

# 4 给 vim 新手的建议

很多人对 vim 抱有偏见的一个原因是,他们认为 vim 纵然有上述诸多优点,但它就是不好用。可是,事实真的是这样吗?我并不认为对于这些人来说,传统的 IDE 就真的更好用,他们更喜欢这些工具只是因为他们熟悉而已。所以,如果我们熟悉了 vim,同样也能够感受到它的好用——至少对于我来说,是这样的。当我强迫自己用 neovim 写了 2 个月的 flutter、终于熟悉它的用法后,我就觉得 vim 很顺手了。

所以,这里就可以给 vim 的新手用户提一些建议。我十分不建议任何新手一上来就去背一大堆命令;一开始,最好就是把 h / j / k / l 移动光标用熟练,学会 y / d / p 这几个命令,弄明白 10j / y10j 这种命令的含义,就差不多可以开始最基本的编辑了。至于使用 f / F / w / b / e 等来进行定位,利用 c / x 等进行剪切,甚至是 gg / G 这种简单的命令,我不认为一开始就需要去记忆。任何稍微复杂一些的命令 / 按键,我认为都可以在后续真的需要的时候再去学习,那个时候你已经对 vim 的基本操作熟悉了,额外的学习不会带来太大的负担。至于更高端的操作,比如宏,就更没有必要着急了。这个过程非常像学习 CSS,我们不需要一开始把所有存在的样式都背下来,而是应该先学基本的,再在使用过程中按需求学习更高级的样式。

出于同样的考虑,我建议 vim 的新手用户先去抄一份配置——这一份配置应该是完整的,而不是东拼西凑、试图博采众长,因为新手朋友可能不知道如何处理很多莫名其妙的冲突,尤其是代码补全这个天坑,更是新手的噩梦。需要注意,这个抄的过程应该是从头抄,一部分一部分抄,而不是找一个 github 上很火的配置,copy 过来了事——那样你会对自己的 vim 毫无了解,以后想要自定义也困难重重。这里,我比较推荐这篇教程Neovim 配置实战:从0到1打造自己的IDE,这里面的配置过程还算详细;或者如果你能找到类似的教程,也可以,但最后是这种一个一个模块带着你配置的教程。

这期间,你几乎一定会遇到很多不舒服的地方,比如你一些固有的快捷键使用习惯,比如 vim 的删除命令会把内容复制到剪贴板。对于这些问题,新手用户一定要善于使用搜索引擎。例如,困扰了我很久的 c / d / x 等命令会将删除的内容添加到剪贴板、覆盖我之前复制 / 剪切的内容的问题,我就是在搜索之后发现了 Blackhole register 这个机制,从而解决了问题。vim 有着庞大的社区,人们热情地拥抱新用户,所以,你永远可以大胆去向其他 vim 用户寻求帮助。

使用 Hugo + Stack 主题构建