├── README ├── after └── ftplugin │ ├── c.vim │ └── cpp.vim ├── autoload └── omni │ ├── common │ ├── debug.vim │ └── utils.vim │ └── cpp │ ├── complete.vim │ ├── includes.vim │ ├── items.vim │ ├── maycomplete.vim │ ├── namespaces.vim │ ├── settings.vim │ ├── tokenizer.vim │ └── utils.vim └── doc └── omnicppcomplete.txt /README: -------------------------------------------------------------------------------- 1 | This is a mirror of http://www.vim.org/scripts/script.php?script_id=1520 2 | 3 | This script is for vim 7.0 or higher it provides an omnifunc cppcomplete function. 4 | You can use the omni completion (intellisense) in C and C++ files. 5 | This is a full vim script and you only need a ctags database. 6 | 7 | It's not finished yet but now you can : 8 | 9 | - Complete namespaces, classes, structs and union members. 10 | - Complete inherited members for classes and structs (single and multiple inheritance). 11 | - Complete attribute members eg: myObject->_child->_child etc... 12 | - Complete type returned by a function eg: myObject->get()->_child. 13 | - Complete the "this" pointer. 14 | - Complete a typedef. 15 | - Complete the current scope (global and class scope). 16 | - Complete an object after a cast (C and C++ cast). 17 | - Complete anonymous types (eg: struct {int a; int b;}g_Var; g_Var.???). It also works for a typedef of an anonymous type. 18 | 19 | 20 | Notes : 21 | - The script manage cached datas for optimization. 22 | - Ambiguous namespaces are detected and are not included in the context stack. 23 | - The parsed code is tokenized so you can run a completion even if the current 24 | instruction has bad indentation, spaces, comments or carriage returns between words 25 | (even if it is not realistic). 26 | 27 | ScreenShots : 28 | 29 | http://vissale.neang.free.fr/Vim/OmniCppComplete/ScreenShots/screenshots.htm 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /after/ftplugin/c.vim: -------------------------------------------------------------------------------- 1 | " OmniCppComplete initialization 2 | call omni#cpp#complete#Init() 3 | -------------------------------------------------------------------------------- /after/ftplugin/cpp.vim: -------------------------------------------------------------------------------- 1 | " OmniCppComplete initialization 2 | call omni#cpp#complete#Init() 3 | -------------------------------------------------------------------------------- /autoload/omni/common/debug.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion debug functions 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | let s:CACHE_DEBUG_TRACE = [] 6 | 7 | " Start debug, clear the debug file 8 | function! omni#common#debug#Start() 9 | let s:CACHE_DEBUG_TRACE = [] 10 | call extend(s:CACHE_DEBUG_TRACE, ['============ Debug Start ============']) 11 | call writefile(s:CACHE_DEBUG_TRACE, "Omni.dbg") 12 | endfunc 13 | 14 | " End debug, write to debug file 15 | function! omni#common#debug#End() 16 | call extend(s:CACHE_DEBUG_TRACE, ["============= Debug End ============="]) 17 | call extend(s:CACHE_DEBUG_TRACE, [""]) 18 | call writefile(s:CACHE_DEBUG_TRACE, "Omni.dbg") 19 | endfunc 20 | 21 | " Debug trace function 22 | function! omni#common#debug#Trace(szFuncName, ...) 23 | let szTrace = a:szFuncName 24 | let paramNum = a:0 25 | if paramNum>0 26 | let szTrace .= ':' 27 | endif 28 | for i in range(paramNum) 29 | let szTrace = szTrace .' ('. string(eval('a:'.string(i+1))).')' 30 | endfor 31 | call extend(s:CACHE_DEBUG_TRACE, [szTrace]) 32 | endfunc 33 | -------------------------------------------------------------------------------- /autoload/omni/common/utils.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion utils 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | " For sort numbers in list 6 | function! omni#common#utils#CompareNumber(i1, i2) 7 | let num1 = eval(a:i1) 8 | let num2 = eval(a:i2) 9 | return num1 == num2 ? 0 : num1 > num2 ? 1 : -1 10 | endfunc 11 | 12 | " TagList function calling the vim taglist() with try catch 13 | " The only throwed exception is 'TagList:UserInterrupt' 14 | " We also force the noignorecase option to avoid linear search when calling 15 | " taglist() 16 | function! omni#common#utils#TagList(szTagQuery) 17 | let result = [] 18 | let bUserIgnoreCase = &ignorecase 19 | " Forcing noignorecase search => binary search can be used in taglist() 20 | " if tags in the tag file are sorted 21 | if bUserIgnoreCase 22 | set noignorecase 23 | endif 24 | try 25 | let result = taglist(a:szTagQuery) 26 | catch /^Vim:Interrupt$/ 27 | " Restoring user's setting 28 | if bUserIgnoreCase 29 | set ignorecase 30 | endif 31 | throw 'TagList:UserInterrupt' 32 | catch 33 | "Note: it seems that ctags can generate corrupted files, in this case 34 | "taglist() will fail to read the tagfile and an exception from 35 | "has_add() is thrown 36 | endtry 37 | 38 | " Restoring user's setting 39 | if bUserIgnoreCase 40 | set ignorecase 41 | endif 42 | return result 43 | endfunc 44 | 45 | " Same as TagList but don't throw exception 46 | function! omni#common#utils#TagListNoThrow(szTagQuery) 47 | let result = [] 48 | try 49 | let result = omni#common#utils#TagList(a:szTagQuery) 50 | catch 51 | endtry 52 | return result 53 | endfunc 54 | 55 | " Get the word under the cursor 56 | function! omni#common#utils#GetWordUnderCursor() 57 | let szLine = getline('.') 58 | let startPos = getpos('.')[2]-1 59 | let startPos = (startPos < 0)? 0 : startPos 60 | if szLine[startPos] =~ '\w' 61 | let startPos = searchpos('\<\w\+', 'cbn', line('.'))[1] - 1 62 | endif 63 | 64 | let startPos = (startPos < 0)? 0 : startPos 65 | let szResult = matchstr(szLine, '\w\+', startPos) 66 | return szResult 67 | endfunc 68 | -------------------------------------------------------------------------------- /autoload/omni/cpp/complete.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 27 sept. 2007 4 | 5 | if v:version < 700 6 | echohl WarningMsg 7 | echomsg "omni#cpp#complete.vim: Please install vim 7.0 or higher for omni-completion" 8 | echohl None 9 | finish 10 | endif 11 | 12 | call omni#cpp#settings#Init() 13 | let s:OmniCpp_ShowScopeInAbbr = g:OmniCpp_ShowScopeInAbbr 14 | let s:OmniCpp_ShowPrototypeInAbbr = g:OmniCpp_ShowPrototypeInAbbr 15 | let s:OmniCpp_ShowAccess = g:OmniCpp_ShowAccess 16 | let s:szCurrentWorkingDir = getcwd() 17 | 18 | " Cache data 19 | let s:CACHE_TAG_POPUP_ITEMS = {} 20 | let s:CACHE_TAG_FILES = {} 21 | let s:CACHE_TAG_ENV = '' 22 | let s:CACHE_OVERLOADED_FUNCTIONS = {} 23 | 24 | " Has preview window? 25 | let s:hasPreviewWindow = match(&completeopt, 'preview')>=0 26 | let s:hasPreviewWindowOld = s:hasPreviewWindow 27 | 28 | " Popup item list 29 | let s:popupItemResultList = [] 30 | 31 | " May complete indicator 32 | let s:bMayComplete = 0 33 | 34 | " Init mappings 35 | function! omni#cpp#complete#Init() 36 | call omni#cpp#settings#Init() 37 | set omnifunc=omni#cpp#complete#Main 38 | inoremap omni#cpp#maycomplete#Complete() 39 | inoremap . omni#cpp#maycomplete#Dot() 40 | inoremap > omni#cpp#maycomplete#Arrow() 41 | inoremap : omni#cpp#maycomplete#Scope() 42 | endfunc 43 | 44 | " Find the start position of the completion 45 | function! s:FindStartPositionOfCompletion() 46 | " Locate the start of the item, including ".", "->" and "[...]". 47 | let line = getline('.') 48 | let start = col('.') - 1 49 | 50 | let lastword = -1 51 | while start > 0 52 | if line[start - 1] =~ '\w' 53 | let start -= 1 54 | elseif line[start - 1] =~ '\.' 55 | " Searching for dot '.' 56 | if lastword == -1 57 | let lastword = start 58 | endif 59 | let start -= 1 60 | elseif start > 1 && line[start - 2] == '-' && line[start - 1] == '>' 61 | " Searching for '->' 62 | if lastword == -1 63 | let lastword = start 64 | endif 65 | let start -= 2 66 | elseif start > 1 && line[start - 2] == ':' && line[start - 1] == ':' 67 | " Searching for '::' for namespaces and class 68 | if lastword == -1 69 | let lastword = start 70 | endif 71 | let start -= 2 72 | elseif line[start - 1] == ']' 73 | " Skip over [...]. 74 | let n = 0 75 | let start -= 1 76 | while start > 0 77 | let start -= 1 78 | if line[start] == '[' 79 | if n == 0 80 | break 81 | endif 82 | let n -= 1 83 | elseif line[start] == ']' " nested [] 84 | let n += 1 85 | endif 86 | endwhile 87 | else 88 | break 89 | endif 90 | endwhile 91 | if lastword==-1 92 | " For completion on the current scope 93 | let lastword = start 94 | endif 95 | return lastword 96 | endfunc 97 | 98 | " Returns if szKey1.szKey2 is in the cache 99 | " @return 100 | " - 0 = key not found 101 | " - 1 = szKey1.szKey2 found 102 | " - 2 = szKey1.[part of szKey2] found 103 | function! s:IsCached(cache, szKey1, szKey2) 104 | " Searching key in the result cache 105 | let szResultKey = a:szKey1 . a:szKey2 106 | let result = [0, szResultKey] 107 | if a:szKey2 != '' 108 | let szKey = a:szKey2 109 | while len(szKey)>0 110 | if has_key(a:cache, a:szKey1 . szKey) 111 | let result[1] = a:szKey1 . szKey 112 | if szKey != a:szKey2 113 | let result[0] = 2 114 | else 115 | let result[0] = 1 116 | endif 117 | break 118 | endif 119 | let szKey = szKey[:-2] 120 | endwhile 121 | else 122 | if has_key(a:cache, szResultKey) 123 | let result[0] = 1 124 | endif 125 | endif 126 | return result 127 | endfunc 128 | 129 | " Extend a tag item to a popup item 130 | function! s:ExtendTagItemToPopupItem(tagItem, szTypeName) 131 | let tagItem = a:tagItem 132 | 133 | " Add the access 134 | let szItemMenu = '' 135 | let accessChar = {'public': '+','protected': '#','private': '-'} 136 | if g:OmniCpp_ShowAccess 137 | if has_key(tagItem, 'access') && has_key(accessChar, tagItem.access) 138 | let szItemMenu = szItemMenu.accessChar[tagItem.access] 139 | else 140 | let szItemMenu = szItemMenu." " 141 | endif 142 | endif 143 | 144 | " Formating optional menu string we extract the scope information 145 | let szName = substitute(tagItem.name, '.*::', '', 'g') 146 | let szItemWord = szName 147 | let szAbbr = szName 148 | 149 | if !g:OmniCpp_ShowScopeInAbbr 150 | let szScopeOfTag = omni#cpp#utils#ExtractScope(tagItem) 151 | let szItemMenu = szItemMenu.' '.szScopeOfTag[2:] 152 | let szItemMenu = substitute(szItemMenu, '\s\+$', '', 'g') 153 | else 154 | let szAbbr = tagItem.name 155 | endif 156 | if g:OmniCpp_ShowAccess 157 | let szItemMenu = substitute(szItemMenu, '^\s\+$', '', 'g') 158 | else 159 | let szItemMenu = substitute(szItemMenu, '\(^\s\+\)\|\(\s\+$\)', '', 'g') 160 | endif 161 | 162 | " Formating information for the preview window 163 | if index(['f', 'p'], tagItem.kind[0])>=0 164 | let szItemWord .= '(' 165 | if g:OmniCpp_ShowPrototypeInAbbr && has_key(tagItem, 'signature') 166 | let szAbbr .= tagItem.signature 167 | else 168 | let szAbbr .= '(' 169 | endif 170 | endif 171 | let szItemInfo = '' 172 | if s:hasPreviewWindow 173 | let szItemInfo = omni#cpp#utils#GetPreviewWindowStringFromTagItem(tagItem) 174 | endif 175 | 176 | " If a function is a ctor we add a new key in the tagItem 177 | if index(['f', 'p'], tagItem.kind[0])>=0 178 | if match(szName, '^\~') < 0 && a:szTypeName =~ '\C\<'.szName.'$' 179 | " It's a ctor 180 | let tagItem['ctor'] = 1 181 | elseif has_key(tagItem, 'access') && tagItem.access == 'friend' 182 | " Friend function 183 | let tagItem['friendfunc'] = 1 184 | endif 185 | endif 186 | 187 | " Extending the tag item to a popup item 188 | let tagItem['word'] = szItemWord 189 | let tagItem['abbr'] = szAbbr 190 | let tagItem['menu'] = szItemMenu 191 | let tagItem['info'] = szItemInfo 192 | let tagItem['dup'] = (s:hasPreviewWindow && index(['f', 'p', 'm'], tagItem.kind[0])>=0) 193 | return tagItem 194 | endfunc 195 | 196 | " Get tag popup item list 197 | function! s:TagPopupList(szTypeName, szBase) 198 | let result = [] 199 | 200 | " Searching key in the result cache 201 | let cacheResult = s:IsCached(s:CACHE_TAG_POPUP_ITEMS, a:szTypeName, a:szBase) 202 | 203 | " Building the tag query, we don't forget dtors when a:szBase=='' 204 | if a:szTypeName!='' 205 | " Scope search 206 | let szTagQuery = '^' . a:szTypeName . '::' . a:szBase . '\~\?\w\+$' 207 | else 208 | " Global search 209 | let szTagQuery = '^' . a:szBase . '\w\+$' 210 | endif 211 | 212 | " If the result is already in the cache we return it 213 | if cacheResult[0] 214 | let result = s:CACHE_TAG_POPUP_ITEMS[ cacheResult[1] ] 215 | if cacheResult[0] == 2 216 | let result = filter(copy(result), 'v:val.name =~ szTagQuery' ) 217 | endif 218 | return result 219 | endif 220 | 221 | try 222 | " Getting tags 223 | let result = omni#common#utils#TagList(szTagQuery) 224 | 225 | " We extend tag items to popup items 226 | call map(result, 's:ExtendTagItemToPopupItem(v:val, a:szTypeName)') 227 | 228 | " We store the result in a cache 229 | if cacheResult[1] != '' 230 | let s:CACHE_TAG_POPUP_ITEMS[ cacheResult[1] ] = result 231 | endif 232 | catch /^TagList:UserInterrupt$/ 233 | endtry 234 | 235 | return result 236 | endfunc 237 | 238 | " Find complete matches for a completion on the global scope 239 | function! s:SearchGlobalMembers(szBase) 240 | if a:szBase != '' 241 | let tagPopupList = s:TagPopupList('', a:szBase) 242 | let tagPopupList = filter(copy(tagPopupList), g:omni#cpp#utils#szFilterGlobalScope) 243 | call extend(s:popupItemResultList, tagPopupList) 244 | endif 245 | endfunc 246 | 247 | " Search class, struct, union members 248 | " @param resolvedTagItem: a resolved tag item 249 | " @param szBase: string base 250 | " @return list of tag items extended to popup items 251 | function! s:SearchMembers(resolvedTagItem, szBase) 252 | let result = [] 253 | if a:resolvedTagItem == {} 254 | return result 255 | endif 256 | 257 | " Get type info without the starting '::' 258 | let szTagName = omni#cpp#utils#ExtractTypeInfoFromTag(a:resolvedTagItem)[2:] 259 | 260 | " Unnamed type case. A tag item representing an unnamed type is a variable 261 | " ('v') a member ('m') or a typedef ('t') 262 | if index(['v', 't', 'm'], a:resolvedTagItem.kind[0])>=0 && has_key(a:resolvedTagItem, 'typeref') 263 | " We remove the 'struct:' or 'class:' etc... 264 | let szTagName = substitute(a:resolvedTagItem.typeref, '^\w\+:', '', 'g') 265 | endif 266 | 267 | return copy(s:TagPopupList(szTagName, a:szBase)) 268 | endfunc 269 | 270 | " Return if the tag env has changed 271 | function! s:HasTagEnvChanged() 272 | if s:CACHE_TAG_ENV == &tags 273 | return 0 274 | else 275 | let s:CACHE_TAG_ENV = &tags 276 | return 1 277 | endif 278 | endfunc 279 | 280 | " Return if a tag file has changed in tagfiles() 281 | function! s:HasATagFileOrTagEnvChanged() 282 | if s:HasTagEnvChanged() 283 | let s:CACHE_TAG_FILES = {} 284 | return 1 285 | endif 286 | 287 | let result = 0 288 | for tagFile in tagfiles() 289 | if tagFile == "" 290 | continue 291 | endif 292 | 293 | if has_key(s:CACHE_TAG_FILES, tagFile) 294 | let currentFiletime = getftime(tagFile) 295 | if currentFiletime > s:CACHE_TAG_FILES[tagFile] 296 | " The file has changed, updating the cache 297 | let s:CACHE_TAG_FILES[tagFile] = currentFiletime 298 | let result = 1 299 | endif 300 | else 301 | " We store the time of the file 302 | let s:CACHE_TAG_FILES[tagFile] = getftime(tagFile) 303 | let result = 1 304 | endif 305 | endfor 306 | return result 307 | endfunc 308 | " Initialization 309 | call s:HasATagFileOrTagEnvChanged() 310 | 311 | " Filter same function signatures of base classes 312 | function! s:FilterOverloadedFunctions(tagPopupList) 313 | let result = [] 314 | for tagPopupItem in a:tagPopupList 315 | if has_key(tagPopupItem, 'kind') && index(['f', 'p'], tagPopupItem.kind[0])>=0 && has_key(tagPopupItem, 'signature') 316 | if !has_key(s:CACHE_OVERLOADED_FUNCTIONS, tagPopupItem.word . tagPopupItem.signature) 317 | let s:CACHE_OVERLOADED_FUNCTIONS[tagPopupItem.word . tagPopupItem.signature] = 1 318 | call extend(result, [tagPopupItem]) 319 | endif 320 | else 321 | call extend(result, [tagPopupItem]) 322 | endif 323 | endfor 324 | return result 325 | endfunc 326 | 327 | " Access filter 328 | function! s:GetAccessFilter(szFilter, szAccessFilter) 329 | let szFilter = a:szFilter 330 | if g:OmniCpp_DisplayMode == 0 331 | if a:szAccessFilter == 'public' 332 | " We only get public members 333 | let szFilter .= "&& v:val.access == 'public'" 334 | elseif a:szAccessFilter == 'protected' 335 | " We get public and protected members 336 | let szFilter .= "&& v:val.access != 'private'" 337 | endif 338 | endif 339 | return szFilter 340 | endfunc 341 | 342 | " Filter class members in the popup menu after a completion with -> or . 343 | function! s:FilterClassMembers(tagPopupList, szAccessFilter) 344 | 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'))" 345 | call filter(a:tagPopupList, s:GetAccessFilter(szFilter, a:szAccessFilter)) 346 | call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList)) 347 | endfunc 348 | 349 | " Filter class scope members in the popup menu after a completion with :: 350 | " We only display attribute and functions members that 351 | " have an access information. We also display nested 352 | " class, struct, union, and enums, typedefs 353 | function! s:FilterClassScopeMembers(tagPopupList, szAccessFilter) 354 | 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'))" 355 | let szFilter = s:GetAccessFilter(szFilter, a:szAccessFilter) 356 | let szFilter .= "|| index(['c','e','g','s','t','u'], v:val.kind[0])>=0" 357 | call filter(a:tagPopupList, szFilter) 358 | call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList)) 359 | endfunc 360 | 361 | " Filter static class members in the popup menu 362 | function! s:FilterStaticClassMembers(tagPopupList, szAccessFilter) 363 | 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)" 364 | let szFilter = s:GetAccessFilter(szFilter, a:szAccessFilter) 365 | let szFilter = szFilter . "|| index(['c','e','g','n','s','t','u','v'], v:val.kind[0])>=0" 366 | call filter(a:tagPopupList, szFilter) 367 | call extend(s:popupItemResultList, s:FilterOverloadedFunctions(a:tagPopupList)) 368 | endfunc 369 | 370 | " Filter scope members in the popup menu 371 | function! s:FilterNamespaceScopeMembers(tagPopupList) 372 | call extend(s:popupItemResultList, a:tagPopupList) 373 | endfunc 374 | 375 | " Init data at the start of completion 376 | function! s:InitComplete() 377 | " Reset the popup item list 378 | let s:popupItemResultList = [] 379 | let s:CACHE_OVERLOADED_FUNCTIONS = {} 380 | 381 | " Reset includes cache when the current working directory has changed 382 | let szCurrentWorkingDir = getcwd() 383 | if s:szCurrentWorkingDir != szCurrentWorkingDir 384 | let s:szCurrentWorkingDir = szCurrentWorkingDir 385 | let g:omni#cpp#includes#CACHE_INCLUDES = {} 386 | let g:omni#cpp#includes#CACHE_FILE_TIME = {} 387 | endif 388 | 389 | " Has preview window ? 390 | let s:hasPreviewWindow = match(&completeopt, 'preview')>=0 391 | 392 | let bResetCache = 0 393 | 394 | " Reset tag env or tag files dependent caches 395 | if s:HasATagFileOrTagEnvChanged() 396 | let bResetCache = 1 397 | endif 398 | 399 | if (s:OmniCpp_ShowScopeInAbbr != g:OmniCpp_ShowScopeInAbbr) 400 | \|| (s:OmniCpp_ShowPrototypeInAbbr != g:OmniCpp_ShowPrototypeInAbbr) 401 | \|| (s:OmniCpp_ShowAccess != g:OmniCpp_ShowAccess) 402 | 403 | let s:OmniCpp_ShowScopeInAbbr = g:OmniCpp_ShowScopeInAbbr 404 | let s:OmniCpp_ShowPrototypeInAbbr = g:OmniCpp_ShowPrototypeInAbbr 405 | let s:OmniCpp_ShowAccess = g:OmniCpp_ShowAccess 406 | let bResetCache = 1 407 | endif 408 | 409 | if s:hasPreviewWindow != s:hasPreviewWindowOld 410 | let s:hasPreviewWindowOld = s:hasPreviewWindow 411 | let bResetCache = 1 412 | endif 413 | 414 | if bResetCache 415 | let g:omni#cpp#namespaces#CacheResolve = {} 416 | let s:CACHE_TAG_POPUP_ITEMS = {} 417 | let g:omni#cpp#utils#CACHE_TAG_INHERITS = {} 418 | call garbagecollect() 419 | endif 420 | 421 | " Check for updates 422 | for szIncludeName in keys(g:omni#cpp#includes#CACHE_INCLUDES) 423 | let fTime = getftime(szIncludeName) 424 | let bNeedUpdate = 0 425 | if has_key(g:omni#cpp#includes#CACHE_FILE_TIME, szIncludeName) 426 | if fTime != g:omni#cpp#includes#CACHE_FILE_TIME[szIncludeName] 427 | let bNeedUpdate = 1 428 | endif 429 | else 430 | let g:omni#cpp#includes#CACHE_FILE_TIME[szIncludeName] = fTime 431 | let bNeedUpdate = 1 432 | endif 433 | 434 | if bNeedUpdate 435 | " We have to update include list and namespace map of this file 436 | call omni#cpp#includes#GetList(szIncludeName, 1) 437 | call omni#cpp#namespaces#GetMapFromBuffer(szIncludeName, 1) 438 | endif 439 | endfor 440 | 441 | let s:bDoNotComplete = 0 442 | endfunc 443 | 444 | 445 | " This function is used for the 'omnifunc' option. 446 | function! omni#cpp#complete#Main(findstart, base) 447 | if a:findstart 448 | "call omni#common#debug#Start() 449 | 450 | call s:InitComplete() 451 | 452 | " Note: if s:bMayComplete==1 g:omni#cpp#items#data is build by MayComplete functions 453 | if !s:bMayComplete 454 | " If the cursor is in a comment we go out 455 | if omni#cpp#utils#IsCursorInCommentOrString() 456 | " Returning -1 is not enough we have to set a variable to let 457 | " the second call of omni#cpp#complete knows that the 458 | " cursor was in a comment 459 | " Why is there a second call when the first call returns -1 ? 460 | let s:bDoNotComplete = 1 461 | return -1 462 | endif 463 | 464 | " We get items here (whend a:findstart==1) because GetItemsToComplete() 465 | " depends on the cursor position. 466 | " When a:findstart==0 the cursor position is modified 467 | let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction()) 468 | endif 469 | 470 | " Get contexts stack 471 | let s:contextStack = omni#cpp#namespaces#GetContexts() 472 | 473 | " Reinit of may complete indicator 474 | let s:bMayComplete = 0 475 | return s:FindStartPositionOfCompletion() 476 | endif 477 | 478 | " If the cursor is in a comment we return an empty result 479 | if s:bDoNotComplete 480 | let s:bDoNotComplete = 0 481 | return [] 482 | endif 483 | 484 | if len(g:omni#cpp#items#data)==0 485 | " A) CURRENT_SCOPE_COMPLETION_MODE 486 | 487 | " 1) Displaying data of each context 488 | let szAccessFilter = 'all' 489 | for szCurrentContext in s:contextStack 490 | if szCurrentContext == '::' 491 | continue 492 | endif 493 | 494 | let resolvedTagItem = omni#cpp#utils#GetResolvedTagItem(s:contextStack, omni#cpp#utils#CreateTypeInfo(szCurrentContext)) 495 | if resolvedTagItem != {} 496 | " We don't search base classes because bases classes are 497 | " already in the context stack 498 | let tagPopupList = s:SearchMembers(resolvedTagItem, a:base) 499 | if index(['c','s'], resolvedTagItem.kind[0])>=0 500 | " It's a class or struct 501 | call s:FilterClassScopeMembers(tagPopupList, szAccessFilter) 502 | let szAccessFilter = 'protected' 503 | else 504 | " It's a namespace or union, we display all members 505 | call s:FilterNamespaceScopeMembers(tagPopupList) 506 | endif 507 | endif 508 | endfor 509 | 510 | " 2) Displaying global scope members 511 | if g:OmniCpp_GlobalScopeSearch 512 | call s:SearchGlobalMembers(a:base) 513 | endif 514 | else 515 | let typeInfo = omni#cpp#items#ResolveItemsTypeInfo(s:contextStack, g:omni#cpp#items#data) 516 | 517 | if typeInfo != {} 518 | if g:omni#cpp#items#data[-1].kind == 'itemScope' 519 | " B) SCOPE_COMPLETION_MODE 520 | if omni#cpp#utils#GetTypeInfoString(typeInfo)=='' 521 | call s:SearchGlobalMembers(a:base) 522 | else 523 | for resolvedTagItem in omni#cpp#utils#GetResolvedTags(s:contextStack, typeInfo) 524 | let tagPopupList = s:SearchMembers(resolvedTagItem, a:base) 525 | if index(['c','s'], resolvedTagItem.kind[0])>=0 526 | let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(resolvedTagItem) 527 | if g:OmniCpp_DisplayMode==0 528 | " We want to complete a class or struct 529 | " If this class is a base class so we display all class members 530 | if index(s:contextStack, szTypeInfo)<0 531 | let szAccessFilter = 'public' 532 | call s:FilterStaticClassMembers(tagPopupList, szAccessFilter) 533 | else 534 | let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected' 535 | call s:FilterClassScopeMembers(tagPopupList, szAccessFilter) 536 | endif 537 | else 538 | if index(s:contextStack, szTypeInfo)<0 539 | let szAccessFilter = 'public' 540 | else 541 | let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected' 542 | endif 543 | call s:FilterClassScopeMembers(tagPopupList, szAccessFilter) 544 | endif 545 | else 546 | " We want to complete a namespace 547 | call s:FilterNamespaceScopeMembers(tagPopupList) 548 | endif 549 | endfor 550 | endif 551 | else 552 | " C) CLASS_MEMBERS_COMPLETION_MODE 553 | for resolvedTagItem in omni#cpp#utils#GetResolvedTags(s:contextStack, typeInfo) 554 | let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(resolvedTagItem) 555 | if index(s:contextStack, szTypeInfo)<0 556 | let szAccessFilter = 'public' 557 | else 558 | let szAccessFilter = (s:contextStack[0] == szTypeInfo)? 'all' : 'protected' 559 | endif 560 | call s:FilterClassMembers(s:SearchMembers(resolvedTagItem, a:base), szAccessFilter) 561 | endfor 562 | endif 563 | endif 564 | endif 565 | 566 | "call omni#common#debug#End() 567 | 568 | return s:popupItemResultList 569 | endfunc 570 | -------------------------------------------------------------------------------- /autoload/omni/cpp/includes.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | let g:omni#cpp#includes#CACHE_INCLUDES = {} 6 | let g:omni#cpp#includes#CACHE_FILE_TIME = {} 7 | 8 | let s:rePreprocIncludePart = '\C#\s*include\s*' 9 | let s:reIncludeFilePart = '\(<\|"\)\(\f\|\s\)\+\(>\|"\)' 10 | let s:rePreprocIncludeFile = s:rePreprocIncludePart . s:reIncludeFilePart 11 | 12 | " Get the include list of a file 13 | function! omni#cpp#includes#GetList(...) 14 | if a:0 > 0 15 | return s:GetIncludeListFromFile(a:1, (a:0 > 1)? a:2 : 0 ) 16 | else 17 | return s:GetIncludeListFromCurrentBuffer() 18 | endif 19 | endfunc 20 | 21 | " Get the include list from the current buffer 22 | function! s:GetIncludeListFromCurrentBuffer() 23 | let listIncludes = [] 24 | let originalPos = getpos('.') 25 | 26 | call setpos('.', [0, 1, 1, 0]) 27 | let curPos = [1,1] 28 | let alreadyInclude = {} 29 | while curPos != [0,0] 30 | let curPos = searchpos('\C\(^'.s:rePreprocIncludeFile.'\)', 'W') 31 | if curPos != [0,0] 32 | let szLine = getline('.') 33 | let startPos = curPos[1] 34 | let endPos = matchend(szLine, s:reIncludeFilePart, startPos-1) 35 | if endPos!=-1 36 | let szInclusion = szLine[startPos-1:endPos-1] 37 | let szIncludeFile = substitute(szInclusion, '\('.s:rePreprocIncludePart.'\)\|[<>""]', '', 'g') 38 | let szResolvedInclude = omni#cpp#utils#ResolveFilePath(szIncludeFile) 39 | 40 | " Protection over self inclusion 41 | if szResolvedInclude != '' && szResolvedInclude != omni#cpp#utils#ResolveFilePath(getreg('%')) 42 | let includePos = curPos 43 | if !has_key(alreadyInclude, szResolvedInclude) 44 | call extend(listIncludes, [{'pos' : includePos, 'include' : szResolvedInclude}]) 45 | let alreadyInclude[szResolvedInclude] = 1 46 | endif 47 | endif 48 | endif 49 | endif 50 | endwhile 51 | 52 | call setpos('.', originalPos) 53 | return listIncludes 54 | endfunc 55 | 56 | " Get the include list from a file 57 | function! s:GetIncludeListFromFile(szFilePath, bUpdate) 58 | let listIncludes = [] 59 | if a:szFilePath == '' 60 | return listIncludes 61 | endif 62 | 63 | if !a:bUpdate && has_key(g:omni#cpp#includes#CACHE_INCLUDES, a:szFilePath) 64 | return copy(g:omni#cpp#includes#CACHE_INCLUDES[a:szFilePath]) 65 | endif 66 | 67 | let g:omni#cpp#includes#CACHE_FILE_TIME[a:szFilePath] = getftime(a:szFilePath) 68 | 69 | let szFixedPath = escape(a:szFilePath, g:omni#cpp#utils#szEscapedCharacters) 70 | execute 'silent! lvimgrep /\C\(^'.s:rePreprocIncludeFile.'\)/gj '.szFixedPath 71 | 72 | let listQuickFix = getloclist(0) 73 | let alreadyInclude = {} 74 | for qf in listQuickFix 75 | let szLine = qf.text 76 | let startPos = qf.col 77 | let endPos = matchend(szLine, s:reIncludeFilePart, startPos-1) 78 | if endPos!=-1 79 | let szInclusion = szLine[startPos-1:endPos-1] 80 | let szIncludeFile = substitute(szInclusion, '\('.s:rePreprocIncludePart.'\)\|[<>""]', '', 'g') 81 | let szResolvedInclude = omni#cpp#utils#ResolveFilePath(szIncludeFile) 82 | 83 | " Protection over self inclusion 84 | if szResolvedInclude != '' && szResolvedInclude != a:szFilePath 85 | let includePos = [qf.lnum, qf.col] 86 | if !has_key(alreadyInclude, szResolvedInclude) 87 | call extend(listIncludes, [{'pos' : includePos, 'include' : szResolvedInclude}]) 88 | let alreadyInclude[szResolvedInclude] = 1 89 | endif 90 | endif 91 | endif 92 | endfor 93 | 94 | let g:omni#cpp#includes#CACHE_INCLUDES[a:szFilePath] = listIncludes 95 | 96 | return copy(listIncludes) 97 | endfunc 98 | 99 | " For debug purpose 100 | function! omni#cpp#includes#Display() 101 | let szPathBuffer = omni#cpp#utils#ResolveFilePath(getreg('%')) 102 | call s:DisplayIncludeTree(szPathBuffer, 0) 103 | endfunc 104 | 105 | " For debug purpose 106 | function! s:DisplayIncludeTree(szFilePath, indent, ...) 107 | let includeGuard = {} 108 | if a:0 >0 109 | let includeGuard = a:1 110 | endif 111 | let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) 112 | if has_key(includeGuard, szFilePath) 113 | return 114 | else 115 | let includeGuard[szFilePath] = 1 116 | endif 117 | 118 | let szIndent = repeat(' ', a:indent) 119 | echo szIndent . a:szFilePath 120 | let incList = omni#cpp#includes#GetList(a:szFilePath) 121 | for inc in incList 122 | call s:DisplayIncludeTree(inc.include, a:indent+1, includeGuard) 123 | endfor 124 | endfunc 125 | 126 | 127 | -------------------------------------------------------------------------------- /autoload/omni/cpp/items.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | " Build the item list of an instruction 6 | " An item is an instruction between a -> or . or ->* or .* 7 | " We can sort an item in different kinds: 8 | " eg: ((MyClass1*)(pObject))->_memberOfClass1.get() ->show() 9 | " | cast | | member | | method | | method | 10 | " @return a list of item 11 | " an item is a dictionnary where keys are: 12 | " tokens = list of token 13 | " kind = itemVariable|itemCast|itemCppCast|itemTemplate|itemFunction|itemUnknown|itemThis|itemScope 14 | function! omni#cpp#items#Get(tokens, ...) 15 | let bGetWordUnderCursor = (a:0>0)? a:1 : 0 16 | 17 | let result = [] 18 | let itemsDelimiters = ['->', '.', '->*', '.*'] 19 | 20 | let tokens = reverse(omni#cpp#utils#BuildParenthesisGroups(a:tokens)) 21 | 22 | " fsm states: 23 | " 0 = initial state 24 | " TODO: add description of fsm states 25 | let state=(bGetWordUnderCursor)? 1 : 0 26 | let item = {'tokens' : [], 'kind' : 'itemUnknown'} 27 | let parenGroup=-1 28 | for token in tokens 29 | if state==0 30 | if index(itemsDelimiters, token.value)>=0 31 | let item = {'tokens' : [], 'kind' : 'itemUnknown'} 32 | let state = 1 33 | elseif token.value=='::' 34 | let state = 9 35 | let item.kind = 'itemScope' 36 | " Maybe end of tokens 37 | elseif token.kind =='cppOperatorPunctuator' 38 | " If it's a cppOperatorPunctuator and the current token is not 39 | " a itemsDelimiters or '::' we can exit 40 | let state=-1 41 | break 42 | endif 43 | elseif state==1 44 | call insert(item.tokens, token) 45 | if token.kind=='cppWord' 46 | " It's an attribute member or a variable 47 | let item.kind = 'itemVariable' 48 | let state = 2 49 | " Maybe end of tokens 50 | elseif token.value=='this' 51 | let item.kind = 'itemThis' 52 | let state = 2 53 | " Maybe end of tokens 54 | elseif token.value==')' 55 | let parenGroup = token.group 56 | let state = 3 57 | elseif token.value==']' 58 | let parenGroup = token.group 59 | let state = 4 60 | elseif token.kind == 'cppDigit' 61 | let state = -1 62 | break 63 | endif 64 | elseif state==2 65 | if index(itemsDelimiters, token.value)>=0 66 | call insert(result, item) 67 | let item = {'tokens' : [], 'kind' : 'itemUnknown'} 68 | let state = 1 69 | elseif token.value == '::' 70 | call insert(item.tokens, token) 71 | " We have to get namespace or classscope 72 | let state = 8 73 | " Maybe end of tokens 74 | else 75 | call insert(result, item) 76 | let state=-1 77 | break 78 | endif 79 | elseif state==3 80 | call insert(item.tokens, token) 81 | if token.value=='(' && token.group == parenGroup 82 | let state = 5 83 | " Maybe end of tokens 84 | endif 85 | elseif state==4 86 | call insert(item.tokens, token) 87 | if token.value=='[' && token.group == parenGroup 88 | let state = 1 89 | endif 90 | elseif state==5 91 | if token.kind=='cppWord' 92 | " It's a function or method 93 | let item.kind = 'itemFunction' 94 | call insert(item.tokens, token) 95 | let state = 2 96 | " Maybe end of tokens 97 | elseif token.value == '>' 98 | " Maybe a cpp cast or template 99 | let item.kind = 'itemTemplate' 100 | call insert(item.tokens, token) 101 | let parenGroup = token.group 102 | let state = 6 103 | else 104 | " Perhaps it's a C cast eg: ((void*)(pData)) or a variable eg:(*pData) 105 | let item.kind = omni#cpp#utils#GetCastType(item.tokens) 106 | let state=-1 107 | call insert(result, item) 108 | break 109 | endif 110 | elseif state==6 111 | call insert(item.tokens, token) 112 | if token.value == '<' && token.group == parenGroup 113 | " Maybe a cpp cast or template 114 | let state = 7 115 | endif 116 | elseif state==7 117 | call insert(item.tokens, token) 118 | if token.kind=='cppKeyword' 119 | " It's a cpp cast 120 | let item.kind = omni#cpp#utils#GetCastType(item.tokens) 121 | let state=-1 122 | call insert(result, item) 123 | break 124 | else 125 | " Template ? 126 | let state=-1 127 | call insert(result, item) 128 | break 129 | endif 130 | elseif state==8 131 | if token.kind=='cppWord' 132 | call insert(item.tokens, token) 133 | let state = 2 134 | " Maybe end of tokens 135 | else 136 | let state=-1 137 | call insert(result, item) 138 | break 139 | endif 140 | elseif state==9 141 | if token.kind == 'cppWord' 142 | call insert(item.tokens, token) 143 | let state = 10 144 | " Maybe end of tokens 145 | else 146 | let state=-1 147 | call insert(result, item) 148 | break 149 | endif 150 | elseif state==10 151 | if token.value == '::' 152 | call insert(item.tokens, token) 153 | let state = 9 154 | " Maybe end of tokens 155 | else 156 | let state=-1 157 | call insert(result, item) 158 | break 159 | endif 160 | endif 161 | endfor 162 | 163 | if index([2, 5, 8, 9, 10], state)>=0 164 | if state==5 165 | let item.kind = omni#cpp#utils#GetCastType(item.tokens) 166 | endif 167 | call insert(result, item) 168 | endif 169 | 170 | return result 171 | endfunc 172 | 173 | " Resolve type information of items 174 | " @param namespaces: list of namespaces used in the file 175 | " @param szCurrentClassScope: the current class scope, only used for the first 176 | " item to detect if this item is a class member (attribute, method) 177 | " @param items: list of item, can be an empty list @see GetItemsToComplete 178 | function! omni#cpp#items#ResolveItemsTypeInfo(contextStack, items) 179 | " Note: kind = itemVariable|cCast|cppCast|template|function|itemUnknown|this 180 | " For the first item, if it's a variable we try to detect the type of the 181 | " variable with the function searchdecl. If it fails, thanks to the 182 | " current class scope, we try to detect if the variable is an attribute 183 | " member. 184 | " If the kind of the item is a function, we have to first check if the 185 | " function is a method of the class, if it fails we try to get a match in 186 | " the global namespace. After that we get the returned type of the 187 | " function. 188 | " It the kind is a C cast or C++ cast, there is no problem, it's the 189 | " easiest case. We just extract the type of the cast. 190 | 191 | let szCurrentContext = '' 192 | let typeInfo = {} 193 | " Note: We search the decl only for the first item 194 | let bSearchDecl = 1 195 | for item in a:items 196 | let curItem = item 197 | if index(['itemVariable', 'itemFunction'], curItem.kind)>=0 198 | " Note: a variable can be : MyNs::MyClass::_var or _var or (*pVar) 199 | " or _var[0][0] 200 | let szSymbol = s:GetSymbol(curItem.tokens) 201 | 202 | " If we have MyNamespace::myVar 203 | " We add MyNamespace in the context stack set szSymbol to myVar 204 | if match(szSymbol, '::\w\+$') >= 0 205 | let szCurrentContext = substitute(szSymbol, '::\w\+$', '', 'g') 206 | let szSymbol = matchstr(szSymbol, '\w\+$') 207 | endif 208 | let tmpContextStack = a:contextStack 209 | if szCurrentContext != '' 210 | let tmpContextStack = [szCurrentContext] + a:contextStack 211 | endif 212 | 213 | if curItem.kind == 'itemVariable' 214 | let typeInfo = s:GetTypeInfoOfVariable(tmpContextStack, szSymbol, bSearchDecl) 215 | else 216 | let typeInfo = s:GetTypeInfoOfReturnedType(tmpContextStack, szSymbol) 217 | endif 218 | 219 | elseif curItem.kind == 'itemThis' 220 | if len(a:contextStack) 221 | let typeInfo = omni#cpp#utils#CreateTypeInfo(substitute(a:contextStack[0], '^::', '', 'g')) 222 | endif 223 | elseif curItem.kind == 'itemCast' 224 | let typeInfo = omni#cpp#utils#CreateTypeInfo(s:ResolveCCast(curItem.tokens)) 225 | elseif curItem.kind == 'itemCppCast' 226 | let typeInfo = omni#cpp#utils#CreateTypeInfo(s:ResolveCppCast(curItem.tokens)) 227 | elseif curItem.kind == 'itemScope' 228 | let typeInfo = omni#cpp#utils#CreateTypeInfo(substitute(s:TokensToString(curItem.tokens), '\s', '', 'g')) 229 | endif 230 | 231 | if omni#cpp#utils#IsTypeInfoValid(typeInfo) 232 | let szCurrentContext = omni#cpp#utils#GetTypeInfoString(typeInfo) 233 | endif 234 | let bSearchDecl = 0 235 | endfor 236 | 237 | return typeInfo 238 | endfunc 239 | 240 | " Get symbol name 241 | function! s:GetSymbol(tokens) 242 | let szSymbol = '' 243 | let state = 0 244 | for token in a:tokens 245 | if state == 0 246 | if token.value == '::' 247 | let szSymbol .= token.value 248 | let state = 1 249 | elseif token.kind == 'cppWord' 250 | let szSymbol .= token.value 251 | let state = 2 252 | " Maybe end of token 253 | endif 254 | elseif state == 1 255 | if token.kind == 'cppWord' 256 | let szSymbol .= token.value 257 | let state = 2 258 | " Maybe end of token 259 | else 260 | " Error 261 | break 262 | endif 263 | elseif state == 2 264 | if token.value == '::' 265 | let szSymbol .= token.value 266 | let state = 1 267 | else 268 | break 269 | endif 270 | endif 271 | endfor 272 | return szSymbol 273 | endfunc 274 | 275 | " Search a declaration. 276 | " eg: std::map 277 | " can be empty 278 | " Note: The returned type info can be a typedef 279 | " The typedef resolution is done later 280 | " @return 281 | " - a dictionnary where keys are 282 | " - type: the type of value same as type() 283 | " - value: the value 284 | function! s:GetTypeInfoOfVariable(contextStack, szVariable, bSearchDecl) 285 | let result = {} 286 | 287 | if a:bSearchDecl 288 | " Search type of declaration 289 | "let result = s:SearchTypeInfoOfDecl(a:szVariable) 290 | let result = s:SearchDecl(a:szVariable) 291 | endif 292 | 293 | if result=={} 294 | let szFilter = "index(['m', 'v'], v:val.kind[0])>=0" 295 | let tagItem = s:ResolveSymbol(a:contextStack, a:szVariable, szFilter) 296 | if tagItem=={} 297 | return result 298 | endif 299 | 300 | let szCmdWithoutVariable = substitute(omni#cpp#utils#ExtractCmdFromTagItem(tagItem), '\C\<'.a:szVariable.'\>.*', '', 'g') 301 | let tokens = omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCodeFromLine(szCmdWithoutVariable)) 302 | let result = omni#cpp#utils#CreateTypeInfo(omni#cpp#utils#ExtractTypeInfoFromTokens(tokens)) 303 | " TODO: Namespace resolution for result 304 | 305 | if result != {} && result.value=='' 306 | " result.value=='' 307 | " eg: 308 | " struct 309 | " { 310 | " }gVariable; 311 | if has_key(tagItem, 'typeref') 312 | " Maybe the variable is a global var of an 313 | " unnamed class, struct or union. 314 | " eg: 315 | " 1) 316 | " struct 317 | " { 318 | " }gVariable; 319 | " In this case we need the tags (the patched version) 320 | " Note: We can have a named type like this: 321 | " 2) 322 | " class A 323 | " { 324 | " }gVariable; 325 | if s:IsUnnamedType(tagItem) 326 | " It's an unnamed type we are in the case 1) 327 | let result = omni#cpp#utils#CreateTypeInfo(tagItem) 328 | else 329 | " It's not an unnamed type we are in the case 2) 330 | 331 | " eg: tagItem.typeref = 'struct:MY_STRUCT::MY_SUBSTRUCT' 332 | let szTypeRef = substitute(tagItem.typeref, '^\w\+:', '', '') 333 | 334 | " eg: szTypeRef = 'MY_STRUCT::MY_SUBSTRUCT' 335 | let result = omni#cpp#utils#CreateTypeInfo(szTypeRef) 336 | endif 337 | endif 338 | endif 339 | endif 340 | return result 341 | endfunc 342 | 343 | " Get the type info string from the returned type of function 344 | function! s:GetTypeInfoOfReturnedType(contextStack, szFunctionName) 345 | let result = {} 346 | 347 | let szFilter = "index(['f', 'p'], v:val.kind[0])>=0" 348 | let tagItem = s:ResolveSymbol(a:contextStack, a:szFunctionName, szFilter) 349 | 350 | if tagItem != {} 351 | let szCmdWithoutVariable = substitute(omni#cpp#utils#ExtractCmdFromTagItem(tagItem), '\C\<'.a:szFunctionName.'\>.*', '', 'g') 352 | let tokens = omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCodeFromLine(szCmdWithoutVariable)) 353 | let result = omni#cpp#utils#CreateTypeInfo(omni#cpp#utils#ExtractTypeInfoFromTokens(tokens)) 354 | " TODO: Namespace resolution for result 355 | return result 356 | endif 357 | return result 358 | endfunc 359 | 360 | " Resolve a symbol, return a tagItem 361 | " Gets the first symbol found in the context stack 362 | function! s:ResolveSymbol(contextStack, szSymbol, szTagFilter) 363 | let tagItem = {} 364 | for szCurrentContext in a:contextStack 365 | if szCurrentContext != '::' 366 | let szTagQuery = substitute(szCurrentContext, '^::', '', 'g').'::'.a:szSymbol 367 | else 368 | let szTagQuery = a:szSymbol 369 | endif 370 | 371 | let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') 372 | call filter(tagList, a:szTagFilter) 373 | if len(tagList) 374 | let tagItem = tagList[0] 375 | break 376 | endif 377 | endfor 378 | return tagItem 379 | endfunc 380 | 381 | " Return if the tag item represent an unnamed type 382 | function! s:IsUnnamedType(tagItem) 383 | let bResult = 0 384 | if has_key(a:tagItem, 'typeref') 385 | " Note: Thanks for __anon ! 386 | let bResult = match(a:tagItem.typeref, '\C\<__anon') >= 0 387 | endif 388 | return bResult 389 | endfunc 390 | 391 | " Search the declaration of a variable and return the type info 392 | function! s:SearchTypeInfoOfDecl(szVariable) 393 | let szReVariable = '\C\<'.a:szVariable.'\>' 394 | 395 | let originalPos = getpos('.') 396 | let origPos = originalPos[1:2] 397 | let curPos = origPos 398 | let stopPos = origPos 399 | 400 | while curPos !=[0,0] 401 | " We go to the start of the current scope 402 | let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) 403 | if curPos != [0,0] 404 | let matchPos = curPos 405 | " Now want to search our variable but we don't want to go in child 406 | " scope 407 | while matchPos != [0,0] 408 | let matchPos = searchpos('{\|'.szReVariable, 'W', stopPos[0]) 409 | if matchPos != [0,0] 410 | " We ignore matches under comment 411 | if omni#cpp#utils#IsCursorInCommentOrString() 412 | continue 413 | endif 414 | 415 | " Getting the current line 416 | let szLine = getline('.') 417 | if match(szLine, szReVariable)>=0 418 | " We found our variable 419 | " Check if the current instruction is a decl instruction 420 | let tokens = omni#cpp#utils#TokenizeCurrentInstruction() 421 | let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) 422 | if szTypeInfo != '' 423 | call setpos('.', originalPos) 424 | return omni#cpp#utils#CreateTypeInfo(szTypeInfo) 425 | endif 426 | else 427 | " We found a child scope, we don't want to go in, thus 428 | " we search for the end } of this child scope 429 | let bracketEnd = searchpairpos('{', '', '}', 'nW', g:omni#cpp#utils#expIgnoreComments) 430 | if bracketEnd == [0,0] 431 | break 432 | endif 433 | 434 | if bracketEnd[0] >= stopPos[0] 435 | " The end of the scope is after our cursor we stop 436 | " the search 437 | break 438 | else 439 | " We move the cursor and continue to search our 440 | " variable 441 | call setpos('.', [0, bracketEnd[0], bracketEnd[1], 0]) 442 | endif 443 | endif 444 | endif 445 | endwhile 446 | 447 | " Backing to the start of the scope 448 | call setpos('.', [0,curPos[0], curPos[1], 0]) 449 | let stopPos = curPos 450 | endif 451 | endwhile 452 | 453 | let result = {} 454 | if s:LocalSearchDecl(a:szVariable)==0 && !omni#cpp#utils#IsCursorInCommentOrString() 455 | let tokens = omni#cpp#utils#TokenizeCurrentInstruction() 456 | let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) 457 | if szTypeInfo != '' 458 | let result = omni#cpp#utils#CreateTypeInfo(szTypeInfo) 459 | endif 460 | endif 461 | 462 | call setpos('.', originalPos) 463 | 464 | return result 465 | endfunc 466 | 467 | " Search a declaration 468 | " @return 469 | " - tokens of the current instruction if success 470 | " - empty list if failure 471 | function! s:SearchDecl(szVariable) 472 | let result = {} 473 | let originalPos = getpos('.') 474 | let searchResult = s:LocalSearchDecl(a:szVariable) 475 | if searchResult==0 476 | " searchdecl() may detect a decl if the variable is in a conditional 477 | " instruction (if, elseif, while etc...) 478 | " We have to check if the detected decl is really a decl instruction 479 | let tokens = omni#cpp#utils#TokenizeCurrentInstruction() 480 | 481 | for token in tokens 482 | " Simple test 483 | if index(['if', 'elseif', 'while', 'for', 'switch'], token.value)>=0 484 | " Invalid declaration instruction 485 | call setpos('.', originalPos) 486 | return result 487 | endif 488 | endfor 489 | 490 | let szTypeInfo = s:ExtractTypeInfoFromDecl(tokens) 491 | if szTypeInfo != '' 492 | let result = omni#cpp#utils#CreateTypeInfo(szTypeInfo) 493 | endif 494 | endif 495 | call setpos('.', originalPos) 496 | return result 497 | endfunc 498 | 499 | " Extract the type info string from an instruction. 500 | " We use a small parser to extract the type 501 | " We parse the code according to a C++ BNF from: http://www.nongnu.org/hcb/#basic.link 502 | " @param tokens: token list of the current instruction 503 | function! s:ExtractTypeInfoFromDecl(tokens) 504 | return omni#cpp#utils#ExtractTypeInfoFromTokens(a:tokens) 505 | endfunc 506 | 507 | " Convert tokens to string 508 | function! s:TokensToString(tokens) 509 | let result = '' 510 | for token in a:tokens 511 | let result = result . token.value . ' ' 512 | endfor 513 | return result[:-2] 514 | endfunc 515 | 516 | " Resolve a cast. 517 | " Resolve a C++ cast 518 | " @param list of token. tokens must be a list that represents 519 | " a cast expression (C++ cast) the function does not control 520 | " if it's a cast or not 521 | " eg: static_cast(something) 522 | " @return type info string 523 | function! s:ResolveCppCast(tokens) 524 | return omni#cpp#utils#ExtractTypeInfoFromTokens(s:ResolveCast(a:tokens, '<', '>')) 525 | endfunc 526 | 527 | " Resolve a cast. 528 | " Resolve a C cast 529 | " @param list of token. tokens must be a list that represents 530 | " a cast expression (C cast) the function does not control 531 | " if it's a cast or not 532 | " eg: (MyClass*)something 533 | " @return type info string 534 | function! s:ResolveCCast(tokens) 535 | return omni#cpp#utils#ExtractTypeInfoFromTokens(s:ResolveCast(a:tokens, '(', ')')) 536 | endfunc 537 | 538 | " Resolve a cast. 539 | " Resolve a C cast 540 | " @param list of token. tokens must be a list that represents 541 | " a cast expression (C cast) the function does not control 542 | " if it's a cast or not 543 | " eg: (MyClass*)something 544 | " @return type tokens 545 | function! s:ResolveCast(tokens, startChar, endChar) 546 | let tokens = omni#cpp#utils#BuildParenthesisGroups(a:tokens) 547 | 548 | " We remove useless parenthesis eg: (((MyClass))) 549 | let tokens = omni#cpp#utils#SimplifyParenthesis(tokens) 550 | 551 | let countItem=0 552 | let startIndex = -1 553 | let endIndex = -1 554 | let i = 0 555 | for token in tokens 556 | if startIndex==-1 557 | if token.value==a:startChar 558 | let countItem += 1 559 | let startIndex = i 560 | endif 561 | else 562 | if token.value==a:startChar 563 | let countItem += 1 564 | elseif token.value==a:endChar 565 | let countItem -= 1 566 | endif 567 | 568 | if countItem==0 569 | let endIndex = i 570 | break 571 | endif 572 | endif 573 | let i+=1 574 | endfor 575 | 576 | return tokens[startIndex+1 : endIndex-1] 577 | endfunc 578 | 579 | " Replacement for build-in function 'searchdecl' 580 | " It does not require that the upper-level bracket is in the first column. 581 | " Otherwise it should be equal to 'searchdecl(name, 0, 1)' 582 | " @param name: name of variable to find declaration for 583 | function! s:LocalSearchDecl(name) 584 | 585 | if g:OmniCpp_LocalSearchDecl == 0 586 | let bUserIgnoreCase = &ignorecase 587 | 588 | " Forcing the noignorecase option 589 | " avoid bug when, for example, if we have a declaration like this : "A a;" 590 | set noignorecase 591 | 592 | let result = searchdecl(a:name, 0, 1) 593 | 594 | " Restoring user's setting 595 | let &ignorecase = bUserIgnoreCase 596 | 597 | return result 598 | endif 599 | 600 | let lastpos = getpos('.') 601 | let winview = winsaveview() 602 | let lastfoldenable = &foldenable 603 | let &foldenable = 0 604 | 605 | " We add \C (noignorecase) to 606 | " avoid bug when, for example, if we have a declaration like this : "A a;" 607 | let varname = "\\C\\<" . a:name . "\\>" 608 | 609 | " Go to first blank line before begin of highest scope 610 | normal 99[{ 611 | let scopepos = getpos('.') 612 | while (line('.') > 1) && (len(split(getline('.'))) > 0) 613 | call cursor(line('.')-1, 0) 614 | endwhile 615 | 616 | let declpos = [ 0, 0, 0, 0 ] 617 | while search(varname, '', scopepos[1]) > 0 618 | " Check if we are a string or a comment 619 | if omni#cpp#utils#IsCursorInCommentOrString() 620 | continue 621 | endif 622 | 623 | " Remember match 624 | let declpos = getpos('.') 625 | endwhile 626 | if declpos[1] != 0 627 | " We found a match 628 | call winrestview(winview) 629 | call setpos('.', declpos) 630 | let &foldenable = lastfoldenable 631 | return 0 632 | endif 633 | 634 | while search(varname, '', lastpos[1]) > 0 635 | " Check if current scope is ending before variable 636 | let old_cur = getpos('.') 637 | normal ]} 638 | let new_cur = getpos('.') 639 | call setpos('.', old_cur) 640 | if (new_cur[1] < lastpos[1]) || ((new_cur[1] == lastpos[1]) && (new_cur[2] < lastpos[2])) 641 | continue 642 | endif 643 | 644 | " Check if we are a string or a comment 645 | if omni#cpp#utils#IsCursorInCommentOrString() 646 | continue 647 | endif 648 | 649 | " We found match 650 | call winrestview(winview) 651 | call setpos('.', old_cur) 652 | let &foldenable = lastfoldenable 653 | return 0 654 | endwhile 655 | 656 | " No match found. 657 | call winrestview(winview) 658 | let &foldenable = lastfoldenable 659 | return 1 660 | endfunc 661 | -------------------------------------------------------------------------------- /autoload/omni/cpp/maycomplete.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | " Check if we can use omni completion in the current buffer 6 | function! s:CanUseOmnicompletion() 7 | " For C and C++ files and only if the omnifunc is omni#cpp#complete#Main 8 | return (index(['c', 'cpp'], &filetype)>=0 && &omnifunc == 'omni#cpp#complete#Main' && !omni#cpp#utils#IsCursorInCommentOrString()) 9 | endfunc 10 | 11 | " Return the mapping of omni completion 12 | function! omni#cpp#maycomplete#Complete() 13 | let szOmniMapping = "\\" 14 | 15 | " 0 = don't select first item 16 | " 1 = select first item (inserting it to the text, default vim behaviour) 17 | " 2 = select first item (without inserting it to the text) 18 | if g:OmniCpp_SelectFirstItem == 0 19 | " We have to force the menuone option to avoid confusion when there is 20 | " only one popup item 21 | set completeopt-=menu 22 | set completeopt+=menuone 23 | let szOmniMapping .= "\" 24 | elseif g:OmniCpp_SelectFirstItem == 2 25 | " We have to force the menuone option to avoid confusion when there is 26 | " only one popup item 27 | set completeopt-=menu 28 | set completeopt+=menuone 29 | let szOmniMapping .= "\" 30 | let szOmniMapping .= "\=pumvisible() ? \"\\\" : \"\"\" 31 | endif 32 | return szOmniMapping 33 | endfunc 34 | 35 | " May complete function for dot 36 | function! omni#cpp#maycomplete#Dot() 37 | if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteDot 38 | let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction('.')) 39 | if len(g:omni#cpp#items#data) 40 | let s:bMayComplete = 1 41 | return '.' . omni#cpp#maycomplete#Complete() 42 | endif 43 | endif 44 | return '.' 45 | endfunc 46 | " May complete function for arrow 47 | function! omni#cpp#maycomplete#Arrow() 48 | if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteArrow 49 | let index = col('.') - 2 50 | if index >= 0 51 | let char = getline('.')[index] 52 | if char == '-' 53 | let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction('>')) 54 | if len(g:omni#cpp#items#data) 55 | let s:bMayComplete = 1 56 | return '>' . omni#cpp#maycomplete#Complete() 57 | endif 58 | endif 59 | endif 60 | endif 61 | return '>' 62 | endfunc 63 | 64 | " May complete function for double points 65 | function! omni#cpp#maycomplete#Scope() 66 | if s:CanUseOmnicompletion() && g:OmniCpp_MayCompleteScope 67 | let index = col('.') - 2 68 | if index >= 0 69 | let char = getline('.')[index] 70 | if char == ':' 71 | let g:omni#cpp#items#data = omni#cpp#items#Get(omni#cpp#utils#TokenizeCurrentInstruction(':')) 72 | if len(g:omni#cpp#items#data) 73 | if len(g:omni#cpp#items#data[-1].tokens) && g:omni#cpp#items#data[-1].tokens[-1].value != '::' 74 | let s:bMayComplete = 1 75 | return ':' . omni#cpp#maycomplete#Complete() 76 | endif 77 | endif 78 | endif 79 | endif 80 | endif 81 | return ':' 82 | endfunc 83 | -------------------------------------------------------------------------------- /autoload/omni/cpp/namespaces.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | let g:omni#cpp#namespaces#CacheResolve = {} 6 | let g:omni#cpp#namespaces#CacheUsing = {} 7 | " TODO: For the next release 8 | "let g:omni#cpp#namespaces#CacheAlias = {} 9 | 10 | " Get the using namespace list from a line 11 | function! s:GetNamespaceAliasListFromLine(szLine) 12 | let result = {} 13 | let tokens = omni#cpp#tokenizer#Tokenize(a:szLine) 14 | let szAlias = '' 15 | let szNamespace = '' 16 | let state = 0 17 | for token in tokens 18 | if state==0 19 | let szAlias = '' 20 | let szNamespace = '' 21 | if token.value == '/*' 22 | let state = 1 23 | elseif token.value == '//' 24 | " It's a comment 25 | let state = -1 26 | break 27 | elseif token.value == 'namespace' 28 | let state = 2 29 | endif 30 | elseif state==1 31 | if token.value == '*/' 32 | let state=0 33 | endif 34 | elseif state==2 35 | if token.kind == 'cppWord' 36 | let szAlias .= token.value 37 | let state = 3 38 | else 39 | let state = -1 40 | break 41 | endif 42 | elseif state == 3 43 | if token.value == '=' 44 | let state = 4 45 | else 46 | let state = -1 47 | break 48 | endif 49 | elseif state == 4 50 | if token.value == '::' 51 | let szNamespace .= token.value 52 | let state = 5 53 | elseif token.kind == 'cppWord' 54 | let szNamespace .= token.value 55 | let state = 6 56 | " Maybe end of tokens 57 | endif 58 | elseif state==5 59 | if token.kind == 'cppWord' 60 | let szNamespace .= token.value 61 | let state = 6 62 | " Maybe end of tokens 63 | else 64 | " Error, we can't have 'namespace ALIAS = Something::' 65 | let state = -1 66 | break 67 | endif 68 | elseif state==6 69 | if token.value == '::' 70 | let szNamespace .= token.value 71 | let state = 5 72 | else 73 | call extend(result, {szAlias : szNamespace}) 74 | let state = 0 75 | endif 76 | endif 77 | endfor 78 | 79 | if state == 6 80 | call extend(result, {szAlias : szNamespace}) 81 | endif 82 | 83 | return result 84 | endfunc 85 | 86 | " Get the using namespace list from a line 87 | function! s:GetNamespaceListFromLine(szLine) 88 | let result = [] 89 | let tokens = omni#cpp#tokenizer#Tokenize(a:szLine) 90 | let szNamespace = '' 91 | let state = 0 92 | for token in tokens 93 | if state==0 94 | let szNamespace = '' 95 | if token.value == '/*' 96 | let state = 1 97 | elseif token.value == '//' 98 | " It's a comment 99 | let state = -1 100 | break 101 | elseif token.value == 'using' 102 | let state = 2 103 | endif 104 | elseif state==1 105 | if token.value == '*/' 106 | let state=0 107 | endif 108 | elseif state==2 109 | if token.value == 'namespace' 110 | let state = 3 111 | else 112 | " Error, 'using' must be followed by 'namespace' 113 | let state = -1 114 | break 115 | endif 116 | elseif state==3 117 | if token.value == '::' 118 | let szNamespace .= token.value 119 | let state = 4 120 | elseif token.kind == 'cppWord' 121 | let szNamespace .= token.value 122 | let state = 5 123 | " Maybe end of tokens 124 | endif 125 | elseif state==4 126 | if token.kind == 'cppWord' 127 | let szNamespace .= token.value 128 | let state = 5 129 | " Maybe end of tokens 130 | else 131 | " Error, we can't have 'using namespace Something::' 132 | let state = -1 133 | break 134 | endif 135 | elseif state==5 136 | if token.value == '::' 137 | let szNamespace .= token.value 138 | let state = 4 139 | else 140 | call extend(result, [szNamespace]) 141 | let state = 0 142 | endif 143 | endif 144 | endfor 145 | 146 | if state == 5 147 | call extend(result, [szNamespace]) 148 | endif 149 | 150 | return result 151 | endfunc 152 | 153 | " Get the namespace list from a namespace map 154 | function! s:GetUsingNamespaceListFromMap(namespaceMap, ...) 155 | let stopLine = 0 156 | if a:0>0 157 | let stopLine = a:1 158 | endif 159 | 160 | let result = [] 161 | let keys = sort(keys(a:namespaceMap), 'omni#common#utils#CompareNumber') 162 | for i in keys 163 | if stopLine != 0 && i > stopLine 164 | break 165 | endif 166 | call extend(result, a:namespaceMap[i]) 167 | endfor 168 | return result 169 | endfunc 170 | 171 | " Get global using namespace list from the current buffer 172 | function! omni#cpp#namespaces#GetListFromCurrentBuffer(...) 173 | let namespaceMap = s:GetAllUsingNamespaceMapFromCurrentBuffer() 174 | let result = [] 175 | if namespaceMap != {} 176 | let result = s:GetUsingNamespaceListFromMap(namespaceMap, (a:0 > 0)? a:1 : line('.')) 177 | endif 178 | return result 179 | endfunc 180 | 181 | " Get global using namespace map from the current buffer and include files recursively 182 | function! s:GetAllUsingNamespaceMapFromCurrentBuffer(...) 183 | let includeGuard = (a:0>0)? a:1 : {} 184 | 185 | let szBufferName = getreg("%") 186 | let szFilePath = omni#cpp#utils#ResolveFilePath(szBufferName) 187 | let szFilePath = (szFilePath=='')? szBufferName : szFilePath 188 | 189 | let namespaceMap = {} 190 | if has_key(includeGuard, szFilePath) 191 | return namespaceMap 192 | else 193 | let includeGuard[szFilePath] = 1 194 | endif 195 | 196 | let namespaceMap = omni#cpp#namespaces#GetMapFromCurrentBuffer() 197 | 198 | if g:OmniCpp_NamespaceSearch != 2 199 | " We don't search included files if OmniCpp_NamespaceSearch != 2 200 | return namespaceMap 201 | endif 202 | 203 | for inc in omni#cpp#includes#GetList() 204 | let lnum = inc.pos[0] 205 | let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard) 206 | if tmpMap != {} 207 | if has_key(namespaceMap, lnum) 208 | call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap)) 209 | else 210 | let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap) 211 | endif 212 | endif 213 | endfor 214 | 215 | return namespaceMap 216 | endfunc 217 | 218 | " Get global using namespace map from a file and include files recursively 219 | function! s:GetAllUsingNamespaceMapFromFile(szFilePath, ...) 220 | let includeGuard = {} 221 | if a:0 >0 222 | let includeGuard = a:1 223 | endif 224 | 225 | let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) 226 | let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath 227 | 228 | let namespaceMap = {} 229 | if has_key(includeGuard, szFilePath) 230 | return namespaceMap 231 | else 232 | let includeGuard[szFilePath] = 1 233 | endif 234 | 235 | " If g:OmniCpp_NamespaceSearch == 1 (search namespaces only in the current 236 | " buffer) we don't use cache for the current buffer 237 | let namespaceMap = omni#cpp#namespaces#GetMapFromBuffer(szFilePath, g:OmniCpp_NamespaceSearch==1) 238 | 239 | if g:OmniCpp_NamespaceSearch != 2 240 | " We don't search included files if OmniCpp_NamespaceSearch != 2 241 | return namespaceMap 242 | endif 243 | 244 | for inc in omni#cpp#includes#GetList(szFilePath) 245 | let lnum = inc.pos[0] 246 | let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard) 247 | if tmpMap != {} 248 | if has_key(namespaceMap, lnum) 249 | call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap)) 250 | else 251 | let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap) 252 | endif 253 | endif 254 | endfor 255 | 256 | return namespaceMap 257 | endfunc 258 | 259 | " Get global using namespace map from a the current buffer 260 | function! omni#cpp#namespaces#GetMapFromCurrentBuffer() 261 | let namespaceMap = {} 262 | let originalPos = getpos('.') 263 | 264 | call setpos('.', [0, 1, 1, 0]) 265 | let curPos = [1,1] 266 | while curPos != [0,0] 267 | let curPos = searchpos('\C^using\s\+namespace', 'W') 268 | if curPos != [0,0] 269 | let szLine = getline('.') 270 | let startPos = curPos[1] 271 | let endPos = match(szLine, ';', startPos-1) 272 | if endPos!=-1 273 | " We get the namespace list from the line 274 | let namespaceMap[curPos[0]] = s:GetNamespaceListFromLine(szLine) 275 | endif 276 | endif 277 | endwhile 278 | 279 | call setpos('.', originalPos) 280 | return namespaceMap 281 | endfunc 282 | 283 | " Get global using namespace map from a file 284 | function! omni#cpp#namespaces#GetMapFromBuffer(szFilePath, ...) 285 | let bUpdate = 0 286 | if a:0 > 0 287 | let bUpdate = a:1 288 | endif 289 | 290 | let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath) 291 | let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath 292 | 293 | if !bUpdate && has_key(g:omni#cpp#namespaces#CacheUsing, szFilePath) 294 | return copy(g:omni#cpp#namespaces#CacheUsing[szFilePath]) 295 | endif 296 | 297 | let namespaceMap = {} 298 | " The file exists, we get the global namespaces in this file 299 | let szFixedPath = escape(szFilePath, g:omni#cpp#utils#szEscapedCharacters) 300 | execute 'silent! lvimgrep /\C^using\s\+namespace/gj '.szFixedPath 301 | 302 | " key = line number 303 | " value = list of namespaces 304 | let listQuickFix = getloclist(0) 305 | for qf in listQuickFix 306 | let szLine = qf.text 307 | let startPos = qf.col 308 | let endPos = match(szLine, ';', startPos-1) 309 | if endPos!=-1 310 | " We get the namespace list from the line 311 | let namespaceMap[qf.lnum] = s:GetNamespaceListFromLine(szLine) 312 | endif 313 | endfor 314 | 315 | if szFixedPath != '' 316 | let g:omni#cpp#namespaces#CacheUsing[szFixedPath] = namespaceMap 317 | endif 318 | 319 | return copy(namespaceMap) 320 | endfunc 321 | 322 | " Get the stop position when searching for local variables 323 | function! s:GetStopPositionForLocalSearch() 324 | " Stop position when searching a local variable 325 | let originalPos = getpos('.') 326 | let origPos = originalPos[1:2] 327 | let stopPosition = origPos 328 | let curPos = origPos 329 | while curPos !=[0,0] 330 | let stopPosition = curPos 331 | let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) 332 | endwhile 333 | call setpos('.', originalPos) 334 | 335 | return stopPosition 336 | endfunc 337 | 338 | " Get namespaces alias used at the cursor postion in a vim buffer 339 | " Note: The result depends on the current cursor position 340 | " @return 341 | " - Map of namespace alias 342 | function! s:GetNamespaceAliasMap() 343 | " We store the cursor position because searchpairpos() moves the cursor 344 | let result = {} 345 | let originalPos = getpos('.') 346 | let origPos = originalPos[1:2] 347 | 348 | let stopPos = s:GetStopPositionForLocalSearch() 349 | let stopLine = stopPos[0] 350 | let curPos = origPos 351 | let lastLine = 0 352 | let nextStopLine = origPos[0] 353 | let szReAlias = '\Cnamespace\s\+\w\+\s\+=' 354 | while curPos !=[0,0] 355 | let curPos = searchpos('}\|\('. szReAlias .'\)', 'bW',stopLine) 356 | if curPos!=[0,0] && curPos[0]!=lastLine 357 | let lastLine = curPos[0] 358 | 359 | let szLine = getline('.') 360 | if origPos[0] == curPos[0] 361 | " We get the line until cursor position 362 | let szLine = szLine[:origPos[1]] 363 | endif 364 | 365 | let szLine = omni#cpp#utils#GetCodeFromLine(szLine) 366 | if match(szLine, szReAlias)<0 367 | " We found a '}' 368 | let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) 369 | else 370 | " We get the namespace alias from the line 371 | call extend(result, s:GetNamespaceAliasListFromLine(szLine)) 372 | let nextStopLine = curPos[0] 373 | endif 374 | endif 375 | endwhile 376 | 377 | " Setting the cursor to the original position 378 | call setpos('.', originalPos) 379 | 380 | call s:ResolveAliasKeys(result) 381 | return result 382 | endfunc 383 | 384 | " Resolve an alias 385 | " eg: namespace IAmAnAlias1 = Ns1 386 | " eg: namespace IAmAnAlias2 = IAmAnAlias1::Ns2 387 | " => IAmAnAlias2 = Ns1::Ns2 388 | function! s:ResolveAliasKey(mapNamespaceAlias, szAlias) 389 | let szResult = a:mapNamespaceAlias[a:szAlias] 390 | " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3'] 391 | let listNamespace = split(szResult, '::') 392 | if len(listNamespace) 393 | " szBeginPart = 'Ns1' 394 | let szBeginPart = remove(listNamespace, 0) 395 | 396 | " Is 'Ns1' an alias ? 397 | if has_key(a:mapNamespaceAlias, szBeginPart) && szBeginPart != a:szAlias 398 | " Resolving alias 'Ns1' 399 | " eg: Ns1 = NsResolved 400 | let szResult = s:ResolveAliasKey(a:mapNamespaceAlias, szBeginPart) 401 | " szEndPart = 'Ns2::Ns3' 402 | let szEndPart = join(listNamespace, '::') 403 | if szEndPart != '' 404 | " Concatenation => szResult = 'NsResolved::Ns2::Ns3' 405 | let szResult .= '::' . szEndPart 406 | endif 407 | endif 408 | endif 409 | return szResult 410 | endfunc 411 | 412 | " Resolve all keys in the namespace alias map 413 | function! s:ResolveAliasKeys(mapNamespaceAlias) 414 | let mapNamespaceAlias = a:mapNamespaceAlias 415 | call map(mapNamespaceAlias, 's:ResolveAliasKey(mapNamespaceAlias, v:key)') 416 | endfunc 417 | 418 | " Resolve namespace alias 419 | function! omni#cpp#namespaces#ResolveAlias(mapNamespaceAlias, szNamespace) 420 | let szResult = a:szNamespace 421 | " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3'] 422 | let listNamespace = split(a:szNamespace, '::') 423 | if len(listNamespace) 424 | " szBeginPart = 'Ns1' 425 | let szBeginPart = remove(listNamespace, 0) 426 | 427 | " Is 'Ns1' an alias ? 428 | if has_key(a:mapNamespaceAlias, szBeginPart) 429 | " Resolving alias 'Ns1' 430 | " eg: Ns1 = NsResolved 431 | let szResult = a:mapNamespaceAlias[szBeginPart] 432 | " szEndPart = 'Ns2::Ns3' 433 | let szEndPart = join(listNamespace, '::') 434 | if szEndPart != '' 435 | " Concatenation => szResult = 'NsResolved::Ns2::Ns3' 436 | let szResult .= '::' . szEndPart 437 | endif 438 | 439 | " If a:szNamespace starts with '::' we add '::' to the beginning 440 | " of the result 441 | if match(a:szNamespace, '^::')>=0 442 | let szResult = omni#cpp#utils#SimplifyScope('::' . szResult) 443 | endif 444 | endif 445 | endif 446 | return szResult 447 | endfunc 448 | 449 | " Resolve namespace alias 450 | function! s:ResolveAliasInNamespaceList(mapNamespaceAlias, listNamespaces) 451 | call map(a:listNamespaces, 'omni#cpp#namespaces#ResolveAlias(a:mapNamespaceAlias, v:val)') 452 | endfunc 453 | 454 | " Get namespaces used at the cursor postion in a vim buffer 455 | " Note: The result depends on the current cursor position 456 | " @return 457 | " - List of namespace used in the reverse order 458 | function! omni#cpp#namespaces#GetUsingNamespaces() 459 | " We have to get local using namespace declarations 460 | " We need the current cursor position and the position of the start of the 461 | " current scope 462 | 463 | " We store the cursor position because searchpairpos() moves the cursor 464 | let result = [] 465 | let originalPos = getpos('.') 466 | let origPos = originalPos[1:2] 467 | 468 | let stopPos = s:GetStopPositionForLocalSearch() 469 | 470 | let stopLine = stopPos[0] 471 | let curPos = origPos 472 | let lastLine = 0 473 | let nextStopLine = origPos[0] 474 | while curPos !=[0,0] 475 | let curPos = searchpos('\C}\|\(using\s\+namespace\)', 'bW',stopLine) 476 | if curPos!=[0,0] && curPos[0]!=lastLine 477 | let lastLine = curPos[0] 478 | 479 | let szLine = getline('.') 480 | if origPos[0] == curPos[0] 481 | " We get the line until cursor position 482 | let szLine = szLine[:origPos[1]] 483 | endif 484 | 485 | let szLine = omni#cpp#utils#GetCodeFromLine(szLine) 486 | if match(szLine, '\Cusing\s\+namespace')<0 487 | " We found a '}' 488 | let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) 489 | else 490 | " We get the namespace list from the line 491 | let result = s:GetNamespaceListFromLine(szLine) + result 492 | let nextStopLine = curPos[0] 493 | endif 494 | endif 495 | endwhile 496 | 497 | " Setting the cursor to the original position 498 | call setpos('.', originalPos) 499 | 500 | " 2) Now we can get all global using namespace declaration from the 501 | " beginning of the file to nextStopLine 502 | let result = omni#cpp#namespaces#GetListFromCurrentBuffer(nextStopLine) + result 503 | 504 | " Resolving alias in the namespace list 505 | " TODO: For the next release 506 | "let g:omni#cpp#namespaces#CacheAlias= s:GetNamespaceAliasMap() 507 | "call s:ResolveAliasInNamespaceList(g:omni#cpp#namespaces#CacheAlias, result) 508 | 509 | return ['::'] + result 510 | endfunc 511 | 512 | " Resolve a using namespace regarding the current context 513 | " For each namespace used: 514 | " - We get all possible contexts where the namespace 515 | " can be define 516 | " - We do a comparison test of each parent contexts with the current 517 | " context list 518 | " - If one and only one parent context is present in the 519 | " current context list we add the namespace in the current 520 | " context 521 | " - If there is more than one of parent contexts in the 522 | " current context the namespace is ambiguous 523 | " @return 524 | " - result item 525 | " - kind = 0|1 526 | " - 0 = unresolved or error 527 | " - 1 = resolved 528 | " - value = resolved namespace 529 | function! s:ResolveNamespace(namespace, mapCurrentContexts) 530 | let result = {'kind':0, 'value': ''} 531 | 532 | " If the namespace is already resolved we add it in the list of 533 | " current contexts 534 | if match(a:namespace, '^::')>=0 535 | let result.kind = 1 536 | let result.value = a:namespace 537 | return result 538 | elseif match(a:namespace, '\w\+::\w\+')>=0 539 | let mapCurrentContextsTmp = copy(a:mapCurrentContexts) 540 | let resolvedItem = {} 541 | for nsTmp in split(a:namespace, '::') 542 | let resolvedItem = s:ResolveNamespace(nsTmp, mapCurrentContextsTmp) 543 | if resolvedItem.kind 544 | " Note: We don't extend the map 545 | let mapCurrentContextsTmp = {resolvedItem.value : 1} 546 | else 547 | break 548 | endif 549 | endfor 550 | if resolvedItem!={} && resolvedItem.kind 551 | let result.kind = 1 552 | let result.value = resolvedItem.value 553 | endif 554 | return result 555 | endif 556 | 557 | " We get all possible parent contexts of this namespace 558 | let listTagsOfNamespace = [] 559 | if has_key(g:omni#cpp#namespaces#CacheResolve, a:namespace) 560 | let listTagsOfNamespace = g:omni#cpp#namespaces#CacheResolve[a:namespace] 561 | else 562 | let listTagsOfNamespace = omni#common#utils#TagList('^'.a:namespace.'$') 563 | let g:omni#cpp#namespaces#CacheResolve[a:namespace] = listTagsOfNamespace 564 | endif 565 | 566 | if len(listTagsOfNamespace)==0 567 | return result 568 | endif 569 | call filter(listTagsOfNamespace, 'v:val.kind[0]=="n"') 570 | 571 | " We extract parent context from tags 572 | " We use a map to avoid multiple entries 573 | let mapContext = {} 574 | for tagItem in listTagsOfNamespace 575 | let szParentContext = omni#cpp#utils#ExtractScope(tagItem) 576 | let mapContext[szParentContext] = 1 577 | endfor 578 | let listParentContext = keys(mapContext) 579 | 580 | " Now for each parent context we test if the context is in the current 581 | " contexts list 582 | let listResolvedNamespace = [] 583 | for szParentContext in listParentContext 584 | if has_key(a:mapCurrentContexts, szParentContext) 585 | call extend(listResolvedNamespace, [omni#cpp#utils#SimplifyScope(szParentContext.'::'.a:namespace)]) 586 | endif 587 | endfor 588 | 589 | " Now we know if the namespace is ambiguous or not 590 | let len = len(listResolvedNamespace) 591 | if len==1 592 | " Namespace resolved 593 | let result.kind = 1 594 | let result.value = listResolvedNamespace[0] 595 | elseif len > 1 596 | " Ambiguous namespace, possible matches are in listResolvedNamespace 597 | else 598 | " Other cases 599 | endif 600 | return result 601 | endfunc 602 | 603 | " Resolve namespaces 604 | "@return 605 | " - List of resolved namespaces 606 | function! omni#cpp#namespaces#ResolveAll(namespacesUsed) 607 | 608 | " We add the default context '::' 609 | let contextOrder = 0 610 | let mapCurrentContexts = {} 611 | 612 | " For each namespace used: 613 | " - We get all possible contexts where the namespace 614 | " can be define 615 | " - We do a comparison test of each parent contexts with the current 616 | " context list 617 | " - If one and only one parent context is present in the 618 | " current context list we add the namespace in the current 619 | " context 620 | " - If there is more than one of parent contexts in the 621 | " current context the namespace is ambiguous 622 | for ns in a:namespacesUsed 623 | let resolvedItem = s:ResolveNamespace(ns, mapCurrentContexts) 624 | if resolvedItem.kind 625 | let contextOrder+=1 626 | let mapCurrentContexts[resolvedItem.value] = contextOrder 627 | endif 628 | endfor 629 | 630 | " Build the list of current contexts from the map, we have to keep the 631 | " order 632 | let mapReorder = {} 633 | for key in keys(mapCurrentContexts) 634 | let mapReorder[ mapCurrentContexts[key] ] = key 635 | endfor 636 | let result = [] 637 | for key in sort(keys(mapReorder)) 638 | call extend(result, [mapReorder[key]]) 639 | endfor 640 | return result 641 | endfunc 642 | 643 | " Build the context stack 644 | function! s:BuildContextStack(namespaces, szCurrentScope) 645 | let result = copy(a:namespaces) 646 | if a:szCurrentScope != '::' 647 | let tagItem = omni#cpp#utils#GetResolvedTagItem(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope)) 648 | if has_key(tagItem, 'inherits') 649 | let listBaseClass = omni#cpp#utils#GetClassInheritanceList(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope)) 650 | let result = listBaseClass + result 651 | elseif has_key(tagItem, 'kind') && index(['c', 's', 'u', 'n'], tagItem.kind[0])>=0 652 | call insert(result, omni#cpp#utils#ExtractTypeInfoFromTag(tagItem)) 653 | endif 654 | endif 655 | return result 656 | endfunc 657 | 658 | " Returns the class scope at the current position of the cursor 659 | " @return a string that represents the class scope 660 | " eg: ::NameSpace1::Class1 661 | " The returned string always starts with '::' 662 | " Note: In term of performance it's the weak point of the script 663 | function! s:GetClassScopeAtCursor() 664 | " We store the cursor position because searchpairpos() moves the cursor 665 | let originalPos = getpos('.') 666 | let endPos = originalPos[1:2] 667 | let listCode = [] 668 | let result = {'namespaces': [], 'scope': ''} 669 | 670 | while endPos!=[0,0] 671 | let endPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments) 672 | let szReStartPos = '[;{}]\|\%^' 673 | let startPos = searchpairpos(szReStartPos, '', '{', 'bWn', g:omni#cpp#utils#expIgnoreComments) 674 | 675 | " If the file starts with a comment so the startPos can be [0,0] 676 | " we change it to [1,1] 677 | if startPos==[0,0] 678 | let startPos = [1,1] 679 | endif 680 | 681 | " Get lines backward from cursor position to last ; or { or } 682 | " or when we are at the beginning of the file. 683 | " We store lines in listCode 684 | if endPos!=[0,0] 685 | " We remove the last character which is a '{' 686 | " We also remove starting { or } or ; if exits 687 | let szCodeWithoutComments = substitute(omni#cpp#utils#GetCode(startPos, endPos)[:-2], '^[;{}]', '', 'g') 688 | call insert(listCode, {'startLine' : startPos[0], 'code' : szCodeWithoutComments}) 689 | endif 690 | endwhile 691 | " Setting the cursor to the original position 692 | call setpos('.', originalPos) 693 | 694 | let listClassScope = [] 695 | let bResolved = 0 696 | let startLine = 0 697 | " Now we can check in the list of code if there is a function 698 | for code in listCode 699 | " We get the name of the namespace, class, struct or union 700 | " and we store it in listClassScope 701 | let tokens = omni#cpp#tokenizer#Tokenize(code.code) 702 | let bContinue=0 703 | let bAddNamespace = 0 704 | let state=0 705 | for token in tokens 706 | if state==0 707 | if index(['namespace', 'class', 'struct', 'union'], token.value)>=0 708 | if token.value == 'namespace' 709 | let bAddNamespace = 1 710 | endif 711 | let state= 1 712 | " Maybe end of tokens 713 | endif 714 | elseif state==1 715 | if token.kind == 'cppWord' 716 | " eg: namespace MyNs { class MyCl {}; } 717 | " => listClassScope = [MyNs, MyCl] 718 | call extend( listClassScope , [token.value] ) 719 | 720 | " Add the namespace in result 721 | if bAddNamespace 722 | call extend(result.namespaces, [token.value]) 723 | let bAddNamespace = 0 724 | endif 725 | 726 | let bContinue=1 727 | break 728 | endif 729 | endif 730 | endfor 731 | if bContinue==1 732 | continue 733 | endif 734 | 735 | " Simple test to check if we have a chance to find a 736 | " class method 737 | let aPos = matchend(code.code, '::\s*\~*\s*\w\+\s*(') 738 | if aPos ==-1 739 | continue 740 | endif 741 | 742 | let startLine = code.startLine 743 | let listTmp = [] 744 | " eg: 'void MyNamespace::MyClass::foo(' 745 | " => tokens = ['MyClass', '::', 'MyNamespace', 'void'] 746 | let tokens = reverse(omni#cpp#tokenizer#Tokenize(code.code[:aPos-1])[:-4]) 747 | let state = 0 748 | " Reading tokens backward 749 | for token in tokens 750 | if state==0 751 | if token.kind=='cppWord' 752 | call insert(listTmp, token.value) 753 | let state=1 754 | endif 755 | elseif state==1 756 | if token.value=='::' 757 | let state=2 758 | else 759 | break 760 | endif 761 | elseif state==2 762 | if token.kind=='cppWord' 763 | call insert(listTmp, token.value) 764 | let state=1 765 | else 766 | break 767 | endif 768 | endif 769 | endfor 770 | 771 | if len(listTmp) 772 | if len(listClassScope) 773 | let bResolved = 1 774 | " Merging class scopes 775 | " eg: current class scope = 'MyNs::MyCl1' 776 | " method class scope = 'MyCl1::MyCl2' 777 | " If we add the method class scope to current class scope 778 | " we'll have MyNs::MyCl1::MyCl1::MyCl2 => it's wrong 779 | " we want MyNs::MyCl1::MyCl2 780 | let index = 0 781 | for methodClassScope in listTmp 782 | if methodClassScope==listClassScope[-1] 783 | let listTmp = listTmp[index+1:] 784 | break 785 | else 786 | let index+=1 787 | endif 788 | endfor 789 | endif 790 | call extend(listClassScope, listTmp) 791 | break 792 | endif 793 | endfor 794 | 795 | let szClassScope = '::' 796 | if len(listClassScope) 797 | if bResolved 798 | let szClassScope .= join(listClassScope, '::') 799 | else 800 | let szClassScope = join(listClassScope, '::') 801 | 802 | " The class scope is not resolved, we have to check using 803 | " namespace declarations and search the class scope in each 804 | " namespace 805 | if startLine != 0 806 | let namespaces = ['::'] + omni#cpp#namespaces#GetListFromCurrentBuffer(startLine) 807 | let namespaces = omni#cpp#namespaces#ResolveAll(namespaces) 808 | let tagItem = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(szClassScope)) 809 | if tagItem != {} 810 | let szClassScope = omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) 811 | endif 812 | endif 813 | endif 814 | endif 815 | 816 | let result.scope = szClassScope 817 | return result 818 | endfunc 819 | 820 | " Get all contexts at the cursor position 821 | function! omni#cpp#namespaces#GetContexts() 822 | " Get the current class scope at the cursor, the result depends on the current cursor position 823 | let scopeItem = s:GetClassScopeAtCursor() 824 | let listUsingNamespace = copy(g:OmniCpp_DefaultNamespaces) 825 | call extend(listUsingNamespace, scopeItem.namespaces) 826 | if g:OmniCpp_NamespaceSearch && &filetype != 'c' 827 | " Get namespaces used in the file until the cursor position 828 | let listUsingNamespace = omni#cpp#namespaces#GetUsingNamespaces() + listUsingNamespace 829 | " Resolving namespaces, removing ambiguous namespaces 830 | let namespaces = omni#cpp#namespaces#ResolveAll(listUsingNamespace) 831 | else 832 | let namespaces = ['::'] + listUsingNamespace 833 | endif 834 | call reverse(namespaces) 835 | 836 | " Building context stack from namespaces and the current class scope 837 | return s:BuildContextStack(namespaces, scopeItem.scope) 838 | endfunc 839 | -------------------------------------------------------------------------------- /autoload/omni/cpp/settings.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | function! omni#cpp#settings#Init() 6 | " Global scope search on/off 7 | " 0 = disabled 8 | " 1 = enabled 9 | if !exists('g:OmniCpp_GlobalScopeSearch') 10 | let g:OmniCpp_GlobalScopeSearch = 1 11 | endif 12 | 13 | " Sets the namespace search method 14 | " 0 = disabled 15 | " 1 = search namespaces in the current file 16 | " 2 = search namespaces in the current file and included files 17 | if !exists('g:OmniCpp_NamespaceSearch') 18 | let g:OmniCpp_NamespaceSearch = 1 19 | endif 20 | 21 | " Set the class scope completion mode 22 | " 0 = auto 23 | " 1 = show all members (static, public, protected and private) 24 | if !exists('g:OmniCpp_DisplayMode') 25 | let g:OmniCpp_DisplayMode = 0 26 | endif 27 | 28 | " Set if the scope is displayed in the abbr column of the popup 29 | " 0 = no 30 | " 1 = yes 31 | if !exists('g:OmniCpp_ShowScopeInAbbr') 32 | let g:OmniCpp_ShowScopeInAbbr = 0 33 | endif 34 | 35 | " Set if the function prototype is displayed in the abbr column of the popup 36 | " 0 = no 37 | " 1 = yes 38 | if !exists('g:OmniCpp_ShowPrototypeInAbbr') 39 | let g:OmniCpp_ShowPrototypeInAbbr = 0 40 | endif 41 | 42 | " Set if the access (+,#,-) is displayed 43 | " 0 = no 44 | " 1 = yes 45 | if !exists('g:OmniCpp_ShowAccess') 46 | let g:OmniCpp_ShowAccess = 1 47 | endif 48 | 49 | " Set the list of default namespaces 50 | " eg: ['std'] 51 | if !exists('g:OmniCpp_DefaultNamespaces') 52 | let g:OmniCpp_DefaultNamespaces = [] 53 | endif 54 | 55 | " Set MayComplete to '.' 56 | " 0 = disabled 57 | " 1 = enabled 58 | " default = 1 59 | if !exists('g:OmniCpp_MayCompleteDot') 60 | let g:OmniCpp_MayCompleteDot = 1 61 | endif 62 | 63 | " Set MayComplete to '->' 64 | " 0 = disabled 65 | " 1 = enabled 66 | " default = 1 67 | if !exists('g:OmniCpp_MayCompleteArrow') 68 | let g:OmniCpp_MayCompleteArrow = 1 69 | endif 70 | 71 | " Set MayComplete to dot 72 | " 0 = disabled 73 | " 1 = enabled 74 | " default = 0 75 | if !exists('g:OmniCpp_MayCompleteScope') 76 | let g:OmniCpp_MayCompleteScope = 0 77 | endif 78 | 79 | " When completeopt does not contain longest option, this setting 80 | " controls the behaviour of the popup menu selection when starting the completion 81 | " 0 = don't select first item 82 | " 1 = select first item (inserting it to the text) 83 | " 2 = select first item (without inserting it to the text) 84 | " default = 0 85 | if !exists('g:OmniCpp_SelectFirstItem') 86 | let g:OmniCpp_SelectFirstItem= 0 87 | endif 88 | 89 | " Use local search function for variable definitions 90 | " 0 = use standard vim search function 91 | " 1 = use local search function 92 | " default = 0 93 | if !exists('g:OmniCpp_LocalSearchDecl') 94 | let g:OmniCpp_LocalSearchDecl= 0 95 | endif 96 | endfunc 97 | -------------------------------------------------------------------------------- /autoload/omni/cpp/tokenizer.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion tokenizer 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | " TODO: Generic behaviour for Tokenize() 5 | 6 | " From the C++ BNF 7 | 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'] 8 | 9 | let s:reCppKeyword = '\C\<'.join(s:cppKeyword, '\>\|\<').'\>' 10 | 11 | " The order of items in this list is very important because we use this list to build a regular 12 | " expression (see below) for tokenization 13 | let s:cppOperatorPunctuator = ['->*', '->', '--', '-=', '-', '!=', '!', '##', '#', '%:%:', '%=', '%>', '%:', '%', '&&', '&=', '&', '(', ')', '*=', '*', ',', '...', '.*', '.', '/=', '/', '::', ':>', ':', ';', '?', '[', ']', '^=', '^', '{', '||', '|=', '|', '}', '~', '++', '+=', '+', '<<=', '<%', '<:', '<<', '<=', '<', '==', '=', '>>=', '>>', '>=', '>'] 14 | 15 | " We build the regexp for the tokenizer 16 | let s:reCComment = '\/\*\|\*\/' 17 | let s:reCppComment = '\/\/' 18 | let s:reComment = s:reCComment.'\|'.s:reCppComment 19 | let s:reCppOperatorOrPunctuator = escape(join(s:cppOperatorPunctuator, '\|'), '*./^~[]') 20 | 21 | 22 | " Tokenize a c++ code 23 | " a token is dictionary where keys are: 24 | " - kind = cppKeyword|cppWord|cppOperatorPunctuator|unknown|cComment|cppComment|cppDigit 25 | " - value = 'something' 26 | " Note: a cppWord is any word that is not a cpp keyword 27 | function! omni#cpp#tokenizer#Tokenize(szCode) 28 | let result = [] 29 | 30 | " The regexp to find a token, a token is a keyword, word or 31 | " c++ operator or punctuator. To work properly we have to put 32 | " spaces and tabs to our regexp. 33 | let reTokenSearch = '\(\w\+\)\|\s\+\|'.s:reComment.'\|'.s:reCppOperatorOrPunctuator 34 | " eg: 'using namespace std;' 35 | " ^ ^ 36 | " start=0 end=5 37 | let startPos = 0 38 | let endPos = matchend(a:szCode, reTokenSearch) 39 | let len = endPos-startPos 40 | while endPos!=-1 41 | " eg: 'using namespace std;' 42 | " ^ ^ 43 | " start=0 end=5 44 | " token = 'using' 45 | " We also remove space and tabs 46 | let token = substitute(strpart(a:szCode, startPos, len), '\s', '', 'g') 47 | 48 | " eg: 'using namespace std;' 49 | " ^ ^ 50 | " start=5 end=15 51 | let startPos = endPos 52 | let endPos = matchend(a:szCode, reTokenSearch, startPos) 53 | let len = endPos-startPos 54 | 55 | " It the token is empty we continue 56 | if token=='' 57 | continue 58 | endif 59 | 60 | " Building the token 61 | let resultToken = {'kind' : 'unknown', 'value' : token} 62 | 63 | " Classify the token 64 | if token =~ '^\d\+' 65 | " It's a digit 66 | let resultToken.kind = 'cppDigit' 67 | elseif token=~'^\w\+$' 68 | " It's a word 69 | let resultToken.kind = 'cppWord' 70 | 71 | " But maybe it's a c++ keyword 72 | if match(token, s:reCppKeyword)>=0 73 | let resultToken.kind = 'cppKeyword' 74 | endif 75 | else 76 | if match(token, s:reComment)>=0 77 | if index(['/*','*/'],token)>=0 78 | let resultToken.kind = 'cComment' 79 | else 80 | let resultToken.kind = 'cppComment' 81 | endif 82 | else 83 | " It's an operator 84 | let resultToken.kind = 'cppOperatorPunctuator' 85 | endif 86 | endif 87 | 88 | " We have our token, let's add it to the result list 89 | call extend(result, [resultToken]) 90 | endwhile 91 | 92 | return result 93 | endfunc 94 | -------------------------------------------------------------------------------- /autoload/omni/cpp/utils.vim: -------------------------------------------------------------------------------- 1 | " Description: Omni completion script for cpp files 2 | " Maintainer: Vissale NEANG 3 | " Last Change: 26 sept. 2007 4 | 5 | let g:omni#cpp#utils#CACHE_TAG_INHERITS = {} 6 | 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')" 7 | let g:omni#cpp#utils#szFilterGlobalScope .= "&& (!has_key(v:val, 'enum') || (has_key(v:val, 'enum') && v:val.enum =~ '^\\w\\+$')))" 8 | 9 | " Expression used to ignore comments 10 | " Note: this expression drop drastically the performance 11 | "let omni#cpp#utils#expIgnoreComments = 'match(synIDattr(synID(line("."), col("."), 1), "name"), '\CcComment')!=-1' 12 | " This one is faster but not really good for C comments 13 | let omni#cpp#utils#reIgnoreComment = escape('\/\/\|\/\*\|\*\/', '*/\') 14 | let omni#cpp#utils#expIgnoreComments = 'getline(".") =~ g:omni#cpp#utils#reIgnoreComment' 15 | 16 | " Characters to escape in a filename for vimgrep 17 | "TODO: Find more characters to escape 18 | let omni#cpp#utils#szEscapedCharacters = ' %#' 19 | 20 | " Resolve the path of the file 21 | " TODO: absolute file path 22 | function! omni#cpp#utils#ResolveFilePath(szFile) 23 | let result = '' 24 | let listPath = split(globpath(&path, a:szFile), "\n") 25 | if len(listPath) 26 | let result = listPath[0] 27 | endif 28 | return simplify(result) 29 | endfunc 30 | 31 | " Get code without comments and with empty strings 32 | " szSingleLine must not have carriage return 33 | function! omni#cpp#utils#GetCodeFromLine(szSingleLine) 34 | " We set all strings to empty strings, it's safer for 35 | " the next of the process 36 | let szResult = substitute(a:szSingleLine, '".*"', '""', 'g') 37 | 38 | " Removing c++ comments, we can use the pattern ".*" because 39 | " we are modifying a line 40 | let szResult = substitute(szResult, '\/\/.*', '', 'g') 41 | 42 | " Now we have the entire code in one line and we can remove C comments 43 | return s:RemoveCComments(szResult) 44 | endfunc 45 | 46 | " Remove C comments on a line 47 | function! s:RemoveCComments(szLine) 48 | let result = a:szLine 49 | 50 | " We have to match the first '/*' and first '*/' 51 | let startCmt = match(result, '\/\*') 52 | let endCmt = match(result, '\*\/') 53 | while startCmt!=-1 && endCmt!=-1 && startCmt0 55 | let result = result[ : startCmt-1 ] . result[ endCmt+2 : ] 56 | else 57 | " Case where '/*' is at the start of the line 58 | let result = result[ endCmt+2 : ] 59 | endif 60 | let startCmt = match(result, '\/\*') 61 | let endCmt = match(result, '\*\/') 62 | endwhile 63 | return result 64 | endfunc 65 | 66 | " Get a c++ code from current buffer from [lineStart, colStart] to 67 | " [lineEnd, colEnd] without c++ and c comments, without end of line 68 | " and with empty strings if any 69 | " @return a string 70 | function! omni#cpp#utils#GetCode(posStart, posEnd) 71 | let posStart = a:posStart 72 | let posEnd = a:posEnd 73 | if a:posStart[0]>a:posEnd[0] 74 | let posStart = a:posEnd 75 | let posEnd = a:posStart 76 | elseif a:posStart[0]==a:posEnd[0] && a:posStart[1]>a:posEnd[1] 77 | let posStart = a:posEnd 78 | let posEnd = a:posStart 79 | endif 80 | 81 | " Getting the lines 82 | let lines = getline(posStart[0], posEnd[0]) 83 | let lenLines = len(lines) 84 | 85 | " Formatting the result 86 | let result = '' 87 | if lenLines==1 88 | let sStart = posStart[1]-1 89 | let sEnd = posEnd[1]-1 90 | let line = lines[0] 91 | let lenLastLine = strlen(line) 92 | let sEnd = (sEnd>lenLastLine)?lenLastLine : sEnd 93 | if sStart >= 0 94 | let result = omni#cpp#utils#GetCodeFromLine(line[ sStart : sEnd ]) 95 | endif 96 | elseif lenLines>1 97 | let sStart = posStart[1]-1 98 | let sEnd = posEnd[1]-1 99 | let lenLastLine = strlen(lines[-1]) 100 | let sEnd = (sEnd>lenLastLine)?lenLastLine : sEnd 101 | if sStart >= 0 102 | let lines[0] = lines[0][ sStart : ] 103 | let lines[-1] = lines[-1][ : sEnd ] 104 | for aLine in lines 105 | let result = result . omni#cpp#utils#GetCodeFromLine(aLine)." " 106 | endfor 107 | let result = result[:-2] 108 | endif 109 | endif 110 | 111 | " Now we have the entire code in one line and we can remove C comments 112 | return s:RemoveCComments(result) 113 | endfunc 114 | 115 | " Extract the scope (context) of a tag item 116 | " eg: ::MyNamespace 117 | " @return a string of the scope. a scope from tag always starts with '::' 118 | function! omni#cpp#utils#ExtractScope(tagItem) 119 | let listKindScope = ['class', 'struct', 'union', 'namespace', 'enum'] 120 | let szResult = '::' 121 | for scope in listKindScope 122 | if has_key(a:tagItem, scope) 123 | let szResult = szResult . a:tagItem[scope] 124 | break 125 | endif 126 | endfor 127 | return szResult 128 | endfunc 129 | 130 | " Simplify scope string, remove consecutive '::' if any 131 | function! omni#cpp#utils#SimplifyScope(szScope) 132 | let szResult = substitute(a:szScope, '\(::\)\+', '::', 'g') 133 | if szResult=='::' 134 | return szResult 135 | else 136 | return substitute(szResult, '::$', '', 'g') 137 | endif 138 | endfunc 139 | 140 | " Check if the cursor is in comment 141 | function! omni#cpp#utils#IsCursorInCommentOrString() 142 | return match(synIDattr(synID(line("."), col(".")-1, 1), "name"), '\C\=0 143 | endfunc 144 | 145 | " Tokenize the current instruction until the cursor position. 146 | " @return list of tokens 147 | function! omni#cpp#utils#TokenizeCurrentInstruction(...) 148 | let szAppendText = '' 149 | if a:0>0 150 | let szAppendText = a:1 151 | endif 152 | 153 | let startPos = searchpos('[;{}]\|\%^', 'bWn') 154 | let curPos = getpos('.')[1:2] 155 | " We don't want the character under the cursor 156 | let column = curPos[1]-1 157 | let curPos[1] = (column<1)?1:column 158 | return omni#cpp#tokenizer#Tokenize(omni#cpp#utils#GetCode(startPos, curPos)[1:] . szAppendText) 159 | endfunc 160 | 161 | " Tokenize the current instruction until the word under the cursor. 162 | " @return list of tokens 163 | function! omni#cpp#utils#TokenizeCurrentInstructionUntilWord() 164 | let startPos = searchpos('[;{}]\|\%^', 'bWn') 165 | 166 | " Saving the current cursor pos 167 | let originalPos = getpos('.') 168 | 169 | " We go at the end of the word 170 | execute 'normal gee' 171 | let curPos = getpos('.')[1:2] 172 | 173 | " Restoring the original cursor pos 174 | call setpos('.', originalPos) 175 | 176 | let szCode = omni#cpp#utils#GetCode(startPos, curPos)[1:] 177 | return omni#cpp#tokenizer#Tokenize(szCode) 178 | endfunc 179 | 180 | " Build parenthesis groups 181 | " add a new key 'group' in the token 182 | " where value is the group number of the parenthesis 183 | " eg: (void*)(MyClass*) 184 | " group1 group0 185 | " if a parenthesis is unresolved the group id is -1 186 | " @return a copy of a:tokens with parenthesis group 187 | function! omni#cpp#utils#BuildParenthesisGroups(tokens) 188 | let tokens = copy(a:tokens) 189 | let kinds = {'(': '()', ')' : '()', '[' : '[]', ']' : '[]', '<' : '<>', '>' : '<>', '{': '{}', '}': '{}'} 190 | let unresolved = {'()' : [], '[]': [], '<>' : [], '{}' : []} 191 | let groupId = 0 192 | 193 | " Note: we build paren group in a backward way 194 | " because we can often have parenthesis unbalanced 195 | " instruction 196 | " eg: doSomething(_member.get()-> 197 | for token in reverse(tokens) 198 | if index([')', ']', '>', '}'], token.value)>=0 199 | let token['group'] = groupId 200 | call extend(unresolved[kinds[token.value]], [token]) 201 | let groupId+=1 202 | elseif index(['(', '[', '<', '{'], token.value)>=0 203 | if len(unresolved[kinds[token.value]]) 204 | let tokenResolved = remove(unresolved[kinds[token.value]], -1) 205 | let token['group'] = tokenResolved.group 206 | else 207 | let token['group'] = -1 208 | endif 209 | endif 210 | endfor 211 | 212 | return reverse(tokens) 213 | endfunc 214 | 215 | " Determine if tokens represent a C cast 216 | " @return 217 | " - itemCast 218 | " - itemCppCast 219 | " - itemVariable 220 | " - itemThis 221 | function! omni#cpp#utils#GetCastType(tokens) 222 | " Note: a:tokens is not modified 223 | let tokens = omni#cpp#utils#SimplifyParenthesis(omni#cpp#utils#BuildParenthesisGroups(a:tokens)) 224 | 225 | if tokens[0].value == '(' 226 | return 'itemCast' 227 | elseif index(['static_cast', 'dynamic_cast', 'reinterpret_cast', 'const_cast'], tokens[0].value)>=0 228 | return 'itemCppCast' 229 | else 230 | for token in tokens 231 | if token.value=='this' 232 | return 'itemThis' 233 | endif 234 | endfor 235 | return 'itemVariable' 236 | endif 237 | endfunc 238 | 239 | " Remove useless parenthesis 240 | function! omni#cpp#utils#SimplifyParenthesis(tokens) 241 | "Note: a:tokens is not modified 242 | let tokens = a:tokens 243 | " We remove useless parenthesis eg: (((MyClass))) 244 | if len(tokens)>2 245 | while tokens[0].value=='(' && tokens[-1].value==')' && tokens[0].group==tokens[-1].group 246 | let tokens = tokens[1:-2] 247 | endwhile 248 | endif 249 | return tokens 250 | endfunc 251 | 252 | " Function create a type info 253 | function! omni#cpp#utils#CreateTypeInfo(param) 254 | let type = type(a:param) 255 | return {'type': type, 'value':a:param} 256 | endfunc 257 | 258 | " Extract type info from a tag item 259 | " eg: ::MyNamespace::MyClass 260 | function! omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) 261 | let szTypeInfo = omni#cpp#utils#ExtractScope(a:tagItem) . '::' . substitute(a:tagItem.name, '.*::', '', 'g') 262 | return omni#cpp#utils#SimplifyScope(szTypeInfo) 263 | endfunc 264 | 265 | " Build a class inheritance list 266 | function! omni#cpp#utils#GetClassInheritanceList(namespaces, typeInfo) 267 | let result = [] 268 | for tagItem in omni#cpp#utils#GetResolvedTags(a:namespaces, a:typeInfo) 269 | call extend(result, [omni#cpp#utils#ExtractTypeInfoFromTag(tagItem)]) 270 | endfor 271 | return result 272 | endfunc 273 | 274 | " Get class inheritance list where items in the list are tag items. 275 | " TODO: Verify inheritance order 276 | function! omni#cpp#utils#GetResolvedTags(namespaces, typeInfo) 277 | let result = [] 278 | let tagItem = omni#cpp#utils#GetResolvedTagItem(a:namespaces, a:typeInfo) 279 | if tagItem!={} 280 | let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTag(tagItem) 281 | if has_key(g:omni#cpp#utils#CACHE_TAG_INHERITS, szTypeInfo) 282 | let result = g:omni#cpp#utils#CACHE_TAG_INHERITS[szTypeInfo] 283 | else 284 | call extend(result, [tagItem]) 285 | if has_key(tagItem, 'inherits') 286 | for baseClassTypeInfo in split(tagItem.inherits, ',') 287 | let namespaces = [omni#cpp#utils#ExtractScope(tagItem), '::'] 288 | call extend(result, omni#cpp#utils#GetResolvedTags(namespaces, omni#cpp#utils#CreateTypeInfo(baseClassTypeInfo))) 289 | endfor 290 | endif 291 | let g:omni#cpp#utils#CACHE_TAG_INHERITS[szTypeInfo] = result 292 | endif 293 | endif 294 | return result 295 | endfunc 296 | 297 | " Get a tag item after a scope resolution and typedef resolution 298 | function! omni#cpp#utils#GetResolvedTagItem(namespaces, typeInfo) 299 | let typeInfo = {} 300 | if type(a:typeInfo) == 1 301 | let typeInfo = omni#cpp#utils#CreateTypeInfo(a:typeInfo) 302 | else 303 | let typeInfo = a:typeInfo 304 | endif 305 | 306 | let result = {} 307 | if !omni#cpp#utils#IsTypeInfoValid(typeInfo) 308 | return result 309 | endif 310 | 311 | " Unnamed type case eg: '1::2' 312 | if typeInfo.type == 4 313 | " Here there is no typedef or namespace to resolve, the tagInfo.value is a tag item 314 | " representing a variable ('v') a member ('m') or a typedef ('t') and the typename is 315 | " always in global scope 316 | return typeInfo.value 317 | endif 318 | 319 | " Named type case eg: 'MyNamespace::MyClass' 320 | let szTypeInfo = omni#cpp#utils#GetTypeInfoString(typeInfo) 321 | 322 | " Resolving namespace alias 323 | " TODO: For the next release 324 | "let szTypeInfo = omni#cpp#namespaces#ResolveAlias(g:omni#cpp#namespaces#CacheAlias, szTypeInfo) 325 | 326 | if szTypeInfo=='::' 327 | return result 328 | endif 329 | 330 | " We can only get members of class, struct, union and namespace 331 | let szTagFilter = "index(['c', 's', 'u', 'n', 't'], v:val.kind[0])>=0" 332 | let szTagQuery = szTypeInfo 333 | 334 | if s:IsTypeInfoResolved(szTypeInfo) 335 | " The type info is already resolved, we remove the starting '::' 336 | let szTagQuery = substitute(szTypeInfo, '^::', '', 'g') 337 | if len(split(szTagQuery, '::'))==1 338 | " eg: ::MyClass 339 | " Here we have to get tags that have no parent scope 340 | " That's why we change the szTagFilter 341 | let szTagFilter .= '&& ' . g:omni#cpp#utils#szFilterGlobalScope 342 | let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') 343 | call filter(tagList, szTagFilter) 344 | if len(tagList) 345 | let result = tagList[0] 346 | endif 347 | else 348 | " eg: ::MyNamespace::MyClass 349 | let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') 350 | call filter(tagList, szTagFilter) 351 | 352 | if len(tagList) 353 | let result = tagList[0] 354 | endif 355 | endif 356 | else 357 | " The type is not resolved 358 | let tagList = omni#common#utils#TagListNoThrow('^'.szTagQuery.'$') 359 | call filter(tagList, szTagFilter) 360 | 361 | if len(tagList) 362 | " Resolving scope (namespace, nested class etc...) 363 | let szScopeOfTypeInfo = s:ExtractScopeFromTypeInfo(szTypeInfo) 364 | if s:IsTypeInfoResolved(szTypeInfo) 365 | let result = s:GetTagOfSameScope(tagList, szScopeOfTypeInfo) 366 | else 367 | " For each namespace of the namespace list we try to get a tag 368 | " that can be in the same scope 369 | if g:OmniCpp_NamespaceSearch && &filetype != 'c' 370 | for scope in a:namespaces 371 | let szTmpScope = omni#cpp#utils#SimplifyScope(scope.'::'.szScopeOfTypeInfo) 372 | let result = s:GetTagOfSameScope(tagList, szTmpScope) 373 | if result!={} 374 | break 375 | endif 376 | endfor 377 | else 378 | let szTmpScope = omni#cpp#utils#SimplifyScope('::'.szScopeOfTypeInfo) 379 | let result = s:GetTagOfSameScope(tagList, szTmpScope) 380 | endif 381 | endif 382 | endif 383 | endif 384 | 385 | if result!={} 386 | " We have our tagItem but maybe it's a typedef or an unnamed type 387 | if result.kind[0]=='t' 388 | " Here we can have a typedef to another typedef, a class, struct, union etc 389 | " but we can also have a typedef to an unnamed type, in that 390 | " case the result contains a 'typeref' key 391 | let namespaces = [omni#cpp#utils#ExtractScope(result), '::'] 392 | if has_key(result, 'typeref') 393 | let result = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(result)) 394 | else 395 | let szCmd = omni#cpp#utils#ExtractCmdFromTagItem(result) 396 | let szCode = substitute(omni#cpp#utils#GetCodeFromLine(szCmd), '\C\<'.result.name.'\>.*', '', 'g') 397 | let szTypeInfo = omni#cpp#utils#ExtractTypeInfoFromTokens(omni#cpp#tokenizer#Tokenize(szCode)) 398 | let result = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(szTypeInfo)) 399 | " TODO: Namespace resolution for result 400 | endif 401 | endif 402 | endif 403 | 404 | return result 405 | endfunc 406 | 407 | " Returns if the type info is valid 408 | " @return 409 | " - 1 if valid 410 | " - 0 otherwise 411 | function! omni#cpp#utils#IsTypeInfoValid(typeInfo) 412 | if a:typeInfo=={} 413 | return 0 414 | else 415 | if a:typeInfo.type == 1 && a:typeInfo.value=='' 416 | " String case 417 | return 0 418 | elseif a:typeInfo.type == 4 && a:typeInfo.value=={} 419 | " Dictionary case 420 | return 0 421 | endif 422 | endif 423 | return 1 424 | endfunc 425 | 426 | " Get the string of the type info 427 | function! omni#cpp#utils#GetTypeInfoString(typeInfo) 428 | if a:typeInfo.type == 1 429 | return a:typeInfo.value 430 | else 431 | return substitute(a:typeInfo.value.typeref, '^\w\+:', '', 'g') 432 | endif 433 | endfunc 434 | 435 | " A resolved type info starts with '::' 436 | " @return 437 | " - 1 if type info starts with '::' 438 | " - 0 otherwise 439 | function! s:IsTypeInfoResolved(szTypeInfo) 440 | return match(a:szTypeInfo, '^::')!=-1 441 | endfunc 442 | 443 | " A returned type info's scope may not have the global namespace '::' 444 | " eg: '::NameSpace1::NameSpace2::MyClass' => '::NameSpace1::NameSpace2' 445 | " 'NameSpace1::NameSpace2::MyClass' => 'NameSpace1::NameSpace2' 446 | function! s:ExtractScopeFromTypeInfo(szTypeInfo) 447 | let szScope = substitute(a:szTypeInfo, '\w\+$', '', 'g') 448 | if szScope =='::' 449 | return szScope 450 | else 451 | return substitute(szScope, '::$', '', 'g') 452 | endif 453 | endfunc 454 | 455 | " @return 456 | " - the tag with the same scope 457 | " - {} otherwise 458 | function! s:GetTagOfSameScope(listTags, szScopeToMatch) 459 | for tagItem in a:listTags 460 | let szScopeOfTag = omni#cpp#utils#ExtractScope(tagItem) 461 | if szScopeOfTag == a:szScopeToMatch 462 | return tagItem 463 | endif 464 | endfor 465 | return {} 466 | endfunc 467 | 468 | " Extract the cmd of a tag item without regexp 469 | function! omni#cpp#utils#ExtractCmdFromTagItem(tagItem) 470 | let line = a:tagItem.cmd 471 | let re = '\(\/\^\)\|\(\$\/\)' 472 | if match(line, re)!=-1 473 | let line = substitute(line, re, '', 'g') 474 | return line 475 | else 476 | " TODO: the cmd is a line number 477 | return '' 478 | endif 479 | endfunc 480 | 481 | " Extract type from tokens. 482 | " eg: examples of tokens format 483 | " 'const MyClass&' 484 | " 'const map < int, int >&' 485 | " 'MyNs::MyClass' 486 | " '::MyClass**' 487 | " 'MyClass a, *b = NULL, c[1] = {}; 488 | " 'hello(MyClass a, MyClass* b' 489 | " @return the type info string eg: ::std::map 490 | " can be empty 491 | function! omni#cpp#utils#ExtractTypeInfoFromTokens(tokens) 492 | let szResult = '' 493 | let state = 0 494 | 495 | let tokens = omni#cpp#utils#BuildParenthesisGroups(a:tokens) 496 | 497 | " If there is an unbalanced parenthesis we are in a parameter list 498 | let bParameterList = 0 499 | for token in tokens 500 | if token.value == '(' && token.group==-1 501 | let bParameterList = 1 502 | break 503 | endif 504 | endfor 505 | 506 | if bParameterList 507 | let tokens = reverse(tokens) 508 | let state = 0 509 | let parenGroup = -1 510 | for token in tokens 511 | if state==0 512 | if token.value=='>' 513 | let parenGroup = token.group 514 | let state=1 515 | elseif token.kind == 'cppWord' 516 | let szResult = token.value.szResult 517 | let state=2 518 | elseif index(['*', '&'], token.value)<0 519 | break 520 | endif 521 | elseif state==1 522 | if token.value=='<' && token.group==parenGroup 523 | let state=0 524 | endif 525 | elseif state==2 526 | if token.value=='::' 527 | let szResult = token.value.szResult 528 | let state=3 529 | else 530 | break 531 | endif 532 | elseif state==3 533 | if token.kind == 'cppWord' 534 | let szResult = token.value.szResult 535 | let state=2 536 | else 537 | break 538 | endif 539 | endif 540 | endfor 541 | return szResult 542 | endif 543 | 544 | for token in tokens 545 | if state==0 546 | if token.value == '::' 547 | let szResult .= token.value 548 | let state = 1 549 | elseif token.kind == 'cppWord' 550 | let szResult .= token.value 551 | let state = 2 552 | " Maybe end of token 553 | endif 554 | elseif state==1 555 | if token.kind == 'cppWord' 556 | let szResult .= token.value 557 | let state = 2 558 | " Maybe end of token 559 | else 560 | break 561 | endif 562 | elseif state==2 563 | if token.value == '::' 564 | let szResult .= token.value 565 | let state = 1 566 | else 567 | break 568 | endif 569 | endif 570 | endfor 571 | return szResult 572 | endfunc 573 | 574 | " Get the preview window string 575 | function! omni#cpp#utils#GetPreviewWindowStringFromTagItem(tagItem) 576 | let szResult = '' 577 | 578 | let szResult .= 'name: '.a:tagItem.name."\n" 579 | for tagKey in keys(a:tagItem) 580 | if index(['name', 'static'], tagKey)>=0 581 | continue 582 | endif 583 | let szResult .= tagKey.': '.a:tagItem[tagKey]."\n" 584 | endfor 585 | 586 | return substitute(szResult, "\n$", '', 'g') 587 | endfunc 588 | -------------------------------------------------------------------------------- /doc/omnicppcomplete.txt: -------------------------------------------------------------------------------- 1 | *omnicppcomplete.txt* Plugin for C/C++ omnicompletion 2 | *omnicppcomplete* 3 | 4 | Author: Vissale NEANG (fromtonrouge AT gmail DOT com) 5 | Last Change: 26 sept. 2007 6 | 7 | OmniCppComplete version 0.41 8 | 9 | For Vim version 7.0 and above 10 | 11 | ============================================================================== 12 | 13 | 1. Overview |omnicpp-overview| 14 | 2. Downloads |omnicpp-download| 15 | 3. Installation |omnicpp-installation| 16 | 4. Options |omnicpp-options| 17 | 5. Features |omnicpp-features| 18 | 6. Limitations |omnicpp-limitations| 19 | 7. FAQ & TIPS |omnicpp-faq| 20 | 8. History |omnicpp-history| 21 | 9. Thanks |omnicpp-thanks| 22 | 23 | ============================================================================== 24 | 1. Overview~ 25 | *omnicpp-overview* 26 | The purpose of this script is to provide an 'omnifunc' function for C and C++ 27 | language. In a C++ file, while in insert mode, you can use CTRL-X CTRL-O to: 28 | 29 | * Complete namespaces, classes, structs and unions 30 | * Complete attribute members and return type of functions 31 | * Complete the "this" pointer 32 | * Complete an object after a cast (C and C++ cast) 33 | * Complete typedefs and anonymous types 34 | 35 | You can set a "may complete" behaviour to start a completion automatically 36 | after a '.', '->' or '::'. Please see |omnicpp-may-complete| for more details. 37 | 38 | The script needs an |Exuberant_ctags| database to work properly. 39 | 40 | ============================================================================== 41 | 2. Downloads~ 42 | *omnicpp-download* 43 | You can download the latest release of the script from this url : 44 | 45 | http://www.vim.org/scripts/script.php?script_id=1520 46 | 47 | You can download |Exuberant_ctags| from : 48 | 49 | http://ctags.sourceforge.net 50 | 51 | ============================================================================== 52 | 3. Installation~ 53 | *omnicpp-installation* 54 | 3.1. Script installation~ 55 | 56 | Unzip the downloaded file in your personal |vimfiles| directory (~/.vim under 57 | unix or %HOMEPATH%\vimfiles under windows). The 'omnifunc' will be 58 | automatically set for C and C++ files. 59 | 60 | You also have to enable plugins by adding these two lines in your|.vimrc|file: > 61 | 62 | set nocp 63 | filetype plugin on 64 | < 65 | Please see |cp| and |filetype-plugin-on| sections for more details. 66 | 67 | 3.1.1. Files~ 68 | 69 | After installation you should find these files : 70 | 71 | after\ftplugin\cpp.vim 72 | after\ftplugin\c.vim 73 | 74 | autoload\omni\common\debug.vim 75 | \utils.vim 76 | 77 | autoload\omni\cpp\complete.vim 78 | \includes.vim 79 | \items.vim 80 | \maycomplete.vim 81 | \namespaces.vim 82 | \settings.vim 83 | \tokenizer.vim 84 | \utils.vim 85 | 86 | doc\omnicppcomplete.txt 87 | 88 | 3.2. Building the Exuberant Ctags database~ 89 | 90 | To extract C/C++ symbols information, the script needs an |Exuberant_ctags| 91 | database. 92 | 93 | You have to build your database with at least the following options: 94 | --c++-kinds=+p : Adds prototypes in the database for C/C++ files. 95 | --fields=+iaS : Adds inheritance (i), access (a) and function 96 | signatures (S) information. 97 | --extra=+q : Adds context to the tag name. Note: Without this 98 | option, the script cannot get class members. 99 | 100 | Thus to build recursively a ctags database from the current directory, the 101 | command looks like this: 102 | > 103 | ctags -R --c++-kinds=+p --fields=+iaS --extra=+q . 104 | < 105 | You can add a map in your |.vimrc| file, eg: > 106 | 107 | map :!ctags -R --c++-kinds=+p --fields=+iaS --extra=+q . 108 | < 109 | Or you can add these options in your ctags config file (~/.ctags under unix or 110 | %HOMEPATH%\ctags.cnf under windows) and execute the command : > 111 | 112 | :!ctags -R . 113 | < 114 | If your project contains files of other languages you may add the following 115 | options: 116 | --languages=c++ : Builds only the tags for C++ files. 117 | 118 | If your project contains macros you may also use the -I option. 119 | 120 | Please read the ctags help or ctags man page for more details. 121 | 122 | 3.3. Setting the 'tags' option~ 123 | 124 | The default value of the option 'tags' is "./tags,tags" ("./tags,./TAGS,tags,TAGS" 125 | when |+emacs_tags| is enabled), if you build your tag database with the cmd above, 126 | you normally don't have to change this setting (The cmd used above generates a 127 | file with the name "tags"). In this case your current working directory must be 128 | the directory where the tags file reside. 129 | 130 | Note: When |+emacs_tags| is enabled, the script may display members twice, it's 131 | recommended to set tags to "./tags,tags' or "./TAGS,TAGS". 132 | 133 | If your tags file is not named "tags" you have to add it in the 'tags' 134 | option eg: > 135 | 136 | set tags+=/usr/tagsdir/mytagfile 137 | < 138 | You can ensure that the 'tags' option is set properly by executing the following 139 | command: > 140 | 141 | :tselect MyClass 142 | < 143 | Where MyClass is a class of your project. This command should display all 144 | possible tags for the type MyClass. 145 | 146 | 3.4. Simple test~ 147 | 148 | Now you can do a simple test. Edit a C++ file and write the simplest case : > 149 | 150 | MyClass myObject; 151 | myObject. 152 | < 153 | You should see class members of MyClass. 154 | 155 | ============================================================================== 156 | 4. Options~ 157 | *omnicpp-options* 158 | 159 | You can change completion behaviour by setting script options in your |.vimrc| 160 | configuration file. 161 | 162 | 4.1. Global scope search toggle~ 163 | *OmniCpp_GlobalScopeSearch* 164 | 165 | You can enable/disable the global scope search by setting the 166 | OmniCpp_GlobalScopeSearch option. 167 | 168 | Possible values are : 169 | 0 = disabled 170 | 1 = enabled 171 | [default=1] > 172 | 173 | let OmniCpp_GlobalScopeSearch = 1 174 | < 175 | 4.2. Namespace search method~ 176 | *OmniCpp_NamespaceSearch* 177 | 178 | You can change the 'using namespace' search behaviour by setting the 179 | OmniCpp_NamespaceSearch option. 180 | 181 | Possible values are : 182 | 0 = namespaces disabled 183 | 1 = search namespaces in the current buffer 184 | 2 = search namespaces in the current buffer and in included files 185 | [default=1] > 186 | 187 | let OmniCpp_NamespaceSearch = 1 188 | < 189 | When OmniCpp_NamespaceSearch is 2, "using namespace" declarations are parsed 190 | in the current buffer and also in included files. To find included files, the 191 | script use the vim env 'path', so you have to set it properly. 192 | 193 | Note: included files are searched with lvimgrep, thus the location list of the 194 | current window is changed. 195 | 196 | Note: When the 'filetype' is "c", namespace search is always disabled even if 197 | OmniCpp_NamespaceSearch != 0 198 | 199 | 4.3. Class scope completion mode~ 200 | *OmniCpp_DisplayMode* 201 | 202 | When you are completing a class scope (eg: MyClass::), depending on 203 | the current scope, you may see sometimes static, public, protected or private 204 | members and sometimes you may see all members. By default the choice is done 205 | automatically by the script but you can override it with the 206 | OmniCpp_DisplayMode option. 207 | 208 | Note: This option can be use when you have friend classes in your project (the 209 | script does not support friend classes). 210 | 211 | Possible values are : 212 | 0 = auto 213 | 1 = always show all members 214 | [default=0] > 215 | 216 | let OmniCpp_DisplayMode = 0 217 | < 218 | 4.4. Show scope in abbreviation~ 219 | *OmniCpp_ShowScopeInAbbr* 220 | 221 | By default, in the |omnicpp-popup| menu, you will see the scope of a match in 222 | the last column. You can remove this column and add the scope at the beginning 223 | of match abbreviation. 224 | eg: 225 | 226 | OmniCpp_ShowScopeInAbbr = 0 227 | +-------------------------------------+ 228 | |method1( f + MyNamespace::MyClass| 229 | |_member1 m + MyNamespace::MyClass| 230 | |_member2 m # MyNamespace::MyClass| 231 | |_member3 m - MyNamespace::MyClass| 232 | +-------------------------------------+ 233 | 234 | OmniCpp_ShowScopeInAbbr = 1 235 | +-------------------------------------+ 236 | |MyNamespace::MyClass::method1( f + | 237 | |MyNamespace::MyClass::_member1 m + | 238 | |MyNamespace::MyClass::_member2 m # | 239 | |MyNamespace::MyClass::_member3 m - | 240 | +-------------------------------------+ 241 | 242 | Possible values are : 243 | 0 = don't show scope in abbreviation 244 | 1 = show scope in abbreviation and remove the last column 245 | [default=0] > 246 | 247 | let OmniCpp_ShowScopeInAbbr = 0 248 | < 249 | 4.5. Show prototype in abbreviation~ 250 | *OmniCpp_ShowPrototypeInAbbr* 251 | 252 | This option allows to display the prototype of a function in the abbreviation 253 | part of the popup menu. 254 | 255 | Possible values are: 256 | 0 = don't display prototype in abbreviation 257 | 1 = display prototype in abbreviation 258 | [default=0] > 259 | 260 | let OmniCpp_ShowPrototypeInAbbr = 0 261 | < 262 | 4.6. Show access~ 263 | *OmniCpp_ShowAccess* 264 | 265 | This option allows to show/hide the access information ('+', '#', '-') in the 266 | popup menu. 267 | 268 | Possible values are: 269 | 0 = hide access 270 | 1 = show access 271 | [default=1] > 272 | 273 | let OmniCpp_ShowAccess = 1 274 | 275 | 4.7. Default using namespace list~ 276 | *OmniCpp_DefaultNamespaces* 277 | 278 | When |OmniCpp_NamespaceSearch| is not 0, the script will parse using namespace 279 | declarations in the current buffer and maybe in included files. 280 | You can specify manually a default namespace list if you want with the 281 | OmniCpp_DefaultNamespaces option. Each item in the list is a namespace name. 282 | eg: If you have 283 | 284 | let OmniCpp_DefaultNamespaces = ["std", "MyNamespace"] 285 | 286 | It will be the same as inserting this declarations at the top of the 287 | current buffer : 288 | 289 | using namespace std; 290 | using namespace MyNamespace; 291 | 292 | This option can be use if you don't want to parse using namespace declarations 293 | in included files and want to add namespaces that are always used in your 294 | project. 295 | 296 | Possible values are : 297 | List of String 298 | [default=[]] > 299 | 300 | let OmniCpp_DefaultNamespaces = [] 301 | < 302 | 4.8. May complete behaviour~ 303 | *omnicpp-may-complete* 304 | 305 | This feature allows you to run automatically a completion after a '.', '->' 306 | or '::'. By default, the "may complete" feature is set automatically for '.' 307 | and '->'. The reason to not set this feature for the scope operator '::' is 308 | sometimes you don't want to complete a namespace that contains many members. 309 | 310 | To enable/disable the "may complete" behaviour for dot, arrow and scope 311 | operator, you can change the option OmniCpp_MayCompleteDot, 312 | OmniCpp_MayCompleteArrow and OmniCpp_MayCompleteScope respectively. 313 | 314 | *OmniCpp_MayCompleteDot* 315 | Possible values are : 316 | 0 = May complete disabled for dot 317 | 1 = May complete enabled for dot 318 | [default=1] > 319 | 320 | let OmniCpp_MayCompleteDot = 1 321 | < 322 | *OmniCpp_MayCompleteArrow* 323 | Possible values are : 324 | 0 = May complete disabled for arrow 325 | 1 = May complete enabled for arrow 326 | [default=1] > 327 | 328 | let OmniCpp_MayCompleteArrow = 1 329 | < 330 | *OmniCpp_MayCompleteScope* 331 | Possible values are : 332 | 0 = May complete disabled for scope 333 | 1 = May complete enabled for scope 334 | [default=0] > 335 | 336 | let OmniCpp_MayCompleteScope = 0 337 | < 338 | 339 | Note: You can obviously continue to use 340 | 341 | 4.9. Select/Don't select first popup item~ 342 | *OmniCpp_SelectFirstItem* 343 | 344 | Note: This option is only used when 'completeopt' does not contain "longest". 345 | 346 | When 'completeopt' does not contain "longest", Vim automatically select the 347 | first entry of the popup menu. You can change this behaviour with the 348 | OmniCpp_SelectFirstItem option. 349 | 350 | Possible values are: 351 | 0 = don't select first popup item 352 | 1 = select first popup item (inserting it to the text) 353 | 2 = select first popup item (without inserting it to the text) 354 | [default=0] > 355 | 356 | let OmniCpp_SelectFirstItem = 0 357 | 358 | 4.10 Use local search function for variable definitions~ 359 | *OmniCpp_LocalSearchDecl* 360 | 361 | The internal search function for variable definitions of vim requires that the 362 | enclosing braces of the function are located in the first column. You can 363 | change this behaviour with the OmniCpp_LocalSearchDecl option. The local 364 | version works irrespective the position of braces. 365 | 366 | Possible values are: 367 | 0 = use standard vim search function 368 | 1 = use local search function 369 | [default=0] > 370 | 371 | ============================================================================== 372 | 5. Features~ 373 | *omnicpp-features* 374 | 5.1. Popup menu~ 375 | *omnicpp-popup* 376 | Popup menu format: 377 | +-------------------------------------+ 378 | |method1( f + MyNamespace::MyClass| 379 | |_member1 m + MyNamespace::MyClass| 380 | |_member2 m # MyNamespace::MyClass| 381 | |_member3 m - MyNamespace::MyClass| 382 | +-------------------------------------+ 383 | ^ ^ ^ ^ 384 | (1) (2)(3) (4) 385 | 386 | (1) name of the symbol, when a match ends with '(' it's a function. 387 | 388 | (2) kind of the symbol, possible kinds are : 389 | * c = classes 390 | * d = macro definitions 391 | * e = enumerators (values inside an enumeration) 392 | * f = function definitions 393 | * g = enumeration names 394 | * m = class, struct, and union members 395 | * n = namespaces 396 | * p = function prototypes 397 | * s = structure names 398 | * t = typedefs 399 | * u = union names 400 | * v = variable definitions 401 | 402 | (3) access, possible values are : 403 | * + = public 404 | * # = protected 405 | * - = private 406 | Note: enumerators have no access information 407 | 408 | (4) scope where the symbol is defined. 409 | Note: If the scope is empty it's a global symbol 410 | Note: anonymous scope may end with __anon[number] 411 | eg: If you have an anonymous enum in MyNamespace::MyClass : > 412 | 413 | namespace MyNamespace 414 | { 415 | class MyClass 416 | { 417 | private: 418 | 419 | enum 420 | { 421 | E_ENUM0, 422 | E_ENUM1, 423 | E_ENUM2 424 | }; 425 | }; 426 | } 427 | < 428 | 429 | You should see : 430 | 431 | +----------------------------------------------+ 432 | |E_ENUM0 e MyNamespace::MyClass::__anon1| 433 | |E_ENUM1 e MyNamespace::MyClass::__anon1| 434 | |E_ENUM2 e MyNamespace::MyClass::__anon1| 435 | +----------------------------------------------+ 436 | ^ 437 | __anon[number] 438 | 439 | 5.2. Global scope completion~ 440 | 441 | The global scope completion allows you to complete global symbols for the base 442 | you are currently typing. The base can start with '::' or not. 443 | Note: Global scope completion only works with a non empty base, if you run a 444 | completion just after a '::' the completion will fail. The reason is that if 445 | there is no base to complete the script will try to display all the tags in 446 | the database. For small project it could be not a problem but for others you 447 | may wait 5 minutes or more for a result. 448 | 449 | eg1 : > 450 | 451 | pthread_cr => pthread_create 452 | < 453 | Where pthread_create is a global function. 454 | eg2: > 455 | ::globa => ::global_func( 456 | +----------------+ 457 | |global_func( f| 458 | |global_var1 v| 459 | |global_var2 v| 460 | +----------------+ 461 | < 462 | Where global_var1, global_var2 and global_func are global symbols 463 | eg3: > 464 | :: => [NO MATCH] 465 | < 466 | No match because a global completion from an empty base is not allowed. 467 | 468 | 5.3. Namespace scope completion~ 469 | 470 | You can complete namespace members after a 'MyNamespace::'. Contrary to global 471 | scope completion you can run a completion from an empty base. 472 | Possible members are: 473 | * Namespaces 474 | * Classes 475 | * Structs 476 | * Unions 477 | * Enums 478 | * Functions 479 | * Variables 480 | * Typedefs 481 | 482 | eg: > 483 | MyNamespace:: 484 | +--------------------------------+ 485 | |E_ENUM0 e MyNamespace| 486 | |E_ENUM1 e MyNamespace| 487 | |E_ENUM2 e MyNamespace| 488 | |MyClass c MyNamespace| 489 | |MyEnum g MyNamespace| 490 | |MyStruct s MyNamespace| 491 | |MyUnion u MyNamespace| 492 | |SubNamespace n MyNamespace| 493 | |doSomething( f MyNamespace| 494 | |myVar v MyNamespace| 495 | |something_t t MyNamespace| 496 | +--------------------------------+ 497 | 498 | 5.4. Class scope completion~ 499 | 500 | You can complete class members after a 'MyClass::'. Contrary to global scope 501 | completion you can run a completion from an empty base. 502 | By default, there is two behaviours for class scope completion. 503 | 504 | a) Completion of a base class of the current class scope 505 | 506 | When you are completing a base class of the current class scope, you 507 | will see all members of this class in the popup menu. 508 | eg: > 509 | 510 | class A 511 | { 512 | public: 513 | enum 514 | { 515 | E_ENUM0, 516 | E_ENUM1, 517 | E_ENUM2, 518 | }; 519 | 520 | void func1(); 521 | static int _staticMember; 522 | 523 | private: 524 | int _member; 525 | }; 526 | 527 | class B : public A 528 | { 529 | public: 530 | void doSomething(); 531 | }; 532 | 533 | 534 | void MyClassB::doSomething() 535 | { 536 | MyClassA:: 537 | +---------------------------+ 538 | |E_ENUM0 e MyClassA| 539 | |E_ENUM1 e MyClassA| 540 | |E_ENUM2 e MyClassA| 541 | |func1( f + MyClassA| 542 | |_member m - MyClassA| 543 | |_staticMember m + MyClassA| 544 | +---------------------------+ 545 | } 546 | < 547 | 548 | b) Completion of a non base class of the current class scope 549 | 550 | When you are completing a class that is not a base class of the 551 | current class you will see only enumerators and static members. 552 | eg: > 553 | 554 | class C 555 | { 556 | public: 557 | void doSomething(); 558 | }; 559 | 560 | void MyClassC::doSomething() 561 | { 562 | MyClassA:: 563 | +---------------------------+ 564 | |E_ENUM0 e MyClassA| 565 | |E_ENUM1 e MyClassA| 566 | |E_ENUM2 e MyClassA| 567 | |_staticMember m + MyClassA| 568 | +---------------------------+ 569 | } 570 | < 571 | You can override the default behaviour by setting the 572 | |OmniCpp_DisplayMode| option. 573 | 574 | 5.5. Current scope completion~ 575 | 576 | When you start a completion from an empty instruction you are in "Current 577 | scope completion" mode. You will see possible members of each context in 578 | the context stack. 579 | eg: > 580 | void MyClass::doSomething() 581 | { 582 | using namespace MyNamespace; 583 | using namespace SubNamespace; 584 | 585 | // You will see members of each context in the context stack 586 | // 1) MyClass members 587 | // 2) MyNamespace::SubNamespace members 588 | // 3) MyNamespace members 589 | 590 | 591 | +------------------------------------------+ 592 | |_member1 m + MyClass | 593 | |_member2 m # MyClass | 594 | |func1( f MyNamespace::SubNamespace| 595 | |var v MyNamespace::SubNamespace| 596 | |func1( f MyNamespace | 597 | |var v MyNamespace | 598 | +------------------------------------------+ 599 | } 600 | < 601 | 602 | 5.6. Class, Struct and Union members completion~ 603 | 604 | You can complete members of class, struct and union instances after a '->' or 605 | '.'. 606 | eg: > 607 | MyClass myObject; 608 | myObject. 609 | +-----------------------+ 610 | |_member1 m + MyClass | 611 | |_member2 m # MyClass | 612 | +-----------------------+ 613 | < 614 | 615 | 5.7. Attribute members and returned type completion~ 616 | 617 | You can complete a class member or a return type of a function. 618 | eg: > 619 | MyClass myObject; 620 | 621 | // Completion of the member _member1 622 | myObject._member1-> 623 | +------------------------+ 624 | |get( m + AnotherClass1| 625 | +------------------------+ 626 | 627 | // Completion of the return type of the function get() 628 | myObject._member1->get()-> 629 | +--------------------------+ 630 | |_member1 m + AnotherClass2| 631 | |_member2 m # AnotherClass2| 632 | |_member3 m - AnotherClass2| 633 | +--------------------------+ 634 | 635 | 5.8. Anonymous type completion~ 636 | 637 | Note: To use this feature you need at least|Exuberant_ctags| version 5.6 638 | 639 | You can complete an anonymous type like this : > 640 | struct 641 | { 642 | int a; 643 | int b; 644 | int c; 645 | }globalVar; 646 | 647 | void func() 648 | { 649 | globalVar. 650 | +---------------+ 651 | |a m + __anon1| 652 | |b m + __anon1| 653 | |c m + __anon1| 654 | +---------------+ 655 | } 656 | < 657 | Where globalVar is a global variable of an anonymous type 658 | 659 | 5.9. Typedef completion~ 660 | 661 | You can complete a typedef. The typedef is resolved recursively, thus typedef 662 | of typedef of... may not be a problem. 663 | 664 | You can also complete a typedef of an anonymous type, eg : > 665 | typedef struct 666 | { 667 | int a; 668 | int b; 669 | int c; 670 | }something_t; 671 | 672 | something_t globalVar; 673 | 674 | void func() 675 | { 676 | globalVar. 677 | +---------------+ 678 | |a m + __anon1| 679 | |b m + __anon1| 680 | |c m + __anon1| 681 | +---------------+ 682 | } 683 | < 684 | Where globalVar is a global variable of typedef of an anonymous type. 685 | 686 | 5.10. Completion of the "this" pointer~ 687 | 688 | You can complete the "this" pointer. 689 | eg: > 690 | this-> 691 | +-----------------------+ 692 | |_member1 m + MyClass | 693 | |_member2 m # MyClass | 694 | +-----------------------+ 695 | 696 | (*this). 697 | +-----------------------+ 698 | |_member1 m + MyClass | 699 | |_member2 m # MyClass | 700 | +-----------------------+ 701 | < 702 | 703 | 5.11. Completion after a cast~ 704 | 705 | You can complete an object after a C or C++ cast. 706 | eg: > 707 | // C cast style 708 | ((AnotherStruct*)pStruct)-> 709 | 710 | // C++ cast style 711 | static_cast(pStruct)-> 712 | < 713 | 714 | 5.12. Preview window~ 715 | 716 | If the 'completeopt' option contains the setting "preview" (this is the 717 | default value), you will see a preview window during the completion. 718 | This window shows useful information like function signature, filename where 719 | the symbol is define etc... 720 | 721 | The preview window contains tag information, the list below is non exhaustive. 722 | 723 | * name : name of the tag 724 | * cmd : regexp or line number that helps to find the tag 725 | * signature : signature for prototypes and functions 726 | * kind : kind of the tag (eg: namespace, class etc...) 727 | * access : access information (eg: public, protected, private) 728 | * inherits : list of base classes 729 | * filename : filename where the tag is define 730 | 731 | 5.13. Code tokenization~ 732 | 733 | When you start a completion, the current instruction is tokenized ignoring 734 | spaces, tabs, carriage returns and comments. Thus you can complete a symbol 735 | even if the current instruction is on multiple lines, has comments between 736 | words etc... : 737 | eg: this case is unrealistic but it's just for illustration > 738 | 739 | myObject [ 0 ]/* Why is there a comment here ?*/ 740 | ->_member 741 | -> 742 | < 743 | 744 | ============================================================================== 745 | 6. Limitations~ 746 | *omnicpp-limitations* 747 | Some C++ features are not supported by the script, some implemented features 748 | may not work properly in some conditions. They are multiple reasons like a 749 | lack of information in the database, performance issues and so on... 750 | 751 | 6.1. Attribute members and returned type completion~ 752 | 753 | To work properly, the completion of attribute members and returned type of 754 | functions depends on how you write your code in the class declaration. 755 | Because the tags database does not contain information like return type or 756 | type of a member, the script use the cmd information of the tag to determine 757 | the type of an attribute member or the return type of a function. 758 | 759 | Thus, because the cmd is a regular expression (or line number for #define) if 760 | you write your code like this : > 761 | 762 | class MyClass 763 | { 764 | public: 765 | 766 | MyOtherClass 767 | _member; 768 | }; 769 | < 770 | The type of _member will not be recognized, because the cmd will be 771 | /^ _member;$/ and does not contain the type MyOtherClass. 772 | The correct case should be : > 773 | 774 | class MyClass 775 | { 776 | public: 777 | 778 | MyOtherClass _member; 779 | }; 780 | < 781 | It's the same problem for return type of function : > 782 | 783 | class MyClass 784 | { 785 | public: 786 | 787 | MyOtherClass 788 | getOtherClass(); 789 | }; 790 | < 791 | Here the cmd will be /^ getOtherClass();$/ and the script won't find the 792 | return type. 793 | The correct case should be : > 794 | class MyClass 795 | { 796 | public: 797 | 798 | MyOtherClass getOtherClass(); 799 | }; 800 | < 801 | 802 | 6.2. Static members~ 803 | 804 | It's the same problem as above, tags database does not contain information 805 | about static members. The only fast way to get this information is to use the 806 | cmd. 807 | 808 | 6.3. Typedef~ 809 | 810 | It's the same problem as above, tags database does not contain information 811 | about the type of a typedef. The script use the cmd information to resolve the 812 | typedef. 813 | 814 | 6.4. Restricted inheritance access~ 815 | 816 | Tags database contains inheritance information but unfortunately inheritance 817 | access are not available. We could use the cmd but we often find code 818 | indentation like this : > 819 | 820 | class A : 821 | public B, 822 | protected C, 823 | private D 824 | { 825 | }; 826 | < 827 | Here the cmd will be /^class A :$/, we can't extract inheritance access. 828 | 829 | 6.5. Using namespace parsing~ 830 | 831 | When you start a completion, using namespace declarations are parsed from the 832 | cursor position to the first scope to detect local using namespace 833 | declarations. After that, global using namespace declarations are parsed in the 834 | file and included files. 835 | 836 | There is a limitation for global using namespace detection, for performance 837 | issues only using namespace that starts a line will be detected. 838 | 839 | 6.6. Friend classes~ 840 | 841 | Tags database does not contain information about friend classes. The script 842 | does not support friend classes. 843 | 844 | 6.7. Templates~ 845 | 846 | At the moment, |Exuberant_ctags| does not provide additional information for 847 | templates. That's why the script does not handle templates. 848 | 849 | ============================================================================== 850 | 7. FAQ & TIPS~ 851 | *omnicpp-faq* 852 | 853 | * How to complete STL objects ? 854 | If you have some troubles to generate a good ctags database for STL you 855 | can try this solution : 856 | 857 | 1) Download SGI's STL from SGI's site 858 | (http://www.sgi.com/tech/stl/download.html) 859 | 2) Replace all __STL_BEGIN_NAMESPACE by "namespace std {" and 860 | __STL_END_NAMESPACE by "}" from header and source files. (with Vim, 861 | or with tar and sed or another tool) 862 | 3) Run ctags and put the generated tags file in a directory eg: 863 | ~/MyTags/stl.tags 864 | 4) set tags+=~/MyTags/stl.tags 865 | 866 | The main problem is that you can't tell to ctags that 867 | __STL_BEGIN_NAMESPACE = "namespace std {" even with the option -I. 868 | That's why you need the step 2). 869 | 870 | Here is another solution if you have STL sources using _GLIBCXX_STD macro 871 | (Tip by Nicola Bonelli) : > 872 | 873 | let OmniCpp_DefaultNamespaces = ["std", "_GLIBCXX_STD"] 874 | < 875 | * How to close automatically the preview window after a completion ? 876 | (Tip by Kamil Renczewski) 877 | 878 | You can add to your |vimrc| the following lines : > 879 | 880 | autocmd CursorMovedI * if pumvisible() == 0|pclose|endif 881 | autocmd InsertLeave * if pumvisible() == 0|pclose|endif 882 | < 883 | ============================================================================== 884 | 8. History~ 885 | *omnicpp-history* 886 | Version O.41 887 | - It's recommended to update ctags to version 5.7 or higher 888 | - The plugin is now activated for C files 889 | - New value for OmniCpp_SelectFirstItem when the option is equal to 890 | 2 the first item is selected without inserting it to 891 | the text (patch from Marek Olszewski) 892 | - Bug when completing union members fixed with ctags 5.7 893 | (reported by Willem-Jan de Hoog) 894 | - New option OmniCpp_LocalSearchDecl (patch from Roland Kuck) 895 | - Bug when tags=something,,somethingelse (reported by Tobias Pflug) 896 | - Bug with nested structure (reported by Mikhail Daen) 897 | - Bug where the script fails to detect the type of a variable when 898 | the ignorecase option is on (reported by Alexey Vakhov) 899 | - Error message when trying to use completion on a not yet saved 900 | Vim buffer (reported by Neil Bird) 901 | - Error message when trying to use completion on an file opened from 902 | a tselect command (reported by Henrique Andrade) 903 | 904 | Version 0.4 905 | - The script is renamed to OmniCppComplete according to the library 906 | script directory structure. 907 | - OmniCpp_ClassScopeCompletionMethod renamed to OmniCpp_DisplayMode 908 | - Fixed a bug where the quickfix list is modified after a completion. 909 | - OmniCpp_ShowPrototypeInAbbr option added. It allows to show the 910 | function signature in the abbreviation. 911 | - OmniCpp_ShowAccess option added. It allows to hide the access 912 | information in the popup menu. 913 | - The tags database format must be a ctags 5.6 database if you want to 914 | complete anonymous types. 915 | - Fixed current scope detection not working properly in destructors. 916 | - Don't show protected and private members according to the current scope. 917 | - Overloaded functions are now filtered properly. 918 | - New cache system using less memory. 919 | - The class scope of a method is now resolved properly with "using 920 | namespace" declarations. 921 | - OmniCpp_SelectFirstItem option added. It allows to not select the first 922 | item in the popup menu when 'completeopt' does not contain "longest". 923 | - Fixed the bug where a "random" item in the popup menu is selected 924 | by default when 'completeopt' does not contain "longest" option. 925 | - The script is now split in library scripts. 926 | - Cache added for 'using namespace' search in included files 927 | - Default value for OmniCpp_NamespaceSearch is now 1 (search only in the 928 | current buffer). 929 | - Namespace search automatically disabled for C files even if 930 | OmniCpp_NamespaceSearch != 0. 931 | - To avoid linear search in tags files, the ignorecase option is now 932 | disabled when getting tags datas (the user setting is restored after). 933 | - Fixed a bug where friend functions may crash the script and also crash vim. 934 | 935 | Version 0.32 936 | - Optimizations in search members methods. 937 | - 'May complete' behaviour is now set to default for dot '.' and arrow 938 | '->' (mappings are set in after/ftplugin/cpp.vim) 939 | - Fixed the option CppOmni_ShowScopeInAbbr not detected after the first 940 | completion. 941 | - Exceptions catched from taglist() when a tag file is corrupted. 942 | - Fixed a bug where enumerators in global scope didn't appear in the 943 | popup menu. 944 | 945 | Version 0.31 946 | WARNING: For this release and future releases you have to build your tags 947 | database with this cmd : 948 | "ctags -R --c++-kinds=+p --fields=+iaS --extra=+q ." 949 | Please read installation instructions in the documentation for details 950 | 951 | - May complete added, please see installation notes for details. 952 | - Fixed a bug where the completion works while in a comment or in a string. 953 | 954 | Version 0.3 955 | WARNING: For this release and future releases you have to build your tags 956 | database with this cmd : 957 | "ctags -R --c++-kinds=+p --fields=+iaS --extra=+q ." 958 | Please read installation instructions in the documentation for details 959 | 960 | - Documentation added. 961 | - Fixed a bug where typedefs were not correctly resolved in namespaces 962 | in some cases. 963 | - Fixed a bug where the type can not be detected when we have a decl 964 | like this: class A {}globalVar; 965 | - Fixed a bug in type detection where searchdecl() (gd) find 966 | incorrect declaration instruction. 967 | - Global scope completion now only works with non-empty base. 968 | - Using namespace list is now parsed in the current buffer and in 969 | included files. 970 | - Fixed a bug where the completion fails in some cases when the user 971 | sets the ignorecase to on 972 | - Preview window information added 973 | - Some improvements in type detection, the type can be properly detected 974 | with a declaration like this: 975 | 'Class1 *class1A = NULL, **class1B = NULL, class1C[9], class1D[1] = {};' 976 | - Fixed a bug where parent scopes were not displayed in the popup menu 977 | in the current scope completion mode. 978 | - Fixed a bug where an error message was displayed when the last 979 | instruction was not finished. 980 | - Fixed a bug where the completion fails if a punctuator or operator was 981 | immediately after the cursor. 982 | - The script can now detect parent contexts at the cursor position 983 | thanks to 'using namespace' declarations. 984 | It can also detect ambiguous namespaces. They are not included in 985 | the context list. 986 | - Fixed a bug where the current scope is not properly detected when 987 | a file starts with a comment 988 | - Fixed a bug where the type is not detected when we have myObject[0] 989 | - Removed the system() call in SearchMembers(), no more calls to the 990 | ctags binary. The user have to build correctly his database with the cmd: 991 | "ctags -R --c++-kinds=+p --fields=+iaS --extra=+q ." 992 | - File time cache removed, the user have to rebuild his data base after a 993 | modification. 994 | 995 | Version 0.22 996 | - Completion of unnamed type (eg: You can complete g_Var defined like 997 | this 'struct {int a; int b;}g_Var;'). It also works for a typedef of 998 | an unnamed type (eg: 'typedef struct {int a; int b;}t_mytype; t_mytype 999 | g_Var;'). 1000 | - Tag file's time cache added, if a tag file has changed the global 1001 | scope result cache is cleared. 1002 | - Fixed a bug where the tokenization process enter in an infinite loop 1003 | when a file starts with '/*'. 1004 | 1005 | Version 0.21 1006 | - Improvements on the global scope completion. 1007 | The user can now see the progression of the search and complete 1008 | matches are stored in a cache for optimization. The cache is cleared 1009 | when the tag env is modified. 1010 | - Within a class scope when the user complete an empty word, the popup 1011 | menu displays the members of the class then members of the global 1012 | scope. 1013 | - Fixed a bug where a current scope completion failed after a punctuator 1014 | or operator (eg: after a '=' or '!='). 1015 | 1016 | Version 0.2 1017 | - Improvements in type detection (eg: when a variable is declared in a 1018 | parameter list, a catch clause, etc...) 1019 | - Code tokenization => ignoring spaces, tabs, carriage returns and comments 1020 | You can complete a code even if the instruction has bad 1021 | indentation, spaces or carriage returns between words 1022 | - Completion of class members added 1023 | - Detection of the current scope at the cursor position. 1024 | If you run a completion from en empty line, members of the current 1025 | scope are displayed. It works on the global namespace and the current 1026 | class scope (but there is not the combination of the 2 for the moment) 1027 | - Basic completion on the global namespace (very slow) 1028 | - Completion of returned type added 1029 | - this pointer completion added 1030 | - Completion after a cast added (C and C++ cast) 1031 | - Fixed a bug where the matches of the complete menu are not filtered 1032 | according to what the user typed 1033 | - Change the output of the popup menu. The type of the member 1034 | (function, member, enum etc...) is now display as a single letter. 1035 | The access information is display like this : '+' for a public member 1036 | '#' for a protected member and '-' for a private member. 1037 | The last information is the class, namespace or enum where the member is define. 1038 | 1039 | Version 0.12: 1040 | - Complete check added to the search process, you can now cancel 1041 | the search during a complete search. 1042 | 1043 | Version 0.1: 1044 | - First release 1045 | 1046 | ============================================================================== 1047 | 9. Thanks~ 1048 | *omnicpp-thanks* 1049 | * For advices, bug report, documentation, help, ideas : 1050 | Alexey Vakhov (bug report) 1051 | Arthur Axel "fREW" Schmidt (documentation) 1052 | Dennis Lubert (bug report) 1053 | Henrique Andrade (bug report) 1054 | Kamil Renczewski (tips) 1055 | Marek Olszewski (patch) 1056 | Markus Trenkwalder (bug report) 1057 | Martin Stubenschrott (bug report) 1058 | Mikhail Daen (bug report) 1059 | Neil Bird (bug report) 1060 | Nicola Bonelli (tips) 1061 | Robert Webb (bug report) 1062 | Roland Kuck (patch) 1063 | Tobias Pflug (bug report) 1064 | Willem-Jan de Hoog (bug report) 1065 | Yegappan Lakshmanan (advices) 1066 | 1067 | 1068 | * Darren Hiebert for Exuberant Ctags 1069 | 1070 | * All Vim devs for Vim 1071 | 1072 | * Bram Moolenaar for Vim 1073 | 1074 | * You for using this script :) 1075 | 1076 | ============================================================================== 1077 | 1078 | vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl: 1079 | --------------------------------------------------------------------------------