搭建基于 Vim 的 C++和 Python 开发环境!
最近 vscode 在技术圈火了一阵子,每天都可以在掘金上看到安利文章和奇葩的插件,什么"杨超越鼓励师",还有上班摸鱼系列,"看小说插件","看股票插件",让我越发觉得 vscode 的功能越来越强大,强大到可以做任何事情,就像操作系统,而这有没有很像神之编辑器 emacs。最近台湾 emacs 圈中出了一个叛徒,最后这个维护人叛逃到 vscode 阵营并将这一消息发布到了台湾官方 emacs 推特。
编辑器之争,在语言还在蓬勃发展的今天仍在持续,Vim 和 Emacs 谁是最好的编辑器,在程序员的圈子里是一个经久不衰的话题,不论在哪个地方都能轻而易举的引起一场圣战。在公司里使用的开发语言主要是 C++ 和 python,小组同事使用的开发环境也是各种各样,有 NetBean,vscode,C++ eclipse,pycharm 以及 vim。 编辑器或 IDE 始终都只是工具,工具的价值不是看它功能有多强大,有多丰富,要看它能给我们产生多少价值 ,所以同个工具的价值没办法放在不同的人上去比较。我的观点是如果你在开发过程中会经常使用两到三个语言,开发过程中习惯的衔接很重要,我会推荐使用 vim;但是IDE是一个经过高度封装的工具,它对开发流程,开发方式等有一定的假设,所以在很多时候特别是实际项目中,也会为我们节约了大量宝贵的时间。
每种 IDE 都有自己的配置和使用习惯,数量一多,切换来切换去学习成本也很高。除了 vim,平时我也会在 windows 下使用 sublime 看看代码,因为轻快且在 windows 下可以快速编辑剪切复制。用过 pycharm 开发过 python,用过一段时间 eclipse 进行 C++ 开发,结合 beyond compare 方便把 windows 下代码同步到远程 linux 上,然后再进行 make 编译,但最后都统一切到了 vim。
说说我选择 vim 作为主力开发环境的原因,IDE 很多,学习成本一样很高,使用 N 种语言就可能会有 N 种 IDE。IDE 有它自己的好处,它已经针对某种语言进行集成优化,用户需要配置的东西其实不会很多。但其实越高度固化的东西可能越不好用,缺少一定的灵活性。vim 插件多,支持各种语言,同时也解决了 linux 同步运行问题,著名的插件在 github 和 stackoverflow 都很活跃,一个 vim 解决所有 ide。
我使用的 vim 进行 python 和 C++ 进行后台开发,最主要的功能还是不能没有, 提示补全,定义跳转,关键词搜索,语法高亮,语法检查,缩进折叠,函数展示大纲,文件模板,目录树等等 ,而我的 vim 现在也可以方便完成这些功能。
下面开始说说 2019,我使用的 vim 插件,主要是简单的介绍,每一个我都会简单介绍插件和使用操作,以及最重要的文档,因为里面有更详细的使用信息。
学习资源
在 github 上有一个不错的 vim 学习资源,从最简单的介绍起:
Vim 从入门到精通: github.com/wsdjeg/vim-… 笨方法学 vimscript : www.treelib.com/book-detail…
另外,很多 vim 插件我还在知乎上关注一些 Vimer,比如:
- 韦易笑 ( www.zhihu.com/people/skyw… )
- 赵启明 ( www.zhihu.com/people/zhao… )
- Vim专栏 ( zhuanlan.zhihu.com/hack-vim )
一般都会介绍如何搭建 Vim IDE 和插件。
另外,我还喜欢在 github 上通过查找 vim 相关的插件,按照 Most Star 降序浏览。
我的 .vimrc
参考: github.com/cposture/my…
插件管理
插件
- plug.vim: github.com/junegunn/vi…
vim 8 已经支持 异步执行 功能了,并且在大多数插件中得到了支持,异步插件管理器,比如 plug.vim,在更新插件再也不用等那么久了,不再推荐老牌的 vundle。
vim-plug 不仅支持异步更新功能,还 支持针对文件类型和启动命令的延迟加载功能 ,让 vim 的启动速度再提高很多。
image文档
- 安装和使用参考: blog.jobbole.com/114132/
- 延迟加载插件的技术: segmentfault.com/a/119000001…
- vim 启动速度慢分析: segmentfault.com/a/119000001…
配置
延迟加载插件
plug.vim 支持插件延迟加载,不至于不相关的插件一开始都启动;plug.vim 支持按命令(command)或文件类型(file_type)这两种配置。
<pre class="prettyprint hljs vim">" 在第一次执行 NERDTreeToggle 命令时,NERD tree 插件才开始加载
Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
" on 支持多命令
Plug 'junegunn/vim-github-dashboard', { 'on': ['GHDashboard', 'GHActivity'] }
" 打开 clojure 类型的文件时,vim-fireplace' 才开始加载
Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
" for 支持多文件类型
Plug 'kovisoft/paredit', { 'for': ['clojure', 'scheme'] }
复制代码</pre>
使用 on 延迟加载 YouCompleteMe 方法:
- 先把 on 命令列表置空,默认不启动
- 我们使用自动命令,可以让Vim自动执行指定的命令,指定的命令会在指定事件发生的时候执行;进入 insert 模式时,手动调用 call plug#load('YouCompleteMe')
- 删除自动命令组
<pre class="prettyprint hljs vim"># on 为空,后面手动加载
Plug '~/YouCompleteMe', {'on': []}
augroup load_ycm
autocmd!
"延迟加载,在 insert 模式手动加载插件
autocmd InsertEnter * call plug#load('YouCompleteMe') | autocmd! load_ycm
augroup END
复制代码</pre>
延迟加载不是银弹,最终要看是不是有这个需求;所以我们一般在出现启动比较慢的情况下才去尝试使用延迟加载。另外,一般插件的作者都要考虑本身延迟加载功能,而不是依靠等待外部插件去实现延迟加载。
那么如何启动速度慢的情况下分析插件问题呢?
vim 启动时可以指定选项, vim --startuptime vim.log -c q
,启动时把计时信息写入文件,用于分析载入 .vimrc、插件和打开首个文件的过程中时哪一步最耗时。
<pre class="prettyprint hljs groovy">times in msec
clock self+sourced self: sourced script
clock elapsed: other lines
...
003.691 002.008 002.008: sourcing /home/luffichen/.vim/autoload/plug.vim
021.676 000.021 000.021: sourcing /usr/local/share/vim/vim81/filetype.vim
021.869 000.017 000.017: sourcing /usr/local/share/vim/vim81/filetype.vim
...
复制代码</pre>
其中第一列是时间点,第二列和第三列都是时长(区别:第二个是self+sourced,第三个是self),我们主要关注第三列,脚本本身的执行时间。
因为同一个插件会有多行数据,我们要手动把同一插件的第三列统计一个总和,这样很繁琐,所以 github 上有人写了一个插件,专门用于分析每个插件的执行耗时,并输出直线图,具体可以看看: github.com/hyiltiz/vim…
image还有其他分析方法,具体可见: VIM加速: segmentfault.com/a/119000001…
简明操作
<pre class="hljs clojure">:PlugInstall
:PlugStatus
:PlugClean
</pre>
离线安装插件
因为公司的 Linux 开发环境一般无法连接到外网,所以不能直接使用 plug 从 github 上安装插件,我的方法是:
以 auto-pairs 为例,
- 在可以访问外网的环境,
git clone https://github.com/jiangmiao/auto-pairs
- 打包压缩 auto-pairs.zip
- 上传压缩包到服务器,并解压,
rz -bye
,unzip auto-pairs.zip
- 统一拷贝到自定义的插件目录,我这里都放在目录
~/vim-plugin
下,cp -rf auto-pairs ~/vim-plugin/
- 配置 .vimrc,指定插件的本地目录,
Plug '~/vim-plugin/auto-pairs'
- 运行 vim,并执行
:PlugInstall
- 查看安装情况,
:PlugStatus
提示补全
插件
- YouCompleteMe: github.com/Valloric/Yo…
- YCM-Generator: github.com/rdnetto/YCM…
- jedi-vim: github.com/davidhalter…
- vim omnicomple:vim 自带
这几个插件完美的解决了 C++ 和 python 的代码补全和提示,其他语言可以参考其他的介绍。
随着 vscode lsp 协议的推广,vim 语法补全也出现了相应的插件,比如 coc.nvim
,主要是异步并且支持很多 vscode 上的语法补全插件,可以试用一下(ps:公司的系统版本比较老并且网络不通,所以一直没去折腾这插件,后面听说出现了一个打包版本,可以不用安装,后面有需求可以试用一下)。
coc.nvim 具体的介绍: zhuanlan.zhihu.com/p/39302327 coc.nvim: github.com/neoclide/co…
目前,C/C++ 的补全的话,请直接使用YouCompleteMe,没有之一;另外 YCM-Generator 主要用于解决 YouCompleteMe 配置文件生成麻烦问题。
本来最新版本的 YCM 也支持 python 的补全,只要安装一个 jedi
就可以了,但是公司开发环境系统版本不是很高,编译不了最新版本的 YCM,所以安装一个以后端 jedi 为补全客户端的插件 jedi-vim
。
本来还想介绍一下 SuperTab( github.com/ervandew/su… ),它实现的功能简单的说就是用 tab 来调用 vim 的补全功能,这和 linux 操作习惯完全一致,并且方便而合理,但是 YCM 本身就包含了 SuperTab 的功能,所以不再多余的安装 SuperTab 插件了,以免冲突。另外 YCM 文档还说了包含其他插件的功能,具体如下:
<pre class="prettyprint hljs nginx">clang_complete
AutoComplPop
Supertab
neocomplcache
复制代码</pre>
最后补充下 omnicomple,有时 YCM 和 jedi-vim 没办法在某些情况下进行补全,比如我经常遇到,我只想补全我之前写过的一个 word,这个 word 可能只是注释里的一个单词,YCM 没办法找出它;这个时候我会使用 omnicomple 命令系列。
文档
- jedi-vim 插件安装参考文档安装部分: xmfbit.github.io/2018/10/02/…
- omnicomplete,vim 万能补全,
:help ins-completion
- YCM 操作详细参考: github.com/Valloric/Yo…
- YCM 配置: zhuanlan.zhihu.com/p/33046090
配置
YCM
YCM 的补全需要对文件进行语法分析,所以需要依赖 .ycm_extra_conf.py
配置文件,生成 .ycm_extra_conf.py
配置文件在 google 上有详细的介绍,我们这里使用 YCM-Generator
插件,可以对 make 编译系统生成需要的配置文件。
<pre class="prettyprint hljs vim">"=========================================
" YCM-Generator 插件配置
"=========================================
" ctrl-I 自动生成 .ycm_extra_conf.py 文件
noremap <C-I> :YcmGenerateConfig -c g++ -v -x c++ -f -b make .<CR>
复制代码</pre>
上面的配置加到 .vimrc 后,只要在项目根目录打开后执行快捷键 Ctrl - i 后就会自动在当前目录生成配置文件
-
ctrl + I
,自动生成 .ycm_extra_conf.py 文件 -
Ctrl + Space
,在任何地方触发完成建议,即使没有字符串前缀也是如此。这对于查看哪些顶级函数可供使用很有用。 -
:YcmRestartServer
,重新启动 -
:YcmDiags
和:YcmDebugInfo
,查询信息 -
:YcmCompleter
命令,配合其他子选项完成功能,例如,GoToDeclaration
跳转到声明处
omnicomple
<pre class="prettyprint hljs">Ctrl-X Ctrl-L
Ctrl-X Ctrl-N
Ctrl-X Ctrl-i
Ctrl-X Ctrl-]
Ctrl-X Ctrl-F
Ctrl-X Ctrl-O
</pre>
有了 YCM 补全为什么需要 omni,YCM 没办法补全 buffer 内的字符串,这个时候就要用 Ctrl-X Ctrl-N
jedi-vim
<pre class="hljs lua">leader + d
leader + n
leader + r
:Pyimport os
Ctrl + space
</pre>
说明
- centos 系统比较老,使用 YCM 版本不是最新的,我这里为 github.com/Valloric/Yo… 版本为 clang+llvm-3.3-amd64-Ubuntu-10.04.4.tar.gz
语法高亮
插件
- vim-polyglot: github.com/sheerun/vim…
vim-polyglot 是一个开箱即用型的语法高亮包,还有对齐功能,支持 134 种语言 ;同时全部语言文件都是针对文件类型进行延迟加载的,不会影响到 vim 的启动速度。
具体的支持语言可以参考 github 上的介绍: github.com/sheerun/vim…
配置
几乎不用加载配置,只需要 vim 打开 syntax 功能。
<pre class="hljs nginx">syntax on
复制代码</pre>
缩进线
插件
- indentLine: github.com/Yggdroot/in…
indentLine 是一个显示竖直对齐线的插件 ,习惯让代码整齐,但是插件只支持空格对齐的代码,所以对于 tab 对齐的不会显示对齐线;因为不同的编辑器对 tab 的展示不太一样,有的展示 4 个空格,有的展示 8 个;我的习惯是让 vim 将输入的 tab 自动转化为 4 个空格,这样的代码在所有的编辑器的展示都是一样的。
tab 自动转化为 4 个空格:
<pre class="prettyprint hljs vim">"将输入的TAB自动展开成空格。开启后要输入TAB,需要Ctrl-V<TAB>
set expandtab
"使用每层缩进的空格数
set shiftwidth=4
"编辑时一个TAB字符占多少个空格的位置
set tabstop=4
"方便在开启了et后使用退格(backspace)键,每次退格将删除X个空格
set softtabstop=4
" 使回格键(backspace)正常处理indent(缩进位置), eol(行结束符), start(段首), 很奇怪 Vim 默认竟然不允许在这些地方使用 backspace
set backspace=indent,eol,start
"开启时,在行首按TAB将加入 shiftwidth 个空格,否则加入 tabstop 个空格
set smarttab
复制代码</pre>
indentLine 配置:
<pre class="prettyprint hljs vim">"打开缩进线
let g:indentLine_enabled = 1
let g:indentLine_char='¦'
复制代码</pre>
操作
-
:IndentLinesToggle
,打开缩进线
语法高亮+缩进线图
image语法检查
插件
- ale: github.com/w0rp/ale
ale 是一款语法检查的插件,与syntastic类似,但有一个明显的优势,一个是语法检查是异步执行的,因此基本上不会出现卡顿的情况,但它只支持Vim 8.0以上的版本。
Ale 支持多种语言的各种代码分析器,就 C/C++ 而言,就支持:gcc, clang, cppcheck 以及 clang-format 等,需要另行安装并放入 PATH下面,ALE能在你修改了文本后自动调用这些 linter 来分析最新代码,然后将各种 linter 的结果进行汇总并显示再界面上。
因为 LSP 协议支持语法检查,所以 ALE 后面又支持 LSP,又顺便支持了 LSP 语法补全功能,导致 ALE 越来越庞大,后面我基本只将它作为语法检查插件来使用。
ALE 和 clang 的工具集集合起来使用应该很不错, https://github.com/w0rp/ale/blob/master/doc/ale-c.txt
在这里有很多关于 clang 的配置选项,支持 compile_commands.json
,免去头文件查找问题,可惜公司的系统版本较老。
文档
- ale 官方文档: github.com/w0rp/ale/tr…
- Vim插件之ale: www.cnblogs.com/awakenedy/a…
配置
ale
ale 的配置一般都要指定语言的特定 linter 和 linter 选项;我这里只配置了 C++,C 和 python 的语法检查,C++ 和 C 使用 cppcheck,python 使用 pylint;所以这里需要额外安装 cppcheck
和 pylint
外部程序。
g:ale_linters
用于指定 linter,同时我配置了只在修改 normal 和离开 insert 时才会进行语法检查,避免影响速度。
<pre class="prettyprint hljs vim">let g:ale_linters = {
\ 'cpp': ['cppcheck'],
\ 'c': ['cppcheck'],
\ 'python': ['pylint'],
}
" normal 模式下文字改变运行 linter
let g:ale_lint_on_text_changed = 'normal'
" 离开 insert 模式的时候运行 linter
let g:ale_lint_on_insert_leave = 1
let g:ale_c_cppcheck_options = '--enable=all'
let g:ale_cpp_cppcheck_options = '--enable=all'
复制代码</pre>
以一小段有代码展示 cppcheck 和 ale 的语法检查结果:
<pre class="hljs vbscript">int *ptr_list = NULL;
*ptr_list = 1;
复制代码</pre>
最下面的状态栏和左边栏都会提示错误: Null pointer dereference
,如下:
关键词搜索
插件
- FlyGrep: github.com/wsdjeg/FlyG…
FlyGrep 是从 SpaceVim(spacevim.org/cn/)中移植出来的实时代码检索工具,而且支持正则表达式,配置一个快捷键 Ctrl-F 后就和其他 IDE 的搜索没有什么区别了。
FlyGrep 只能搜索当前目录下的文件,所以如果想搜索整个项目,需要先切换到项目根目录。
文档
- FlyGrep 具体介绍和使用方法可以参考官方文档: github.com/wsdjeg/FlyG…
配置
FlyGrep
绑定快捷键 Ctrl-F:
<pre class="prettyprint hljs vim">"=========================================
" FlyGrep 插件配置
"=========================================
nnoremap <C-F> :FlyGrep<CR>
复制代码</pre>
操作
FlyGrep
<pre class="prettyprint hljs xml">Ctrl-F
<Esc>
<Enter>
<Tab>
<S-Tab>
<Home>
<End>
</pre>
vim 自带
以搜索关键词 "main" 为例:
<pre class="prettyprint hljs vim">:vim /main/ % | copen
vim /main/ * | copen
vim /main/ ../** | copen
vim /main path1/** path2/** | copen
</pre>
说明:
<pre class="hljs vim">%
copen
</pre>
搜索图
image源文件头文件切换
插件
- CurtineIncSw.vim: github.com/ericcurtin/…
C++ 项目经常需要在 header 和对应的 source 文件之前切换;CurtineIncSw 就提供了这样的功能,不过它的切换是有一定的前提的:
- 头文件和源文件除了后缀名和目录不一样,文件名应该是一样的;比如 foo.c 对应 foo.h
- 两个文件要么在同级目录,要么将要打开的文件在已打开文件的子目录
操作:
-
leader-R
:从头文件和源文件互切换
跳转
- jedi-vim: github.com/davidhalter…
- ctags
- vim-gutentags: github.com/ludovicchab…
- vim-matchup: github.com/andymass/vi…
python 的跳转,我这边使用的是 jedi-vim 插件;C++ 的跳转我使用 vim + ctags 工具;标签的跳转使用 vim-matchup。
ctags 需要自己安装。
过去写几行代码又需要运行一下 ctags 来生成索引,每次生成耗费不少时间。
如今 Vim 8 下面自动异步生成 tags 的工具有很多,这里推荐:vim-gutentags,这个插件主要做两件事情:
- 确定文件所属的工程目录,即文件当前路径向上递归查找是否有 .git, .svn, .project 等标志性文件(可以自定义)来确定当前文档所属的工程目录。
- 检测同一个工程下面的文件改动,能会自动增量更新对应工程的 .tags 文件。每次改了几行不用全部重新生成,并且这个增量更新能够保证 .tags 文件的符号排序,方便 Vim 中用二分查找快速搜索符号。
配置
vim-gutentags
<pre class="prettyprint hljs vim">" 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']
复制代码</pre>
有了上面的设置,你平时基本感觉不到 tags 文件的生成过程了,只要文件修改过,gutentags 都在后台为你默默打点是否需要更新数据文件,你根本不用管,还会帮你: setlocal tags+=...
为当前文件添加上对应的 tags 文件的路劲而不影响其他文件。得益于 Vim 8 的异步机制,你可以任意随时使用 ctags 相关功能,并且数据库都是最新的。需要注意的是,gutentags 需要靠上面定义的 project_root 里的标志,判断文件所在的工程,如果一个文件没有托管在 .git/.svn 中,gutentags 找不到工程目录的话,就不会为该野文件生成 tags,这也很合理。想要避免的话,你可以在你的野文件目录中放一个名字为 .root 的空白文件,主动告诉 gutentags 这里就是工程目录。
操作
- 操作:
<pre class="hljs erlang">ctrl+]
ctrl+o
%
</pre>
文档
- stackoverflow.com/questions/1…
- vim.wikia.com/wiki/Single…
- blog.csdn.net/gangyanlian…
- releases.llvm.org/3.7.0/tools…
- www.skywind.me/blog/archiv…
文件模版
vim模板插件 segmentfault.com/a/119000000… Vim为特定文件载入模板 blog.csdn.net/demorngel/a…
状态栏
插件
- vim-airline: github.com/vim-airline…
- tagbar: github.com/majutsushi/…
- NERD tree:
vim-airline 是 powerline 的替代品,并且能够和 tarbar 一起工作。这两个插件装完,状态栏,大纲预览以及任务栏都齐了。
文档
- vim-airline 官方文档: github.com/vim-airline…
- tarbar 安装文档: www.wklken.me/posts/2015/…
窗口
类/方法/变量相关侧边栏
- 操作:
- F9 打开
- 插件:
- majutsushi/tagbar
上方 Tab 栏
- 操作:
<pre class="hljs vbscript">ctrl+left
ctrl+right
ctrl+n
ctrl+p
</pre>
C/C++ 格式化
- 插件: github.com/rhysd/vim-c…
- 操作:
- F4,在 normal 模式,格式化文件代码;在 visual 模式,格式化选中的代码
- 配置文件:~/.clang-format
- 文档: github.com/rhysd/vim-c… kxp555.coding.me/2017/12/12/… blog.csdn.net/softimite_z…
折叠
插件: github.com/tmhedberg/S… 操作:zc关闭折叠并zo打开折叠
缩进
操作: <<
和 >>
,==命令来缩进当前行,可视化模式选择多行,使用=命令缩进选中的行 文档: yyq123.blogspot.com/2010/10/vim…
复制粘贴
vim 复制到 windows
<pre class="prettyprint hljs nginx">yum install libX11 libX11-devel libXtst-devel libXtst libXt-devel libXt libSM-devel libSM libXpm libXpm-devel
</pre>
./configure --prefix=/data/luffichen/bin/vim-8.1 --with-features=huge --with-luajit --enable-luainterp=yes --enable-fail-if-missing --enable-pythoninterp=yes --with-x=yes --enable-gui=auto
- 查看是否支持 X11:
grep X11 src/auto/config.h
如果有#define HAVE_X11 1
#define HAVE_X11_XPM_H 1
#define HAVE_X11_SM_SMLIB_H 1
即表示依赖成功 make -j4 && make install
-
vim --version | grep clipboard
查看是否支持 - Run Xshell and connect to the server using the SSH protocol with X11 forwarding,具体的操作:文件-默认会话属性-隧道-X11转移,选择 X Display
- 配置ssh,
vi /etc/ssh/sshd_config
确认有配置X11Forwrding yes
,允许SSH的X转发 - 安装VcXsrv X11 Server, sourceforge.net/projects/vc… 的剪切板功能貌似对中文支持不好,所以这里使用 VcXsrv)
- 打开 XLaunch,选择 multiple windows,和 display number 为 0,start no client,勾选 clipbord
- 操作:
- visual 模式,选择要复制的内容后执行
+y
,即可 安装luajit : blog.csdn.net/tao_627/art…