vim 插件 YouCompleteMe 的安装 for Mac

系统:macOS Mojave 10.14.6

vim 版本:8.1 (高于 7.4.1578 且支持 Python2 或 Python3 即可)

CMake 的安装

CMake 是一个跨平台的项目管理工具。可以直接通过 brew 获取

brew install cmake

Vundle 的安装与使用

Vundle 是一个自动化的 vim 插件管理器。由于版本更新,Vundle 的配置方式和网上给出的半数教程不再一致。参考 Vundle 的 官方文档 ,可以使用以下命令安装 Vundle

git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim

下载完成后,就可以使用 Vundle 去下载和管理其他插件了。首先在 ~/.vimrc 中指明要使用 Vundle 管理的插件

set nocompatible
filetype off
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

Plugin 'VundleVim/Vundle.vim'
Plugin 'scrooloose/nerdtree'
Plugin 'jistr/vim-nerdtree-tabs'
Plugin 'vim-scripts/taglist.vim'
Plugin 'tomasr/molokai'
Plugin 'vim-syntastic/syntastic'
Plugin 'Valloric/YouCompleteMe'

call vundle#end()
filetype plugin indent on

Plugin 命令后的参数是要管理的插件在 github 的地址。保存好之后,打开 vim 并运行命令

:PluginInstall

Vundle 就会检查这些插件是否已经安装,如果没有安装则会自动到 github 上下载安装。

YCM 的安装

YouCompleteMe 也是一个插件,因此可以通过 Vundle 进行安装。不同点在于

  1. YCM 体积较大,下载速度慢,最好找一个访问 github 快的地方下载。
  2. YCM 需要编译,因此下载完成之后会报错,不用管他。

切换到 YCM 的下载目录运行安装脚本

cd ~/.vim/bundle/YouCompleteMe/
./install.py

如果需要对 C 家族进行语义补全,使用参数
c

./install.py --clang-completer

如果你足够幸运的话,这样应该就装好了。但是一般情况下是不行的(史上最难安装插件这个名头不是白来的?)所以 YCM 官方文档 中给出了全手动安装的方法

1. 下载 YCM

这一步可以通过 Vundle 或手动拉取 git 仓库

cd ~/.vim/bundle/
git clone https://github.com/ycm-core/YouCompleteMe.git
git submodule update --init --recursive

需要注意的是,YCM 仓库中引用了很多第三方仓库。尤其是

~/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/cregex/

这个仓库,博主两次重装都没有拉取成功(不知道是不是设计如此)。据官网说 cregex 的安装是可选的,不下载可能也没关系。不过毕竟大头都下载完了,也不差这一点了

cd ~/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/
rm -rf ./cregex
git submodule update --init --recursive

2. 下载 libclang

这一步只对于 C 家族语义补全是必须的,不需要的用户可以跳过。直接前往 llvm 官网 下载 llvm 二进制文件即可

llvm 8.0.0 for macOS

YCM 官方要求 llvm 版本不低于 8.0.0,但是最高版本 8.0.1 目前没有 macOS 版,因此就只能先下载这一版了。如果以后有了最新版还是最好下载新版本,免得因为 YCM 更新还要重新编译。

下载预编译 llvm 二进制文件是 YCM 官方推荐的做法,不过使用本机 llvm 也是可行的

brew install llvm

因为博主网速感人,死活下不下来,结果也就不得而知了。安装好的小伙伴可以试一下。

备份!!!

前面说过的两步可能是安装过程中耗时最长的了。因此在继续安装之前,最好把这些下载好的文件备份一下,以免后面安装失败还要重新下载。博主把下载好的文件放在百度网盘

链接:https://pan.baidu.com/s/1y05NTT0PKpiDe-PXCKRs3w 密码:zd3o

压缩后有 600 MB 左右,大家可以斟酌一下速度选择性下载。

编译 ycm_core

接下来就是编译 YCM 的核心了,首先需要把解压好的 llvm 放到 ~/ycm_temp 目录下

/Users/lutingwang
└── ycm_temp
	└── llvm_root_dir
	    ├── bin
	    ├── include
	    ├── lib
	    ├── libexec
	    └── share

然后切换到编译目录

cd ~
mkdir ycm_build
cd ycm_build/

开始编译

cmake -G "Unix Makefiles" -DPATH_TO_LLVM_ROOT=~/ycm_temp/llvm_root_dir . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp
cmake --build . --target ycm_core --config Release

如果运行正常,你的 YCM 应该已经可以使用了。删除上面创建的临时路径不会影响 YCM 工作

cd ~
rm -r ycm_temp
rm -r ycm_build

编译 regex

这一步是可选的,目的是加快正则表达式的解析速度。编译过程与上面类似

cd ~
mkdir regex_build
cd regex_build
cmake -G "Unix Makefiles" . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/cregex
cmake --build . --target _regex --config Release
cd ~
rm -r regex_build

YCM 的配置

安装完成之后,还需要对 YCM 进行配置。如果使用 Vundle 进行管理(YCM 放在 ~/.vim/bundle/ 目录下),请先确认~/.vimrc 中有没有

set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
" ...
Plugin 'Valloric/YouCompleteMe'
" ...
call vundle#end()

注意 YCM 的声明要放在 vundle#begin()vundle#end() 之间。

然后修改 .ycm_extra_conf.py 这个文件的位置众说纷纭,我在安装的时候发现他就放在 YouCompleteMe/ 目录下。估计又是一个更新过程中的变化。总之,找到这个文件然后在 import 语句后定义

# import os.path as p
# import subprocess

flags = [
        'std=c++11',
        '-x',
        'c++',
        '-I', '.',
        '-isystem',
        '/usr/local/include',
        '-isystem',
        '/Library/Developer/CommandLineTools/usr/include/c++/v1',
        '-isystem',
        '/Library/Developer/CommandLineTools/usr/lib/clang/10.0.1/include',
        '-isystem',
        '/Library/Developer/CommandLineTools/usr/include',
        '-isystem',
        '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include',
        '-isystem',
        '/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks'
]

def FlagsForFile(filename):
    return { 'flags': flags }

# DIR_OF_THIS_SCRIPT = p.abspath( p.dirname( __file__ ) )
# DIR_OF_THIRD_PARTY = p.join( DIR_OF_THIS_SCRIPT, 'third_party' )

注释掉的部分是原本就有的。可以看到这里我们定义了一个 flags 列表作为 FlagsForFile 函数的返回值。这个列表的作用相当于 gcc 命令后面跟的参数。先看 -isystem 的一系列值,这些值所包含的路径就是 #include < ... > 时编译器的搜索路径。虽然大体相同,但是不同电脑的路径可能还是有些许差异,可以通过命令查询

Lutings-MacBook-Air:ycm4mac lutingwang$ echo | clang -v -E -x c++ -
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/Library/Developer/CommandLineTools/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.14.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free -disable-llvm-verifier -discard-value-names -main-file-name - -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.14 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 450.3 -v -resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/10.0.1 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -I/usr/local/include -stdlib=libc++ -Wno-atomic-implicit-seq-cst -Wno-framework-include-private-from-public -Wno-atimport-in-framework-header -Wno-quoted-include-in-framework-header -fdeprecated-macro -fdebug-compilation-dir /Users/lutingwang/.Trash/ycm4mac -ferror-limit 19 -fmessage-length 80 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.14.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o - -x c++ -
clang -cc1 version 10.0.1 (clang-1001.0.46.4) default target x86_64-apple-darwin18.7.0
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/v1"
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/local/include"
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Library/Developer/CommandLineTools/usr/include/c++/v1
 /Library/Developer/CommandLineTools/usr/lib/clang/10.0.1/include
 /Library/Developer/CommandLineTools/usr/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks (framework directory)
End of search list.
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 373 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

注意到其中有一行 #include <...> search starts here: 后面连续几行就是我们想要的信息。

编辑完 .ycm_extra_conf.py ,还需要修改 ~/.vimrc 。在 YCM 的 Plugin 语句后写上

let g:ycm_global_ycm_extra_conf='~/.vim/bundle/YouCompleteMe/.ycm_extra_conf.py'

相当于把刚刚修改的文件设置成全局 YCM 配置。这样 YCM 应该就可以工作了。随意打开一个源文件,在输入标识符时会自动弹出补全选项。如果没有反应,或者像博主一样在底线命令行提示 The ycmd server SHUT DOWN ... ,就还需要一点工序。

不过如果你安装成功了,那么恭喜,你的 vim 已经拥有了 IDE 级别的自动补全功能。如果需要的话,还可以在 ~/.vimrc 中配置

let g:ycm_key_list_select_completion=['<c-n>']
let g:ycm_key_list_previous_completion=['<c-p>']
let g:ycm_collect_indentifiers_from_tags_files=1
let g:ycm_seed_identifiers_with_syntax=1
let g:ycm_confirm_extra_conf=0 " 避免YCM每次加载都对用户提示是否加载
let g:ycm_autoclose_preview_window_after_completion=1
let g:ycm_complete_in_comments=1 " 在注释输入中也能补全
let g:ycm_complete_in_strings=1 " 在字符串输入中也能补全
let g:ycm_collect_identifiers_from_comments_and_strings=1 " 注释和字符串中的文字也会被收入补全

YCM 安装常见问题

根据博主三天安装 YCM 的经历来看,这个插件之所以难装其实主要是因为下载速度慢。提前下载好 YouCompleteMe 仓库和 llvm 二进制文件的话,安装起来并不算慢(博主最后一次重装 5 分钟之内就运行完了)。因此在安装之前只要做好了备份,重装不是什么难事。下面就来说说我安装时遇到的问题

vim 闪退,报告 python 的 MemoryError

这个问题出现在我第一次使用 Vundle 安装 YCM 之后。那时一切都顺利得不可思议,Vundle 在不到半个小时就把 YCM 下载好了,还报了一个错误 MemoryError。只是那时的我以为这个错误就是传说中“正常”的报错,没有管他。等到一套安装流程走下来,我才发现我的 vim 再也用不了了……

后来查资料发现我安装的 Vundle 是旧版本,使用的命令都还是 Bundle 'Valloric/YouCompleteMe' 。可能是兼容问题之类,总之 vim 在处理 call vundle#rc() 时就会触发 MemoryError。最后把 Vundle 删除了,重新安装一个就好了。

The ycmd server SHUT DOWN … Unexpected exit code -11 …

根据提示查看 log 发现是空的,在网上搜了很久也没有人说过 -11 这个码对应的问题是什么……可能真的是 Unexpected. 于是博主就使用了万能的补救方法

cd ~/.vim/bundle/
rm -rf ./YouCompleteMe
cp -r ~/Desktop/YouCompleteMe ./
cd YouCompleteMe/
./install.py --clang-completer

重新安装。然而重新装了两次之后我就放弃了,一般来说安装错误不会这么频繁的。后来在一个网站上看到网友提供的思路

  1. 使用命令 :YcmDiag 获取具体信息
  2. 注释掉 ~/.vimrclet g:ycm_confirm_extra_conf=0 以确认编译正常

第一个方法其实相当于查看 log,只不过 log 都没有记录信息,而这个命令居然返回了 内置补全功能不能识别该文件类型 这样的报错。也正是因为这个报错信息,让我怀疑到 llvm 的安装(之前一直使用的是 install.py 没有想过自动下载也会出现问题)。

第二个方法稍微复杂一点。正常情况下,进入 vim 之后 YCM 都会在底线命令行提示询问是否加载某个 .ycm_extra_conf.py 文件,除非 ~/.vimrc 中禁用了这一功能(也就是上面那个命令的作用)。因此如果编译正常,应该会有相似的询问。即使出现错误,也应该在询问之后崩溃。但是博主的 vim 只要一进入就会提示 server shutdown,因此怀疑还是编译过程出了问题。

从结果来看,重装是必须的了,但是用脚本安装已经不靠谱了,所以我才选择了手动安装。不得不说官方文档还是要认认真真看,就算照着安装完还有问题,一般也不会致命。

Python 版本

网上问的最多的问题就是版本不一致。这里的不一致主要有 Python 版本和 llvm 版本两个方面。由于 llvm 我们是手动从官网下载的,因此不存在这方面问题。至于 Python 版本,博主也一直没有搞懂问题出在哪……不过结果就是,ycmd server 又 shutdown 了。这次倒不再是 Unexpected 了,而是提示我编译时与运行时使用的 Python 版本不同,需要在 ~/.vimrc 中给出 Python2 的安装路径。于是有了这么一句

let g:ycm_server_python_interpreter='/usr/bin/python2.7'

YCM 的使用

上文中提到,修改 ~/.vim/bundle/YouCompleteMe/.ycm_extra_conf.py 时增加了一个 flags 列表。这个列表中 -isystem 选项指定了系统头文件的搜索路径。如果项目中使用了不在本目录下的头文件,比如

.
├── CMakeLists.txt
├── Makefile
├── algorithmConfig.h.in
├── build
├── include
│   ├── algorithm.h
│   ├── algorithmConfig.h
│   └── bitmap.h
├── src
│   ├── CMakeLists.txt
│   ├── bitmap.cpp
│   ├── main.cpp
│   └── permutation
│       ├── CMakeLists.txt
│       ├── permutation.cpp
│       └── permutation.h
└── tags

就还需要来修改这个文件

flags = [
	// ...
	'-isystem', '/Users/lutingwang/Developer/Algorithm/include'
	// ...
]

但是随着项目增加,这个配置文件也会变得冗杂,降低开发效率与编译效率。因此比较好的做法是,复制一份配置文件到工程源码的根目录下,将上述修改加入项目内配置文件。由于 YCM 搜索 .ycm_extra_conf.py 时优先搜索当前目录,而后逐个父目录搜索,最后才会使用全局配置文件。因此,工程根目录的配置文件可以覆盖全局文件。这样就相当于为每个工程单独指定了一个配置,不会污染系统设置。

另外,YCM 虽然增强了 vim 的自动补全功能,但是选择补全项的时候还需要用方向键。这样对于 vim 来说是很低效的。因此可以在 ~/.vimrc 中插入函数来实现 IDE 式的 tab 补全

inoremap <TAB> <c-r>=SkipPair()<CR>

func SkipPair()
    if getline('.')[col('.') - 1] == ')' || getline('.')[col('.') - 1] == ']' || getline('.')[col('.') - 1] == '"' || getline('.')[col('.') - 1] == "'" || getline('.')[col('.') - 1] == '}'
        return "\<ESC>la"
	elseif pumvisible()
		return "\<Down>\<CR>"
    else
        return "\t"
    endif
endfunc

这个函数原本的功能是跳出括号,新增一重判断后,可以自动选择 YCM 的第一个补全项。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LutingWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值