• 【vim环境配置】详细实录


    【写在前面】

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

      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个制表符' ',一类是用多个空格' '。

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

      在.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的结合。

    ####################

    2015.10.14更新内容:

    在server上编辑c程序的时候,处理了两个与标签有关的问题。

    先说两个问题,再统一记录解决方案。

    (1)一个遗留问题:为什么在.vimrc中引入系统标签(/usr/include),vim就变得超级慢呢?

      原因是,当时为了图省事,直接在/usr/include路径下用“ctags -R *”来生成的标签。注意,-R是递归生成标签。我们先来看看/usr/include路径下有多少文件:

      

      这么多系统文件(甚至有些不是系统文件)并不需要都生成标签,我们看都生成标签,产生的标签有多少:

      

      总共19W个标签。对于这种系统库函数的标签生成策略,最好不要是这种暴力的方式,很多根本用到的内容的标签都生成了,结果就是降低了检索速率。

    (2)发现一个新问题:为什么即使我用了ctag -R *这种暴力生成标签的策略,还是有些函数包含不进来呢

      

      比如,我已经include进来了signal.h,并且用了ycm的标签补全策略,但是还是不能补全出来最基本的signal函数。

    两个问题的解决方法都可以在下面的blog中找到:http://blog.csdn.net/zklth/article/details/7027798

      (1)生成标签的时候,不能直接ctag -R,而是需要哪个文件夹下面内容的标签,就把哪个文件夹包含进来。

      (2)ctags默认的生成标签策略,是不会考虑__THROW、__attribute_pure__、__nonnull __attribute__这些内容的,因此在执行ctags命令的时候,必须把这些内容用-I给包含进来。

    具体的解决方法已经ctags命令主要参考blog中的资料如下:

      

      重新用ctags生成标签,新标签文件放在.vim目录下。最后signal可以看到补全出来了:

      

      今天的这个补全问题,并没有拖太长的时间。想起几天前学习ctags的时候,本想糊弄糊弄过去算了,但还是本着认真些的态度研究了一下。

      解决今天这个问题的时候,就得到了回报了。有些知识并不是特别复杂,但是多想一些就会给后面解决问题带来很大的便利。

    插播结束。

    ####################

      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命令:

      

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

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

      这里的标签类型比较抽象,都是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族

      #########################

      2015.10.14补充

        由于在.vimrc中配置了“let g:UltiSnipsSnippetDirectories=["mysnippets"]”

        因此,ultisnippets插件会在.vim/bundle/ultisnips/mysnippets这个路径下搜寻各类文件的代码模板信息。

        如图:

        

        如果编辑*.cpp的文件,ultisnippets就加载cpp.snippets中的模板;如果编辑*.c文件,ultisnippets就加载c.snippets文件中的模板。

      #########################

    (八)代码自动补全

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

      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,再开启标签引擎就可以做到,如下图:

        

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

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

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

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

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

      (3)行跳转

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

        

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

        

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

    【写在后面】

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

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

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

      

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

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

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

      

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

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

      

  • 相关阅读:
    Element.scrollIntoView()
    dot.js
    微信小程序页面跳转导航wx.navigateTo和wx.redirectTo
    小程序swiper 滑块视图容器
    小程序表单提交
    自适应宽度圆角导航如何实现
    PC端和移动端一些奇葩兼容性问题
    如何实现两行内容增多和一行内容增多,多余的内容显示省略号
    表单提交判断,数据只能提交一次判断
    改变CSS世界纵横规则的writing-mode属性
  • 原文地址:https://www.cnblogs.com/xbf9xbf/p/4860484.html
Copyright © 2020-2023  润新知