vim介绍

vim,作为与emacs齐名的编辑器,无需更多溢美之词。
emacs由于学习曲线陡峭,但是学会之人,无不表示其方便。相比之下,vim操作的简洁,更适合不太愿意折腾的我。

插件管理

在学会基本的vim使用之后,每个人都会走向使用插件的道路,或者使用业界流行的插件,或者自己造轮子。

这么多的插件在过去管理非常混乱,Vundle这个老牌管理在插件越来越多的情况下,每次更新都要好几分钟,使用不太方便了。
下面推荐vim-plug来作为插件管理:

  • 支持全异步插件安装,安装50个插件才不到一分钟
  • 插件更新也很快,不像原来每次更新都可以去喝杯茶去
  • 支持插件延迟加载

在Unix下安装:

1
2
$ curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

安装好后就可以在.vimrc里便捷地添加插件了。

.vimrc尾部追加如下内容:

1
2
3
4
5
6
7
8
9
" Specify a directory for plugins
" - For Neovim: stdpath('data') . '/plugged'
" - Avoid using standard Vim directory names like 'plugin'
call plug#begin('~/.vim/plugged')

" Add plugins here

" Initialize plugin system
call plug#end()

添加、管理插件:

1
2
3
4
5
6
7
8
9
10
11
" 添加插件,和 Vundle 的语法差不多
Plug 'junegunn/vim-easy-align'
Plug 'skywind3000/quickmenu.vim'

" 延迟按需加载,使用到命令的时候再加载或者打开对应文件类型才加载
Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }

" 确定插件仓库中的分支或者 tag
Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }

将插件配置成延迟加载,启动速度将提高了不少。

  • 安装插件::PlugInstall并行安装所有插件
  • 更新插件::PlugUpgrade自我更新
    使用时建议给插件分组,同类别插件放一个组里,vimrc 里面只需要确定下启用哪些组就行了。

插件推荐

最好将vim升级至vim8之后,再配置各种插件。

符号索引

使用符号索引插件 ctags 以及 tags 异步生成工具 vim-gutentags:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Plug 'universal-ctags/ctags'
Plug 'ludovicchabant/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']
" 检测 ~/.cache/tags 不存在就新建
if !isdirectory(s:vim_tags)
silent! call mkdir(s:vim_tags, 'p')
endif

代码检查ALE

ALE(Asynchronous Lint Engine)支持多种语言的各种代码分析器,如 gcc、cppcheck 等,但需要另行安装并放入 PATH。
ALE 能在你修改了文本后自动调用这些 linter 来分析最新代码,然后将各种 linter 的结果进行汇总并显示再界面上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Plug 'w0rp/ale'
let g:ale_linters_explicit = 1
let g:ale_linters = {
\ 'csh': ['shell'],
\ 'zsh': ['shell'],
\ 'go': ['gofmt', 'golint'],
\ 'python': ['flake8', 'mypy', 'pylint'],
\ 'c': ['gcc', 'cppcheck'],
\ 'cpp': ['gcc', 'cppcheck'],
\ 'text': [],
\}
let g:ale_completion_delay = 500
let g:ale_echo_delay = 20
let g:ale_lint_delay = 500
let g:ale_echo_msg_format = '[%linter%] %code: %%s'
let g:ale_lint_on_text_changed = 'normal'
let g:ale_lint_on_insert_leave = 1
let g:airline#extensions#ale#enabled = 1
let g:ale_c_gcc_options = '-Wall -O2 -std=c99'
let g:ale_cpp_gcc_options = '-Wall -O2 -std=c++11'
let g:ale_c_cppcheck_options = ''
let g:ale_cpp_cppcheck_options = ''

编译运行AsyncRun

1
2
3
4
5
6
7
8
9
10
11
12
Plug 'skywind3000/asyncrun.vim'
" asyncrun 搜索工程目录的标志,碰到这些文件/目录名就停止向上一级目录递归。
" 如果递归到根目录还没找到,那么文件所在目录就被当作项目目录。
let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs', 'build.xml']
" 自动打开 quickfix window ,高度为 6
let g:asyncrun_open = 6
" 任务结束时候响铃提醒
let g:asyncrun_bell = 1
" 设置 F10 打开/关闭 Quickfix 窗口
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>
" 设置 F7 从工程根目录编译整个工程
nnoremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>

语法高亮 vim-cpp-enhanced-highlight

1
2
Plug 'octol/vim-cpp-enhanced-highlight'
let c_no_curly_error = 1

代码补全 YouCompleteMe

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
Plug 'ycm-core/YouCompleteMe'
let g:ycm_global_ycm_extra_conf='~/.vim/plugged/YouCompleteMe/third_party/ycmd/examples/.ycm_extra_conf.py'
let g:ycm_add_preview_to_completeopt = 0
let g:ycm_show_diagnostics_ui = 0
let g:ycm_server_log_level = 'info'
let g:ycm_min_num_identifier_candidate_chars = 2
let g:ycm_collect_identifiers_from_comments_and_strings = 1
let g:ycm_complete_in_strings=1
let g:ycm_key_invoke_completion = '<c-z>' " 使用 Ctrl+Z 主动触发语义补全
noremap <c-z> <NOP>
set completeopt=menu,menuone

" 修改补全列表配色
highlight PMenu ctermfg=0 ctermbg=242 guifg=black guibg=darkgrey
highlight PMenuSel ctermfg=242 ctermbg=8 guifg=darkgrey guibg=black

" 对指定源文件,输入两个字母后即触发语义补全
let g:ycm_semantic_triggers = {
\ 'c,cpp,python,java,go,erlang,perl': ['re!\w{2}'],
\ 'cs,lua,javascript': ['re!\w{2}'],
\ }

let g:ycm_filetype_whitelist = {
\ "c":1,
\ "cpp":1,
\ "go":1,
\ "python":1,
\ "sh":1,
\ "zsh":1,
\ }

let g:ycm_filetype_blacklist = {
\ 'markdown' : 1,
\ 'text' : 1,
\ 'pandoc' : 1,
\ 'infolog' : 1,
\}

写好配置后,安装YCM参见YouCompleteMe实现vim自动补全

参数提示 echodoc

写 C/C++ 时函数忘了可以用上面的 YCM 补全,若是参数忘记了则可以使用 echodoc 插件。
当用 YCM 的 tab 补全了一个函数名后,只要输入左括号,下面命令行就会显示出该函数的参数信息,并且随着光标移动高亮出当前参数位置。

1
2
Plug 'Shougo/echodoc.vim'
set noshowmode

文件结构

1
2
3
4
5
6
Plug 'majutsushi/tagbar'
let g:tagbar_width=35 " 宽度设置,默认为40
let g:tagbar_autofocus=1 " tagbar一打开,光标在tagbar页面内,默认在vim界面内
let g:tagbar_left=0 " 默认右侧,设置为1则为左侧
let g:tagbar_sort = 1       " 标签排序,默认排序
nmap <F3> :TagbarToggle<CR> " F3可以调出TagBar的窗格

在vim界面内,随时可以底行输入:h tagbar 查看tagbar帮助手册。
在tagbar一栏内输入F1可以查看快捷方式。

函数列表 LeaderF

这里定义了 CTRL+P 在当前项目目录打开文件搜索,CTRL+N 打开 MRU 搜索,ALT+P 打开函数搜索,ALT+N 打开 Buffer 搜索,ALT+M 打开 Tag 搜索:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Plug 'Yggdroot/LeaderF', { 'do': './install.sh' }
let g:Lf_ShortcutF = '<c-p>'
let g:Lf_ShortcutB = '<m-n>'
noremap <c-n> :LeaderfMru<cr>
noremap <m-p> :LeaderfFunction!<cr>
noremap <m-n> :LeaderfBuffer<cr>
noremap <m-m> :LeaderfTag<cr>
let g:Lf_StlSeparator = { 'left': '', 'right': '', 'font': '' }
let g:Lf_RootMarkers = ['.project', '.root', '.svn', '.git']
let g:Lf_WorkingDirectoryMode = 'Ac'
let g:Lf_WindowHeight = 0.30
let g:Lf_CacheDirectory = expand('~/.vim/cache')
let g:Lf_ShowRelativePath = 0
let g:Lf_HideHelp = 1
let g:Lf_StlColorscheme = 'powerline'
let g:Lf_PreviewResult = {'Function':0, 'BufTag':0}

文件浏览 vim-dirvish

头文件和源文件之间可以使用 :A 命令快速切换,其它可以折腾下 dirvish

1
2
Plug 'vim-scripts/a.vim'
Plug 'justinmk/vim-dirvish'

修改比较vim-signify

1
2
3
Plug 'mhinz/vim-signify'
let g:signify_vcs_list = [ 'git' ]
let g:signify_sign_show_text = 1

编辑辅助 vim-unimpaired

1
Plug 'tpope/vim-unimpaired'

unimpaired 插件定义了一系列括号开头的快捷键,被称为官方 Vim 中丢失的快捷键。详见插件文档。

其它

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
set autoindent             " Indent according to previous line.
set expandtab " Use spaces instead of tabs.
set softtabstop =4 " Tab key indents by 4 spaces.
set shiftwidth =4 " >> indents by 4 spaces.
set shiftround " >> indents to next multiple of 'shiftwidth'.

set hidden " Switch between buffers without having to save first.
set display =lastline " Show as much as possible of the last line.

set ttyfast " Faster redrawing.
set lazyredraw " Only redraw when necessary.

set splitbelow " Open new windows below the current window.
set splitright " Open new windows right of the current window.

set cursorline " Find the current line quickly.
set wrapscan " Searches wrap around end-of-file.
set report =0 " Always report changed lines.
set synmaxcol =120 " Only highlight the first 200 columns.

set list " Show non-printable characters.
if has('multi_byte') && &encoding ==# 'utf-8'
let &listchars = 'tab:▸ ,extends:❯,precedes:❮,nbsp:±'
else
let &listchars = 'tab:> ,extends:>,precedes:<,nbsp:.'
endif

" Put all temporary files under the same directory.
let s:vim_backup = expand("$HOME/.vim/files/backup/")
if !isdirectory(s:vim_backup)
silent! call mkdir(s:vim_backup, 'p')
endif
let s:vim_swap = expand("$HOME/.vim/files/swap/")
if !isdirectory(s:vim_swap)
silent! call mkdir(s:vim_swap, 'p')
endif
let s:vim_undo = expand("$HOME/.vim/files/undo/")
if !isdirectory(s:vim_undo)
silent! call mkdir(s:vim_undo, 'p')
endif
let s:vim_info = expand("$HOME/.vim/files/info/")
if !isdirectory(s:vim_info)
silent! call mkdir(s:vim_info, 'p')
endif
set backup
set backupdir =$HOME/.vim/files/backup/
set backupext =-vimbackup
set backupskip =
set directory =$HOME/.vim/files/swap/
set updatecount =100
set undofile
set undodir =$HOME/.vim/files/undo/
set viminfo ='100,n$HOME/.vim/files/info/viminfo

.vimrc配置文件

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
" === 通用 ===
filetype plugin indent on "自动补全:自动识别文件类型,用文件类型plugin脚本,使用缩进定义文件
set helplang=cn "使用中文帮助文档
set mouse=a "使用鼠标
set selection=exclusive
set selectmode=mouse,key
set backspace=indent,eol,start
set backspace=2

" === 显示 ===
set number "设置行号
"set nowrap "取消自动换行:一行就一行,别弄到第二行去
syn on "语法高亮
set cursorline "突出显示当前行
set showmatch "插入括号时,短暂地跳转到匹配的对应括号
set matchtime=2 "短暂跳转到匹配括号的时间

" === 缩进 ===
set tabstop=4 "tab的宽度: 空格的个数
set softtabstop=4 "按退格键一次删掉的空格数
set shiftwidth=4 "缩进的空格
set autoindent "自动缩进
set smartindent "智能缩进

" === 折叠 ===
set foldenable
" 折叠方法
" manual 手工折叠
" indent 使用缩进表示折叠
" expr 使用表达式定义折叠
" syntax 使用语法定义折叠
" diff 对没有更改的文本进行折叠
" marker 使用标记进行折叠, 默认标记是 {{{ 和 }}}
set foldmethod=syntax
" 在左侧显示折叠的层次
"set foldcolumn=4

" === 搜索 ===
set ignorecase "搜索忽略大小写
set hlsearch "搜索高亮显示
"set nohlsearch
set ignorecase "搜索时忽略大小写
set incsearch "随着键入即时搜索
set smartcase "有一个或以上大写字母时仍大小写敏感

set ruler "在右下角显示光标位置
set showcmd "显示未敲完的命令
set cmdheight=1 "设定命令行的行数为 1
set laststatus=2 "显示状态栏 (默认值为 1, 无法显示状态栏)
set sidescroll=10 "屏幕放不下时,按一次屏幕移动一个字符
set whichwrap=b,s,<,>,[,] "跨行移动
set fileformats=unix,dos

" vim-plug
"if filereadable(expand("~/.vimplug"))
" source ~/.vimplug
"endif

" Specify a directory for plugins
" - For Neovim: stdpath('data') . '/plugged'
" - Avoid using standard Vim directory names like 'plugin'
call plug#begin('~/.vim/plugged')

" Make sure you use single quotes

" let Vundle manage Vundle, required
Plug 'VundleVim/Vundle.vim'

" Type1: plugin on GitHub repo
Plug 'tpope/vim-fugitive'

" Type2: plugin from http://vim-scripts.org/vim/scripts.html
" Plug 'L9'

" Type3: Git plugin not hosted on GitHub
Plug 'git://git.wincent.com/command-t.git'

" The sparkup vim script is in a subdirectory of this repo called vim.
" Pass the path to set the runtimepath properly.
Plug 'rstacruz/sparkup', {'rtp': 'vim/'}

" Install L9 and avoid a Naming conflict if you've already installed a
" different version somewhere else.
" Plug 'ascenator/L9', {'name': 'newL9'}

Plug 'christoomey/vim-run-interactive'

" 状态栏
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'

" 代码补全
" Plug 'ervandew/supertab'
Plug 'ycm-core/YouCompleteMe'
let g:ycm_global_ycm_extra_conf='~/.vim/plugged/YouCompleteMe/third_party/ycmd/examples/.ycm_extra_conf.py'
let g:ycm_add_preview_to_completeopt = 0
let g:ycm_show_diagnostics_ui = 0
let g:ycm_server_log_level = 'info'
let g:ycm_min_num_identifier_candidate_chars = 2
let g:ycm_collect_identifiers_from_comments_and_strings = 1
let g:ycm_complete_in_strings=1
let g:ycm_key_invoke_completion = '<c-z>' " 使用 Ctrl+Z 主动触发语义补全
noremap <c-z> <NOP>
set completeopt=menu,menuone

" 修改补全列表配色
highlight PMenu ctermfg=0 ctermbg=242 guifg=black guibg=darkgrey
highlight PMenuSel ctermfg=242 ctermbg=8 guifg=darkgrey guibg=black

" 对指定源文件,输入两个字母后即触发语义补全
let g:ycm_semantic_triggers = {
\ 'c,cpp,python,java,go,erlang,perl': ['re!\w{2}'],
\ 'cs,lua,javascript': ['re!\w{2}'],
\ }

let g:ycm_filetype_whitelist = {
\ "c":1,
\ "cpp":1,
\ "go":1,
\ "python":1,
\ "sh":1,
\ "zsh":1,
\ }

let g:ycm_filetype_blacklist = {
\ 'markdown' : 1,
\ 'text' : 1,
\ 'pandoc' : 1,
\ 'infolog' : 1,
\}

Plug 'Shougo/echodoc.vim'
set noshowmode

" 语法检查插件
Plug 'vim-syntastic/syntastic'

" 语法高亮
Plug 'octol/vim-cpp-enhanced-highlight'
let c_no_curly_error = 1

" 符号索引
Plug 'universal-ctags/ctags'
Plug 'ludovicchabant/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']
" 检测 ~/.cache/tags 不存在就新建
if !isdirectory(s:vim_tags)
silent! call mkdir(s:vim_tags, 'p')
endif

" 编译运行
Plug 'skywind3000/asyncrun.vim'
" asyncrun 搜索工程目录的标志,碰到这些文件/目录名就停止向上一级目录递归。
" 如果递归到根目录还没找到,那么文件所在目录就被当作项目目录。
let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs', 'build.xml']
" 自动打开 quickfix window ,高度为 6
let g:asyncrun_open = 6
" 任务结束时候响铃提醒
let g:asyncrun_bell = 1
" 设置 F10 打开/关闭 Quickfix 窗口
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>
" 设置 F7 从工程根目录编译整个工程
nnoremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>

" 参数提示
Plug 'Shougo/echodoc.vim'
set noshowmode

" 编辑辅助
Plug 'tpope/vim-unimpaired'

" 目录结构
Plug 'scrooloose/nerdtree'
" open a NERDTree automatically when vim starts up
"autocmd vimenter * NERDTree
"open a NERDTree automatically when vim starts up if no files were specified
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | NERDTree | endif
"open NERDTree automatically when vim starts up on opening a directory
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 1 && isdirectory(argv()[0]) && !exists("s:std_in") | exe 'NERDTree' argv()[0] | wincmd p | ene | endif
"map F2 to open/close NERDTree
map <F2> :NERDTreeToggle<CR>
"close vim if the only window left open is a NERDTree
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif

" 文件结构
Plug 'majutsushi/tagbar'
" Tagbar 配置
" 宽度设置,默认为40
let g:tagbar_width=35
" tagbar一打开,光标在tagbar页面内,默认在vim界面内
let g:tagbar_autofocus=1
" 默认右侧,设置为1则为左侧
let g:tagbar_left=0
" F3可以调出TagBar的窗格
nmap <F3> :TagbarToggle<CR>

" 搜索文件
Plug 'ctrlpvim/ctrlp.vim'
" 空格+ff: 启用
let g:ctrlp_map = '<leader>ff'
let g:ctrlp_cmd = 'CtrlP'
" 空格+fp: 显示最近打开文件
map <leader>fp :CtrlPMRU<CR>
"set wildignore+=*/tmp/*,*.so,*.swp,*.zip " MacOSX/Linux"
let g:ctrlp_custom_ignore = {
\ 'dir': '\v[\/]\.(git|hg|svn|rvm)$',
\ 'file': '\v\.(exe|so|dll|zip|tar|tar.gz)$',
\ }
"\ 'link': 'SOME_BAD_SYMBOLIC_LINKS',
let g:ctrlp_working_path_mode=0
let g:ctrlp_match_window_bottom=1
let g:ctrlp_max_height=15
let g:ctrlp_match_window_reversed=0
let g:ctrlp_mruf_max=500
let g:ctrlp_follow_symlinks=1


" Initialize plugin system
call plug#end()

参考

打造 vim 编辑 C/C++ 环境