转载

【vim环境配置】详细实录

【写在前面】

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

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

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

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

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

【系统环境 & 内容提纲】

我用操作系统环境:

mac OS X Yosemite 10.10.3

脚本环境用的zsh:

【vim环境配置】详细实录

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

【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

报错:

【vim环境配置】详细实录

简单分析一下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,如下图

【vim环境配置】详细实录

这样你每次输入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).vimrc增加配置,使界面美化生效

在.vimrc中添加的命令如下

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

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

syntax enable syntax on

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

【vim环境配置】详细实录

2. 添加辅助信息

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

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

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

【vim环境配置】详细实录

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

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

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

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

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

1. 安装pathogen

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

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

【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

如下:

【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环境配置】详细实录

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

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

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

【vim环境配置】详细实录

红框框里面就是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环境配置】详细实录

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

【vim环境配置】详细实录

可以看到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环境配置】详细实录

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

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

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

3. 代码折叠

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

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

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

命令:za

效果:全都折叠上了

【vim环境配置】详细实录

命令:zr

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

【vim环境配置】详细实录

命令:zR

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

命令:za

效果:把一层代码折上

【vim环境配置】详细实录

命令:za

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

【vim环境配置】详细实录

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

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

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

【vim环境配置】详细实录

目的就是实现 同名 的.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文件:

【vim环境配置】详细实录

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

【vim环境配置】详细实录

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

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

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

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

ctags --list-kinds=c++

结果如下:

【vim环境配置】详细实录

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

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

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

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

如果想返回到原来的位置,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命令,效果如下图:

【vim环境配置】详细实录

可以看到,在默认情况下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环境配置】详细实录

看到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",效果如图:

【vim环境配置】详细实录

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

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

【vim环境配置】详细实录

如果想回到原来编辑的代码,按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环境配置】详细实录

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

部分注释。

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

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

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

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

【vim环境配置】详细实录

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

也可以设定自己的快捷键。

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

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

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

【vim环境配置】详细实录

整个过程非常连贯。通过这个例子,了解了自定义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环境配置】详细实录

效果还可以:如下图:

【vim环境配置】详细实录

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

【vim环境配置】详细实录

这种方式显示的信息,都是执行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环境配置】详细实录

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

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

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

【vim环境配置】详细实录

(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环境配置】详细实录

(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的默认语义补全引擎识别不出来。如下图:

【vim环境配置】详细实录

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

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

【vim环境配置】详细实录

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

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

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

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

【vim环境配置】详细实录

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

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

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

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

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

【vim环境配置】详细实录

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

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

效果如图所示:

【vim环境配置】详细实录

光标停留在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环境配置】详细实录

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

(十)静态语法分析器

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

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

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

效果大概入下图:

【vim环境配置】详细实录

在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 : 从全局找匹配的单个字符,并用字母标记

(3)行跳转

<leader><leader>j/k : 向下/上标出所有行,并用字母标记。输入";;j",效果如图:

【vim环境配置】详细实录

输入字母h后,结果如图:

【vim环境配置】详细实录

easymotion提供的这种标记跳转方式,可以直接把需要跳到的位置与字母关联上,不用再去输入数字或先搜索在不断的移动去定位。

【写在后面】

这篇随笔完成时,相同的.vim环境大概搭建了两遍:

第一遍是纯初学摸索,很多东西不太清楚;跳坑的时候甚至想放弃算了,反正还有sublime,再不行还有VS大法

第二遍是为了写随笔,索性把第一遍搭建好的vim环境给除去了,step by step再把环境再搭建出来;觉得vim不仅能实现ide的相同功能,更重要的是很多东西可以自己定制,真正做到个性化的编辑环境,大大提升工作效率

完成了这篇随笔的过程,是知识沉淀的过程,是逻辑思路重新整理的过程:

第一遍只是填鸭式照着去做,能做出来就行了,觉得方便就好

第二遍开始思考为什么要有这样的功能,同样的编辑功能用不同的方法去达到各有什么利弊,以及体会各种插件设计者的巧妙思路

一开始真心是不愿意去碰vim,但不愿意碰也得碰,碰不碰最后还是碰了(前一篇日志有说明原因)。我不会去想什么vim哲学之类的事情,只是觉得既然决定要碰vim,就要一点点积累把工具打磨好。

最后希望这篇随笔能对大家也有一点点帮助。

正文到此结束
Loading...