661 lines
23 KiB
VimL
661 lines
23 KiB
VimL
" 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
|