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 二进制文件即可

vim 插件 YouCompleteMe 的安装 for Mac_第1张图片

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 ""
# 1 "" 1
# 1 "" 3
# 373 "" 3
# 1 "" 1
# 1 "" 2
# 1 "" 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=['']
let g:ycm_key_list_previous_completion=['']
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  =SkipPair()

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

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

你可能感兴趣的:(vim 插件 YouCompleteMe 的安装 for Mac)