# VimLeo
**Repository Path**: luyvu/vimleo
## Basic Information
- **Project Name**: VimLeo
- **Description**: Vim 配置,适应 Windows 和 Linux 平台。
- **Primary Language**: VimL
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 4
- **Created**: 2020-06-14
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 我的 vim 配置及插件
lwh@vip.163.com
2016-03-30 16:39:33
##【致谢】
本配置文件前后很多参考了网上配置说明及源文件,在此不一一列出,统一对各位朋友致以诚挚谢意。
##【版本】
----
* v0.1.0,2016-07-10,新增。发布初始版本。
##【目录】
----
[0 vim 必知会 ](#0)
........[0.1 .vimrc 文件 ](#0.1)
........[0.2 .vim/ 目录 ](#0.2)
[1 源码安装编辑器 vim ](#1)
[2 插件管理 ](#2)
[3 界面美化 ](#3)
........[3.1 主题风格 ](#3.1)
........[3.2 营造专注氛围 ](#3.2)
........[3.3 添加辅助信息 ](#3.3)
........[3.4 其他 ](#3.4)
[4 代码分析 ](#4)
........[4.1 语法高亮 ](#4.1)
........[4.2 代码缩进 ](#4.2)
........[4.3 代码折叠 ](#4.3)
........[4.4 接口与实现快速切换 ](#4.4)
........[4.5 代码收藏 ](#4.5)
........[4.6 标识符列表 ](#4.6)
................[标签系统 ](#4.6.1)
................[语义系统 ](#4.6.2)
................[基于标签的标识符列表 ](#4.6.3)
........[4.7 声明 / 定义跳转 ](#4.7)
................[基于标签的声明 / 定义跳转 ](#4.7.1)
........[4.8 内容查找 ](#4.8)
........[4.9 内容替换 ](#4.9)
................[快捷替换 ](#4.9.1)
................[精确替换 ](#4.9.2)
[5 代码开发 ](#5)
........[5.1 快速开关注释 ](#5.1)
........[5.2 模板补全 ](#5.2)
........[5.3 智能补全 ](#5.4)
................[基于标签的智能补全 ](#5.3.1)
........[5.4 库信息参考 ](#5.5)
[6 工程管理 ](#6)
........[6.1 工程文件浏览 ](#6.1)
........[6.2 多文档编辑 ](#6.2)
........[6.3 环境恢复 ](#6.3)
[7 工具链集成 ](#7)
........[7.1 构建工具集成 ](#7.1)
................[代码编译 ](#7.1.1)
................[系统构建 ](#7.1.2)
................[一键编译 ](#7.1.3)
........[7.2 静态分析器集成 ](#7.2)
[8 其他辅助 ](#8)
........[8.1 快速编辑结对符 ](#8.1)
........[8.2 支持分支的 undo ](#8.2)
........[8.3 快速移动 ](#8.3)
[9 结束语](#9)
##【正文】
----
开始前,我假设你:0)具备基本的 vim 操作能力,清楚如何打开 / 编辑 / 保存文档、命令与插入模式间切换;1)希望将 vim 打造成 C/C++ 语言的 IDE,而非其他语言。
关于 vim 的优点,你在网上能查到 128+ 项,对我而言,只有两项:0)所思即所得,让手输入的速度跟上大脑思考的速度,1)所需即所获,只有你想不到的功能、没有实现不了的插件。希望获得前者的能力,你需要两本教程深入学习,《Practical Vim: Edit Text at the Speed of Thought》和《vim user manual》;要想拥有后者的能力,通读本文 -。-#。
vim 用户手册中,50% 的例子都是在讲 vim 如何高效编写代码,由此可见,vim 是一款面向于程序员的编辑器,即使某些功能 vim 无法直接完成,借助其丰富的插件资源,必定可以达成目标,这就是所需即所获。
我是个目标驱动的信奉者,本文内容,我会先给出优秀 C/C++ IDE 应具备哪些功能,再去探索如何通过 vim 的操作或插件来达到目标。最终至少要像这个样子:
(图形环境下 IDE 总揽)
(纯字符模式下 IDE 总揽)
## 0 vim 必知会
在正式开始前先介绍几个 vim 的必知会,这不是关于如何使用而是如何配置 vim 的要点,这对理解后续相关配置非常有帮助。
### 0.1 .vimrc 文件
.vimrc 是控制 vim 行为的配置文件,位于 ~/.vimrc,不论 vim 窗口外观、显示字体,还是操作方式、快捷键、插件属性均可通过编辑该配置文件将 vim 调教成最适合你的编辑器。
很多人之所以觉得 vim 难用,是因为 vim 缺少默认设置,甚至安装完后你连配置文件自身都找不到,不进行任何配置的 vim 的确难看、难用。不论用于代码还是普通文本编辑,有必要将如下基本配置加入 .vimrc 中。
前缀键。各类 vim 插件帮助文档中经常出现 \,即,前缀键。vim 自带有很多快捷键,再加上各类插件的快捷键,大量快捷键出现在单层空间中难免引起冲突,为缓解该问题,引入了前缀键 \,这样,键 r 可以配置成 r、\r、\\r 等等多个快捷键。前缀键是 vim 使用率较高的一个键(最高的当属 Esc),选一个最方便输入的键作为前缀键,将有助于提高编辑效率。找个无须眼睛查找、无须移动手指的键 —— 分号键,挺方便的,就在你右手小指处:
```
" 定义快捷键的前缀,即
let mapleader=";"
```
既然前缀键是为快捷键服务的,那随便说下快捷键设定原则:不同快捷键尽量不要有同序的相同字符。比如,\e 执行操作 0 和 \eb 执行操作 1,在你键入 \e 后,vim 不会立即执行操作 0,而是继续等待用户键入 b,即便你只想键入 \e,vim 也不得不花时间等待输入以确认是哪个快捷键,显然,这让 \e 响应速度变慢。\ea 和 \eb 就没问题。
文件类型侦测。允许基于不同语言加载不同插件(如,C++ 的语法高亮插件与 python 的不同):
```
" 开启文件类型侦测
filetype on
" 根据侦测到的不同类型加载对应的插件
filetype plugin on
```
快捷键。把 vim(非插件)常用操作设定成快捷键,提升效率:
```
" 设置快捷键将选中文本块复制至系统剪贴板
vnoremap c "+y
" 设置快捷键将系统剪贴板内容粘贴至 vim
nmap v "+p
" 依次遍历子窗口
nnoremap nw
" 跳转至右方的窗口
nnoremap lw l
" 跳转至左方的窗口
nnoremap hw h
" 跳转至上方的子窗口
nnoremap kw k
" 跳转至下方的子窗口
nnoremap jw j
" 定义快捷键在结对符之间跳转
nmap M %
```
其他。搜索、vim 命令补全等设置:
```
" 开启实时搜索功能
set incsearch
" 搜索时大小写不敏感
set ignorecase
" 关闭兼容模式
set nocompatible
" vim 自身命令行模式智能补全
set wildmenu
```
以上的四类配置不仅影响 vim,而且影响插件是否能正常运行。很多插件不仅要在 .vimrc 中添加各自特有的配置信息,还要增加 vim 自身的配置信息,在后文的各类插件介绍中,我只介绍对应插件特有配置信息,当你发现按文中介绍操作后插件未生效,很可能是 vim 自身配置信息未添加,所以一定要把上述配置拷贝至到你的 .vimrc 中,再对照本文介绍一步步操作。.vimrc 完整配置信息参见附录,每个配置项都有对应注释。另外,由于有些插件还未来得及安装,在你实验前面的插件是否生效时,vim 可能有报错信息提示,先别理会,安装完所有插件后自然对了。
### 0.2 .vim/ 目录
.vim/ 目录是存放所有插件的地方。vim 有一套自己的脚本语言 vimscript,通过这种脚本语言可以实现与 vim 交互,达到功能扩展的目的。一组 vimscript 就是一个 vim 插件,vim 的很多功能都由各式插件实现。此外,vim 还支持 perl、python、lua、ruby 等主流脚本语言编写的插件,前提是 vim 源码编译时增加 ---enable-perlinterp、--enable-pythoninterp、--enable-luainterp、--enable-rubyinterp 等选项。vim.org 和 github.com 有丰富的插件资源,任何你想得到的功能,如果 vim 无法直接支持,那一般都有对应的插件为你服务,有需求时可以去逛逛。
vim 插件目前分为 \*.vim 和 \*.vba 两类,前者是传统格式的插件,实际上就是一个文本文件,通常 someplugin.vim(插件脚本)与 someplugin.txt(插件帮助文件)并存在一个打包文件中,解包后将 someplugin.vim 拷贝到 ~/.vim/plugin/ 目录,someplugin.txt 拷贝到 ~/.vim/doc/ 目录即可完成安装,重启 vim 后刚安装的插件就已经生效,但帮助文件需执行 :helptags ~/.vim/doc/ 才能生效,可通过 :h someplugin 查看插件帮助信息。传统格式插件需要解包和两次拷贝才能完成安装,相对较繁琐,所以后来又出现了 \*.vba 格式插件,安装便捷,只需在 shell 中依次执行如下命令即可:
```
vim someplugin.vim
:so %
:q
```
不论是直接拷贝插件到目录,还是通过 \*.vim 安装,都不便于插件卸载、升级,后来又出现了管理插件的插件 vim-plug。
后面就正式开始了喽,文中前后内容顺序敏感,请依次查阅。
## 1 源码安装编辑器 vim
发行套件的软件源中预编译的 vim 要么不是最新版本,要么功能有阉割,有必要升级成全功能的最新版,当然,源码安装必须滴:
```
git clone git@github.com:vim/vim.git
cd vim/
./configure --with-features=huge --enable-pythoninterp --enable-rubyinterp --enable-luainterp --enable-perlinterp --with-python-config-dir=/usr/lib/python2.7/config/ --enable-gui=gtk2 --enable-cscope --prefix=/usr
make
make install
```
其中,--enable-pythoninterp、--enable-rubyinterp、--enable-perlinterp、--enable-luainterp 等分别表示支持 ruby、python、perl、lua 编写的插件,--enable-gui=gtk2 表示生成采用 GNOME2 风格的 gvim,--enable-cscope 支持 cscope,--with-python-config-dir=/usr/lib/python2.7/config/ 指定 python 路径(先自行安装 python 的头文件 python-devel),这几个特性非常重要,影响后面各类插件的使用。注意,你得预先安装相关依赖库的头文件,python-devel、python3-devel、ruby-devel、lua-devel、libX11-devel、gtk-devel、gtk2-devel、gtk3-devel、ncurses-devel,如果缺失,源码构建过程虽不会报错,但最终生成的 vim 很可能缺失某些功能。构建完成后在 vim 中执行
```
:echo has('python')
```
若输出 1 则表示构建出的 vim 已支持 python,反之,0 则不支持。
## 2 插件管理
既然本文主旨在于讲解如何通过插件将 vim 打造成中意的 C/C++ IDE,那么高效管理插件是首要解决的问题。
vim 自身希望通过在 .vim/ 目录中预定义子目录管理所有插件(比如,子目录 doc/ 存放插件帮助文档、plugin/ 存放通用插件脚本),vim 的各插件打包文档中通常也包含上述两个(甚至更多)子目录,用户将插件打包文档中的对应子目录拷贝至 .vim/ 目录即可完成插件的安装。一般情况下这种方式没问题,但我等重度插件用户,.vim/ 将变得混乱不堪,至少存在如下几个问题:
* 插件名字冲突。所有插件的帮助文档都在 doc/ 子目录、插件脚本都在 plugin/ 子目录,同个名字空间下必然引发名字冲突;
* 插件卸载易误。你需要先知道 doc/ 和 plugin/ 子目录下哪些文件是属于该插件的,再逐一删除,容易多删 / 漏删。
我希望每个插件在 .vim/ 下都有各自独立子目录,这样需要升级、卸载插件时,直接找到对应插件目录变更即可;另外,我希望所有插件清单能在某个配置文件中集中罗列,通过某种机制实现批量自动安装 / 更新 / 升级所有插件。vim-plug(https://github.com/vim-plug/vim-plug.vim )为此而生,它让管理插件变得更清晰、智能。
另外一个流行的vim插件管理工具是Vundle,相对于Vundle,vim-plug 具有同步更新插件的特性,速度更快
```
" -----------------------------------------------------------------------------
" < vim-plug 插件管理工具配置 >
" -----------------------------------------------------------------------------
"
" 用于更方便的管理vim插件,具体用法参考 :h Plug 帮助
" 安装方法为在https://github.com/junegunn/vim-plug下载plug.vim后拷贝到
" ~\vimfiles\autoload
" set the runtime path to include Vundle and initialize
if g:isLinux
call plug#begin('~/.vim/bundle')
else
call plug#begin('$VIM/vimfiles/bundle')
endif
" 以下为要安装或更新的插件
Plug 'skywind3000/asyncrun.vim'
Plug 'liwangmj/green_vim_switchtoinc'
Plug 'vim-scripts/Align'
Plug 'junegunn/vim-easy-align'
Plug 'jiangmiao/auto-pairs'
Plug 'jlanzarotta/bufexplorer'
Plug 'fholgado/minibufexpl.vim'
Plug 'vim-scripts/ccvext.vim'
Plug 'Yggdroot/indentLine'
Plug 'Shougo/neocomplete'
Plug 'scrooloose/nerdcommenter'
Plug 'scrooloose/nerdtree'
Plug 'Xuyuanp/nerdtree-git-plugin'
Plug 'vim-scripts/OmniCppComplete'
Plug 'MarcWeber/vim-addon-mw-utils'
Plug 'tomtom/tlib_vim'
Plug 'garbas/vim-snipmate'
Plug 'drmingdrmer/xptemplate'
Plug 'wesleyche/SrcExpl'
Plug 'vim-scripts/std_c.zip'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-repeat'
Plug 'kshenoy/vim-signature'
Plug 'majutsushi/tagbar'
Plug 'Li-Wenhui/taglist'
Plug 'ctrlpvim/ctrlp.vim'
Plug 'vim-scripts/ZoomWin'
Plug 'hotoo/pangu.vim'
Plug 'kien/rainbow_parentheses.vim'
Plug 'adelarsq/vim-matchit'
Plug 'vim-scripts/cSyntaxAfter'
Plug 'octol/vim-cpp-enhanced-highlight'
Plug 'vim-latex/vim-latex'
Plug 'terryma/vim-multiple-cursors'
Plug 'Lokaltog/vim-easymotion'
Plug 'rking/ag.vim'
Plug 'kjk/the_silver_searcher'
Plug 'haya14busa/incsearch.vim'
Plug 'vim-scripts/EasyGrep'
Plug 'terryma/vim-expand-region'
Plug 'tpope/vim-fugitive'
Plug 'ryanoasis/vim-devicons'
Plug 'vim-scripts/DoxygenToolkit.vim'
Plug 'dyng/ctrlsf.vim'
Plug 'vim-scripts/TxtBrowser'
Plug 'sjl/gundo.vim'
Plug 'benizi/vim-automkdir'
Plug 'mhinz/vim-startify'
if g:AleOrSyntastic
Plug 'w0rp/ale'
else
Plug 'scrooloose/syntastic'
endif
if g:isAirLine
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
endif
call plug#end()"
" -----------------------------------------------------------------------------
" < green_vim_switchtoinc 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于头文件和源文件间快速切换。注:在a.vim插件基础上修改的。
" :A ---切换头文件并独占整个窗口
" :AV ---切换头文件并垂直分割窗口
" :AS ---切换头文件并水平分割窗口
" :AN ---多个重名文件之间的切换
" :SearchIncAndSrcPath ---初始化查找目录(只在用最后一种方式前使用,其他方法不用)
"
" 基于源文件的相对路径查找(如下是默认值):
" let g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc'
" 基于绝对路径查找(例子):
" let g:alternateSearchPath = 'abs:/home/my/myporject/inc,abs:/home/my/myproject/src'
" }}}
" -----------------------------------------------------------------------------
" < Align 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 一个对齐的插件,用来——排版与对齐代码,功能强大,不过用到的机会不多
" }}}
" ----------------------------------------------------------------------------
" vim-easy-align快速赋值语句对齐 \a
" ----------------------------------------------------------------------------
" {{{
vnoremap ea (EasyAlign)
nnoremap ea (EasyAlign)
if !exists('g:easy_align_delimiters')
let g:easy_align_delimiters = {}
endif
let g:easy_align_delimiters['#'] = { 'pattern': '#', 'ignore_groups': ['String'] }
" }}}
" -----------------------------------------------------------------------------
" < auto-pairs 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于括号与引号自动补全,不过会与函数原型提示插件echofunc冲突
" 所以我就没有加入echofunc插件
" }}}
v" -----------------------------------------------------------------------------
" < BufExplorer 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 快速轻松的在缓存中切换(相当于另一种多个文件间的切换方式)
" be 在当前窗口显示缓存列表并打开选定文件
" bs 水平分割窗口显示缓存列表,并在缓存列表窗口中打开选定文件
" bv 垂直分割窗口显示缓存列表,并在缓存列表窗口中打开选定文件
let g:bufExplorerSortBy = 'name' " 按文件名排序
" }}}
" -----------------------------------------------------------------------------
" < MiniBufExplorer 插件配置 >
" -----------------------------------------------------------------------------
" {{{
nnoremap mbe :MBEOpen
nnoremap mbc :MBEClose
nnoremap mbt :MBEToggle
" }}}
" -----------------------------------------------------------------------------
" < ccvext.vim 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于对指定文件自动生成tags与cscope文件并连接
" 如果是Windows系统, 则生成的文件在源文件所在盘符根目录的.symbs目录下(如: X:\.symbs\)
" 如果是Linux系统, 则生成的文件在~/.symbs/目录下
" 具体用法可参考www.vim.org中此插件的说明
" gs 自动生成tags与cscope文件并连接
" cs 连接已存在的tags与cscope文件
" }}}
" -----------------------------------------------------------------------------
" < cscope 工具配置 >
" -----------------------------------------------------------------------------
" {{{
" 用Cscope自己的话说 - 你可以把它当做是超过频的ctags
if has("cscope")
"设定可以使用 quickfix 窗口来查看 cscope 结果
set cscopequickfix=s-,c-,d-,i-,t-,e-
"使支持用 Ctrl+] 和 Ctrl+t 快捷键在代码间跳转
set cscopetag
"如果你想反向搜索顺序设置为1
set csto=0
"在当前目录中添加任何数据库
if filereadable("cscope.out")
cs add cscope.out
"否则添加数据库环境中所指出的
elseif $CSCOPE_DB != ""
cs add $CSCOPE_DB
endif
set cscopeverbose
"快捷键设置
nnoremap s :cs find s =expand("")
nnoremap g :cs find g =expand("")
nnoremap c :cs find c =expand("")
nnoremap t :cs find t =expand("")
nnoremap e :cs find e =expand("")
nnoremap f :cs find f =expand("")
nnoremap i :cs find i ^=expand("")$
nnoremap d :cs find d =expand("")
endif
" }}}
" -----------------------------------------------------------------------------
" < ctags 工具配置 >
" -----------------------------------------------------------------------------
" {{{
" 对浏览代码非常的方便,可以在函数,变量之间跳转等
set tags+=tags; " 向上级目录递归查找tags文件
"set tags+=./addtags/qt5_h " 用户可以加入其它程序头文件以便ctags建立索引
"set tags+=./addtags/cpp_stl
"set tags+=./addtags/qt5_cpp
"set tags+=/usr/include/glibc-2.0/glibc.tags
" }}}
" -----------------------------------------------------------------------------
" < indentLine 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于显示对齐线,与 indent_guides 在显示方式上不同,根据自己喜好选择了
" 在终端上会有屏幕刷新的问题,这个问题能解决有更好了
" \il 显示/关闭对齐线
nnoremap il :IndentLinesToggle
" 设置Gvim的对齐线样式
if g:isGUI
let g:indentLine_char = "|"
let g:indentLine_first_char = "|"
endif
" 设置终端对齐线颜色,如果不喜欢可以将其注释掉采用默认颜色
let g:indentLine_color_term = 239
" 设置 GUI 对齐线颜色,如果不喜欢可以将其注释掉采用默认颜色
" let g:indentLine_color_gui = '#A4E57E'
" }}}
" -----------------------------------------------------------------------------
" < neocomplete 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 关键字补全、文件路径补全、tag补全等等,各种,非常好用,速度超快。
" Enable omni completion.
" Disable AutoComplPop.
let g:acp_enableAtStartup = 0
" Use neocomplete.
let g:neocomplete#enable_at_startup = 1
" Use smartcase.
let g:neocomplete#enable_smart_case = 1
" Set minimum syntax keyword length.
let g:neocomplete#sources#syntax#min_keyword_length = 3
" Define keyword.
if !exists('g:neocomplete#keyword_patterns')
let g:neocomplete#keyword_patterns = {}
endif
let g:neocomplete#keyword_patterns['default'] = '\h\w*'
" Plugin key-mappings.
" inoremap neocomplete#undo_completion()
" inoremap neocomplete#complete_common_string()
" Recommended key-mappings.
" : close popup and save indent.
inoremap =my_cr_function()
function! s:my_cr_function()
return neocomplete#close_popup() . "\"
" For no inserting key.
"return pumvisible() ? neocomplete#close_popup() : "\"
endfunction
" : completion.
" inoremap pumvisible() ? "\" : "\"
" , : close popup and delete backword char.
inoremap neocomplete#smart_close_popup()."\"
inoremap neocomplete#smart_close_popup()."\"
" Close popup by .
"inoremap pumvisible() ? "\" : "\"
" AutoComplPop like behavior.
"let g:neocomplete#enable_auto_select = 1
" Enable omni completion.
autocmd FileType css setlocal omnifunc=csscomplete#CompleteCSS
autocmd FileType html,markdown setlocal omnifunc=htmlcomplete#CompleteTags
autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS
autocmd FileType python setlocal omnifunc=pythoncomplete#Complete
autocmd FileType xml setlocal omnifunc=xmlcomplete#CompleteTags
autocmd FileType c setlocal omnifunc=ccomplete#Complete
" Enable heavy omni completion.
"if !exists('g:neocomplete#sources#omni#input_patterns')
" let g:neocomplete#sources#omni#input_patterns = {}
"endif
"
"let g:neocomplete#sources#omni#input_patterns.php = '[^. \t]->\h\w*\|\h\w*::'
"let g:neocomplete#sources#omni#input_patterns.c = '[^.[:digit:] *\t]\%(\.\|->\)'
"let g:neocomplete#sources#omni#input_patterns.cpp = '[^.[:digit:] *\t]\%(\.\|->\)\|\h\w*::'
" }}}
" -----------------------------------------------------------------------------
" < nerdcommenter 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 我主要用于C/C++代码注释(其它的也行)
" 以下为插件默认快捷键,其中的说明是以C/C++为例的,其它语言类似
" ci 以每行一个 /* */ 注释选中行(选中区域所在行),再输入则取消注释
" cm 以一个 /* */ 注释选中行(选中区域所在行),再输入则称重复注释
" cc 以每行一个 /* */ 注释选中行或区域,再输入则称重复注释
" cu 取消选中区域(行)的注释,选中区域(行)内至少有一个 /* */
" ca 在/*...*/与//这两种注释方式中切换(其它语言可能不一样了)
" cA 行尾注释
let NERDSpaceDelims = 1 "自动在左注释符之后,右注释符之前留有空格
" }}}
" -----------------------------------------------------------------------------
" < nerdtree 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 有目录村结构的文件浏览插件
let g:NERDTreeShowHidden = 1 " 显示隐藏文件
" 常规模式下输入 F2 调用插件
nnoremap :NERDTreeToggle
" \nt 打开文件树窗口,在左侧栏显示 [NERDTree插件]
nnoremap nt :NERDTree
let NERDTreeHighlightCursorline=1
let NERDTreeIgnore=[ '\.pyc$', '\.pyo$', '\.obj$', '\.o$', '\.so$', '\.egg$', '^\.git$', '^\.svn$', '^\.hg$' ]
" s/v 分屏打开文件
let g:NERDTreeMapOpenSplit = 's'
let g:NERDTreeMapOpenVSplit = 'v'
" 设置图标显示效果
let g:NERDTreeRespectWildIgnore=1
let g:NERDTreeDirArrows=0
let g:webdevicons_enable = 1
let g:webdevicons_enable_nerdtree = 1
let g:webdevicons_enable_unite = 1
let g:webdevicons_enable_vimfiler = 1
let g:WebDevIconsUnicodeDecorateFileNodes = 1
let g:WebDevIconsUnicodeDecorateFolderNodes = 1
let g:WebDevIconsUnicodeDecorateFolderNodesExactMatches = 1
let g:WebDevIconsUnicodeGlyphDoubleWidth = 1
let g:webdevicons_conceal_nerdtree_brackets = 1
let g:WebDevIconsNerdTreeAfterGlyphPadding = ''
let g:WebDevIconsNerdTreeGitPluginForceVAlign=1
" }}}
" -----------------------------------------------------------------------------
" < nerdtree-git-plugin 插件配置 >
" -----------------------------------------------------------------------------
"{{{
" 在NerdTree窗口里显示 Git 管理的项目文件变更状态
"}}}
" -----------------------------------------------------------------------------
" < vim-gitgutter 工具配置 >
" -----------------------------------------------------------------------------
" {{{
" shows a git diff in the gutter (sign column) and stages/undoes hunks
" \gi 开启或关闭GitGutter
nnoremap gi :GitGutterToggle:GitGutterSignsToggle:GitGutterLineHighlightsToggle
" \gd 打开Git文件对比模式 [竖直]
nnoremap gid :Gdiff
" \gs 打开Git文件对比模式 [水平]
nnoremap gis :Gsdiff
let g:gitgutter_enabled = 0 " 默认不开启
let g:gitgutter_signs = 0 " 默认不开启提示
let g:gitgutter_highlight_lines = 0 " 默认不高亮行
let g:gitgutter_sign_added = '+' " 自定义新增指示符
let g:gitgutter_sign_modified = '>' " 自定义修改指示符
let g:gitgutter_sign_removed = '-' " 自定义删除指示符
let g:gitgutter_sign_modified_removed = '->' " 自定义既修改又删除指示符
" }}}
" -----------------------------------------------------------------------------
" < OmniCppComplete 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于C/C++代码补全,这种补全主要针对命名空间、类、结构、共同体等进行补全,详细
" 说明可以参考帮助或网络教程等
" 使用前先执行如下 ctags 命令(本配置中可以直接使用 ccvext 插件来执行以下命令)
" ctags -R --c++-kinds=+p --fields=+iaS --extra=+q
" 我使用上面的参数生成标签后,对函数使用跳转时会出现多个选择
" 定义快捷键 \tc
noremap tc :!ctags -R --c++-kinds=+p --fields=+iaS --extra=+q . :TlistUpdate
set completeopt=menu " 关闭预览窗口
let OmniCpp_GlobalScopeSearch = 1 " 全局查找控制。0:禁止;1:允许(缺省)
let OmniCpp_NamespaceSearch = 1 " 命名空间查找控制。
" 0 : 禁止查找命名空间
" 1 : 查找当前文件缓冲区内的命名空间(缺省)
" 2 : 查找当前文件缓冲区和包含文件中的命名空间
let OmniCpp_DisplayMode = 0 " 类成员显示控制(是否显示全部公有(public)私有(private)保护(protected)成员)。
" 0 : 自动
" 1 : 显示所有成员
let OmniCpp_ShowScopeInAbbr = 0 " 选项用来控制匹配项所在域的显示位置。
" 缺省情况下,omni显示的补全提示菜单中总是将匹配项所在域信息显示在缩略信息最后一列。
" 0 : 信息缩略中不显示匹配项所在域(缺省)
" 1 : 显示匹配项所在域,并移除缩略信息中最后一列
let OmniCpp_ShowPrototypeInAbbr = 1 " 是否是补全提示缩略信息中显示函数原型。
" 0 : 不显示(缺省)
" 1 : 显示原型
let OmniCpp_ShowAccess = 1 " 是否显示访问控制信息('+', '-', '#')。0/1, 缺省为1(显示)
let OmniCpp_DefaultNamespaces = ["std", "_GLIBCXX_STD"] " 默认命名空间列表,项目间使用','隔开。
" 如:let OmniCpp_DefaultNamespaces = ["std', "MyNamespace"]
let OmniCpp_MayCompleteDot = 1 " 在'.'号后是否自动运行omnicppcomplete给出提示信息。0/1, 缺省为1
let OmniCpp_MayCompleteArrow = 1 " 在"->"后是否自动运行omnicppcomplete给出提示信息。0/1, 缺省为1
let OmniCpp_MayCompleteScope = 1 " 在域标识符"::"后是否自动运行omnicppcomplete给出提示信息。0/1, 缺省为0
let OmniCpp_SelectFirstItem = 0 " 是否自动选择第一个匹配项。仅当"completeopt"不为"longest"时有效。
" 0 : 不选择第一项(缺省)
" 1 : 选择第一项并插入到光标位置
" 2 : 选择第一项但不插入光标位置
" }}}
" -----------------------------------------------------------------------------
" < snipmate 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于各种代码补全,这种补全是一种对代码中的词与代码块的缩写补全,详细用法可以参
" 考使用说明或网络教程等。不过有时候也会与 supertab 插件在补全时产生冲突,如果大
" 侠有什么其它解决方法希望不要保留呀
" }}}
" -----------------------------------------------------------------------------
" < xptemplate 插件配置 >
" -----------------------------------------------------------------------------
" {{{
let g:xptemplate_key = ''
let g:xptemplate_vars = "SPcmd=&BRloop=\n"
let g:xptemplate_vars = "BRfun= "
let g:xptemplate_vars = "author=Leo&email=lwh@vip.163.com&..."
let g:xptemplate_minimal_prefix = 0
" }}}
" -----------------------------------------------------------------------------
" < SrcExpl 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 增强源代码浏览,其功能就像Windows中的"Source Insight"
nnoremap :SrcExplToggle "打开/闭浏览窗口
" }}}
" -----------------------------------------------------------------------------
" < std_c 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于增强C语法高亮
" 启用 // 注视风格
let c_cpp_comments = 0
" }}}
" -----------------------------------------------------------------------------
" < surround 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 快速给单词/句子两边增加符号(包括html标签),缺点是不能用"."来重复命令
" 不过 repeat 插件可以解决这个问题,详细帮助见 :h surround.txt
" }}}
" -----------------------------------------------------------------------------
" < repeat 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 主要用"."命令来重复上次插件使用的命令
" }}}
" -----------------------------------------------------------------------------
" < vim-signature 插件配置 >
" -----------------------------------------------------------------------------
" {{{
let g:SignatureMap = {
\ 'Leader' : "m",
\ 'PlaceNextMark' : "m,",
\ 'ToggleMarkAtLine' : "m.",
\ 'PurgeMarksAtLine' : "m-",
\ 'DeleteMark' : "dm",
\ 'PurgeMarks' : "mda",
\ 'PurgeMarkers' : "m",
\ 'GotoNextLineAlpha' : "']",
\ 'GotoPrevLineAlpha' : "'[",
\ 'GotoNextSpotAlpha' : "`]",
\ 'GotoPrevSpotAlpha' : "`[",
\ 'GotoNextLineByPos' : "]'",
\ 'GotoPrevLineByPos' : "['",
\ 'GotoNextSpotByPos' : "mn",
\ 'GotoPrevSpotByPos' : "mp",
\ 'GotoNextMarker' : "[+",
\ 'GotoPrevMarker' : "[-",
\ 'GotoNextMarkerAny' : "]=",
\ 'GotoPrevMarkerAny' : "[=",
\ 'ListLocalMarks' : "ms",
\ 'ListLocalMarkers' : "m?"
\ }
" }}}
if g:AleOrSyntastic
" -----------------------------------------------------------------------------
" < ale 插件配置 >
" -----------------------------------------------------------------------------
" {{{
"
" 始终开启标志列
let g:ale_sign_column_always = 1
let g:ale_set_highlights = 0
" 自定义error和warning图标
let g:ale_sign_error = '✗'
let g:ale_sign_warning = '⚠'
" 在vim自带的状态栏中整合ale
let g:ale_statusline_format = ['✗ %d', '⚡ %d', '✔ OK']
" 显示Linter名称,出错或警告等相关信息
let g:ale_echo_msg_error_str = 'E'
let g:ale_echo_msg_warning_str = 'W'
let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
" 普通模式下,sp前往上一个错误或警告,sn前往下一个错误或警告
nmap sp (ale_previous_wrap)
nmap sn (ale_next_wrap)
" s触发/关闭语法检查
nmap s :ALEToggle
" d查看错误或警告的详细信息
nmap d :ALEDetail
" 文件内容发生变化时不进行检查
let g:ale_lint_on_text_changed = 'never'
" 打开文件时不进行检查
let g:ale_lint_on_enter = 0
" 设置状态栏显示
set statusline+=%{ALEGetStatusLine()}
" }}}
else
" -----------------------------------------------------------------------------
" < syntastic 插件配置 >
" -----------------------------------------------------------------------------
" {{{
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
" 设置error和warning的标志
let g:syntastic_enable_signs = 1
let g:syntastic_error_symbol='✗'
let g:syntastic_warning_symbol='⚠'
" 总是打开Location List(相当于QuickFix)窗口,如果你发现syntastic因为与其他插件冲突而经常崩溃,将下面选项置0
let g:syntastic_always_populate_loc_list = 1
" 自动打开Locaton List,默认值为2,表示发现错误时不自动打开,当修正以后没有再发现错误时自动关闭,置1表示自动打开自动关闭,0表示关闭自动打开和自动关闭,3表示自动打开,但不自动关闭
let g:syntastic_auto_loc_list = 1
" 修改Locaton List窗口高度
let g:syntastic_loc_list_height = 5
" 打开文件时自动进行检查
let g:syntastic_check_on_open = 1
" 自动跳转到发现的第一个错误或警告处
let g:syntastic_auto_jump = 1
" 进行实时检查,如果觉得卡顿,将下面的选项置为1
let g:syntastic_check_on_wq = 0
" 高亮错误
let g:syntastic_enable_highlighting=1
" 让syntastic支持C++11
let g:syntastic_cpp_checkers = ['gcc']
let g:syntastic_cpp_compiler = 'gcc'
let g:syntastic_cpp_compiler_options = '-std=c++11'
" 设置pyflakes为默认的python语法检查工具
let g:syntastic_python_checkers = ['pyflakes']
" 修改高亮的背景色, 适应主题
highlight SyntasticErrorSign guifg=white guibg=black
function! ToggleErrors()
let old_last_winnr = winnr('$')
lclose
if old_last_winnr == winnr('$')
" Nothing was closed, open syntastic error location panel
Errors
endif
endfunction
nnoremap s :call ToggleErrors()
" nnoremap sn :lnext
" nnoremap sp :lprevious
" }}}
endif
" -----------------------------------------------------------------------------
" < Tagbar 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 相对 TagList 能更好的支持面向对象
" \tb 常规模式下输入 \tb 调用插件,如果有打开 TagList 窗口则先将其关闭
nnoremap tb :TlistClose:TagbarToggle
let g:tagbar_width=30 "设置窗口宽度
" let g:tagbar_left=1 "在左侧窗口中显示
let g:tagbar_sort = 0 " 关闭排序 [也就是按标签本身在文件中的位置排序]
let g:tagbar_show_linenumbers = -1 " 显示行号 [使用全局关于行号的默认配置]
let g:tagbar_autopreview = 1 " 开启自动预览 [随着光标在标签上的移动,顶部会出现一个实时的预览窗口]
let g:tagbar_compact=1 " tagbar 子窗口中不显示冗余帮助信息
" 设置 ctags 对哪些代码标识符生成标签
let g:tagbar_type_cpp = {
\ 'kinds' : [
\ 'c:classes:0:1',
\ 'd:macros:0:1',
\ 'e:enumerators:0:0',
\ 'f:functions:0:1',
\ 'g:enumeration:0:1',
\ 'l:local:0:1',
\ 'm:members:0:1',
\ 'n:namespaces:0:1',
\ 'p:functions_prototypes:0:1',
\ 's:structs:0:1',
\ 't:typedefs:0:1',
\ 'u:unions:0:1',
\ 'v:global:0:1',
\ 'x:external:0:1'
\ ],
\ 'sro' : '::',
\ 'kind2scope' : {
\ 'g' : 'enum',
\ 'n' : 'namespace',
\ 'c' : 'class',
\ 's' : 'struct',
\ 'u' : 'union'
\ },
\ 'scope2kind' : {
\ 'enum' : 'g',
\ 'namespace' : 'n',
\ 'class' : 'c',
\ 'struct' : 's',
\ 'union' : 'u'
\ }
\ }
" }}}
" -----------------------------------------------------------------------------
" < TagList 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 高效地浏览源码, 其功能就像vc中的workpace
" 那里面列出了当前文件中的所有宏,全局变量, 函数名等
" \tl 常规模式下输入 \tl 调用插件,如果有打开 Tagbar 窗口则先将其关闭
nnoremap tl :TagbarClose:Tlist
let Tlist_Show_One_File=1 "只显示当前文件的tags
" let Tlist_Enable_Fold_Column=0 "使taglist插件不显示左边的折叠行
let Tlist_Exit_OnlyWindow=1 "如果Taglist窗口是最后一个窗口则退出Vim
let Tlist_File_Fold_Auto_Close=1 "自动折叠
let Tlist_WinWidth=30 "设置窗口宽度
let Tlist_Use_Right_Window=1 "在右侧窗口中显示taglist窗口
" }}}
" -----------------------------------------------------------------------------
" < ctrlp.vim 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 一个全路径模糊文件,缓冲区,最近最多使用,... 检索插件;详细帮助见 :h ctrlp
" 常规模式下输入:Ctrl + p 调用插件
let g:ctrlp_map = 'p'
let g:ctrlp_cmd = 'CtrlP'
noremap f :CtrlPMRU
set wildignore+=*/tmp/*,*.so,*.o,*.a,*.obj,*.swp,*.zip,*.pyc,*.pyo,*.class,.DS_Store
let g:ctrlp_custom_ignore = {
\ 'dir': '\v[\/]\.(git|hg|svn|rvm)$',
\ 'file': '\v\.(exe|so|dll|zip|tar|tar.gz|pyc)$',
\ }
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
" }}}
" ------------------------------------------------------------------------------
" < ZoomWin 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于分割窗口的最大化与还原
" 常规模式下按快捷键 o 在最大化与还原间切换
" }}}
" -----------------------------------------------------------------------------
" rainbow_parentheses括号显示增强
" -----------------------------------------------------------------------------
" {{{
" 不加入这行, 防止黑色括号出现, 很难识别
" \ ['black', 'SeaGreen3'],
let g:rbpt_colorpairs = [
\ ['brown', 'RoyalBlue3'],
\ ['Darkblue', 'SeaGreen3'],
\ ['darkgray', 'DarkOrchid3'],
\ ['darkgreen', 'firebrick3'],
\ ['darkcyan', 'RoyalBlue3'],
\ ['darkred', 'SeaGreen3'],
\ ['darkmagenta', 'DarkOrchid3'],
\ ['brown', 'firebrick3'],
\ ['gray', 'RoyalBlue3'],
\ ['darkmagenta', 'DarkOrchid3'],
\ ['Darkblue', 'firebrick3'],
\ ['darkgreen', 'RoyalBlue3'],
\ ['darkcyan', 'SeaGreen3'],
\ ['darkred', 'DarkOrchid3'],
\ ['red', 'firebrick3'],
\ ]
let g:rbpt_max = 16
let g:rbpt_loadcmd_toggle = 0
au VimEnter * RainbowParenthesesToggle
au Syntax * RainbowParenthesesLoadRound
au Syntax * RainbowParenthesesLoadSquare
au Syntax * RainbowParenthesesLoadBraces
" }}}
" -----------------------------------------------------------------------------
" vim-matchit
" -----------------------------------------------------------------------------
" {{{
" 显示marks - 方便自己进行标记和跳转
" m[a-zA-Z] add mark
" '[a-zA-Z] go to mark
" m del all marks
" }}}
" -----------------------------------------------------------------------------
" < cSyntaxAfter 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 高亮括号与运算符等
"au! BufRead,BufNewFile,BufEnter *.{c,cpp,h,java,javascript} call CSyntaxAfter()
" }}}
" -----------------------------------------------------------------------------
" < cSyntaxAfter 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" C++11/14 语法高亮插件从 STL-Syntax 换为 vim-cpp-enhanced-highlight,后者支持
" STL 以及自行编写函数、容器、模版等的高亮
"
let g:cpp_class_scope_highlight = 1
let g:cpp_member_variable_highlight = 1
let g:cpp_experimental_simple_template_highlight = 1
let g:cpp_experimental_template_highlight = 1
let g:cpp_concepts_highlight = 1
"}}}
" -----------------------------------------------------------------------------
" vim-latex
" -----------------------------------------------------------------------------
" {{{
set grepprg=grep\ -nH\ $* " 使grep总是生成文件名
let g:tex_flavor='latex' " vim默认把空的tex文件设为plaintex而不是tex,导致latex-suite不被加载
set iskeyword+=:
autocmd BufEnter *.tex set sw=2
let g:Tex_CompileRule_dvi='latex -src-specials -interaction=nonstopmode $*'
let g:Tex_DefaultTargetFormat = 'pdf'
let g:Tex_ViewRule_pdf = 'SumatraPDF -reuse-instance -inverse-search "gvim -c \":RemoteOpen +\%l \%f\""'
let g:Tex_CompileRule_pdf = 'xelatex --synctex=-1 -src-specials -interaction=nonstopmode $*'
" }}}
" -----------------------------------------------------------------------------
" vim-multiple-cursors
" ----------------------------------------------------------------------------
" {{{
" Set multiple cursore key
"
let g:multi_cursor_use_default_mapping=0
" Default mapping
let g:multi_cursor_next_key=''
let g:multi_cursor_prev_key=''
let g:multi_cursor_skip_key=''
let g:multi_cursor_quit_key=''
let g:multi_cursor_start_key=''
"let g:multi_cursor_start_word_key='g'
" Called once right before you start selecting multiple cursors
function! Multiple_cursors_before()
if exists(':NeoCompleteLock')==2
exe 'NeoCompleteLock'
endif
endfunction
" Called once only when the multiple selection is canceled (default )
function! Multiple_cursors_after()
if exists(':NeoCompleteUnlock')==2
exe 'NeoCompleteUnlock'
endif
endfunction
" }}}
" -----------------------------------------------------------------------------
" vim-easymotion
" -----------------------------------------------------------------------------
" {{{
let g:EasyMotion_do_mapping = 0 " Disable default mappings
" Bi-directional find motion
" Jump to anywhere you want with minimal keystrokes, with just one key binding.
" `s{char}{label}`
" nnoremap s (easymotion-s)
" or
" `s{char}{char}{label}`
" Need one more keystroke, but on average, it may be more comfortable.
nnoremap s (easymotion-s2)
" Turn on case insensitive feature
let g:EasyMotion_smartcase = 1
" JK motions: Line motions
noremap j (easymotion-j)
noremap k (easymotion-k)
" }}}
" -----------------------------------------------------------------------------
" incsearch
" -----------------------------------------------------------------------------
" {{{
nnoremap :nohlsearch
" }}}
"------grep工具配置------------------------------------------------------------
" {{{
"定义快捷键关闭当前分割窗口
"nnoremapq :q
"使用Grep.vim插件在工程内全局查找,设置快捷键。快捷键速记法:searchin project
nnoremap gp :Grep
"使用Grep.vim插件在工程内全局查找,设置快捷键。快捷键速记法:searchin buffer
nnoremap gb :GrepBuffer -ir
" }}}
" -----------------------------------------------------------------------------
" < expand-regiond 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 选择区域扩展按键配置(Visual模式下)
vnoremap v (expand_region_expand)
vnoremap (expand_region_shrink)
" }}}
if g:isAirLine
" -----------------------------------------------------------------------------
" < vim_airline 插件配置 >
" -----------------------------------------------------------------------------
" {{{
"设置状态栏符号显示"
" let g:airline_theme='cool'
let g:airline_powerline_fonts = 1
function! AirLineInit()
let spc = g:airline_symbols.space
" let g:airline_section_a = airline#section#create_left(['mode', 'crypt', 'paste', 'spell', 'capslock', 'xkblayout', 'iminsert'])
" let g:airline_section_b = let g:airline_section_b = airline#section#create(['hunks', 'branch'])
" if exists("+autochdir") && &autochdir == 1
" let g:airline_section_c = airline#section#create(['%<', 'path', spc, 'readonly'])
" else
" let g:airline_section_c = airline#section#create(['%<', 'file', spc, 'readonly'])
" endif
" let g:airline_section_x = airline#section#create_right(['tagbar', 'filetype'])
" let g:airline_section_y = airline#section#create_right(['ffenc'])
if winwidth(0) > 80
let g:airline_section_z = airline#section#create(['windowswap', 'obsession', '%3p%%'.spc, 'linenr', 'maxlinenr', spc.':%3v', ': [%b/0x%B]'])
else
let g:airline_section_z = airline#section#create(['%3p%%'.spc, 'linenr', ':%3v', ': [%b/0x%B]'])
endif
endfunction
autocmd VimEnter * call AirLineInit()
" }}}
endif
" -----------------------------------------------------------------------------
" DoxygenToolkit.vim
" -----------------------------------------------------------------------------
"{{{
let g:DoxygenToolkit_briefTag_funcName = "yes"
let g:DoxygenToolkit_briefTag_pre = "@brief "
let g:DoxygenToolkit_templateParamTag_pre = "@tparam "
let g:DoxygenToolkit_paramTag_pre = "@param "
let g:DoxygenToolkit_returnTag = "@return "
let g:DoxygenToolkit_fileTag = "@file "
let g:DoxygenToolkit_dateTag = "@date "
let g:DoxygenToolkit_authorTag = "@author "
let g:DoxygenToolkit_versionTag = "@version "
let g:DoxygenToolkit_blockTag = "@name "
let g:DoxygenToolkit_classTag = "@class "
let g:DoxygenToolkit_authorName = "LI Wenhui, lwh@vip.163.com"
let g:doxygen_enhanced_color = 1
" Load doxygen syntax file for c/cpp/idl files
let load_doxygen_syntax=1
"}}}
" -----------------------------------------------------------------------------
" ctrlsf.vim
" -----------------------------------------------------------------------------
" {{{
" 使用 ctrlsf.vim 插件在工程内全局查找光标所在关键字,设置快捷键。快捷键速记法:search in project
nnoremap sp :CtrlSF
" }}}
" -----------------------------------------------------------------------------
" < txtbrowser 插件配置 >
" -----------------------------------------------------------------------------
" 用于文本文件生成标签与与语法高亮(调用TagList插件生成标签,如果可以)
" {{{
"au BufRead,BufNewFile *.txt setlocal ft=txt
" }}}
" -----------------------------------------------------------------------------
" gundo.vim
" -----------------------------------------------------------------------------
" {{{
" 调用 gundo 树
nnoremap ud :GundoToggle
" }}}
" -----------------------------------------------------------------------------
" < startify 配置 > 请确保已安装了工具
" -----------------------------------------------------------------------------
" {{{
let g:startify_custom_header = [
\ ' ________ _______ ',
\ ' __ /\_______\/\ ___ \ ',
\ ' __ __ /\_\ ___ ___ / / ___ /\ \ \ /\ \ ',
\ ' /\ \/\ \\/\ \ / __` __`\ / / /_\/ / \ \ \ \ \ ',
\ ' \ \ \_/ |\ \ \/\ \/\ \/\ \/ / __ / __ \ \ \__\ \ ',
\ ' \ \___/ \ \_\ \_\ \_\ \_\/ /_\/ / /\_\ \ \______\ ',
\ ' \/__/ \/_/\/_/\/_/\/_/_______/ \/_/ \/______/ ',
\ ' ',
\ ]
" }}}
```
其中,每项
```
Plug '*'
```
对应一个插件,后续若有新增插件,只需追加至该列表中即可。vim-plug 支持源码托管在 https://github.com/ 的插件,同时 vim 官网 http://www.vim.org/ 上的所有插件均在 https://github.com/vim-scripts/ 有镜像,所以,基本上主流插件都可以纳入 vim-plug 管理。具体而言,以 ctrlsf.vim 为例,它在 .vimrc 中配置信息为 dyng/ctrlsf.vim,vim-plug 很容易构造出其真实下载地址 https://github.com/dyng/ctrlsf.vim.git ,然后借助 git 工具进行下载及安装。
此后,需要安装插件,先找到其在 github.com 的地址,再将配置信息其加入 .vimrc 中。
## 3 界面美化
玉不琢不成器,vim 不配不算美。刚安装好的 vim 朴素得吓人,这是与我同时代的软件么?
(默认 vim 界面)
就我的审美观而言,至少有几个问题:语法高亮太单薄、主题风格太简陋、窗口元素太冗余、辅助信息太欠缺。
### 3.1 主题风格
一套好的配色方案绝对会影响你的编码效率,vim 内置了 10 多种配色方案供你选择,GUI 下,可以通过菜单(Edit -> Color Scheme)试用不同方案,字符模式下,需要你手工调整配置信息,再重启 vim 查看效果(csExplorer 插件,可在字符模式下不用重启即可查看效果)。不满意,可以去 http://vimcolorschemetest.googlecode.com/svn/html/index-c.html 慢慢选。我自认为“阅美无数”,目前最夯三甲:
* 素雅 solarized(https://github.com/altercation/vim-colors-solarized )
* 多彩 molokai(https://github.com/tomasr/molokai )
在 .vimrc 中选用某个主题:
```
" 配色方案
set background=dark
colorscheme solarized
```
其中,不同主题都有暗 / 亮色系之分,这样三种主题六种风格,久不久换一换,给你不一样的心情:
(solarized 主题风格)
### 3.2 营造专注氛围
如今的 UX 设计讲究的是内容至上,从 GNOME3 的变化就能看出。编辑器界面展示的应全是代码,不应该有工具条、菜单、滚动条浪费空间的元素,另外,编程是种精神高度集中的脑力劳动,不应出现闪烁光标、花哨鼠标这些分散注意力的东东。配置如下:
```
" 显示 / 隐藏菜单栏、工具栏、滚动条,可用 F12 切换
" {{{
if g:isGUI
set guioptions-=m " 删除菜单栏
set guioptions-=T " 删除工具栏
set guioptions-=r " 删除右侧滚动条
set guioptions-=l " 删除左侧滚动条
set guioptions-=L " 即使存在垂直窗口分割也删除左侧滚动条
set guioptions-=b " 删除底部滚动条
noremap :if &guioptions =~# 'm'
\set guioptions-=m
\set guioptions-=T
\set guioptions-=r
\set guioptions-=l
\set guioptions-=L
\else
\set guioptions+=m
\set guioptions+=T
\set guioptions+=r
\set guioptions-=l
\set guioptions+=L
\endif
endif
" }}}
```
重启 vim 后效果如下:
(去除冗余窗口元素)
还容易分神?好吧,我们把 vim 弄成全屏模式。vim 自身无法实现全屏,必须借助第三方工具 gvimtweak,一个控制窗口 XYZ 坐标、窗口尺寸的命令行工具。本 vim 包已包含 gvimtweak,再在 .vimrc 中增加如下信息:
```
" -----------------------------------------------------------------------------
" < gvimtweak 工具配置 > 请确保以已装了工具
" -----------------------------------------------------------------------------
" {{{ Win平台下窗口全屏组件 gvimtweak.dll
" F11 全屏切换
" Ctrl + ↑ --减少窗口透明度 [非插入模式]
" Ctrl + ↓ --增加窗口透明度 [非插入模式]
" Ctrl + T --窗口置顶模式切换 [非插入模式]
" Vim启动的时候自动使用当前颜色的背景色以去除Vim的白色边框
if g:isWindows && g:isGUI && has("libcall")
if has("win64")
let g:MyVimLib = "gvimtweak_x64.dll"
else
let g:MyVimLib = "gvimtweak.dll"
endif
function! ToggleFullScreen()
call libcall(g:MyVimLib, "ToggleFullScreen", 1)
endfunction
let g:VimAlpha = 245
function! SetAlpha(alpha)
let g:VimAlpha = g:VimAlpha + a:alpha
if g:VimAlpha < 100
let g:VimAlpha = 100
endif
if g:VimAlpha > 255
let g:VimAlpha = 255
endif
call libcall(g:MyVimLib, "SetAlpha", g:VimAlpha)
endfunction
let g:VimTopMost = 0
function! SwitchVimTopMostMode()
if g:VimTopMost == 0
let g:VimTopMost = 1
else
let g:VimTopMost = 0
endif
call libcall(g:MyVimLib, "EnableTopMost", g:VimTopMost)
endfunction
"映射 F11 切换全屏vim, 全屏后再隐藏菜单栏、工具栏、滚动条效果更好
noremap :call ToggleFullScreen()
"切换Vim是否在最前面显示
nnoremap :call SwitchVimTopMostMode()
"减少Vim窗体的透明度
nnoremap :call SetAlpha(+10)
"增加Vim窗体的不透明度
nnoremap :call SetAlpha(-10)
" 默认设置透明
autocmd GUIEnter * call libcallnr(g:MyVimLib, "SetAlpha", g:VimAlpha)
endif
" }}}
```
上面是一段简单的 vimscript 脚本,外部命令 wmctrl 及其命令行参数控制将指定窗口 windowid(即,vim)全屏,绑定快捷键 F11 实现全屏 / 窗口模式切换(linux 下各 GUI 软件约定使用 F11 全屏,最好遵守约定),最后配置启动时自动全屏。
### 3.3 添加辅助信息
去除了冗余元素让 vim 界面清爽多了,为那些实用辅助信息腾出了空间。光标当前位置、显示行号、高亮当前行 / 列等等都很有用:
```
" 总是显示状态栏
set laststatus=2
" 显示光标当前位置
set ruler
" 开启行号显示
set number
" 高亮显示当前行 / 列
set cursorline
set cursorcolumn
" 高亮显示搜索结果
set hlsearch
```
效果如下:
(添加辅助信息)
### 3.4 其他美化
默认字体不好看,挑个自己喜欢的,前提是你得先安装好该字体。中文字体,我喜欢饱满方正的(微软雅黑),英文字体喜欢圆润的(Consolas),vim 无法同时使用两种字体,怎么办?有人制作发布了一款中文字体用微软雅黑、英文字体用 Consolas 的混合字体 —— yahei consolas hybrid 字体,号称最适合中国程序员使用的字体,效果非常不错(本文全文采用该字体)。在 .vimrc 中设置下:
```
" 设置 gvim 显示字体
set guifont=Microsoft_YaHei_Mono:h11
```
上面的字体设置方式是在 Windows 操作系统下,如果是在 Linux 设置方式如下
```
set guifont=Microsoft\ YaHei\ Mono:11
```
其中,由于字体名存在空格,需要用转义符“\”进行转义;最后的 12 用于指定字体大小。
代码折行也不太美观,禁止掉:
```
" 禁止折行
set nowrap
```
前面介绍的主题风格对状态栏不起作用,需要借助插件 Airline(https://github.com/bling/vim-airline )美化状态栏,在 .vimrc 中设定状态栏主题风格:
```
" 个性化状栏(这里提供两种方式,要使用其中一种去掉注释即可,不使用反之)
" 在使用 AirLine 时,这个设置无效,应修改 AirLine 设置
" {{{
if (!g:isAirLine)
" 状态栏颜色配置
function! InsertStatuslineColor(mode)
if a:mode == 'i'
hi statusline guibg=blue
elseif a:mode == 'r'
hi statusline guibg=magenta
else
hi statusline guibg=red
endif
endfunction
au InsertEnter * call InsertStatuslineColor(v:insertmode)
au InsertChange * call InsertStatuslineColor(v:insertmode)
au InsertLeave * hi statusline guibg=black
" default the statusline to black when entering Vim
hi statusline guibg=black
" 状态栏显示内容配置
" set statusline=%F%m%r%h%w[ %=[FORMAT=%{&ff}][ [TYPE=%Y][ [POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")}
set statusline=%t[ %m\ %r\ %h%w\ %=[%l/%L(%p%%),%c%V][ [%b:0x%B]\ [%{&ft==''?'TEXT':toupper(&ft)},%{toupper(&ff)},%{toupper(&fenc!=''?&fenc:&enc)}%{&bomb?',BOM':''}%{&eol?'':',NOEOL'}]%{strftime(\"\ [%d/%m/%y-%H:%M]\")}
endif
" }}}
```
效果如下:
(界面美化最终效果)
图中,中英文混合字体看着是不是很舒服哈;增强后的状态栏,不仅界面漂亮多了,而且多了好些辅助信息(所在函数名、文件编码格式、文件类型)。
## 4 代码分析
阅读优秀开源项目源码是提高能力的重要手段,营造舒适、便利的阅读环境至关重要。
### 4.1 语法高亮
代码只有一种颜色的编辑器,就好像红绿灯只有一种颜色的路口,全然无指引。现在已是千禧年后的十年了,早已告别上世纪六、七十年代黑底白字的时代,即使在字符模式下编程(感谢伟大的 fbterm),我也需要语法高亮。所幸 vim 自身支持语法高亮,只需显式打开即可:
```
" 开启语法高亮功能
syntax enable
" 允许用指定语法高亮配色方案替换默认方案
syntax on
```
效果如下:
(语法高亮)
上图中 STL 容器模板类 unordered\_multimap 并未高亮,对滴,vim 对 C++ 语法高亮支持不够好(特别是 C++11/14 新增元素),必须借由插件 vim-cpp-enhanced-highlight(https://github.com/octol/vim-cpp-enhanced-highlight )进行增强。效果如下:
(增强 C++11 及 STL 的语法高亮)
vim-cpp-enhanced-highlight 主要通过 .vim/bundle/vim-cpp-enhanced-highlight/after/syntax/cpp.vim 控制高亮关键字及规则,所以,当你发现某个 STL 容器类型未高亮,那么将该类型追加进 cpp.vim 即可。如,initializer_list 默认并不会高亮,需要添加
```
syntax keyword cppSTLtype initializer_list
```
### 4.2 代码缩进
C/C++ 中的代码执行流由复合语句控制,如 if(){} 判断复合语句、for(){} 循环符号语句等等,这势必出现大量缩进。缩进虽然不影响语法正确性,但对提升代码清晰度有不可替代的功效。
在 vim 中有两类缩进表示法,一类是用 1 个制表符('\t'),一类是用多个空格(' ')。两者并无本质区别,只是源码文件存储的字符不同而已,但,缩进可视化插件对两类缩进显示方式不同,前者只能显示为粗块,后者可显示为细条,就我的审美观而言,选后者。增加如下配置信息:
```
" 自适应不同语言的智能缩进
filetype indent on
" 将制表符扩展为空格
set expandtab
" 设置编辑时制表符占用空格数
set tabstop=4
" 设置格式化时制表符占用空格数
set shiftwidth=4
" 让 vim 把连续数量的空格视为一个制表符
set softtabstop=4
```
其中,注意下 expandtab、tabstop 与 shiftwidth、softtabstop、retab:
* expandtab,把制表符转换为多个空格,具体空格数量参考 tabstop 和 shiftwidth 变量;
* tabstop 与 shiftwidth 是有区别的。tabstop 指定我们在插入模式下输入一个制表符占据的空格数量,linux 内核编码规范建议是 8,看个人需要;shiftwidth 指定在进行缩进格式化源码时制表符占据的空格数。所谓缩进格式化,指的是通过 vim 命令由 vim 自动对源码进行缩进处理,比如其他人的代码不满足你的缩进要求,你就可以对其进行缩进格式化。缩进格式化,需要先选中指定行,要么键入 = 让 vim 对该行进行智能缩进格式化,要么按需键入多次 < 或 > 手工缩进格式化;
* softtabstop,如何处理连续多个空格。因为 expandtab 已经把制表符转换为空格,当你要删除制表符时你得连续删除多个空格,该设置就是告诉 vim 把连续数量的空格视为一个制表符,即,只删一个字符即可。通常应将这 tabstop、shiftwidth、softtabstop 三个变量设置为相同值;
另外,你总会阅读其他人的代码吧,他们对制表符定义规则与你不同,这时你可以手工执行 vim 的 retab 命令,让 vim 按上述规则重新处理制表符与空格关系。
很多编码规范建议缩进(代码嵌套类似)最多不能超过 4 层,但难免有更多层的情况,缩进一多,我那个晕啊:
(多层缩进)
我希望有种可视化的方式能将相同缩进的代码关联起来,indentLine(https://github.com/Yggdroot/indentLine )来了。安装好该插件后,增加如下配置信息:
```
" -----------------------------------------------------------------------------
" < indentLine 插件配置 >
" -----------------------------------------------------------------------------
" {{{
" 用于显示对齐线,与 indent_guides 在显示方式上不同,根据自己喜好选择了
" 在终端上会有屏幕刷新的问题,这个问题能解决有更好了
" \il 显示 / 关闭对齐线
nnoremap il :IndentLinesToggle
" 设置 Gvim 的对齐线样式
if g:isGUI
let g:indentLine_char = "|"
let g:indentLine_first_char = "|"
endif
" 设置终端对齐线颜色,如果不喜欢可以将其注释掉采用默认颜色
let g:indentLine_color_term = 239
" 设置 GUI 对齐线颜色,如果不喜欢可以将其注释掉采用默认颜色
" let g:indentLine_color_gui = '#A4E57E'
" }}}
```
重启 vim 效果如下:
(不连续的缩进可视化)
断节?Indent Guides 通过识别制表符来绘制缩进连接线,断节处是空行,没有制表符,自然绘制不出来,算是个小 bug,但瑕不掩瑜,有个小技巧可以解决,换行 - 空格 - 退格:
(完美可视化缩进)
### 4.3 代码折叠
有时为了去除干扰,集中精力在某部分代码片段上,我会把不关注部分代码折叠起来。vim 自身支持多种折叠:手动建立折叠(manual)、基于缩进进行折叠(indent)、基于语法进行折叠(syntax)、未更改文本构成折叠(diff)等等,其中,indent、syntax 比较适合编程,按需选用。增加如下配置信息:
```
" 基于缩进或语法进行代码折叠
"set foldmethod=indent
set foldmethod=syntax
" 启动 vim 时关闭折叠代码
set nofoldenable
```
操作:za,打开或关闭当前折叠;zM,关闭所有折叠;zR,打开所有折叠。效果如下:
(代码折叠)
### 4.4 接口与实现快速切换
我习惯把类的接口和实现分在不同文件中,常有在接口文件(MyClass.h)和实现文件(MyClass.cpp)中来回切换的操作。你当然可以先分别打开接口文件和实现文件,再手动切换,但效率不高。我希望,假如在接口文件中,vim 自动帮我找到对应的实现文件,当键入快捷键,在新 buffer 中打开对应实现文件。
switchtoinc(https://github.com/liwangmj/green_vim_switchtoinc)来了。安装后增加配置信息:
```
" 用于头文件和源文件间快速切换。注:在a.vim插件基础上修改的。
" :A ---切换头文件并独占整个窗口
" :AV ---切换头文件并垂直分割窗口
" :AS ---切换头文件并水平分割窗口
" :AN ---多个重名文件之间的切换
```
这样,键入 :A 就能在实现文件和接口文件间切换。如下图所示:
(接口文件与实现文件切换)
上图中,初始状态先打开了接口文件 MyClass.h,键入 :A 后,vim 在新 buffer 中打开实现文件 MyClass.cpp,并在当前窗口中显示;再次键入 :A 后,当前窗口切回接口文件。
### 4.5 代码收藏
源码分析过程中,常常需要在不同代码间来回跳转,我需要“收藏”分散在不同处的代码行,以便需要查看时能快速跳转过去,这时,vim 的书签(mark)功能派上大用途了。
vim 书签的使用很简单,在你需要收藏的代码行键入 mm,这样就收藏好了,你试试,没反应?不会吧,难道你 linux 内核编译参数有问题,或者,vim 的编译参数没给全,让我想想,别急,喔,对了,你是指看不到书签?好吧,我承认这是 vim 最大的坑,书签所在行与普通行外观上没任何差别,肉眼,你是找不到他滴。这可不行,得来个让书签可视化的插件,vim-signature(https://github.com/kshenoy/vim-signature )。vim-signature 通过在书签所在行的前面添加字符的形式,以此可视化书签,这就要求你源码安装的 vim 具备 signs 特性,具体可在 vim 命令模式下键入
```
:echo has('signs')
```
若显示 1 则具备该特性,反之 0 则不具备该特性,需参考“1 源码安装编辑器 vim ”重新编译 vim。
vim 的书签分为两类,独立书签和分类书签。独立书签,书签名只能由字母(a-zA-Z)组成,长度最多不超过 2 个字母,并且,同个文件中,不同独立书签名中不能含有相同字母,比如,a 和 bD 可以同时出现在同个文件在,而 Fc 和 c 则不行。分类书签,书签名只能由可打印特殊字符(!@#$%^&\*())组成,长度只能有 1 个字符,同个文件中,你可以把不同行设置成同名书签,这样,这些行在逻辑上就归类成相同类型的书签了。下图定义了名为 a 和 dF 两个独立书签(分别 259 行和 261 行)、名为 # 的一类分类书签(含 256 行和 264 行)、名为 @ 的一类分类书签(257 行),如下所示:
(独立书签和分类书签)
两种形式的书签完全分布在各自不同的空间中,所以,它俩的任何操作都是互不相同的,比如,你无法遍历所有书签,要么只能在各个独立书签间遍历,要么只能在分类书签间遍历。显然,两种形式的书签都有各自的使用场景,就我而言,只使用独立书签,原因有二:一是独立书签可保存,当我设置好独立书签后关闭文档,下次重新打开该文档时,先前的独立书签仍然有效,而分类书签没有该特性(其他文档环境恢复参见“6.3 环境恢复”);一是减少记忆快捷键,光是独立书签就有 8 种遍历方式,每种遍历对应一种快捷键,太难记了。
vim-signature 快捷键如下:
```
let g:SignatureMap = {
\ 'Leader' : "m",
\ 'PlaceNextMark' : "m,",
\ 'ToggleMarkAtLine' : "m.",
\ 'PurgeMarksAtLine' : "m-",
\ 'DeleteMark' : "dm",
\ 'PurgeMarks' : "mda",
\ 'PurgeMarkers' : "m",
\ 'GotoNextLineAlpha' : "']",
\ 'GotoPrevLineAlpha' : "'[",
\ 'GotoNextSpotAlpha' : "`]",
\ 'GotoPrevSpotAlpha' : "`[",
\ 'GotoNextLineByPos' : "]'",
\ 'GotoPrevLineByPos' : "['",
\ 'GotoNextSpotByPos' : "mn",
\ 'GotoPrevSpotByPos' : "mp",
\ 'GotoNextMarker' : "[+",
\ 'GotoPrevMarker' : "[-",
\ 'GotoNextMarkerAny' : "]=",
\ 'GotoPrevMarkerAny' : "[=",
\ 'ListLocalMarks' : "ms",
\ 'ListLocalMarkers' : "m?"
\ }
```
够多了吧,粗体部分是按个人习惯重新定义的快捷键,请添加进 .vimrc 中。
常用的操作也就如下几类:
* 书签设定。mx,设定 / 取消当前行名为 x 的标签;m,,自动设定下一个可用书签名,前面提说,独立书签名是不能重复的,在你已经有了多个独立书签,当想再设置书签时,需要记住已经设定的所有书签名,否则很可能会将已有的书签冲掉,这可不好,所以,vim-signature 为你提供了 m, 快捷键,自动帮你选定下一个可用独立书签名;mda,删除当前文件中所有独立书签。
* 书签罗列。m?,罗列出当前文件中所有书签,选中后回车可直接跳转;
* 书签跳转。mn,按行号前后顺序,跳转至下个独立书签;mp,按行号前后顺序,跳转至前个独立书签。书签跳转方式很多,除了这里说的行号前后顺序,还可以基于书签名字母顺序跳转、分类书签同类跳转、分类书签不同类间跳转等等。
效果如下:
(可视化书签)
我虽然选用了 vim-signature,但不代表它完美了,对我而言,无法在不同文件的书签间跳转绝对算是硬伤。另外,如果觉得收藏的代码行只有行首符号来表示不够醒目,你可以考虑 BOOKMARKS--Mark-and-Highlight-Full-Lines 这个插件(https://github.com/vim-scripts/BOOKMARKS--Mark-and-Highlight-Full-Lines ),它可以让书签行高亮,如下是它的快捷键:,高亮所有书签行;,关闭所有书签行高亮;,清除 [a-z] 的所有书签;,收藏当前行;,取消收藏当前行。
### 4.6 标识符列表
本节之前的内容,虽说与代码开发有些关系,但最多也只能算作用户体验层面的,真正提升生产效率的内容将从此开始。
本文主题是探讨如何将 vim 打造成高效的 C/C++ 开发环境,希望实现标识符列表、定义跳转、声明提示、实时诊断、代码补全等等系列功能,这些都需要 vim 能够很好地理解我们的代码(不论是 vim 自身还是借助插件甚至第三方工具),如何帮助 vim 理解代码?基本上,有两种主流方式:标签系统和语义系统。至于优劣,简单来说,标签系统配置简单,而语义系统效果精准,后者是趋势。目前对于高阶 IDE 功能,部分已经有对应基于语义的插件支撑,而部分仍只能通过基于标签的方式实现,若同个功能既有语义插件又有标签插件,优选语义。
#### 标签系统
代码中的类、结构、类成员、函数、对象、宏等等这些统称为标识符,每个标识符的定义、所在文件中的行位置、所在文件的路径等等信息就是标签(tag)。
Exuberant Ctags(http://ctags.sourceforge.net/ ,后简称 ctags)就是一款经典的用于生成代码标签信息的工具 。ctags 最初只支持生成 C/C++ 语言,目前已支持 41 种语言,具体列表运行如下命令获取:
```
ctags --list-languages
```
学习知识最好方式就是动手实践。我们以 main.cpp、my_class.h、my_class.cpp 三个文件为例:
第一步,准备代码文件。创建演示目录 /data/workplace/example/、库子目录 /data/workplace/example/lib/,创建如下内容的 main.cpp:
```
#include
#include
#include "lib/my_class.h"
using namespace std;
int g_num = 128;
// 重载函数
static void
printMsg (char ch)
{
std::cout << ch << std::endl;
}
int
main (void)
{
// 局部对象
const string name = "yangyang.gnu";
// 类
MyClass one;
// 成员函数
one.printMsg();
// 使用局部对象
cout << g_num << name << endl;
return (EXIT_SUCCESS);
}
```
创建如下内容的 my_class.h:
```
#pragma once
class MyClass
{
public:
void printMsg(void);
private:
;
};
```
创建如下内容的 my_class.cpp:
```
#include "my_class.h"
// 重载函数
static void
printMsg (int i)
{
std::cout << i << std::endl;
}
void
MyClass::printMsg (void)
{
std::cout << "I'M MyClass!" << std::endl;
}
```
第二步,生成标签文件。现在运行 ctags 生成标签文件:
```
cd /data/workplace/example/
ctags -R --c++-kinds=+p+l+x+c+d+e+f+g+m+n+s+t+u+v --fields=+liaS --extra=+q --language-force=c++
```
命令行参数较多,主要关注 --c++-kinds,ctags 默认并不会提取所有标签,运行
```
ctags --list-kinds=c++
```
可看到 ctags 支持生成标签类型的全量列表:
```
c classes
d macro definitions
e enumerators (values inside an enumeration)
f function definitions
g enumeration names
l local variables [off]
m class, struct, and union members
n namespaces
p function prototypes [off]
s structure names
t typedefs
u union names
v variable definitions
x external and forward variable declarations [off]
```
其中,标为 off 的局部对象、函数声明、外部对象等类型默认不会生成标签,所以我显式加上所有类型。运行完后,example/ 下多了个文件 tags,内容大致如下:
```
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
!_TAG_PROGRAM_NAME Exuberant Ctags //
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
!_TAG_PROGRAM_VERSION 5.8 //
MyClass lib/my_class.h /^class MyClass $/;" c
MyClass::printMsg lib/my_class.cpp /^MyClass::printMsg (void) $/;" f class:MyClass signature:(void)
MyClass::printMsg lib/my_class.h /^ void printMsg(void);$/;" p class:MyClass access:public signature:(void)
endl lib/my_class.cpp /^ std::cout << "I'M MyClass!" << std::endl;$/;" m class:std file:
endl lib/my_class.cpp /^ std::cout << i << std::endl;$/;" m class:std file:
endl main.cpp /^ cout << g_num << name << endl;$/;" l
endl main.cpp /^ std::cout << ch << std::endl;$/;" m class:std file:
g_num main.cpp /^int g_num = 128;$/;" v
main main.cpp /^main (void) $/;" f signature:(void)
name main.cpp /^ const string name = "yangyang.gnu";$/;" l
one main.cpp /^ MyClass one;$/;" l
printMsg lib/my_class.cpp /^MyClass::printMsg (void) $/;" f class:MyClass signature:(void)
printMsg lib/my_class.cpp /^printMsg (int i) $/;" f file: signature:(int i)
printMsg lib/my_class.h /^ void printMsg(void);$/;" p class:MyClass access:public signature:(void)
printMsg main.cpp /^ one.printMsg();$/;" p file: signature:()
printMsg main.cpp /^printMsg (char ch) $/;" f file: signature:(char ch)
std::endl lib/my_class.cpp /^ std::cout << "I'M MyClass!" << std::endl;$/;" m class:std file:
std::endl lib/my_class.cpp /^ std::cout << i << std::endl;$/;" m class:std file:
std::endl main.cpp /^ std::cout << ch << std::endl;$/;" m class:std file:
```
其中,! 开头的几行是 ctags 生成的软件信息忽略之,下面的就是我们需要的标签,每个标签项至少有如下字段(命令行参数不同标签项的字段数不同):标识符名、标识符所在的文件名(也是该文件的相对路径)、标识符所在行的内容、标识符类型(如,l 表示局部对象),另外,若是函数,则有函数签名字段,若是成员函数,则有访问属型字段等等。
#### 语义系统
通过 ctags 这类标签系统在一定程度上助力 vim 理解我们的代码,对于 C 语言这类简单语言来说,差不多也够了。近几年,随着 C++11/14 的推出,诸如类型推导、lamda 表达式、模版等等新特性,标签系统显得有心无力,这个星球最了解代码的工具非编译器莫属,如果编译器能在语义这个高度帮助 vim 理解代码,那么我们需要的各项 IDE 功能肯定能达到另一个高度。
语义系统,编译器必不可少。GCC 和 clang 两大主流 C/C++ 编译器,作为语义系统的支撑工具,我选择后者,除了 clang 对新标准支持及时、错误诊断信息清晰这些优点之外,更重要的是,它在高内聚、低耦合方面做得非常好,各类插件可以调用 libclang 获取非常完整的代码分析结果,从而轻松且优雅地实现高阶 IDE 功能。你对语义系统肯定还是比较懵懂,紧接着的“基于语义的声明 / 定义跳转”会让你有更为直观的了解,现在,请跳转至“7.1 编译器 / 构建工具集成”,一是了解 clang 相较 GCC 的优势,二是安装好最新版 clang 及其标准库,之后再回来。
#### 基于标签的标识符列表
在阅读代码时,经常分析指定函数实现细节,我希望有个插件能把从当前代码文件中提取出的所有标识符放在一个侧边子窗口中,并且能能按语法规则将标识符进行归类,tagbar (https://github.com/majutsushi/tagbar )是一款基于标签的标识符列表插件,它自动周期性调用 ctags 获取标签信息(仅保留在内存,不落地成文件)。安装完 tagbar 后,在 .vimrc 中增加如下信息:
```
" 设置 tagbar 子窗口的位置出现在主编辑区的左边
let tagbar_left=1
" 设置显示/隐藏标签列表子窗口的快捷键
nnoremap tb :TagbarToggle
" 设置标签子窗口的宽度
let tagbar_width=30
" tagbar 子窗口中不显示冗余帮助信息
let g:tagbar_compact=1
" 设置 ctags 对哪些代码标识符生成标签
let g:tagbar_type_cpp = {
\ 'kinds' : [
\ 'c:classes:0:1',
\ 'd:macros:0:1',
\ 'e:enumerators:0:0',
\ 'f:functions:0:1',
\ 'g:enumeration:0:1',
\ 'l:local:0:1',
\ 'm:members:0:1',
\ 'n:namespaces:0:1',
\ 'p:functions_prototypes:0:1',
\ 's:structs:0:1',
\ 't:typedefs:0:1',
\ 'u:unions:0:1',
\ 'v:global:0:1',
\ 'x:external:0:1'
\ ],
\ 'sro' : '::',
\ 'kind2scope' : {
\ 'g' : 'enum',
\ 'n' : 'namespace',
\ 'c' : 'class',
\ 's' : 'struct',
\ 'u' : 'union'
\ },
\ 'scope2kind' : {
\ 'enum' : 'g',
\ 'namespace' : 'n',
\ 'class' : 'c',
\ 'struct' : 's',
\ 'union' : 'u'
\ }
\ }
```
前面提过,ctags 默认并不会提取局部对象、函数声明、外部对象等类型的标签,我必须让 tagbar 告诉 ctags 改变默认参数,这是 tagbar_type_cpp 变量存在的主要目的,所以前面的配置信息中将局部对象、函数声明、外部对象等显式将其加进该变量的 kinds 域中。具体格式为
```
{short}:{long}[:{fold}[:{stl}]]
```
用于描述函数、变量、结构体等等不同类型的标识符,每种类型对应一行。其中,short 将作为 ctags 的 --c++-kinds 命令行选项的参数,类似:
```
--c++-kinds=+p+l+x+c+d+e+f+g+m+n+s+t+u+v
```
long 将作为 short 的简要描述展示在 vim 的 tagbar 子窗口中;fold 表示这种类型的标识符是否折叠显示;stl 指定是否在 vim 状态栏中显示附加信息。
重启 vim 后,打开一个 C/C++ 源码文件,键入 tb,将在左侧的 tagbar 窗口中将可看到标签列表:
(基于标签的标识符列表)
从上图可知 tagbar 的几个特点:
* 按作用域归类不同标签。按名字空间 n_foo、类 Foo 进行归类,在内部有声明、有定义;
* 显示标签类型。名字空间、类、函数等等;
* 显示完整函数原型;
* 图形化显示共有成员(+)、私有成员(-)、保护成员(#);
在标识符列表中选中对应标识符后回车即可跳至源码中对应位置;在源码中停顿几秒,tagbar 将高亮对应标识符;每次保存文件时或者切换到不同代码文件时 tagbar 自动调用 ctags 更是标签数据库;tagbar 有两种排序方式,一是按标签名字母先后顺序、一是按标签在源码中出现的先后顺序,在 .vimrc 中我配置选用后者,键入 s 切换不同不同排序方式。
### 4.7 声明 / 定义跳转
假设你正在分析某个开源项目源码,在 main.cpp 中遇到调用函数 func(),想要查看它如何实现,一种方式:在 main.cpp 中查找 -> 若没有在工程内查找 -> 找到后打开对应文件 -> 文件内查找其所在行 -> 移动光标到该行 -> 分析完后切换会先前文件,不仅效率太低更要命的是影响我的思维连续性。我需要另外高效的方式,就像真正函数调用一样:光标选中调用处的 func() -> 键入某个快捷键自动转换到 func() 实现处 -> 键入某个键又回到 func() 调用处,这就是所谓的定义跳转。
基本上,vim 世界存在两类导航:基于标签的跳转和基于语义的跳转。
#### 基于标签的声明 / 定义跳转
继续延用前面接收标签系统的例子文件 main.cpp、my_class.h、my_class.cpp,第二步已经生成好了标签文件,那么要实现声明 / 定义跳转,需要第三步,引入标签文件。这让 vim 知晓标签文件的路径。在 /data/workplace/example/ 目录下用 vim 打开 main.cpp,在 vim 中执行如下目录引入标签文件 tags:
```
:set tags+=/data/workplace/example/tags
```
既然 vim 有个专门的命令来引入标签,说明 vim 能识别标签。虽然标签文件中并无行号,但已经有标签所在文件,以及标签所在行的完整内容,vim 只需切换至对应文件,再在文件内作内容查找即可找到对应行。换言之,只要有对应的标签文件,vim 就能根据标签跳转至标签定义处。
这时,你可以体验下初级的声明 / 定义跳转功能。把光标移到 main.cpp 的 one.printMsg() 那行的 printMsg 上,键入快捷键 g],vim 将罗列出名为 printMsg 的所有标签候选列表,按需选择键入编号即可跳转进入。如下图:
(待选标签)
目前为止,离我预期还有差距。
第一,选择候选列表影响思维连续性。首先得明白为何会出现待选列表。前面说过,vim 做的事情很简单,就是把光标所在单词放到标签文件中查找,如果只有一个,当然你可以直接跳转过去,大部分时候会找到多项匹配标签,比如,函数声明、函数定义、函数调用、函数重载等等都会导致同个函数名出现在多个标签中,vim 无法知道你要查看哪项,只能让你自己选择。其实,因为标签文件中已经包含了函数签名属性,vim 的查找机制如果不是基于关键字,而是基于语义的话,那也可以直接命中,期待后续 vim 有此功能吧。既然无法直接解决,换个思路,我不想选择列表,但可以接受遍历匹配标签。就是说,我不想输入数字选择第几项,但可以接受键入正向快捷键后遍历第一个匹配标签,再次键入快捷键遍历第二个,直到最后一个,键入反向快捷键逆序遍历。这下事情简单了,命令 :tnext 和 :tprevious 分别先后和向前遍历匹配标签,定义两个快捷键搞定:
```
" 正向遍历同名标签
nmap tn :tnext
" 反向遍历同名标签
nmap tp :tprevious
```
等等,这还不行,vim 中有个叫标签栈(tags stack)的机制,:tnext、:tprevious 只能遍历已经压入标签栈内的标签,所以,你在遍历前需要通过快捷键 ctrl-] 将光标所在单词匹配的所有标签压入标签栈中,然后才能遍历。不说复杂了,以后你只需先键入 ctrl-],若没跳转至需要的标签,再键入 tn 往后或者 tp 往前遍历即可。如下图所示:
(基于标签的跳转)
第二,如何返回先前位置。当分析完函数实现后,我需要返回先前调用处,可以键入 vim 快捷键 ctrl-t 返回,如果想再次进入,可以用前面介绍的方式,或者键入 ctrl-i。另外,注意,ctrl-o 以是一种返回快捷键,但与 ctrl-t 的返回不同,前者是返回上次光标停留行、后者返回上个标签。
### 4.7 内容查找
vim 支持正则表达式,那么已经具有强劲的查供能力,在当前文件内查找,vim 的 / 和 ? 查找命令非常好用,但工程内查找,自带的查找用户体验还无法达到我的预期。
内容查找,你第一反应会想到 grep 和 ack 两个工具,没错,它俩强大的正则处理能力无需质疑,如果有插件能在 vim 中集成两个工具之一,那么任何查找任务均可轻松搞定,为此,出现了 grep.vim(https://github.com/yegappan/grep )和 ack.vim(https://github.com/mileszs/ack.vim )两个插件,通过它们,你可以在 vim 中自在地使用高度整合的 grep 或 ack 两个外部命令,就像 vim 的内部命令一样:查找时,把光标定位到待查找关键字上后,通过快捷键立即查找该关键字,查询结果通过列表形式将关键字所在行罗列出来,选择后就能跳转到对应位置。很好,这全部都是我想要的,但是,不是我想要的全部。
你知道,在分析源码时,同个关键字会在不同文件的不同位置多次出现,grep.vim 和 ack.vim 只能“将关键字所在行罗列出来”,如果关键字出现的那几行完全相同,那么,我单凭这个列表是无法确定哪行是我需要的,比如,我查找关键字 cnt,代码中,cnt 在 4 行出现过、64 行、128 行、1024 行都出现过,且每行内容均为
```
++cnt;
```
这时,即便 grep.vim 或 ack.vim 在一个有四个选项的列表中为你罗列出相关行,因为完全相同,所以你也无法确定到底应该查看第几项。换言之,除了罗列关键字所在行之外,我还需要看到所在行的上下几行,这样,有了上下文,我就可以最终决定哪一行是我需要的了。ctrlsf.vim(https://github.com/dyng/ctrlsf.vim )为此而生。
ctrlsf.vim 后端调用 ack,所以你得提前自行安装,版本不得低于 v2.0,openSUSE 用户可以
```
zypper --no-refresh in ack
```
进行安装。ctrlsf.vim 支持 ack 所有选项,要查找某个关键字(如,yangyang),你可以想让光标定位在该关键字上面,然后命令模式下键入
```
:CtrlSF
```
将自动提取光标所在关键字进行查找,你也可以指定 ack 的选项
```
:CtrlSF -i -C 1 [pattern] /my/path/
```
为方便操作,我设定了快捷键:
```
" 使用 ctrlsf.vim 插件在工程内全局查找光标所在关键字,设置快捷键。快捷键速记法:search in project
nnoremap sp :CtrlSF
```
避免手工键入命令的麻烦。查找结果将以子窗口在左侧呈现,不仅罗列出所有匹配项,而且给出匹配项的上下文。如果从上下文中你还觉得信息量不够,没事,可以键入 p 键,将在右侧子窗口中给出该匹配项的完整代码,而不再仅有前后几行。不想跳至任何匹配项,可以直接键入 q 退出 ctrlsf.vim;如果有钟意的匹配项,光标定位该项后回车,立即跳至新 buffer 中对应位置。
太性感了,以关键字 CmdlineOption 为例,如下所示:
(内容查找)
### 4.8 内容替换
有个名为 iFoo 的全局变量,被工程中 16 个文件引用过,由于你岳母觉得匈牙利命名法严重、异常、绝对以及十分万恶,为讨岳母欢心,不得不将该变量更名为 foo,怎么办?依次打开每个文件,逐一查找后替换?对我而言,内容替换存在两种场景:快捷替换和精确替换。
#### 快捷替换
前面介绍的 ctrlsf 已经把匹配的字符串汇总在侧边子窗口中显示了,同时,它还允许我们直接在该子窗口中进行编辑操作,在这种环境下,如果我们能快捷选中所有匹配字符串,那么就可以先批量删除再在原位插入新的字符串,这岂不是我们需要的替换功能么?
快捷选中 ctrlsf 子窗口中的多个匹配项,关键还是这些匹配项分散在不同行的不同位置,这就需要多光标编辑功能,vim-multiple-cursors 插件(https://github.com/terryma/vim-multiple-cursors )为次而生。装好 vim-multiple-cursors 后,你随便编辑个文档,随便输入多个相同的字符串,先在可视化模式下选中其中一个,接着键入 ctrl-n,你会发现第二个该字符串也被选中了,持续键入 ctrl-n,你可以选中所有相同的字符串,把这个功能与 ctrlsf 结合,你来感受下:
(快捷替换)
上图中,我想将 prtHelpInfo() 更名为 showHelpInfo(),先通过 ctrlsf 找到工程中所有 prtHelpInfo,然后直接在 ctrlsf 子窗口中选中第一个 ptr,再通过 vim-multiple-cursors 选中第二个 ptr,接着统一删除 ptr 并统一键入 show,最后保存并重新加载替换后的文件。
vim-multiple-cursors 默认快捷键与我系统中其他软件的快捷键冲突,按各自习惯重新设置:
```
let g:multi_cursor_next_key=''
let g:multi_cursor_skip_key='
```
#### 精确替换
vim 有强大的内容替换命令:
```
:[range]s/{pattern}/{string}/[flags]
```
在进行内容替换操作时,我关注几个因素:如何指定替换文件范围、是否整词匹配、是否逐一确认后再替换。
如何指定替换文件范围?
* 如果在当前文件内替换,[range] 不用指定,默认就在当前文件内;
* 如果在当前选中区域,[range] 也不用指定,在你键入替换命令时,vim 自动将生成如下命令:
```
:'<,'>s/{pattern}/{string}/[flags]
```
* 你也可以指定行范围,如,第三行到第五行:
```
:3,5s/{pattern}/{string}/[flags]
```
* 如果对打开文件进行替换,你需要先通过 :bufdo 命令显式告知 vim 范围,再执行替换;
* 如果对工程内所有文件进行替换,先 :args \*\*/*.cpp **/*.h 告知 vim 范围,再执行替换;
是否整词匹配?{pattern} 用于指定匹配模式。如果需要整词匹配,则该字段应由 \< 和 \> 修饰待替换字符串(如,\);无须整词匹配则不用修饰,直接给定该字符串即可;
是否逐一确认后再替换?[flags] 可用于指定是否需要确认。若无须确认,该字段设定为 ge 即可;有时不见得所有匹配的字符串都需替换,若在每次替换前进行确认,该字段设定为 gec 即可。
是否整词匹配和是否确认两个条件叠加就有 4 种组合:非整词且不确认、非整词且确认、整词且不确认、整词且确认,每次手工输入这些命令真是麻烦;我把这些组合封装到一个函数中,如下 Replace() 所示:
```
" 替换函数。参数说明:
" confirm:是否替换前逐一确认
" wholeword:是否整词匹配
" replace:被替换字符串
function! Replace(confirm, wholeword, replace)
wa
let flag = ''
if a:confirm
let flag .= 'gec'
else
let flag .= 'ge'
endif
let search = ''
if a:wholeword
let search .= '\<' . escape(expand(''), '/\.*$^~[') . '\>'
else
let search .= expand('')
endif
let replace = escape(a:replace, '/\&~')
execute 'argdo %s/' . search . '/' . replace . '/' . flag . '| update'
endfunction
```
为最大程度减少手工输入,Replace() 还能自动提取待替换字符串(只要把光标移至待替换字符串上),同时,替换完成后自动为你保存更改的文件。现在要做的就是赋予 confirm、wholeword 不同实参实现 4 种组合,再绑定 4 个快捷键即可。如下:
```
" 不确认、非整词
nnoremap R :call Replace(0, 0, input('Replace '.expand('').' with: '))
" 不确认、整词
nnoremap rw :call Replace(0, 1, input('Replace '.expand('').' with: '))