summaryrefslogtreecommitdiff
path: root/.config/vim/autoload/omni/cpp/namespaces.vim
blob: 386b3f997d28e96a970fe0b23575e5b74458663c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
" Description: Omni completion script for cpp files
" Maintainer:  Vissale NEANG
" Last Change: 26 sept. 2007

let g:omni#cpp#namespaces#CacheResolve = {}
let g:omni#cpp#namespaces#CacheUsing = {}
" TODO: For the next release
"let g:omni#cpp#namespaces#CacheAlias = {}

" Get the using namespace list from a line
function! s:GetNamespaceAliasListFromLine(szLine)
    let result = {}
    let tokens = omni#cpp#tokenizer#Tokenize(a:szLine)
    let szAlias = ''
    let szNamespace = ''
    let state = 0
    for token in tokens
        if state==0
            let szAlias = ''
            let szNamespace = ''
            if token.value == '/*'
                let state = 1
            elseif token.value == '//'
                " It's a comment
                let state = -1
                break
            elseif token.value == 'namespace'
                let state = 2
            endif
        elseif state==1
            if token.value == '*/'
                let state=0
            endif
        elseif state==2
            if token.kind == 'cppWord'
                let szAlias .= token.value
                let state = 3
            else
                let state = -1
                break
            endif
        elseif state == 3
            if token.value == '='
                let state = 4
            else
                let state = -1
                break
            endif
        elseif state == 4
            if token.value == '::'
                let szNamespace .= token.value
                let state = 5
            elseif token.kind == 'cppWord'
                let szNamespace .= token.value
                let state = 6
                " Maybe end of tokens
            endif
        elseif state==5
            if token.kind == 'cppWord'
                let szNamespace .= token.value
                let state = 6
                " Maybe end of tokens
            else
                " Error, we can't have 'namespace ALIAS = Something::'
                let state = -1
                break
            endif
        elseif state==6
            if token.value == '::'
                let szNamespace .= token.value
                let state = 5
            else
                call extend(result, {szAlias : szNamespace})
                let state = 0
            endif
        endif
    endfor

    if state == 6
        call extend(result, {szAlias : szNamespace})
    endif

    return result
endfunc

" Get the using namespace list from a line
function! s:GetNamespaceListFromLine(szLine)
    let result = []
    let tokens = omni#cpp#tokenizer#Tokenize(a:szLine)
    let szNamespace = ''
    let state = 0
    for token in tokens
        if state==0
            let szNamespace = ''
            if token.value == '/*'
                let state = 1
            elseif token.value == '//'
                " It's a comment
                let state = -1
                break
            elseif token.value == 'using'
                let state = 2
            endif
        elseif state==1
            if token.value == '*/'
                let state=0
            endif
        elseif state==2
            if token.value == 'namespace'
                let state = 3
            else
                " Error, 'using' must be followed by 'namespace'
                let state = -1
                break
            endif
        elseif state==3
            if token.value == '::'
                let szNamespace .= token.value
                let state = 4
            elseif token.kind == 'cppWord'
                let szNamespace .= token.value
                let state = 5
                " Maybe end of tokens
            endif
        elseif state==4
            if token.kind == 'cppWord'
                let szNamespace .= token.value
                let state = 5
                " Maybe end of tokens
            else
                " Error, we can't have 'using namespace Something::'
                let state = -1
                break
            endif
        elseif state==5
            if token.value == '::'
                let szNamespace .= token.value
                let state = 4
            else
                call extend(result, [szNamespace])
                let state = 0
            endif
        endif
    endfor

    if state == 5
        call extend(result, [szNamespace])
    endif

    return result
endfunc

" Get the namespace list from a namespace map
function! s:GetUsingNamespaceListFromMap(namespaceMap, ...)
    let stopLine = 0
    if a:0>0
        let stopLine = a:1
    endif

    let result = []
    let keys = sort(keys(a:namespaceMap), 'omni#common#utils#CompareNumber')
    for i in keys
        if stopLine != 0 && i > stopLine
            break
        endif
        call extend(result, a:namespaceMap[i])
    endfor
    return result
endfunc

" Get global using namespace list from the current buffer
function! omni#cpp#namespaces#GetListFromCurrentBuffer(...)
    let namespaceMap = s:GetAllUsingNamespaceMapFromCurrentBuffer()
    let result = []
    if namespaceMap != {}
        let result = s:GetUsingNamespaceListFromMap(namespaceMap, (a:0 > 0)? a:1 : line('.'))
    endif
    return result
endfunc

" Get global using namespace map from the current buffer and include files recursively
function! s:GetAllUsingNamespaceMapFromCurrentBuffer(...)
    let includeGuard = (a:0>0)? a:1 : {}

    let szBufferName = getreg("%")
    let szFilePath = omni#cpp#utils#ResolveFilePath(szBufferName)
    let szFilePath = (szFilePath=='')? szBufferName : szFilePath

    let namespaceMap = {}
    if has_key(includeGuard, szFilePath)
        return namespaceMap
    else
        let includeGuard[szFilePath] = 1
    endif

    let namespaceMap = omni#cpp#namespaces#GetMapFromCurrentBuffer()

    if g:OmniCpp_NamespaceSearch != 2
        " We don't search included files if OmniCpp_NamespaceSearch != 2
        return namespaceMap
    endif

    for inc in omni#cpp#includes#GetList()
        let lnum = inc.pos[0]
        let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard)
        if tmpMap != {}
            if has_key(namespaceMap, lnum)
                call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap))
            else
                let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap)
            endif
        endif
    endfor

    return namespaceMap
endfunc

" Get global using namespace map from a file and include files recursively
function! s:GetAllUsingNamespaceMapFromFile(szFilePath, ...)
    let includeGuard = {}
    if a:0 >0
        let includeGuard = a:1
    endif

    let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath)
    let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath

    let namespaceMap = {}
    if has_key(includeGuard, szFilePath)
        return namespaceMap
    else
        let includeGuard[szFilePath] = 1
    endif

    " If g:OmniCpp_NamespaceSearch == 1 (search namespaces only in the current
    " buffer) we don't use cache for the current buffer
    let namespaceMap = omni#cpp#namespaces#GetMapFromBuffer(szFilePath, g:OmniCpp_NamespaceSearch==1)

    if g:OmniCpp_NamespaceSearch != 2
        " We don't search included files if OmniCpp_NamespaceSearch != 2
        return namespaceMap
    endif

    for inc in omni#cpp#includes#GetList(szFilePath)
        let lnum = inc.pos[0]
        let tmpMap = s:GetAllUsingNamespaceMapFromFile(inc.include, includeGuard)
        if tmpMap != {}
            if has_key(namespaceMap, lnum)
                call extend(namespaceMap[lnum], s:GetUsingNamespaceListFromMap(tmpMap))
            else
                let namespaceMap[lnum] = s:GetUsingNamespaceListFromMap(tmpMap)
            endif
        endif
    endfor

    return namespaceMap
endfunc

" Get global using namespace map from a the current buffer
function! omni#cpp#namespaces#GetMapFromCurrentBuffer()
    let namespaceMap = {}
    let originalPos = getpos('.')

    call setpos('.', [0, 1, 1, 0])
    let curPos = [1,1]
    while curPos != [0,0]
        let curPos = searchpos('\C^using\s\+namespace', 'W')
        if curPos != [0,0]
            let szLine = getline('.')
            let startPos = curPos[1]
            let endPos = match(szLine, ';', startPos-1)
            if endPos!=-1
                " We get the namespace list from the line
                let namespaceMap[curPos[0]] = s:GetNamespaceListFromLine(szLine)
            endif
        endif
    endwhile

    call setpos('.', originalPos)
    return namespaceMap
endfunc

" Get global using namespace map from a file
function! omni#cpp#namespaces#GetMapFromBuffer(szFilePath, ...)
    let bUpdate = 0
    if a:0 > 0
        let bUpdate = a:1
    endif

    let szFilePath = omni#cpp#utils#ResolveFilePath(a:szFilePath)
    let szFilePath = (szFilePath=='')? a:szFilePath : szFilePath

    if !bUpdate && has_key(g:omni#cpp#namespaces#CacheUsing, szFilePath)
        return copy(g:omni#cpp#namespaces#CacheUsing[szFilePath])
    endif

    let namespaceMap = {}
    " The file exists, we get the global namespaces in this file
    let szFixedPath = escape(szFilePath, g:omni#cpp#utils#szEscapedCharacters)
    execute 'silent! lvimgrep /\C^using\s\+namespace/gj '.szFixedPath

    " key = line number
    " value = list of namespaces
    let listQuickFix = getloclist(0)
    for qf in listQuickFix
        let szLine = qf.text
        let startPos = qf.col
        let endPos = match(szLine, ';', startPos-1)
        if endPos!=-1
            " We get the namespace list from the line
            let namespaceMap[qf.lnum] = s:GetNamespaceListFromLine(szLine)
        endif
    endfor

    if szFixedPath != ''
        let g:omni#cpp#namespaces#CacheUsing[szFixedPath] = namespaceMap
    endif

    return copy(namespaceMap)
endfunc

" Get the stop position when searching for local variables
function! s:GetStopPositionForLocalSearch()
    " Stop position when searching a local variable
    let originalPos = getpos('.')
    let origPos = originalPos[1:2]
    let stopPosition = origPos
    let curPos = origPos
    while curPos !=[0,0]
        let stopPosition = curPos
        let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments)
    endwhile
    call setpos('.', originalPos)

    return stopPosition
endfunc

" Get namespaces alias used at the cursor postion in a vim buffer
" Note: The result depends on the current cursor position
" @return
"   -   Map of namespace alias
function! s:GetNamespaceAliasMap()
    " We store the cursor position because searchpairpos() moves the cursor
    let result = {}
    let originalPos = getpos('.')
    let origPos = originalPos[1:2]

    let stopPos = s:GetStopPositionForLocalSearch()
    let stopLine = stopPos[0]
    let curPos = origPos
    let lastLine = 0 
    let nextStopLine = origPos[0]
    let szReAlias = '\Cnamespace\s\+\w\+\s\+='
    while curPos !=[0,0]
        let curPos = searchpos('}\|\('. szReAlias .'\)', 'bW',stopLine)
        if curPos!=[0,0] && curPos[0]!=lastLine
            let lastLine = curPos[0]

            let szLine = getline('.')
            if origPos[0] == curPos[0]
                " We get the line until cursor position
                let szLine = szLine[:origPos[1]]
            endif

            let szLine = omni#cpp#utils#GetCodeFromLine(szLine)
            if match(szLine, szReAlias)<0
                " We found a '}'
                let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments)
            else
                " We get the namespace alias from the line
                call extend(result, s:GetNamespaceAliasListFromLine(szLine))
                let nextStopLine = curPos[0]
            endif
        endif
    endwhile

    " Setting the cursor to the original position
    call setpos('.', originalPos)

    call s:ResolveAliasKeys(result)
    return result
endfunc

" Resolve an alias
" eg: namespace IAmAnAlias1 = Ns1
" eg: namespace IAmAnAlias2 = IAmAnAlias1::Ns2
" => IAmAnAlias2 = Ns1::Ns2
function! s:ResolveAliasKey(mapNamespaceAlias, szAlias)
    let szResult = a:mapNamespaceAlias[a:szAlias]
    " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3']
    let listNamespace = split(szResult, '::')
    if len(listNamespace)
        " szBeginPart = 'Ns1'
        let szBeginPart = remove(listNamespace, 0)

        " Is 'Ns1' an alias ?
        if has_key(a:mapNamespaceAlias, szBeginPart) && szBeginPart != a:szAlias
            " Resolving alias 'Ns1'
            " eg: Ns1 = NsResolved
            let szResult = s:ResolveAliasKey(a:mapNamespaceAlias, szBeginPart)
            " szEndPart = 'Ns2::Ns3'
            let szEndPart = join(listNamespace, '::')
            if szEndPart != ''
                " Concatenation => szResult = 'NsResolved::Ns2::Ns3'
                let szResult .= '::' . szEndPart
            endif
        endif
    endif
    return szResult
endfunc

" Resolve all keys in the namespace alias map
function! s:ResolveAliasKeys(mapNamespaceAlias)
    let mapNamespaceAlias = a:mapNamespaceAlias
    call map(mapNamespaceAlias, 's:ResolveAliasKey(mapNamespaceAlias, v:key)')
endfunc

" Resolve namespace alias
function! omni#cpp#namespaces#ResolveAlias(mapNamespaceAlias, szNamespace)
    let szResult = a:szNamespace
    " ::Ns1::Ns2::Ns3 => ['Ns1', 'Ns2', 'Ns3']
    let listNamespace = split(a:szNamespace, '::')
    if len(listNamespace)
        " szBeginPart = 'Ns1'
        let szBeginPart = remove(listNamespace, 0)

        " Is 'Ns1' an alias ?
        if has_key(a:mapNamespaceAlias, szBeginPart)
            " Resolving alias 'Ns1'
            " eg: Ns1 = NsResolved
            let szResult = a:mapNamespaceAlias[szBeginPart]
            " szEndPart = 'Ns2::Ns3'
            let szEndPart = join(listNamespace, '::')
            if szEndPart != ''
                " Concatenation => szResult = 'NsResolved::Ns2::Ns3'
                let szResult .= '::' . szEndPart
            endif

            " If a:szNamespace starts with '::' we add '::' to the beginning
            " of the result
            if match(a:szNamespace, '^::')>=0
                let szResult = omni#cpp#utils#SimplifyScope('::' .  szResult)
            endif
        endif
    endif
    return szResult
endfunc

" Resolve namespace alias
function! s:ResolveAliasInNamespaceList(mapNamespaceAlias, listNamespaces)
    call map(a:listNamespaces, 'omni#cpp#namespaces#ResolveAlias(a:mapNamespaceAlias, v:val)')
endfunc

" Get namespaces used at the cursor postion in a vim buffer
" Note: The result depends on the current cursor position
" @return
"   -   List of namespace used in the reverse order
function! omni#cpp#namespaces#GetUsingNamespaces()
    " We have to get local using namespace declarations
    " We need the current cursor position and the position of the start of the
    " current scope

    " We store the cursor position because searchpairpos() moves the cursor
    let result = []
    let originalPos = getpos('.')
    let origPos = originalPos[1:2]

    let stopPos = s:GetStopPositionForLocalSearch()

    let stopLine = stopPos[0]
    let curPos = origPos
    let lastLine = 0 
    let nextStopLine = origPos[0]
    while curPos !=[0,0]
        let curPos = searchpos('\C}\|\(using\s\+namespace\)', 'bW',stopLine)
        if curPos!=[0,0] && curPos[0]!=lastLine
            let lastLine = curPos[0]

            let szLine = getline('.')
            if origPos[0] == curPos[0]
                " We get the line until cursor position
                let szLine = szLine[:origPos[1]]
            endif

            let szLine = omni#cpp#utils#GetCodeFromLine(szLine)
            if match(szLine, '\Cusing\s\+namespace')<0
                " We found a '}'
                let curPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments)
            else
                " We get the namespace list from the line
                let result = s:GetNamespaceListFromLine(szLine) + result
                let nextStopLine = curPos[0]
            endif
        endif
    endwhile

    " Setting the cursor to the original position
    call setpos('.', originalPos)

    " 2) Now we can get all global using namespace declaration from the
    " beginning of the file to nextStopLine
    let result = omni#cpp#namespaces#GetListFromCurrentBuffer(nextStopLine) + result

    " Resolving alias in the namespace list
    " TODO: For the next release
    "let g:omni#cpp#namespaces#CacheAlias= s:GetNamespaceAliasMap()
    "call s:ResolveAliasInNamespaceList(g:omni#cpp#namespaces#CacheAlias, result)

    return ['::'] + result
endfunc

" Resolve a using namespace regarding the current context
" For each namespace used:
"   -   We get all possible contexts where the namespace
"       can be define
"   -   We do a comparison test of each parent contexts with the current
"       context list
"           -   If one and only one parent context is present in the
"               current context list we add the namespace in the current
"               context
"           -   If there is more than one of parent contexts in the
"               current context the namespace is ambiguous
" @return
"   - result item
"       - kind = 0|1
"           - 0 = unresolved or error
"           - 1 = resolved
"       - value = resolved namespace
function! s:ResolveNamespace(namespace, mapCurrentContexts)
    let result = {'kind':0, 'value': ''}

    " If the namespace is already resolved we add it in the list of 
    " current contexts
    if match(a:namespace, '^::')>=0
        let result.kind = 1
        let result.value = a:namespace
        return result
    elseif match(a:namespace, '\w\+::\w\+')>=0
        let mapCurrentContextsTmp = copy(a:mapCurrentContexts) 
        let resolvedItem = {}
        for nsTmp in  split(a:namespace, '::')
            let resolvedItem = s:ResolveNamespace(nsTmp, mapCurrentContextsTmp)
            if resolvedItem.kind
                " Note: We don't extend the map
                let mapCurrentContextsTmp = {resolvedItem.value : 1}
            else
                break
            endif
        endfor
        if resolvedItem!={} && resolvedItem.kind
            let result.kind = 1
            let result.value = resolvedItem.value
        endif
        return result
    endif

    " We get all possible parent contexts of this namespace
    let listTagsOfNamespace = []
    if has_key(g:omni#cpp#namespaces#CacheResolve, a:namespace)
        let listTagsOfNamespace = g:omni#cpp#namespaces#CacheResolve[a:namespace]
    else
        let listTagsOfNamespace = omni#common#utils#TagList('^'.a:namespace.'$')
        let g:omni#cpp#namespaces#CacheResolve[a:namespace] = listTagsOfNamespace
    endif

    if len(listTagsOfNamespace)==0
        return result
    endif
    call filter(listTagsOfNamespace, 'v:val.kind[0]=="n"')

    " We extract parent context from tags
    " We use a map to avoid multiple entries
    let mapContext = {}
    for tagItem in listTagsOfNamespace
        let szParentContext = omni#cpp#utils#ExtractScope(tagItem)
        let mapContext[szParentContext] = 1
    endfor
    let listParentContext = keys(mapContext)

    " Now for each parent context we test if the context is in the current
    " contexts list
    let listResolvedNamespace = []
    for szParentContext in listParentContext
        if has_key(a:mapCurrentContexts, szParentContext)
            call extend(listResolvedNamespace, [omni#cpp#utils#SimplifyScope(szParentContext.'::'.a:namespace)])
        endif
    endfor

    " Now we know if the namespace is ambiguous or not
    let len = len(listResolvedNamespace)
    if len==1
        " Namespace resolved
        let result.kind = 1
        let result.value = listResolvedNamespace[0]
    elseif len > 1
        " Ambiguous namespace, possible matches are in listResolvedNamespace
    else
        " Other cases
    endif
    return result
endfunc

" Resolve namespaces
"@return
"   - List of resolved namespaces
function! omni#cpp#namespaces#ResolveAll(namespacesUsed)

    " We add the default context '::'
    let contextOrder = 0
    let mapCurrentContexts  = {}

    " For each namespace used:
    "   -   We get all possible contexts where the namespace
    "       can be define
    "   -   We do a comparison test of each parent contexts with the current
    "       context list
    "           -   If one and only one parent context is present in the
    "               current context list we add the namespace in the current
    "               context
    "           -   If there is more than one of parent contexts in the
    "               current context the namespace is ambiguous
    for ns in a:namespacesUsed
        let resolvedItem = s:ResolveNamespace(ns, mapCurrentContexts)
        if resolvedItem.kind
            let contextOrder+=1
            let mapCurrentContexts[resolvedItem.value] = contextOrder
        endif
    endfor

    " Build the list of current contexts from the map, we have to keep the
    " order
    let mapReorder = {}
    for key in keys(mapCurrentContexts)
        let mapReorder[ mapCurrentContexts[key] ] = key
    endfor
    let result = []
    for key in sort(keys(mapReorder))
        call extend(result, [mapReorder[key]])
    endfor
    return result
endfunc

" Build the context stack
function! s:BuildContextStack(namespaces, szCurrentScope)
    let result = copy(a:namespaces)
    if a:szCurrentScope != '::'
        let tagItem = omni#cpp#utils#GetResolvedTagItem(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope))
        if has_key(tagItem, 'inherits')
            let listBaseClass = omni#cpp#utils#GetClassInheritanceList(a:namespaces, omni#cpp#utils#CreateTypeInfo(a:szCurrentScope))
            let result = listBaseClass + result
        elseif has_key(tagItem, 'kind') && index(['c', 's', 'u', 'n'], tagItem.kind[0])>=0
            call insert(result, omni#cpp#utils#ExtractTypeInfoFromTag(tagItem))
        endif
    endif
    return result
endfunc

" Returns the class scope at the current position of the cursor
" @return a string that represents the class scope
" eg: ::NameSpace1::Class1
" The returned string always starts with '::'
" Note: In term of performance it's the weak point of the script
function! s:GetClassScopeAtCursor()
    " We store the cursor position because searchpairpos() moves the cursor
    let originalPos = getpos('.')
    let endPos = originalPos[1:2]
    let listCode = []
    let result = {'namespaces': [], 'scope': ''}

    while endPos!=[0,0]
        let endPos = searchpairpos('{', '', '}', 'bW', g:omni#cpp#utils#expIgnoreComments)
        let szReStartPos = '[;{}]\|\%^'
        let startPos = searchpairpos(szReStartPos, '', '{', 'bWn', g:omni#cpp#utils#expIgnoreComments)

        " If the file starts with a comment so the startPos can be [0,0]
        " we change it to [1,1]
        if startPos==[0,0]
            let startPos = [1,1]
        endif

        " Get lines backward from cursor position to last ; or { or }
        " or when we are at the beginning of the file.
        " We store lines in listCode
        if endPos!=[0,0]
            " We remove the last character which is a '{'
            " We also remove starting { or } or ; if exits
            let szCodeWithoutComments = substitute(omni#cpp#utils#GetCode(startPos, endPos)[:-2], '^[;{}]', '', 'g')
            call insert(listCode, {'startLine' : startPos[0], 'code' : szCodeWithoutComments})
        endif
    endwhile
    " Setting the cursor to the original position
    call setpos('.', originalPos)

    let listClassScope = []
    let bResolved = 0
    let startLine = 0
    " Now we can check in the list of code if there is a function
    for code in listCode
        " We get the name of the namespace, class, struct or union
        " and we store it in listClassScope
        let tokens = omni#cpp#tokenizer#Tokenize(code.code)
        let bContinue=0
        let bAddNamespace = 0
        let state=0
        for token in tokens
            if state==0
                if index(['namespace', 'class', 'struct', 'union'], token.value)>=0
                    if token.value == 'namespace'
                        let bAddNamespace = 1
                    endif
                    let state= 1
                    " Maybe end of tokens
                endif
            elseif state==1
                if token.kind == 'cppWord'
                    " eg: namespace MyNs { class MyCl {}; }
                    " => listClassScope = [MyNs, MyCl]
                    call extend( listClassScope , [token.value] )

                    " Add the namespace in result
                    if bAddNamespace
                        call extend(result.namespaces, [token.value])
                        let bAddNamespace = 0
                    endif

                    let bContinue=1
                    break
                endif
            endif
        endfor
        if bContinue==1
            continue
        endif

        " Simple test to check if we have a chance to find a
        " class method
        let aPos = matchend(code.code, '::\s*\~*\s*\w\+\s*(')
        if aPos ==-1
            continue
        endif

        let startLine = code.startLine
        let listTmp = []
        " eg: 'void MyNamespace::MyClass::foo('
        " => tokens = ['MyClass', '::', 'MyNamespace', 'void']
        let tokens = reverse(omni#cpp#tokenizer#Tokenize(code.code[:aPos-1])[:-4])
        let state = 0
        " Reading tokens backward
        for token in tokens
            if state==0
                if token.kind=='cppWord'
                    call insert(listTmp, token.value)
                    let state=1
                endif
            elseif state==1
                if token.value=='::'
                    let state=2
                else
                    break
                endif
            elseif state==2
                if token.kind=='cppWord'
                    call insert(listTmp, token.value)
                    let state=1
                else
                    break
                endif
            endif
        endfor
        
        if len(listTmp)
            if len(listClassScope)
                let bResolved = 1
                " Merging class scopes
                " eg: current class scope = 'MyNs::MyCl1'
                " method class scope = 'MyCl1::MyCl2'
                " If we add the method class scope to current class scope
                " we'll have MyNs::MyCl1::MyCl1::MyCl2 => it's wrong
                " we want MyNs::MyCl1::MyCl2
                let index = 0
                for methodClassScope in listTmp
                    if methodClassScope==listClassScope[-1]
                        let listTmp = listTmp[index+1:]
                        break
                    else
                        let index+=1
                    endif
                endfor
            endif
            call extend(listClassScope, listTmp)
            break
        endif
    endfor

    let szClassScope = '::'
    if len(listClassScope)
        if bResolved
            let szClassScope .= join(listClassScope, '::')
        else
            let szClassScope = join(listClassScope, '::')
            
            " The class scope is not resolved, we have to check using
            " namespace declarations and search the class scope in each
            " namespace
            if startLine != 0
                let namespaces = ['::'] + omni#cpp#namespaces#GetListFromCurrentBuffer(startLine)
                let namespaces = omni#cpp#namespaces#ResolveAll(namespaces)
                let tagItem = omni#cpp#utils#GetResolvedTagItem(namespaces, omni#cpp#utils#CreateTypeInfo(szClassScope))
                if tagItem != {}
                    let szClassScope = omni#cpp#utils#ExtractTypeInfoFromTag(tagItem)
                endif
            endif
        endif
    endif

    let result.scope = szClassScope
    return result
endfunc

" Get all contexts at the cursor position
function! omni#cpp#namespaces#GetContexts()
    " Get the current class scope at the cursor, the result depends on the current cursor position
    let scopeItem = s:GetClassScopeAtCursor()
    let listUsingNamespace = copy(g:OmniCpp_DefaultNamespaces)
    call extend(listUsingNamespace, scopeItem.namespaces)
    if g:OmniCpp_NamespaceSearch && &filetype != 'c'
        " Get namespaces used in the file until the cursor position
        let listUsingNamespace = omni#cpp#namespaces#GetUsingNamespaces() + listUsingNamespace
        " Resolving namespaces, removing ambiguous namespaces
        let namespaces = omni#cpp#namespaces#ResolveAll(listUsingNamespace)
    else
        let namespaces = ['::'] + listUsingNamespace
    endif
    call reverse(namespaces)

    " Building context stack from namespaces and the current class scope
    return s:BuildContextStack(namespaces, scopeItem.scope)
endfunc