Scoped kinds (#696)

* Add support for scope
Closes #508, #516

Add --fields=e (end) to the ctags field types to look for the end of the scope.
Update the `s:GetNearbyTag()` routine to use this scope to look for the correct tag.

* Update comment to call out exuberant ctags not supporting the -e option

* Update autoload/tagbar.vim

Co-authored-by: Caleb Maclennan <caleb@alerque.com>

* Optimize nearesttag search, add option for scoped-stl search method

Co-authored-by: Caleb Maclennan <caleb@alerque.com>
This commit is contained in:
raven42 2020-11-02 15:15:55 -06:00 committed by GitHub
parent 601b5c0073
commit 55b8ffa85c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 53 deletions

View File

@ -1342,7 +1342,7 @@ function! s:ExecuteCtagsOnFile(fname, realfname, typeinfo) abort
\ '-',
\ '--format=2',
\ '--excmd=pattern',
\ '--fields=nksSaft',
\ '--fields=nksSafet',
\ '--sort=no',
\ '--append=no'
\ ]
@ -1463,7 +1463,7 @@ function! s:ParseTagline(part1, part2, typeinfo, fileinfo) abort
let fielddict[key] = 'yes'
endif
if len(val) > 0
if key ==# 'line' || key ==# 'column'
if key ==# 'line' || key ==# 'column' || key ==# 'end'
let fielddict[key] = str2nr(val)
else
let fielddict[key] = val
@ -1525,6 +1525,22 @@ function! s:ProcessTag(name, filename, pattern, fields, is_split, typeinfo, file
let taginfo.fields.line = 0
endif
" Make sure our 'end' is valid
if taginfo.fields.end < taginfo.fields.line
if a:typeinfo.getKind(taginfo.fields.kind).stl
" the config indicates this is a scoped kind due to 'stl', but we
" don't have scope vars, assume scope goes to end of file. This
" can also be the case for exhuberant ctags which doesn't support
" the --fields=e option.
" When we call the GetNearbyTag(), it will look up for the nearest
" tag, so if we have multiples that have scope to the end of the
" file it will still only grab the first one above the current line
let taginfo.fields.end = line('$')
else
let taginfo.fields.end = taginfo.fields.line
endif
endif
if !has_key(taginfo.fields, 'kind')
call tagbar#debug#log(
\ "Warning: No 'kind' field found for tag " . a:name[0] . '!')
@ -2153,9 +2169,9 @@ function! s:HighlightTag(openfolds, ...) abort
let force = a:0 > 0 ? a:1 : 0
if a:0 > 1
let tag = s:GetNearbyTag('highlight', 0, a:2)
let tag = s:GetNearbyTag('nearest-stl', 0, a:2)
else
let tag = s:GetNearbyTag('highlight', 0)
let tag = s:GetNearbyTag('nearest-stl', 0)
endif
if !empty(tag)
let tagline = tag.tline
@ -2615,7 +2631,7 @@ function! s:OpenParents(...) abort
if a:0 == 1
let tag = a:1
else
let tag = s:GetNearbyTag('parent', 0)
let tag = s:GetNearbyTag('nearest', 0)
endif
if !empty(tag)
@ -3058,19 +3074,17 @@ function! s:GetNearbyTag(request, forcecurrent, ...) abort
for line in range(curline, 1, -1)
if has_key(fileinfo.fline, line)
let curtag = fileinfo.fline[line]
if a:request ==# 'highlight' && typeinfo.getKind(curtag.fields.kind).stl
if a:request ==# 'nearest-stl'
\ && typeinfo.getKind(curtag.fields.kind).stl || line == curline
let tag = curtag
break
endif
if a:request ==# 'highlight' && line == curline
elseif a:request ==# 'scoped-stl'
\ && typeinfo.getKind(curtag.fields.kind).stl
\ && curtag.fields.line <= curline
\ && curline <= curtag.fields.end
let tag = curtag
break
endif
if a:request ==# 'statusline' && typeinfo.getKind(curtag.fields.kind).stl
let tag = curtag
break
endif
if a:request ==# 'parent'
elseif a:request ==# 'nearest'
let tag = curtag
break
endif
@ -3586,31 +3600,6 @@ endfunction
" Autoload functions {{{1
" Wrappers {{{2
function! tagbar#GetTagNearLine(lnum, ...) abort
if a:0 > 0
let fmt = a:1
let longsig = a:2 =~# 's'
let fullpath = a:2 =~# 'f'
let prototype = a:2 =~# 'p'
else
let fmt = '%s'
let longsig = 0
let fullpath = 0
let prototype = 0
endif
let taginfo = s:GetNearbyTag('statusline', 1, a:lnum)
if empty(taginfo)
return ''
endif
if prototype
return taginfo.getPrototype(1)
else
return printf(fmt, taginfo.str(longsig, fullpath))
endif
endfunction
function! tagbar#ToggleWindow(...) abort
let flags = a:0 > 0 ? a:1 : ''
@ -3719,28 +3708,67 @@ function! tagbar#autoopen(...) abort
call tagbar#debug#log('tagbar#autoopen finished without finding valid file')
endfunction
" tagbar#GetTagNearLine() {{{2
function! tagbar#GetTagNearLine(lnum, ...) abort
if a:0 >= 2
let fmt = a:1
let longsig = a:2 =~# 's'
let fullpath = a:2 =~# 'f'
let prototype = a:2 =~# 'p'
if a:0 >= 3
let search_method = a:3
else
let search_method = 'nearest-stl'
endif
else
let fmt = '%s'
let longsig = 0
let fullpath = 0
let prototype = 0
let search_method = 'nearest-stl'
endif
let taginfo = s:GetNearbyTag(search_method, 1, a:lnum)
if empty(taginfo)
return ''
endif
if prototype
return taginfo.getPrototype(1)
else
return printf(fmt, taginfo.str(longsig, fullpath))
endif
endfunction
" tagbar#currenttag() {{{2
function! tagbar#currenttag(fmt, default, ...) abort
" Indicate that the statusline functionality is being used. This prevents
" the CloseWindow() function from removing the autocommands.
let s:statusline_in_use = 1
if a:0 > 0
if a:0 >= 1
" also test for non-zero value for backwards compatibility
let longsig = a:1 =~# 's' || (type(a:1) == type(0) && a:1 != 0)
let fullpath = a:1 =~# 'f'
let prototype = a:1 =~# 'p'
if a:0 >= 2
let search_method = a:2
else
let search_method = 'nearest-stl'
endif
else
let longsig = 0
let fullpath = 0
let prototype = 0
let search_method = 'nearest-stl'
endif
if !s:Init(1)
return a:default
endif
let tag = s:GetNearbyTag('statusline', 1)
let tag = s:GetNearbyTag(search_method, 1)
if !empty(tag)
if prototype
@ -3812,7 +3840,7 @@ function! tagbar#currenttagtype(fmt, default) abort
" the CloseWindow() function from removing the autocommands.
let s:statusline_in_use = 1
let kind = ''
let tag = s:GetNearbyTag('statusline', 1)
let tag = s:GetNearbyTag('scoped-stl', 1)
if empty(tag)
return a:default

View File

@ -14,6 +14,7 @@ function! tagbar#prototypes#basetag#new(name) abort
let newobj.fields = {}
let newobj.fields.line = 0
let newobj.fields.column = 0
let newobj.fields.end = 0
let newobj.prototype = ''
let newobj.data_type = ''
let newobj.path = ''

View File

@ -334,14 +334,17 @@ FUNCTIONS *tagbar-functions*
This could be used in a custom foldtext function to show the current tag
the fold current fold is located in.
Example:
>
set foldtext=MyFoldFunc()
function! MyFoldFunc()
let tag = tagbar#GetTagNearLine(v:foldend, '%s', 'p')
let lines = v:foldend - v:foldstart + 1
return tag . ' --- ' . lines . ' lines'
endfunction
This function can also take in a search method similar to the
|tagbar#currenttag()| function. Full syntax is as follows:
tagbar#GetTagNearLine(lnum [, {fmt}, {flags} [, {search-method}]])
Example: >
set foldtext=MyFoldFunc()
function! MyFoldFunc()
let tag = tagbar#GetTagNearLine(v:foldend, '%s', 'p')
let lines = v:foldend - v:foldstart + 1
return tag . ' --- ' . lines . ' lines'
endfunction
<
*tagbar#ForceUpdate()*
Forcefully update a file even if it exceeds the |g:tagbar_file_size_limit|
@ -1182,7 +1185,7 @@ read |tagbar-extend| (especially the "kinds" entry) on how to do that.
The function has the following signature:
tagbar#currenttag({format}, {default} [, {flags}])
tagbar#currenttag({format}, {default} [, {flags} [, {search-method}]])
{format} is a |printf()|-compatible format string where "%s" will be
replaced by the name of the tag. {default} will be displayed instead of
the format string if no tag can be found.
@ -1197,10 +1200,33 @@ tagbar#currenttag({format}, {default} [, {flags}])
useful in cases where ctags doesn't report some information, like
the signature. Note that this can get quite long.
The optional {search-method} argument specified how to search for the
nearest tag. Valid options are:
'nearest' This will look for the closest tag above the current line
regardless of type. This will match even one line tags or
other tags not defined with the {stl} flag in their kind
definition. This is the quickest option, but least
accurate.
'nearest-stl' This will look for the closest tag above the current line
which is defined with the {stl} flag in its kind
definition. This is a little slower, but provides a little
more context and accuracy.
'scoped-stl' This will look for the closest tag above the current line
taking scope into account as well as the {stl} flag. The
scope is determined by the ctags 'end' field. This is the
slowest of the options as when outside of a function
scope, it could end up searching all the way to the top of
the file for the nearest scoped tag (or possibly none if
not in any scope at all).
For example, if you put the following into your statusline: >
%{tagbar#currenttag('[%s] ','')}
< then the function "myfunc" will be shown as "[myfunc()] ".
< then the function "myfunc" will be shown as "[myfunc()] ".
As another example, we can use the following in our status line to find
the current scoped tag and also print the full path when found: >
%{tagbar#currenttag('%s', '', 'f', 'scoped-stl')}
< then the function "myfunc" within class "myclass" will be shown as
"myclass::myfunc()". But when outside of the function, it will be shown as
"myclass"
Additionally you can show the kind (type) of the current tag, using following
function: