折腾
这篇文章不是教程,只是自己的一些体会。之前经常想这样一个问题,为什么我离开了VS或者Pycharm这些东西再写代码就感觉举步维艰呢? 其实在各种IDE的繁杂的外表下,我们编码所需要的一系列辅助功能的重要性已经被掩盖了。我们在使用IDE的时候,感觉每项功能都是必须的,如果没这些功能,自己根本写不了代码。
自从尝试使用Vim之后,最大的体验就是:折腾。没有比这些上古编辑器更能折腾的东西。不过通过一番折腾,也学到了不少东西。
可能你会问,为啥不用vscode呢?当然了,我也非常想用VSCode,但是VSCode是在是太慢了。在我的笔记本上,用VSCode写一个很小的C++工程(一两千行左右的样子),过一会儿上下翻页就非常的卡。另外,最近写Python比较多,VSCode的Python跳转也非常的慢,不知道是不是环境配置的问题。
本质上Vim的精髓在于它的编辑模式(可以参考《Practical Vim》这本书),倒不必执着Vim本身这个编辑器。理论上比较完美的配置是现代IDE和Vim的那一套编辑模式(其实用那套著名的快捷键来给Vim贴标签不足以突出Vim的核心),但是用过很多IDE + Vim插件,基本上或多或少都有问题,无法带来100%的Vim编辑模式的体验。
由于LSP(Language Server Protocal)和 Neovim(Vim 8)的出现,让这个上古编辑器有了新的生机。至少通过LSP和异步的特性,基本上使Vim有了现代编辑器的体验。这是前提,如果没有这两个东西,我是绝对不会去折腾Vim的,即使折腾这东西让人看起来非常的Geek。
由于以上考虑,当Vim有了现代特性的前提下,基于TUI的Vim还是值得一试的。
不过即使有了这两个特性,但要想达到可用的程度,还是免不了一番折腾。由于我的水平以及精力非常有限,所以选择了Neovim和SpaceVim这个开箱即用的环境,加上coc.nvim这个LSP插件,基本上在编码方面(只谈编码方面,其他的肯定比不过现代编辑器和IDE)可以有类似甚至超过VSCode的体验。
之前自己非常的菜(现在也很菜),主要是写C++,离开了IDE(VS)几乎不能写代码。确实是这样,尤其是对于像C/C++这种古老并且贴近硬件的语言,没有一个官方的现代化的工程和包管理工具。都是直接把源代码扔给编译器编译然后链接,实际上也确实是这么做的。对于C/C++来说,包管理自始至终是个大问题,这也就是为什么写c++首推IDE,而不是编辑器。有了IDE的加持,这些细节便不再重要,只管写代码了。然而C/C++的根本问题还是没解决:依赖的管理。当需要外部依赖的时候,即使是IDE,也需要一番折腾,甚至有时候折腾起来更加的麻烦。最近的情况有所改观,当微软推出了vcpkg,经过两三年的发展,在Windows上的C/C++的包管理的问题得到了一定程度的缓解,到目前为止,大多数常用的库都能通过vcpkg来管理,并且和vs工程和CMake紧密结合。而且自VS2017之后,VS原生支持CMake,使得VS编写C/C++更加方便。如果不考虑其他的因素,在Windows平台上,VS + CMake + ResharperC++写C/C++应该有最完美的体验。
总结一下IDE在编码方面提供的一些便利:
- 工程管理
- 语法高亮
- 自动补全
- 静态检查
- 查找定义,引用
- 调试
- 重构
- 性能分析
- 方便的快捷键
- …
上面列出来的每一个特性都是一个合格的IDE必须提供并且每一项功能的体验应该都非常完美。因为IDE为每一项特性提供了完美的体验,让我们感觉到少了任何一项特性都会让编码体验大幅度降低。 然而,当仔细地去思考一下在编码过程中,使用每一项特性的频率或者说是重要性都无差别吗?如果确实相等,那编辑器没有任何和IDE较量的可能,IDE之所以是IDE,就是因为它能在各个方面达到平衡,如果每一项都是我们所必须的,那编辑器如何和每一项都做得最好的IDE抗衡呢?很显然不能。
其实离开了一些功能或者说一些功能不是特别完美的情况下,对我们的编码过程影响不是很大。如果我们对这些特性进行重要性的取舍,就会发现编辑器还是可以和功能完备的IDE较量的。
Vim有一定的学习曲线是因为除了编辑功能之外,其他的操作都需要通过Vimscript来自定义,否则在Vim中会寸步难行。这个DSL会阻止大部分想接触Vim的人,这确实是Vim的一大问题。但是要考虑到使用Vim的环境,或许只能这样了。以我目前的水平,无法评论Vimscripts的好坏。但有一点可以肯定,这个问题不是一个非黑即白的问题,是和自己的积累和经验有关的。
在忽略Vim的使用经验的前提下,下面根据不同语言的特性,来说明Vim是否可以胜任一些编码工作。
C++
对于写C/C++这种静态语言,自动补全和语法检查是最重要的。如果没有自动补全,首先手动输入标识符的每个字母完全是在浪费时间,其次是写出来十有八九会出错。因此只能在编译阶段检查是不是写得正确。对于C++来说,编译的时间是很长的。如果没有自动补全和静态语法检查,每次去等编译反馈,其编码痛苦程度可想而知。 除此之外,查找定义和引用对阅读代码和编写代码也很重要。 好在对于静态语言来说,已经有了比较可靠的解决方案。调用编译器前端用来进行语法检查是比较成熟的做法。这方面的插件有很多,如比较有名的YCM,支持非常多的语言,功能很全,完成度很高,体验还不错。但是配置起来有点麻烦,尤其是对于刚接触Vim的人来说。而且这个是属于大而全的那种插件,完全不像其他的Vim插件那样轻巧。需要依赖于一个巨大的运行时。不过也可以理解,毕竟对于任何语言来说,自动补全不是一个简单的问题,也不是几行Vimscripts能解决的。
除此之外,我现在使用的就是coc.nvim这是一个LSP的实现,它能够管理很多其他的插件。简单来说,用了它之后理论上可以把VSCode的插件拿过来用,相当于把Vim变成了VSCode。那么对于c++来说,自动补全,语法检查用的是clangd,需要在coc.nvim下面安装coc-clangd这个插件。这是一个基于clang的LSP实现,属于clang的一部分。目前看起来可以胜任大部分关于C++的工作。 而且可以提供语义级别的定义和引用查找,但不方便的地方它需要依赖于compile_commands.json,也就是整个项目必须通过Configuration。如果你想阅读一些不太容易配环境的项目的代码,这个方法可能就不太好用了。这个插件还涉及到一些基本的重构功能,这里不再展开。
其实对于查找定义和引用,我们可以放宽条件,大部分情况我们只需要字符匹配级别的定义和引用查找就够了,而不需要语义级别的。这方面来讲gtags做的非常不错,速度够快,虽然找的不是特别准确,但是对于大型项目来说,能缩小定义引用的查找范围到同名的级别,已经是莫大的方便了,剩下的只需要自己去过滤一下就可以了。
Python
对Python这种动态语言做静态分析是比较困难的,自动补全和语法检查不太好做得完美。好在有失必有得,动态语言不用编译,我们可以用动态语言执行的便捷性进行运行时检查换取一定程度的静态检查。也就是我们可以通过快速试错来代替类型检查。剩下的工作还是由coc.nvim的 coc-python来完成,具体配置和VSCode是一样的。
不能做精确的静态分析就意味着不能做精确的定义和引用的查找。因此,再查找定义和引用方面,还是得靠gtags。
总结
总的来说,对于编码,自动补全,语法检查,查找定义和引用是最需要的功能呢。IDE在这方面做的足够好,Vim在这方面通过一番折腾也能做的可以接受。此外Vim的适用性广,编辑模式出色也可以作为加分项。当然这一切的前提都是需要折腾的。再次说明,这里并不是在鼓吹Vim,毕竟这种编辑器有它的局限性。需要强调的是,它的闪光点在他的编辑模式上。让它和现代编辑器甚至IDE去比实在是有失公平,但是在有些情况下,正如之前所说,如果我们对IDE的特性做一些取舍,配合一些插件,在编码放方面,Vim是可以比较出色的完成任务,并且它的是适用性是比大多数编辑器和IDE要广的。
虽然说了这么多,完成一项工程远不止编码,这就是Vim的痛处。除了自动补全,语法检查,查找定义个引用之外,还有一些很重要的辅助功能可以辅助我们更快更好地完成整个工作,甚至是不可或缺的。比如调式,用Vim写代码当然可以调试,然而Vim作为一个极简地编辑器,在可视化调试方面几乎没有任何帮助。当然有一些插件(Vimspector)可以进行可视化调试,但是其体验远不如IDE调试来得舒服,调试是整个工程级别的一项工作,远比编码复杂,而这正是IDE所擅长的,这样的工作还包括性能分析和重构。虽说经过一番配置之后,Vim也可以获得良好的调试体验,但是这必须付出比IDE更多的配置成本,而且在细节方面远不如IDE,期间可能出现许多破坏调试体验的功能缺失。
除此之外,Vim做不了更加现代化的工作,比如这篇文章是用VSCode来写的。VSCode通过现代化的图形界面,同时配合上各种功能的插件,可以实现出编码之外的非常复杂的编辑操作。比如Markdown的实时预览和可视化编辑矢量图,我曾经尝试用Vim来写Markdown,体验远不如VSCode。任何编辑器,没有最好。我们需要做的就是在自己可承受的折腾代价内,找到最适合完成当下工作的工具。