mirror of
https://github.com/preservim/tagbar.git
synced 2024-11-23 08:07:18 +08:00
Add name-indexed dicts of tags alongside lists
When constructing the tag tree it is often necessary to look up tags with a certain name and other attributes at the current depth. This was previously done with the filter() function on the list of all the relevant tags. However, this filtering is very slow. This commit changes the FileInfo and TagInfo objects to save added tags in a dictionary indexed by the name of the tag in addition to the list (which is still maintained in parallel since it is necessary for things like sorting). This significatly speeds up tag lookup since most tags have unique names.
This commit is contained in:
parent
9ca422ce09
commit
72a7ef378f
|
@ -1318,10 +1318,11 @@ function! s:BaseTag._init(name) abort dict
|
|||
let self.fullpath = a:name
|
||||
let self.depth = 0
|
||||
let self.parent = {}
|
||||
let self.children = []
|
||||
let self.tline = -1
|
||||
let self.fileinfo = {}
|
||||
let self.typeinfo = {}
|
||||
let self._childlist = []
|
||||
let self._childdict = {}
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.isNormalTag() {{{3
|
||||
|
@ -1348,7 +1349,7 @@ endfunction
|
|||
function! s:BaseTag._getPrefix() abort dict
|
||||
let fileinfo = self.fileinfo
|
||||
|
||||
if !empty(self.children)
|
||||
if !empty(self._childlist)
|
||||
if fileinfo.tagfolds[self.fields.kind][self.fullpath]
|
||||
let prefix = s:icon_closed
|
||||
else
|
||||
|
@ -1413,7 +1414,7 @@ endfunction
|
|||
|
||||
" s:BaseTag.isFoldable() {{{3
|
||||
function! s:BaseTag.isFoldable() abort dict
|
||||
return !empty(self.children)
|
||||
return !empty(self._childlist)
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.isFolded() {{{3
|
||||
|
@ -1465,6 +1466,41 @@ function! s:BaseTag.openParents() abort dict
|
|||
endwhile
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.addChild() {{{3
|
||||
function! s:BaseTag.addChild(tag) abort dict
|
||||
call add(self._childlist, a:tag)
|
||||
|
||||
if has_key(self._childdict, a:tag.name)
|
||||
call add(self._childdict[a:tag.name], a:tag)
|
||||
else
|
||||
let self._childdict[a:tag.name] = [a:tag]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.getChildren() {{{3
|
||||
function! s:BaseTag.getChildren() dict abort
|
||||
return self._childlist
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.getChildrenByName() {{{3
|
||||
function! s:BaseTag.getChildrenByName(tagname) dict abort
|
||||
return get(self._childdict, a:tagname, [])
|
||||
endfunction
|
||||
|
||||
" s:BaseTag.removeChild() {{{3
|
||||
function! s:BaseTag.removeChild(tag) dict abort
|
||||
let idx = index(self._childlist, a:tag)
|
||||
if idx >= 0
|
||||
call remove(self._childlist, idx)
|
||||
endif
|
||||
|
||||
let namelist = get(self._childdict, a:tag.name, [])
|
||||
let idx = index(namelist, a:tag)
|
||||
if idx >= 0
|
||||
call remove(namelist, idx)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Normal tag {{{2
|
||||
let s:NormalTag = copy(s:BaseTag)
|
||||
|
||||
|
@ -1681,7 +1717,8 @@ function! s:FileInfo.New(fname, ftype, typeinfo) abort dict
|
|||
|
||||
" List of the tags that are present in the file, sorted according to the
|
||||
" value of 'g:tagbar_sort'
|
||||
let newobj.tags = []
|
||||
let newobj._taglist = []
|
||||
let newobj._tagdict = {}
|
||||
|
||||
" Dictionary of the tags, indexed by line number in the file
|
||||
let newobj.fline = {}
|
||||
|
@ -1711,12 +1748,48 @@ function! s:FileInfo.New(fname, ftype, typeinfo) abort dict
|
|||
return newobj
|
||||
endfunction
|
||||
|
||||
" s:FileInfo.addTag() {{{3
|
||||
function! s:FileInfo.addTag(tag) abort dict
|
||||
call add(self._taglist, a:tag)
|
||||
|
||||
if has_key(self._tagdict, a:tag.name)
|
||||
call add(self._tagdict[a:tag.name], a:tag)
|
||||
else
|
||||
let self._tagdict[a:tag.name] = [a:tag]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" s:FileInfo.getTags() {{{3
|
||||
function! s:FileInfo.getTags() dict abort
|
||||
return self._taglist
|
||||
endfunction
|
||||
|
||||
" s:FileInfo.getTagsByName() {{{3
|
||||
function! s:FileInfo.getTagsByName(tagname) dict abort
|
||||
return get(self._tagdict, a:tagname, [])
|
||||
endfunction
|
||||
|
||||
" s:FileInfo.removeTag() {{{3
|
||||
function! s:FileInfo.removeTag(tag) dict abort
|
||||
let idx = index(self._taglist, a:tag)
|
||||
if idx >= 0
|
||||
call remove(self._taglist, idx)
|
||||
endif
|
||||
|
||||
let namelist = get(self._tagdict, a:tag.name, [])
|
||||
let idx = index(namelist, a:tag)
|
||||
if idx >= 0
|
||||
call remove(namelist, idx)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" s:FileInfo.reset() {{{3
|
||||
" Reset stuff that gets regenerated while processing a file and save the old
|
||||
" tag folds
|
||||
function! s:FileInfo.reset() abort dict
|
||||
let self.mtime = getftime(self.fpath)
|
||||
let self.tags = []
|
||||
let self._taglist = []
|
||||
let self._tagdict = {}
|
||||
let self.fline = {}
|
||||
let self.tline = {}
|
||||
|
||||
|
@ -1738,9 +1811,9 @@ endfunction
|
|||
" s:FileInfo.sortTags() {{{3
|
||||
function! s:FileInfo.sortTags() abort dict
|
||||
if get(s:compare_typeinfo, 'sort', g:tagbar_sort)
|
||||
call s:SortTags(self.tags, 's:CompareByKind')
|
||||
call s:SortTags(self._taglist, 's:CompareByKind')
|
||||
else
|
||||
call s:SortTags(self.tags, 's:CompareByLine')
|
||||
call s:SortTags(self._taglist, 's:CompareByLine')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
@ -2243,7 +2316,7 @@ function! s:ProcessFile(fname, ftype) abort
|
|||
continue
|
||||
endif
|
||||
|
||||
let curtags = filter(copy(fileinfo.tags),
|
||||
let curtags = filter(copy(fileinfo.getTags()),
|
||||
\ 'v:val.fields.kind ==# kind.short && ' .
|
||||
\ '!has_key(v:val, "scope")')
|
||||
call s:debug('Processing kind: ' . kind.short .
|
||||
|
@ -2441,7 +2514,7 @@ function! s:ParseTagline(part1, part2, typeinfo, fileinfo) abort
|
|||
" If this filetype doesn't have any scope information then we can stop
|
||||
" here after adding the tag to the list
|
||||
if !has_key(a:typeinfo, 'scope2kind')
|
||||
call add(a:fileinfo.tags, taginfo)
|
||||
call a:fileinfo.addTag(taginfo)
|
||||
return
|
||||
endif
|
||||
|
||||
|
@ -2476,39 +2549,56 @@ function! s:ParseTagline(part1, part2, typeinfo, fileinfo) abort
|
|||
return
|
||||
endtry
|
||||
|
||||
call s:add_tag_recursive(a:fileinfo.tags, {}, taginfo, pathlist)
|
||||
call s:add_tag_recursive({}, taginfo, pathlist)
|
||||
endfunction
|
||||
|
||||
" s:add_tag_recursive() {{{2
|
||||
function! s:add_tag_recursive(tags, parent, taginfo, pathlist) abort
|
||||
" Add a tag recursively as a child of its parent, or if there is no parent, to
|
||||
" the root tag list in the fileinfo object.
|
||||
function! s:add_tag_recursive(parent, taginfo, pathlist) abort
|
||||
" If the pathlist is empty we are at the correct scope for the current tag
|
||||
if empty(a:pathlist)
|
||||
if !a:taginfo.isPseudoTag()
|
||||
" If a childtag got processed before a parent tag then there will
|
||||
" be a pseudotag here as a placeholder. Copy the children over and
|
||||
" then replace the pseudotag with the real one.
|
||||
let pseudotags = filter(copy(a:tags),
|
||||
\ 'v:val.name == a:taginfo.name && ' .
|
||||
\ '(v:val.fields.kind ==# "?" || v:val.fields.kind ==# a:taginfo.fields.kind) && ' .
|
||||
\ 'v:val.isPseudoTag()')
|
||||
if len(pseudotags) == 1
|
||||
let pseudotag = pseudotags[0]
|
||||
let a:taginfo.children = pseudotag.children
|
||||
for child in a:taginfo.children
|
||||
let child.parent = a:taginfo
|
||||
endfor
|
||||
call remove(a:tags, index(a:tags, pseudotag))
|
||||
elseif len(pseudotags) > 1
|
||||
echoerr 'Tagbar: Found duplicate pseudotag; this should never happen!'
|
||||
\ 'Please contact the script maintainer with an example.'
|
||||
\ 'Pseudotag name:' pseudotag.name
|
||||
endif
|
||||
" If a child tag got processed before a parent tag then there will
|
||||
" be a pseudotag here as a placeholder. Copy the children over and
|
||||
" then replace the pseudotag with the real one.
|
||||
let pseudotags = []
|
||||
if empty(a:parent)
|
||||
let name_siblings = a:taginfo.fileinfo.getTagsByName(a:taginfo.name)
|
||||
else
|
||||
let name_siblings = a:parent.getChildrenByName(a:taginfo.name)
|
||||
endif
|
||||
|
||||
if !empty(a:parent)
|
||||
for tag in name_siblings
|
||||
if (tag.fields.kind ==# '?'
|
||||
\ || tag.fields.kind ==# a:taginfo.fields.kind)
|
||||
\ && tag.isPseudoTag()
|
||||
call add(pseudotags, tag)
|
||||
endif
|
||||
endfor
|
||||
|
||||
if len(pseudotags) == 1
|
||||
let pseudotag = pseudotags[0]
|
||||
for child in pseudotag.getChildren()
|
||||
call a:taginfo.addChild(child)
|
||||
let child.parent = a:taginfo
|
||||
endfor
|
||||
if empty(a:parent)
|
||||
call a:taginfo.fileinfo.removeTag(pseudotag)
|
||||
else
|
||||
call a:parent.removeChild(pseudotag)
|
||||
endif
|
||||
elseif len(pseudotags) > 1
|
||||
echoerr 'Tagbar: Found duplicate pseudotag; this should never happen!'
|
||||
\ 'Please contact the script maintainer with an example.'
|
||||
\ 'Pseudotag name:' pseudotag.name
|
||||
endif
|
||||
|
||||
if empty(a:parent)
|
||||
call a:taginfo.fileinfo.addTag(a:taginfo)
|
||||
else
|
||||
call a:parent.addChild(a:taginfo)
|
||||
let a:taginfo.parent = a:parent
|
||||
endif
|
||||
call add(a:tags, a:taginfo)
|
||||
return
|
||||
endif
|
||||
|
||||
|
@ -2520,13 +2610,24 @@ function! s:add_tag_recursive(tags, parent, taginfo, pathlist) abort
|
|||
let grandparent = a:parent
|
||||
let parentname = remove(a:pathlist, 0)
|
||||
|
||||
let parentcond = 'v:val.name == "' . parentname . '"'
|
||||
if empty(grandparent)
|
||||
let name_siblings = a:taginfo.fileinfo.getTagsByName(parentname)
|
||||
else
|
||||
let name_siblings = grandparent.getChildrenByName(parentname)
|
||||
endif
|
||||
if empty(a:pathlist)
|
||||
" If the current tag is a direct child of the parent we're looking for
|
||||
" then we can also filter the parents based on the scope information
|
||||
let parentcond .= ' && (v:val.fields.kind ==# "?" || get(a:taginfo.typeinfo.kind2scope, v:val.fields.kind, "") == a:taginfo.scope)'
|
||||
let parents = []
|
||||
for tag in name_siblings
|
||||
if tag.fields.kind ==# '?'
|
||||
\ || get(a:taginfo.typeinfo.kind2scope, tag.fields.kind, "") == a:taginfo.scope
|
||||
call add(parents, tag)
|
||||
endif
|
||||
endfor
|
||||
else
|
||||
let parents = name_siblings
|
||||
endif
|
||||
let parents = filter(copy(a:tags), parentcond)
|
||||
|
||||
if empty(parents)
|
||||
" No parents found, so either the parent is a pseudotag or it hasn't
|
||||
|
@ -2539,7 +2640,11 @@ function! s:add_tag_recursive(tags, parent, taginfo, pathlist) abort
|
|||
endif
|
||||
let parent = s:create_pseudotag(parentname, grandparent,
|
||||
\ pseudokind, a:taginfo.typeinfo, a:taginfo.fileinfo)
|
||||
call add(a:tags, parent)
|
||||
if empty(grandparent)
|
||||
call a:taginfo.fileinfo.addTag(parent)
|
||||
else
|
||||
call grandparent.addChild(parent)
|
||||
endif
|
||||
else
|
||||
" If there are multiple possible parents (c.f. issue #139, or tags
|
||||
" with the same name but a different kind) then we will pick the one
|
||||
|
@ -2569,7 +2674,7 @@ function! s:add_tag_recursive(tags, parent, taginfo, pathlist) abort
|
|||
endif
|
||||
endif
|
||||
|
||||
call s:add_tag_recursive(parent.children, parent, a:taginfo, a:pathlist)
|
||||
call s:add_tag_recursive(parent, a:taginfo, a:pathlist)
|
||||
endfunction
|
||||
|
||||
" s:create_pseudotag() {{{2
|
||||
|
@ -2614,8 +2719,8 @@ function! s:SortTags(tags, comparemethod) abort
|
|||
call sort(a:tags, a:comparemethod)
|
||||
|
||||
for tag in a:tags
|
||||
if !empty(tag.children)
|
||||
call s:SortTags(tag.children, a:comparemethod)
|
||||
if !empty(tag.getChildren())
|
||||
call s:SortTags(tag.getChildren(), a:comparemethod)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
@ -2749,7 +2854,7 @@ function! s:RenderContent(...) abort
|
|||
|
||||
let typeinfo = fileinfo.typeinfo
|
||||
|
||||
if !empty(fileinfo.tags)
|
||||
if !empty(fileinfo.getTags())
|
||||
" Print tags
|
||||
call s:PrintKinds(typeinfo, fileinfo)
|
||||
else
|
||||
|
@ -2811,7 +2916,7 @@ function! s:PrintKinds(typeinfo, fileinfo) abort
|
|||
let output = []
|
||||
|
||||
for kind in a:typeinfo.kinds
|
||||
let curtags = filter(copy(a:fileinfo.tags),
|
||||
let curtags = filter(copy(a:fileinfo.getTags()),
|
||||
\ 'v:val.fields.kind ==# kind.short')
|
||||
call s:debug('Printing kind: ' . kind.short .
|
||||
\ ', number of (top-level) tags: ' . len(curtags))
|
||||
|
@ -2900,7 +3005,7 @@ function! s:PrintTag(tag, depth, output, fileinfo, typeinfo) abort
|
|||
let childfilter .=
|
||||
\ ' && get(v:val.fields, "access", "public") ==# "public"'
|
||||
endif
|
||||
let childtags = filter(copy(a:tag.children), childfilter)
|
||||
let childtags = filter(copy(a:tag.getChildren()), childfilter)
|
||||
if len(childtags) > 0
|
||||
" Print 'kind' header of following children, but only if they
|
||||
" are not scope-defining tags (since those already have an
|
||||
|
@ -3358,7 +3463,7 @@ function! s:SetFoldLevel(level, force) abort
|
|||
return
|
||||
endif
|
||||
|
||||
call s:SetFoldLevelRecursive(fileinfo, fileinfo.tags, a:level)
|
||||
call s:SetFoldLevelRecursive(fileinfo, fileinfo.getTags(), a:level)
|
||||
|
||||
let typeinfo = fileinfo.typeinfo
|
||||
|
||||
|
@ -3390,8 +3495,8 @@ function! s:SetFoldLevelRecursive(fileinfo, tags, level) abort
|
|||
call tag.setFolded(0)
|
||||
endif
|
||||
|
||||
if !empty(tag.children)
|
||||
call s:SetFoldLevelRecursive(a:fileinfo, tag.children, a:level)
|
||||
if !empty(tag.getChildren())
|
||||
call s:SetFoldLevelRecursive(a:fileinfo, tag.getChildren(), a:level)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
@ -3422,7 +3527,7 @@ function! s:GotoNextFold() abort
|
|||
|
||||
if empty(taginfo)
|
||||
continue
|
||||
elseif !empty(taginfo.children) || taginfo.isKindheader()
|
||||
elseif !empty(taginfo.getChildren()) || taginfo.isKindheader()
|
||||
let newlinenr = linenr
|
||||
break
|
||||
endif
|
||||
|
@ -3455,8 +3560,8 @@ function! s:GotoPrevFold() abort
|
|||
" same parent as the current one, or
|
||||
" - a closed parent fold.
|
||||
elseif (!empty(taginfo.parent) && taginfo.parent != curparent &&
|
||||
\ empty(taginfo.children)) ||
|
||||
\ ((!empty(taginfo.children) || taginfo.isKindheader()) &&
|
||||
\ empty(taginfo.getChildren())) ||
|
||||
\ ((!empty(taginfo.getChildren()) || taginfo.isKindheader()) &&
|
||||
\ taginfo.isFolded())
|
||||
let newlinenr = linenr
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue
Block a user