@author zhanjzh
YouCompleteMe
简称YCM,使用vim编程的人应该都有所耳闻,YCM作为Vim的补全插件真的是神器。
一般情况下,我使用vscode来编程,vscode本身就已经有很不错的补全插件,但是在进行cuda编程时,很无奈的发现vscode只有一个很残缺的vscode-cudacpp
插件。为什么说残缺呢,是因为这个插件能提供的功能只有.cu
文件中对cuda 语法高亮,支持的语法补全都是缩写,只支持很少数的一部分函数,并且没有函数提示。
相比之下,vim 下的YouCompleteMe就表现得十分优秀。
前提条件
安装YCM是有版本前提的,
- vim版本要8.0 或者以上(最新)
- 自己安装 git 工具包
- vim支持 python2或者 python3
安装过程
在网上找到了不止一个版本的安装博客,翻来翻去都试了一下,大致的方法有下面几种:
- 使用系统自带的clang
- 使用YCM自己下载的clang
- 使用自己编译的clang
无论哪种安装之后都不太行,YCM好用是好用,安装是真的坑。
官网README的安装教程: readme
我的系统为 Ubuntu
1. 配置vim的插件管理器 vundle
在~/.vim
目录下,先自行下载vundle
文件:
git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle
修改vim
的配置文件~/.vimrc
(第一次使用vim的人请自行创建)
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++ 的自动补全。
按照官网的安装步骤,下面应该执行:
cd ~/.vim/bundle/YouCompleteMe
python3 install.py --all
然而很遗憾,如果直接按照这个步骤来执行在下载 clang 时会报出类似这个错误:
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
之类的奇妙错误。下面我说一下我的做法:
- 自行下载对应的 clang + llvm 安装包,没记错的话我是在这里下载的: clang+llvm7.0
- 将下载完的压缩包放入
~/.vim/bundle/YouCompleteMe/third_party/ycmd/clang_archives
目录下
cp ~/Downloads/libclang-7.0.0-x86_64-unknown-linux-gnu.tar.bz2 ~/.vim/bundle/YouCompleteMe/third_party/ycmd/clang_archives
- 执行官网的安装命令:
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
文件
cp ~/.vim/bundle/YouCompleteMe/third_party/ycmd/.ycm_extra_conf.py ~/.vim
在~/.vimrc
添加配置:
let g:ycm_global_ycm_extra_conf='~/.vim/.ycm_extra_conf.py'
这之后需要配置C++ 和 C 或者 cuda 的头文件所在地
我的.ycm_extra_conf.py
文件如下:
# 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
那里:
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的补全的话,有些地方要修改一下:
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm', '.cu' ] # 添加 .cu
配置输入两个字符就能提示:
let g:ycm_semantic_triggers={'c,cpp,python,cuda': ['re!\w{2}'] }
取消函数提示时在上方跳出的框框:
set completeopt=menu,menuone
let g:ycm_add_pre_view_to_completeopt=0
更多功能有待发掘。