Fork me on GitHub

Vim自动补全插件YouCompleteMe安装

@author zhanjzh

YouCompleteMe

简称YCM,使用vim编程的人应该都有所耳闻,YCM作为Vim的补全插件真的是神器。

一般情况下,我使用vscode来编程,vscode本身就已经有很不错的补全插件,但是在进行cuda编程时,很无奈的发现vscode只有一个很残缺的vscode-cudacpp 插件。为什么说残缺呢,是因为这个插件能提供的功能只有.cu 文件中对cuda 语法高亮,支持的语法补全都是缩写,只支持很少数的一部分函数,并且没有函数提示。

相比之下,vim 下的YouCompleteMe就表现得十分优秀。

前提条件

安装YCM是有版本前提的,

  1. vim版本要8.0 或者以上(最新)
  2. 自己安装 git 工具包
  3. vim支持 python2或者 python3

安装过程

在网上找到了不止一个版本的安装博客,翻来翻去都试了一下,大致的方法有下面几种:

  • 使用系统自带的clang
  • 使用YCM自己下载的clang
  • 使用自己编译的clang

无论哪种安装之后都不太行,YCM好用是好用,安装是真的坑。

官网README的安装教程: readme

我的系统为 Ubuntu

1. 配置vim的插件管理器 vundle

~/.vim 目录下,先自行下载vundle 文件:

1
git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle

修改vim的配置文件~/.vimrc (第一次使用vim的人请自行创建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set nocompatible
"filetype off
set rtp+=~/.vim/bundle/vundle
call vundle#begin()
Bundle 'gmarik/vundle'
Plugin 'Valloric/YouCompleteMe'
"Brief help of vundle
":BundleList
":BundleInstall
":BundleSearch
":BundleClean
":help vundle
"End brief help

call vundle#end()

2. 使用vundle安装YCM

第一步修改配置文件后退出重新进入vim ,在vim 下执行BundleInstall .

具体步骤:按下Esc 键,shift + ; 按出 : ,输入BundleInstall

等待YCM安装完毕之后,在目录 ~/.vim/bundle/ 下应该会看到有 YouCompleteMe 这个目录。

3. 配置YCM的自动补全

主要配置的是 C和C++ 的自动补全。

按照官网的安装步骤,下面应该执行:

1
2
cd ~/.vim/bundle/YouCompleteMe
python3 install.py --all

然而很遗憾,如果直接按照这个步骤来执行在下载 clang 时会报出类似这个错误:

1
2
3
4
for file: [/home/irvinglwj/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/../clang_archives/clang+llvm-3.9.0-x86_64-opensuse13.2.tar.xz]
expected hash: [9153b473dc37d2e21a260231e90e43b97aba1dff5d27f14c947815a90fbdc8d7]
actual hash: [e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855]
status: [7;"Couldn't connect to server"]

意思是下载的hash不匹配。网上查了信息后有网友说原因是这个:clang官网地址迁移,但是安装包还是使用的旧的地址,因此会解析失败。

既然下载失败那就只能自行编译或者加上 --system-libclang 参数

但是经常会报出YCM server shut down 之类的奇妙错误。下面我说一下我的做法:

  1. 自行下载对应的 clang + llvm 安装包,没记错的话我是在这里下载的: clang+llvm7.0
  2. 将下载完的压缩包放入~/.vim/bundle/YouCompleteMe/third_party/ycmd/clang_archives 目录下
1
cp ~/Downloads/libclang-7.0.0-x86_64-unknown-linux-gnu.tar.bz2 ~/.vim/bundle/YouCompleteMe/third_party/ycmd/clang_archives
  1. 执行官网的安装命令:
1
2
cd ~/.vim/bundle/YouCompleteMe
python3 install.py --clang-completer

如果执行 --all 的话在配置c# 补全时会出现一些错误,但是我目前不需要c# 的补全,因此没去管,另外YCM默认是有python补全的。

到目前位置应该安装完,重新进入vim会提示没有配置文件.ycm_extra_conf.py ,下面会说一下.ycm_extra_conf.py 的配置。

4. 后续配置

复制.ycm_extra_conf.py 文件

1
cp ~/.vim/bundle/YouCompleteMe/third_party/ycmd/.ycm_extra_conf.py ~/.vim

~/.vimrc 添加配置:

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

这之后需要配置C++ 和 C 或者 cuda 的头文件所在地

我的.ycm_extra_conf.py 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright (C) 2014 Google Inc.
#
# This file is part of ycmd.
#
# ycmd is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ycmd is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ycmd. If not, see <http://www.gnu.org/licenses/>.

import os
import ycm_core

# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-Wall',
'-Wextra',
'-Werror',
'-fexceptions',
'-DNDEBUG',
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
# language to use when compiling headers. So it will guess. Badly. So C++
# headers will be compiled as C headers. You don't want that so ALWAYS specify
# a "-std=<something>".
# For a C project, you would set this to something like 'c99' instead of
# 'c++11'.
'-std=c++11',
# ...and the same thing goes for the magic -x option which specifies the
# language that the files to be compiled are written in. This is mostly
# relevant for c++ headers.
# For a C project, you would set this to 'c' instead of 'c++'.
'-x',
'c++',
'-isystem',
'/usr/include',
'-isystem',
'/usr/local/include',
#'-isystem',
#'/usr/include/c++/7.3.0',
'-isystem',
'/usr/include/c++/4.8',
'-isystem',
'/usr/local/cuda-9.0/include',
'-isystem',
'/usr/local/cuda',
'-isystem',
'/usr/lib/gcc/x86_64-linux-gnu/4.8',
#'-isystem',
#'/usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/backward/',
#'-isystem',
#'/usr/include/clang/6.0.0/include',
'-isystem',
'/usr/include/x86_64-linux-gnu',
#'-isystem',
#'/usr/lib/llvm-6.0/lib/clang/6.0.0/include',
#'-isystem',
#'/usr/include/clang/6.0.0/include',
'-isystem',
'/usr/include/c++/4.8'
]


# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags.
compilation_database_folder = ''

if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None

SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm', '.cu' ]

def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )


def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]


def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )


# This is the entry point; this function is called by ycmd to produce flags for
# a file.
def FlagsForFile( filename, **kwargs ):
if not database:
return {
'flags': flags,
'include_paths_relative_to_dir': DirectoryOfThisScript()
}

compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None

# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object.
return {
'flags': list( compilation_info.compiler_flags_ ),
'include_paths_relative_to_dir': compilation_info.compiler_working_dir_
}

重要的地方只有flags 那里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
flags = [
'-Wall',
'-Wextra',
'-Werror',
'-fexceptions',
'-DNDEBUG',
'-x',
'c++',
'-isystem',
'/usr/include',
'-isystem',
'/usr/local/include',
'-isystem',
'/usr/include/c++/4.8',
'-isystem',
'/usr/local/cuda-9.0/include',
'-isystem',
'/usr/local/cuda',
'-isystem',
'/usr/lib/gcc/x86_64-linux-gnu/4.8',
'-isystem',
'/usr/include/x86_64-linux-gnu',
'-isystem',
'/usr/include/c++/4.8'
]

另外如果是跟我一样要配置cuda的补全的话,有些地方要修改一下:

1
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm', '.cu' ] # 添加 .cu

配置输入两个字符就能提示:

1
let g:ycm_semantic_triggers={'c,cpp,python,cuda': ['re!\w{2}'] }

取消函数提示时在上方跳出的框框:

1
2
set completeopt=menu,menuone
let g:ycm_add_pre_view_to_completeopt=0

更多功能有待发掘。