成哥的999天进修历程-第一百零一天

 

Vim自动补全神器:YouCompleteMe第一次听说这个插件还是在偶然的情况下看到别人的博客,听说了这个...



Vim自动补全神器:YouCompleteMe

第一次听说这个插件还是在偶然的情况下看到别人的博客,听说了这个插件的大名。本来打算在实训期间来完成安装的,无奈网实在不给力,也就拖到了回家的时候。在开始准备工作的时候就了解到这个插件不是很容易安装,安装的时候果然名不虚传。(关于这方面的内容,请查看另一篇文章)不过,有付出总有回报,安装之后用上这个插件,真心为这个插件的强大所折服。

那这个插件有何不同?

YouCompleteMe的特别之处

基于语义补全

总所周知,
Vim
是一款文本编辑器。也就是说,其最基础的工作就是编辑文本,不管该文本的内容是什么。在
Vim
被程序员所使用后,其慢慢的被肩负了与IDE一样的工作,文本自动补全(ie.
acp
,
omnicppcompleter
),代码检查(
Syntastic
)等等工作。

针对文本自动补全这个功能来说,主要有两种实现方式。

  • 基于文本
我们常用的
omnicppcompleter
,
acp
,vim自带的
c-x, c-n
的实现方式就是基于文本。更通俗的说法,其实就是一个字:

其通过文本进行一些正则表达式的匹配,再根据生成的tags(利用
ctags
生成)来实现自动补全的效果。

  • 基于语义
顾名思义,其是通过分析源文件,经过语法分析以后进行补全。由于对源文件进行分析,基于语义的补全可以做到很精确。但是这显然是vim所不可能支持的。而且经过这么多年发展,由于语法分析有很高的难度,也一直没有合适的工具出现。直到,由apple支持的
clang/llvm
横空出世。
YouCompleteMe
也正是在
clang/llvm
的基础上进行构建的。

整合实现了多种插件

  • clang_complete
  • AutoComplPop
  • Supertab
  • neocomplcache
  • Syntastic(类似功能,仅仅针对c/c++/obj-c代码)

支持语言

  • c
  • c++
  • obj-c
  • c#
  • python
对于其他的语言,会调用vim设置的
omnifunc
来匹配,因此同样支持
php
,
ruby
等语言。

已知的有 * javascript —-tern_for_vim * ruby/java —-eclim

使用效果图

使用感受

  • 和IDE一样,自动补全,
  • 根据
    include
    的文件进行补全
  • 不用再蹩脚的生成tags
  • 补全非常精准,而且速度很快,不会有延迟(以前在大项目上,acp用起来实在是很卡)
  • 支持类似tags的跳转,跳到定义处以及使用处
  • 出错提示很智能,并且用起来真的是如丝般柔滑,不用输入
    :w
    进行强制检测

安装

说完了那么多好处,就要说到安装了。不同于以往其他vim插件,YCM是一款编译型的插件。在下载完后,需要手动编译后才能使用。对应其他的插件来说,仅仅就是把.vim的文件丢到相应文件夹下就可以。而这也加大了使用YCM的难度。

安装准备

  • 最新版的Vim(7.3.584+),编译时添加+python标志(已经安装的可以通过
    vim --version
    查看)
  • cmake(mac可以通过homebrew安装,
    brew install cmake
    ,ubuntu可以通过
    sudo apt-get install cmake
    )
  • 安装vundle插件,用于安装管理vim的插件

mac下快速安装

.vimrc
中添加下列代码

1

Bundle 
'Valloric/YouCompleteMe'


保存退出后打开vim,在正常模式下输入

1

:BundleInstall


等待
vundle
将YouCompleteMe安装完成

而后进行编译安装:

1

2

cd
~/.vim
/bundle/YouCompleteMe


.
/install
--clang-completer


如果不需要c-family的补全,可以去掉
--clang-completer。
如果需要
c#
的补全,请加上
--omnisharp-completer。


正常来说,YCM会去下载clang的包,如果已经有,也可以用系统
--system-libclang。


就这样,安装结束。打开vim,如果没有提示YCM未编译,则说明安装已经成功了。

手动编译安装

安装的脚本并不是什么时候都好用,至少对我来说是这样的。安装完之后出现了问题,参考issue#809。

在用
:BundleInstall
安装完成或者使用

1

git clone --recursive https:
//github
.com
/Valloric/YouCompleteMe
.git


获取最新的仓库,而后使用
git submodule update --init --recursive
确认仓库的完整性后,开始安装流程。

  1. 下载最新的clang二进制文件 YCM要求
    clang
    版本 > 3.2,一般来说都是下载最新的。
  2. 安装python-dev.(ubuntu下使用
    sudo apt-get install python-dev
    ,mac下默认提供,否则请安装command line tools)
  3. 编译1234
    cd
    ~
    mkdir
    ycm_build
    cd
    ycm_build
    cmake -G “Unix Makefiles” -DPATH_TO_LLVM_ROOT=~
    /ycm_temp/llvm_root_dir
    . ~/.vim
    /bundle/YouCompleteMe/cpp
    make
    ycm_support_libs
    这里需要注意的是,~/ycm_temp/llvm_root_dir中包含的是根据第一步下载的压缩包解压出来的内容(包括
    include
    bin
    等等文件)。
这样就完成了,开始感受YCM提供的完全不逊色于大型IDE所提供的自动补全功能吧。

配置

不同于很多vim插件,YCM首先需要编译,另外还需要有配置。在vim启动后,YCM会找寻当前路径以及上层路径的
.ycm_extra_conf.py
.在
~/.vim/bundle/YouCompleteMe/cpp/ycm/.ycm_extra_conf.py
中提供了默认的模板。也可以参考我的(就在模板上改改而已)。不过这个解决了标准库提示找不到的问题。

一般来说,我会在
~
目录下放一个默认的模板,而后再根据不同的项目在当前目录下再拷贝个
.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

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

# This file is NOT licensed under the GPLv3, which is the license for the rest


# of YouCompleteMe.


#


# Here's the license text for this file:


#


# This is free and unencumbered software released into the public domain.


#


# Anyone is free to copy, modify, publish, use, compile, sell, or


# distribute this software, either in source code form or as a compiled


# binary, for any purpose, commercial or non-commercial, and by any


# means.


#


# In jurisdictions that recognize copyright laws, the author or authors


# of this software dedicate any and all copyright interest in the


# software to the public domain. We make this dedication for the benefit


# of the public at large and to the detriment of our heirs and


# successors. We intend this dedication to be an overt act of


# relinquishment in perpetuity of all present and future rights to this


# software under copyright law.


#


# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,


# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF


# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.


# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR


# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,


# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR


# OTHER DEALINGS IN THE SOFTWARE.


#


# For more information, please refer to 


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',


#'-Wc++98-compat',


'-Wno-long-long'
,


'-Wno-variadic-macros'
,


'-fexceptions'
,


'-stdlib=libc++'
,


# THIS IS IMPORTANT! Without a "-std=" 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=".


# 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++'
,


'-I'
,


'.'
,


'-isystem'
,


'/usr/include'
,


'-isystem'
,


'/usr/local/include'
,


'-isystem'
,


'/Library/Developer/CommandLineTools/usr/include'
,


'-isystem'
,


'/Library/Developer/CommandLineTools/usr/bin/../lib/c++/v1'
,


]


# 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. Notice that YCM itself uses that approach.


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'
]


def DirectoryOfThisScript():


  
return
os.path.
dirname
( os.path.abspath( __file__ ) )


def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):


  
if
not working_directory:


    
return
list( flags )


  
new_flags = []


  
make_next_absolute = False


  
path_flags = [ 
'-isystem'
, 
'-I'
, 
'-iquote'
, 
'--sysroot='
]


  
for
flag 
in
flags:


    
new_flag = flag


    
if
make_next_absolute:


      
make_next_absolute = False


      
if
not flag.startswith( 
'/'
):


        
new_flag = os.path.
join
( working_directory, flag )


    
for
path_flag 
in
path_flags:


      
if
flag == path_flag:


        
make_next_absolute = True


        
break


      
if
flag.startswith( path_flag ):


        
path = flag[ len( path_flag ): ]


        
new_flag = path_flag + os.path.
join
( working_directory, path )


        
break


    
if
new_flag:


      
new_flags.append( new_flag )


  
return
new_flags


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 )


def FlagsForFile( filename, **kwargs ):


  
if
database:


    
# Bear in mind that compilation_info.compiler_flags_ does NOT return a


    
# python list, but a "list-like" StringVec object


    
compilation_info = GetCompilationInfoForFile( filename )


    
if
not compilation_info:


      
return
None


    
final_flags = MakeRelativePathsInFlagsAbsolute(


      
compilation_info.compiler_flags_,


      
compilation_info.compiler_working_dir_ )


    
# NOTE: This is just for YouCompleteMe; it's highly likely that your project


    
# does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR


    
# ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.


    
#try:


    
#  final_flags.remove( '-stdlib=libc++' )


    
#except ValueError:


    
#  pass


  
else
:


    
relative_to = DirectoryOfThisScript()


    
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )


  
return
{


    
'flags'
: final_flags,


    
'do_cache'
: True


  
}


YouCompleteMe提供的其他功能

YCM除了提供了基本的补全功能,自动提示错误的功能外,还提供了类似tags的功能:

  • 跳转到定义
    GoToDefinition
  • 跳转到声明
    GoToDeclaration
  • 以及两者的合体
    GoToDefinitionElseDeclaration
可以在.vimrc中配置相应的快捷键。

1

2

3

nnoremap gl :YcmCompleter GoToDeclaration


nnoremap gf :YcmCompleter GoToDefinition


nnoremap gg :YcmCompleter GoToDefinitionElseDeclaration


另外,YCM也提供了丰富的配置选项,同样在.vimrc中配置。具体请参考

1

2

let
g:ycm_error_symbol = 
'>>'


let
g:ycm_warning_symbol = 
'>*'


同时,YCM可以打开location-list来显示警告和错误的信息
:YcmDiags。
个人关于ycm的配置如下:

1

2

3

4

5

6

7

" 
for
ycm


let
g:ycm_error_symbol = 
'>>'


let
g:ycm_warning_symbol = 
'>*'


nnoremap gl :YcmCompleter GoToDeclaration


nnoremap gf :YcmCompleter GoToDefinition


nnoremap gg :YcmCompleter GoToDefinitionElseDeclaration


nmap

:YcmDiags


YCM提供的跳跃功能采用了vim的
jumplist,
往前跳和往后跳的快捷键为
Ctrl+O
以及
Ctrl+I。


    关注 成哥的999天进修历程


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册