Tag Archives:Vim
Vim 文本过滤/文字处理插件
我经常有文本处理的需求,例如将 html 转换成纯文本,或者移除 markdown 里的所有连接,或者繁体转换简体。因此我做了一个插件来管理和执行各种外部文本过滤器。
所谓 “文本过滤器” 是一个命令行程序,它从标准输入读取文本,然后进行一些处理后写到标准输出,在 Vim 里可以用原生的:{range}! xxx 命令将选中文本发送给 xxx 命令的标准输入,然后用该命令的标准输出替换选中文本,这个命令很有用,但每次输入一长串命令略显繁琐,并且过滤器多了以后也很难管理。
因此我做了这个插件来统一管理文本过滤程序,并且提供接口来执行他们:

比如上图演示了将 HTML 转换成文本,以及去除 markdown 中的连接,使用命令:{range}TP {name} 就能调用名为{name} 的文本过滤程序了。这些程序可以用你喜欢的语言编写,放到统一的目录,加上可执行属性就行,该插件就能找到它。

我为什么使用 Vim?
很多人说用 Vim 是因为键位比较方便,其实这只是部分原因,不知道你思考过没,为什么今天大部分 Editor/IDE 都支持 vim keymap 的情况下,还有那么多人用 Vim 呢?如果仅仅因为键位原因,他们大可以用其他东西啊,为何还继续用原始的 Vim 呢?
也许在你看来,有的人配置 Vim 一半天最后就是类似 IDE/Vscode 的样子,既然如此,那么为啥那些人不直接去用 IDE/Vscode 呢?(二者也都支持 vim keymap),为什么他们还抱着原版的 Vim 不放呢?难道真的是他们没听过/没用过时下最先进的 Editor/IDE 么?
事实可能恰恰相反,很多 vimer 都是非常热衷尝试各种新的编辑器的,我给 vscode 写过不少高赞回答,也给 atom editor 开发过插件,不少 vimer 机器上 terminal/gui based 的 editor 加起来都有十多个,对主流 editor/ide 的熟悉程度未必比非 vimer 差。
那么究竟为啥还有人用 vim 呢?CoC 作者赵启明老兄已经回答了:”Vim 大概是世界上扩展能力最强的编辑器”。Vim 就像编辑器世界里的 MineCraft,你说 MineCraft 最终目标也就是去杀条龙,为什么那么多人不直接玩也能杀龙的黑魂/老头环,却一直迷恋 MineCraft 呢?
因为 MineCraft 每一寸土地都可以定制化,每种游戏元素都可以自己制作(除了工具武器制作外还可以制作各种玩法的副本关卡,还有人用 MC 里的红石从门电路做起,搭了个 CPU),而 Vim 里每个字符你都可以定制。
方便定制体现在两个方面:首先是能定制的地方很多,到处都能改(不像 vscode 一样对插件开发者诸多限制,连个 toolbar 按钮你想加都加不了);其次是非常容易上手修改,改的代价非常低,不用加个简单功能都要创建个插件项目,编辑 package 配置等一堆七七八八的文件,一半天才能开始写一行代码。
我用过非常多的编辑器,有一些至今我都非常喜欢,但不管我多么喜欢一款编辑器,总有一些地方是我不满意的,碰到这种时候,大部分我只能祈求开发商发慈悲哪天给我加一下,即便有些开源的,支持扩展的,很多都没达到 Vim 的扩展性,可以那么容易的让普通用户随心定制,四处定制。
用其他 Editor/IDE 的用户大部分都是下下扩展,改下按键之类的,而 vim 用过一年以上的几乎人人都可以随手扩展,举几个例子:

Vim2022:实时代码格式化
大部分 IDE/编辑器 都有代码格式化的功能或者插件,但都需要你主动触发格式化命令,而且每次写很多代码在保存的时候一次性格式化,总会有种不放心的感觉,需要跳过头去检查。
有没有可能让我一边写一边实时格式化呢?这样每次我都能看到最终的效果。
于是我写了个小脚本vim-rt-format,再 INSERT 下面每次按回车就能自动格式化当前行:

有了这个东西以后,写代码爽多了,释放注意力,完全专注于 “编码”,再也不用为 “格式化”这个事情花费额外的精力,变量名和运算符之间无需加空格,直接回车就自动变成干净清爽的代码了,能自动识别语法元素,并且格式化的过程无需离开 INSERT 模式。
目前支持:Python, Lua, Javascript 几种语言,使用的话,只需要 Vim 支持 +python3 特性,且 Python 安装 autopep8 模块即可,配置如下:
" 使用 vim-plug 安装插件Plug 'skywind3000/vim-rt-format'" 默认在 INSERT 模式下按 ENTER 格式化当前代码行,将下面设置" 成 1 的话,可以用 CTRL+ENTER 来格式化,ENTER 将保留原来的功能let g:rtf_ctrl_enter = 0" 离开 INSERT 模式的时候再格式化一次let g:rtf_on_insert_leave = 1行了,保存配置并重启 Vim,随便打开一个源代码开始编辑,就是这么简单。
你会忽然发现,天空变得更加晴朗,空气变得更加的清新,多么美好的一天啊。
项目主页:
https://github.com/skywind3000/vim-rt-format

学习 Vim 的三个阶段
学习使用 vim 有三个阶段:
首先是孩童时期,刚刚开始接触 Vim,觉得非常别扭,这里也不顺畅,那里也不够高效,表现为在知乎上问,“Vim 到底好在哪里?”,或者“学完 vimtutor 以后该干嘛?”
接下来是青春期,感受到 Vim 的爽点,开始欣喜若狂的探索 Vim 的边界,表现为疯狂的尝试各种 mapping 和插件,阅读网上一切关于 Vim 的文章和内容,尝试一切可能的事情。
最后进入成熟期,开始移除所有无用的东西,真正变得高效起来。表现为开始冷静思考自己的工作流程,逐步针对性定制 vim ,让 vim 越来越顺手。
只有对编辑器足够挑剔的人,才会使用 Vim。真要尝试,你要先问问自己是不是对编辑器有很高的要求,是不是对身边的编辑器都有不满意的地方,还是只是好奇,并不希望投入多少时间:

当你真的决定入坑了,那么主要问题就是怎么完成第一个阶段到第二个阶段的切换呢。
(点击 more/continue 继续)

Vim2021:超轻量级代码补全系统
2121年了,应该尝试些新东西,这里介绍一个超级轻量级(169 行代码)的代码补全系统,针对:历史输入,字典,tags 等多个源提供类似 YouCompleteMe 的操作体验,并且无需安装各种后端的补全 LSP 服务器。
语义补全是很爽,但有时候,当你用某些缺乏 LSP 支持的小众语言写代码时,或者你去到一台临时的服务器上工作时,你并不想花时间编译和设置一套复杂的补全系统。
这种时候,其实 Vim 内建补全系统其实就已经足够你用了,它能从当前文件收集单词,能从 dict 文件以及 tags 文件收集单词,并且在你按下<c-n> 或者<c-x><c-k> 时弹出补全框。
这个小脚本就是在你每次输入 1-2 个字符的时候为你自动弹出补全窗口用的,并且提供类似 YouComplete 的补全体验(点击查看 GIF 动图):

特性说明:
- 自动弹出补全框。
- 使用 TAB 和 SHIFT+TAB 来循环选择补全内容,
<c-e>关闭补全框。 - 提供同 YouCompleteMe 完全一致的体验(针对:buffer, dict, tags 几个源)。
- 纯绿色,所有操作都是对当前 buffer 生效,不会影响其他 buffer。
- 能够和其他补全系统一起共存(可以设置只对某些文件或者 buffer 生效)。
- 无需种量级补全服务,无需编译后台 LSP 模块。
- 轻量级,响应快,比大部分补全系统反应都要灵敏。
- 只有一个 160 行的 apc.vim 文件,你甚至可以直接把内容拷出来粘贴到你 vimrc 里。
- 适合作为各种大型补全系统的一个理想备份方案。
如何使用?
只需要这样就行了:(点击 more/continue 继续)

Vim 8 中的 C/C++ 编译运行:类 vscode 的任务系统
谦卑的向大家介绍我的新插件:asynctasks.vim,一套类似 vscode 的 tasks 系统,用于解决 vim 下长期没法轻松优雅的编译/运行 C/C++ 程序的问题。这个插件我去年酝酿了很长时间了,今年打算给他做一点宣传。
最近两年 Vim/NeoVim 发展非常迅速,各种:异步补全/LSP/查错,DAP 等项目相继出现,就连vimspector 这样以前只能奢望 emacs 的项目如今都出现了。
然而 Vim 任然缺少一套优雅的通用的任务系统来加速你的内部开发循环(编辑,编译,测试)。很多人在处理这些 编译/测试/部署 类任务时,任然还在使用一些比较原始的方法,所以我创建了这个插件,将 vscode 的任务系统引入 Vim。
vscode 为每个项目的根目录下新建了一个 .vscode 目录,里面保存了一个 tasks.json 配置文件来描述针对该项目的各类:编译/运行任务。而 asynctasks.vim 采用类似的机制,在每个项目的根文件夹下面放一个 .tasks 配置文件来描述针对该项目的任务,同时维护一份 ~/.vim/tasks.ini 的全局配置,适配一些通用性很强的项目,避免每个项目重复写 tasks 配置。
说起来好像很简单?其实这是概念简单,很多好的设计从概念上来讲往往非常简单,但是用起来却十分灵活强大,这不是我设计的好,而是 vscode 的 tasks 系统设计的好,我只是大自然的搬运工,这应该是目前 Vim 下最强的构建工具,下面就试用一下:

用 Vim/VsCode 来写 WordPress 博客
试用过一段时间各种静态页面博客系统,Hugo 这些,虽然发展的不错,但是比起 WordPress 来还是太弱了。WordPress 毕竟是发展了 15 年的东西各种功能和插件都比较完善。
所以这次回过头来重新使用 WordPress,顺便做了升级,速度更快了(升级 PHP7,引入页面缓存等),代码高亮等各种小功能也调优了一下,又加了一些类似热门文章和访问计数等小功能。
然后我写了一个命令行工具,可以让我在喜欢的文本编辑器里用 MarkDown 写博客,然后命令行发布到 WordPress,具体见markpress 相关文档。
下面是一些调优后的效果,首先 Markdown 的代码块,使用 highlight.js 以后好看很多:
#include <stdio.h>int main(void){ printf("Hello, World !!\n"); return 0;}这个插件支持 185 种语言(包括 Vim)的高亮,可以选择 89 种主题,是目前最强的代码高亮解决方案。
MarkPress 页面生成基本尊崇 Github 规范:
- 连接会被自动识别,只需要直写 URL,就会自动识别出来加上
<a>标签。 - 比如双波浪线包围的内容
~~测试~~会被划掉显示为:测试。 - 比如 Github Emoji,直接写
:smile:的 shortcode,就会变成
除此之外还有很多 github 没有的扩展,比如:
折叠菜单点击左边箭头打开
第一行隐藏的折叠内容
第二行隐藏的折叠内容
MathJax 的内嵌公式,被$ 符号包围住的内容会被识别成 latex 公式:
$z=\sqrt{x^2 + \sqrt{y^2}}$得到:
$z=\sqrt{x^2 + \sqrt{y^2}}$
然后是 GraphViz 图形,现在在 MarkDown 中用viz-{引擎名称} 开头的代码块:
```viz-dotdigraph G { A -> B B -> C B -> D}```能被转换为内嵌 SVG 代码,并被主流浏览器正常显示:

VimScript 五分钟入门(翻译)
译注:折腾 Vim 当然要能看懂和改写相关脚本,而中文资料匮乏,缺一个提纲挈领的教程。本文翻译自 Andrew Scala 的 《Five Minute Vimscript》,立足于让你用最短的时间掌握 VimScript 的基础和要点,你可以把它看成一份语言速查表。
Vim有着丰富的内建文档系统,使用:h <关键词> 就可以阅读,如果你想在方便的尝试各种 vimscript ,你可以通过 NORMAL 模式下使用gQ 命令进入 VimScript 的交互式环境调试命令。
注意:下面的例子中会包含一些形如<符号> 的符号,意味着正式使用时应该被完全替换成真实的东西,包括左右两边的尖括号。而单独的< 和> 在 VimScript 中被用作比较符号。
变量
let命令用来对变量进行初始化或者赋值。unlet命令用来删除一个变量。unlet!命令同样可以用来删除变量,但是会忽略诸如变量不存在的错误提示。
默认情况下,如果一个变量在函数体以外初始化的,那么它的作用域是全局变量;而如果它是在函数体以内初始化的,那它的作用于是局部变量。同时你可以通过变量名称前加冒号前缀明确的指明变量的作用域:
- g:var – 全局
- a:var – 函数参数
- l:var – 函数局部变量
- b:var – buffer 局部变量
- w:var – window 局部变量
- t:var – tab 局部变量
- s:var – 当前脚本内可见的局部变量
- v:var – Vim 预定义的内部变量
你可以通过 $name 的模式读取或者改写环境变量,同时可以用 &option 的方式来读写 vim 内部的设置值。
数据类型
Number:32 位有符号整数
-1230x100177Float: 浮点数,需要编译 Vim 的时候,有+float 特性支持
123.4561.15e-6-1.1e3String: NULL 结尾的 8位无符号字符串
"ab\txx\"--"'x-z''a,c'Funcref: 函数引用,函数引用类型的变量名必须以大写字母开头
:let Myfunc = function("strlen"):echo Myfunc('foobar') " Call strlen on 'foobar'.6List: 有序列表
:let mylist = [1, 2, ['a', 'b']]:echo mylist[0]1:echo mylist[2][0]a:echo mylist[-2]2:echo mylist[999]E684: list index out of range: 999:echo get(mylist, 999, "THERE IS NO 1000th ELEMENT")THERE IS NO 1000th ELEMENTDictionary: 无序的 Key/Value 容器
:let mydict = {'blue': "#0000ff", 'foo': {999: "baz"}}:echo mydict["blue"]#0000ff:echo mydict.foo{999: "baz"}:echo mydict.foo.999baz:let mydict.blue = "BLUE":echo mydict.blueBLUE没有布尔类型,整数 0 被当作假,其他被当作真。字符串在比较真假前会被转换成整数,大部分字符串都会被转化为 0,除非以非零开头的字符串才会转化成非零。

Vim 8 下 C/C++ 开发环境搭建
挺多人问怎么在 Vim 中搭建 C/C++ 开发环境,我本来想找篇文章发给人家,结果网上看了一圈,要不就是内容太过陈旧,要不就是太过零碎,不成体系。2018 年了,Vim 8 发布已经一年半,各大 Linux 发行版和 Mac OS X自带的 Vim 都已经跟进到 8了,不少文章还在介绍一些十年前的老方法。于是有了这篇文章。
那如何高效的再 Vim 8 中开发 C/C++ 项目呢?假设你已经有一定 Vim 使用经验,并且折腾过 Vim 配置,能够相对舒适的在 Vim 中编写其他代码的时候,准备在 Vim 开始 C/C++ 项目开发,或者你已经用 Vim 编写了几年 C/C++ 代码,想要更进一步,让自己的工作更加顺畅的话,本文就是为你准备的:
插件管理
为什么把插件管理放在第一个来讲呢?这是比较基本的一个东西,如今 Vim 下熟练开发的人,基本上手都有 20-50 个插件,遥想十年前,Vim里常用的插件一只手都数得过来。过去我一直使用老牌的Vundle 来管理插件,但是随着插件越来越多,更新越来越频繁,Vundle 这种每次更新就要好几分钟的东西实在是不堪重负了,在我逐步对 Vundle 失去耐心之后,我试用了vim-plug ,用了两天以后就再也回不去 Vundle了,它支持全异步的插件安装,安装50个插件只需要一分钟不到的时间,这在 Vundle 下面根本不可想像的事情,插件更新也很快,不像原来每次更新都可以去喝杯茶去,最重要的是它支持插件延迟加载:
" 定义插件,默认用法,和 Vundle 的语法差不多Plug 'junegunn/vim-easy-align'Plug 'skywind3000/quickmenu.vim'" 延迟按需加载,使用到命令的时候再加载或者打开对应文件类型才加载Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }Plug 'tpope/vim-fireplace', { 'for': 'clojure' }" 确定插件仓库中的分支或者 tagPlug 'rdnetto/YCM-Generator', { 'branch': 'stable' }Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }定义好插件以后一个::PlugInstall 命令就并行安装所有插件了,比 Vundle 快捷不少,关键是 vim-plug 只有单个文件,正好可以放在我 github 上的 vim 配置仓库中,每次需要更新 vim-plug 时只需要:PlugUpgrade,即可自我更新。
抛弃 Vundle 切换到 vim-plug 以后,不仅插件安装和更新快了一个数量级,大量的插件我都配置成了延迟加载,Vim 启动速度比 Vundle 时候提高了不少。使用 Vundle 的时候一旦插件数量超过30个,管理是一件很痛苦的事情,而用了 vim-plug 以后,50-60个插件都轻轻松松。
符号索引
现在有好多 ctags 的代替品,比如 gtags, etags 和 cquery。然而我并不排斥 ctags,因为他支持 50+ 种语言,没有任何一个符号索引工具有它支持的语言多。同时 Vim 和 ctags 集成的相当好,用它依赖最少,大量基础工作可以直接通过 ctags 进行,然而到现在为止,我就没见过几个人把 ctags 用对了的。
就连配置文件他们都没写对,正确的 ctags 配置应该是:
set tags=./.tags;,.tags这里解释一下,首先我把 tag 文件的名字从tags 换成了.tags,前面多加了一个点,这样即便放到项目中也不容易污染当前项目的文件,删除时也好删除,gitignore 也好写,默认忽略点开头的文件名即可。
前半部分./.tags; 代表在文件的所在目录下(不是:pwd 返回的 Vim 当前目录)查找名字为.tags 的符号文件,后面一个分号代表查找不到的话向上递归到父目录,直到找到.tags 文件或者递归到了根目录还没找到,这样对于复杂工程很友好,源代码都是分布在不同子目录中,而只需要在项目顶层目录放一个.tags文件即可;逗号分隔的后半部分.tags 是指同时在 Vim 的当前目录(:pwd命令返回的目录,可以用:cd ..命令改变)下面查找.tags 文件。
最后请更新你的 ctags,不要再使用老旧的 Exuberant Ctags,这货停止更新快十年了,请使用最新的Universal CTags 代替之,它在 Exuberant Ctags 的基础上继续更新迭代了近十年,如今任然活跃的维护着,功能更强大,语言支持更多。
(注意最新版 universal ctags 调用时需要加一个 –output-format=e-ctags,输出格式才和老的 exuberant ctags 兼容否则会有 windows 下路径名等小问题)。
自动索引
过去写几行代码又需要运行一下 ctags 来生成索引,每次生成耗费不少时间。如今 Vim 8 下面自动异步生成 tags 的工具有很多,这里推荐最好的一个:vim-gutentags,这个插件主要做两件事情:
- 确定文件所属的工程目录,即文件当前路径向上递归查找是否有
.git,.svn,.project等标志性文件(可以自定义)来确定当前文档所属的工程目录。 - 检测同一个工程下面的文件改动,能会自动增量更新对应工程的
.tags文件。每次改了几行不用全部重新生成,并且这个增量更新能够保证.tags文件的符号排序,方便 Vim 中用二分查找快速搜索符号。
vim-gutentags 需要简单配置一下:
" gutentags 搜索工程目录的标志,碰到这些文件/目录名就停止向上一级目录递归let g:gutentags_project_root = ['.root', '.svn', '.git', '.hg', '.project']" 所生成的数据文件的名称let g:gutentags_ctags_tagfile = '.tags'" 将自动生成的 tags 文件全部放入 ~/.cache/tags 目录中,避免污染工程目录let s:vim_tags = expand('~/.cache/tags')let g:gutentags_cache_dir = s:vim_tags" 配置 ctags 的参数let g:gutentags_ctags_extra_args = ['--fields=+niazS', '--extra=+q']let g:gutentags_ctags_extra_args += ['--c++-kinds=+px']let g:gutentags_ctags_extra_args += ['--c-kinds=+px']有了上面的设置,你平时基本感觉不到 tags 文件的生成过程了,只要文件修改过,gutentags 都在后台为你默默打点是否需要更新数据文件,你根本不用管,还会帮你:
setlocal tags+=...为当前文件添加上对应的 tags 文件的路劲而不影响其他文件。得益于 Vim 8 的异步机制,你可以任意随时使用 ctags 相关功能,并且数据库都是最新的。需要注意的是,gutentags 需要靠上面定义的 project_root 里的标志,判断文件所在的工程,如果一个文件没有托管在 .git/.svn 中,gutentags 找不到工程目录的话,就不会为该野文件生成 tags,这也很合理。想要避免的话,你可以在你的野文件目录中放一个名字为.root 的空白文件,主动告诉 gutentags 这里就是工程目录。
最后啰嗦两句,少用CTRL-] 直接在当前窗口里跳转到定义,多使用CTRL-W ] 用新窗口打开并查看光标下符号的定义,或者CTRL-W } 使用 preview 窗口预览光标下符号的定义。
我自己还写过不少关于 ctags 的 vimscript,例如在最下面命令行显示函数的原型而不用急着跳转,或者重复按ALT+; 在 preview 窗口中轮流查看多个定义,不切走当前窗口,不会出一个很长的列表让你选择,有兴趣可以刨我的vim dotfiles 。
编译运行
再 Vim 8 以前,编译和运行程序要么就让 vim 傻等着结束,不能做其他事情,要么切到一个新的终端下面去单独运行编译命令和执行命令,要么开个 tmux 左右切换。如今新版本的异步模式可以让这个流程更加简化,这里我们使用AsyncRun 插件,简单设置下:
Plug 'skywind3000/asyncrun.vim'" 自动打开 quickfix window ,高度为 6let g:asyncrun_open = 6" 任务结束时候响铃提醒let g:asyncrun_bell = 1" 设置 F10 打开/关闭 Quickfix 窗口nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>该插件可以在后台运行 shell 命令,并且把结果输出到 quickfix 窗口:

最简单的编译单个文件,和 sublime 的默认 build system 差不多,我们定义 F9 为编译单文件:

Vim 中文速查表/Cheatsheet(全网最完善)
春节期间整理了一份 Vim 中文速查表,免得经常东搜索西搜索的:
https://github.com/skywind3000/awesome-cheatsheets/blob/master/editors/vim.txt
看了一下,应该是现在 Vim 所有中英文速查表里最完善的一份,有时候速查表比看书搜网页高效多了。

