我的 Vim 配置 [已过时]

已过时

本篇内容已过时,Vim 配置已经发生了翻天覆地的变化,适时会开新篇重写。


因篇幅有限,本篇内容仅包括配置文件 .vimrc 的介绍,最多会稍微提及其余相关配置,但不会作为主要内容。

后来我的 AutoHotkey 脚本进行了由 v1.1 语法转向 v2 语法的重构,因此可能与本文脚本有较大差异。最新以 GitHub 仓库为准。

由于我的配置文件比较杂乱,注释信息少,很多以前配的内容现在已经遗忘了。于是出于加深对自己配置的了解,同时边重温边更新或修正的目的,决定写下这篇博文。也为将来投 Neovim 打好基础

设置

基本

第一部分 Set 有点多,也没分类,我就每 15 行单拎出来吧。

同时这一部分很多都是网上东拼西凑抄过来的,很多即使我看 help 也不知所以,因此也不会细谈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set nocompatible
set fileformat=unix
set fileencodings=utf-8,gbk2312,gbk,gb18030,cp936
set encoding=utf-8
set nobomb
set magic
set smartcase
set laststatus=2
set showtabline=2
set history=1024
set autochdir
set whichwrap=b,<,>,[,]
set backspace=indent,eol,start
set clipboard+=unnamed
set winaltkeys=no

设置了 magic,大概是为了搜索方便吧。

设置 winaltkeysno,毕竟我要映射 Alt,同时我不需要用 Alt 访问菜单(毕竟都没菜单了)。

1
2
3
4
5
6
7
8
9
10
set langmenu=zh_CN
set cursorline
set hlsearch
set incsearch
set number
set relativenumber
set wrap
set linebreak
set splitbelow
set splitright

设置 cursorline,方便看到正在编辑的行。

设置 hlsearch,高亮搜索。

设置 numberrelativenumber,显示相对行号。

设置 wraplinebreak,长行在相对合理(有时效果不如在最后一个字符断开)地方回绕。

设置 splitbelowsplitright,优先向下和向右分割窗口。

1
2
3
4
5
6
7
8
9
10
11
12
set guioptions-=e
set guioptions-=g
set guioptions-=m
set guioptions-=r
set guioptions-=L
set guioptions-=t
set guioptions-=T
set autoindent
set smartindent
set foldmethod=marker
set guifont=JetBrains_Mono:h15
set guifontwide=Microsoft_Yahei_Mono:h15

guioptions 去掉 egmrLtT,好像就啥也不剩了(默认也才 egmrLT)。这样一通设置后,Vim 窗口打开就是下面那样了。

当然这还是很不爽,最上面的白框框太突兀了,这时候就要用 AutoHotkey 了。

Vim.ahkGitHub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#Requires AutoHotkey v1.1+

SysGet WorkAreaInfo, MonitorWorkArea

ListJoin(list, string) {
for index, content in list
str .= string . content
return SubStr(str, StrLen(string) + 1)
}

!q::
Clip := ClipboardAll
Clipboard := ""
SendInput {Ctrl Down}c{Ctrl Up}
ClipWait 0
if InStr(Clipboard, "`r`n")
Run % "gvim -d """ ListJoin(StrSplit(Clipboard, "`r`n"), """ """) """", , , process_id
else if !ErrorLevel
Run gvim "%Clipboard%", , , process_id
else
Run gvim, , , process_id
Process Priority, %process_id%, High
WinWait ahk_pid %process_id%, , 10
WinSet Style, -0xC40000, ahk_pid %process_id%
WinMove ahk_pid %process_id%, , 0, 0, %WorkAreaInfoRight%, %WorkAreaInfoBottom%
WinActivate ahk_pid %process_id%
Clipboard := Clip
return

AutoHotkey 的内容就不细谈的,毕竟要东拉西扯的话这篇博文不知道要写多长多久。简单来说这个脚本的作用就是按 Alt + Q 打开无白框框的 gVim 窗口。

当没选择文件时,直接打开空白窗口;当选择了一个文件时,打开该文件;当选择了多个文件时,对比多个文件。

当然还是有缺陷的:选择文本时会打开文件名为该文本的窗口,多行文本则会进行对比。

但是总的来说还是够用的。快捷键在写,在测试时只是随便赋的,结果一直用到现在,好不好用我也不好评价,习惯了倒是真的。

设置 autoindentsmartindent,自动智能缩进。

设置 foldmethodmarker,指定用标志折叠。这个设置可能不太好。

设置 guifontJetBrains MonoguifontwideMicrosoft Yahei Mono。微软雅黑 Mono 是为了弥补中文字体而设置的。尝试过设置 Nerd Font,但是怎么弄都不行,于是就放弃了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set conceallevel=2
set wildmenu
set scrolloff=10
set noshowmode
set tabstop=4
set expandtab
set softtabstop=4
set shiftwidth=4
set viewoptions-=options
set undofile
set undodir=D:\Temp\.vim\.undo\
set directory=D:\Temp\.vim\.swap\
set viewdir=D:\Temp\.vim\.view\
set shortmess+=F
set background=dark
set listchars=tab:!>,trail:·,lead:·
set list
set gdefault
set titlestring=%{mode()}\&%{v:servername}

let $LANG = 'en_US.UTF-8'
let &pythonthreedll = 'D:\Program Files\Python\python310.dll'

设置 wildmenu,应该与补全有关。

设置 scrolloff 为 10,防止蒙着眼横冲直撞。

设置 noshowmode,有了状态栏就不用了。

设置 tabstop 为 4,默认制表符长度为 4 个空格。有时觉得有点长了。同时设置 expandtab,我好像不是很喜欢制表符。softtabstop 应该是相关设置。

viewoptions 去掉了 options于是只剩下 view,剩余 folds,cursor,curdir。即下次打开相同文件时保留折叠、光标和窗口目录,不保留局部选项和映射。

undodir directory viewdir 分别是「撤销文件位置」「交换文件所在位置」和「view 储存位置」。

shortmess 增加 F 变成 filnxtToOSF,这个选项解决了编辑文件时有时会出现要多按一下才能继续编辑的问题。

background 设置深色主题。

listlistchars 设置制表符和首尾空格的显示方式。

gdefault 默认全局替换。难怪,我就说为什么有时候开 g 选项反而没有全局替换,原来是开了 gdefault 啊。

titlestring 设置为 %{mode()}\&%{v:servername},像上面一样 n&GVIM2 就是一个例子。第一个字符表示模式。后面 GVIM 表示第一个窗口,GVIM2 表示这是第三个 gVim 窗口。可以用来与 AutoHotkey 交互。下面是一个例子。

Vim.ahkGitHub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#Requires AutoHotkey v1.1+

IsNotEnglish() {
DetectHiddenWindows On
WinGet winid, ID, A
wintitle := "ahk_id " DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", winid, "Uint")
SendMessage 0x283, 0x001, 0, , %wintitle%
DetectHiddenWindows Off
return ErrorLevel
}

#If WinActive("^(i|s|v|V)")

CapsLock::SendInput ô
+CapsLock:: SendInput {Shift Down}ô{Shift Up}

#If WinActive("^i") and IsNotEnglish()

#Hotstring * C0 ? X

::jkk::SendInput {Esc}
::kjj::SendInput {Esc}

在插入、选择、Visual 及多行 Visual 模式下,CapsLock 会被映射为「ô」(即 Alt + T)。

已经改为如下所示的快捷键。

1
2
3
4
5
6
#HotIf WinActive("^(i|s|v|V)") && WinActive("ahk_class Vim")

CapsLock::SendInput("{Alt Down}{F12}{Alt Up}")
+CapsLock::SendInput("{Alt Down}{Shift Down}{F12}{Shift Up}{Alt Up}")
^CapsLock::SendInput("{Ctrl Down}{Alt Down}{F12}{Alt Up}{Ctrl Up}")
^+CapsLock::SendInput("{Ctrl Down}{Shift Down}{Alt Down}{F12}{Alt Up}{Shift Up}{Ctrl Up}")

CapsLock 映射为一个几乎用不到的键 Alt + F12,具体内容在讲 UltiSnips 时再谈。

在插入模式且非英文输入下,jkkkjj 会退出插入模式。本来用的是 jkkj,但是误触率不小(毕竟以这些为辅音的词语可不少),于是改成这样,但是这样导致中文输入下退出难度增大,还没有养成肌肉记忆。

当然还有一点弊端,这样咋知道这是 Vim 窗口?一个标题名为「visual」的窗口也会触发,原本是不会的,后来我为了精简标题而删掉无关文本。还是要加个的。 后来加入了 ahk_class 的判断,消除了这个问题。

$LANG 设置为 en_US.UTF-8,可能是为了避免提示信息中英混杂吧。

&pythonthreedll 设置 Python。有段时间打开 Vim 时一直报错,经过我严密的测试发现问题出在 Vim 9.0 patch 845 上(根据我的备忘录记录,撰博时未再次测试),也许是编译的人环境发生了变化,我没看出有什么端倪。最后设置了这个才好。

1
2
3
4
filetype plugin indent on
syntax enable

colorscheme gruvbox

上面两行设置在了杂项下,主题设置在了插件下。

杂项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
" WindowsTerminal {{{1
if &term == 'win32'
if exists('+termguicolors')
let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
set termguicolors
endif
let &t_SI .= "\e[5 q"
let &t_SR .= "\e[3 q"
let &t_EI .= "\e[0 q"
autocmd VimEnter * silent !echo -ne "\e[0 q"
autocmd VimLeave * silent !echo -ne "\e[5 q"
endif
" }}}1

备注了个 WindowsTerminal,查了一下应该是修改 Windows Terminal 下 Vim 的光标形状。不过其实我现在用 gVim,完全可以把这个删掉。

更新

已经移除了

1
2
3
4
5
6
7
augroup auto_view
" auto_view {{{1
autocmd!
autocmd BufWinLeave *.* mkview
autocmd BufWinEnter *.* silent loadview
" }}}1
augroup end

自动记录和加载 view,本来还有 _vimrc 的,但是我改名为 .vimrc 后就删掉了。

1
2
3
4
5
6
7
augroup spell_check
" spell_check {{{1
autocmd!
autocmd FileType tex,markdown,gitcommit setlocal spell spelllang=en_us,cjk
autocmd FileType tex,markdown,gitcommit inoremap <silent><C-n> <C-g>u<Esc>[s1z=`'a<C-g>u
" }}}1
augroup end

自动更正拼写,目前用于 markdown, TeX\TeX 和 Git commit 信息。使用快捷键 Ctrl + N。Castel 的原映射如下。我不知道怎么改了一点,我记得我在用 Castel 的映射时出现了一点问题,于是进行了部分修改。

inoremap <C-l> <c-g>u<Esc>[s1z=`]a<c-g>u
1
2
3
4
5
6
7
8
9
10
11
12
13
14
" win-clipboard {{{1
let g:clipboard = {
\ 'name': 'win32yank',
\ 'copy': {
\ '+': 'win32yank.exe -i --crlf',
\ '*': 'win32yank.exe -i --crlf',
\ },
\ 'paste': {
\ '+': 'win32yank.exe -o --lf',
\ '*': 'win32yank.exe -o --lf',
\ },
\ 'cache_enabled': 0,
\ }
" }}}1

好像是用来互通剪贴板的?

插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let g:language_types = ['python', 'javascript', 'vim', 'autohotkey']
call plug#begin("~/vimfiles/plugged")
" Plug {{{1
Plug 'morhetz/gruvbox'
Plug 'yianwillis/vimcdoc'
Plug 'preservim/nerdtree'
Plug 'Xuyuanp/nerdtree-git-plugin'
Plug 'itchyny/lightline.vim'
Plug 'mengelbrecht/lightline-bufferline'
Plug 'maximbaz/lightline-ale', { 'for': g:language_types }
Plug 'mhinz/vim-startify'
Plug 'junegunn/vim-easy-align'
Plug 'neoclide/coc.nvim', { 'branch': 'release' }
Plug 'dense-analysis/ale', { 'for': g:language_types }
Plug 'SirVer/ultisnips'
Plug 'Yggdroot/LeaderF'
Plug 'tpope/vim-fugitive'
Plug 'tpope/vim-commentary'
Plug 'tpope/vim-repeat'
Plug 'pilgrimlyieu/vim-surround'
Plug 'easymotion/vim-easymotion'
Plug 'ZSaberLv0/vim-easymotion-chs'
Plug 'mg979/vim-visual-multi'
Plug 'luochen1990/rainbow'
Plug 'Yggdroot/indentLine'
Plug 'lervag/vimtex', { 'for': ['tex', 'markdown'] }
Plug 'pilgrimlyieu/md-img-paste.vim', { 'for': 'markdown' }
Plug 'mzlogin/vim-markdown-toc', { 'for': 'markdown'}
Plug 'python-mode/python-mode', { 'for': 'python', 'branch': 'develop' }
Plug 'rrethy/vim-hexokinase', { 'do': 'make hexokinase' }
Plug 'vim-autoformat/vim-autoformat'
" }}}1
call plug#end()

gruvbox:主题。

vimcdoc:中文文档。

nerdtree:显示文件列表。

nerdtree-git-plugin:NerdTree 集成 Git。

lightline:状态栏。

lightline-bufferline:缓冲区栏。

lightline-ale:状态栏显示语法警告错误信息等。

vim-startify:开始面板。没怎么见过,也就没修改了,使用了默认的。

vim-easy-align:对齐。

coc.nvim:代码补全。

ale:语法检查。

ultisnips:Snippets 工具。

LeaderF:搜索。

vim-fugitive:Git。

vim-commentary:注释。

vim-repeat:重复操作(为适配部分插件如 vim-surround 等)

vim-surround:添加引号、括号、标签等。因为作者不知出于什么原因没有 merge 一个重要 PR: Support deleting/changing custom surrounds,我就 Fork 了一个(没用 PR 作者的,怕他删掉了)。

vim-easymotion:快速移动。

vim-easymotion-chs:为快速移动添加中文拼音支持。

vim-visual-multi:多光标。

rainbow:彩色括号。

indentLine:缩进线。刚刚去看才发现 archived 了。

vimtex:提供 TeX\TeX 及 markdown 数学支持。

md-img-paste.vim:markdown 插入剪贴板图片。我根据自己需求 Fork 然后改了一点。前些日子里不小心 Revert 了,靠本地文件恢复,只是就不知道我落后几个 commits 了,也许有些错误修复。

vim-markdown-toc:创建目录。只不过现在似乎用处不大。

python-mode:Python 支持。感觉也用处不大。Python 写得少,要么摸鱼要么在写 AutoHotkey,而且我 Python 可能不在 Vim 里写。

vim-hexokinase:显示色彩。有时倒让人啼笑皆非,例如微软的 Azure 也会显示颜色。这个去看发现也 archived 了。看到它有个 Fork 有 10 多个 commits ahead,看样子是添加了 LaTeX\LaTeX 相关支持。

vim-autoformat:格式化代码。

映射

1
2
3
4
5
6
let g:mapleader = " "

function! Execute(cmd)
execute a:cmd
return ''
endfunction

设置 Leader 键为空格键。同时定义一个执行函数方便调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
inoremap jk      <Esc>
inoremap kj <Esc>
nnoremap U <C-r>
vnoremap ; :
nnoremap ; :
nnoremap : ,
nnoremap , ;
nnoremap ` '
nnoremap ' `
noremap H 0
onoremap H 0
noremap L $
onoremap L $
nnoremap <expr>0 col('.') == 1 ? '^' : '0'
nnoremap <C-f> <C-d>
nnoremap <C-b> <C-u>

jkkj 上面谈过了。

U 重做。

; 进入命令模式,这样只用按一个键,比较方便。

, 重复寻找命令,因为 ; 被占用了,用 , 替代。

: 用来反向寻找。原因同上。

交换了 '`,精准位置应该更方便按才行。

HL 分别到行首和行尾。

0 可以在行首和行首非空白字符跳转。

剩下两个就是翻页了,改为翻半页(用整页主要是因为好按)。

1
2
nnoremap <silent> <leader>o :<C-u>call append(line("."),   repeat([""], v:count1))<CR>
nnoremap <silent> <leader>O :<C-u>call append(line(".")-1, repeat([""], v:count1))<CR>

用来在 normal 模式插入空行(光标位置不动),摘抄自 Stack Overflow

1
2
3
4
nnoremap <silent><leader>/ :noh<Cr>
vnoremap / /\v
nnoremap ? ?\v
vnoremap ? ?\v

查找。Space + / 清除高亮搜索结果。\v 应该是为了更 Magic。

1
2
3
4
5
6
7
8
9
10
11
12
inoremap <silent><C-j> <C-r>=Execute('normal! <C-v><C-w>j<C-v><Esc>')<Cr>
inoremap <silent><C-k> <C-r>=Execute('normal! <C-v><C-w>k<C-v><Esc>')<Cr>
inoremap <silent><C-h> <C-r>=Execute('normal! <C-v><C-w>h<C-v><Esc>')<Cr>
inoremap <silent><C-l> <C-r>=Execute('normal! <C-v><C-w>l<C-v><Esc>')<Cr>
nnoremap <C-j> <C-W>j
nnoremap <C-k> <C-W>k
nnoremap <C-h> <C-W>h
nnoremap <C-l> <C-W>l
nnoremap <C-S-j> <C-W>-
nnoremap <C-S-k> <C-W>+
nnoremap <C-S-h> <C-W><
nnoremap <C-S-l> <C-W>>

这些是用来调整窗口的。窗口切换不用多说,插入和普通模式都加入了。窗口大小的变动遵循「上加下减,左减右加」原则,即按左下角窗口的视角调整。

1
2
3
4
nnoremap k  gk
nnoremap gk k
nnoremap j gj
nnoremap gj j

回绕处理。

nnoremap gA ga

ga 用来对齐了,用 gA 接任显示 ASCII 值的任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
inoremap <silent><C-s>     <C-r>=Execute('w')<Cr>
inoremap <silent><C-q> <C-r>=Execute('x')<Cr>
inoremap <silent><C-S-q> <C-r>=Execute('q!')<Cr>
inoremap <silent><C-S-c> <C-r>=Execute('bw')<Cr>
nnoremap <C-q> ZZ
nnoremap <C-S-q> ZQ
nnoremap <silent><C-s> :w<Cr>
" nnoremap <silent><C-S-c> :bw<Cr>
nnoremap <leader>q ZZ
nnoremap <leader>Q ZQ
nnoremap <silent><leader>w :w<Cr>
" nnoremap <silent><leader>C :bw<Cr>
nnoremap <silent><S-Esc> :qa!<Cr>
vnoremap <C-q> <Esc>ZZ
vnoremap <C-S-q> <Esc>ZQ
vnoremap <silent><C-s> <Esc>:w<Cr>
" vnoremap <silent><C-S-c> <Esc>:bw<Cr>
vnoremap <silent><S-Esc> <Esc>:qa!<Cr>

基本上就是保存和退出命令。不知道为什么注释了几个清除缓冲区的,但是我也不敢动。

1
2
3
4
tnoremap <F1>           <C-\><C-N>
tnoremap <S-F1> <C-W><C-C>
tnoremap <silent><S-F5> <C-W>N:bw!<Cr>
nnoremap <silent><S-F5> :call CloseTerminal()<CR>

终端。可以说大概是我的 .vimrc 里自主完成最精致的一部分了。这部分只是映射罢了。F1 暂停终端,切换到终端普通模式。Shift + F1 结束作业。Shift + F5 抹杀终端。

1
2
nnoremap Q  <Nop>
nnoremap gq Q

经常误触进入 Ex 模式,改成 gq

1
2
3
4
5
set mouse=
noremap <ScrollWheelUp> <nop>
noremap <ScrollWheelDown> <nop>
inoremap <ScrollWheelUp> <nop>
inoremap <ScrollWheelDown> <nop>

禁用鼠标。

插件

这里的下辖标题不代表插件名称,只代表这在我的 .vimrc 被放在一个 part 里。出于「历史原因」,可能会莫名其妙。

vim-surround

1
2
3
4
5
6
7
8
9
10
11
12
" Custom Surroundings {{{1
let g:surround_{char2nr('(')} = "(\r)"
let g:surround_{char2nr(')')} = "(\r)"
let g:surround_{char2nr('【')} = "【\r】"
let g:surround_{char2nr('】')} = "【\r】"
let g:surround_{char2nr('《')} = "《\r》"
let g:surround_{char2nr('》')} = "《\r》"
let g:surround_{char2nr('‘')} = "「\r」"
let g:surround_{char2nr('’')} = "「\r」"
let g:surround_{char2nr('“')} = "『\r』"
let g:surround_{char2nr('”')} = "『\r』"
" }}}1

加入了中文标点。不过「【】」没啥用的样子。

Markdown

这就来了,不是插件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
" Markdown {{{1
" autocmd FileType markdown inoremap <silent><C-x> <Cr><Cr><hr class='section'><Cr><Cr>
autocmd FileType markdown inoremap <silent><C-p> <C-r>=Execute('call mdip#MarkdownClipboardImage()')<Cr>
autocmd FileType markdown inoremap <silent><C-t> <C-r>=Execute('UpdateToc')<Cr>
autocmd FileType markdown nnoremap <silent><leader>mt :UpdateToc<Cr>
autocmd FileType markdown vnoremap <silent><leader>vl <Plug>(EasyAlign)*<C-x>\\\@<!<Bar><Cr>
autocmd FileType markdown vnoremap <silent><leader>vr <Plug>(EasyAlign)*<C-a><Bs>r<Cr><C-x>\\\@<!<Bar><Cr>
autocmd FileType markdown vnoremap <silent><leader>vv <Plug>(EasyAlign)*<C-a><Bs>c<Cr><C-x>\\\@<!<Bar><Cr>
autocmd FileType markdown nnoremap <silent><leader>vl <Plug>(EasyAlign)ip*<C-x>\\\@<!<Bar><Cr>
autocmd FileType markdown nnoremap <silent><leader>vr <Plug>(EasyAlign)ip*<C-a><Bs>r<Cr><C-x>\\\@<!<Bar><Cr>
autocmd FileType markdown nnoremap <silent><leader>vv <Plug>(EasyAlign)ip*<C-a><Bs>c<Cr><C-x>\\\@<!<Bar><Cr>

autocmd FileType markdown inoreabbr <silent>toc <C-r>=Execute('GenTocGFM')<Cr>

autocmd FileType markdown let b:coc_pairs_disabled = ["'"]

第一个是 Ctrl + X 添加特制分隔符,用于已经弃用的高中 ENotebook。因此注释掉了。

然后是 Ctrl + P 加入图片及 Ctrl + T 更新目录,都是插入模式的。正常模式则是 Space + M + T

接下来六个如果我没记错的话是表格对齐,前缀是 Space + V,然后 l r v 分别代表左、右、中对齐。

toc 则是从没用过的缩写,生成 GFM 格式的 TOC。

b:coc_pairs_disabled 则是 coc-pairs 的一个选项,我设置不会自动补全 ',理由忘了,但这种莫名其妙的设置一定是有深意的,所以也就不动了。

1
2
3
4
5
6
let g:vmt_auto_update_on_save = 0
let g:vmt_fence_text = 'TOC Start'
let g:vmt_fence_closing_text = 'TOC End'
let g:vmt_list_item_char = '-'
let g:mdip_imgdir = 'images'
let g:mdip_imgname = ''

TOC 和保存图片的设置,浅显易懂,也没啥好解释的。

VimTeX

这里比较重量级,毕竟配置内容多,又是到处抄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let g:tex_conceal = ''

" Viewer options: One may configure the viewer either by specifying a built-in
" viewer method:
" let g:vimtex_view_method = 'SumatraPDF'

" Or with a generic interface:
let g:vimtex_view_general_viewer = 'SumatraPDF'
" let g:vimtex_view_general_options = ''

" SumatraPDF Setting
" gvim --servername GVIM --remote-send "<C-\><C-n>:drop %f<CR>:%l<CR>:normal! zzzv<CR>:execute 'drop ' . fnameescape('%f')<CR>:%l<CR>:normal! zzzv<CR>:call remote_foreground('GVIM')<CR><CR>"

let g:vimtex_view_general_options = '-reuse-instance -forward-search @tex @line @pdf'

我似乎关闭了 conceal,这个功能吧不好评价。默认是 admgs,即替换重音/连写体、定界符、数学符号、希腊字母及上下标。

注释掉了 g:vimtex_view_method,采用了 g:vimtex_view_general_viewerSumatraPDF

SumatraPDF Setting 是自己加的注释。应该就是把下面内容加入 SumatraPDF 设置里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
" VimTeX uses latexmk as the default compiler backend. If you use it, which is
" strongly recommended, you probably don't need to configure anything. If you
" want another compiler backend, you can change it as follows. The list of
" supported backends and further explanation is provided in the documentation,
" see ":help vimtex-compiler".
let g:vimtex_compiler_method = 'latexmk'

" Most VimTeX mappings rely on localleader and this can be changed with the
" following line. The default is usually fine and is the symbol "\".
let maplocalleader = ","

let g:tex_flavor = "latex"

let g:vimtex_texcount_custom_arg = " -ch -total"

autocmd FileType tex noremap <buffer> <silent> <leader>lw :VimtexCountWords! <CR><CR>

使用推荐的 latexmk 作为编译后端。同时映射了一个局部 Leader,,没怎么用过。

g:tex_flavor 采用 latex,没什么好说的。

最后一个选项应该是统计字数什么的。

然后映射了 ,lw 来统计字数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let g:Tex_ViewRule_pdf = 'D:\Program Files\SumatraPDF\SumatraPDF.exe -reuse-instance -inverse-search "gvim -c \":RemoteOpen +\%l \%f\""'

let g:vimtex_compiler_latexmk_engines = {
\ '_' : '-pdf',
\ 'pdflatex' : '-pdf',
\ 'dvipdfex' : '-pdfdvi',
\ 'lualatex' : '-lualatex',
\ 'xelatex' : '-xelatex',
\ 'context (pdftex)' : '-pdf -pdflatex=texexec',
\ 'context (luatex)' : '-pdf -pdflatex=context',
\ 'context (xetex)' : '-pdf -pdflatex=''texexec --xtx''',
\}

let g:vimtex_compiler_latexmk = {
\ 'out_dir' : {-> 'out'},
\ 'callback' : 1,
\ 'continuous' : 1,
\ 'executable' : 'latexmk',
\ 'hooks' : [],
\ 'options' : [
\ '-verbose',
\ '-file-line-error',
\ '-shell-escape',
\ '-synctex=1',
\ '-interaction=nonstopmode',
\ ],
\}

这些设置都是抄的,不明所以,更是动也不敢动。

1
2
3
4
5
6
7
let g:vimtex_syntax_conceal_disable   = 1
let g:vimtex_quickfix_open_on_warning = 0

let g:vimtex_toggle_fractions = {
\ 'frac': 'dfrac',
\ 'dfrac': 'frac',
\}

禁用 conceal 及编译警告。

然后是分数切换。

1
2
3
4
augroup vimtex_config
autocmd!
autocmd User VimtexEventQuit call vimtex#compiler#clean(0)
augroup end

这个是被我注释掉的。用处是清理副产品。

这个配置得找个机会大改一下。之前不怎么用 LaTeX\LaTeX,但是到后面还是会用到的,得更新一下。

查了一下我配置的出处大概是这里

UltiSnips

万恶之源。

1
2
3
4
5
6
let g:UltiSnipsExpandTrigger       = '<C-e>'
let g:UltiSnipsListSnippets = '<S-Tab>'
let g:UltiSnipsJumpForwardTrigger = 'ô'
let g:UltiSnipsJumpBackwardTrigger = '<S-ô>'
let g:UltiSnipsEditSplit = "vertical"
let g:UltiSnipsSnippetDirectories = ['Snips']

也很清晰。Ctrl + E 触发;Shift + Tab 列举;CapsLockShift + CapsLock 跳转。垂直分割打开编辑窗口;Snippets 存在 Snips 里。

一开始用的是全 Tab,但是冲突确实严重。然后改成 CapsLock 触发,Tab 跳转,但是缩进还是用到了 Tab。最后完全放弃 Tab,但是确实不如之前舒服了。Ctrl + E 这个键我也觉得不好,可能要改。

更新

全面重制了 UltiSnips 快捷键

1
2
3
4
5
6
let g:UltiSnipsEditSplit           = "vertical"
let g:UltiSnipsSnippetDirectories = ["Snips"]
let g:UltiSnipsListSnippets = "<C-S-A-F12>"
let g:UltiSnipsExpandTrigger = "<C-A-F12>"
let g:UltiSnipsJumpForwardTrigger = "<A-F12>"
let g:UltiSnipsJumpBackwardTrigger = "<A-S-F12>"

上面说了将 CapsLock 映射为了 Alt + F12。也就是说 Ctrl + Shift + CapsLock 可以显示这里能触发什么 snippets;Ctrl + CapsLock 可以展开 snippets;CapsLockShift + CapsLock 能跳转 tabstops。这个快捷键设置就合理且方便多了。

1
2
inoremap <silent><A-F12>   <C-r>=IncreaseColNumber()<Cr>
inoremap <silent><A-S-F12> <C-r>=DecreaseColNumber()<Cr>

同时还为跳转键加了两个 Tabout 映射,这样即使无法跳转 tabstops 也能起到功效。具体见 Tabout 那里。

nnoremap <silent><C-d> <Esc>:call UltiSnips#RefreshSnippets()<Cr>

这是用来 Debug Snippets 的。使用 Ctrl + D 刷新 Snippets。唯一不足就是 pythonx 文件的修改仍然需要重启。

2023 年 9 月 29 日更新

在魔改 UltiSnips 源码的同时又进行了部分改动。

以上的快捷键需要 Ctrl + CapsLock 才能展开 snippet,终究还是不爽。于是希望 CapsLock 能同时起到展开和跳转的功能,在歧义时优先展开,但为了歧义时也能正常跳转,我还为 Ctrl + CapsLock 单独映射了跳转的功能,如下:

1
2
3
4
5
let g:UltiSnipsExpandTrigger      = "<A-F12>"
let g:UltiSnipsJumpForwardTrigger = "<A-F12>"

inoremap <silent><C-A-F12> <C-r>=UltiSnips#JumpForwards()<Cr>
snoremap <silent><C-A-F12> <Esc>:call UltiSnips#JumpForwards()<Cr>

但实际体验中我发现,我觉得优先考虑跳转似乎会好一点。不过 UltiSnips 并不支持,于是我依葫芦画瓢魔改了一下源码并提交了一个 PR(Add JumpOrExpand support),目前还未合并。

按照我的 PR,只需要改为下面就好了:

1
2
3
4
let g:UltiSnipsJumpOrExpandTrigger = "<A-F12>"

inoremap <silent><C-A-F12> <C-r>=UltiSnips#ExpandSnippet()<Cr>
snoremap <silent><C-A-F12> <Esc>:call UltiSnips#ExpandSnippet()<Cr>

但我发现这样一来,就无法使用 Tabout 了。即使按文档中三元表达式方法,我也没能成功(因为没两个 Trigger 它就原样映射了,我也懒得加判断了)。于是我就再魔改了一下(当然没提交到 PR,纯粹自用),把映射 i s 的部分禁用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function! UltiSnips#map_keys#MapKeys() abort
if 0
if exists("g:UltiSnipsExpandOrJumpTrigger")
exec "inoremap <silent> " . g:UltiSnipsExpandOrJumpTrigger . " <C-R>=UltiSnips#ExpandSnippetOrJump()<cr>"
exec "snoremap <silent> " . g:UltiSnipsExpandOrJumpTrigger . " <Esc>:call UltiSnips#ExpandSnippetOrJump()<cr>"
elseif exists("g:UltiSnipsJumpOrExpandTrigger")
exec "inoremap <silent> " . g:UltiSnipsJumpOrExpandTrigger . " <C-R>=UltiSnips#JumpOrExpandSnippet()<cr>"
exec "snoremap <silent> " . g:UltiSnipsJumpOrExpandTrigger . " <Esc>:call UltiSnips#JumpOrExpandSnippet()<cr>"
elseif g:UltiSnipsExpandTrigger == g:UltiSnipsJumpForwardTrigger
exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <C-R>=UltiSnips#ExpandSnippetOrJump()<cr>"
exec "snoremap <silent> " . g:UltiSnipsExpandTrigger . " <Esc>:call UltiSnips#ExpandSnippetOrJump()<cr>"
else
exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <C-R>=UltiSnips#ExpandSnippet()<cr>"
exec "snoremap <silent> " . g:UltiSnipsExpandTrigger . " <Esc>:call UltiSnips#ExpandSnippet()<cr>"
endif
endif
exec "xnoremap <silent> " . g:UltiSnipsExpandTrigger. " :call UltiSnips#SaveLastVisualSelection()<cr>gvs"
if len(g:UltiSnipsListSnippets) > 0
exec "inoremap <silent> " . g:UltiSnipsListSnippets . " <C-R>=UltiSnips#ListSnippets()<cr>"
exec "snoremap <silent> " . g:UltiSnipsListSnippets . " <Esc>:call UltiSnips#ListSnippets()<cr>"
endif

snoremap <silent> <BS> <c-g>"_c
snoremap <silent> <DEL> <c-g>"_c
snoremap <silent> <c-h> <c-g>"_c
snoremap <c-r> <c-g>"_c<c-r>
endf

然后配置文件改为如下(相同部分略):

1
2
3
4
5
6
7
8
9
10
11
12
let g:ulti_expand_or_jump_res = 0
function! Ulti_JumpOrExpand_and_getRes()
call UltiSnips#JumpOrExpandSnippet()
return g:ulti_expand_or_jump_res
endfunction

let g:UltiSnipsExpandTrigger = "<A-F12>"

inoremap <silent><A-F12> <C-r>=(Ulti_JumpOrExpand_and_getRes() > 0) ? "" : IncreaseColNumber()<Cr>
snoremap <silent><A-F12> <Esc>:exec (Ulti_JumpOrExpand_and_getRes() > 0) ? "" : IncreaseColNumber()<Cr>
inoremap <silent><C-A-F12> <C-r>=UltiSnips#ExpandSnippet()<Cr>
snoremap <silent><C-A-F12> <Esc>:call UltiSnips#ExpandSnippet()<Cr>

最后删掉之前的一行映射即可:

inoremap <silent><A-F12> <C-r>=IncreaseColNumber()<Cr>

同时我还把 snippet_manager.py 相关的 _handle_failure() 注释了,原因和一个 issue(if expand fails, the trigger key is inserted) 一样。

easymotion

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

nnoremap <leader>j <Plug>(easymotion-j)
nnoremap <leader>k <Plug>(easymotion-k)
nnoremap <leader>h <Plug>(easymotion-linebackward)
nnoremap <leader>l <Plug>(easymotion-lineforward)
nnoremap <leader>. <Plug>(easymotion-repeat)
nnoremap <leader>f <Plug>(easymotion-f)
nnoremap <leader>F <Plug>(easymotion-F)
nnoremap <leader>t <Plug>(easymotion-t)
nnoremap <leader>T <Plug>(easymotion-T)
nnoremap <leader><leader>f <Plug>(easymotion-f2)
nnoremap <leader><leader>F <Plug>(easymotion-F2)
nnoremap <leader><leader>t <Plug>(easymotion-t2)
nnoremap <leader><leader>T <Plug>(easymotion-T2)
nnoremap <leader>s <Plug>(easymotion-s)
nnoremap s <Plug>(easymotion-s2)
nnoremap / <Plug>(easymotion-sn)\v
onoremap / <Plug>(easymotion-tn)\v
nnoremap n <Plug>(easymotion-next)
nnoremap N <Plug>(easymotion-prev)

基本上就是 Space 加操作符 hjklfFtTs 进行对应跳转。除了单独一个 s,因为不常用额外配置了。然后用其搜索替换了自带正向搜索。

vim-visual-multi

1
2
3
4
5
6
7
8
9
10
11
12
let g:VM_theme = 'iceblue'

let g:VM_maps = {}
let g:VM_maps["Exit"] = '<C-c>'
let g:VM_maps["Add Cursor Down"] = '<A-s>'
let g:VM_maps["Add Cursor Up"] = '<A-w>'
let g:VM_maps["Select l"] = '<A-d>'
let g:VM_maps["Select h"] = '<A-a>'
let g:VM_maps["Move Left"] = '<A-S-a>'
let g:VM_maps["Move Right"] = '<A-S-d>'
let g:VM_maps["Undo"] = 'u'
let g:VM_maps["Redo"] = 'U'

这些键击还是比较好理解的。Ctrl + C 本身就常作为退出键;WASD 也算是移动的经典按键。

LeaderF

这也是经典抄配置的例子了。应该来自这篇博文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let g:Lf_WorkingDirectoryMode = 'AF'
let g:Lf_RootMarkers = ['.git', '.root']
let g:Lf_DefaultExternalTool = 'rg'
let g:Lf_PreviewResult = {
\ 'File' : 0,
\ 'Buffer' : 0,
\ 'Mru' : 0,
\ 'Tag' : 0,
\ 'BufTag' : 1,
\ 'Function' : 1,
\ 'Line' : 0,
\ 'Colorscheme' : 0,
\ 'Rg' : 1,
\ 'Gtags' : 0
\}
let g:Lf_StlColorscheme = 'gruvbox_material'
let g:Lf_PopupColorscheme = 'gruvbox_material'
let g:Lf_WindowPosition = 'popup' " Use <leader>pt to switch between 'popup' & 'bottom' to avoid 'Replace can not be used in popout window' issue
let g:Lf_PreviewInPopup = 1

简而言之前两行配置就是找搜索起点。我设置了存在 .git.root 为根目录。A 表示找前级目录里最近的根目录;F 表示找工作目录,看当前文件是不是在 Vim 工作目录。都匹配不成功以此文件所在目录为起点(即默认的 c)。

然后使用外部工具 ripgrep,即 rg

g:Lf_PreviewResult 应该就是设置了查找 BufTag FunctionRg 时自动启用预览。为什么我这样配置我也不清楚。

搜索窗口我选择了 Popup,因为挺好看的。但是呢 Popup 窗口无法使用替换功能。于是我注释里写道使用 Space + P + T 切换 Popup 和 Bottom 显示。具体映射如下

noremap <expr><silent><leader>pt ":let g:Lf_WindowPosition = '" . (g:Lf_WindowPosition == "popup" ? "bottom" : "popup") . "'<Cr>"
1
2
3
4
5
noremap <unique><leader>p  <Nop>
noremap <silent><leader>pp :LeaderfSelf<Cr>
noremap <silent><leader>pl :LeaderfLine<Cr>
noremap <silent><leader>pf :LeaderfFile<Cr>
noremap <unique><leader>pr <Plug>LeaderfRgPrompt

Space + P 作为前缀按键。P 代表显示自己,自行选择功能;L 代表 搜索本文件行;F 代表查找文件;R 代表用 Rg 搜索,也是我最常用的,因为其他没记住。说实话映射确实有点少了,不过我觉得映射还是得建立在理解的基础上。

vim-easy-align

1
2
3
4
5
6
7
8
9
nnoremap ga <Plug>(EasyAlign)
vnoremap ga <Plug>(EasyAlign)

nnoremap <silent>g: <Plug>(EasyAlign)ip*<C-l>0<Cr><C-x>\\\@<!:\(=\)\@!<Cr>
vnoremap <silent>g: <Plug>(EasyAlign)*<C-l>0<Cr><C-x>\\\@<!:\(=\)\@!<Cr>
nnoremap <silent>g= <Plug>(EasyAlign)ip=<Cr>
vnoremap <silent>g= <Plug>(EasyAlign)*=<Cr>
nnoremap <silent><expr>g<Space> '<C-u><Plug>(EasyAlign)ip' . v:count1 . ' \<Cr>'
vnoremap <silent><expr>g<Space> '<Plug>(EasyAlign)' . v:count1 . ' \<Cr>'

ga 是基础键。剩下的是我配置的常用键。

例如 g: 用来对齐合理英文冒号(指前面没有 \ 与后面没有 =,主要是避免 := 被拆分,但是呢 \\: 也不行)。结果是对齐后冒号左边无间隔,右边有间隔,即 xxx: xxx 型。

再比如g= 就是对齐等号。

然后 g<Space> 就是对齐空格,这个还支持选择对齐第几个空格。v:count1 用法也是摸索出来的,虽然现在已经忘却了。算是我的一个得意之作。

ale

1
2
let g:ale_sign_error   = '>>'
let g:ale_sign_warning = '--'

寒酸的配置。似乎没用到过,AutoHotkey 没见过,Python 有 Python Mode,也许是 JS 和 C 会有?

coc

真正重头戏。配置近 200 行。我的配置与 GitHub 上示例已经大有不同了,我也就边写边改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let g:coc_data_home = $USERPROFILE . '/vimfiles/extra/coc'
let g:coc_global_extensions = [
\ 'coc-json',
\ 'coc-css',
\ 'coc-html',
\ 'coc-webview',
\ 'coc-markdown-preview-enhanced',
\ 'coc-git',
\ 'coc-highlight',
\ 'coc-pyright',
\ 'coc-snippets',
\ 'coc-ltex',
\ 'coc-pairs',
\ 'coc-tsserver',
\ 'coc-vimlsp',
\ 'coc-lists',
\ 'coc-vimtex',
\]

let g:coc_snippet_next = '<Tab>'
let g:coc_snippet_prev = '<S-Tab>'

设置 coc 存储目录,比较好整合。然后是一堆额外插件,就不额外介绍了。

1
2
3
4
5
6
7
8
9
10
11
" Some servers have issues with backup files, see #649.
set nobackup
set nowritebackup

" Having longer updatetime (default is 4000 ms = 4 s) leads to noticeable
" delays and poor user experience.
set updatetime=300

" Always show the signcolumn, otherwise it would shift the text each time
" diagnostics appear/become resolved.
set signcolumn=yes

照做,改。

1
2
3
4
5
6
7
8
9
10
11
12
13
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <C-z>
\ coc#pum#visible() ? coc#pum#next(1):
\ CheckBackspace() ? "\<C-z>" :
\ coc#refresh()
inoremap <silent><expr><C-S-z> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"

" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <C-c> coc#pum#visible() ? coc#pum#confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

更改快捷键:选择改为 Ctrl + Z,触发改为 Ctrl + C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function! CheckBackspace() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction

" Use <c-space> to trigger completion.
inoremap <silent><expr> <c-@> coc#refresh()

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)

" GoTo code navigation.
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

" Use K to show documentation in preview window.
nnoremap <silent> K :call ShowDocumentation()<CR>

function! ShowDocumentation()
if CocAction('hasProvider', 'hover')
call CocActionAsync('doHover')
else
call feedkeys('K', 'in')
endif
endfunction

" Highlight the symbol and its references when holding the cursor.
autocmd CursorHold * silent call CocActionAsync('highlight')

" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

都是没怎么用过的功能。真是暴殄天物。基本没动,把 nvim 的判断删了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
" Formatting selected code.
xmap <leader>f <Plug>(coc-format-selected)
nmap <leader>f <Plug>(coc-format-selected)

augroup mygroup
autocmd!
" Setup formatexpr specified filetype(s).
autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
" Update signature help on jump placeholder.
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

" Applying code actions to the selected code block
" Example: `<leader>aap` for current paragraph
" xmap <leader>a <Plug>(coc-codeaction-selected)
" nmap <leader>a <Plug>(coc-codeaction-selected)

" Remap keys for applying code actions at the cursor position
nmap <leader>ac <Plug>(coc-codeaction-cursor)
" Remap keys for apply code actions affect whole buffer
nmap <leader>as <Plug>(coc-codeaction-source)
" Apply the most preferred quickfix action to fix diagnostic on the current line
" nmap <leader>qf <Plug>(coc-fix-current)

" Remap keys for applying refactor code actions
nmap <silent> <leader>re <Plug>(coc-codeaction-refactor)
xmap <silent> <leader>r <Plug>(coc-codeaction-refactor-selected)
nmap <silent> <leader>r <Plug>(coc-codeaction-refactor-selected)

" Run the Code Lens action on the current line.
" nmap <leader>cl <Plug>(coc-codelens-action)

" Map function and class text objects
" NOTE: Requires 'textDocument.documentSymbol' support from the language server.
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
xmap ic <Plug>(coc-classobj-i)
omap ic <Plug>(coc-classobj-i)
xmap ac <Plug>(coc-classobj-a)
omap ac <Plug>(coc-classobj-a)

" Remap <C-f> and <C-b> for scroll float windows/popups.
" nnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
" nnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
inoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? "\<C-r>=coc#float#scroll(1)\<Cr>" : "\<Right>"
inoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? "\<C-r>=coc#float#scroll(0)\<Cr>" : "\<Left>"
vnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
vnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"

" Use CTRL-S for selections ranges.
" Requires 'textDocument/selectionRange' support of language server.
" nmap <silent> <C-s> <Plug>(coc-range-select)
" xmap <silent> <C-s> <Plug>(coc-range-select)

" Add `:Format` command to format current buffer.
command! -nargs=0 Format :call CocActionAsync('format')

" Add `:Fold` command to fold current buffer.
command! -nargs=? Fold :call CocAction('fold', <f-args>)

" Add `:OR` command for organize imports of the current buffer.
command! -nargs=0 OR :call CocActionAsync('runCommand', 'editor.action.organizeImport')

" Add (Neo)Vim's native statusline support.
" NOTE: Please see `:h coc-status` for integrations with external plugins that
" provide custom statusline: lightline.vim, vim-airline.
" set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}

" Mappings for CoCList
" Show all diagnostics.
" nnoremap <silent><nowait> <space>a :<C-u>CocList diagnostics<cr>
" Manage extensions.
" nnoremap <silent><nowait> <space>e :<C-u>CocList extensions<cr>
" Show commands.
" nnoremap <silent><nowait> <space>c :<C-u>CocList commands<cr>
" Find symbol of current document.
" nnoremap <silent><nowait> <space>o :<C-u>CocList outline<cr>
" Search workspace symbols.
" nnoremap <silent><nowait> <space>s :<C-u>CocList -I symbols<cr>
" Do default action for next item.
" nnoremap <silent><nowait> <space>j :<C-u>CocNext<CR>
" Do default action for previous item.
" nnoremap <silent><nowait> <space>k :<C-u>CocPrev<CR>
" Resume latest coc list.
" nnoremap <silent><nowait> <space>p :<C-u>CocListResume<CR>
" nnoremap <silent><nowait> <space>l :<C-u>CocList<CR>

剩下的都和文档一致,只是有些因为冲突或种种原因而被注释掉了。

NerdTree

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nnoremap <silent>T :NERDTree<CR>
let g:NERDTreeWinSize = 36
let g:NERDTreeGitStatusIndicatorMapCustom = {
\ 'Modified' :'✹',
\ 'Staged' :'✚',
\ 'Untracked' :'✭',
\ 'Renamed' :'➜',
\ 'Unmerged' :'═',
\ 'Deleted' :'✖',
\ 'Dirty' :'✗',
\ 'Ignored' :'☒',
\ 'Clean' :'✔︎',
\ 'Unknown' :'?',
\ }

朴实无华的设置。

Python

说是 Python,其实大部分与 Python 无关。先列出来跟 Python 有关的配置吧。

1
2
let g:python3_host_skip_check = 1
let g:python3_host_prog = '/usr/local/bin/python3'

咦好像就没了呢。不过接下来才是重头戏。一个完全不会 VimScript 的人千辛万苦写出的一个稀烂的 RunProgram 函数。作用是终端运行程序,支持 Python, JavaScript, AutoHotkey, batch 及 markdown 预览,其余情况打开内置终端。

现在让我解释我也解释不清了。尽量讲一点吧。

外部
1
2
3
4
5
6
7
8
nnoremap <silent><F5> :call RunProgram()<CR>
inoremap <silent><F5> <C-r>=Execute('call RunProgram()')<Cr>

autocmd FileType python,javascript nnoremap <silent><leader>Q :call CloseTerminal()<CR>

let g:terminal_settings = {'vertical': 1, 'term_cols': 60}

highlight default link Terminal Normal

F5 执行。在文件类型为 Python 或 JavaScript 时按 Space + Shift + Q 关闭终端。

终端采用垂直分割,宽度为 60,

RunProgram
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function! RunProgram()
if &filetype == ''
return
endif

silent execute 'w'
let l:filename = expand('%')
let l:opts = g:terminal_settings

if &filetype == 'python'
let l:term_col = OpenTerminal()
let l:opts.term_name = 'python_terminal'
if l:term_col
let l:opts.term_cols = l:term_col
endif
call term_start('python "' . l:filename . '"', l:opts)
elseif &filetype == 'javascript'
let l:term_col = OpenTerminal()
let l:opts.term_name = 'javascript_terminal'
if l:term_col
let l:opts.term_cols = l:term_col
endif
call term_start('node "'. l:filename . '"', l:opts)
elseif &filetype == 'autohotkey'
let l:autohotkey_ux_path = $ProgramFiles . '/AutoHotkey/UX/'
silent execute '!start "' . l:autohotkey_ux_path . 'AutoHotkeyUX.exe" "' . l:autohotkey_ux_path . 'launcher.ahk" /restart "%:p"'
elseif &filetype == 'markdown'
silent execute 'CocCommand markdown-preview-enhanced.openPreview'
elseif &filetype == 'dosbatch'
silent !%
else
let l:term_col = OpenTerminal()
let l:opts.term_name = 'Terminal'
if l:term_col
let l:opts.term_cols = l:term_col
endif
call term_start('cmd', l:opts)
endif
endfunction

分文件类型对应执行。Python 和 JavaScript 类似。AutoHotkey 及 markdown 预览都需另行配置。batch 最简单。

Terminal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function! OpenTerminal()
let l:windowsWithTerminal = filter(range(1, winnr('$')), 'getwinvar(v:val, "&buftype") ==# "terminal" || term_getstatus(winbufnr(v:val))')
if !empty(l:windowsWithTerminal)
silent execute l:windowsWithTerminal[0] . 'wincmd w'
let l:current_col = winwidth(l:windowsWithTerminal[0])
call CloseTerminal()
return l:current_col
endif
endfunction

function! CloseTerminal()
let l:winnumber = winnr()
if getwinvar(l:winnumber, "&buftype") ==# "terminal" || term_getstatus(winbufnr(l:winnumber))
silent execute winbufnr(l:winnumber) . 'bw!'
else
silent execute 'q!'
endif
endfunction

这个我现在自己也搞不懂啥意思了。反正呢,看名字就是一开一关。OpenTerminal 函数会关闭已有的终端,即保证始终只有一个终端在运行。

lightline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let g:lightline = {
\ 'colorscheme': 'gruvbox',
\ 'active': {
\ 'left': [ [ 'mode', 'paste' ],
\ [ 'gitbranch', 'readonly', 'filename' ] ],
\ 'right': [ [ 'lineinfo' ],
\ [ 'linter_checking', 'linter_errors', 'linter_warnings', 'linter_infos', 'linter_ok' ],
\ [ 'fileformat', 'fileencoding', 'filetype'] ]
\ },
\ 'tabline': {
\ 'left' : [ ['buffers'] ],
\ 'right' : [ ['close'] ]
\ },
\ 'component_expand': {
\ 'buffers' : 'lightline#bufferline#buffers',
\ 'linter_checking' : 'lightline#ale#checking',
\ 'linter_infos' : 'lightline#ale#infos',
\ 'linter_warnings' : 'lightline#ale#warnings',
\ 'linter_errors' : 'lightline#ale#errors',
\ 'linter_ok' : 'lightline#ale#ok',
\ },
\ 'component_type': {
\ 'buffers' : 'tabsel',
\ 'linter_warnings' : 'warning',
\ 'linter_errors' : 'error',
\ 'linter_ok' : 'ale',
\ },
\ 'component_function': {
\ 'gitbranch' : 'LightLineGitBranch',
\ 'lineinfo' : 'LightLineLineInfo',
\ },
\ 'separator': { 'left': "\ue0b0", 'right': "\ue0b2" },
\ 'subseparator': { 'left': "\ue0b1", 'right': "\ue0b3" }
\}

好累啊,感觉这个也没啥好讲的。本来也是在示例配置下,东拼西凑的。

1
2
3
function! LightLineGitBranch() 
return "\uE0A0" . (exists('*FugitiveHead') ? " " . FugitiveHead() : "")
endfunction

嗯这个应该是提供 Git 信息的,出处不详。

1
2
3
4
5
let g:viewplugins = 'NERD_tree\|\[Plugins\]\|\[coc-explorer\]'

function! LightLineLineInfo()
return expand('%:t') =~# g:viewplugins ? '' : printf(' %d/%d | %d/%d', line('.'), line('$'), col('.'), col('$'))
endfunction

在特定场合(NerdTree,插件更新及 coc-explorer)下不显示特定信息。

1
2
3
4
5
6
7
8
9
10
let s:palette = g:lightline#colorscheme#gruvbox#palette
let s:palette.tabline.ale = [['#282C34', '#92A4A4', 0, 21]]

let g:lightline#ale#indicator_checking = '⏳'
let g:lightline#ale#indicator_infos = '📜'
let g:lightline#ale#indicator_errors = '💥'
let g:lightline#ale#indicator_warnings = '⚡'
let g:lightline#ale#indicator_ok = '✨'
let g:lightline#bufferline#show_number = 2
let g:lightline#bufferline#unicode_symbols = 1

ale 的配置。颜色不太满意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
nnoremap <Leader>1  <Plug>lightline#bufferline#go(1)
nnoremap <Leader>2 <Plug>lightline#bufferline#go(2)
nnoremap <Leader>3 <Plug>lightline#bufferline#go(3)
nnoremap <Leader>4 <Plug>lightline#bufferline#go(4)
nnoremap <Leader>5 <Plug>lightline#bufferline#go(5)
nnoremap <Leader>6 <Plug>lightline#bufferline#go(6)
nnoremap <Leader>7 <Plug>lightline#bufferline#go(7)
nnoremap <Leader>8 <Plug>lightline#bufferline#go(8)
nnoremap <Leader>9 <Plug>lightline#bufferline#go(9)
nnoremap <Leader>0 <Plug>lightline#bufferline#go(10)
nnoremap <Leader>d1 <Plug>lightline#bufferline#delete(1)
nnoremap <Leader>d2 <Plug>lightline#bufferline#delete(2)
nnoremap <Leader>d3 <Plug>lightline#bufferline#delete(3)
nnoremap <Leader>d4 <Plug>lightline#bufferline#delete(4)
nnoremap <Leader>d5 <Plug>lightline#bufferline#delete(5)
nnoremap <Leader>d6 <Plug>lightline#bufferline#delete(6)
nnoremap <Leader>d7 <Plug>lightline#bufferline#delete(7)
nnoremap <Leader>d8 <Plug>lightline#bufferline#delete(8)
nnoremap <Leader>d9 <Plug>lightline#bufferline#delete(9)
nnoremap <Leader>d0 <Plug>lightline#bufferline#delete(10)

一些 buffer 的操作。

Rainbow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let g:rainbow_active = 1

let g:rainbow_conf = {
\ 'guifgs': ['#858580', '#8FBCBB', '#D08770', '#A3BE8C', '#EBCB8B', '#B48EAD', '#80a880', '#887070'],
\ 'ctermfgs': ['lightblue', 'lightyellow', 'lightcyan', 'lightmagenta'],
\ 'operators': '_,_',
\ 'parentheses': ['start=/(/ end=/)/ fold', 'start=/\[/ end=/\]/ fold', 'start=/{/ end=/}/ fold'],
\ 'separately': {
\ '*': {},
\ 'markdown': {
\ 'parentheses_options': 'containedin=markdownCode contained',
\ },
\ 'tex': {
\ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/'],
\ },
\ 'lisp': {
\ 'guifgs': ['royalblue3', 'darkorange3', 'seagreen3', 'firebrick', 'darkorchid3'],
\ },
\ 'vim': {
\ 'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/', 'start=/{/ end=/}/ fold', 'start=/(/ end=/)/ containedin=vimFuncBody', 'start=/\[/ end=/\]/ containedin=vimFuncBody', 'start=/{/ end=/}/ fold containedin=vimFuncBody'],
\ },
\ 'html': {
\ 'parentheses': ['start=/\v\<((area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)[ >])@!\z([-_:a-zA-Z0-9]+)(\s+[-_:a-zA-Z0-9]+(\=("[^"]*"|'."'".'[^'."'".']*'."'".'|[^ '."'".'"><=`]*))?)*\>/ end=#</\z1># fold'],
\ },
\ 'css': 0,
\ }
\}

照抄的。

indentLine

1
2
3
4
5
6
7
let g:indentLine_fileTypeExclude = ['json', 'markdown']
let g:indentLine_conceallevel = 2
let g:indentLine_concealcursor = ''
let g:indentLine_setConceal = 0
let g:indentLine_enabled = 1
let g:indent_guides_guide_size = 1
let g:indent_guides_start_level = 1

Fugitive

1
2
3
4
5
nnoremap <silent>mm :G<Cr>

autocmd FileType fugitive nnoremap <silent>mp :G push<Cr>
autocmd FileType fugitive nnoremap <silent>mq :G pull<Cr>
autocmd FileType fugitive nnoremap <silent>mus :G submodule update --remote<Cr>

mm 打开 Fugitive;在 Fugitive 下 mp push,mq pull,mus 更新 submodules

autoformat

noremap <silent><F3> :Autoformat<Cr>

F3 自动格式化。但我没咋用过,格式都是手动保持的。

除此以外写了一行注释,似乎要安装依赖。

npm install -g js-beautify

Tabout

这个也不是插件。终于有出处了。

除了上面加的 UltiSnips 拓展键,为了即使在能跳转 tabstops 时也能完成 Tabout,还加了两个映射。(虽然叫 Tabout,但其实是 Endout)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
" https://stackoverflow.com/questions/20038550/step-over-bracket-parenthesis-etc-with-tab-in-vim

" Tabout {{{1
inoremap <silent><End> <C-r>=IncreaseColNumber()<CR>
inoremap <silent><S-End> <C-r>=DecreaseColNumber()<CR>

let s:delimiters_exp = '[\[\]{}()$&"' . "'" . '<>]'

function! IncreaseColNumber()
let l:line = getline('.')
if l:line[col('.') - 1] =~# s:delimiters_exp
return "\<Right>"
endif
return ""
endfunction

function! DecreaseColNumber()
let l:line = getline('.')
if l:line[col('.') - 2] =~# s:delimiters_exp
return "\<Left>"
endif
return ""
endfunction
" }}}1

使用 EndShift + End 在括号等反复横跳。

额外

已经够长了。本篇就不讲额外内容了。