VIM环境配置

以下的所有内容主要参照:

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

  原blog作者写的非常用心,建议大家都去看看。(个人觉得通过这个blog大大提升了学习vim配置环境的效率,因此给作者捐了顿外卖的钱这个随笔就是学习上述blog内容的辅助记录,并不能代替学习原著;把学习过程中遇到的一些坑列出来,初步做一下知识沉淀

 

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

 

  另,本人纯vim初学者,因此学习过程中肯定存在吃不透甚至错误的地方。一方面后续会自我不断改进,另一方面也非常欢迎大家拍砖指点。

 

【系统环境 & 内容提纲】

我用操作系统环境:

  mac OS X Yosemite 10.10.3

脚本环境用的zsh:

  

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

  VIM环境配置_第1张图片

  可以看到:标签(函数变量)、工程目录、自动补全、状态栏提示、语法分析错误提示都集成在了里面。在实现了一个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 。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/文件夹下面,如下图所示

  VIM环境配置_第2张图片

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

  在.vimrc中添加的命令如下

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

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

syntax enable
syntax on

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

  VIM环境配置_第3张图片

2. 添加辅助信息

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

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

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

 VIM环境配置_第4张图片

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

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

 

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

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

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

1. 安装pathogen

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

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

  VIM环境配置_第5张图片

(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/路径下,执行如下命令:

  VIM环境配置_第6张图片

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

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

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

  VIM环境配置_第7张图片

红框框里面就是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环境配置_第8张图片

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

  VIM环境配置_第9张图片

可以看到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文件:

  VIM环境配置_第10张图片

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

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

  首先需要引进vim-indent-guides这个插件,用pathogen引入,不再赘述。(由于在我的mac上,一直觉得这个插件的使用效果一般,就不上图了

3. 代码折叠

  需要在.vimrc中配置如下的内容:  

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

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

  命令:za

  效果:全都折叠上了

  VIM环境配置_第11张图片

  命令:zr 

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

  VIM环境配置_第12张图片

  命令:zR

  效果:把所有折叠都打开了(不上图了)

  命令:za

  效果:把一层代码折上

  VIM环境配置_第13张图片

  命令:za

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

  VIM环境配置_第14张图片

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

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

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

  VIM环境配置_第15张图片

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

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

  再在.vimrc中如下配置:

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

  用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文件:

  VIM环境配置_第16张图片

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

  VIM环境配置_第17张图片

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

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

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

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

ctags --list-kinds=c++

  结果如下:

  VIM环境配置_第18张图片

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

  现在体验一下标签导航的功能。

  vim打开main.cpp,然后:set tags+=你自己的路径/test_tags/tags

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

  VIM环境配置_第19张图片

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

  VIM环境配置_第20张图片

  如果想返回到原来的位置,ctrl + o即可。

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

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

  现在.vimrc中做如下配置:

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

  再用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 tl :TagbarToggle 
" 设置标签子窗口的宽度 
let tagbar_width=32 
" tagbar 子窗口中不显示冗余帮助信息 
let g:tagbar_compact=1
复制代码

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

  VIM环境配置_第21张图片

  可以看到,在默认情况下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配置后的效果图:

  VIM环境配置_第22张图片

  看到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 sp :CtrlSF

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

  VIM环境配置_第23张图片

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

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

  VIM环境配置_第24张图片  

  如果想回到原来编辑的代码,按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" 添加注释

  效果如图:

  VIM环境配置_第25张图片

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

  部分注释

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

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

  VIM环境配置_第26张图片

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

  VIM环境配置_第27张图片

  这种部分注释的也很常用,如果快捷键使用熟练可以省去不少时间。

  另外,对于大中小括号、双引号、单引号等各种号中变量的删除、复制、选择都比较常用。可以参考(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=""
let g:UltiSnipsJumpForwardTrigger=""
let g:UltiSnipsJumpBackwardTrigger=""
" 告诉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分别触发不同模板,如下图:

  VIM环境配置_第28张图片 

  以此类推,可以多学习一下原作者的模板快捷键。

  也可以设定自己的快捷键

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

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

  VIM环境配置_第29张图片

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

    VIM环境配置_第30张图片

  (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中配置如下:

  VIM环境配置_第31张图片

  效果还可以:如下图:

  VIM环境配置_第32张图片

  有时候需要看的信息比较多,可以修改“set completeopt=”这个参数,获得效果如下:

  VIM环境配置_第33张图片

  这种方式显示的信息,都是执行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

    效果如图:

    VIM环境配置_第34张图片

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

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

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

    VIM环境配置_第35张图片

  (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

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

    VIM环境配置_第36张图片

  (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 ; 
" 补全内容不以分割子窗口形式出现,只显示补全列表
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的默认语义补全引擎识别不出来。如下图:

    VIM环境配置_第37张图片

    可以看到根本没有fork这个选项,即使有#include 。这是因为YCM默认的随键补全策略是不管系统头文件函数的。

    这时候可以告诉YCM:“我发现默认的随键补全不好使了,请给我换成OmniCppComplete补全引擎”。因为,Omni可以做到只要#include中包含了头文件,就可以补全出来。这里用一个比较巧的触发Omni补全的快捷方式,在.vimrc中设置“inoremap ; ”,即";;"就可以触发Omni补全引擎。效果如图:

    VIM环境配置_第38张图片

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

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

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

    在.vimrc中引入系统标签sys.tags,再开启标签引擎就可以做到,如下图:

    VIM环境配置_第39张图片

    为什么原文作者不推荐这种形式呢?我猜原因可能有两点:

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

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

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

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

       VIM环境配置_第40张图片

 

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

  1. 工程文件浏览

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

  (1)在bundle下安装插件。

  (2)在.vimrc中加配置:

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

  效果如图所示:

  VIM环境配置_第41张图片

  光标停留在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

  效果如下图:

  VIM环境配置_第42张图片

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

 

(十)静态语法分析器

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

  由于YCM中集成了这个插件,因此基本不用配置了。

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

  效果大概入下图:

  VIM环境配置_第43张图片

  在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)向后找 & 跳。

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

  (2)全局找 & 跳。

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

以下的所有内容主要参照:

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

  原blog作者写的非常用心,建议大家都去看看。(个人觉得通过这个blog大大提升了学习vim配置环境的效率,因此给作者捐了顿外卖的钱这个随笔就是学习上述blog内容的辅助记录,并不能代替学习原著;把学习过程中遇到的一些坑列出来,初步做一下知识沉淀

 

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

 

  另,本人纯vim初学者,因此学习过程中肯定存在吃不透甚至错误的地方。一方面后续会自我不断改进,另一方面也非常欢迎大家拍砖指点。

 

【系统环境 & 内容提纲】

我用操作系统环境:

  mac OS X Yosemite 10.10.3

脚本环境用的zsh:

  

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

  VIM环境配置_第44张图片

  可以看到:标签(函数变量)、工程目录、自动补全、状态栏提示、语法分析错误提示都集成在了里面。在实现了一个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 。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/文件夹下面,如下图所示

  VIM环境配置_第45张图片

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

  在.vimrc中添加的命令如下

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

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

syntax enable
syntax on

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

  VIM环境配置_第46张图片

2. 添加辅助信息

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

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

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

 VIM环境配置_第47张图片

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

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

 

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

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

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

1. 安装pathogen

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

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

  VIM环境配置_第48张图片

(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/路径下,执行如下命令:

  VIM环境配置_第49张图片

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

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

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

  VIM环境配置_第50张图片

红框框里面就是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环境配置_第51张图片

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

  VIM环境配置_第52张图片

可以看到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文件:

  VIM环境配置_第53张图片

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

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

  首先需要引进vim-indent-guides这个插件,用pathogen引入,不再赘述。(由于在我的mac上,一直觉得这个插件的使用效果一般,就不上图了

3. 代码折叠

  需要在.vimrc中配置如下的内容:  

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

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

  命令:za

  效果:全都折叠上了

  VIM环境配置_第54张图片

  命令:zr 

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

  VIM环境配置_第55张图片

  命令:zR

  效果:把所有折叠都打开了(不上图了)

  命令:za

  效果:把一层代码折上

  VIM环境配置_第56张图片

  命令:za

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

  VIM环境配置_第57张图片

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

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

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

  VIM环境配置_第58张图片

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

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

  再在.vimrc中如下配置:

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

  用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文件:

  VIM环境配置_第59张图片

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

  VIM环境配置_第60张图片

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

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

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

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

ctags --list-kinds=c++

  结果如下:

  VIM环境配置_第61张图片

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

  现在体验一下标签导航的功能。

  vim打开main.cpp,然后:set tags+=你自己的路径/test_tags/tags

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

  VIM环境配置_第62张图片

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

  VIM环境配置_第63张图片

  如果想返回到原来的位置,ctrl + o即可。

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

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

  现在.vimrc中做如下配置:

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

  再用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 tl :TagbarToggle 
" 设置标签子窗口的宽度 
let tagbar_width=32 
" tagbar 子窗口中不显示冗余帮助信息 
let g:tagbar_compact=1
复制代码

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

  VIM环境配置_第64张图片

  可以看到,在默认情况下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配置后的效果图:

  VIM环境配置_第65张图片

  看到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 sp :CtrlSF

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

  VIM环境配置_第66张图片

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

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

  VIM环境配置_第67张图片  

  如果想回到原来编辑的代码,按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" 添加注释

  效果如图:

  VIM环境配置_第68张图片

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

  部分注释

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

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

  VIM环境配置_第69张图片

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

  VIM环境配置_第70张图片

  这种部分注释的也很常用,如果快捷键使用熟练可以省去不少时间。

  另外,对于大中小括号、双引号、单引号等各种号中变量的删除、复制、选择都比较常用。可以参考(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=""
let g:UltiSnipsJumpForwardTrigger=""
let g:UltiSnipsJumpBackwardTrigger=""
" 告诉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分别触发不同模板,如下图:

  VIM环境配置_第71张图片 

  以此类推,可以多学习一下原作者的模板快捷键。

  也可以设定自己的快捷键

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

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

  VIM环境配置_第72张图片

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

    VIM环境配置_第73张图片

  (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中配置如下:

  VIM环境配置_第74张图片

  效果还可以:如下图:

  VIM环境配置_第75张图片

  有时候需要看的信息比较多,可以修改“set completeopt=”这个参数,获得效果如下:

  VIM环境配置_第76张图片

  这种方式显示的信息,都是执行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

    效果如图:

    VIM环境配置_第77张图片

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

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

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

    VIM环境配置_第78张图片

  (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

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

    VIM环境配置_第79张图片

  (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 ; 
" 补全内容不以分割子窗口形式出现,只显示补全列表
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的默认语义补全引擎识别不出来。如下图:

    VIM环境配置_第80张图片

    可以看到根本没有fork这个选项,即使有#include 。这是因为YCM默认的随键补全策略是不管系统头文件函数的。

    这时候可以告诉YCM:“我发现默认的随键补全不好使了,请给我换成OmniCppComplete补全引擎”。因为,Omni可以做到只要#include中包含了头文件,就可以补全出来。这里用一个比较巧的触发Omni补全的快捷方式,在.vimrc中设置“inoremap ; ”,即";;"就可以触发Omni补全引擎。效果如图:

    VIM环境配置_第81张图片

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

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

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

    在.vimrc中引入系统标签sys.tags,再开启标签引擎就可以做到,如下图:

    VIM环境配置_第82张图片

    为什么原文作者不推荐这种形式呢?我猜原因可能有两点:

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

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

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

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

       VIM环境配置_第83张图片

 

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

  1. 工程文件浏览

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

  (1)在bundle下安装插件。

  (2)在.vimrc中加配置:

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

  效果如图所示:

  VIM环境配置_第84张图片

  光标停留在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

  效果如下图:

  VIM环境配置_第85张图片

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

 

(十)静态语法分析器

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

  由于YCM中集成了这个插件,因此基本不用配置了。

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

  效果大概入下图:

  VIM环境配置_第86张图片

  在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)向后找 & 跳。

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

  (2)全局找 & 跳。

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

你可能感兴趣的:(VIM环境配置)