From dd5427baaf49f8de4355abeb6bc8c6dd14f74e25 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Wed, 7 Oct 2009 17:05:19 +0200 Subject: Initial check-in of files --- .vim/autoload/omni/cpp/utils.vim | 587 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+) create mode 100644 .vim/autoload/omni/cpp/utils.vim (limited to '.vim/autoload/omni/cpp/utils.vim') 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-70-g09d2