YouCompleteMe is a fast, as-you-type, fuzzy-search(亲~~支持模糊匹配哦) code completion engine for Vim. It has two completion engines: an identifier-based engine that works with every programming language and a semantic, Clang-based engine that provides semantic code completion for C/C++/Objective-C/Objective-C++ (from now on referred to as “the C-family languages”).
特色:
Auto-triggers as you type. 自动触发,键入即提示,无需快捷键(对C函数的补全需要快捷键)
Uses subsequence-based completion filtering.(模糊匹配)
Uses smart heuristics to intelligently rank whatever candidates survive the filtering step.(词频调整的策略)
Offers semantic completions.(语法层面的补全)
Fast, fast, fast!(号称比clang那个插件更快)
另外,这个插件还支持跳转到函数、变量的定义处,很强大!
YouCompleteMe 插件官方网站 http://val.markovic.io/blog/youcompleteme-a-fast-as-you-type-fuzzy-search-code-completion-engine-for-vim
github地址 https://github.com/Valloric/YouCompleteMe
效果演示
实际使用效果图1,来自官网
至今为止,其官方网站还没有说支持windows(mac和linux环境下都可以直接下载编译使用),但已经被别人在windows下搞定并使用了。我安装的过程也是参照老外的文章,直接下载老外编译好的文件并下载配置各种环境,花了一天才搞定的。
搞定之后试用了一下效果确实很不错,支持语义级别的自动补齐,无论是c++的stl库,还是普通的C函数,还是自己定义的结构体,类,都可以实时识别。
支持类中的成员变量:
支持C++ stl库
支持系统函数
并且再加上syntastic插件,就连你出错的语法、写错的变量都可以给你提示出来。
安装 YouCompleteMe for Windows
请注意:我安装在的vim版本是7.4,官方是说要7.3以上的版本。
首先根据这个帖子回复的地址,去下载YouCompleteMe for Windows插件、 LLVM for Windows。
这个都是别人在windows上编译好的,两者是配套的,如果不用这个版本的插件而用官方最新版的会匹配不上,功能用不了。
下载上面的两个包,前者放到vim的插件目录或者vundle目录下(我是用vim bundle下载了最新的YouCompleteMe插件然后用上面这个版本替换的),后者(LLVM)只需要里面的libclang.dll文件,把libclang.dll文件放到YouCompleteMe目录下的python文件夹中(和ycm_core.pyd文件一起)。
然后要保证你的机器上安装了python2.7(我试了python2.6不行),并且python.exe所在目录在系统的环境变量path中。
这时候启动vim可以测试一下插件安装成功了没有。输入命令
如果vim说找不到命令那就是插件没有被vim找到,如果有出来新的窗口,提示错误,那就说明插件安装好了,接下来还需要继续配置。
如果你安装完了启动vim出现这样的弹出框:
- Runtime Error!
-
- Program: \gvim.exe
-
- R6034
- An application has made an attempt to load the C runtime
- library incorrectly.
- Please contact the application's support team for more
- information.
请按照 这里
的说法解决。
YouCompleteMe这个插件对C/C++能提供基于语法的自动补全功能,依靠的是clang的语法解析。按照其官方文档的说明,我们还需要配置一个.ycm_extra_conf.py文件,该文件即可以在vimrc文件中指定,也可以放置在代码工程的根目录。
官方给出的示例在这里。
我修改的文件如下:
- import os
- import ycm_core
- flags = [
- '-std=c++11',
- '-stdlib=libc++',
- '-Wno-deprecated-declarations',
- '-Wno-disabled-macro-expansion',
- '-Wno-float-equal',
- '-Wno-c++98-compat',
- '-Wno-c++98-compat-pedantic',
- '-Wno-global-constructors',
- '-Wno-exit-time-destructors',
- '-Wno-missing-prototypes',
- '-Wno-padded',
- '-x',
- 'c++',
- '-I',
- '.',
- '-isystem',
- 'D:/android-ndk-r9c/platforms/android-19/arch-arm/usr/include/sys',
- '-isystem',
- 'D:/android-ndk-r9c/platforms/android-19/arch-arm/usr/include',
- '-isystem',
- 'D:/android-ndk-r9c/sources/cxx-stl/llvm-libc++/libcxx/include',
- '-I',
- 'D:/work/jni/inc',
- '-I',
- 'D:/work/jni/common',
- ]
- compilation_database_folder = ''
- if 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 ):
- 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:
- compilation_info = GetCompilationInfoForFile( filename )
- if not compilation_info:
- return None
- final_flags = MakeRelativePathsInFlagsAbsolute(
- compilation_info.compiler_flags_,
- compilation_info.compiler_working_dir_ )
- else:
- relative_to = DirectoryOfThisScript()
- final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
- return {
- 'flags': final_flags,
- 'do_cache': True
- }
这其实是一个python脚本,
这个文件是每个人的设置/每个工程都不一样的
,当然绝大多数情况(官方上写的是99%)只需要修改 flags 数组中的编译相关参数。里面最重要的参数就是‘-I’和‘-isystem’下面指定的头文件所在目录。
libclang.dll基于语法检查、自动补全/提示的时候就会到这些目录下去找头文件中的函数定义。
有一点特别要强调:如果首次编译不能通过的话,插件的自动提示就会显示很多不相关的全局变量和函数,这时候你会误以为这个插件的自动补全功能好垃圾啊,其实……
这里的编译通过是指YouCompleteMe插件调用libclang.dll在语法上能不能编译通过,而不是你的代码最终是否能够在你指定的环境下编译链接出二进制文件。
最后要说一下,我这里只是针对C/C++代码的,python的自动补齐功能我还没有用过,如果有其他问题应该可以从官方文档那1000多行说明中找到答案。
写的仓促,不能保证中间没有漏掉的细节,如果有哪位朋友按照我的步骤,最后没有搞定的,请阅读官方文档或者给我留言。
PS:该插件对C函数不自动提示,需要按快捷键显示,但是官方的快捷键设置的CTRL + SPACE,所以中文输入法环境下要改,参加下面的vimrc片段。
另外,现在YouCompleteMe插件在我的vim里面有个问题,只要被编辑的文件中有中文就会报python的解码错误,还不知道怎么解决。如果大家有解决办法,麻烦指点一下,谢谢!
最后贴上vimrc中对该插件的设置:参照了这篇文章
- " YouCompleteMe 功能
- " 补全功能在注释中同样有效
- let g:ycm_complete_in_comments=1
- " 允许 vim 加载 .ycm_extra_conf.py 文件,不再提示
- let g:ycm_confirm_extra_conf=0
- " 开启 YCM 基于标签引擎
- let g:ycm_collect_identifiers_from_tags_files=1
- " 引入 C++ 标准库tags,这个没有也没关系,只要.ycm_extra_conf.py文件中指定了正确的标准库路径
- set tags+=/data/misc/software/misc./vim/stdcpp.tags
- " YCM 集成 OmniCppComplete 补全引擎,设置其快捷键
- inoremap ;
- " 补全内容不以分割子窗口形式出现,只显示补全列表
- set completeopt-=preview
- " 从第一个键入字符就开始罗列匹配项
- let g:ycm_min_num_of_chars_for_completion=1
- " 禁止缓存匹配项,每次都重新生成匹配项
- let g:ycm_cache_omnifunc=0
- " 语法关键字补全
- let g:ycm_seed_identifiers_with_syntax=1
- " 修改对C函数的补全快捷键,默认是CTRL + space,修改为ALT + ;
- let g:ycm_key_invoke_completion = ''
- " 设置转到定义处的快捷键为ALT + G,这个功能非常赞
- nmap :YcmCompleter GoToDefinitionElseDeclaration =expand("")