VIM环境配置

如下的全部内容主要参照:html

  https://github.com/yangyangwithgnu/use_vim_as_ide 。python

  原blog做者写的很是用心,建议你们都去看看。(我的以为经过这个blog大大提高了学习vim配置环境的效率,所以给做者捐了顿外卖的钱这个随笔就是学习上述blog内容的辅助记录,并不能代替学习原著;把学习过程当中遇到的一些坑列出来,初步作一下知识沉淀linux

 

  之前我我的的学习理念是“重剑无锋、大巧不工”,瞧不上这些快捷键之类的“奇技淫巧”。可是,打磨完了vim的开发环境后,我改变了这个见解:知识能够厚,可是顺手的工具必需要用心去一点点儿打磨锋利,提升工做效率的‘量变’会带来能力水平的‘质变’。c++

 

  另,本人纯vim初学者,所以学习过程当中确定存在吃不透甚至错误的地方。一方面后续会自我不断改进,另外一方面也很是欢迎你们拍砖指点。git

 

【系统环境 & 内容提纲】github

我用操做系统环境:面试

  mac OS X Yosemite 10.10.3编程

脚本环境用的zsh:json

  

搭建完成后的编辑环境以下:vim

  

  能够看到:标签(函数变量)、工程目录、自动补全、状态栏提示、语法分析错误提示都集成在了里面。在实现了一个IDE的主要功能同时,VIM还可让你最大程度自由定制我的的开发环境,快捷键等等。

全文的提纲汇总以下:

(一)从新编译安装vim7.4

(二)编辑界面显示美化插件

(三)安装pathogen插件管理工具

(四)语法高亮、代码缩进、代码折叠、强命名下的接口与实现快速切换

(五)基于标签的代码导航

(六)内容查找、内容替换、批量注释

(七)代码模板

(八)代码自动补全

(九)工程文件浏览 & 多文档编辑

(十)静态语法分析器

(十一)快速移动功能

下面对上述十一块内容逐一说明,各个部分有先后逻辑限制,改变顺序不保证每一个功能都能实现正确

 

 

 【步入正题】

(一)从新编译安装vim7.4

个人mac上自带的vim版本是7.3,但有个极其重要的自动补全插件YouCompletetMe须要的vim版本是7.4,所以必须再装一套高版本vim。

vim7.4压缩包地址:ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2

基本上按照blog中步骤执行以下命令(假设上述文件解压到了~/Download/vim74中):

cd ~/Downloads/vim74
./configure --with-features=huge --enable-rubyinterp --enable-pythoninterp --with-python-config-dir=/usr/lib/python2.7/config/ --enable-perlinterp --enable-gui=gtk2 --enable-cscope --prefix=/usr --enable-luainterp 
make

报错:

简单分析一下error,应该是命名冲突了。google了http://zhouyichu.com/vim/Vim-in-Mac.html

解决方案是在vim74/src/os_unix.h中加入#include <AvailabilityMacros.h>。Done。

再执行

sudo make install

输入root密码后,Done。

再输入vim,版本就升级到7.4了

===================================================================

注意./configure的参数中

--prefix=/usr

是vim安装路径的,若是不想替换系统原有的,就能够把vim安装到自定义的路径下。再使用alias命令,在.bashrc中加一句

  alias vi="vim安装路径/vim"。

再注意,若是你跟我同样用了zsh这类的bash增强版,就须要在~/.zshrc中再加一句 source ~/.bashrc,以下图

这样你每次输入vi启动的才是vim7.4,不然zsh仍是会默认调用原来系统自定义的vim。

===================================================================

另外,要想成功编译安装vim,还须要python-devel、python3-devel、ruby-devel、libX11-devel、gtk-devel、gtk2-devel、gtk3-devel、ncurses-devel等支持。(我在服务器端安装的时候,就遇到了ncurses没有的状况,这个后续再去解决)

 

(二)编辑界面显示美化插件

因为pathogen没法管理界面美化插件,所以界面美化单独拎出来

1. 界面配色

(1)创建.vim/colors/文件夹,并将三个配色的vim文件copy到.vim/colors/文件夹下面,以下图所示

  

(2).vimrc增长配置,使界面美化生效

  在.vimrc中添加的命令以下

" 配色方案
set background=dark
"colorscheme solarized
colorscheme molokai
"colorscheme phd

  仅仅这样还不够,此时vim的编辑环境仍是黑白的,还要让vim开启语法高亮而且可以应用配色文件。须要继续在.vimrc中添加下面的配置;

syntax enable
syntax on

  这样,再次打开vim的编辑环境,语法高亮配色就体现出来了:

  

2. 添加辅助信息

  为了防止编程过程当中串行了,在设置一些辅助信息以下:

复制代码
" 老是显示状态栏
set laststatus=2
" 显示光标当前位置
set ruler
" 开启行号显示
set number
" 高亮显示当前行/列
set cursorline
set cursorcolumn
" 高亮显示搜索结果
set hlsearch
" 禁止折行
set nowrap
复制代码

  添加了这些信息后,能够看到vim编辑界面以下(光标、高亮、行号、状态栏都有了):

 

通过上述的配置,如今vim的编辑环境有了颜色,有了行号。但仅仅是一个带语法颜色提示的txt,其余强大的功能还要借助于各类插件。

要想管理好各种插件,就要使用下面一节介绍的pathogen插件管理工具。

 

(三)安装pathogen插件管理工具

用pathogen来管理vim插件的特点是:.vim/bundle/下每一个插件有独立的文件夹,插件之间几乎是彻底解耦。

这样带来的好处是:在添加、删除某个插件时,几乎不影响其余的插件。

1. 安装pathogen

pathogen虽然说是管理vim插件的,可是它自身就是一个插件。安装过程以下。

(1)在~/.vim路径下执行以下命令

  

(2)下载pathogen.vim到autoload

https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim

执行以下命令:

curl -o pathogen.vim https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim

以下:

  

(3)在.vimrc中增长配置信息

一是告诉vim,pathogen在哪;二是告诉vim执行pathogen的infect命令

" 将 pathogen 自身也置于独立目录中,需指定其路径 
runtime bundle/pathogen/autoload/pathogen.vim
" 运行 pathogen
execute pathogen#infect()

2. 用pathogen工具引入Powerline插件

以状态栏加强工具Powerline插件的安装过程为例,体验一下pathogen管理插件的便捷。

简单来讲用pathogen引入插件分两步:

(1)进入.vim/bundle/路径下:若是有git管理的,能够直接“git clone 插件所在的git的url”;若是没有git,就创建一个新的文件夹,再下载插件的zip包,解压到新建的这个文件夹下。

(2)在.vimrc中针对该插件的使用进行配置

下面看看Powerline如何搞:

Powerline插件的连接:https://github.com/Lokaltog/vim-powerline

(1)进入.vim/bundle/路径下,执行以下命令:

  

(2)原教程还推荐了个设置:

" 设置状态栏主题风格
let g:Powerline_colorscheme='solarized256'

  可是感受不用这个设置,Powerline也表现的不错,因此我也没有在.vimrc中配置。效果以下图所示:

  

红框框里面就是Powerline强化后的状态栏。并且在normal visual insert不一样的状态下能够显示不一样的功能,能够自行试试。

 

(四)语法高亮、代码缩进、代码折叠、强命名下的接口与实现快速切换

1. 语法高亮

因为目标是打造c/c++的vim环境,所以为了支持c++ STL的元素高亮。能够借助stl.vim插件补强vim的c++语法高亮功能。

(1)新建~/.vim/bunlde/STL-Syntax/after/syntax/cpp/文件夹

(2)下载stl.vim到~/.vim/bunlde/STL-Syntax/after/syntax/cpp/文件夹下

  

重启vim,咱们再打开一个cpp文件:

  

能够看到unordered_map这个关键字支持变色了(可是也看到了有些弊端,好比line22的begin和end都是通常的变量,也被识别为了关键字,这个后续但愿能找到改进的方法

同时,咱们看到上面的一些代码在缩进管理上是有问题的(line14 line22);这对c++还好,可是python这类对缩进严格要求的语言就不行。至此,引出了代码缩进的设置。

2. 代码缩进

  vim中有两种缩进表示法,一类是用1个制表符'\t',一类是用多个空格' '。

  缩进可视化插件对两类缩进显示的方式不一样:'\t'只能显示为粗块,而' '能够显示伟细条。

  在.vimrc中增长以下的配置:

复制代码
" 自适应不一样语言的智能缩进
filetype indent on
" 将制表符扩展为空格
set expandtab
" 设置编辑时制表符占用空格数
set tabstop=4
" 设置格式化时制表符占用空格数
set shiftwidth=4
" 让 vim 把连续数量的空格视为一个制表符
set softtabstop=4
复制代码

  设置完成,重启vim后,咱们再用vim打开的上面的cpp文件:

  

  能够看到,代码老老实实的按照规矩缩进回去了。

  阅读代码的时候,常常会遇到for while if 等等包含的代码行数比较多的状况。所以须要讲相同缩进的代码关联起来,便于阅读。

  首先须要引进vim-indent-guides这个插件,用pathogen引入,再也不赘述。(因为在个人mac上,一直以为这个插件的使用效果通常,就不上图了

3. 代码折叠

  须要在.vimrc中配置以下的内容:  

 " 代码折叠
 "set foldmethod=indent "基于缩进的代码折叠
 set foldmethod=syntax "基于语法的代码折叠
 " 启动 vim 时不折叠代码
 set nofoldenable

  仍是打开上述cpp文件,均在NORMAL模式下进行操做。

  命令:za

  效果:全都折叠上了

  

  命令:zr 

  效果:每次输入zr打开一层折叠

  

  命令:zR

  效果:把全部折叠都打开了(不上图了)

  命令:za

  效果:把一层代码折上

  

  命令:za

  效果:把当前折上的代码代开(这里连续使用za能够比较方便的折叠、打开一个代码段

  

折叠代码部分的结论就是:za命令最好用;想折哪段就za,再想打开这段代码再za(我的感受za按键位置比较舒服,因此一个za命令就足够了

4.强命名下的接口与实现快速切换

  习惯上c++中实现和接口是分开的:如my_class.cpp存放实现,my_class.h存放接口,而且两者在同一个目录下。以下:

  

  目的就是实现同名的.cpp和.h跳转。

  先安装插件:https://github.com/vim-scripts/a.vim

  再在.vimrc中以下配置:

 " *.cpp 和 *.h 间切换
 nmap <Leader>ch :A<CR>
 " 子窗口中显示 *.cpp 或 *.h
 nmap <Leader>sch :AS<CR>

  用vim打开my_class.cpp文件,不断输入“;ch”就能够在my_class.cpp和my_class.h中切换。

  注意,这里必需要求.cpp和.h文件在同一个文件夹中而且命名相同;所以这种强命名条件使得切换的做用很是有限。这个插件如今并不太推荐,至于有多大做用还得看工做中的场景吧。

 

(五)基于标签的代码导航

  标签(tag)的概念很是关键,原教程中评价标签是现代IDE的基石之一,深觉得然。

  什么是标签?

  代码中的类、结构、类成员、函数、对象、宏这些元素就是标签,每一个标签有它本身的名字、定义、类型、所在文件中的行的位置、所在文件的路径等属性。

  编译环节之一就是提取标签,但因为编译器并未把生成的标签输出到文本中,后来出现了专门用于生成标签的工具Exuberant Ctags(就是如今常说的ctags)。

  若是你的机器上没有ctags,那么请去这里下载http://ctags.sourceforge.net,并参照教程(http://blog.csdn.net/duguteng/article/details/7412652)去安装。

  ctags与vim有什么关系?

  其实ctags与vim彻底就是两个东西,两者本来各司其职,但基于标签的代码导航把两者联系在了一块儿。

  要想让vim支持基于标签的导航,大致上须要完成以下两件事情:

    (1)给代码文件生成tags(先得生成标签原料

    (2)在.vimrc中增长些配置(让vim知道上哪去找标签,以及一些辅助配置

  下面经过实际的例子来体会下ctags与vim的结合。

  ctags的应用实例(参照原教程)

  (1)创建test_tags文件夹,再创建test_tags/lib/子文件夹。

  (2)在test_tags目录下,创建main.cpp

  (3)在test_tags/lib目录下,创建my_class.h, my_class.cpp

  回到test_tags目录下,执行以下命令(先执行命令看结果,回过头来再分析):  

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++

  这时候,在test_tags目录下,多了一个tags文件:

  

  打开tags文件,执行:set list命令:

  

  每一行表明一条标签记录,且一行中各个字段间用\t分割:

  标签名 \t 标签所在的文件路径 \t 标签所在的行内容 \t 标签类型 \t 标签的语言 ...

  这里的标签类型比较抽象,都是m l f p 这些单个的字母,都表示什么意思呢?

  能够执行以下命令查看c++标签类型的含义:

ctags --list-kinds=c++

  结果以下:

  

  这时候,咱们就能够知道生成tags的命令中“--c++-kinds=”的含义了:就是告诉ctags要生成哪些元素的标签。

  如今体验一下标签导航的功能。

  vim打开main.cpp,而后:set tags+=你本身的路径/test_tags/tags

  光标移到one.printMsg()的printMsy()上,输入g],看效果:

  

  输入须要的标签的序号,好比5,就跳转到了lib/my_class.cpp文件中,而且光标锁定在line4的位置上。

  

  若是想返回到原来的位置,ctrl + o便可。

  上述的方法做为替代手工查找已经便捷了不少;可是正如同上面的例子,有时候常常得在同名的标签中选择。

  一种替代的方式是,输入某种快捷键,每次遍历一个标签。

  如今.vimrc中作以下配置:

 " 正向遍历同名标签
 nmap <Leader>tn :tnext<CR>
 " 反向遍历同名标签
 nmap <Leader>tp :tprevious<CR>

  再用vim打开main.cpp文件;光标停留在printMsg上面:

  ctrl + ] (将printMsg设定为要标签导航的目标)

  ;tn ;tp(向后/前找下一个同名的标签导航的位置)

  ctrl + o (回到上一个标签)

  ctrl + t (回到调用的位置)  

  经过这种方式,能够不间断地遍历同名标签,大致上仍是优于屏幕上选数字的方法。

  (若是是工程化的开发,还能够用indexer这个vim插件来周期性针对工程文件自动生成标签文件,并通知vim引入该标签文件;但目前缺乏应用场景,所以先不去讨论。

  tagbar插件。

  以上内容,能够作到单个标签的导航。若是要看正在编辑的代码的所有标签内容呢?最好把标签分门别类列出来,相似Eclipse里面那种导航bar的效果?

  这里能够引入tagbar的插件。

  (1)先安装:https://github.com/majutsushi/tagbar

  (2)再在.vimrc中作配置:

复制代码
" 设置 tagbar 子窗口的位置出如今主编辑区的左边 
let tagbar_left=1 
" 设置显示/隐藏标签列表子窗口的快捷键。速记:tag list 
nnoremap <Leader>tl :TagbarToggle<CR> 
" 设置标签子窗口的宽度 
let tagbar_width=32 
" tagbar 子窗口中不显示冗余帮助信息 
let g:tagbar_compact=1
复制代码

  再用vim打开上面的main.cpp文件,并执行;tl命令,效果以下图:

  

  能够看到,在默认状况下tagbar会列出来当前编辑文件的function和variable两类的标签。

  若是要显示更多类别的标签,就须要在.vimrc中加入以下配置:

复制代码
" 设置 ctags 对哪些代码元素生成标签
let g:tagbar_type_cpp = {
    \ 'kinds' : [
        \ 'd:macros:1',
        \ 'g:enums',
        \ 't:typedefs:0:0',
        \ 'e:enumerators:0:0',
        \ 'n:namespaces',
        \ 'c:classes',
        \ 's:structs',
        \ 'u:unions',
        \ 'f:functions',
        \ 'm:members:0:0',
        \ 'v:global:0:0',
        \ 'x:external:0:0',
        \ 'l:local:0:0'
     \ ],
     \ 'sro'        : '::',
     \ 'kind2scope' : {
         \ 'g' : 'enum',
         \ 'n' : 'namespace',
         \ 'c' : 'class',
         \ 's' : 'struct',
         \ 'u' : 'union'
     \ },
     \ 'scope2kind' : {
         \ 'enum'      : 'g',
         \ 'namespace' : 'n',
         \ 'class'     : 'c',
         \ 'struct'    : 's',
         \ 'union'     : 'u'
     \ }
\ }
复制代码

  看配置项中的名称也能分析出个大概,不具体深究了。再看修改tagbar配置后的效果图:

  

  看到tagbar中的选项丰富了不少(这里只须要记住,之后要是但愿tagbar中显示不一样的内容,在.vimrc中配置tagbar_type_XXX这个就行了

  标签→源码:选中tagbar中的标签,回车就定位到了源码

  源码→标签:光标在源码上停一下子,对应的tagbar中的标签就高亮了(这算是一个不容易发现的彩蛋

  对tagbar中的标签排序:默认是按照每一类的字母顺序排序;还能够按照标签出现的前后顺序排序;切换的按键是s

 

(六)内容查找、内容替换、批量注释

  1. 内容查找

  我遇到的内容查找分如下几种:

  (1)在正在编辑的文件中查找。以前本身在vim中查找都用"/"这个方法,效果就是把正在编辑的文件中含有该关键字的都高亮显示。

  (2)在文件夹中查找。若是分析工程代码的时候,可能须要查找的关键字在多个文件中,我都是用grep(还有ack可用,可是我没用过

  以上两种状况综合起来,内容查找不只要求可以标出来关键字,并且还须要关键字所在文件的信息(不一样文件含有关键字的语句相同)。

  能够用ctrlsf插件(https://github.com/dyng/ctrlsf.vim)来完成这个事情。

  (1)ctrlsf插件依赖于ack支持,所以还须要先装一下ack(参照http://www.jianshu.com/p/2f1c140c7eb8)。

  (2)在.vimrc中给ctrlsf插件启动设置快捷键  

" 使用 ctrlsf.vim 插件在工程内全局查找光标所在关键字,设置快捷键。快捷键速记法:search in project
nnoremap <Leader>sp :CtrlSF<CR>

  以后再打开main.cpp文件,光标停留在printMsg上,再输入";sp",效果如图:

  

  这个查找功能简直碉堡了,迅速显示出了关键字的上下文信息。

  在左边选择须要的关键字上下文,再按p就显示了所在的源码,以下图:

    

  若是想回到原来编辑的代码,按q就能够了。这样查找功能就很好的集成到了vim中,我的感受能够与标签导航结合使用来查看代码逻辑。

  2. 内容替换

  这个在开发工做中更常见了:好比须要对某个变量从新命名,尤为须要把全部与这个变量关联的内容(可能在不一样文件中)都替换掉。

  原教程中并无提供插件来作这个事情,而是做者本身写了一份vim的脚本函数;因为不是标准化插件,所以先不作配置了。

  可是同窗在面试中说竟遇到了面试官问vim中如何替换字符的问题,所以掌握一下基本的操做仍是必要的。

  能够参照这个blog(http://blog.sciencenet.cn/blog-724080-725117.html)学习。

  大致来讲,vim的内容替换命令的模板伟“:n,ms/string1/string2/g”

  (1)n,m:替换发生做用的行(若是是%s就表明全部行,有其余替换要求能够再具体查阅)

  (2)string1:目标字符串

  (3)string2:要把目标字符串替换成的字符串

  (4)g:若是有g就是表明每行全部的匹配字符串;若是没有g就是每行匹配的第一个字符串

  3. 批量注释

  因为是c/c++的开发,所以引入nerdcommenter这个插件(https://github.com/scrooloose/nerdcommenter)。

  还须要在.vimrc中加上一句话“filetype plugin on”。

  整行注释

  用vim打开main.cpp。对前四行添加注释。

  (1)“V”(注意是大写的V,这样进入的是选择全行的模式,若是是v则不是全行)

  (2)“jjj” 选中前四行

  (3)";cc" 添加注释

  效果如图:

  

  重复(1)(2),将(3)改成“;cu”则消除了注释

  部分注释

  好比要注释掉printMsg函数括号中的参数。

  光标停留在括号内,执行“vi)”,选中括号中的所有内容,如图:

  

  再执行";cc",效果如图:

  

  这种部分注释的也很经常使用,若是快捷键使用熟练能够省去很多时间。

  另外,对于大中小括号、双引号、单引号等各类号中变量的删除、复制、选择都比较经常使用。能够参考(http://www.linuxsong.org/2010/09/vim-quick-select-copy-delete/)学习。

 

(七)代码模板

  所谓的代码模板就是经常使用的 if() while() for()等等固定的套路。

  安装UltiSnips插件能够实现这个功能(https://github.com/SirVer/ultisnips)。

  有了UltiSnips还不够,还须要告诉UltiSnips按照什么样的模板来补全。

  这里就用了原文做者写的一个模板cpp.snippets(因为比较长,就不放上来了)。

  把这个模板放在.vim/bundle/ultisnips/mysnippets/cpp.snippets这里。

  而后还须要配置下.vimrc文件:

复制代码
" UltiSnips的tag键与YCM冲突 须要从新设定
let g:UltiSnipsExpandTrigger="<leader><tab>"
let g:UltiSnipsJumpForwardTrigger="<leader><tab>"
let g:UltiSnipsJumpBackwardTrigger="<leader><s-tab>"
" 告诉ultisnips模板文件在哪
let g:UltiSnipsSnippetDirectories=["mysnippets"]
复制代码

  须要注意的是,原文做者提供的cpp.snippets对同一个关键字的不一样模板给出了不一样的触发关键字。

  什么意思呢?我截取cpp.snippets中的一段以下:

复制代码
# 经过迭代器遍历容器(可读写) 
snippet for 
for (auto ${2:iter} = ${1:c}.begin(); ${3:$2} != $1.end(); ${4:++iter}) {
    ${5:TODO} 
} 
endsnippet 
# 经过迭代器遍历容器(只读) 
snippet cfor 
for (auto ${2:citer} = ${1:c}.cbegin(); ${3:$2} != $1.cend(); ${4:++citer}) { 
    ${5:TODO} 
} 
endsnippet 
# 经过下标遍历容器 
snippet For 
for (auto ${2:i} = 0; $2 != ${1}.size(); ${3:++}$2) { 
    ${4:TODO} 
} 
endsnippet 
复制代码

  for、cfor、For分别触发不一样模板,以下图:

   

  以此类推,能够多学习一下原做者的模板快捷键。

  也能够设定本身的快捷键

  以vector为例分析,利用代码模板怎么快速定义一个"vector<int>  v_int_test"。

  先看cpp.snippets中的vector段落以下:

  

  (1)在INSERT模式下输入vec,再输入“;Tab”,默认就出来"vector<char>  v;",而且光标停留在"<>"中内容被选中,如图:

    

  (2)此时,因为char都处于选中状态,所以直接输入int就变成以下的状态:

     

  (3)此时,再输入";Tab",则光标跳到了v的后面了,如图:

     

  (4)这时,再输入"_int_test",最后结果如图:

    

  整个过程很是连贯。经过这个例子,了解了自定义snippets的关键三点:

  a. 触发。科学定义模板补全的触发前缀(这个例子中触发vector的前缀是vec

  a. 定义骨架。经过${序号}来表示光标跳转的顺序(为了防止与后面自动补全的Tab冲突,在这里设置;Tab为模板补全的快捷键,上面已经说过了

  b. 手动填肉。经过";Tab"完成跳转。

  定义的snippets能够最大化发挥代码模板补全的做用,省去了很是多的时间,达到与IDE相同的效果这算是个人一个痛点,让我决定投身vim族

 

(八)代码自动补全

  代码补全的方式有两种:基于标签的补全 & 基于语义的补全

  1. 基于标签的补全

  OmniCppComplete + SuperTab 两个插件:

  https://github.com/vim-scripts/OmniCppComplete

  https://github.com/vim-scripts/SuperTab

  先在/usr/include和/usr/include/c++/4.8.4中执行ctags命令,生成c++和system的标签,并引入.vimrc。

  须要在.vimrc中配置以下:

  

  效果还能够:以下图:

  

  有时候须要看的信息比较多,能够修改“set completeopt=”这个参数,得到效果以下:

  

  这种方式显示的信息,都是执行ctags产生的标签信息。有的时候信息多了还杂,因此每每就不单独列出来一个buffer中显示信息了。

  上面这种方式主要是我我的补充的,原教程主要但愿使用YouCompleteMe这个终极补全神器,所以Omni的补全方式不细说了。

  2. 基于语义的补全

  这里只须要记住一个终极补全神器YouCompleteMe(YCM)插件(https://github.com/Valloric/YouCompleteMe)。

  YCM插件安装

  这个插件须要编译安装,过程比较复杂,主要四个安装步骤:

  (1)拉下来YCM的源码包以及相关依赖。

cd ~/.vim/bundle/ 
git clone https://github.com/Valloric/YouCompleteMe.git 
cd YouCompleteMe/ 
git submodule update --init --recursive

    效果如图:

    

  (2)下载libclang。(YCM后端调用libclang)

    这里下载的是做者推荐的http://llvm.org/releases/download.html中的预编译二进制文件(Pre-built Binaries)。

    解压后,将文件夹重命名为"clang+llvm"。存放的路径以下:

    

  (3)编译YCM共享库(用到了上一步下载的clang+llvm中的标准liblang

    执行以下命令:

cd /opt/local
sudo mkdir ycm_build 
cd ycm_build 
sudo cmake -G "Unix Makefiles" -DPATH_TO_LLVM_ROOT=/opt/local/clang+llvm . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp sudo make ycm_support_libs

    一个是看终端是否返回所有完成;另外一个去看是否生成了三个文件:

    

  (4)配置.ycm_extra_conf.py文件

    YCM支持每一个工程文件配置不一样的conf文件(这样的好处是不用每次新建工程就改.vimrc)。

    个人配置文件以下:

复制代码
 1 import os
 2 import ycm_core
 3 from clang_helpers import PrepareClangFlags
 4 
 5 # Set this to the absolute path to the folder (NOT the file!) containing the
 6 # compile_commands.json file to use that instead of 'flags'. See here for
 7 # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
 8 # Most projects will NOT need to set this to anything; you can just change the
 9 # 'flags' list of compilation flags. Notice that YCM itself uses that approach.
10 compilation_database_folder = ''
11 
12 # These are the compilation flags that will be used in case there's no
13 # compilation database set.
14 flags = [
15     '-Wall', 16     '-std=c++11', 17     '-stdlib=libc++', 18     '-x', 19     'c++', 20     '-I', 21     '.', 22     '-isystem', 23     '/usr/include/', 24     '-isystem', 25     '/usr/lib/', 26     '-I/opt/local/clang+llvm/include/c++/v1'
27 ]
28 
29 if compilation_database_folder:
30     database = ycm_core.CompilationDatabase(compilation_database_folder)
31 else:
32     database = None
33 
34 
35 def DirectoryOfThisScript():
36     return os.path.dirname(os.path.abspath(__file__))
37 
38 
39 def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
40     if not working_directory:
41         return flags
42     new_flags = []
43     make_next_absolute = False
44     path_flags = ['-isystem', '-I', '-iquote', '--sysroot=']
45     for flag in flags:
46         new_flag = flag
47 
48         if make_next_absolute:
49             make_next_absolute = False
50             if not flag.startswith('/'):
51                 new_flag = os.path.join(working_directory, flag)
52 
53         for path_flag in path_flags:
54             if flag == path_flag:
55                 make_next_absolute = True
56                 break
57 
58             if flag.startswith(path_flag):
59                 path = flag[len(path_flag):]
60                 new_flag = path_flag + os.path.join(working_directory, path)
61                 break
62 
63         if new_flag:
64             new_flags.append(new_flag)
65     return new_flags
66 
67 
68 def FlagsForFile(filename):
69     if database:
70         # Bear in mind that compilation_info.compiler_flags_ does NOT return a
71         # python list, but a "list-like" StringVec object
72         compilation_info = database.GetCompilationInfoForFile(filename)
73         final_flags = PrepareClangFlags(
74             MakeRelativePathsInFlagsAbsolute(
75                 compilation_info.compiler_flags_,
76                 compilation_info.compiler_working_dir_),
77             filename)
78     else:
79         relative_to = DirectoryOfThisScript()
80         final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
81 
82     return {
83         'flags': final_flags,
84         'do_cache': True}
复制代码

    其中line15~line26是重点的配置项,须要改为本身的路径。

    另请注意"-isystem","/usr/include"是一对配置:"-isystem"是配置项参数名称;"/usr/include"是配置项参数内容(这个不要跳坑了)

  插件使用配置

  YCM集成了各类补全引擎:语义补全引擎、标签补全引擎、OmniCppComplete补全引擎、其余补全引擎(如路径补全)

  主要集中在.vimrc中,先列出所有可能的配置:

复制代码
" YCM 补全菜单配色
" 补全功能在注释中一样有效
let g:ycm_complete_in_comments=1
" 容许 vim 加载 .ycm_extra_conf.py 文件,再也不提示
let g:ycm_confirm_extra_conf=0
" 开启 YCM 标签补全引擎
let g:ycm_collect_identifiers_from_tags_files=1
" 引入 C++ 标准库tags
set tags+=/usr/include/c++/4.8.4/stdcpp.tags
" YCM 集成 OmniCppComplete 补全引擎,设置其快捷键
inoremap <leader>; <C-x><C-o>
" 补全内容不以分割子窗口形式出现,只显示补全列表
set completeopt-=preview
" 从第一个键入字符就开始罗列匹配项
let g:ycm_min_num_of_chars_for_completion=1
" 禁止缓存匹配项,每次都从新生成匹配项
let g:ycm_cache_omnifunc=0
" 语法关键字补全         
let g:ycm_seed_identifiers_with_syntax=1
复制代码

  (1)语义补全:这个是默认的补全策略。就是键入字符,补全候选项就列出来了。而后再tab选择。很少说。

  (2)标签补全。标签补全已经说过了:先生成标签,再.vimrc中将标签路径加进来,再开启YCM标签引擎。可是标签引擎的开启,会极大的影响自动补全反应速度,有时候你输入的快一些补全根本反应不过来。要注意引入标签路径的数量。(我我的目前是不开标签引擎)

  (3)OmniCppComplete补全。这里个也满经常使用的,好比linux系统开发时最经常使用的fork()函数,YCM的默认语义补全引擎识别不出来。以下图:

    

    能够看到根本没有fork这个选项,即便有#include <unistd.h>。这是由于YCM默认的随键补全策略是无论系统头文件函数的。

    这时候能够告诉YCM:“我发现默认的随键补全很差使了,请给我换成OmniCppComplete补全引擎”。由于,Omni能够作到只要#include中包含了头文件,就能够补全出来。这里用一个比较巧的触发Omni补全的快捷方式,在.vimrc中设置“inoremap <leader>; <C-x><C-o>”,即";;"就能够触发Omni补全引擎。效果如图:

    

    能够看到,卡卡瞬间第一个就是fork。并且,这种补全引擎切换是暂时的,一旦这个fork补全过以后又恢复到最经常使用的默认语义补全了。

=======================================================

    若是非要钻牛角尖,硬要YCM随键补全出来fork,到底能不能行呢?是能够的

    在.vimrc中引入系统标签sys.tags,再开启标签引擎就能够作到,以下图:

    

    为何原文做者不推荐这种形式呢?我猜缘由可能有两点:

    1、这须要引入系统标签sys.tag,这个文件不小;引入sys.tags会下降vim的补全反应效率,极可能跟不上键入字符的速度了

    2、随键补全一次引入的补全候选项太多了。卡卡列出来一大堆,选的时候可能会影响效率。

=======================================================

    (4)路径补全。感受这个功能稍微弱一些,反应有些慢。不过仍是聊胜于无,看图:

       

 

(九)工程文件浏览 & 多文档编辑

  1. 工程文件浏览

  这里用 NERDtree (https://github.com/scrooloose/nerdtree)插件能够查看文件列表。

  (1)在bundle下安装插件。

  (2)在.vimrc中加配置:

复制代码
" 使用 NERDTree 插件查看工程文件。设置快捷键,速记:file list
nmap <Leader>fl :NERDTreeToggle<CR>
" 设置NERDTree子窗口宽度
let NERDTreeWinSize=32
" 设置NERDTree子窗口位置
let NERDTreeWinPos="right"
" 显示隐藏文件
let NERDTreeShowHidden=1
" NERDTree 子窗口中不显示冗余帮助信息
let NERDTreeMinimalUI=1
" 删除文件时自动删除文件对应 buffer
let NERDTreeAutoDeleteBuffer=1
复制代码

  效果如图所示:

  

  光标停留在tree那个window中:

  'r' : 刷新tree

  'I' : 切换是否显示隐藏文件

  2. 多文档编辑

  这里引入MiniBufExploer这个插件(https://github.com/fholgado/minibufexpl.vim)。我的以为这个插件的默认配置就OK了,结合NERDTree插件能够实现比较好的多文档编辑。

  这里注意几个快捷键:

  当光标停留在某个buffer上的时候

    ‘d’ : 关闭这个buffer

    ‘v’ : 垂直分屏

    ‘s’ : 水平分屏

  当处于Normal模式的时候:

    ‘:bn’ : 跳到下一个buffer

    ‘:bp’ : 跳到前一个buffer

    ‘:bdn’ : 关闭序号为n的buffer

  效果以下图:

  

  至此,既有左侧的标签列表(函数、变量、类等),又有右侧的工程文件列表,终于有点儿IDE的样子了,再加上自动补全,终于有些IDE的样子了。

 

(十)静态语法分析器

  这里主要用到Syntastic插件(https://github.com/scrooloose/syntastic

  因为YCM中集成了这个插件,所以基本不用配置了。

  这里要注意的事情是语法分析有必定延迟:即错的不能立刻显示出来,得过一下子;改对了也不能立刻消除错误提示,得等一下。

  效果大概入下图:

  

  在line18写了有错误的语句;':w'写入后,光标下移;卡卡就出现了错误提示:

  (1)所在行有特殊标记

  (2)所在错误位置也变色

  (3)状态栏下面还有错误提示

  又多了一项IDE的功能。

 

(十一)快速移动功能

  在Normal模式下:

  (1)hjkl自没必要说,是常规四个方向移动

  (2)w和e是按单词移动

  这里介绍一个easymotion插件(https://github.com/easymotion/vim-easymotion

  安装以后,几乎不用配置。(这个教程easymotion不错:http://www.wklken.me/posts/2015/06/07/vim-plugin-easymotion.html

  这个插件的功能用一句话说就是:“把知足条件的位置用 [A~Za~z] 间的标签字符标出来,找到你想去的位置再键入对应标签字符便可快速到达”

  (1)向后找 & 跳。

  <leader><leader>f :从当前光标位置向后找匹配的单个字符,并用字母标记。

  (2)全局找 & 跳。

  <leader><leader>s : 从全局找匹配的单个字符,并用字母标记

如下的全部内容主要参照:

  https://github.com/yangyangwithgnu/use_vim_as_ide 。

  原blog做者写的很是用心,建议你们都去看看。(我的以为经过这个blog大大提高了学习vim配置环境的效率,所以给做者捐了顿外卖的钱这个随笔就是学习上述blog内容的辅助记录,并不能代替学习原著;把学习过程当中遇到的一些坑列出来,初步作一下知识沉淀

 

  之前我我的的学习理念是“重剑无锋、大巧不工”,瞧不上这些快捷键之类的“奇技淫巧”。可是,打磨完了vim的开发环境后,我改变了这个见解:知识能够厚,可是顺手的工具必需要用心去一点点儿打磨锋利,提升工做效率的‘量变’会带来能力水平的‘质变’。

 

  另,本人纯vim初学者,所以学习过程当中确定存在吃不透甚至错误的地方。一方面后续会自我不断改进,另外一方面也很是欢迎你们拍砖指点。

 

【系统环境 & 内容提纲】

我用操做系统环境:

  mac OS X Yosemite 10.10.3

脚本环境用的zsh:

  

搭建完成后的编辑环境以下:

  

  能够看到:标签(函数变量)、工程目录、自动补全、状态栏提示、语法分析错误提示都集成在了里面。在实现了一个IDE的主要功能同时,VIM还可让你最大程度自由定制我的的开发环境,快捷键等等。

全文的提纲汇总以下:

(一)从新编译安装vim7.4

(二)编辑界面显示美化插件

(三)安装pathogen插件管理工具

(四)语法高亮、代码缩进、代码折叠、强命名下的接口与实现快速切换

(五)基于标签的代码导航

(六)内容查找、内容替换、批量注释

(七)代码模板

(八)代码自动补全

(九)工程文件浏览 & 多文档编辑

(十)静态语法分析器

(十一)快速移动功能

下面对上述十一块内容逐一说明,各个部分有先后逻辑限制,改变顺序不保证每一个功能都能实现正确

 

 

 【步入正题】

(一)从新编译安装vim7.4

个人mac上自带的vim版本是7.3,但有个极其重要的自动补全插件YouCompletetMe须要的vim版本是7.4,所以必须再装一套高版本vim。

vim7.4压缩包地址:ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2

基本上按照blog中步骤执行以下命令(假设上述文件解压到了~/Download/vim74中):

cd ~/Downloads/vim74
./configure --with-features=huge --enable-rubyinterp --enable-pythoninterp --with-python-config-dir=/usr/lib/python2.7/config/ --enable-perlinterp --enable-gui=gtk2 --enable-cscope --prefix=/usr --enable-luainterp 
make

报错:

简单分析一下error,应该是命名冲突了。google了http://zhouyichu.com/vim/Vim-in-Mac.html

解决方案是在vim74/src/os_unix.h中加入#include <AvailabilityMacros.h>。Done。

再执行

sudo make install

输入root密码后,Done。

再输入vim,版本就升级到7.4了

===================================================================

注意./configure的参数中

--prefix=/usr

是vim安装路径的,若是不想替换系统原有的,就能够把vim安装到自定义的路径下。再使用alias命令,在.bashrc中加一句

  alias vi="vim安装路径/vim"。

再注意,若是你跟我同样用了zsh这类的bash增强版,就须要在~/.zshrc中再加一句 source ~/.bashrc,以下图

这样你每次输入vi启动的才是vim7.4,不然zsh仍是会默认调用原来系统自定义的vim。

===================================================================

另外,要想成功编译安装vim,还须要python-devel、python3-devel、ruby-devel、libX11-devel、gtk-devel、gtk2-devel、gtk3-devel、ncurses-devel等支持。(我在服务器端安装的时候,就遇到了ncurses没有的状况,这个后续再去解决)

 

(二)编辑界面显示美化插件

因为pathogen没法管理界面美化插件,所以界面美化单独拎出来

1. 界面配色

(1)创建.vim/colors/文件夹,并将三个配色的vim文件copy到.vim/colors/文件夹下面,以下图所示

  

(2).vimrc增长配置,使界面美化生效

  在.vimrc中添加的命令以下

" 配色方案
set background=dark
"colorscheme solarized
colorscheme molokai
"colorscheme phd

  仅仅这样还不够,此时vim的编辑环境仍是黑白的,还要让vim开启语法高亮而且可以应用配色文件。须要继续在.vimrc中添加下面的配置;

syntax enable
syntax on

  这样,再次打开vim的编辑环境,语法高亮配色就体现出来了:

  

2. 添加辅助信息

  为了防止编程过程当中串行了,在设置一些辅助信息以下:

复制代码
" 老是显示状态栏
set laststatus=2
" 显示光标当前位置
set ruler
" 开启行号显示
set number
" 高亮显示当前行/列
set cursorline
set cursorcolumn
" 高亮显示搜索结果
set hlsearch
" 禁止折行
set nowrap
复制代码

  添加了这些信息后,能够看到vim编辑界面以下(光标、高亮、行号、状态栏都有了):

 

通过上述的配置,如今vim的编辑环境有了颜色,有了行号。但仅仅是一个带语法颜色提示的txt,其余强大的功能还要借助于各类插件。

要想管理好各种插件,就要使用下面一节介绍的pathogen插件管理工具。

 

(三)安装pathogen插件管理工具

用pathogen来管理vim插件的特点是:.vim/bundle/下每一个插件有独立的文件夹,插件之间几乎是彻底解耦。

这样带来的好处是:在添加、删除某个插件时,几乎不影响其余的插件。

1. 安装pathogen

pathogen虽然说是管理vim插件的,可是它自身就是一个插件。安装过程以下。

(1)在~/.vim路径下执行以下命令

  

(2)下载pathogen.vim到autoload

https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim

执行以下命令:

curl -o pathogen.vim https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim

以下:

  

(3)在.vimrc中增长配置信息

一是告诉vim,pathogen在哪;二是告诉vim执行pathogen的infect命令

" 将 pathogen 自身也置于独立目录中,需指定其路径 
runtime bundle/pathogen/autoload/pathogen.vim
" 运行 pathogen
execute pathogen#infect()

2. 用pathogen工具引入Powerline插件

以状态栏加强工具Powerline插件的安装过程为例,体验一下pathogen管理插件的便捷。

简单来讲用pathogen引入插件分两步:

(1)进入.vim/bundle/路径下:若是有git管理的,能够直接“git clone 插件所在的git的url”;若是没有git,就创建一个新的文件夹,再下载插件的zip包,解压到新建的这个文件夹下。

(2)在.vimrc中针对该插件的使用进行配置

下面看看Powerline如何搞:

Powerline插件的连接:https://github.com/Lokaltog/vim-powerline

(1)进入.vim/bundle/路径下,执行以下命令:

  

(2)原教程还推荐了个设置:

" 设置状态栏主题风格
let g:Powerline_colorscheme='solarized256'

  可是感受不用这个设置,Powerline也表现的不错,因此我也没有在.vimrc中配置。效果以下图所示:

  

红框框里面就是Powerline强化后的状态栏。并且在normal visual insert不一样的状态下能够显示不一样的功能,能够自行试试。

 

(四)语法高亮、代码缩进、代码折叠、强命名下的接口与实现快速切换

1. 语法高亮

因为目标是打造c/c++的vim环境,所以为了支持c++ STL的元素高亮。能够借助stl.vim插件补强vim的c++语法高亮功能。

(1)新建~/.vim/bunlde/STL-Syntax/after/syntax/cpp/文件夹

(2)下载stl.vim到~/.vim/bunlde/STL-Syntax/after/syntax/cpp/文件夹下

  

重启vim,咱们再打开一个cpp文件:

  

能够看到unordered_map这个关键字支持变色了(可是也看到了有些弊端,好比line22的begin和end都是通常的变量,也被识别为了关键字,这个后续但愿能找到改进的方法

同时,咱们看到上面的一些代码在缩进管理上是有问题的(line14 line22);这对c++还好,可是python这类对缩进严格要求的语言就不行。至此,引出了代码缩进的设置。

2. 代码缩进

  vim中有两种缩进表示法,一类是用1个制表符'\t',一类是用多个空格' '。

  缩进可视化插件对两类缩进显示的方式不一样:'\t'只能显示为粗块,而' '能够显示伟细条。

  在.vimrc中增长以下的配置:

复制代码
" 自适应不一样语言的智能缩进
filetype indent on
" 将制表符扩展为空格
set expandtab
" 设置编辑时制表符占用空格数
set tabstop=4
" 设置格式化时制表符占用空格数
set shiftwidth=4
" 让 vim 把连续数量的空格视为一个制表符
set softtabstop=4
复制代码

  设置完成,重启vim后,咱们再用vim打开的上面的cpp文件:

  

  能够看到,代码老老实实的按照规矩缩进回去了。

  阅读代码的时候,常常会遇到for while if 等等包含的代码行数比较多的状况。所以须要讲相同缩进的代码关联起来,便于阅读。

  首先须要引进vim-indent-guides这个插件,用pathogen引入,再也不赘述。(因为在个人mac上,一直以为这个插件的使用效果通常,就不上图了

3. 代码折叠

  须要在.vimrc中配置以下的内容:  

 " 代码折叠
 "set foldmethod=indent "基于缩进的代码折叠
 set foldmethod=syntax "基于语法的代码折叠
 " 启动 vim 时不折叠代码
 set nofoldenable

  仍是打开上述cpp文件,均在NORMAL模式下进行操做。

  命令:za

  效果:全都折叠上了

  

  命令:zr 

  效果:每次输入zr打开一层折叠

  

  命令:zR

  效果:把全部折叠都打开了(不上图了)

  命令:za

  效果:把一层代码折上

  

  命令:za

  效果:把当前折上的代码代开(这里连续使用za能够比较方便的折叠、打开一个代码段

  

折叠代码部分的结论就是:za命令最好用;想折哪段就za,再想打开这段代码再za(我的感受za按键位置比较舒服,因此一个za命令就足够了

4.强命名下的接口与实现快速切换

  习惯上c++中实现和接口是分开的:如my_class.cpp存放实现,my_class.h存放接口,而且两者在同一个目录下。以下:

  

  目的就是实现同名的.cpp和.h跳转。

  先安装插件:https://github.com/vim-scripts/a.vim

  再在.vimrc中以下配置:

 " *.cpp 和 *.h 间切换
 nmap <Leader>ch :A<CR>
 " 子窗口中显示 *.cpp 或 *.h
 nmap <Leader>sch :AS<CR>

  用vim打开my_class.cpp文件,不断输入“;ch”就能够在my_class.cpp和my_class.h中切换。

  注意,这里必需要求.cpp和.h文件在同一个文件夹中而且命名相同;所以这种强命名条件使得切换的做用很是有限。这个插件如今并不太推荐,至于有多大做用还得看工做中的场景吧。

 

(五)基于标签的代码导航

  标签(tag)的概念很是关键,原教程中评价标签是现代IDE的基石之一,深觉得然。

  什么是标签?

  代码中的类、结构、类成员、函数、对象、宏这些元素就是标签,每一个标签有它本身的名字、定义、类型、所在文件中的行的位置、所在文件的路径等属性。

  编译环节之一就是提取标签,但因为编译器并未把生成的标签输出到文本中,后来出现了专门用于生成标签的工具Exuberant Ctags(就是如今常说的ctags)。

  若是你的机器上没有ctags,那么请去这里下载http://ctags.sourceforge.net,并参照教程(http://blog.csdn.net/duguteng/article/details/7412652)去安装。

  ctags与vim有什么关系?

  其实ctags与vim彻底就是两个东西,两者本来各司其职,但基于标签的代码导航把两者联系在了一块儿。

  要想让vim支持基于标签的导航,大致上须要完成以下两件事情:

    (1)给代码文件生成tags(先得生成标签原料

    (2)在.vimrc中增长些配置(让vim知道上哪去找标签,以及一些辅助配置

  下面经过实际的例子来体会下ctags与vim的结合。

  ctags的应用实例(参照原教程)

  (1)创建test_tags文件夹,再创建test_tags/lib/子文件夹。

  (2)在test_tags目录下,创建main.cpp

  (3)在test_tags/lib目录下,创建my_class.h, my_class.cpp

  回到test_tags目录下,执行以下命令(先执行命令看结果,回过头来再分析):  

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++

  这时候,在test_tags目录下,多了一个tags文件:

  

  打开tags文件,执行:set list命令:

  

  每一行表明一条标签记录,且一行中各个字段间用\t分割:

  标签名 \t 标签所在的文件路径 \t 标签所在的行内容 \t 标签类型 \t 标签的语言 ...

  这里的标签类型比较抽象,都是m l f p 这些单个的字母,都表示什么意思呢?

  能够执行以下命令查看c++标签类型的含义:

ctags --list-kinds=c++

  结果以下:

  

  这时候,咱们就能够知道生成tags的命令中“--c++-kinds=”的含义了:就是告诉ctags要生成哪些元素的标签。

  如今体验一下标签导航的功能。

  vim打开main.cpp,而后:set tags+=你本身的路径/test_tags/tags

  光标移到one.printMsg()的printMsy()上,输入g],看效果:

  

  输入须要的标签的序号,好比5,就跳转到了lib/my_class.cpp文件中,而且光标锁定在line4的位置上。

  

  若是想返回到原来的位置,ctrl + o便可。

  上述的方法做为替代手工查找已经便捷了不少;可是正如同上面的例子,有时候常常得在同名的标签中选择。

  一种替代的方式是,输入某种快捷键,每次遍历一个标签。

  如今.vimrc中作以下配置:

 " 正向遍历同名标签
 nmap <Leader>tn :tnext<CR>
 " 反向遍历同名标签
 nmap <Leader>tp :tprevious<CR>

  再用vim打开main.cpp文件;光标停留在printMsg上面:

  ctrl + ] (将printMsg设定为要标签导航的目标)

  ;tn ;tp(向后/前找下一个同名的标签导航的位置)

  ctrl + o (回到上一个标签)

  ctrl + t (回到调用的位置)  

  经过这种方式,能够不间断地遍历同名标签,大致上仍是优于屏幕上选数字的方法。

  (若是是工程化的开发,还能够用indexer这个vim插件来周期性针对工程文件自动生成标签文件,并通知vim引入该标签文件;但目前缺乏应用场景,所以先不去讨论。

  tagbar插件。

  以上内容,能够作到单个标签的导航。若是要看正在编辑的代码的所有标签内容呢?最好把标签分门别类列出来,相似Eclipse里面那种导航bar的效果?

  这里能够引入tagbar的插件。

  (1)先安装:https://github.com/majutsushi/tagbar

  (2)再在.vimrc中作配置:

复制代码
" 设置 tagbar 子窗口的位置出如今主编辑区的左边 
let tagbar_left=1 
" 设置显示/隐藏标签列表子窗口的快捷键。速记:tag list 
nnoremap <Leader>tl :TagbarToggle<CR> 
" 设置标签子窗口的宽度 
let tagbar_width=32 
" tagbar 子窗口中不显示冗余帮助信息 
let g:tagbar_compact=1
复制代码

  再用vim打开上面的main.cpp文件,并执行;tl命令,效果以下图:

  

  能够看到,在默认状况下tagbar会列出来当前编辑文件的function和variable两类的标签。

  若是要显示更多类别的标签,就须要在.vimrc中加入以下配置:

复制代码
" 设置 ctags 对哪些代码元素生成标签
let g:tagbar_type_cpp = {
    \ 'kinds' : [
        \ 'd:macros:1',
        \ 'g:enums',
        \ 't:typedefs:0:0',
        \ 'e:enumerators:0:0',
        \ 'n:namespaces',
        \ 'c:classes',
        \ 's:structs',
        \ 'u:unions',
        \ 'f:functions',
        \ 'm:members:0:0',
        \ 'v:global:0:0',
        \ 'x:external:0:0',
        \ 'l:local:0:0'
     \ ],
     \ 'sro'        : '::',
     \ 'kind2scope' : {
         \ 'g' : 'enum',
         \ 'n' : 'namespace',
         \ 'c' : 'class',
         \ 's' : 'struct',
         \ 'u' : 'union'
     \ },
     \ 'scope2kind' : {
         \ 'enum'      : 'g',
         \ 'namespace' : 'n',
         \ 'class'     : 'c',
         \ 'struct'    : 's',
         \ 'union'     : 'u'
     \ }
\ }
复制代码

  看配置项中的名称也能分析出个大概,不具体深究了。再看修改tagbar配置后的效果图:

  

  看到tagbar中的选项丰富了不少(这里只须要记住,之后要是但愿tagbar中显示不一样的内容,在.vimrc中配置tagbar_type_XXX这个就行了

  标签→源码:选中tagbar中的标签,回车就定位到了源码

  源码→标签:光标在源码上停一下子,对应的tagbar中的标签就高亮了(这算是一个不容易发现的彩蛋

  对tagbar中的标签排序:默认是按照每一类的字母顺序排序;还能够按照标签出现的前后顺序排序;切换的按键是s

 

(六)内容查找、内容替换、批量注释

  1. 内容查找

  我遇到的内容查找分如下几种:

  (1)在正在编辑的文件中查找。以前本身在vim中查找都用"/"这个方法,效果就是把正在编辑的文件中含有该关键字的都高亮显示。

  (2)在文件夹中查找。若是分析工程代码的时候,可能须要查找的关键字在多个文件中,我都是用grep(还有ack可用,可是我没用过

  以上两种状况综合起来,内容查找不只要求可以标出来关键字,并且还须要关键字所在文件的信息(不一样文件含有关键字的语句相同)。

  能够用ctrlsf插件(https://github.com/dyng/ctrlsf.vim)来完成这个事情。

  (1)ctrlsf插件依赖于ack支持,所以还须要先装一下ack(参照http://www.jianshu.com/p/2f1c140c7eb8)。

  (2)在.vimrc中给ctrlsf插件启动设置快捷键  

" 使用 ctrlsf.vim 插件在工程内全局查找光标所在关键字,设置快捷键。快捷键速记法:search in project
nnoremap <Leader>sp :CtrlSF<CR>

  以后再打开main.cpp文件,光标停留在printMsg上,再输入";sp",效果如图:

  

  这个查找功能简直碉堡了,迅速显示出了关键字的上下文信息。

  在左边选择须要的关键字上下文,再按p就显示了所在的源码,以下图:

    

  若是想回到原来编辑的代码,按q就能够了。这样查找功能就很好的集成到了vim中,我的感受能够与标签导航结合使用来查看代码逻辑。

  2. 内容替换

  这个在开发工做中更常见了:好比须要对某个变量从新命名,尤为须要把全部与这个变量关联的内容(可能在不一样文件中)都替换掉。

  原教程中并无提供插件来作这个事情,而是做者本身写了一份vim的脚本函数;因为不是标准化插件,所以先不作配置了。

  可是同窗在面试中说竟遇到了面试官问vim中如何替换字符的问题,所以掌握一下基本的操做仍是必要的。

  能够参照这个blog(http://blog.sciencenet.cn/blog-724080-725117.html)学习。

  大致来讲,vim的内容替换命令的模板伟“:n,ms/string1/string2/g”

  (1)n,m:替换发生做用的行(若是是%s就表明全部行,有其余替换要求能够再具体查阅)

  (2)string1:目标字符串

  (3)string2:要把目标字符串替换成的字符串

  (4)g:若是有g就是表明每行全部的匹配字符串;若是没有g就是每行匹配的第一个字符串

  3. 批量注释

  因为是c/c++的开发,所以引入nerdcommenter这个插件(https://github.com/scrooloose/nerdcommenter)。

  还须要在.vimrc中加上一句话“filetype plugin on”。

  整行注释

  用vim打开main.cpp。对前四行添加注释。

  (1)“V”(注意是大写的V,这样进入的是选择全行的模式,若是是v则不是全行)

  (2)“jjj” 选中前四行

  (3)";cc" 添加注释

  效果如图:

  

  重复(1)(2),将(3)改成“;cu”则消除了注释

  部分注释

  好比要注释掉printMsg函数括号中的参数。

  光标停留在括号内,执行“vi)”,选中括号中的所有内容,如图:

  

  再执行";cc",效果如图:

  

  这种部分注释的也很经常使用,若是快捷键使用熟练能够省去很多时间。

  另外,对于大中小括号、双引号、单引号等各类号中变量的删除、复制、选择都比较经常使用。能够参考(http://www.linuxsong.org/2010/09/vim-quick-select-copy-delete/)学习。

 

(七)代码模板

  所谓的代码模板就是经常使用的 if() while() for()等等固定的套路。

  安装UltiSnips插件能够实现这个功能(https://github.com/SirVer/ultisnips)。

  有了UltiSnips还不够,还须要告诉UltiSnips按照什么样的模板来补全。

  这里就用了原文做者写的一个模板cpp.snippets(因为比较长,就不放上来了)。

  把这个模板放在.vim/bundle/ultisnips/mysnippets/cpp.snippets这里。

  而后还须要配置下.vimrc文件:

复制代码
" UltiSnips的tag键与YCM冲突 须要从新设定
let g:UltiSnipsExpandTrigger="<leader><tab>"
let g:UltiSnipsJumpForwardTrigger="<leader><tab>"
let g:UltiSnipsJumpBackwardTrigger="<leader><s-tab>"
" 告诉ultisnips模板文件在哪
let g:UltiSnipsSnippetDirectories=["mysnippets"]
复制代码

  须要注意的是,原文做者提供的cpp.snippets对同一个关键字的不一样模板给出了不一样的触发关键字。

  什么意思呢?我截取cpp.snippets中的一段以下:

复制代码
# 经过迭代器遍历容器(可读写) 
snippet for 
for (auto ${2:iter} = ${1:c}.begin(); ${3:$2} != $1.end(); ${4:++iter}) {
    ${5:TODO} 
} 
endsnippet 
# 经过迭代器遍历容器(只读) 
snippet cfor 
for (auto ${2:citer} = ${1:c}.cbegin(); ${3:$2} != $1.cend(); ${4:++citer}) { 
    ${5:TODO} 
} 
endsnippet 
# 经过下标遍历容器 
snippet For 
for (auto ${2:i} = 0; $2 != ${1}.size(); ${3:++}$2) { 
    ${4:TODO} 
} 
endsnippet 
复制代码

  for、cfor、For分别触发不一样模板,以下图:

   

  以此类推,能够多学习一下原做者的模板快捷键。

  也能够设定本身的快捷键

  以vector为例分析,利用代码模板怎么快速定义一个"vector<int>  v_int_test"。

  先看cpp.snippets中的vector段落以下:

  

  (1)在INSERT模式下输入vec,再输入“;Tab”,默认就出来"vector<char>  v;",而且光标停留在"<>"中内容被选中,如图:

    

  (2)此时,因为char都处于选中状态,所以直接输入int就变成以下的状态:

     

  (3)此时,再输入";Tab",则光标跳到了v的后面了,如图:

     

  (4)这时,再输入"_int_test",最后结果如图:

    

  整个过程很是连贯。经过这个例子,了解了自定义snippets的关键三点:

  a. 触发。科学定义模板补全的触发前缀(这个例子中触发vector的前缀是vec

  a. 定义骨架。经过${序号}来表示光标跳转的顺序(为了防止与后面自动补全的Tab冲突,在这里设置;Tab为模板补全的快捷键,上面已经说过了

  b. 手动填肉。经过";Tab"完成跳转。

  定义的snippets能够最大化发挥代码模板补全的做用,省去了很是多的时间,达到与IDE相同的效果这算是个人一个痛点,让我决定投身vim族

 

(八)代码自动补全

  代码补全的方式有两种:基于标签的补全 & 基于语义的补全

  1. 基于标签的补全

  OmniCppComplete + SuperTab 两个插件:

  https://github.com/vim-scripts/OmniCppComplete

  https://github.com/vim-scripts/SuperTab

  先在/usr/include和/usr/include/c++/4.8.4中执行ctags命令,生成c++和system的标签,并引入.vimrc。

  须要在.vimrc中配置以下:

  

  效果还能够:以下图:

  

  有时候须要看的信息比较多,能够修改“set completeopt=”这个参数,得到效果以下:

  

  这种方式显示的信息,都是执行ctags产生的标签信息。有的时候信息多了还杂,因此每每就不单独列出来一个buffer中显示信息了。

  上面这种方式主要是我我的补充的,原教程主要但愿使用YouCompleteMe这个终极补全神器,所以Omni的补全方式不细说了。

  2. 基于语义的补全

  这里只须要记住一个终极补全神器YouCompleteMe(YCM)插件(https://github.com/Valloric/YouCompleteMe)。

  YCM插件安装

  这个插件须要编译安装,过程比较复杂,主要四个安装步骤:

  (1)拉下来YCM的源码包以及相关依赖。

cd ~/.vim/bundle/ 
git clone https://github.com/Valloric/YouCompleteMe.git 
cd YouCompleteMe/ 
git submodule update --init --recursive

    效果如图:

    

  (2)下载libclang。(YCM后端调用libclang)

    这里下载的是做者推荐的http://llvm.org/releases/download.html中的预编译二进制文件(Pre-built Binaries)。

    解压后,将文件夹重命名为"clang+llvm"。存放的路径以下:

    

  (3)编译YCM共享库(用到了上一步下载的clang+llvm中的标准liblang

    执行以下命令:

cd /opt/local
sudo mkdir ycm_build 
cd ycm_build 
sudo cmake -G "Unix Makefiles" -DPATH_TO_LLVM_ROOT=/opt/local/clang+llvm . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp sudo make ycm_support_libs

    一个是看终端是否返回所有完成;另外一个去看是否生成了三个文件:

    

  (4)配置.ycm_extra_conf.py文件

    YCM支持每一个工程文件配置不一样的conf文件(这样的好处是不用每次新建工程就改.vimrc)。

    个人配置文件以下:

复制代码
 1 import os
 2 import ycm_core
 3 from clang_helpers import PrepareClangFlags
 4 
 5 # Set this to the absolute path to the folder (NOT the file!) containing the
 6 # compile_commands.json file to use that instead of 'flags'. See here for
 7 # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
 8 # Most projects will NOT need to set this to anything; you can just change the
 9 # 'flags' list of compilation flags. Notice that YCM itself uses that approach.
10 compilation_database_folder = ''
11 
12 # These are the compilation flags that will be used in case there's no
13 # compilation database set.
14 flags = [
15     '-Wall', 16     '-std=c++11', 17     '-stdlib=libc++', 18     '-x', 19     'c++', 20     '-I', 21     '.', 22     '-isystem', 23     '/usr/include/', 24     '-isystem', 25     '/usr/lib/', 26     '-I/opt/local/clang+llvm/include/c++/v1'
27 ]
28 
29 if compilation_database_folder:
30     database = ycm_core.CompilationDatabase(compilation_database_folder)
31 else:
32     database = None
33 
34 
35 def DirectoryOfThisScript():
36     return os.path.dirname(os.path.abspath(__file__))
37 
38 
39 def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
40     if not working_directory:
41         return flags
42     new_flags = []
43     make_next_absolute = False
44     path_flags = ['-isystem', '-I', '-iquote', '--sysroot=']
45     for flag in flags:
46         new_flag = flag
47 
48         if make_next_absolute:
49             make_next_absolute = False
50             if not flag.startswith('/'):
51                 new_flag = os.path.join(working_directory, flag)
52 
53         for path_flag in path_flags:
54             if flag == path_flag:
55                 make_next_absolute = True
56                 break
57 
58             if flag.startswith(path_flag):
59                 path = flag[len(path_flag):]
60                 new_flag = path_flag + os.path.join(working_directory, path)
61                 break
62 
63         if new_flag:
64             new_flags.append(new_flag)
65     return new_flags
66 
67 
68 def FlagsForFile(filename):
69     if database:
70         # Bear in mind that compilation_info.compiler_flags_ does NOT return a
71         # python list, but a "list-like" StringVec object
72         compilation_info = database.GetCompilationInfoForFile(filename)
73         final_flags = PrepareClangFlags(
74             MakeRelativePathsInFlagsAbsolute(
75                 compilation_info.compiler_flags_,
76                 compilation_info.compiler_working_dir_),
77             filename)
78     else:
79         relative_to = DirectoryOfThisScript()
80         final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
81 
82     return {
83         'flags': final_flags,
84         'do_cache': True}
复制代码

    其中line15~line26是重点的配置项,须要改为本身的路径。

    另请注意"-isystem","/usr/include"是一对配置:"-isystem"是配置项参数名称;"/usr/include"是配置项参数内容(这个不要跳坑了)

  插件使用配置

  YCM集成了各类补全引擎:语义补全引擎、标签补全引擎、OmniCppComplete补全引擎、其余补全引擎(如路径补全)

  主要集中在.vimrc中,先列出所有可能的配置:

复制代码
" YCM 补全菜单配色
" 补全功能在注释中一样有效
let g:ycm_complete_in_comments=1
" 容许 vim 加载 .ycm_extra_conf.py 文件,再也不提示
let g:ycm_confirm_extra_conf=0
" 开启 YCM 标签补全引擎
let g:ycm_collect_identifiers_from_tags_files=1
" 引入 C++ 标准库tags
set tags+=/usr/include/c++/4.8.4/stdcpp.tags
" YCM 集成 OmniCppComplete 补全引擎,设置其快捷键
inoremap <leader>; <C-x><C-o>
" 补全内容不以分割子窗口形式出现,只显示补全列表
set completeopt-=preview
" 从第一个键入字符就开始罗列匹配项
let g:ycm_min_num_of_chars_for_completion=1
" 禁止缓存匹配项,每次都从新生成匹配项
let g:ycm_cache_omnifunc=0
" 语法关键字补全         
let g:ycm_seed_identifiers_with_syntax=1
复制代码

  (1)语义补全:这个是默认的补全策略。就是键入字符,补全候选项就列出来了。而后再tab选择。很少说。

  (2)标签补全。标签补全已经说过了:先生成标签,再.vimrc中将标签路径加进来,再开启YCM标签引擎。可是标签引擎的开启,会极大的影响自动补全反应速度,有时候你输入的快一些补全根本反应不过来。要注意引入标签路径的数量。(我我的目前是不开标签引擎)

  (3)OmniCppComplete补全。这里个也满经常使用的,好比linux系统开发时最经常使用的fork()函数,YCM的默认语义补全引擎识别不出来。以下图:

    

    能够看到根本没有fork这个选项,即便有#include <unistd.h>。这是由于YCM默认的随键补全策略是无论系统头文件函数的。

    这时候能够告诉YCM:“我发现默认的随键补全很差使了,请给我换成OmniCppComplete补全引擎”。由于,Omni能够作到只要#include中包含了头文件,就能够补全出来。这里用一个比较巧的触发Omni补全的快捷方式,在.vimrc中设置“inoremap <leader>; <C-x><C-o>”,即";;"就能够触发Omni补全引擎。效果如图:

    

    能够看到,卡卡瞬间第一个就是fork。并且,这种补全引擎切换是暂时的,一旦这个fork补全过以后又恢复到最经常使用的默认语义补全了。

=======================================================

    若是非要钻牛角尖,硬要YCM随键补全出来fork,到底能不能行呢?是能够的

    在.vimrc中引入系统标签sys.tags,再开启标签引擎就能够作到,以下图:

    

    为何原文做者不推荐这种形式呢?我猜缘由可能有两点:

    1、这须要引入系统标签sys.tag,这个文件不小;引入sys.tags会下降vim的补全反应效率,极可能跟不上键入字符的速度了

    2、随键补全一次引入的补全候选项太多了。卡卡列出来一大堆,选的时候可能会影响效率。

=======================================================

    (4)路径补全。感受这个功能稍微弱一些,反应有些慢。不过仍是聊胜于无,看图:

       

 

(九)工程文件浏览 & 多文档编辑

  1. 工程文件浏览

  这里用 NERDtree (https://github.com/scrooloose/nerdtree)插件能够查看文件列表。

  (1)在bundle下安装插件。

  (2)在.vimrc中加配置:

复制代码
" 使用 NERDTree 插件查看工程文件。设置快捷键,速记:file list
nmap <Leader>fl :NERDTreeToggle<CR>
" 设置NERDTree子窗口宽度
let NERDTreeWinSize=32
" 设置NERDTree子窗口位置
let NERDTreeWinPos="right"
" 显示隐藏文件
let NERDTreeShowHidden=1
" NERDTree 子窗口中不显示冗余帮助信息
let NERDTreeMinimalUI=1
" 删除文件时自动删除文件对应 buffer
let NERDTreeAutoDeleteBuffer=1
复制代码

  效果如图所示:

  

  光标停留在tree那个window中:

  'r' : 刷新tree

  'I' : 切换是否显示隐藏文件

  2. 多文档编辑

  这里引入MiniBufExploer这个插件(https://github.com/fholgado/minibufexpl.vim)。我的以为这个插件的默认配置就OK了,结合NERDTree插件能够实现比较好的多文档编辑。

  这里注意几个快捷键:

  当光标停留在某个buffer上的时候

    ‘d’ : 关闭这个buffer

    ‘v’ : 垂直分屏

    ‘s’ : 水平分屏

  当处于Normal模式的时候:

    ‘:bn’ : 跳到下一个buffer

    ‘:bp’ : 跳到前一个buffer

    ‘:bdn’ : 关闭序号为n的buffer

  效果以下图:

  

  至此,既有左侧的标签列表(函数、变量、类等),又有右侧的工程文件列表,终于有点儿IDE的样子了,再加上自动补全,终于有些IDE的样子了。

 

(十)静态语法分析器

  这里主要用到Syntastic插件(https://github.com/scrooloose/syntastic

  因为YCM中集成了这个插件,所以基本不用配置了。

  这里要注意的事情是语法分析有必定延迟:即错的不能立刻显示出来,得过一下子;改对了也不能立刻消除错误提示,得等一下。

  效果大概入下图:

  

  在line18写了有错误的语句;':w'写入后,光标下移;卡卡就出现了错误提示:

  (1)所在行有特殊标记

  (2)所在错误位置也变色

  (3)状态栏下面还有错误提示

  又多了一项IDE的功能。

 

(十一)快速移动功能

  在Normal模式下:

  (1)hjkl自没必要说,是常规四个方向移动

  (2)w和e是按单词移动

  这里介绍一个easymotion插件(https://github.com/easymotion/vim-easymotion

  安装以后,几乎不用配置。(这个教程easymotion不错:http://www.wklken.me/posts/2015/06/07/vim-plugin-easymotion.html

  这个插件的功能用一句话说就是:“把知足条件的位置用 [A~Za~z] 间的标签字符标出来,找到你想去的位置再键入对应标签字符便可快速到达”

  (1)向后找 & 跳。

  <leader><leader>f :从当前光标位置向后找匹配的单个字符,并用字母标记。

  (2)全局找 & 跳。

  <leader><leader>s : 从全局找匹配的单个字符,并用字母标记