config/.vim/autoload/omni/cpp/items.vim

661 lines
23 KiB
VimL
Raw Normal View History

2009-11-01 19:04:42 +01:00
" Description: Omni completion script for cpp files
" Maintainer: Vissale NEANG
" Last Change: 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<MyClass*>(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