Rewrite the processing of scoped tags

This commit is contained in:
Jan Larres 2011-02-13 19:20:15 +13:00
parent 5f400b06b7
commit 762d5b9968

View File

@ -5,7 +5,7 @@
" Licence: Vim licence
" Website: http://github.com/majutsushi/tagbar
" Note: This plugin was heavily inspired by the 'Taglist' plugin by
" Yegappan Lakshmanan and uses some small amounts of code from it.
" Yegappan Lakshmanan and uses a small amount of code from it.
" ============================================================================
if &cp || exists('g:loaded_tagbar')
@ -713,11 +713,11 @@ function! s:InitTypes()
endfor
endfor
let s:access_symbols = {}
let s:access_symbols.public = '+'
let s:access_symbols.protected = '#'
let s:access_symbols.private = '-'
let s:access_symbols = {
\ 'public' : '+',
\ 'protected' : '#',
\ 'private' : '-'
\ }
let s:type_init_done = 1
endfunction
@ -1046,11 +1046,12 @@ function! s:ProcessFile(fname, ftype)
endfor
let processedtags = []
call s:AddChildren(scopedtags, processedtags, '', '', 1, typeinfo)
call s:AddScopedTags(scopedtags, processedtags, '', '', 0, typeinfo)
" 'scopedtags' can still contain some tags that don't have any
" children
call extend(fileinfo.tags, scopedtags)
if !empty(scopedtags)
echoerr '''scopedtags'' not empty, this should never happen!'
\ 'Please notify the script maintainer with an example.'
endif
call extend(fileinfo.tags, processedtags)
endif
@ -1115,10 +1116,6 @@ function! s:ParseTagline(part1, part2, typeinfo)
let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
\ taginfo.name
let index = strridx(taginfo.fields[scope], a:typeinfo.sro)
let taginfo.parentpath = strpart(taginfo.fields[scope],
\ 0, index)
break
endif
endfor
@ -1128,162 +1125,135 @@ function! s:ParseTagline(part1, part2, typeinfo)
return taginfo
endfunction
" s:AddChildren() {{{1
" Extract children from the tag list and correctly add it to their parents.
" Unfortunately the parents aren't necessarily actually there -- for example,
" in C++ a class can be defined in a header file and implemented in a .cpp
" file (so the class itself doesn't appear in the .cpp file and thus doesn't
" genereate a tag). Another example are anonymous
" namespaces/structs/enums/unions that also don't get a tag themselves. These
" tags are thus called 'pseudo-tags' in Tagbar.
" This (in conjunction with ProcessPseudoTag) is probably the most cryptic
" function since it has to deal with things that aren't actually there and
" several corner cases. Try not to think about it too much.
function! s:AddChildren(tags, processedtags, curpath, pscope, depth, typeinfo)
if empty(a:curpath)
let is_child = ''
else
let is_child = ' && match(v:val.path, ''^\C'' . a:curpath) == 0'
" s:AddScopedTags() {{{1
" Recursively process tags. Unfortunately there is a problem: not all tags in
" a hierarchy are actually there. For example, in C++ a class can be defined
" in a header file and implemented in a .cpp file (so the class itself doesn't
" appear in the .cpp file and thus doesn't generate a tag). Another example
" are anonymous structures like namespaces, structs, enums, and unions, that
" also don't get a tag themselves. These tags are thus called 'pseudo-tags' in
" Tagbar. Properly parsing them is quite tricky, so try not to think about it
" too much.
function! s:AddScopedTags(tags, processedtags, curpath, pscope, depth, typeinfo)
let is_cur_tag = 'v:val.depth == a:depth'
if !empty(a:curpath)
" Check whether the tag is either a direct child at the current depth
" or at least a proper grandchild with pseudo-tags in between. If it
" is a direct child also check for matching scope.
let is_cur_tag .= ' &&
\ (v:val.path == a:curpath ||
\ match(v:val.path, ''\V\^\C'' . a:curpath . a:typeinfo.sro) == 0) &&
\ (v:val.path == a:curpath ? (v:val.scope == a:pscope) : 1)'
endif
let is_cur_child = 'v:val.depth == a:depth' . is_child
let curchildren = filter(copy(a:tags), is_cur_child)
let curtags = filter(copy(a:tags), is_cur_tag)
" 'curchildren' are children at the current depth
if !empty(curchildren)
call filter(a:tags, '!(' . is_cur_child . ')')
if !empty(curtags)
call filter(a:tags, '!(' . is_cur_tag . ')')
for child in curchildren
let parentlist = s:ExtractParentList(a:tags, a:processedtags,
\ child.path, child.scope, a:typeinfo)
let realtags = []
let pseudotags = []
if empty(parentlist)
" If we don't have a parent at this point it must be a
" new pseudo-tag, so create an entry for it
call s:ProcessPseudoTag(a:tags, a:processedtags, child,
\ a:curpath, a:pscope, a:typeinfo)
while !empty(curtags)
let tag = remove(curtags, 0)
if tag.path != a:curpath
" tag is child of a pseudo-tag, so create a new pseudo-tag and
" add all its children to it
let pseudotag = s:ProcessPseudoTag(curtags, tag, a:curpath,
\ a:pscope, a:typeinfo)
call add(pseudotags, pseudotag)
else
let parent = parentlist[0]
if has_key(parent, 'children')
call add(parent.children, child)
else
let parent.children = [child]
endif
call add(a:processedtags, parent)
call add(realtags, tag)
endif
endfor
endwhile
" Recursively add children
for tag in a:processedtags
if !has_key(tag, 'children')
" Recursively add the children of the tags on the current level
for tag in realtags
if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
continue
endif
if empty(a:curpath)
let fullpath = tag.name
else
let fullpath = a:curpath . a:typeinfo.sro . tag.name
if !has_key(tag, 'children')
let tag.children = []
endif
let parentscope = a:typeinfo.kind2scope[tag.fields.kind]
call s:AddChildren(a:tags, tag.children, fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
call s:AddScopedTags(a:tags, tag.children, tag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
endfor
call extend(a:processedtags, realtags)
" Recursively add the children of the tags that are children of the
" pseudo-tags on the current level
for tag in pseudotags
call s:ProcessPseudoChildren(a:tags, tag, a:depth, a:typeinfo)
endfor
call extend(a:processedtags, pseudotags)
endif
" Now we have to check if there are any pseudo-tags at the current level
" so we have to check for real tags at a lower level, i.e. grandchildren
let is_grandchild = 'v:val.depth > a:depth'
if !empty(a:curpath)
let is_grandchild .=
\ ' && match(v:val.path, ''\V\^\C'' . a:curpath . a:typeinfo.sro) == 0'
endif
" Grandchildren are children that are not direct ancestors of a tag. This
" can happen when pseudo-tags are in between.
let is_grandchild = 'v:val.depth > a:depth' . is_child
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
call s:AddChildren(a:tags, a:processedtags, a:curpath,
\ a:pscope, a:depth + 1, a:typeinfo)
call s:AddScopedTags(a:tags, a:processedtags, a:curpath,
\ a:pscope, a:depth + 1, a:typeinfo)
endif
endfunction
" s:ProcessPseudoTag() {{{1
function! s:ProcessPseudoTag(tags, processedtags, child, curpath,
\ pscope, typeinfo)
" First check if the pseudo-tag is child of an existing tag.
let parentname = substitute(a:child.path, a:curpath, '', '')
let parentname = substitute(parentname, '\V\^' . a:typeinfo.sro, '', '')
let curpathlist = split(a:curpath, '\V' . a:typeinfo.sro)
let childpathlist = split(a:child.path, '\V' . a:typeinfo.sro)
function! s:ProcessPseudoTag(curtags, tag, curpath, pscope, typeinfo)
let pseudoname = substitute(a:tag.path, a:curpath, '', '')
let pseudoname = substitute(pseudoname, '\V\^' . a:typeinfo.sro, '', '')
let pseudotag = s:CreatePseudoTag(pseudoname, a:curpath, a:pscope,
\ a:tag.scope, a:typeinfo)
let pseudotag.children = [a:tag]
let pseudoparentlist = []
for i in range(len(childpathlist) - 2, len(curpathlist), -1)
let pseudoparentpath = childpathlist[:i]
for scope in a:typeinfo.scopes
let pseudoparentlist = s:ExtractParentList(a:tags, a:processedtags,
\ join(pseudoparentpath, a:typeinfo.sro),
\ scope, a:typeinfo)
if !empty(pseudoparentlist)
break
endif
endfor
if !empty(pseudoparentlist)
break
endif
endfor
if !empty(pseudoparentlist)
" The pseudo-tag is child of an existing (real) tag -- so we have to
" add the real tag to the list of processed tags, create a pseudo-tag,
" add the pseudo-tag to the children of the real tag and add the
" /current/ tag ('child') to the children of the pseudo-tag. Yuck.
let pseudoparent = pseudoparentlist[0]
let parentname = substitute(parentname, pseudoparent.name, '', '')
let parentname = substitute(parentname, '\V\^' . a:typeinfo.sro,
\ '', '')
if has_key(pseudoparent, 'children')
let is_existingparent = 'v:val.name ==# parentname &&
\ v:val.fields.kind ==# a:typeinfo.scope2kind[a:child.scope]'
let existingparent = filter(copy(pseudoparent.children),
\ is_existingparent)
if !empty(existingparent)
call filter(pseudoparent.children,
\ '!(' . is_existingparent . ')')
let parent = existingparent[0]
call add(parent.children, a:child)
else
let parent = s:CreatePseudoTag(parentname, a:curpath, a:pscope,
\ a:child.scope, a:typeinfo)
let parent.children = [a:child]
endif
call add(pseudoparent.children, parent)
else
let parent = s:CreatePseudoTag(parentname, a:curpath, a:pscope,
\ a:child.scope, a:typeinfo)
let parent.children = [a:child]
let pseudoparent.children = [parent]
endif
call add(a:processedtags, pseudoparent)
else
let parent = s:CreatePseudoTag(parentname, a:curpath, a:pscope,
\ a:child.scope, a:typeinfo)
let parent.children = [a:child]
call add(a:processedtags, parent)
" get all the other (direct) children of the current pseudo-tag
let ispseudochild = 'v:val.path == a:tag.path && v:val.scope == a:tag.scope'
let pseudochildren = filter(copy(a:curtags), ispseudochild)
if !empty(pseudochildren)
call filter(a:curtags, '!(' . ispseudochild . ')')
call extend(pseudotag.children, pseudochildren)
endif
return pseudotag
endfunction
" s:ExtractParentList() {{{1
function! s:ExtractParentList(tags, processedtags, path, scope, typeinfo)
let is_parent = 'has_key(a:typeinfo.kind2scope, v:val.fields.kind) &&
\ a:typeinfo.kind2scope[v:val.fields.kind] ==# a:scope &&
\ v:val.fullpath ==# a:path'
let parentlist = filter(copy(a:processedtags), is_parent)
if !empty(parentlist)
call filter(a:processedtags, '!(' . is_parent . ')')
else
let parentlist = filter(copy(a:tags), is_parent)
if !empty(parentlist)
call filter(a:tags, '!(' . is_parent . ')')
" s:ProcessPseudoChildren() {{{1
function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo)
for childtag in a:tag.children
if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
continue
endif
endif
return parentlist
if !has_key(childtag, 'children')
let childtag.children = []
endif
let parentscope = a:typeinfo.kind2scope[childtag.fields.kind]
call s:AddScopedTags(a:tags, childtag.children, childtag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
endfor
let is_grandchild = 'v:val.depth > a:depth &&
\ match(v:val.path, ''^\C'' . a:tag.fullpath) == 0'
let grandchildren = filter(copy(a:tags), is_grandchild)
if !empty(grandchildren)
let parentscope = a:typeinfo.kind2scope[a:tag.fields.kind]
call s:AddScopedTags(a:tags, a:tag.children, a:tag.fullpath,
\ parentscope, a:depth + 1, a:typeinfo)
endif
endfunction
" s:CreatePseudoTag() {{{1
@ -1309,9 +1279,6 @@ function! s:CreatePseudoTag(name, curpath, pscope, scope, typeinfo)
endif
let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
let index = strridx(parentscope, a:typeinfo.sro)
let pseudotag.parentpath = strpart(parentscope, 0, index)
return pseudotag
endfunction