From 0bf28391b00b1e28c44324bcd7647df416314667 Mon Sep 17 00:00:00 2001 From: bnewbold Date: Thu, 25 Mar 2010 06:29:54 -0400 Subject: lots and lots of vim stuff for c development... --- .vim/autoload/omni/common/debug.vim | 32 ++ .vim/autoload/omni/common/utils.vim | 67 +++ .vim/autoload/omni/cpp/complete.vim | 569 ++++++++++++++++++++++ .vim/autoload/omni/cpp/includes.vim | 126 +++++ .vim/autoload/omni/cpp/items.vim | 660 ++++++++++++++++++++++++++ .vim/autoload/omni/cpp/maycomplete.vim | 82 ++++ .vim/autoload/omni/cpp/namespaces.vim | 838 +++++++++++++++++++++++++++++++++ .vim/autoload/omni/cpp/settings.vim | 96 ++++ .vim/autoload/omni/cpp/tokenizer.vim | 93 ++++ .vim/autoload/omni/cpp/utils.vim | 587 +++++++++++++++++++++++ 10 files changed, 3150 insertions(+) create mode 100644 .vim/autoload/omni/common/debug.vim create mode 100644 .vim/autoload/omni/common/utils.vim create mode 100644 .vim/autoload/omni/cpp/complete.vim create mode 100644 .vim/autoload/omni/cpp/includes.vim create mode 100644 .vim/autoload/omni/cpp/items.vim create mode 100644 .vim/autoload/omni/cpp/maycomplete.vim create mode 100644 .vim/autoload/omni/cpp/namespaces.vim create mode 100644 .vim/autoload/omni/cpp/settings.vim create mode 100644 .vim/autoload/omni/cpp/tokenizer.vim create mode 100644 .vim/autoload/omni/cpp/utils.vim (limited to '.vim/autoload') diff --git a/.vim/autoload/omni/common/debug.vim b/.vim/autoload/omni/common/debug.vim new file mode 100644 index 0000000..eded649 --- /dev/null +++ b/.vim/autoload/omni/common/debug.vim @@ -0,0 +1,32 @@ +" Description: Omni completion debug functions +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +let s:CACHE_DEBUG_TRACE = [] + +" Start debug, clear the debug file +function! omni#common#debug#Start() + let s:CACHE_DEBUG_TRACE = [] + call extend(s:CACHE_DEBUG_TRACE, ['============ Debug Start ============']) + call writefile(s:CACHE_DEBUG_TRACE, "Omni.dbg") +endfunc + +" End debug, write to debug file +function! omni#common#debug#End() + call extend(s:CACHE_DEBUG_TRACE, ["============= Debug End ============="]) + call extend(s:CACHE_DEBUG_TRACE, [""]) + call writefile(s:CACHE_DEBUG_TRACE, "Omni.dbg") +endfunc + +" Debug trace function +function! omni#common#debug#Trace(szFuncName, ...) + let szTrace = a:szFuncName + let paramNum = a:0 + if paramNum>0 + let szTrace .= ':' + endif + for i in range(paramNum) + let szTrace = szTrace .' ('. string(eval('a:'.string(i+1))).')' + endfor + call extend(s:CACHE_DEBUG_TRACE, [szTrace]) +endfunc diff --git a/.vim/autoload/omni/common/utils.vim b/.vim/autoload/omni/common/utils.vim new file mode 100644 index 0000000..c880ad2 --- /dev/null +++ b/.vim/autoload/omni/common/utils.vim @@ -0,0 +1,67 @@ +" Description: Omni completion utils +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +" For sort numbers in list +function! omni#common#utils#CompareNumber(i1, i2) + let num1 = eval(a:i1) + let num2 = eval(a:i2) + return num1 == num2 ? 0 : num1 > num2 ? 1 : -1 +endfunc + +" TagList function calling the vim taglist() with try catch +" The only throwed exception is 'TagList:UserInterrupt' +" We also force the noignorecase option to avoid linear search when calling +" taglist() +function! omni#common#utils#TagList(szTagQuery) + let result = [] + let bUserIgnoreCase = &ignorecase + " Forcing noignorecase search => binary search can be used in taglist() + " if tags in the tag file are sorted + if bUserIgnoreCase + set noignorecase + endif + try + let result = taglist(a:szTagQuery) + catch /^Vim:Interrupt$/ + " Restoring user's setting + if bUserIgnoreCase + set ignorecase + endif + throw 'TagList:UserInterrupt' + catch + "Note: it seems that ctags can generate corrupted files, in this case + "taglist() will fail to read the tagfile and an exception from + "has_add() is thrown + endtry + + " Restoring user's setting + if bUserIgnoreCase + set ignorecase + endif + return result +endfunc + +" Same as TagList but don't throw exception +function! omni#common#utils#TagListNoThrow(szTagQuery) + let result = [] + try + let result = omni#common#utils#TagList(a:szTagQuery) + catch + endtry + return result +endfunc + +" Get the word under the cursor +function! omni#common#utils#GetWordUnderCursor() + let szLine = getline('.') + let startPos = getpos('.')[2]-1 + let startPos = (startPos < 0)? 0 : startPos + if szLine[startPos] =~ '\w' + let startPos = searchpos('\<\w\+', 'cbn', line('.'))[1] - 1 + endif + + let startPos = (startPos < 0)? 0 : startPos + let szResult = matchstr(szLine, '\w\+', startPos) + return szResult +endfunc diff --git a/.vim/autoload/omni/cpp/complete.vim b/.vim/autoload/omni/cpp/complete.vim new file mode 100644 index 0000000..a7e4edc --- /dev/null +++ b/.vim/autoload/omni/cpp/complete.vim @@ -0,0 +1,569 @@ +" 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 omni#cpp#maycomplete#Complete() + inoremap . omni#cpp#maycomplete#Dot() + inoremap > omni#cpp#maycomplete#Arrow() + inoremap : 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 diff --git a/.vim/autoload/omni/cpp/includes.vim b/.vim/autoload/omni/cpp/includes.vim new file mode 100644 index 0000000..10a89bc --- /dev/null +++ b/.vim/autoload/omni/cpp/includes.vim @@ -0,0 +1,126 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +let g:omni#cpp#includes#CACHE_INCLUDES = {} +let g:omni#cpp#includes#CACHE_FILE_TIME = {} + +let s:rePreprocIncludePart = '\C#\s*include\s*' +let s:reIncludeFilePart = '\(<\|"\)\(\f\|\s\)\+\(>\|"\)' +let s:rePreprocIncludeFile = s:rePreprocIncludePart . s:reIncludeFilePart + +" Get the include list of a file +function! omni#cpp#includes#GetList(...) + if a:0 > 0 + return s:GetIncludeListFromFile(a:1, (a:0 > 1)? a:2 : 0 ) + else + return s:GetIncludeListFromCurrentBuffer() + endif +endfunc + +" Get the include list from the current buffer +function! s:GetIncludeListFromCurrentBuffer() + let listIncludes = [] + let originalPos = getpos('.') + + call setpos('.', [0, 1, 1, 0]) + let curPos = [1,1] + let alreadyInclude = {} + while curPos != [0,0] + let curPos = searchpos('\C\(^'.s:rePreprocIncludeFile.'\)', 'W') + if curPos != [0,0] + let szLine = getline('.') + let startPos = curPos[1] + let endPos = matchend(szLine, s:reIncludeFilePart, startPos-1) + if endPos!=-1 + let szInclusion = szLine[startPos-1:endPos-1] + let szIncludeFile = substitute(szInclusion, '\('.s:rePreprocIncludePart.'\)\|[<>""]', '', 'g') + let szResolvedInclude = omni#cpp#utils#ResolveFilePath(szIncludeFile) + + " Protection over self inclusion + if szResolvedInclude != '' && szResolvedInclude != omni#cpp#utils#ResolveFilePath(getreg('%')) + let includePos = curPos + if !has_key(alreadyInclude, szResolvedInclude) + call extend(listIncludes, [{'pos' : includePos, 'include' : szResolvedInclude}]) + let alreadyInclude[szResolvedInclude] = 1 + endif + endif + endif + endif + endwhile + + call setpos('.', originalPos) + return listIncludes +endfunc + +" Get the include list from a file +function! s:GetIncludeListFromFile(szFilePath, bUpdate) + let listIncludes = [] + if a:szFilePath == '' + return listIncludes + endif + + if !a:bUpdate && has_key(g:omni#cpp#includes#CACHE_INCLUDES, a:szFilePath) + return copy(g:omni#cpp#includes#CACHE_INCLUDES[a:szFilePath]) + endif + + let g:omni#cpp#includes#CACHE_FILE_TIME[a:szFilePath] = getftime(a:szFilePath) + + let szFixedPath = escape(a:szFilePath, g:omni#cpp#utils#szEscapedCharacters) + execute 'silent! lvimgrep /\C\(^'.s:rePreprocIncludeFile.'\)/gj '.szFixedPath + + let listQuickFix = getloclist(0) + let alreadyInclude = {} + for qf in listQuickFix + let szLine = qf.text + let startPos = qf.col + let endPos = matchend(szLine, s:reIncludeFilePart, startPos-1) + if endPos!=-1 + let szInclusion = szLine[startPos-1:endPos-1] + let szIncludeFile = substitute(szInclusion, '\('.s:rePreprocIncludePart.'\)\|[<>""]', '', 'g') + let szResolvedInclude = omni#cpp#utils#ResolveFilePath(szIncludeFile) + + " Protection over self inclusion + if szResolvedInclude != '' && szResolvedInclude != a:szFilePath + let includePos = [qf.lnum, qf.col] + if !has_key(alreadyInclude, szResolvedInclude) + call extend(listIncludes, [{'pos' : includePos, 'include' : szResolvedInclude}]) + let alreadyInclude[szResolvedInclude] = 1 + endif + endif + endif + endfor + + let g:omni#cpp#includes#CACHE_INCLUDES[a:szFilePath] = listIncludes + + return copy(listIncludes) +endfunc + +" For debug purpose +function! omni#cpp#includes#Display() + let szPathBuffer = omni#cpp#utils#ResolveFilePath(getreg('%')) + call s:DisplayIncludeTree(szPathBuffer, 0) +endfunc + +" For debug purpose +function! s:DisplayIncludeTree(szFilePath, indent, ...) + let includeGuard = {} + if a:0 >0 + let includeGuard = a:1 + endif + let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) + if has_key(includeGuard, szFilePath) + return + else + let includeGuard[szFilePath] = 1 + endif + + let szIndent = repeat(' ', a:indent) + echo szIndent . a:szFilePath + let incList = omni#cpp#includes#GetList(a:szFilePath) + for inc in incList + call s:DisplayIncludeTree(inc.include, a:indent+1, includeGuard) + endfor +endfunc + + diff --git a/.vim/autoload/omni/cpp/items.vim b/.vim/autoload/omni/cpp/items.vim new file mode 100644 index 0000000..b943ad4 --- /dev/null +++ b/.vim/autoload/omni/cpp/items.vim @@ -0,0 +1,660 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +" Build the item list of an instruction +" An item is an instruction between a -> or . or ->* or .* +" We can sort an item in different kinds: +" eg: ((MyClass1*)(pObject))->_memberOfClass1.get() ->show() +" | cast | | member | | method | | method | +" @return a list of item +" an item is a dictionnary where keys are: +" tokens = list of token +" kind = itemVariable|itemCast|itemCppCast|itemTemplate|itemFunction|itemUnknown|itemThis|itemScope +function! omni#cpp#items#Get(tokens, ...) + let bGetWordUnderCursor = (a:0>0)? a:1 : 0 + + let result = [] + let itemsDelimiters = ['->', '.', '->*', '.*'] + + let tokens = reverse(omni#cpp#utils#BuildParenthesisGroups(a:tokens)) + + " fsm states: + " 0 = initial state + " TODO: add description of fsm states + let state=(bGetWordUnderCursor)? 1 : 0 + let item = {'tokens' : [], 'kind' : 'itemUnknown'} + let parenGroup=-1 + for token in tokens + if state==0 + if index(itemsDelimiters, token.value)>=0 + let item = {'tokens' : [], 'kind' : 'itemUnknown'} + let state = 1 + elseif token.value=='::' + let state = 9 + let item.kind = 'itemScope' + " Maybe end of tokens + elseif token.kind =='cppOperatorPunctuator' + " If it's a cppOperatorPunctuator and the current token is not + " a itemsDelimiters or '::' we can exit + let state=-1 + break + endif + elseif state==1 + call insert(item.tokens, token) + if token.kind=='cppWord' + " It's an attribute member or a variable + let item.kind = 'itemVariable' + let state = 2 + " Maybe end of tokens + elseif token.value=='this' + let item.kind = 'itemThis' + let state = 2 + " Maybe end of tokens + elseif token.value==')' + let parenGroup = token.group + let state = 3 + elseif token.value==']' + let parenGroup = token.group + let state = 4 + elseif token.kind == 'cppDigit' + let state = -1 + break + endif + elseif state==2 + if index(itemsDelimiters, token.value)>=0 + call insert(result, item) + let item = {'tokens' : [], 'kind' : 'itemUnknown'} + let state = 1 + elseif token.value == '::' + call insert(item.tokens, token) + " We have to get namespace or classscope + let state = 8 + " Maybe end of tokens + else + call insert(result, item) + let state=-1 + break + endif + elseif state==3 + call insert(item.tokens, token) + if token.value=='(' && token.group == parenGroup + let state = 5 + " Maybe end of tokens + endif + elseif state==4 + call insert(item.tokens, token) + if token.value=='[' && token.group == parenGroup + let state = 1 + endif + elseif state==5 + if token.kind=='cppWord' + " It's a function or method + let item.kind = 'itemFunction' + call insert(item.tokens, token) + let state = 2 + " Maybe end of tokens + elseif token.value == '>' + " Maybe a cpp cast or template + let item.kind = 'itemTemplate' + call insert(item.tokens, token) + let parenGroup = token.group + let state = 6 + else + " Perhaps it's a C cast eg: ((void*)(pData)) or a variable eg:(*pData) + let item.kind = omni#cpp#utils#GetCastType(item.tokens) + let state=-1 + call insert(result, item) + break + endif + elseif state==6 + call insert(item.tokens, token) + if token.value == '<' && token.group == parenGroup + " Maybe a cpp cast or template + let state = 7 + endif + elseif state==7 + call insert(item.tokens, token) + if token.kind=='cppKeyword' + " It's a cpp cast + let item.kind = omni#cpp#utils#GetCastType(item.tokens) + let state=-1 + call insert(result, item) + break + else + " Template ? + let state=-1 + call insert(result, item) + break + endif + elseif state==8 + if token.kind=='cppWord' + call insert(item.tokens, token) + let state = 2 + " Maybe end of tokens + else + let state=-1 + call insert(result, item) + break + endif + elseif state==9 + if token.kind == 'cppWord' + call insert(item.tokens, token) + let state = 10 + " Maybe end of tokens + else + let state=-1 + call insert(result, item) + break + endif + elseif state==10 + if token.value == '::' + call insert(item.tokens, token) + let state = 9 + " Maybe end of tokens + else + let state=-1 + call insert(result, item) + break + endif + endif + endfor + + if index([2, 5, 8, 9, 10], state)>=0 + if state==5 + let item.kind = omni#cpp#utils#GetCastType(item.tokens) + endif + call insert(result, item) + endif + + return result +endfunc + +" Resolve type information of items +" @param namespaces: list of namespaces used in the file +" @param szCurrentClassScope: the current class scope, only used for the first +" item to detect if this item is a class member (attribute, method) +" @param items: list of item, can be an empty list @see GetItemsToComplete +function! omni#cpp#items#ResolveItemsTypeInfo(contextStack, items) + " Note: kind = itemVariable|cCast|cppCast|template|function|itemUnknown|this + " For the first item, if it's a variable we try to detect the type of the + " variable with the function searchdecl. If it fails, thanks to the + " current class scope, we try to detect if the variable is an attribute + " member. + " If the kind of the item is a function, we have to first check if the + " function is a method of the class, if it fails we try to get a match in + " the global namespace. After that we get the returned type of the + " function. + " It the kind is a C cast or C++ cast, there is no problem, it's the + " easiest case. We just extract the type of the cast. + + let szCurrentContext = '' + let typeInfo = {} + " Note: We search the decl only for the first item + let bSearchDecl = 1 + for item in a:items + let curItem = item + if index(['itemVariable', 'itemFunction'], curItem.kind)>=0 + " Note: a variable can be : MyNs::MyClass::_var or _var or (*pVar) + " or _var[0][0] + let szSymbol = s:GetSymbol(curItem.tokens) + + " If we have MyNamespace::myVar + " We add MyNamespace in the context stack set szSymbol to myVar + if match(szSymbol, '::\w\+$') >= 0 + let szCurrentContext = substitute(szSymbol, '::\w\+$', '', 'g') + let szSymbol = matchstr(szSymbol, '\w\+$') + endif + let tmpContextStack = a:contextStack + if szCurrentContext != '' + let tmpContextStack = [szCurrentContext] + a:contextStack + endif + + if curItem.kind == 'itemVariable' + let typeInfo = s:GetTypeInfoOfVariable(tmpContextStack, szSymbol, bSearchDecl) + else + let typeInfo = s:GetTypeInfoOfReturnedType(tmpContextStack, szSymbol) + endif + + elseif curItem.kind == 'itemThis' + if len(a:contextStack) + let typeInfo = omni#cpp#utils#CreateTypeInfo(substitute(a:contextStack[0], '^::', '', 'g')) + endif + elseif curItem.kind == 'itemCast' + let typeInfo = omni#cpp#utils#CreateTypeInfo(s:ResolveCCast(curItem.tokens)) + elseif curItem.kind == 'itemCppCast' + let typeInfo = omni#cpp#utils#CreateTypeInfo(s:ResolveCppCast(curItem.tokens)) + elseif curItem.kind == 'itemScope' + let typeInfo = omni#cpp#utils#CreateTypeInfo(substitute(s:TokensToString(curItem.tokens), '\s', '', 'g')) + endif + + if omni#cpp#utils#IsTypeInfoValid(typeInfo) + let szCurrentContext = omni#cpp#utils#GetTypeInfoString(typeInfo) + endif + let bSearchDecl = 0 + endfor + + return typeInfo +endfunc + +" Get symbol name +function! s:GetSymbol(tokens) + let szSymbol = '' + let state = 0 + for token in a:tokens + if state == 0 + if token.value == '::' + let szSymbol .= token.value + let state = 1 + elseif token.kind == 'cppWord' + let szSymbol .= token.value + let state = 2 + " Maybe end of token + endif + elseif state == 1 + if token.kind == 'cppWord' + let szSymbol .= token.value + let state = 2 + " Maybe end of token + else + " Error + break + endif + elseif state == 2 + if token.value == '::' + let szSymbol .= token.value + let state = 1 + else + break + endif + endif + endfor + return szSymbol +endfunc + +" Search a declaration. +" eg: std::map +" can be empty +" Note: The returned type info can be a typedef +" The typedef resolution is done later +" @return +" - a dictionnary where keys are +" - type: the type of value same as type() +" - value: the value +function! s:GetTypeInfoOfVariable(contextStack, szVariable, bSearchDecl) + let result = {} + + if a:bSearchDecl + " Search type of declaration + "let result = s:SearchTypeInfoOfDecl(a:szVariable) + let result = s:SearchDecl(a:szVariable) + endif + + if result=={} + let szFilter = "index(['m', 'v'], v:val.kind[0])>=0" + let tagItem = s:ResolveSymbol(a:contextStack, a:szVariable, szFilter) + if tagItem=={} + return result + endif + + let szCmdWithoutVariable = substitute(omni#cpp#utils#ExtractCmdFromTagItem(tagItem), '\C\<'.a:szVariable.'\>.*', '', 'g') + let tokens = omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCodeFromLine(szCmdWithoutVariable)) + let result = omni#cpp#utils#CreateTypeInfo(omni#cpp#utils#ExtractTypeInfoFromTokens(tokens)) + " TODO: Namespace resolution for result + + if result != {} && result.value=='' + " result.value=='' + " eg: + " struct + " { + " }gVariable; + if has_key(tagItem, 'typeref') + " Maybe the variable is a global var of an + " unnamed class, struct or union. + " eg: + " 1) + " struct + " { + " }gVariable; + " In this case we need the tags (the patched version) + " Note: We can have a named type like this: + " 2) + " class A + " { + " }gVariable; + if s:IsUnnamedType(tagItem) + " It's an unnamed type we are in the case 1) + let result = omni#cpp#utils#CreateTypeInfo(tagItem) + else + " It's not an unnamed type we are in the case 2) + + " eg: tagItem.typeref = 'struct:MY_STRUCT::MY_SUBSTRUCT' + let szTypeRef = substitute(tagItem.typeref, '^\w\+:', '', '') + + " eg: szTypeRef = 'MY_STRUCT::MY_SUBSTRUCT' + let result = omni#cpp#utils#CreateTypeInfo(szTypeRef) + endif + endif + endif + endif + return result +endfunc + +" Get the type info string from the returned type of function +function! s:GetTypeInfoOfReturnedType(contextStack, szFunctionName) + let result = {} + + let szFilter = "index(['f', 'p'], v:val.kind[0])>=0" + let tagItem = s:ResolveSymbol(a:contextStack, a:szFunctionName, szFilter) + + if tagItem != {} + let szCmdWithoutVariable = substitute(omni#cpp#utils#ExtractCmdFromTagItem(tagItem), '\C\<'.a:szFunctionName.'\>.*', '', 'g') + let tokens = omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCodeFromLine(szCmdWithoutVariable)) + let result = omni#cpp#utils#CreateTypeInfo(omni#cpp#utils#ExtractTypeInfoFromTokens(tokens)) + " TODO: Namespace resolution for result + return result + endif + return result +endfunc + +" Resolve a symbol, return a tagItem +" Gets the first symbol found in the context stack +function! s:ResolveSymbol(contextStack, szSymbol, szTagFilter) + let tagItem = {} + for szCurrentContext in a:contextStack + if szCurrentContext != '::' + let szTagQuery = substitute(szCurrentContext, '^::', '', 'g').'::'.a:szSymbol + else + let szTagQuery = a:szSymbol + endif + + let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') + call filter(tagList, a:szTagFilter) + if len(tagList) + let tagItem = tagList[0] + break + endif + endfor + return tagItem +endfunc + +" Return if the tag item represent an unnamed type +function! s:IsUnnamedType(tagItem) + let bResult = 0 + if has_key(a:tagItem, 'typeref') + " Note: Thanks for __anon ! + let bResult = match(a:tagItem.typeref, '\C\<__anon') >= 0 + endif + return bResult +endfunc + +" Search the declaration of a variable and return the type info +function! s:SearchTypeInfoOfDecl(szVariable) + let szReVariable = '\C\<'.a:szVariable.'\>' + + let originalPos = getpos('.') + let origPos = originalPos[1:2] + let curPos = origPos + let stopPos = origPos + + while curPos !=[0,0] + " We go to the start of the current scope + let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) + if curPos != [0,0] + let matchPos = curPos + " Now want to search our variable but we don't want to go in child + " scope + while matchPos != [0,0] + let matchPos = searchpos('{\|'.szReVariable, 'W', stopPos[0]) + if matchPos != [0,0] + " We ignore matches under comment + if omni#cpp#utils#IsCursorInCommentOrString() + continue + endif + + " Getting the current line + let szLine = getline('.') + if match(szLine, szReVariable)>=0 + " We found our variable + " Check if the current instruction is a decl instruction + let tokens = omni#cpp#utils#TokenizeCurrentInstruction() + let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) + if szTypeInfo != '' + call setpos('.', originalPos) + return omni#cpp#utils#CreateTypeInfo(szTypeInfo) + endif + else + " We found a child scope, we don't want to go in, thus + " we search for the end } of this child scope + let bracketEnd = searchpairpos('{', '', '}', 'nW', g:omni#cpp#utils#expIgnoreComments) + if bracketEnd == [0,0] + break + endif + + if bracketEnd[0] >= stopPos[0] + " The end of the scope is after our cursor we stop + " the search + break + else + " We move the cursor and continue to search our + " variable + call setpos('.', [0, bracketEnd[0], bracketEnd[1], 0]) + endif + endif + endif + endwhile + + " Backing to the start of the scope + call setpos('.', [0,curPos[0], curPos[1], 0]) + let stopPos = curPos + endif + endwhile + + let result = {} + if s:LocalSearchDecl(a:szVariable)==0 && !omni#cpp#utils#IsCursorInCommentOrString() + let tokens = omni#cpp#utils#TokenizeCurrentInstruction() + let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) + if szTypeInfo != '' + let result = omni#cpp#utils#CreateTypeInfo(szTypeInfo) + endif + endif + + call setpos('.', originalPos) + + return result +endfunc + +" Search a declaration +" @return +" - tokens of the current instruction if success +" - empty list if failure +function! s:SearchDecl(szVariable) + let result = {} + let originalPos = getpos('.') + let searchResult = s:LocalSearchDecl(a:szVariable) + if searchResult==0 + " searchdecl() may detect a decl if the variable is in a conditional + " instruction (if, elseif, while etc...) + " We have to check if the detected decl is really a decl instruction + let tokens = omni#cpp#utils#TokenizeCurrentInstruction() + + for token in tokens + " Simple test + if index(['if', 'elseif', 'while', 'for', 'switch'], token.value)>=0 + " Invalid declaration instruction + call setpos('.', originalPos) + return result + endif + endfor + + let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) + if szTypeInfo != '' + let result = omni#cpp#utils#CreateTypeInfo(szTypeInfo) + endif + endif + call setpos('.', originalPos) + return result +endfunc + +" Extract the type info string from an instruction. +" We use a small parser to extract the type +" We parse the code according to a C++ BNF from: http://www.nongnu.org/hcb/#basic.link +" @param tokens: token list of the current instruction +function! s:ExtractTypeInfoFromDecl(tokens) + return omni#cpp#utils#ExtractTypeInfoFromTokens(a:tokens) +endfunc + +" Convert tokens to string +function! s:TokensToString(tokens) + let result = '' + for token in a:tokens + let result = result . token.value . ' ' + endfor + return result[:-2] +endfunc + +" Resolve a cast. +" Resolve a C++ cast +" @param list of token. tokens must be a list that represents +" a cast expression (C++ cast) the function does not control +" if it's a cast or not +" eg: static_cast(something) +" @return type info string +function! s:ResolveCppCast(tokens) + return omni#cpp#utils#ExtractTypeInfoFromTokens(s:ResolveCast(a:tokens, '<', '>')) +endfunc + +" Resolve a cast. +" Resolve a C cast +" @param list of token. tokens must be a list that represents +" a cast expression (C cast) the function does not control +" if it's a cast or not +" eg: (MyClass*)something +" @return type info string +function! s:ResolveCCast(tokens) + return omni#cpp#utils#ExtractTypeInfoFromTokens(s:ResolveCast(a:tokens, '(', ')')) +endfunc + +" Resolve a cast. +" Resolve a C cast +" @param list of token. tokens must be a list that represents +" a cast expression (C cast) the function does not control +" if it's a cast or not +" eg: (MyClass*)something +" @return type tokens +function! s:ResolveCast(tokens, startChar, endChar) + let tokens = omni#cpp#utils#BuildParenthesisGroups(a:tokens) + + " We remove useless parenthesis eg: (((MyClass))) + let tokens = omni#cpp#utils#SimplifyParenthesis(tokens) + + let countItem=0 + let startIndex = -1 + let endIndex = -1 + let i = 0 + for token in tokens + if startIndex==-1 + if token.value==a:startChar + let countItem += 1 + let startIndex = i + endif + else + if token.value==a:startChar + let countItem += 1 + elseif token.value==a:endChar + let countItem -= 1 + endif + + if countItem==0 + let endIndex = i + break + endif + endif + let i+=1 + endfor + + return tokens[startIndex+1 : endIndex-1] +endfunc + +" Replacement for build-in function 'searchdecl' +" It does not require that the upper-level bracket is in the first column. +" Otherwise it should be equal to 'searchdecl(name, 0, 1)' +" @param name: name of variable to find declaration for +function! s:LocalSearchDecl(name) + + if g:OmniCpp_LocalSearchDecl == 0 + let bUserIgnoreCase = &ignorecase + + " Forcing the noignorecase option + " avoid bug when, for example, if we have a declaration like this : "A a;" + set noignorecase + + let result = searchdecl(a:name, 0, 1) + + " Restoring user's setting + let &ignorecase = bUserIgnoreCase + + return result + endif + + let lastpos = getpos('.') + let winview = winsaveview() + let lastfoldenable = &foldenable + let &foldenable = 0 + + " We add \C (noignorecase) to + " avoid bug when, for example, if we have a declaration like this : "A a;" + let varname = "\\C\\<" . a:name . "\\>" + + " Go to first blank line before begin of highest scope + normal 99[{ + let scopepos = getpos('.') + while (line('.') > 1) && (len(split(getline('.'))) > 0) + call cursor(line('.')-1, 0) + endwhile + + let declpos = [ 0, 0, 0, 0 ] + while search(varname, '', scopepos[1]) > 0 + " Check if we are a string or a comment + if omni#cpp#utils#IsCursorInCommentOrString() + continue + endif + + " Remember match + let declpos = getpos('.') + endwhile + if declpos[1] != 0 + " We found a match + call winrestview(winview) + call setpos('.', declpos) + let &foldenable = lastfoldenable + return 0 + endif + + while search(varname, '', lastpos[1]) > 0 + " Check if current scope is ending before variable + let old_cur = getpos('.') + normal ]} + let new_cur = getpos('.') + call setpos('.', old_cur) + if (new_cur[1] < lastpos[1]) || ((new_cur[1] == lastpos[1]) && (new_cur[2] < lastpos[2])) + continue + endif + + " Check if we are a string or a comment + if omni#cpp#utils#IsCursorInCommentOrString() + continue + endif + + " We found match + call winrestview(winview) + call setpos('.', old_cur) + let &foldenable = lastfoldenable + return 0 + endwhile + + " No match found. + call winrestview(winview) + let &foldenable = lastfoldenable + return 1 +endfunc diff --git a/.vim/autoload/omni/cpp/maycomplete.vim b/.vim/autoload/omni/cpp/maycomplete.vim new file mode 100644 index 0000000..610526b --- /dev/null +++ b/.vim/autoload/omni/cpp/maycomplete.vim @@ -0,0 +1,82 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +" Check if we can use omni completion in the current buffer +function! s:CanUseOmnicompletion() + " For C and C++ files and only if the omnifunc is omni#cpp#complete#Main + return (index(['c', 'cpp'], &filetype)>=0 && &omnifunc == 'omni#cpp#complete#Main' && !omni#cpp#utils#IsCursorInCommentOrString()) +endfunc + +" Return the mapping of omni completion +function! omni#cpp#maycomplete#Complete() + let szOmniMapping = "\\" + + " 0 = don't select first item + " 1 = select first item (inserting it to the text, default vim behaviour) + " 2 = select first item (without inserting it to the text) + if g:OmniCpp_SelectFirstItem == 0 + " We have to force the menuone option to avoid confusion when there is + " only one popup item + set completeopt-=menu + set completeopt+=menuone + let szOmniMapping .= "\" + elseif g:OmniCpp_SelectFirstItem == 2 + " We have to force the menuone option to avoid confusion when there is + " only one popup item + set completeopt-=menu + set completeopt+=menuone + let szOmniMapping .= "\" + let szOmniMapping .= "\=pumvisible() ? \"\\\" : \"\"\" + endif + return szOmniMapping +endfunc + +" May complete function for dot +function! omni#cpp#maycomplete#Dot() + if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteDot + let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction('.')) + if len(g:omni#cpp#items#data) + let s:bMayComplete = 1 + return '.' . omni#cpp#maycomplete#Complete() + endif + endif + return '.' +endfunc +" May complete function for arrow +function! omni#cpp#maycomplete#Arrow() + if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteArrow + let index = col('.') - 2 + if index >= 0 + let char = getline('.')[index] + if char == '-' + let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction('>')) + if len(g:omni#cpp#items#data) + let s:bMayComplete = 1 + return '>' . omni#cpp#maycomplete#Complete() + endif + endif + endif + endif + return '>' +endfunc + +" May complete function for double points +function! omni#cpp#maycomplete#Scope() + if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteScope + let index = col('.') - 2 + if index >= 0 + let char = getline('.')[index] + if char == ':' + let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction(':')) + if len(g:omni#cpp#items#data) + if len(g:omni#cpp#items#data[-1].tokens) && g:omni#cpp#items#data[-1].tokens[-1].value != '::' + let s:bMayComplete = 1 + return ':' . omni#cpp#maycomplete#Complete() + endif + endif + endif + endif + endif + return ':' +endfunc diff --git a/.vim/autoload/omni/cpp/namespaces.vim b/.vim/autoload/omni/cpp/namespaces.vim new file mode 100644 index 0000000..386b3f9 --- /dev/null +++ b/.vim/autoload/omni/cpp/namespaces.vim @@ -0,0 +1,838 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +let g:omni#cpp#namespaces#CacheResolve = {} +let g:omni#cpp#namespaces#CacheUsing = {} +" TODO: For the next release +"let g:omni#cpp#namespaces#CacheAlias = {} + +" Get the using namespace list from a line +function! s:GetNamespaceAliasListFromLine(szLine) + let result = {} + let tokens = omni#cpp#tokenizer#Tokenize(a:szLine) + let szAlias = '' + let szNamespace = '' + let state = 0 + for token in tokens + if state==0 + let szAlias = '' + let szNamespace = '' + if token.value == '/*' + let state = 1 + elseif token.value == '//' + " It's a comment + let state = -1 + break + elseif token.value == 'namespace' + let state = 2 + endif + elseif state==1 + if token.value == '*/' + let state=0 + endif + elseif state==2 + if token.kind == 'cppWord' + let szAlias .= token.value + let state = 3 + else + let state = -1 + break + endif + elseif state == 3 + if token.value == '=' + let state = 4 + else + let state = -1 + break + endif + elseif state == 4 + if token.value == '::' + let szNamespace .= token.value + let state = 5 + elseif token.kind == 'cppWord' + let szNamespace .= token.value + let state = 6 + " Maybe end of tokens + endif + elseif state==5 + if token.kind == 'cppWord' + let szNamespace .= token.value + let state = 6 + " Maybe end of tokens + else + " Error, we can't have 'namespace ALIAS = Something::' + let state = -1 + break + endif + elseif state==6 + if token.value == '::' + let szNamespace .= token.value + let state = 5 + else + call extend(result, {szAlias : szNamespace}) + let state = 0 + endif + endif + endfor + + if state == 6 + call extend(result, {szAlias : szNamespace}) + endif + + return result +endfunc + +" Get the using namespace list from a line +function! s:GetNamespaceListFromLine(szLine) + let result = [] + let tokens = omni#cpp#tokenizer#Tokenize(a:szLine) + let szNamespace = '' + let state = 0 + for token in tokens + if state==0 + let szNamespace = '' + if token.value == '/*' + let state = 1 + elseif token.value == '//' + " It's a comment + let state = -1 + break + elseif token.value == 'using' + let state = 2 + endif + elseif state==1 + if token.value == '*/' + let state=0 + endif + elseif state==2 + if token.value == 'namespace' + let state = 3 + else + " Error, 'using' must be followed by 'namespace' + let state = -1 + break + endif + elseif state==3 + if token.value == '::' + let szNamespace .= token.value + let state = 4 + elseif token.kind == 'cppWord' + let szNamespace .= token.value + let state = 5 + " Maybe end of tokens + endif + elseif state==4 + if token.kind == 'cppWord' + let szNamespace .= token.value + let state = 5 + " Maybe end of tokens + else + " Error, we can't have 'using namespace Something::' + let state = -1 + break + endif + elseif state==5 + if token.value == '::' + let szNamespace .= token.value + let state = 4 + else + call extend(result, [szNamespace]) + let state = 0 + endif + endif + endfor + + if state == 5 + call extend(result, [szNamespace]) + endif + + return result +endfunc + +" Get the namespace list from a namespace map +function! s:GetUsingNamespaceListFromMap(namespaceMap, ...) + let stopLine = 0 + if a:0>0 + let stopLine = a:1 + endif + + let result = [] + let keys = sort(keys(a:namespaceMap), 'omni#common#utils#CompareNumber') + for i in keys + if stopLine != 0 && i > stopLine + break + endif + call extend(result, a:namespaceMap[i]) + endfor + return result +endfunc + +" Get global using namespace list from the current buffer +function! omni#cpp#namespaces#GetListFromCurrentBuffer(...) + let namespaceMap = s:GetAllUsingNamespaceMapFromCurrentBuffer() + let result = [] + if namespaceMap != {} + let result = s:GetUsingNamespaceListFromMap(namespaceMap, (a:0 > 0)? a:1 : line('.')) + endif + return result +endfunc + +" Get global using namespace map from the current buffer and include files recursively +function! s:GetAllUsingNamespaceMapFromCurrentBuffer(...) + let includeGuard = (a:0>0)? a:1 : {} + + let szBufferName = getreg("%") + let szFilePath = omni#cpp#utils#ResolveFilePath(szBufferName) + let szFilePath = (szFilePath=='')? szBufferName : szFilePath + + let namespaceMap = {} + if has_key(includeGuard, szFilePath) + return namespaceMap + else + let includeGuard[szFilePath] = 1 + endif + + let namespaceMap = omni#cpp#namespaces#GetMapFromCurrentBuffer() + + if g:OmniCpp_NamespaceSearch != 2 + " We don't search included files if OmniCpp_NamespaceSearch != 2 + return namespaceMap + endif + + for inc in omni#cpp#includes#GetList() + let lnum = inc.pos[0] + let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard) + if tmpMap != {} + if has_key(namespaceMap, lnum) + call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap)) + else + let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap) + endif + endif + endfor + + return namespaceMap +endfunc + +" Get global using namespace map from a file and include files recursively +function! s:GetAllUsingNamespaceMapFromFile(szFilePath, ...) + let includeGuard = {} + if a:0 >0 + let includeGuard = a:1 + endif + + let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) + let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath + + let namespaceMap = {} + if has_key(includeGuard, szFilePath) + return namespaceMap + else + let includeGuard[szFilePath] = 1 + endif + + " If g:OmniCpp_NamespaceSearch == 1 (search namespaces only in the current + " buffer) we don't use cache for the current buffer + let namespaceMap = omni#cpp#namespaces#GetMapFromBuffer(szFilePath, g:OmniCpp_NamespaceSearch==1) + + if g:OmniCpp_NamespaceSearch != 2 + " We don't search included files if OmniCpp_NamespaceSearch != 2 + return namespaceMap + endif + + for inc in omni#cpp#includes#GetList(szFilePath) + let lnum = inc.pos[0] + let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard) + if tmpMap != {} + if has_key(namespaceMap, lnum) + call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap)) + else + let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap) + endif + endif + endfor + + return namespaceMap +endfunc + +" Get global using namespace map from a the current buffer +function! omni#cpp#namespaces#GetMapFromCurrentBuffer() + let namespaceMap = {} + let originalPos = getpos('.') + + call setpos('.', [0, 1, 1, 0]) + let curPos = [1,1] + while curPos != [0,0] + let curPos = searchpos('\C^using\s\+namespace', 'W') + if curPos != [0,0] + let szLine = getline('.') + let startPos = curPos[1] + let endPos = match(szLine, ';', startPos-1) + if endPos!=-1 + " We get the namespace list from the line + let namespaceMap[curPos[0]] = s:GetNamespaceListFromLine(szLine) + endif + endif + endwhile + + call setpos('.', originalPos) + return namespaceMap +endfunc + +" Get global using namespace map from a file +function! omni#cpp#namespaces#GetMapFromBuffer(szFilePath, ...) + let bUpdate = 0 + if a:0 > 0 + let bUpdate = a:1 + endif + + let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) + let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath + + if !bUpdate && has_key(g:omni#cpp#namespaces#CacheUsing, szFilePath) + return copy(g:omni#cpp#namespaces#CacheUsing[szFilePath]) + endif + + let namespaceMap = {} + " The file exists, we get the global namespaces in this file + let szFixedPath = escape(szFilePath, g:omni#cpp#utils#szEscapedCharacters) + execute 'silent! lvimgrep /\C^using\s\+namespace/gj '.szFixedPath + + " key = line number + " value = list of namespaces + let listQuickFix = getloclist(0) + for qf in listQuickFix + let szLine = qf.text + let startPos = qf.col + let endPos = match(szLine, ';', startPos-1) + if endPos!=-1 + " We get the namespace list from the line + let namespaceMap[qf.lnum] = s:GetNamespaceListFromLine(szLine) + endif + endfor + + if szFixedPath != '' + let g:omni#cpp#namespaces#CacheUsing[szFixedPath] = namespaceMap + endif + + return copy(namespaceMap) +endfunc + +" Get the stop position when searching for local variables +function! s:GetStopPositionForLocalSearch() + " Stop position when searching a local variable + let originalPos = getpos('.') + let origPos = originalPos[1:2] + let stopPosition = origPos + let curPos = origPos + while curPos !=[0,0] + let stopPosition = curPos + let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) + endwhile + call setpos('.', originalPos) + + return stopPosition +endfunc + +" Get namespaces alias used at the cursor postion in a vim buffer +" Note: The result depends on the current cursor position +" @return +" - Map of namespace alias +function! s:GetNamespaceAliasMap() + " We store the cursor position because searchpairpos() moves the cursor + let result = {} + let originalPos = getpos('.') + let origPos = originalPos[1:2] + + let stopPos = s:GetStopPositionForLocalSearch() + let stopLine = stopPos[0] + let curPos = origPos + let lastLine = 0 + let nextStopLine = origPos[0] + let szReAlias = '\Cnamespace\s\+\w\+\s\+=' + while curPos !=[0,0] + let curPos = searchpos('}\|\('. szReAlias .'\)', 'bW',stopLine) + if curPos!=[0,0] && curPos[0]!=lastLine + let lastLine = curPos[0] + + let szLine = getline('.') + if origPos[0] == curPos[0] + " We get the line until cursor position + let szLine = szLine[:origPos[1]] + endif + + let szLine = omni#cpp#utils#GetCodeFromLine(szLine) + if match(szLine, szReAlias)<0 + " We found a '}' + let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) + else + " We get the namespace alias from the line + call extend(result, s:GetNamespaceAliasListFromLine(szLine)) + let nextStopLine = curPos[0] + endif + endif + endwhile + + " Setting the cursor to the original position + call setpos('.', originalPos) + + call s:ResolveAliasKeys(result) + return result +endfunc + +" Resolve an alias +" eg: namespace IAmAnAlias1 = Ns1 +" eg: namespace IAmAnAlias2 = IAmAnAlias1::Ns2 +" => IAmAnAlias2 = Ns1::Ns2 +function! s:ResolveAliasKey(mapNamespaceAlias, szAlias) + let szResult = a:mapNamespaceAlias[a:szAlias] + " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3'] + let listNamespace = split(szResult, '::') + if len(listNamespace) + " szBeginPart = 'Ns1' + let szBeginPart = remove(listNamespace, 0) + + " Is 'Ns1' an alias ? + if has_key(a:mapNamespaceAlias, szBeginPart) && szBeginPart != a:szAlias + " Resolving alias 'Ns1' + " eg: Ns1 = NsResolved + let szResult = s:ResolveAliasKey(a:mapNamespaceAlias, szBeginPart) + " szEndPart = 'Ns2::Ns3' + let szEndPart = join(listNamespace, '::') + if szEndPart != '' + " Concatenation => szResult = 'NsResolved::Ns2::Ns3' + let szResult .= '::' . szEndPart + endif + endif + endif + return szResult +endfunc + +" Resolve all keys in the namespace alias map +function! s:ResolveAliasKeys(mapNamespaceAlias) + let mapNamespaceAlias = a:mapNamespaceAlias + call map(mapNamespaceAlias, 's:ResolveAliasKey(mapNamespaceAlias, v:key)') +endfunc + +" Resolve namespace alias +function! omni#cpp#namespaces#ResolveAlias(mapNamespaceAlias, szNamespace) + let szResult = a:szNamespace + " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3'] + let listNamespace = split(a:szNamespace, '::') + if len(listNamespace) + " szBeginPart = 'Ns1' + let szBeginPart = remove(listNamespace, 0) + + " Is 'Ns1' an alias ? + if has_key(a:mapNamespaceAlias, szBeginPart) + " Resolving alias 'Ns1' + " eg: Ns1 = NsResolved + let szResult = a:mapNamespaceAlias[szBeginPart] + " szEndPart = 'Ns2::Ns3' + let szEndPart = join(listNamespace, '::') + if szEndPart != '' + " Concatenation => szResult = 'NsResolved::Ns2::Ns3' + let szResult .= '::' . szEndPart + endif + + " If a:szNamespace starts with '::' we add '::' to the beginning + " of the result + if match(a:szNamespace, '^::')>=0 + let szResult = omni#cpp#utils#SimplifyScope('::' . szResult) + endif + endif + endif + return szResult +endfunc + +" Resolve namespace alias +function! s:ResolveAliasInNamespaceList(mapNamespaceAlias, listNamespaces) + call map(a:listNamespaces, 'omni#cpp#namespaces#ResolveAlias(a:mapNamespaceAlias, v:val)') +endfunc + +" Get namespaces used at the cursor postion in a vim buffer +" Note: The result depends on the current cursor position +" @return +" - List of namespace used in the reverse order +function! omni#cpp#namespaces#GetUsingNamespaces() + " We have to get local using namespace declarations + " We need the current cursor position and the position of the start of the + " current scope + + " We store the cursor position because searchpairpos() moves the cursor + let result = [] + let originalPos = getpos('.') + let origPos = originalPos[1:2] + + let stopPos = s:GetStopPositionForLocalSearch() + + let stopLine = stopPos[0] + let curPos = origPos + let lastLine = 0 + let nextStopLine = origPos[0] + while curPos !=[0,0] + let curPos = searchpos('\C}\|\(using\s\+namespace\)', 'bW',stopLine) + if curPos!=[0,0] && curPos[0]!=lastLine + let lastLine = curPos[0] + + let szLine = getline('.') + if origPos[0] == curPos[0] + " We get the line until cursor position + let szLine = szLine[:origPos[1]] + endif + + let szLine = omni#cpp#utils#GetCodeFromLine(szLine) + if match(szLine, '\Cusing\s\+namespace')<0 + " We found a '}' + let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) + else + " We get the namespace list from the line + let result = s:GetNamespaceListFromLine(szLine) + result + let nextStopLine = curPos[0] + endif + endif + endwhile + + " Setting the cursor to the original position + call setpos('.', originalPos) + + " 2) Now we can get all global using namespace declaration from the + " beginning of the file to nextStopLine + let result = omni#cpp#namespaces#GetListFromCurrentBuffer(nextStopLine) + result + + " Resolving alias in the namespace list + " TODO: For the next release + "let g:omni#cpp#namespaces#CacheAlias= s:GetNamespaceAliasMap() + "call s:ResolveAliasInNamespaceList(g:omni#cpp#namespaces#CacheAlias, result) + + return ['::'] + result +endfunc + +" Resolve a using namespace regarding the current context +" For each namespace used: +" - We get all possible contexts where the namespace +" can be define +" - We do a comparison test of each parent contexts with the current +" context list +" - If one and only one parent context is present in the +" current context list we add the namespace in the current +" context +" - If there is more than one of parent contexts in the +" current context the namespace is ambiguous +" @return +" - result item +" - kind = 0|1 +" - 0 = unresolved or error +" - 1 = resolved +" - value = resolved namespace +function! s:ResolveNamespace(namespace, mapCurrentContexts) + let result = {'kind':0, 'value': ''} + + " If the namespace is already resolved we add it in the list of + " current contexts + if match(a:namespace, '^::')>=0 + let result.kind = 1 + let result.value = a:namespace + return result + elseif match(a:namespace, '\w\+::\w\+')>=0 + let mapCurrentContextsTmp = copy(a:mapCurrentContexts) + let resolvedItem = {} + for nsTmp in split(a:namespace, '::') + let resolvedItem = s:ResolveNamespace(nsTmp, mapCurrentContextsTmp) + if resolvedItem.kind + " Note: We don't extend the map + let mapCurrentContextsTmp = {resolvedItem.value : 1} + else + break + endif + endfor + if resolvedItem!={} && resolvedItem.kind + let result.kind = 1 + let result.value = resolvedItem.value + endif + return result + endif + + " We get all possible parent contexts of this namespace + let listTagsOfNamespace = [] + if has_key(g:omni#cpp#namespaces#CacheResolve, a:namespace) + let listTagsOfNamespace = g:omni#cpp#namespaces#CacheResolve[a:namespace] + else + let listTagsOfNamespace = omni#common#utils#TagList('^'.a:namespace.'$') + let g:omni#cpp#namespaces#CacheResolve[a:namespace] = listTagsOfNamespace + endif + + if len(listTagsOfNamespace)==0 + return result + endif + call filter(listTagsOfNamespace, 'v:val.kind[0]=="n"') + + " We extract parent context from tags + " We use a map to avoid multiple entries + let mapContext = {} + for tagItem in listTagsOfNamespace + let szParentContext = omni#cpp#utils#ExtractScope(tagItem) + let mapContext[szParentContext] = 1 + endfor + let listParentContext = keys(mapContext) + + " Now for each parent context we test if the context is in the current + " contexts list + let listResolvedNamespace = [] + for szParentContext in listParentContext + if has_key(a:mapCurrentContexts, szParentContext) + call extend(listResolvedNamespace, [omni#cpp#utils#SimplifyScope(szParentContext.'::'.a:namespace)]) + endif + endfor + + " Now we know if the namespace is ambiguous or not + let len = len(listResolvedNamespace) + if len==1 + " Namespace resolved + let result.kind = 1 + let result.value = listResolvedNamespace[0] + elseif len > 1 + " Ambiguous namespace, possible matches are in listResolvedNamespace + else + " Other cases + endif + return result +endfunc + +" Resolve namespaces +"@return +" - List of resolved namespaces +function! omni#cpp#namespaces#ResolveAll(namespacesUsed) + + " We add the default context '::' + let contextOrder = 0 + let mapCurrentContexts = {} + + " For each namespace used: + " - We get all possible contexts where the namespace + " can be define + " - We do a comparison test of each parent contexts with the current + " context list + " - If one and only one parent context is present in the + " current context list we add the namespace in the current + " context + " - If there is more than one of parent contexts in the + " current context the namespace is ambiguous + for ns in a:namespacesUsed + let resolvedItem = s:ResolveNamespace(ns, mapCurrentContexts) + if resolvedItem.kind + let contextOrder+=1 + let mapCurrentContexts[resolvedItem.value] = contextOrder + endif + endfor + + " Build the list of current contexts from the map, we have to keep the + " order + let mapReorder = {} + for key in keys(mapCurrentContexts) + let mapReorder[ mapCurrentContexts[key] ] = key + endfor + let result = [] + for key in sort(keys(mapReorder)) + call extend(result, [mapReorder[key]]) + endfor + return result +endfunc + +" Build the context stack +function! s:BuildContextStack(namespaces, szCurrentScope) + let result = copy(a:namespaces) + if a:szCurrentScope != '::' + let tagItem = omni#cpp#utils#GetResolvedTagItem(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope)) + if has_key(tagItem, 'inherits') + let listBaseClass = omni#cpp#utils#GetClassInheritanceList(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope)) + let result = listBaseClass + result + elseif has_key(tagItem, 'kind') && index(['c', 's', 'u', 'n'], tagItem.kind[0])>=0 + call insert(result, omni#cpp#utils#ExtractTypeInfoFromTag(tagItem)) + endif + endif + return result +endfunc + +" Returns the class scope at the current position of the cursor +" @return a string that represents the class scope +" eg: ::NameSpace1::Class1 +" The returned string always starts with '::' +" Note: In term of performance it's the weak point of the script +function! s:GetClassScopeAtCursor() + " We store the cursor position because searchpairpos() moves the cursor + let originalPos = getpos('.') + let endPos = originalPos[1:2] + let listCode = [] + let result = {'namespaces': [], 'scope': ''} + + while endPos!=[0,0] + let endPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) + let szReStartPos = '[;{}]\|\%^' + let startPos = searchpairpos(szReStartPos, '', '{', 'bWn', g:omni#cpp#utils#expIgnoreComments) + + " If the file starts with a comment so the startPos can be [0,0] + " we change it to [1,1] + if startPos==[0,0] + let startPos = [1,1] + endif + + " Get lines backward from cursor position to last ; or { or } + " or when we are at the beginning of the file. + " We store lines in listCode + if endPos!=[0,0] + " We remove the last character which is a '{' + " We also remove starting { or } or ; if exits + let szCodeWithoutComments = substitute(omni#cpp#utils#GetCode(startPos, endPos)[:-2], '^[;{}]', '', 'g') + call insert(listCode, {'startLine' : startPos[0], 'code' : szCodeWithoutComments}) + endif + endwhile + " Setting the cursor to the original position + call setpos('.', originalPos) + + let listClassScope = [] + let bResolved = 0 + let startLine = 0 + " Now we can check in the list of code if there is a function + for code in listCode + " We get the name of the namespace, class, struct or union + " and we store it in listClassScope + let tokens = omni#cpp#tokenizer#Tokenize(code.code) + let bContinue=0 + let bAddNamespace = 0 + let state=0 + for token in tokens + if state==0 + if index(['namespace', 'class', 'struct', 'union'], token.value)>=0 + if token.value == 'namespace' + let bAddNamespace = 1 + endif + let state= 1 + " Maybe end of tokens + endif + elseif state==1 + if token.kind == 'cppWord' + " eg: namespace MyNs { class MyCl {}; } + " => listClassScope = [MyNs, MyCl] + call extend( listClassScope , [token.value] ) + + " Add the namespace in result + if bAddNamespace + call extend(result.namespaces, [token.value]) + let bAddNamespace = 0 + endif + + let bContinue=1 + break + endif + endif + endfor + if bContinue==1 + continue + endif + + " Simple test to check if we have a chance to find a + " class method + let aPos = matchend(code.code, '::\s*\~*\s*\w\+\s*(') + if aPos ==-1 + continue + endif + + let startLine = code.startLine + let listTmp = [] + " eg: 'void MyNamespace::MyClass::foo(' + " => tokens = ['MyClass', '::', 'MyNamespace', 'void'] + let tokens = reverse(omni#cpp#tokenizer#Tokenize(code.code[:aPos-1])[:-4]) + let state = 0 + " Reading tokens backward + for token in tokens + if state==0 + if token.kind=='cppWord' + call insert(listTmp, token.value) + let state=1 + endif + elseif state==1 + if token.value=='::' + let state=2 + else + break + endif + elseif state==2 + if token.kind=='cppWord' + call insert(listTmp, token.value) + let state=1 + else + break + endif + endif + endfor + + if len(listTmp) + if len(listClassScope) + let bResolved = 1 + " Merging class scopes + " eg: current class scope = 'MyNs::MyCl1' + " method class scope = 'MyCl1::MyCl2' + " If we add the method class scope to current class scope + " we'll have MyNs::MyCl1::MyCl1::MyCl2 => it's wrong + " we want MyNs::MyCl1::MyCl2 + let index = 0 + for methodClassScope in listTmp + if methodClassScope==listClassScope[-1] + let listTmp = listTmp[index+1:] + break + else + let index+=1 + endif + endfor + endif + call extend(listClassScope, listTmp) + break + endif + endfor + + let szClassScope = '::' + if len(listClassScope) + if bResolved + let szClassScope .= join(listClassScope, '::') + else + let szClassScope = join(listClassScope, '::') + + " The class scope is not resolved, we have to check using + " namespace declarations and search the class scope in each + " namespace + if startLine != 0 + let namespaces = ['::'] + omni#cpp#namespaces#GetListFromCurrentBuffer(startLine) + let namespaces = omni#cpp#namespaces#ResolveAll(namespaces) + let tagItem = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(szClassScope)) + if tagItem != {} + let szClassScope = omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) + endif + endif + endif + endif + + let result.scope = szClassScope + return result +endfunc + +" Get all contexts at the cursor position +function! omni#cpp#namespaces#GetContexts() + " Get the current class scope at the cursor, the result depends on the current cursor position + let scopeItem = s:GetClassScopeAtCursor() + let listUsingNamespace = copy(g:OmniCpp_DefaultNamespaces) + call extend(listUsingNamespace, scopeItem.namespaces) + if g:OmniCpp_NamespaceSearch && &filetype != 'c' + " Get namespaces used in the file until the cursor position + let listUsingNamespace = omni#cpp#namespaces#GetUsingNamespaces() + listUsingNamespace + " Resolving namespaces, removing ambiguous namespaces + let namespaces = omni#cpp#namespaces#ResolveAll(listUsingNamespace) + else + let namespaces = ['::'] + listUsingNamespace + endif + call reverse(namespaces) + + " Building context stack from namespaces and the current class scope + return s:BuildContextStack(namespaces, scopeItem.scope) +endfunc diff --git a/.vim/autoload/omni/cpp/settings.vim b/.vim/autoload/omni/cpp/settings.vim new file mode 100644 index 0000000..6683d3a --- /dev/null +++ b/.vim/autoload/omni/cpp/settings.vim @@ -0,0 +1,96 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +function! omni#cpp#settings#Init() + " Global scope search on/off + " 0 = disabled + " 1 = enabled + if !exists('g:OmniCpp_GlobalScopeSearch') + let g:OmniCpp_GlobalScopeSearch = 1 + endif + + " Sets the namespace search method + " 0 = disabled + " 1 = search namespaces in the current file + " 2 = search namespaces in the current file and included files + if !exists('g:OmniCpp_NamespaceSearch') + let g:OmniCpp_NamespaceSearch = 1 + endif + + " Set the class scope completion mode + " 0 = auto + " 1 = show all members (static, public, protected and private) + if !exists('g:OmniCpp_DisplayMode') + let g:OmniCpp_DisplayMode = 0 + endif + + " Set if the scope is displayed in the abbr column of the popup + " 0 = no + " 1 = yes + if !exists('g:OmniCpp_ShowScopeInAbbr') + let g:OmniCpp_ShowScopeInAbbr = 0 + endif + + " Set if the function prototype is displayed in the abbr column of the popup + " 0 = no + " 1 = yes + if !exists('g:OmniCpp_ShowPrototypeInAbbr') + let g:OmniCpp_ShowPrototypeInAbbr = 0 + endif + + " Set if the access (+,#,-) is displayed + " 0 = no + " 1 = yes + if !exists('g:OmniCpp_ShowAccess') + let g:OmniCpp_ShowAccess = 1 + endif + + " Set the list of default namespaces + " eg: ['std'] + if !exists('g:OmniCpp_DefaultNamespaces') + let g:OmniCpp_DefaultNamespaces = [] + endif + + " Set MayComplete to '.' + " 0 = disabled + " 1 = enabled + " default = 1 + if !exists('g:OmniCpp_MayCompleteDot') + let g:OmniCpp_MayCompleteDot = 1 + endif + + " Set MayComplete to '->' + " 0 = disabled + " 1 = enabled + " default = 1 + if !exists('g:OmniCpp_MayCompleteArrow') + let g:OmniCpp_MayCompleteArrow = 1 + endif + + " Set MayComplete to dot + " 0 = disabled + " 1 = enabled + " default = 0 + if !exists('g:OmniCpp_MayCompleteScope') + let g:OmniCpp_MayCompleteScope = 0 + endif + + " When completeopt does not contain longest option, this setting + " controls the behaviour of the popup menu selection when starting the completion + " 0 = don't select first item + " 1 = select first item (inserting it to the text) + " 2 = select first item (without inserting it to the text) + " default = 0 + if !exists('g:OmniCpp_SelectFirstItem') + let g:OmniCpp_SelectFirstItem= 0 + endif + + " Use local search function for variable definitions + " 0 = use standard vim search function + " 1 = use local search function + " default = 0 + if !exists('g:OmniCpp_LocalSearchDecl') + let g:OmniCpp_LocalSearchDecl= 0 + endif +endfunc diff --git a/.vim/autoload/omni/cpp/tokenizer.vim b/.vim/autoload/omni/cpp/tokenizer.vim new file mode 100644 index 0000000..16e0be2 --- /dev/null +++ b/.vim/autoload/omni/cpp/tokenizer.vim @@ -0,0 +1,93 @@ +" Description: Omni completion tokenizer +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 +" TODO: Generic behaviour for Tokenize() + +" From the C++ BNF +let s:cppKeyword = ['asm', 'auto', 'bool', 'break', 'case', 'catch', 'char', 'class', 'const', 'const_cast', 'continue', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'operator', 'private', 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed', 'sizeof', 'static', 'static_cast', 'struct', 'switch', 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'] + +let s:reCppKeyword = '\C\<'.join(s:cppKeyword, '\>\|\<').'\>' + +" The order of items in this list is very important because we use this list to build a regular +" expression (see below) for tokenization +let s:cppOperatorPunctuator = ['->*', '->', '--', '-=', '-', '!=', '!', '##', '#', '%:%:', '%=', '%>', '%:', '%', '&&', '&=', '&', '(', ')', '*=', '*', ',', '...', '.*', '.', '/=', '/', '::', ':>', ':', ';', '?', '[', ']', '^=', '^', '{', '||', '|=', '|', '}', '~', '++', '+=', '+', '<<=', '<%', '<:', '<<', '<=', '<', '==', '=', '>>=', '>>', '>=', '>'] + +" We build the regexp for the tokenizer +let s:reCComment = '\/\*\|\*\/' +let s:reCppComment = '\/\/' +let s:reComment = s:reCComment.'\|'.s:reCppComment +let s:reCppOperatorOrPunctuator = escape(join(s:cppOperatorPunctuator, '\|'), '*./^~[]') + + +" Tokenize a c++ code +" a token is dictionary where keys are: +" - kind = cppKeyword|cppWord|cppOperatorPunctuator|unknown|cComment|cppComment|cppDigit +" - value = 'something' +" Note: a cppWord is any word that is not a cpp keyword +function! omni#cpp#tokenizer#Tokenize(szCode) + let result = [] + + " The regexp to find a token, a token is a keyword, word or + " c++ operator or punctuator. To work properly we have to put + " spaces and tabs to our regexp. + let reTokenSearch = '\(\w\+\)\|\s\+\|'.s:reComment.'\|'.s:reCppOperatorOrPunctuator + " eg: 'using namespace std;' + " ^ ^ + " start=0 end=5 + let startPos = 0 + let endPos = matchend(a:szCode, reTokenSearch) + let len = endPos-startPos + while endPos!=-1 + " eg: 'using namespace std;' + " ^ ^ + " start=0 end=5 + " token = 'using' + " We also remove space and tabs + let token = substitute(strpart(a:szCode, startPos, len), '\s', '', 'g') + + " eg: 'using namespace std;' + " ^ ^ + " start=5 end=15 + let startPos = endPos + let endPos = matchend(a:szCode, reTokenSearch, startPos) + let len = endPos-startPos + + " It the token is empty we continue + if token=='' + continue + endif + + " Building the token + let resultToken = {'kind' : 'unknown', 'value' : token} + + " Classify the token + if token =~ '^\d\+' + " It's a digit + let resultToken.kind = 'cppDigit' + elseif token=~'^\w\+$' + " It's a word + let resultToken.kind = 'cppWord' + + " But maybe it's a c++ keyword + if match(token, s:reCppKeyword)>=0 + let resultToken.kind = 'cppKeyword' + endif + else + if match(token, s:reComment)>=0 + if index(['/*','*/'],token)>=0 + let resultToken.kind = 'cComment' + else + let resultToken.kind = 'cppComment' + endif + else + " It's an operator + let resultToken.kind = 'cppOperatorPunctuator' + endif + endif + + " We have our token, let's add it to the result list + call extend(result, [resultToken]) + endwhile + + return result +endfunc diff --git a/.vim/autoload/omni/cpp/utils.vim b/.vim/autoload/omni/cpp/utils.vim new file mode 100644 index 0000000..5d74d34 --- /dev/null +++ b/.vim/autoload/omni/cpp/utils.vim @@ -0,0 +1,587 @@ +" Description: Omni completion script for cpp files +" Maintainer: Vissale NEANG +" Last Change: 26 sept. 2007 + +let g:omni#cpp#utils#CACHE_TAG_INHERITS = {} +let g:omni#cpp#utils#szFilterGlobalScope = "(!has_key(v:val, 'class') && !has_key(v:val, 'struct') && !has_key(v:val, 'union') && !has_key(v:val, 'namespace')" +let g:omni#cpp#utils#szFilterGlobalScope .= "&& (!has_key(v:val, 'enum') || (has_key(v:val, 'enum') && v:val.enum =~ '^\\w\\+$')))" + +" Expression used to ignore comments +" Note: this expression drop drastically the performance +"let omni#cpp#utils#expIgnoreComments = 'match(synIDattr(synID(line("."), col("."), 1), "name"), '\CcComment')!=-1' +" This one is faster but not really good for C comments +let omni#cpp#utils#reIgnoreComment = escape('\/\/\|\/\*\|\*\/', '*/\') +let omni#cpp#utils#expIgnoreComments = 'getline(".") =~ g:omni#cpp#utils#reIgnoreComment' + +" Characters to escape in a filename for vimgrep +"TODO: Find more characters to escape +let omni#cpp#utils#szEscapedCharacters = ' %#' + +" Resolve the path of the file +" TODO: absolute file path +function! omni#cpp#utils#ResolveFilePath(szFile) + let result = '' + let listPath = split(globpath(&path, a:szFile), "\n") + if len(listPath) + let result = listPath[0] + endif + return simplify(result) +endfunc + +" Get code without comments and with empty strings +" szSingleLine must not have carriage return +function! omni#cpp#utils#GetCodeFromLine(szSingleLine) + " We set all strings to empty strings, it's safer for + " the next of the process + let szResult = substitute(a:szSingleLine, '".*"', '""', 'g') + + " Removing c++ comments, we can use the pattern ".*" because + " we are modifying a line + let szResult = substitute(szResult, '\/\/.*', '', 'g') + + " Now we have the entire code in one line and we can remove C comments + return s:RemoveCComments(szResult) +endfunc + +" Remove C comments on a line +function! s:RemoveCComments(szLine) + let result = a:szLine + + " We have to match the first '/*' and first '*/' + let startCmt = match(result, '\/\*') + let endCmt = match(result, '\*\/') + while startCmt!=-1 && endCmt!=-1 && startCmt0 + let result = result[ : startCmt-1 ] . result[ endCmt+2 : ] + else + " Case where '/*' is at the start of the line + let result = result[ endCmt+2 : ] + endif + let startCmt = match(result, '\/\*') + let endCmt = match(result, '\*\/') + endwhile + return result +endfunc + +" Get a c++ code from current buffer from [lineStart, colStart] to +" [lineEnd, colEnd] without c++ and c comments, without end of line +" and with empty strings if any +" @return a string +function! omni#cpp#utils#GetCode(posStart, posEnd) + let posStart = a:posStart + let posEnd = a:posEnd + if a:posStart[0]>a:posEnd[0] + let posStart = a:posEnd + let posEnd = a:posStart + elseif a:posStart[0]==a:posEnd[0] && a:posStart[1]>a:posEnd[1] + let posStart = a:posEnd + let posEnd = a:posStart + endif + + " Getting the lines + let lines = getline(posStart[0], posEnd[0]) + let lenLines = len(lines) + + " Formatting the result + let result = '' + if lenLines==1 + let sStart = posStart[1]-1 + let sEnd = posEnd[1]-1 + let line = lines[0] + let lenLastLine = strlen(line) + let sEnd = (sEnd>lenLastLine)?lenLastLine : sEnd + if sStart >= 0 + let result = omni#cpp#utils#GetCodeFromLine(line[ sStart : sEnd ]) + endif + elseif lenLines>1 + let sStart = posStart[1]-1 + let sEnd = posEnd[1]-1 + let lenLastLine = strlen(lines[-1]) + let sEnd = (sEnd>lenLastLine)?lenLastLine : sEnd + if sStart >= 0 + let lines[0] = lines[0][ sStart : ] + let lines[-1] = lines[-1][ : sEnd ] + for aLine in lines + let result = result . omni#cpp#utils#GetCodeFromLine(aLine)." " + endfor + let result = result[:-2] + endif + endif + + " Now we have the entire code in one line and we can remove C comments + return s:RemoveCComments(result) +endfunc + +" Extract the scope (context) of a tag item +" eg: ::MyNamespace +" @return a string of the scope. a scope from tag always starts with '::' +function! omni#cpp#utils#ExtractScope(tagItem) + let listKindScope = ['class', 'struct', 'union', 'namespace', 'enum'] + let szResult = '::' + for scope in listKindScope + if has_key(a:tagItem, scope) + let szResult = szResult . a:tagItem[scope] + break + endif + endfor + return szResult +endfunc + +" Simplify scope string, remove consecutive '::' if any +function! omni#cpp#utils#SimplifyScope(szScope) + let szResult = substitute(a:szScope, '\(::\)\+', '::', 'g') + if szResult=='::' + return szResult + else + return substitute(szResult, '::$', '', 'g') + endif +endfunc + +" Check if the cursor is in comment +function! omni#cpp#utils#IsCursorInCommentOrString() + return match(synIDattr(synID(line("."), col(".")-1, 1), "name"), '\C\=0 +endfunc + +" Tokenize the current instruction until the cursor position. +" @return list of tokens +function! omni#cpp#utils#TokenizeCurrentInstruction(...) + let szAppendText = '' + if a:0>0 + let szAppendText = a:1 + endif + + let startPos = searchpos('[;{}]\|\%^', 'bWn') + let curPos = getpos('.')[1:2] + " We don't want the character under the cursor + let column = curPos[1]-1 + let curPos[1] = (column<1)?1:column + return omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCode(startPos, curPos)[1:] . szAppendText) +endfunc + +" Tokenize the current instruction until the word under the cursor. +" @return list of tokens +function! omni#cpp#utils#TokenizeCurrentInstructionUntilWord() + let startPos = searchpos('[;{}]\|\%^', 'bWn') + + " Saving the current cursor pos + let originalPos = getpos('.') + + " We go at the end of the word + execute 'normal gee' + let curPos = getpos('.')[1:2] + + " Restoring the original cursor pos + call setpos('.', originalPos) + + let szCode = omni#cpp#utils#GetCode(startPos, curPos)[1:] + return omni#cpp#tokenizer#Tokenize(szCode) +endfunc + +" Build parenthesis groups +" add a new key 'group' in the token +" where value is the group number of the parenthesis +" eg: (void*)(MyClass*) +" group1 group0 +" if a parenthesis is unresolved the group id is -1 +" @return a copy of a:tokens with parenthesis group +function! omni#cpp#utils#BuildParenthesisGroups(tokens) + let tokens = copy(a:tokens) + let kinds = {'(': '()', ')' : '()', '[' : '[]', ']' : '[]', '<' : '<>', '>' : '<>', '{': '{}', '}': '{}'} + let unresolved = {'()' : [], '[]': [], '<>' : [], '{}' : []} + let groupId = 0 + + " Note: we build paren group in a backward way + " because we can often have parenthesis unbalanced + " instruction + " eg: doSomething(_member.get()-> + for token in reverse(tokens) + if index([')', ']', '>', '}'], token.value)>=0 + let token['group'] = groupId + call extend(unresolved[kinds[token.value]], [token]) + let groupId+=1 + elseif index(['(', '[', '<', '{'], token.value)>=0 + if len(unresolved[kinds[token.value]]) + let tokenResolved = remove(unresolved[kinds[token.value]], -1) + let token['group'] = tokenResolved.group + else + let token['group'] = -1 + endif + endif + endfor + + return reverse(tokens) +endfunc + +" Determine if tokens represent a C cast +" @return +" - itemCast +" - itemCppCast +" - itemVariable +" - itemThis +function! omni#cpp#utils#GetCastType(tokens) + " Note: a:tokens is not modified + let tokens = omni#cpp#utils#SimplifyParenthesis(omni#cpp#utils#BuildParenthesisGroups(a:tokens)) + + if tokens[0].value == '(' + return 'itemCast' + elseif index(['static_cast', 'dynamic_cast', 'reinterpret_cast', 'const_cast'], tokens[0].value)>=0 + return 'itemCppCast' + else + for token in tokens + if token.value=='this' + return 'itemThis' + endif + endfor + return 'itemVariable' + endif +endfunc + +" Remove useless parenthesis +function! omni#cpp#utils#SimplifyParenthesis(tokens) + "Note: a:tokens is not modified + let tokens = a:tokens + " We remove useless parenthesis eg: (((MyClass))) + if len(tokens)>2 + while tokens[0].value=='(' && tokens[-1].value==')' && tokens[0].group==tokens[-1].group + let tokens = tokens[1:-2] + endwhile + endif + return tokens +endfunc + +" Function create a type info +function! omni#cpp#utils#CreateTypeInfo(param) + let type = type(a:param) + return {'type': type, 'value':a:param} +endfunc + +" Extract type info from a tag item +" eg: ::MyNamespace::MyClass +function! omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) + let szTypeInfo = omni#cpp#utils#ExtractScope(a:tagItem) . '::' . substitute(a:tagItem.name, '.*::', '', 'g') + return omni#cpp#utils#SimplifyScope(szTypeInfo) +endfunc + +" Build a class inheritance list +function! omni#cpp#utils#GetClassInheritanceList(namespaces, typeInfo) + let result = [] + for tagItem in omni#cpp#utils#GetResolvedTags(a:namespaces, a:typeInfo) + call extend(result, [omni#cpp#utils#ExtractTypeInfoFromTag(tagItem)]) + endfor + return result +endfunc + +" Get class inheritance list where items in the list are tag items. +" TODO: Verify inheritance order +function! omni#cpp#utils#GetResolvedTags(namespaces, typeInfo) + let result = [] + let tagItem = omni#cpp#utils#GetResolvedTagItem(a:namespaces, a:typeInfo) + if tagItem!={} + let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) + if has_key(g:omni#cpp#utils#CACHE_TAG_INHERITS, szTypeInfo) + let result = g:omni#cpp#utils#CACHE_TAG_INHERITS[szTypeInfo] + else + call extend(result, [tagItem]) + if has_key(tagItem, 'inherits') + for baseClassTypeInfo in split(tagItem.inherits, ',') + let namespaces = [omni#cpp#utils#ExtractScope(tagItem), '::'] + call extend(result, omni#cpp#utils#GetResolvedTags(namespaces, omni#cpp#utils#CreateTypeInfo(baseClassTypeInfo))) + endfor + endif + let g:omni#cpp#utils#CACHE_TAG_INHERITS[szTypeInfo] = result + endif + endif + return result +endfunc + +" Get a tag item after a scope resolution and typedef resolution +function! omni#cpp#utils#GetResolvedTagItem(namespaces, typeInfo) + let typeInfo = {} + if type(a:typeInfo) == 1 + let typeInfo = omni#cpp#utils#CreateTypeInfo(a:typeInfo) + else + let typeInfo = a:typeInfo + endif + + let result = {} + if !omni#cpp#utils#IsTypeInfoValid(typeInfo) + return result + endif + + " Unnamed type case eg: '1::2' + if typeInfo.type == 4 + " Here there is no typedef or namespace to resolve, the tagInfo.value is a tag item + " representing a variable ('v') a member ('m') or a typedef ('t') and the typename is + " always in global scope + return typeInfo.value + endif + + " Named type case eg: 'MyNamespace::MyClass' + let szTypeInfo = omni#cpp#utils#GetTypeInfoString(typeInfo) + + " Resolving namespace alias + " TODO: For the next release + "let szTypeInfo = omni#cpp#namespaces#ResolveAlias(g:omni#cpp#namespaces#CacheAlias, szTypeInfo) + + if szTypeInfo=='::' + return result + endif + + " We can only get members of class, struct, union and namespace + let szTagFilter = "index(['c', 's', 'u', 'n', 't'], v:val.kind[0])>=0" + let szTagQuery = szTypeInfo + + if s:IsTypeInfoResolved(szTypeInfo) + " The type info is already resolved, we remove the starting '::' + let szTagQuery = substitute(szTypeInfo, '^::', '', 'g') + if len(split(szTagQuery, '::'))==1 + " eg: ::MyClass + " Here we have to get tags that have no parent scope + " That's why we change the szTagFilter + let szTagFilter .= '&& ' . g:omni#cpp#utils#szFilterGlobalScope + let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') + call filter(tagList, szTagFilter) + if len(tagList) + let result = tagList[0] + endif + else + " eg: ::MyNamespace::MyClass + let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') + call filter(tagList, szTagFilter) + + if len(tagList) + let result = tagList[0] + endif + endif + else + " The type is not resolved + let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') + call filter(tagList, szTagFilter) + + if len(tagList) + " Resolving scope (namespace, nested class etc...) + let szScopeOfTypeInfo = s:ExtractScopeFromTypeInfo(szTypeInfo) + if s:IsTypeInfoResolved(szTypeInfo) + let result = s:GetTagOfSameScope(tagList, szScopeOfTypeInfo) + else + " For each namespace of the namespace list we try to get a tag + " that can be in the same scope + if g:OmniCpp_NamespaceSearch && &filetype != 'c' + for scope in a:namespaces + let szTmpScope = omni#cpp#utils#SimplifyScope(scope.'::'.szScopeOfTypeInfo) + let result = s:GetTagOfSameScope(tagList, szTmpScope) + if result!={} + break + endif + endfor + else + let szTmpScope = omni#cpp#utils#SimplifyScope('::'.szScopeOfTypeInfo) + let result = s:GetTagOfSameScope(tagList, szTmpScope) + endif + endif + endif + endif + + if result!={} + " We have our tagItem but maybe it's a typedef or an unnamed type + if result.kind[0]=='t' + " Here we can have a typedef to another typedef, a class, struct, union etc + " but we can also have a typedef to an unnamed type, in that + " case the result contains a 'typeref' key + let namespaces = [omni#cpp#utils#ExtractScope(result), '::'] + if has_key(result, 'typeref') + let result = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(result)) + else + let szCmd = omni#cpp#utils#ExtractCmdFromTagItem(result) + let szCode = substitute(omni#cpp#utils#GetCodeFromLine(szCmd), '\C\<'.result.name.'\>.*', '', 'g') + let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTokens(omni#cpp#tokenizer#Tokenize(szCode)) + let result = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(szTypeInfo)) + " TODO: Namespace resolution for result + endif + endif + endif + + return result +endfunc + +" Returns if the type info is valid +" @return +" - 1 if valid +" - 0 otherwise +function! omni#cpp#utils#IsTypeInfoValid(typeInfo) + if a:typeInfo=={} + return 0 + else + if a:typeInfo.type == 1 && a:typeInfo.value=='' + " String case + return 0 + elseif a:typeInfo.type == 4 && a:typeInfo.value=={} + " Dictionary case + return 0 + endif + endif + return 1 +endfunc + +" Get the string of the type info +function! omni#cpp#utils#GetTypeInfoString(typeInfo) + if a:typeInfo.type == 1 + return a:typeInfo.value + else + return substitute(a:typeInfo.value.typeref, '^\w\+:', '', 'g') + endif +endfunc + +" A resolved type info starts with '::' +" @return +" - 1 if type info starts with '::' +" - 0 otherwise +function! s:IsTypeInfoResolved(szTypeInfo) + return match(a:szTypeInfo, '^::')!=-1 +endfunc + +" A returned type info's scope may not have the global namespace '::' +" eg: '::NameSpace1::NameSpace2::MyClass' => '::NameSpace1::NameSpace2' +" 'NameSpace1::NameSpace2::MyClass' => 'NameSpace1::NameSpace2' +function! s:ExtractScopeFromTypeInfo(szTypeInfo) + let szScope = substitute(a:szTypeInfo, '\w\+$', '', 'g') + if szScope =='::' + return szScope + else + return substitute(szScope, '::$', '', 'g') + endif +endfunc + +" @return +" - the tag with the same scope +" - {} otherwise +function! s:GetTagOfSameScope(listTags, szScopeToMatch) + for tagItem in a:listTags + let szScopeOfTag = omni#cpp#utils#ExtractScope(tagItem) + if szScopeOfTag == a:szScopeToMatch + return tagItem + endif + endfor + return {} +endfunc + +" Extract the cmd of a tag item without regexp +function! omni#cpp#utils#ExtractCmdFromTagItem(tagItem) + let line = a:tagItem.cmd + let re = '\(\/\^\)\|\(\$\/\)' + if match(line, re)!=-1 + let line = substitute(line, re, '', 'g') + return line + else + " TODO: the cmd is a line number + return '' + endif +endfunc + +" Extract type from tokens. +" eg: examples of tokens format +" 'const MyClass&' +" 'const map < int, int >&' +" 'MyNs::MyClass' +" '::MyClass**' +" 'MyClass a, *b = NULL, c[1] = {}; +" 'hello(MyClass a, MyClass* b' +" @return the type info string eg: ::std::map +" can be empty +function! omni#cpp#utils#ExtractTypeInfoFromTokens(tokens) + let szResult = '' + let state = 0 + + let tokens = omni#cpp#utils#BuildParenthesisGroups(a:tokens) + + " If there is an unbalanced parenthesis we are in a parameter list + let bParameterList = 0 + for token in tokens + if token.value == '(' && token.group==-1 + let bParameterList = 1 + break + endif + endfor + + if bParameterList + let tokens = reverse(tokens) + let state = 0 + let parenGroup = -1 + for token in tokens + if state==0 + if token.value=='>' + let parenGroup = token.group + let state=1 + elseif token.kind == 'cppWord' + let szResult = token.value.szResult + let state=2 + elseif index(['*', '&'], token.value)<0 + break + endif + elseif state==1 + if token.value=='<' && token.group==parenGroup + let state=0 + endif + elseif state==2 + if token.value=='::' + let szResult = token.value.szResult + let state=3 + else + break + endif + elseif state==3 + if token.kind == 'cppWord' + let szResult = token.value.szResult + let state=2 + else + break + endif + endif + endfor + return szResult + endif + + for token in tokens + if state==0 + if token.value == '::' + let szResult .= token.value + let state = 1 + elseif token.kind == 'cppWord' + let szResult .= token.value + let state = 2 + " Maybe end of token + endif + elseif state==1 + if token.kind == 'cppWord' + let szResult .= token.value + let state = 2 + " Maybe end of token + else + break + endif + elseif state==2 + if token.value == '::' + let szResult .= token.value + let state = 1 + else + break + endif + endif + endfor + return szResult +endfunc + +" Get the preview window string +function! omni#cpp#utils#GetPreviewWindowStringFromTagItem(tagItem) + let szResult = '' + + let szResult .= 'name: '.a:tagItem.name."\n" + for tagKey in keys(a:tagItem) + if index(['name', 'static'], tagKey)>=0 + continue + endif + let szResult .= tagKey.': '.a:tagItem[tagKey]."\n" + endfor + + return substitute(szResult, "\n$", '', 'g') +endfunc -- cgit v1.2.3