Vim自動補全神器:YouCompleteMe(轉)

轉自:http://blog.jobbole.com/58978/ javascript

可能會有一段時間寫linxu,免不了用vim,留着,找時間實操之 php

原文出處: marchtea 的博客 html

第一次據說這個插件仍是在偶然的狀況下看到別人的博客,據說了這個插件的大名。原本打算在實訓期間來完成安裝的,無奈網實在不給力,也就拖到了回家的時候。在開始準備工做的時候就瞭解到這個插件不是很容易安裝,安裝的時候果真名不虛傳。(關於這方面的內容,請查看另外一篇文章)不過,有付出總有回報,安裝以後用上這個插件,真心爲這個插件的強大所折服。 java

那這個插件有何不一樣? python

YouCompleteMe的特別之處

基於語義補全

總所周知,Vim是一款文本編輯器。也就是說,其最基礎的工做就是編輯文本,無論該文本的內容是什麼。在Vim被程序員所使用後,其慢慢的被肩負了與IDE同樣的工做,文本自動補全(ie.acp,omnicppcompleter),代碼檢查(Syntastic)等等工做。 c++

針對文本自動補全這個功能來講,主要有兩種實現方式。 git

  • 基於文本

咱們經常使用的omnicppcompleter,acp,vim自帶的c-x, c-n的實現方式就是基於文本。更通俗的說法,其實就是一個字: 程序員

其經過文本進行一些正則表達式的匹配,再根據生成的tags(利用ctags生成)來實現自動補全的效果。 github

  • 基於語義

顧名思義,其是經過分析源文件,通過語法分析之後進行補全。因爲對源文件進行分析,基於語義的補全能夠作到很精確。可是這顯然是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

使用效果圖

ycmsdfsdgeerg

使用感覺
  • 和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中添加下列代碼

Shell

1

Bundle 'Valloric/YouCompleteMe'

保存退出後打開vim,在正常模式下輸入

Shell

1

:BundleInstall

等待vundle將YouCompleteMe安裝完成

然後進行編譯安裝:

Shell

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安裝完成或者使用

Shell

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. 編譯

    Shell

    1

    2

    3

    4

    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。

Shell

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 <http://unlicense.org/>

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

'-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中配置相應的快捷鍵。

Shell

1

2

3

nnoremap <leader>gl :YcmCompleter GoToDeclaration<CR>

nnoremap <leader>gf :YcmCompleter GoToDefinition<CR>

nnoremap <leader>gg :YcmCompleter GoToDefinitionElseDeclaration<CR>

另外,YCM也提供了豐富的配置選項,一樣在.vimrc中配置。具體請參考

Shell

1

2

let g:ycm_error_symbol = '>>'

let g:ycm_warning_symbol = '>*'

同時,YCM能夠打開location-list來顯示警告和錯誤的信息:YcmDiags。我的關於ycm的配置以下:

Shell

1

2

3

4

5

6

7

" for ycm

let g:ycm_error_symbol = '>>'

let g:ycm_warning_symbol = '>*'

nnoremap <leader>gl :YcmCompleter GoToDeclaration<CR>

nnoremap <leader>gf :YcmCompleter GoToDefinition<CR>

nnoremap <leader>gg :YcmCompleter GoToDefinitionElseDeclaration<CR>

nmap <F4> :YcmDiags<CR>

YCM提供的跳躍功能採用了vim的jumplist,往前跳和日後跳的快捷鍵爲Ctrl+O以及Ctrl+I。

總結

YouCompleteMe是我用過的最爽的一個自動補全的插件了。以前使用acp時,遇到大文件基本上就卡死了,以致於都不怎麼敢使用。因爲YCM使用的時C/S結構,部分使用vim腳本編寫,部分認爲原生代碼,使得跑起來速度飛快。

拋棄Vim自帶的坑爹的補全吧,拋棄ctags吧,拋棄cscope吧,YCM纔是終極補全神器。

在安裝過程當中,我也遇到了很多的坑。一會會發一篇解決這些坑的文章。

最後祝你們碼年順利,一碼平川,碼到功成。

相關文章
相關標籤/搜索