欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

在 ubuntu 20.04 下构建基于 vim 的 C/C++ 开发环境

最编程 2024-04-07 15:23:05
...

新装了ubuntu 20.04,因为要修改内核相关代码,所以要找一套可以*跳转的内核代码查看工具。除了vs code之外,vim也是个不错的工具。前面看了很多教程,ubuntu 20.04已经自带vim 8.x版本了。基于很多网上的教程使用vundle去配置vim比较复杂,把别人的vimrc复制过来用,要么是代码无法自动补全,要么是vim的窗口划分有问题。后面又找了一大堆,最后发现有两篇介绍的不错,使用vimplug来管理插件。

网上老旧的文章使用手工生成 tags,请使用自动代码索引生成工具,比如 vim-gutentags https://github.com/ludovicchabant/vim-gutentags,现在网上好像就没有一篇能正确讨论 Vim C/C++ 环境搭建的,都在谈些十年前的东西,所以我写了篇关于 Vim 8 和 C/C++ 相关插件的介绍:

假设你已经有一定 Vim 使用经验,并且折腾过 Vim 配置,能够相对舒适的在 Vim 中编写其他代码的时候,准备在 Vim 开始 C/C++ 项目开发,或者你已经用 Vim 编写了几年 C/C++ 代码,想要更进一步,让自己的工作更加顺畅的话,本文就是为你准备的:

插件管理

为什么把插件管理放在第一个来讲呢?这是比较基本的一个东西,如今 Vim 下熟练开发的人,基本上手都有 20-50 个插件,遥想十年前,Vim里常用的插件一只手都数得过来。过去我一直使用老牌的 Vundle https://github.com/VundleVim/Vundle.vim来管理插件,但是随着插件越来越多,更新越来越频繁,Vundle 这种每次更新就要好几分钟的东西实在是不堪重负了。在我逐步对 Vundle 失去耐心之后,我试用了 vim-plug https://github.com/junegunn/vim-plug ,用了两天以后就再也回不去 Vundle了,它支持全异步的插件安装,安装50个插件只需要一分钟不到的时间,这在 Vundle 下面根本不可想像的事情,插件更新也很快,不像原来每次更新都可以去喝杯茶去,最重要的是它支持插件延迟加载:

vim 插件安装

  1. 下载 vimplus
    git clone https://github.com/chxuan/vimplus.git

  2. 将vimplus 移动自~/.vimplus

     

    11.png

  3. 去掉 YCM 插件
    vim ~/.vimplus/.vimrc 将 YCM 插件注释掉

22.png

  1. 执行安装(装到/root/.vim/或者/home/user/.vim之下,并生成对应的.vimrc文件),中间会问使用python2还是3编译YCM,我们选择3
    sudo ./install.sh 或者 ./install

  2. 更改权限,和相应用户对应
    sudo chown -R qtumser:qtumser ~/.vim
    sudo chown -R qtumser:qtumser ~/.LfCache/

  3. 以上将打开文件将会是以下;

     

    44.png

YCM 安装
说明: YCM 安装需要vim 7.4 以上版本,并且支持python3 . 具体可用
vim --version 查看。不过 Ubuntu18.04 默认安装vim 为8.0 版本已经支持。

  1. 下载YCM
    git clone https://github.com/Valloric/YouCompleteMe

  2. 将YCM 移动至~/.vim/pluged

     

     

  3. 执行命令
    git submodule update --init --recursive

  4. 打开 ~/.vimplus/.vimrc 对YCM 支持

     

     

  5. 在 ~/.vimplus/ 下 执行
    sudo ./install.sh 或者 ./install.sh 和前面一致,如果使用sudo,则应用root账户使用vim。中间会问使用python2还是3编译YCM,我们选择3

安装 YCM 插件, 完成后的效果图

 

到此,基础的vim配置完成了,也支持代码补全。下面我们配置更高级的功能。

安装其他插件:

重要的是它支持插件延迟加载:

" 定义插件,默认用法,和 Vundle 的语法差不多
Plug 'junegunn/vim-easy-align'
Plug 'skywind3000/quickmenu.vim'

" 延迟按需加载,使用到命令的时候再加载或者打开对应文件类型才加载
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }

" 确定插件仓库中的分支或者 tag
Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }

定义好插件以后一个 :PlugInstall 命令就并行安装所有插件了,比 Vundle 快捷不少,关键是 vim-plug 只有单个文件,正好可以放在我 github 上的 vim 配置仓库中,每次需要更新 vim-plug 时只需要 :PlugUpgrade,即可自我更新。使用时建议给插件分组,同类别插件放一个组里,vimrc 里面只需要确定下启用哪些组就行了。

抛弃 Vundle 切换到 vim-plug 以后,不仅插件安装和更新快了一个数量级,大量的插件我都配置成了延迟加载,Vim 启动速度比 Vundle 时候提高了不少。使用 Vundle 的时候一旦插件数量超过30个,管理是一件很痛苦的事情,而用了 vim-plug 以后,50-60个插件都轻轻松松。

 

符号索引

现在有好多 ctags 的代替品,比如 gtags, etags 和 cquery。然而我并不排斥 ctags,因为他支持 50+ 种语言,没有任何一个符号索引工具有它支持的语言多。同时 Vim 和 ctags 集成的相当好,用它依赖最少,大量基础工作可以直接通过 ctags 进行,然而到现在为止,我就没见过几个人把 ctags 用对了的。

就连配置文件他们都没写对,正确的 ctags 配置应该是:

set tags=./.tags;,.tags

这里解释一下,首先我把 tag 文件的名字从“tags” 换成了 “.tags”,前面多加了一个点,这样即便放到项目中也不容易污染当前项目的文件,删除时也好删除,gitignore 也好写,默认忽略点开头的文件名即可。

前半部分 “./.tags; ”代表在文件的所在目录下(不是 “:pwd”返回的 Vim 当前目录)查找名字为 “.tags”的符号文件,后面一个分号代表查找不到的话向上递归到父目录,直到找到 .tags 文件或者递归到了根目录还没找到,这样对于复杂工程很友好,源代码都是分布在不同子目录中,而只需要在项目顶层目录放一个 .tags文件即可;逗号分隔的后半部分 .tags 是指同时在 Vim 的当前目录(“:pwd”命令返回的目录,可以用 :cd ..命令改变)下面查找 .tags 文件。

最后请更新你的 ctags,不要再使用老旧的 Exuberant Ctags,这货停止更新快十年了,请使用最新的 Universal CTags 代替之,它在 Exuberant Ctags 的基础上继续更新迭代了近十年,如今任然活跃的维护着,功能更强大,语言支持更多。

(注意最新版 universal ctags 调用时需要加一个 --output-format=e-ctags,输出格式才和老的 exuberant ctags 兼容否则会有 windows 下路径名等小问题)。

universal-ctags安装过程:https://www.jianshu.com/p/34c5f4dd5528

安装 universal-ctags

$ sudo apt install autoconf
$ cd /tmp
$ git clone https://github.com/universal-ctags/ctags
$ cd ctags
$ sudo apt install \
    gcc make \
    pkg-config autoconf automake \
    python3-docutils \
    libseccomp-dev \
    libjansson-dev \
    libyaml-dev \
    libxml2-dev
$ ./autogen.sh
$ ./configure --prefix=/opt/software/universal-ctags  # 安装路径可以况调整。
$ make -j8
$ sudo make install

创建 ctags 链接

$ sudo ln -s /opt/software/universal-ctags.ac/bin/ctags /usr/bin/ctags  #注意ctags的安装文件夹名称

在vim中配置ctags

"set tags=tags 我们不用这个
set tags=./.tags;,.tags
set autochdir

使用ctags

cd ~/work/opencv   # 随便进入到一个你的c++工程目录
ctags -R .  #递归地生成.tags文件

然后在vim中用Ctrl+](跳转到函数定义)和Ctrl+t(跳出定义)。注意,可以多层级嵌套使用,所以这两个操作相当于入栈和弹栈。

 

自动索引

过去写几行代码又需要运行一下 ctags 来生成索引,每次生成耗费不少时间。如今 Vim 8 下面自动异步生成 tags 的工具有很多,这里推荐最好的一个:vim-gutentags,这个插件主要做两件事情:

- 确定文件所属的工程目录,即文件当前路径向上递归查找是否有 `.git`, `.svn`, `.project` 等标志性文件(可以自定义)来确定当前文档所属的工程目录。

- 检测同一个工程下面的文件改动,能会自动增量更新对应工程的 `.tags` 文件。每次改了几行不用全部重新生成,并且这个增量更新能够保证 `.tags` 文件的符号排序,方便 Vim 中用二分查找快速搜索符号。

安装方式:在.vimrc 中增加配置项 Plug 'ludovicchabant/vim-gutentags' 后再在vim命令行模式下执行命令 :PlugInstall 即可完成vim-gutentags插件的安装。

vim-gutentags 需要简单配置一下:

" gutentags 搜索工程目录的标志,碰到这些文件/目录名就停止向上一级目录递归
let g:gutentags_project_root = ['.root', '.svn', '.git', '.hg', '.project']

" 所生成的数据文件的名称
let g:gutentags_ctags_tagfile = '.tags'

" 将自动生成的 tags 文件全部放入 ~/.cache/tags 目录中,避免污染工程目录
let s:vim_tags = expand('~/.cache/tags')
let g:gutentags_cache_dir = s:vim_tags

" 配置 ctags 的参数
let g:gutentags_ctags_extra_args = ['--fields=+niazS', '--extra=+q']
let g:gutentags_ctags_extra_args += ['--c++-kinds=+px']
let g:gutentags_ctags_extra_args += ['--c-kinds=+px']

" 检测 ~/.cache/tags 不存在就新建
if !isdirectory(s:vim_tags)
   silent! call mkdir(s:vim_tags, 'p')
endif

有了上面的设置,你平时基本感觉不到 tags 文件的生成过程了,只要文件修改过,gutentags 都在后台为你默默打点是否需要更新数据文件,你根本不用管,还会帮你:setlocal tags+=... 添加到局部 tags 搜索列表中。

为当前文件添加上对应的 tags 文件的路劲而不影响其他文件。得益于 Vim 8 的异步机制,你可以任意随时使用 ctags 相关功能,并且数据库都是最新的。需要注意的是,gutentags 需要靠上面定义的 project_root 里的标志,判断文件所在的工程,如果一个文件没有托管在 .git/.svn 中,gutentags 找不到工程目录的话,就不会为该野文件生成 tags,这也很合理。想要避免的话,你可以在你的野文件目录中放一个名字为 .root 的空白文件,主动告诉 gutentags 这里就是工程目录。

最后啰嗦两句,少用 CTRL-] 直接在当前窗口里跳转到定义,多使用 CTRL-W ] 用新窗口打开并查看光标下符号的定义,或者 CTRL-W } 使用 preview 窗口预览光标下符号的定义。

我自己还写过不少关于 ctags 的 vimscript,例如在最下面命令行显示函数的原型而不用急着跳转,或者重复按 `ALT+;` 在 preview 窗口中轮流查看多个定义,不切走当前窗口,不会出一个很长的列表让你选择,有兴趣可以刨我的 vim dotfiles

 

编译运行

再 Vim 8 以前,编译和运行程序要么就让 vim 傻等着结束,不能做其他事情,要么切到一个新的终端下面去单独运行编译命令和执行命令,要么开个 tmux 左右切换。如今新版本的异步模式可以让这个流程更加简化,这里我们使用 AsyncRun 插件,简单设置下:

Plug 'skywind3000/asyncrun.vim'

" 自动打开 quickfix window ,高度为 6
let g:asyncrun_open = 6

" 任务结束时候响铃提醒
let g:asyncrun_bell = 1

" 设置 F10 打开/关闭 Quickfix 窗口
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>

该插件可以在后台运行 shell 命令,并且把结果输出到 quickfix 窗口:

 

最简单的编译单个文件,和 sublime 的默认 build system 差不多,我们定义 F9 为编译单文件:

nnoremap <silent> <F9> :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

其中 $(...) 形式的宏在执行时会被替换成实际的文件名或者文件目录,这样按 F9 就可以编译当前文件,同时按 F5 运行:

nnoremap <silent> <F5> :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

用双引号引起来避免文件名包含空格,“-cwd=$(VIM_FILEDIR)” 的意思时在文件文件的所在目录运行可执行,后面可执行使用了全路径,避免 linux 下面当前路径加 “./” 而 windows 不需要的跨平台问题。

参数 `-raw` 表示输出不用匹配错误检测模板 (errorformat) ,直接原始内容输出到 quickfix 窗口。这样你可以一边编辑一边 F9 编译,出错了可以在 quickfix 窗口中按回车直接跳转到错误的位置,编译正确就接着执行。

接下来是项目的编译,不管你直接使用 make 还是 cmake,都是对一群文件做点什么,都需要定位到文件所属项目的目录,AsyncRun 识别当前文件的项目目录方式和 gutentags相同,从文件所在目录向上递归,直到找到名为 “.git”, “.svn”, “.hg”或者 “.root”文件或者目录,如果递归到根目录还没找到,那么文件所在目录就被当作项目目录,你重新定义项目标志:

let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs', 'build.xml'] 

然后在 AsyncRun 命令行中,用 “<root>” 或者 “$(VIM_ROOT)”来表示项目所在路径,于是我们可以定义按 F7 编译整个项目:

nnoremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>

那么如果你有一个项目不在 svn 也不在 git 中怎么查找 <root> 呢?很简单,放一个空的 .root 文件到你的项目目录下就行了,前面配置过,识别名为 .root 的文件。

继续配置用 F8 运行当前项目:

nnoremap <silent> <F8> :AsyncRun -cwd=<root> -raw make run <cr>

当然,你的 makefile 中需要定义怎么 run ,接着按 F6 执行测试:

nnoremap <silent> <F6> :AsyncRun -cwd=<root> -raw make test <cr>

如果你使用了 cmake 的话,还可以照葫芦画瓢,定义 F4 为更新 Makefile 文件,如果不用 cmake 可以忽略:

nnoremap <silent> <F4> :AsyncRun -cwd=<root> cmake . <cr>

由于 C/C++ 标准库的实现方式是发现在后台运行时会缓存标准输出直到程序退出,你想实时看到 printf 输出的话需要 fflush(stdout) 一下,或者程序开头关闭缓存:“setbuf(stdout, NULL);” 即可。

同时,如果你开发 C++ 程序使用 std::cout 的话,后面直接加一个 std::endl 就强制刷新缓存了,不需要弄其他。而如果你在 Windows 下使用 GVim 的话,可以弹出新的 cmd.exe 窗口来运行刚才的程序:

nnoremap <silent> <F5> :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
nnoremap <silent> <F8> :AsyncRun -cwd=<root> -mode=4 make run <cr>

在 Windows 下使用 -mode=4 选项可以跟 Visual Studio 执行命令行工具一样,弹出一个新的 cmd.exe窗口来运行程序或者项目,于是我们有了下面的快捷键:

  • F4:使用 cmake 生成 Makefile
  • F5:单文件:运行
  • F6:项目:测试
  • F7:项目:编译
  • F8:项目:运行
  • F9:单文件:编译
  • F10:打开/关闭底部的 quickfix 窗口

恩,编译和运行基本和 NotePad++ / GEdit 的体验差不多了。如果你重度使用 cmake 的话,你还可以写点小脚本,将 F4 和 F7 的功能合并,检测 CMakeLists.txt 文件改变的话先执行 cmake 更新一下 Makefile,然后再执行 make,否则直接执行 make,这样更自动化些。

 

动态检查

代码检查是个好东西,让你在编辑文字的同时就帮你把潜在错误标注出来,不用等到编译或者运行了才发现。我很奇怪 2018 年了,为啥网上还在到处介绍老旧的 syntastic,但凡见到介绍这个插件的文章基本都可以不看了。老的 syntastic 基本没法用,不能实时检查,一保存文件就运行检查器并且等待半天,所以请用实时 linting 工具 ALE

大概长这个样子,随着你不断的编辑新代码,有语法错误的地方会实时帮你标注出来,侧边会标注本行有错,光标移动过去的时候下面会显示错误原因,而具体错误的符号下面会有红色波浪线提醒。Ale 支持多种语言的各种代码分析器,就 C/C++ 而言,就支持:gcc, clang, cppcheck 以及 clang-format 等,需要另行安装并放入 PATH下面,ALE能在你修改了文本后自动调用这些 linter 来分析最新代码,然后将各种 linter 的结果进行汇总并显示再界面上。https://www.cnblogs.com/wudongwei/p/9083546.html

同样,我们也需要简单配置一下:

let g:ale_linters_explicit = 1
let g:ale_completion_delay = 500
let g:ale_echo_delay = 20
let g:ale_lint_delay = 500
let g:ale_echo_msg_format = '[%linter%] %code: %%s'
let g:ale_lint_on_text_changed = 'normal'
let g:ale_lint_on_insert_leave = 1
let g:airline#extensions#ale#enabled = 1

let g:ale_c_gcc_options = '-Wall -O2 -std=c99'
let g:ale_cpp_gcc_options = '-Wall -O2 -std=c++14'
let g:ale_c_cppcheck_options = ''
let g:ale_cpp_cppcheck_options = ''

基本上就是定义了一下运行规则,信息显示格式以及几个 linter 的运行参数,其中 6,7 两行比较重要,它规定了如果 normal 模式下文字改变以及离开 insert 模式的时候运行 linter,这是相对保守的做法,如果没有的话,会导致 YouCompleteMe 的补全对话框频繁刷新。

记得设置一下各个 linter 的参数,忽略一些你觉得没问题的规则,不然错误太多没法看。默认错误和警告的风格都太难看了,你需要修改一下,比如我使用 GVim,就重新定义了警告和错误的样式,去除默认难看的红色背景,代码正文使用干净的波浪下划线表示:

let g:ale_sign_error = "\ue009\ue009"
hi! clear SpellBad
hi! clear SpellCap
hi! clear SpellRare
hi! SpellBad gui=undercurl guisp=red
hi! SpellCap gui=undercurl guisp=blue
hi! SpellRare gui=undercurl guisp=magenta

不同项目之间如果评测标准不一样还可以具体单独制定 linter 的参数,具体见 ALE 帮助文档了。我基本使用两个检查器:gcc 和 cppcheck,都可以在 ALE 中进行详细配置,前者主要检查有无语法错误,后者主要会给出一些编码建议,和对危险写法的警告。

我之前用 syntastic 时就用了两天就彻底删除了,而开始用 ALE 后,一用上就停不下来,头两天我还一度觉得它就是个可有可无的点缀,但是第三天它帮我找出两个潜在的 bug 的时候,我开始觉得没白安装,比如:

即便你使用各类 C/C++ IDE,也只能给实时你标注一些编译错误或者警告,而使用 ALE + cppcheck/gcc,连上面类似的潜在错误都能帮你自动找出来,并且当你光标移动过去时在最下面命令行提示你错误原因。

用上一段时间以后,让你写 C/C++ 有一种安心和舒适的感觉。

 

修改比较

这是个小功能,在侧边栏显示一个修改状态,对比当前文本和 git/svn 仓库里的版本,在侧边栏显示修改情况,以前 Vim 做不到实时显示修改状态,如今推荐使用 vim-signify 来实时显示修改状态,它比 gitgutter 强,除了 git 外还支持 svn/mercurial/cvs 等十多种主流版本管理系统。https://mounui.com/152.html

没注意到它时,你可能觉得它不存在,当你有时真的看上两眼时,你会发现这个功能很贴心。最新版 signify 还有一个命令`:SignifyDiff`,可以左右分屏对比提交前后记录,比你命令行 svn/git diff 半天直观多了。并且对我这种同时工作在 subversion 和 git 环境下的情况契合的比较好。

Signify 和前面的 ALE 都会在侧边栏显示一些标记,默认侧边栏会自动隐藏,有内容才会显示,不喜欢侧边栏时有时无的行为可设置强制显示侧边栏:“set signcolumn=yes” 。

v

文本对象(我没有用这个)

相信大家用 Vim 进行编辑时都很喜欢文本对象这个概念,diw 删除光标所在单词,ciw 改写单词,vip 选中段落等,ci"/ci( 改写引号/括号中的内容。而编写 C/C++ 代码时我推荐大家补充几个十分有用的文本对象,可以使用 textobj-user 全家桶:

Plug 'kana/vim-textobj-user'
Plug 'kana/vim-textobj-indent'
Plug 'kana/vim-textobj-syntax'
Plug 'kana/vim-textobj-function', { 'for':['c', 'cpp', 'vim', 'java'] }
Plug 'sgur/vim-textobj-parameter'

它新定义的文本对象主要有:

  • i, 和 a, :参数对象,写代码一半在修改,现在可以用 di, 或 ci, 一次性删除/改写当前参数
  • ii 和 ai :缩进对象,同一个缩进层次的代码,可以用 vii 选中,dii / cii 删除或改写
  • if 和 af :函数对象,可以用 vif / dif / cif 来选中/删除/改写函数的内容

最开始我不太想用额外的文本对象,一直在坚持 Vim 固有的几个默认对象,生怕手练习惯了肌肉形成记忆到远端没有环境的 vim 下形成依赖改不过来,后来我慢慢发现挺有用的,比如改写参数,以前是比较麻烦的事情,这下流畅了很多,当我发现自己编码效率得到比较大的提升时,才发现习惯依赖不重要,行云流水才是真重要。以前看到过无数次都选择性忽略的东西,有时候试试可能会有新的发现。

编辑辅助

大家都知道 color 文件定义了众多不同语法元素的色彩,还有一个关键因素就是语法文件本身能否识别并标记得出众多不同的内容来?语法文件对某些东西没标注,你 color 文件确定了颜色也没用。因此 Vim 下面写 C/C++ 代码,语法高亮准确丰富的话能让你编码的心情好很多,这里推荐 vim-cpp-enhanced-highlight 插件,提供比 Vim 自带语法文件更好的 C/C++ 语法标注,支持 标准 11/14/17。

前面编译运行时需要频繁的操作 quickfix 窗口,ale查错时也需要快速再错误间跳转(location list),就连文件比较也会用到快速跳转到上/下一个差异处,unimpaired 插件帮你定义了一系列方括号开头的快捷键,被称为官方 Vim 中丢失的快捷键。

我们好些地方用到了 quickfix / location 窗口,你在 quickfix 中回车选中一条错误的话,默认会把你当前窗口给切走,变成新文件,虽然按 CTRL+O 可以返回,但是如果不太喜欢这样切走当前文件的做法,可以设置 switchbuf,发现文件已在 Vim 中打开就跳过去,没打开过就新建窗口/标签打开,具体见帮助。

Vim最爽的地方是把所有 ALT 键映射全部留给用户了,尽量使用 Vim 的 ALT键映射,可以让冗长的快捷键缩短很多,请参考:《Vim和终端软件中支持ALT映射》。

"   .  . .  .  . .  . S@t.. .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  .  . .  ..
"    .     ..t%SXSS%:;t.X8S% .   .      .       .       .       .       .       .       .       .       .       .       .
"      . %St8;8:X;8:8:8%8;%%:@S:    . .    . .    ....    .  .    .  .    .  .    .  .    .  .    .  .    .  .    .  ....
"  .    8: %.;t;S;%@88:X.8X8%;8S t@@%   .  %@@t  .X88X .      .       .   %@@@@@@@@@X:  .     .       .       .       .  
"    ..X.;X%8t8%8ttX.88;8.8%:;% ;8:SX%.   SX.8S.  St88:  .  .   .  .    ..XS.@%SSS88S@:. X@@%  . . .    .  .    .  ......
"   . X;:;8SS888;8tt;8:8:8; t:t8S 8:Xt.  :8888: .%888:.  .SSSSSSSSSSS%:  .S888t   @@8X: .%.88  .SSt  .:SS;  .%SSSSSSSS%. 
"    :t8 :;X8S;8.8S;8S.8.t8:%8XS.. S8.8:.S8;8;  :@;@88 . S:88 X.88@88:@t..%S.  .. X;8@: :%:;8. X%:X;. 8;.;  %S8@XXSXSS8..
"  .t88; X;8S8888;8S8t 8S88SSStt:. @.%8St;@8X  . t .8S   S:88:%888%;8t8:..S.S@%SSS8S88t .% @;  X:.X.  88t :.t@t8@ .......
"  8; :888XSStS;88;88X%;;tt::;;8@ ..%X88:88Xt    .S@.::. S@8% X8.@;S888X .%;88SSSS.SX.:. 8S88: @;88t. 8.S8  t;@8@88@88S..
"  S. :tX: ;%8S8 : .::. %8t  %S 8.  @88t8 8t.  . . .@8;  8888 @@%S;t8.8S .:SX8; .:.... . S8; ..8888:..8:8@: ;St@@888.@@..
"    :8:;888888 .; .     8%8@       .8X.@8X  .    X%8@  .t@8S X88X:%888X .@8@8t  ..  .   SX%X .X;;S@%tS8; ;..SttSXS8888S.
"    t.8XX;;8X% XX.  .    %8X8;   . :tX8@t     .  t8X8:  %@@S X8@@:t8tXt...:%t..       . X:8X  X8@@88@888t. %88t888 888t.
"  .    :8;S: . S@.       t8;8:: .   .;:;. . .   .%@%:   t%%; .%%;..: t. .;  :  . . .    %;8.  ;X;X%.:.: t  ;t  ;:: :t;..
"     :%@t%8   88.  .  .  :: . ..   .   .          .   . ..  .      ..   .    .       . . ... .   . .   .        ..      
"      .. 8888   ..      ...   . .    .   .  . .     .   ..    .  .    .        .   .   . ..    .  .  .   .  . .     ....
"
" Author: chxuan <787280310@qq.com>
" Repository: https://github.com/chxuan/vimplus
" Create Date: 2016-04-10
" License: MIT

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 通用设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let mapleader = ","      " 定义<leader>键
set nocompatible         " 设置不兼容原始vi模式
filetype on              " 设置开启文件类型侦测
filetype plugin on       " 设置加载对应文件类型的插件
set noeb                 " 关闭错误的提示
syntax enable            " 开启语法高亮功能
syntax on                " 自动语法高亮
set t_Co=256             " 开启256色支持
set cmdheight=2          " 设置命令行的高度
set showcmd              " select模式下显示选中的行数
set ruler                " 总是显示光标位置
set laststatus=2         " 总是显示状态栏
set number               " 开启行号显示
set cursorline           " 高亮显示当前行
set whichwrap+=<,>,h,l   " 设置光标键跨行
set ttimeoutlen=0        " 设置<ESC>键响应时间
set virtualedit=block,onemore   " 允许光标出现在最后一个字符的后面

set fenc= 

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 代码缩进和排版
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set autoindent           " 设置自动缩进
set cindent              " 设置使用C/C++语言的自动缩进方式
set cinoptions=g0,:0,N-s,(0    " 设置C/C++语言的具体缩进方式
set smartindent          " 智能的选择对其方式
filetype indent on       " 自适应不同语言的智能缩进
set expandtab            " 将制表符扩展为空格
set tabstop=4            " 设置编辑时制表符占用空格数
set shiftwidth=4         " 设置格式化时制表符占用空格数
set softtabstop=4        " 设置4个空格为制表符
set smarttab             " 在行和段开始处使用制表符
set nowrap               " 禁止折行
set backspace=2          " 使用回车键正常处理indent,eol,start等
set sidescroll=10        " 设置向右滚动字符数
set nofoldenable         " 禁用折叠代码

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 代码补全
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set wildmenu             " vim自身命名行模式智能补全
set completeopt-=preview " 补全时不显示窗口,只显示补全列表

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 搜索设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set hlsearch            " 高亮显示搜索结果
set incsearch           " 开启实时搜索功能
set ignorecase          " 搜索时大小写不敏感

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 缓存设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set nobackup            " 设置不备份
set noswapfile          " 禁止生成临时文件
set autoread            " 文件在vim之外修改过,自动重新读入
set autowrite           " 设置自动保存
set confirm             " 在处理未保存或只读文件的时候,弹出确认

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 编码设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set langmenu=zh_CN.UTF-8
set helplang=cn
set termencoding=utf-8
set encoding=utf8
set fileencodings=utf8,ucs-bom,gbk,cp936,gb2312,gb18030

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" gvim/macvim设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if has("gui_running")
    let system = system('uname -s')
    if system == "Darwin\n"
        set guifont=Droid\ Sans\ Mono\ Nerd\ Font\ Complete:h18 " 设置字体
    else
        set guifont=DroidSansMono\ Nerd\ Font\ Regular\ 18      " 设置字体
    endif
    set guioptions-=m           " 隐藏菜单栏
    set guioptions-=T           " 隐藏工具栏
    set guioptions-=L           " 隐藏左侧滚动条
    set guioptions-=r           " 隐藏右侧滚动条
    set guioptions-=b           " 隐藏底部滚动条
    set showtabline=0           " 隐藏Tab栏
    set guicursor=n-v-c:ver5    " 设置光标为竖线
endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 卸载默认插件UnPlug
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:deregister(repo)
  let repo = substitute(a:repo, '[\/]\+$', '', '')
  let name = fnamemodify(repo, ':t:s?\.git$??')
  call remove(g:plugs, name)
endfunction
command! -nargs=1 -bar UnPlug call s:deregister(<args>)

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 插件列表
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
call plug#begin('~/.vim/plugged')

Plug 'chxuan/cpp-mode'
Plug 'chxuan/vim-edit'
Plug 'chxuan/change-colorscheme'
Plug 'chxuan/prepare-code'
Plug 'chxuan/vim-buffer'
Plug 'chxuan/vimplus-startify'
Plug 'chxuan/tagbar'
Plug 'easymotion/vim-easymotion'
Plug 'godlygeek/tabular'
Plug 'haya14busa/incsearch.vim'
Plug 'jiangmiao/auto-pairs'
Plug 'junegunn/vim-slash'
Plug 'junegunn/gv.vim'
Plug 'junegunn/vim-easy-align'
Plug 'kana/vim-textobj-user'
Plug 'kana/vim-textobj-indent'
Plug 'kana/vim-textobj-syntax'
Plug 'kana/vim-textobj-function'
Plug 'ludovicchabant/vim-gutentags'
Plug 'mhinz/vim-signify'
Plug 'mileszs/ack.vim'
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
Plug 'octol/vim-cpp-enhanced-highlight'
Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
Plug 'rhysd/clever-f.vim'
Plug 'ryanoasis/vim-devicons'
Plug 'scrooloose/nerdtree'

Plug 'sgur/vim-textobj-parameter'
Plug 'Shougo/echodoc.vim'
Plug 'skywind3000/asyncrun.vim'
Plug 'skywind3000/quickmenu.vim'
Plug 'terryma/vim-smooth-scroll'
Plug 'tiagofumo/vim-nerdtree-syntax-highlight'
Plug 'tpope/vim-fugitive'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary'
Plug 'tpope/vim-repeat'
Plug 'tpope/vim-endwise'
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
Plug 'Valloric/YouCompleteMe'
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'vim-scripts/indentpython.vim'
Plug 'w0rp/ale'
Plug 'Xuyuanp/nerdtree-git-plugin'
Plug 'Yggdroot/LeaderF'
Plug 'shougo/echodoc.vim'

" 加载自定义插件
if filereadable(expand($HOME . '/.vimrc.custom.plugins'))
    source $HOME/.vimrc.custom.plugins
endif

call plug#end()  

" load vim default plugin
runtime macros/matchit.vim

" 编辑vimrc相关配置文件
nnoremap <leader>e :edit $MYVIMRC<cr>
nnoremap <leader>vc :edit ~/.vimrc.custom.config<cr>
nnoremap <leader>vp :edit ~/.vimrc.custom.plugins<cr>

" 查看vimplus的help文件
nnoremap <leader>h :view +let\ &l:modifiable=0 ~/.vimplus/help.md<cr>

" 打开当前光标所在单词的vim帮助文档
nnoremap <leader>H :execute ":help " . expand("<cword>")<cr>

" 重新加载vimrc文件
nnoremap <leader>s :source $MYVIMRC<cr>

" 安装、更新、删除插件
nnoremap <leader><leader>i :PlugInstall<cr>
nnoremap <leader><leader>u :PlugUpdate<cr>
nnoremap <leader><leader>c :PlugClean<cr>

" 分屏窗口移动
nnoremap <c-j> <c-w>j
nnoremap <c-k> <c-w>k
nnoremap <c-h> <c-w>h
nnoremap <c-l> <c-w>l

" 复制当前选中到系统剪切板
vmap <leader><leader>y "+y

" 将系统剪切板内容粘贴到vim
nnoremap <leader><leader>p "+p

" 打开文件自动定位到最后编辑的位置
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | execute "normal! g'\"" | endif

" 主题设置
set background=dark
let g:onedark_termcolors=256
colorscheme onedark

" ctags
set tags=./.tags;,.tags
set autochdir

let g:echodoc#enable_at_startup = 1
let g:echodoc#type = 'floating'

" airline
let g:airline_theme="onedark"
let g:airline_powerline_fonts = 1
let g:airline#extensions#tabline#enabled = 1
if !exists('g:airline_symbols')
    let g:airline_symbols = {}
endif
let g:airline_left_sep = ''
let g:airline_left_alt_sep = ''
let g:airline_right_sep = ''
let g:airline_right_alt_sep = ''

" ale-setting {{{
let g:ale_linters_explicit = 1
let g:ale_completion_delay = 500
let g:ale_echo_delay = 20
let g:ale_lint_delay = 500
"let g:ale_echo_msg_format = '[%linter%] %code: %%s'
let g:ale_lint_on_text_changed = 'normal'
let g:ale_lint_on_insert_leave = 1
let g:airline#extensions#ale#enabled = 1

let g:ale_c_gcc_options = '-Wall -O2 -std=c99'
let g:ale_cpp_gcc_options = '-Wall -O2 -std=c++14'
let g:ale_c_cppcheck_options = ''
let g:ale_cpp_cppcheck_options = ''


let g:ale_set_highlights = 0
"自定义error和warning图标
let g:ale_sign_error = '✗'
let g:ale_sign_warning = '⚡'
"在vim自带的状态栏中整合ale
let g:ale_statusline_format = ['✗ %d', '⚡ %d', '✔ OK']
"显示Linter名称,出错或警告等相关信息
let g:ale_echo_msg_error_str = 'E'
let g:ale_echo_msg_warning_str = 'W'
let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
"打开文件时不进行检查
let g:ale_lint_on_enter = 0

"普通模式下,sp前往上一个错误或警告,sn前往下一个错误或警告
nmap sp <Plug>(ale_previous_wrap)
nmap sn <Plug>(ale_next_wrap)
"<Leader>s触发/关闭语法检查
nmap <Leader>s :ALEToggle<CR>
"<Leader>d查看错误或警告的详细信息
nmap <Leader>d :ALEDetail<CR>
"使用clang对c和c++进行语法检查,对python使用pylint进行语法检查
let g:ale_linters = {
\   'c++': ['clang'],
\   'c': ['clang'],
\   'python': ['pylint'],
\}
" }}}

"cpp-enhanced-highlight
"高亮类,成员函数,标准库和模板
let g:cpp_class_scope_highlight = 1
let g:cpp_member_variable_highlight = 1
let g:cpp_concepts_highlight = 1
let g:cpp_experimental_simple_template_highlight = 1
"文件较大时使用下面的设置高亮模板速度较快,但会有一些小错误
"let g:cpp_experimental_template_highlight = 1


" cpp-mode
nnoremap <leader>y :CopyCode<cr>
nnoremap <leader>p :PasteCode<cr>
nnoremap <leader>U :GoToFunImpl<cr>
nnoremap <silent> <leader>a :Switch<cr>
nnoremap <leader><leader>fp :FormatFunParam<cr>
nnoremap <leader><leader>if :FormatIf<cr>
nnoremap <leader><leader>t dd :GenTryCatch<cr>
xnoremap <leader><leader>t d :GenTryCatch<cr>

" change-colorscheme
nnoremap <silent> <F9> :PreviousColorScheme<cr>
inoremap <silent> <F9> <esc> :PreviousColorScheme<cr>
nnoremap <silent> <F10> :NextColorScheme<cr>
inoremap <silent> <F10> <esc> :NextColorScheme<cr>
nnoremap <silent> <F11> :RandomColorScheme<cr>
inoremap <silent> <F11> <esc> :RandomColorScheme<cr>
nnoremap <silent> <F12> :ShowColorScheme<cr>
inoremap <silent> <F12> <esc> :ShowColorScheme<cr>

" prepare-code
let g:prepare_code_plugin_path = expand($HOME . "/.vim/plugged/prepare-code")

" vim-buffer
nnoremap <silent> <c-p> :PreviousBuffer<cr>
nnoremap <silent> <c-n> :NextBuffer<cr>
nnoremap <silent> <leader>d :CloseBuffer<cr>
nnoremap <silent> <leader>D :BufOnly<cr>

" vim-edit
nnoremap Y :CopyText<cr>
nnoremap D :DeleteText<cr>
nnoremap C :ChangeText<cr>
nnoremap <leader>r :ReplaceTo<space>

" gutentags 搜索工程目录的标志,碰到这些文件/目录名就停止向上一级目录递归
let g:gutentags_project_root = ['.root', '.svn', '.git', '.hg', '.project']

" 所生成的数据文件的名称
let g:gutentags_ctags_tagfile = '.tags'

" 将自动生成的 tags 文件全部放入 ~/.cache/tags 目录中,避免污染工程目录
let s:vim_tags = expand('~/.cache/tags')
let g:gutentags_cache_dir = s:vim_tags

" 配置 ctags 的参数
let g:gutentags_ctags_extra_args = ['--fields=+niazS', '--extra=+q']
let g:gutentags_ctags_extra_args += ['--c++-kinds=+px']
let g:gutentags_ctags_extra_args += ['--c-kinds=+px']

" 检测 ~/.cache/tags 不存在就新建
if !isdirectory(s:vim_tags)
   silent! call mkdir(s:vim_tags, 'p')
endif

"asyncrun 
" 自动打开 quickfix window ,高度为 6
let g:asyncrun_open = 6

" 任务结束时候响铃提醒
let g:asyncrun_bell = 1

" 设置 F10 打开/关闭 Quickfix 窗口
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>
" 设置 F9编译单个文件
nnoremap <silent> <F9> :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
" 设置 F5运行单个文件
nnoremap <silent> <F5> :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
"设置项目标志,让asyncrun知道文件属于某个项目,项目根目录下至少要有一个.root的空文件
let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs', 'build.xml'] 
"设置F7 编译整个项目
nnoremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>
"设置F8编译当前项目
nnoremap <silent> <F8> :AsyncRun -cwd=<root> -raw make run <cr>
"设置F6 执行测试
nnoremap <silent> <F6> :AsyncRun -cwd=<root> -raw make test <cr>
"设置F4 执行cmake 更新Makefile
nnoremap <silent> <F4> :AsyncRun -cwd=<root> cmake . <cr>




" nerdtree
nnoremap <silent> <leader>n :NERDTreeToggle<cr>
let g:NERDTreeFileExtensionHighlightFullName = 1
let g:NERDTreeExactMatchHighlightFullName = 1
let g:NERDTreePatternMatchHighlightFullName = 1
let g:NERDTreeHighlightFolders = 1         
let g:NERDTreeHighlightFoldersFullName = 1 
let g:NERDTreeDirArrowExpandable='▷'
let g:NERDTreeDirArrowCollapsible='▼'

" YCM
" 如果不指定python解释器路径,ycm会自己搜索一个合适的(与编译ycm时使用的python版本匹配)
" let g:ycm_server_python_interpreter = '/usr/bin/python2.7'
let g:ycm_confirm_extra_conf = 0 
let g:ycm_error_symbol = '✗'
let g:ycm_warning_symbol = '✹'
let g:ycm_seed_identifiers_with_syntax = 1 
let g:ycm_complete_in_comments = 1 
let g:ycm_complete_in_strings = 1 
let g:ycm_collect_identifiers_from_tags_files = 1
let g:ycm_semantic_triggers =  {
            \   'c' : ['->', '.','re![_a-zA-z0-9]'],
            \   'objc' : ['->', '.', 're!\[[_a-zA-Z]+\w*\s', 're!^\s*[^\W\d]\w*\s',
            \             're!\[.*\]\s'],
            \   'ocaml' : ['.', '#'],
            \   'cpp,objcpp' : ['->', '.', '::','re![_a-zA-Z0-9]'],
            \   'perl' : ['->'],
            \   'php' : ['->', '::'],
            \   'cs,java,javascript,typescript,d,python,perl6,scala,vb,elixir,go' : ['.'],
            \   'ruby' : ['.', '::'],
            \   'lua' : ['.', ':'],
            \   'erlang' : [':'],
            \ }
nnoremap <leader>u :YcmCompleter GoToDeclaration<cr>
" 已经使用cpp-mode插件提供的转到函数实现的功能
" nnoremap <leader>i :YcmCompleter GoToDefinition<cr> 
nnoremap <leader>o :YcmCompleter GoToInclude<cr>
nnoremap <leader>ff :YcmCompleter FixIt<cr>
nmap <F5> :YcmDiags<cr>

" tagbar
let g:tagbar_width = 30
nnoremap <silent> <leader>t :TagbarToggle<cr>

" incsearch.vim
map /  <Plug>(incsearch-forward)
map ?  <Plug>(incsearch-backward)
map g/ <Plug>(incsearch-stay)

" vim-easymotion
let g:EasyMotion_smartcase = 1
map <leader>w <Plug>(easymotion-bd-w)
nmap <leader>w <Plug>(easymotion-overwin-w)

" nerdtree-git-plugin
let g:NERDTreeGitStatusIndicatorMapCustom = {
            \ "Modified"  : "✹",
            \ "Staged"    : "✚",
            \ "Untracked" : "✭",
            \ "Renamed"   : "➜",
            \ "Unmerged"  : "═",
            \ "Deleted"   : "✖",
            \ "Dirty"     : "✗",
            \ "Clean"     : "✔︎",
            \ 'Ignored'   : '☒',
            \ "Unknown"   : "?"
            \ }

" LeaderF
nnoremap <leader>f :LeaderfFile .<cr>
let g:Lf_WildIgnore = {
            \ 'dir': ['.svn','.git','.hg','.vscode','.wine','.deepinwine','.oh-my-zsh'],
            \ 'file': ['*.sw?','~$*','*.bak','*.exe','*.o','*.so','*.py[co]']
            \}
let g:Lf_UseCache = 0

" ack
nnoremap <leader>F :Ack!<space>

" echodoc.vim
let g:echodoc_enable_at_startup = 1

" tabular
nnoremap <leader>l :Tab /\|<cr>
nnoremap <leader>= :Tab /=<cr>

" vim-smooth-scroll
noremap <silent> <c-u> :call smooth_scroll#up(&scroll, 0, 2)<CR>
noremap <silent> <c-d> :call smooth_scroll#down(&scroll, 0, 2)<CR>
noremap <silent> <c-b> :call smooth_scroll#up(&scroll*2, 0, 4)<CR>
noremap <silent> <c-f> :call smooth_scroll#down(&scroll*2, 0, 4)<CR>

" gv
nnoremap <leader>g :GV<cr>
nnoremap <leader>G :GV!<cr>
nnoremap <leader>gg :GV?<cr>

" 加载自定义配置
if filereadable(expand($HOME . '/.vimrc.custom.config'))
    source $HOME/.vimrc.custom.config
endif

最后,可以使用别人已经配置好的Vim来开发,其实就是封装了vim的一个ide :https://spacevim.org/cn/use-vim-as-a-c-cpp-ide/

curl -sLf https://spacevim.org/cn/install.sh | bash


链接:https://www.jianshu.com/p/feb6de510399