config/.vim/autoload/omni/cpp/complete.vim

570 lines
20 KiB
VimL
Raw Normal View History

2009-11-01 19:04:42 +01:00
" Description: Omni completion script for cpp files
" Maintainer: Vissale NEANG
" Last Change: 27 sept. 2007
if v:version < 700
echohl WarningMsg
echomsg "omni#cpp#complete.vim: Please install vim 7.0 or higher for omni-completion"
echohl None
finish
endif
call omni#cpp#settings#Init()
let s:OmniCpp_ShowScopeInAbbr = g:OmniCpp_ShowScopeInAbbr
let s:OmniCpp_ShowPrototypeInAbbr = g:OmniCpp_ShowPrototypeInAbbr
let s:OmniCpp_ShowAccess = g:OmniCpp_ShowAccess
let s:szCurrentWorkingDir = getcwd()
" Cache data
let s:CACHE_TAG_POPUP_ITEMS = {}
let s:CACHE_TAG_FILES = {}
let s:CACHE_TAG_ENV = ''
let s:CACHE_OVERLOADED_FUNCTIONS = {}
" Has preview window?
let s:hasPreviewWindow = match(&completeopt, 'preview')>=0
let s:hasPreviewWindowOld = s:hasPreviewWindow
" Popup item list
let s:popupItemResultList = []
" May complete indicator
let s:bMayComplete = 0
" Init mappings
function! omni#cpp#complete#Init()
call omni#cpp#settings#Init()
set omnifunc=omni#cpp#complete#Main
inoremap <expr> <C-X><C-O> omni#cpp#maycomplete#Complete()
inoremap <expr> . omni#cpp#maycomplete#Dot()
inoremap <expr> > omni#cpp#maycomplete#Arrow()
inoremap <expr> : omni#cpp#maycomplete#Scope()
endfunc
" Find the start position of the completion
function! s:FindStartPositionOfCompletion()
" Locate the start of the item, including ".", "->" and "[...]".
let line = getline('.')
let start = col('.') - 1
let lastword = -1
while start > 0
if line[start - 1] =~ '\w'
let start -= 1
elseif line[start - 1] =~ '\.'
" Searching for dot '.'
if lastword == -1
let lastword = start
endif
let start -= 1
elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>'
" Searching for '->'
if lastword == -1
let lastword = start
endif
let start -= 2
elseif start > 1 && line[start - 2] == ':' && line[start - 1] == ':'
" Searching for '::' for namespaces and class
if lastword == -1
let lastword = start
endif
let start -= 2
elseif line[start - 1] == ']'
" Skip over [...].
let n = 0
let start -= 1
while start > 0
let start -= 1
if line[start] == '['
if n == 0
break
endif
let n -= 1
elseif line[start] == ']' " nested []
let n += 1
endif
endwhile
else
break
endif
endwhile
if lastword==-1
" For completion on the current scope
let lastword = start
endif
return lastword
endfunc
" Returns if szKey1.szKey2 is in the cache
" @return
" - 0 = key not found
" - 1 = szKey1.szKey2 found
" - 2 = szKey1.[part of szKey2] found
function! s:IsCached(cache, szKey1, szKey2)
" Searching key in the result cache
let szResultKey = a:szKey1 . a:szKey2
let result = [0, szResultKey]
if a:szKey2 != ''
let szKey = a:szKey2
while len(szKey)>0
if has_key(a:cache, a:szKey1 . szKey)
let result[1] = a:szKey1 . szKey
if szKey != a:szKey2
let result[0] = 2
else
let result[0] = 1
endif
break
endif
let szKey = szKey[:-2]
endwhile
else
if has_key(a:cache, szResultKey)
let result[0] = 1
endif
endif
return result
endfunc
" Extend a tag item to a popup item
function! s:ExtendTagItemToPopupItem(tagItem, szTypeName)
let tagItem = a:tagItem
" Add the access
let szItemMenu = ''
let accessChar = {'public': '+','protected': '#','private': '-'}
if g:OmniCpp_ShowAccess
if has_key(tagItem, 'access') && has_key(accessChar, tagItem.access)
let szItemMenu = szItemMenu.accessChar[tagItem.access]
else
let szItemMenu = szItemMenu." "
endif
endif
" Formating optional menu string we extract the scope information
let szName = substitute(tagItem.name, '.*::', '', 'g')
let szItemWord = szName
let szAbbr = szName
if !g:OmniCpp_ShowScopeInAbbr
let szScopeOfTag = omni#cpp#utils#ExtractScope(tagItem)
let szItemMenu = szItemMenu.' '.szScopeOfTag[2:]
let szItemMenu = substitute(szItemMenu, '\s\+$', '', 'g')
else
let szAbbr = tagItem.name
endif
if g:OmniCpp_ShowAccess
let szItemMenu = substitute(szItemMenu, '^\s\+$', '', 'g')
else
let szItemMenu = substitute(szItemMenu, '\(^\s\+\)\|\(\s\+$\)', '', 'g')
endif
" Formating information for the preview window
if index(['f', 'p'], tagItem.kind[0])>=0
let szItemWord .= '('
if g:OmniCpp_ShowPrototypeInAbbr && has_key(tagItem, 'signature')
let szAbbr .= tagItem.signature
else
let szAbbr .= '('
endif
endif
let szItemInfo = ''
if s:hasPreviewWindow
let szItemInfo = omni#cpp#utils#GetPreviewWindowStringFromTagItem(tagItem)
endif
" If a function is a ctor we add a new key in the tagItem
if index(['f', 'p'], tagItem.kind[0])>=0
if match(szName, '^\~') < 0 && a:szTypeName =~ '\C\<'.szName.'$'
" It's a ctor
let tagItem['ctor'] = 1
elseif has_key(tagItem, 'access') && tagItem.access == 'friend'
" Friend function
let tagItem['friendfunc'] = 1
endif
endif
" Extending the tag item to a popup item
let tagItem['word'] = szItemWord
let tagItem['abbr'] = szAbbr
let tagItem['menu'] = szItemMenu
let tagItem['info'] = szItemInfo
let tagItem['dup'] = (s:hasPreviewWindow && index(['f', 'p', 'm'], tagItem.kind[0])>=0)
return tagItem
endfunc
" Get tag popup item list
function! s:TagPopupList(szTypeName, szBase)
let result = []
" Searching key in the result cache
let cacheResult = s:IsCached(s:CACHE_TAG_POPUP_ITEMS, a:szTypeName, a:szBase)
" Building the tag query, we don't forget dtors when a:szBase==''
if a:szTypeName!=''
" Scope search
let szTagQuery = '^' . a:szTypeName . '::' . a:szBase . '\~\?\w\+$'
else
" Global search
let szTagQuery = '^' . a:szBase . '\w\+$'
endif
" If the result is already in the cache we return it
if cacheResult[0]
let result = s:CACHE_TAG_POPUP_ITEMS[ cacheResult[1] ]
if cacheResult[0] == 2
let result = filter(copy(result), 'v:val.name =~ szTagQuery' )
endif
return result
endif
try
" Getting tags
let result = omni#common#utils#TagList(szTagQuery)
" We extend tag items to popup items
call map(result, 's:ExtendTagItemToPopupItem(v:val, a:szTypeName)')
" We store the result in a cache
if cacheResult[1] != ''
let s:CACHE_TAG_POPUP_ITEMS[ cacheResult[1] ] = result
endif
catch /^TagList:UserInterrupt$/
endtry
return result
endfunc
" Find complete matches for a completion on the global scope
function! s:SearchGlobalMembers(szBase)
if a:szBase != ''
let tagPopupList = s:TagPopupList('', a:szBase)
let tagPopupList = filter(copy(tagPopupList), g:omni#cpp#utils#szFilterGlobalScope)
call extend(s:popupItemResultList, tagPopupList)
endif
endfunc
" Search class, struct, union members
" @param resolvedTagItem: a resolved tag item
" @param szBase: string base
" @return list of tag items extended to popup items
function! s:SearchMembers(resolvedTagItem, szBase)
let result = []
if a:resolvedTagItem == {}
return result
endif
" Get type info without the starting '::'
let szTagName = omni#cpp#utils#ExtractTypeInfoFromTag(a:resolvedTagItem)[2:]
" Unnamed type case. A tag item representing an unnamed type is a variable
" ('v') a member ('m') or a typedef ('t')
if index(['v', 't', 'm'], a:resolvedTagItem.kind[0])>=0 && has_key(a:resolvedTagItem, 'typeref')
" We remove the 'struct:' or 'class:' etc...
let szTagName = substitute(a:resolvedTagItem.typeref, '^\w\+:', '', 'g')
endif
return copy(s:TagPopupList(szTagName, a:szBase))
endfunc
" Return if the tag env has changed
function! s:HasTagEnvChanged()
if s:CACHE_TAG_ENV == &tags
return 0
else
let s:CACHE_TAG_ENV = &tags
return 1
endif
endfunc
" Return if a tag file has changed in tagfiles()
function! s:HasATagFileOrTagEnvChanged()
if s:HasTagEnvChanged()
let s:CACHE_TAG_FILES = {}
return 1
endif
let result = 0
for tagFile in tagfiles()
if tagFile == ""
continue
endif
if has_key(s:CACHE_TAG_FILES, tagFile)
let currentFiletime = getftime(tagFile)
if currentFiletime > s:CACHE_TAG_FILES[tagFile]
" The file has changed, updating the cache
let s:CACHE_TAG_FILES[tagFile] = currentFiletime
let result = 1
endif
else
" We store the time of the file
let s:CACHE_TAG_FILES[tagFile] = getftime(tagFile)
let result = 1
endif
endfor
return result
endfunc
" Initialization
call s:HasATagFileOrTagEnvChanged()
" Filter same function signatures of base classes
function! s:FilterOverloadedFunctions(tagPopupList)
let result = []
for tagPopupItem in a:tagPopupList
if has_key(tagPopupItem, 'kind') && index(['f', 'p'], tagPopupItem.kind[0])>=0 && has_key(tagPopupItem, 'signature')
if !has_key(s:CACHE_OVERLOADED_FUNCTIONS, tagPopupItem.word . tagPopupItem.signature)
let s:CACHE_OVERLOADED_FUNCTIONS[tagPopupItem.word . tagPopupItem.signature] = 1
call extend(result, [tagPopupItem])
endif
else
call extend(result, [tagPopupItem])
endif
endfor
return result
endfunc
" Access filter
function! s:GetAccessFilter(szFilter, szAccessFilter)
let szFilter = a:szFilter
if g:OmniCpp_DisplayMode == 0
if a:szAccessFilter == 'public'
" We only get public members
let szFilter .= "&& v:val.access == 'public'"
elseif a:szAccessFilter == 'protected'
" We get public and protected members
let szFilter .= "&& v:val.access != 'private'"
endif
endif
return szFilter
endfunc
" Filter class members in the popup menu after a completion with -> or .
function! s:FilterClassMembers(tagPopupList, szAccessFilter)
let szFilter = "(!has_key(v:val, 'friendfunc') && !has_key(v:val, 'ctor') && has_key(v:val, 'kind') && index(['m', 'p', 'f'], v:val.kind[0])>=0 && has_key(v:val, 'access'))"
call filter(a:tagPopupList, s:GetAccessFilter(szFilter, a:szAccessFilter))
call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList))
endfunc
" Filter class scope members in the popup menu after a completion with ::
" We only display attribute and functions members that
" have an access information. We also display nested
" class, struct, union, and enums, typedefs
function! s:FilterClassScopeMembers(tagPopupList, szAccessFilter)
let szFilter = "!has_key(v:val, 'friendfunc') && has_key(v:val, 'kind') && (index(['m', 'p', 'f'], v:val.kind[0])>=0 && has_key(v:val, 'access'))"
let szFilter = s:GetAccessFilter(szFilter, a:szAccessFilter)
let szFilter .= "|| index(['c','e','g','s','t','u'], v:val.kind[0])>=0"
call filter(a:tagPopupList, szFilter)
call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList))
endfunc
" Filter static class members in the popup menu
function! s:FilterStaticClassMembers(tagPopupList, szAccessFilter)
let szFilter = "!has_key(v:val, 'friendfunc') && has_key(v:val, 'kind') && (index(['m', 'p', 'f'], v:val.kind[0])>=0 && has_key(v:val, 'access') && match(v:val.cmd, '\\Cstatic')!=-1)"
let szFilter = s:GetAccessFilter(szFilter, a:szAccessFilter)
let szFilter = szFilter . "|| index(['c','e','g','n','s','t','u','v'], v:val.kind[0])>=0"
call filter(a:tagPopupList, szFilter)
call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList))
endfunc
" Filter scope members in the popup menu
function! s:FilterNamespaceScopeMembers(tagPopupList)
call extend(s:popupItemResultList, a:tagPopupList)
endfunc
" Init data at the start of completion
function! s:InitComplete()
" Reset the popup item list
let s:popupItemResultList = []
let s:CACHE_OVERLOADED_FUNCTIONS = {}
" Reset includes cache when the current working directory has changed
let szCurrentWorkingDir = getcwd()
if s:szCurrentWorkingDir != szCurrentWorkingDir
let s:szCurrentWorkingDir = szCurrentWorkingDir
let g:omni#cpp#includes#CACHE_INCLUDES = {}
let g:omni#cpp#includes#CACHE_FILE_TIME = {}
endif
" Has preview window ?
let s:hasPreviewWindow = match(&completeopt, 'preview')>=0
let bResetCache = 0
" Reset tag env or tag files dependent caches
if s:HasATagFileOrTagEnvChanged()
let bResetCache = 1
endif
if (s:OmniCpp_ShowScopeInAbbr != g:OmniCpp_ShowScopeInAbbr)
\|| (s:OmniCpp_ShowPrototypeInAbbr != g:OmniCpp_ShowPrototypeInAbbr)
\|| (s:OmniCpp_ShowAccess != g:OmniCpp_ShowAccess)
let s:OmniCpp_ShowScopeInAbbr = g:OmniCpp_ShowScopeInAbbr
let s:OmniCpp_ShowPrototypeInAbbr = g:OmniCpp_ShowPrototypeInAbbr
let s:OmniCpp_ShowAccess = g:OmniCpp_ShowAccess
let bResetCache = 1
endif
if s:hasPreviewWindow != s:hasPreviewWindowOld
let s:hasPreviewWindowOld = s:hasPreviewWindow
let bResetCache = 1
endif
if bResetCache
let g:omni#cpp#namespaces#CacheResolve = {}
let s:CACHE_TAG_POPUP_ITEMS = {}
let g:omni#cpp#utils#CACHE_TAG_INHERITS = {}
call garbagecollect()
endif
" Check for updates
for szIncludeName in keys(g:omni#cpp#includes#CACHE_INCLUDES)
let fTime = getftime(szIncludeName)
let bNeedUpdate = 0
if has_key(g:omni#cpp#includes#CACHE_FILE_TIME, szIncludeName)
if fTime != g:omni#cpp#includes#CACHE_FILE_TIME[szIncludeName]
let bNeedUpdate = 1
endif
else
let g:omni#cpp#includes#CACHE_FILE_TIME[szIncludeName] = fTime
let bNeedUpdate = 1
endif
if bNeedUpdate
" We have to update include list and namespace map of this file
call omni#cpp#includes#GetList(szIncludeName, 1)
call omni#cpp#namespaces#GetMapFromBuffer(szIncludeName, 1)
endif
endfor
let s:bDoNotComplete = 0
endfunc
" This function is used for the 'omnifunc' option.
function! omni#cpp#complete#Main(findstart, base)
if a:findstart
"call omni#common#debug#Start()
call s:InitComplete()
" Note: if s:bMayComplete==1 g:omni#cpp#items#data is build by MayComplete functions
if !s:bMayComplete
" If the cursor is in a comment we go out
if omni#cpp#utils#IsCursorInCommentOrString()
" Returning -1 is not enough we have to set a variable to let
" the second call of omni#cpp#complete knows that the
" cursor was in a comment
" Why is there a second call when the first call returns -1 ?
let s:bDoNotComplete = 1
return -1
endif
" We get items here (whend a:findstart==1) because GetItemsToComplete()
" depends on the cursor position.
" When a:findstart==0 the cursor position is modified
let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction())
endif
" Get contexts stack
let s:contextStack = omni#cpp#namespaces#GetContexts()
" Reinit of may complete indicator
let s:bMayComplete = 0
return s:FindStartPositionOfCompletion()
endif
" If the cursor is in a comment we return an empty result
if s:bDoNotComplete
let s:bDoNotComplete = 0
return []
endif
if len(g:omni#cpp#items#data)==0
" A) CURRENT_SCOPE_COMPLETION_MODE
" 1) Displaying data of each context
let szAccessFilter = 'all'
for szCurrentContext in s:contextStack
if szCurrentContext == '::'
continue
endif
let resolvedTagItem = omni#cpp#utils#GetResolvedTagItem(s:contextStack, omni#cpp#utils#CreateTypeInfo(szCurrentContext))
if resolvedTagItem != {}
" We don't search base classes because bases classes are
" already in the context stack
let tagPopupList = s:SearchMembers(resolvedTagItem, a:base)
if index(['c','s'], resolvedTagItem.kind[0])>=0
" It's a class or struct
call s:FilterClassScopeMembers(tagPopupList, szAccessFilter)
let szAccessFilter = 'protected'
else
" It's a namespace or union, we display all members
call s:FilterNamespaceScopeMembers(tagPopupList)
endif
endif
endfor
" 2) Displaying global scope members
if g:OmniCpp_GlobalScopeSearch
call s:SearchGlobalMembers(a:base)
endif
else
let typeInfo = omni#cpp#items#ResolveItemsTypeInfo(s:contextStack, g:omni#cpp#items#data)
if typeInfo != {}
if g:omni#cpp#items#data[-1].kind == 'itemScope'
" B) SCOPE_COMPLETION_MODE
if omni#cpp#utils#GetTypeInfoString(typeInfo)==''
call s:SearchGlobalMembers(a:base)
else
for resolvedTagItem in omni#cpp#utils#GetResolvedTags(s:contextStack, typeInfo)
let tagPopupList = s:SearchMembers(resolvedTagItem, a:base)
if index(['c','s'], resolvedTagItem.kind[0])>=0
let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(resolvedTagItem)
if g:OmniCpp_DisplayMode==0
" We want to complete a class or struct
" If this class is a base class so we display all class members
if index(s:contextStack, szTypeInfo)<0
let szAccessFilter = 'public'
call s:FilterStaticClassMembers(tagPopupList, szAccessFilter)
else
let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected'
call s:FilterClassScopeMembers(tagPopupList, szAccessFilter)
endif
else
if index(s:contextStack, szTypeInfo)<0
let szAccessFilter = 'public'
else
let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected'
endif
call s:FilterClassScopeMembers(tagPopupList, szAccessFilter)
endif
else
" We want to complete a namespace
call s:FilterNamespaceScopeMembers(tagPopupList)
endif
endfor
endif
else
" C) CLASS_MEMBERS_COMPLETION_MODE
for resolvedTagItem in omni#cpp#utils#GetResolvedTags(s:contextStack, typeInfo)
let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(resolvedTagItem)
if index(s:contextStack, szTypeInfo)<0
let szAccessFilter = 'public'
else
let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected'
endif
call s:FilterClassMembers(s:SearchMembers(resolvedTagItem, a:base), szAccessFilter)
endfor
endif
endif
endif
"call omni#common#debug#End()
return s:popupItemResultList
endfunc