跳转至

浅谈 Vim 的配置

用来用去,兜兜转转,还是回到了 Vim 这该死的 Editor 上面来。

之前再一次放弃 Vim 的原因是响应速度突然慢的完全受不了,我起初认为是 Gvim windows 适配的问题。

后来得知了一些关于 Vim 底层相关的东西之后,排查了自己原来的 .vimrc,发现罪魁祸首是 Vim-airline。

它和一个叫做 Whitespace check 的玩意儿冲突了,导致文件加载速度奇慢无比,具体可以看这个 issue

和 EarthMessenger 交流了一下,觉得其实 Airline 这东西,本质上就是花里胡哨的玩意儿,其实也没有必要。

除了它 git repo 的一些显示之外,其实也没啥用。

还有些原因是,有些使用非 vim script 的语言编写的 extension,在和 vim 交互的时候容易因为接口之类的问题响应过慢。

综合考虑之后,我觉得应当尝试一下一个,尽量轻量级,配置上手很快,依赖其他第三方语言和软件极少的 Vim,并且,最重要的是,效率。

有些之前觉得方便,实际上违背了初衷的习惯也可以改过来。

配置相关仓库,不定期更新。


考场 Vimrc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
syntax on
color desert
set ai ts=4 sts=4 sw=4 
set nu rnu mouse=a ru si cin cinoptions+=j1
set ar acd backup swapfile undofile nocompatible
set guifont=Consolas:h12:cANSI
set timeoutlen=666 ttimeoutlen=0
set backspace=indent,eol,start
set encoding=utf-8

nmap F :e ./<CR>

inoremap [ []<ESC>i
inoremap ( ()<ESC>i
inoremap " ""<ESC>i
inoremap ' ''<ESC>i
inoremap {<CR> {}<ESC>i<CR><ESC>O

C++ψ(`∇´)ψ

这个是最基本的需求。

正常来说需要的东西也就这些:

  • 语法高亮
  • 括号补全
  • 一些基本的代码格式化(tab = 4,自动换行)
  • 编译和运行
  • 调试

第一点比较简单,只需要在 vimrc 里面写一句:

1
syntax on

就可以了。

第二点也比较简单,我个人使用习惯是,希望输入前半括号,能直接弹出后半括号并且自动跳到括号里面。

这个利用 vim 的映射就可以做到:

1
2
3
4
5
inoremap [ []<Esc>i
inoremap {<CR> {}<ESC>i<CR><ESC>O
inoremap ( ()<Esc>i
inoremap ' ''<Esc>i
inoremap " ""<Esc>i

第二行需要在有 autoindent 选项的情况下使用,这样会自动换行并缩进到大括号里面,这个原理也很简单,就是基本的 vim command.

因为我一般是大括号不换行的,所以我一般是 <SPACE>{<CR>,行内花括号我用的不多,用了也习惯不补全,所以这样就够用了。

当然,因为 vim 在处理有公共前缀的映射时会有一个 timeoutlen,为了方便可以按照自己手速调整一下,我一般设置在 600ms 左右。

第三点,也是 vimrc 能直接解决的:

1
set tabstop=4 softtabstop=4 shiftwidth=4 autoindent

就可以了。

第四点,如果直接在 vim 里使用 !<command>,在 win 下会弹出窗口,linux 下会直接在 vim 里切屏到一个终端。

但是这个时候你就不能对 vim 里原有的代码进行操作了,这很麻烦,所以我们需要异步编译的功能。

这个需要 Vim 7.4 以及之后的版本,因为 7.4 开始,Bram 才引入了 job 这个概念。

Vim 圈著名大神 skywind3000 曾经写过一个异步运行插件 asyncrun.vim,原理什么的可以在他的博客看到。

因为我目前没有时间深入学习这个东西,所以我选择比较粗暴的方式,直接开一个 Terminal 编译,vim8.1 之后可以有内置的 terminal,比较方便

反正有上下键,也不是很费时间,而且改一些选项也很快。

但还有一个问题是,我经常在粘样例的时候把代码粘贴进去,这就导致我用↑找不到我的编译命令了。

于是想到了一个办法!开两个终端!我是天才!

第五点,我已经习惯了 GDB 这种命令行调试的模式,所以直接 Terminal 就行,也可以在 Vim8.1 过后使用

1
packadd termdebug

来引入 TermDebug 包,然后你就可以在 Vim 里实时查看断点了。

话说 Bram 在更新 8.1 的时候都在想啥,咋更新了这么多人性化功能。

很可惜我们学校装的是 8.0,有点难受,所以我一般都是手动 vim90。

但是 TermDebug 有一个很严重的问题,它不能支持 <CR> 继续上一个命令。

所以我还是直接 :Terminal,然后 <C-w> <S-n> 就可以在 Terminal 里面进入 NORMAL 模式了。

但是调用的还是 CMD,所以我在思考怎么调用外部终端。

upd: EarthMessenger 提供了一种解决方案::term 是可以调用外部终端的。

类似这样::term msys2_shell.cmd -here -no-start -defterm(参数可以自己使用 msys2_shell.cmd -help 查看)

别忘记加 Path.

把这个映射成命令就好了!

不过如果调用 Msys2 的话,系统 PATH 里的程序是没法调用的,所以有的时候还是要用一用 Terminal

然后 Msys2 如果没有配置过,可能需要更新一下 pacman,不然下载会出事:

1
2
pacman -Su
pacman -Sy

如果是用 Gitbash,也是类似的::term bash.exe --login -i (后面的参数是为了阻止它打开窗口)

Markdownψ(`∇´)ψ

这个也是很重要的需求。

  • 语法高亮和一些基本的补全
  • Preview
  • 文件名补全。
  • 一些数学公式的快速输入(写莫反题题解太痛苦了)

关于第一个,之前选择了插件:preservim\vim-markdown,但是后来发现它会拖慢整个 vim 的效率,这是不能忍受的,所以删除了,干脆直接 syntax off,或者就用原生的 syntax。

但是发现代码块高亮没了,烦恼,最后在知乎上发现了解决方案:

let g:markdown_fenced_languages =['c', 'cpp']

这个甚至可以识别到我缩进过的代码块,比 vim-markdown 牛逼多了。

果然,原生 vim 才是最牛的()

然后公式渲染没了,愤怒,所以在国外大神博客里找到了解决方案

大致就是尝试匹配 $$ 并给他加上高亮,不过还有点 bug,之后来修。

第二个,我之前就一直在用,是一个国内大佬开发的,感觉挺好用,毕竟可以同步预览还可以导出:iamcco/markdown-preview.nvim

第三个主要是因为,我经常会引用我自己博客目录下的图片,文章,所以这东西对我来说很有必要。

我以前以为 vim 没有内置补全,某天闲来无事乱按的时候按出了一个 popmenu,发现可以补全文件,搜索了一下之后发现只需要 <C-x><C-f> 就可以了。感叹,vim 果然还是技高一筹,早就有了这样的功能。

第四个的话,我写了几个映射,放在 $VIM/ftplugin/markdown.vim 里面就可以了。

Miscψ(`∇´)ψ

还有一些其他的功能,比如字典,这个 vim 其实也自带了,不过不会自动弹出,而是要 一下。

有点难受,之前选用了 skywind3000 大佬的 vim-autopopmenu,但是因为我需要读取 buffer,在大文件编辑时会很卡,于是放弃。

upd:其实不需要读取 Buffer,可以用 i 只读取当前文件和 include,已经够用了,但考试的时候要禁用。

然后关于系统剪贴板这个事情,这个我之前喜欢同步 vim 的剪切板和系统剪贴板。

不过现在还是觉得,独立一下会比较好,因为 vim 里经常 dd 啊,临时移动一下代码之类的,剪切操作很多,而且 vim 本身这个 register 功能又不是摆设,肯定还是要用用的,所以我现在的 vimrc 就没有同步了。

还有一个是关于相对行号的,发现其实这个比直接弄行号要好的多,可以少打一个 shift?

但是要快速跳到视野之外的地方就不太方便,但是可以直接滚屏,所以还挺不错。

中文输入法的话,之前看到过一个内嵌,可以方便在服务器之类的地方操作,不过我没有在奇怪的环境下输入中文的需求,所以暂时不需要,有系统词库就好了。

不过还有一些需求,比如一个内置 todolist manager,我没有看到比较符合我心意的,于是打算自己写一个,vim script 太烦,所以我的想法是使用 c++ 实现,在终端当中工作,有时间了再考虑通过调用的方式内嵌到 vim 里,或者是用 Qt 加个图形化界面直接封装成应用。

有一个叫 Nerdcommenter 的插件,可以提供一些快速注释的功能,经过以下配置之后可以直接用 leader 键来实现注释(leader 默认是 \)。

1
2
3
4
5
6
7
8
9
let g:NERDCreateDefaultMappings = 1
let g:NERDSpaceDelims = 1
let g:NERDCompactSexyComs = 1
let g:NERDDefaultAlign = 'left'
let g:NERDAltDelims_java = 1
let g:NERDCustomDelimiters = { 'c': { 'left': '/**','right': '*/' } }
let g:NERDCommentEmptyLines = 1
let g:NERDTrimTrailingWhitespace = 1
let g:NERDToggleCheckAllLines = 1

就只需要记住 \ci 以及 \cc 即可(\<leader> 键)。

还有一个事情是,我不喜欢在我的代码文件夹里放 ~un, swp 之类的文件。

不过这些文件还是非常有必要的,所以我选择把它们存在一个指定的位置,这个可以使用以下的设置:

1
2
3
4
set backup swapfile undofile
set backupdir=C:\Users\Administrator\.vimtmp\backup
set undodir=C:\Users\Administrator\.vimtmp\undo
set directory=C:\Users\Administrator\.vimtmp\swp

当然,如果提示了无法写入,建议检查一下路径格式是不是正确的,这个可以看 :help backupdir,或者是把当前文件原有的 ~, ~un, .swp 文件删掉解决冲突就行。


另外,vim 自带了一个文件管理系统 netrw,我很喜欢,这个放到单独的一篇文章里来写。


last but not least, 我写了一个主题,感觉挺爽,有兴趣可以用一用: enonya/yuyuko.vim

当然有一些地方的 syntax 因为我还没发现所以可能会显示为青色,看起来不太爽,发现了可以 open an issue.

upd: 修复了,并且调整了对比度,现在更舒服一点。


最近尝试了 fzf 和 fzf.vim。

感觉很好用啊!只是似乎它不太能和我的 yuyuko.vim 适配,可能是因为我没做非 256 色的支持。

有时间再来弄一下好了。

然后发现老是 Files: 太麻烦,刚好发现一个 [N]f 空着没啥用,就拿来映射了一下。

又突然想到我经常直接 e ./,为啥不提升一下效率,直接映射呢?于是在 _vimrc 里面就又多了几行:

1
2
3
4
nmap f :FZF<CR>
nmap F e ./<CR>
nmap <leader>t :Terminal
nmap <leader>n :tabnew

感叹,Vim 果然是越调教效率越高啊。


更新了 yuyuko.vim 对 Termcolor 的适配。

之前一直不知道 g:terminal_ansi_colors 是什么,今天翻阅文档并实验之后发现,似乎是给 vim 中启动的终端默认提供的颜色列表。

是一个包含十六个 HEX code 的列表,编号 0~15,终端启动的时候如果输出颜色文本,就会发起对这个颜色的请求。

所以之前没加这个适配的时候,fzf 没有显示出对应的颜色的问题就得到了解决。

我写了一个简单的词频统计,对 yuyuko.vim 统计了一下常用颜色,然后放进去之后做了一些调整,fzf 就支持 yuyuko.vim 的 theme 了!

一开始其实只是想改 fzf 的适配,后来发现 fzf 本质是调用了一个 cmd,所以启动的 cmd 当中的带颜色文本也是 yuyuko.vim 的主题了,包括上面调用的 msys2 和 gitbash.


upd: 回家之后发现 fzf 对中文的支持有问题:#issue 1377

而且作者咕咕咕了好久了,我也没那个能力自己 fork 然后修复,所以我找到了另外的一个替代品 vim-clap

因为 LeaderF 安装和依赖太麻烦,我的 gvim 的 python3 支持总是弄不好,然后 CtrlP 又太慢。

这个东西配置主题也挺方便的,速度和 FZF 差不多,功能甚至更强大,而且对中文的支持没问题(毕竟是国人作者)

然后就 fork 了一下原 repo 的 default 主题,改了一个适合我的配色:yuyuko-clap

不过现在里面还有一些部分依赖于 yuyuko.vim (因为用了 hi! link),反正现在只有我一个人用,以后再说修复的事情()

另外发现了 vimtweak.dll 这个东西,可以支持 gvim 的透明,原理好像是 Vim 可以调用外部扩展函数,dll 本质是 msvc 编译出来的一个扩展库,有人用 msvc 写了窗口透明,然后加了进来,vim 就可以调用了。

现在 vimrc 多了这些:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let g:clap_theme = 'yuyuko_clap'

command! W :Clap windows
command! GitTerm :term bash.exe --login -i

nmap F :e ./<CR>
nmap f :Clap files<CR>
nmap <leader>f :Clap blines<CR>
nmap <leader>F :Clap buffers<CR> 
nmap <leader>n :tabnew<CR>
nmap <leader>tg :GitTerm<CR>
nmap <leader>tc :terminal<CR>
" 记得加 PATH

func EnableTransparency()
  silent exec libcallnr("vimtweak.dll", "SetAlpha", 200)
endfunc
func DisableTransparency()
  silent exec libcallnr("vimtweak.dll", "SetAlpha", 255)
endfunc

map <F9> <ESC>:call EnableTransparency()<CR>
map <F10> <ESC>:call DisableTransparency()<CR>

最近在适应 vim 的滚动,但是发现这个切屏一样的感觉,每次滚动完都反应不过来自己在那里(got lost)

所以找到了之前用过的 smooth-scroll,挺好用的。


在调试考场 vimrc 的时候发现可视模式如果使用上下左右移动会取消选择。

经过测试发现是 nocompatible 没有开,这个是兼容 vi 的设置,看起来会导致这样那样的各种问题,还是开了为好。


有一个叫 renderoptions 的选项可以在 ms gvim 下开启字体连字,设为 type:directx

原理我还没看,不过这个略有一定缺陷,需要以主动或者被动方式刷新才能正确显示。

不过已经够用了。


之前在 noilinux 下用的 vim 都会自动高亮匹配(搜索,不是括号匹配)

但是没找到这个选项,今天发现是 hlsearch,但是匹配完了需要用 :noh(nohlsearch) 取消。

所以就给 <C-l> 绑了下,刷新的同时 :noh


<C-r> <reg(寄存器名称)> 可以在 command 模式下复制粘贴。


又是一个困扰我好久的老问题:在一些 vim 上我的 inoremap {<CR> {}<ESC>i<CR><ESC>O 不起作用,他不会自动缩进。

和 Earthmessager 经过二分和删除 vimrc 的排查,发现是因为没有开启 smartindentcindent

发现 ms-vim 好像是不用开的,很神秘,但是又发现一个问题,这两个选项不管什么语言,它都会直接判大括号的缩进,如果没 close 就会缩进。

书写本文的上上句的时候就遇到了这个问题,不过鉴于一般在 md 里也不会写大括号,真要写也是 \{,cindent 倒是不会判这个。

所以就这样吧,如果之后遇到问题了再做修改,保留 cin si 两个设置。

然后在测试 cindent 的时候发现它对 lambda 表达式的缩进不是很满意,help ci<TAB> 发现还有一个 cinoptions,查看文档发现我需要的是 j1

默认的是(在我上面那个 imap 生效之后):

1
2
3
sort(a.begin(), a.end(), [&](int x, int y) -> {
        return dfn[x] < dfn[y];
        });

如果是 j1,那么效果是:

1
2
3
sort(a.begin(), a.end(), [&](int x, int y) -> {
    return dfn[x] < dfn[y];
});

我比较喜欢的是这种。

然后对于 if 里面多条件的缩进,我想要的差不多是这样:

1
2
3
4
5
if(pos[x][y].second.first.top() < pos[x][y].first.first.top() ||
        y < x.top().second.first.second.first.back() && !y && !q.empty() &&
        cmp(foo, bar)) {
    // do something.    
}

这个似乎不需要额外设置了。

另外 set list 可以看不可见字符,所有设置取消不是用 unset 而是 set noxxxx


最后更新: November 19, 2023