Closed hholst80 closed 3 years ago
So there are three questions here. First is an easier one to handle the C++ output, next we need ctags to output the correct output for the markdown language, and third more to your specific question is the way the tags are displayed. I'll do my best to answer each as best I can.
For the cpp file, the ctags command you are referencing is a little different than what tagbar executes. The command as tagbar executes it is as follows, and results in the following output of the test.cpp file you provided:
ctags --extras=+F -f - --format=2 --excmd=pattern --fields=nksSafet --sort=no --append=no -V --language-force=c++ --c++-kinds=hdpgetncsufmv test.cpp
Option: --language-force=c++
Option: --c++-kinds=hdpgetncsufmv
Initialize parser: C++
Initialize parser: QtMoc
Reading command line arguments
OPENING test2.cpp as C++ language file [new]
Initialize parser: CPreProcessor
Foo test2.cpp /^class Foo$/;" c line:1 file: end:5
foo_method test2.cpp /^ void foo_method();$/;" p line:4 class:Foo typeref:typename:void file: access:public signature:() end:4
Bar test2.cpp /^class Bar$/;" c line:7 file: end:11
bar_method test2.cpp /^ void bar_method();$/;" p line:10 class:Bar typeref:typename:void file: access:public signature:() end:10
Specifically missing is the --extras=+F
flag to include file scope for tags, and the --c++-kinds
definition telling it what all to find.
But it looks like in your case, we have two problems. Not only is the tagbar definition for markdown inaccurate (discussed below), but it also looks like your version of universal ctags is a little older (compiled 2 years ago). I'm not an expert on universal ctags, but I'm taking a guess that the markdown language has added additional support since then. As such, the scope information is not visible in your output, where in my test it was visible using the same file and command.
ctags --extras=+F -f - --format=2 --excmd=pattern --fields=nksSafet --sort=no --append=no -V test.md
Reading command line arguments
Get file language for test.md
pattern: test.md
#candidates: 1
0: Markdown (extension: "md")
#candidates after sorting and filtering: 1
0: Markdown (extension: "md")
OPENING test.md as Markdown language file [new]
Initialize parser: Markdown
... // output truncated
My chapter test.md /^# My chapter$/;" c line:1 end:7
My section test.md /^## My section$/;" s line:2 chapter:My chapter end:7
My sub section test.md /^### My sub section$/;" S line:3 section:My chapter""My section end:7
The last chapter test.md /^# The last chapter$/;" c line:7 end:9
The last section test.md /^## The last section$/;" s line:8 chapter:The last chapter end:9
The last sub section test.md /^### The last sub section$/;" S line:9 section:The last chapter""The last section end:9
If you notice in the output after the line numbers, there is additional information regarding the scope of the tag and it's corresponding parent sections. So compiling a new version of universal ctags might get this part fixed for you.
Now as for your more pertinent question about the markdown, I believe this is due to a lack of a correct markdown
definition in tagbar for the different tag kinds and scope information. By default tagbar is always going to group similar tag kinds together, so in the case of markdown, all chapters
will be grouped together, all sections
will be grouped together, and so on. However the definition for tagbar's markdown language support simply calls each section h1
, h2
, and so forth for the different heading levels. Also there is an option where tagbar can provide scope information though as well to ensure that tag kinds that are scoped can show the proper hierarchy, but for the markdown language, that is currently not implemented in tagbar.
To fix this, there are two ways we can do it. First option is a local definition in your .vimrc
to override the default markdown language definition for tagbar. That can be done by adding the following in your .vimrc
. This will rename the tag kinds to be more accurate for what ctags outputs, and also provide the scope2kind
and kind2scope
definitions needed to get the proper scope hierarchy.
let g:tagbar_type_markdown = {
\ 'ctagstype' : 'markdown',
\ 'kinds' : [
\ 'c:chapter:0:1',
\ 's:section:0:1',
\ 'S:subsection:0:1',
\ 't:subsubsection:0:1',
\ 'T:l4subsection:0:1',
\ 'u:l5subsection:0:1',
\ ],
\ 'sro' : '""',
\ 'kind2scope' : {
\ 'c' : 'chapter',
\ 's' : 'section',
\ 'S' : 'subsection',
\ 't' : 'subsubsection',
\ 'T' : 'l4subsection',
\ },
\ 'scope2kind' : {
\ 'chapter' : 'c',
\ 'section' : 's',
\ 'subsection' : 'S',
\ 'subsubsection' : 't',
\ 'l4subsection' : 'T',
\ },
\ }
The second option would be to provide a fix for this for all users in the uctags.vim
file in tagbar. This could be derived from the above output to correct the tagbar definition for the markdown language. I will defer to someone who works with markdown more specifically to implement this as I do not write anything in markdown and so I'm not in the best position to test or validate.
With either of the solutions, you should see output like this:
" Press <F1>, ? for help
▼ My chapter : #
▼ My section : ##
My sub section : ###
▼ The last chapter : #
▼ The last section : ##
The last sub section : ###
Hope this helps.
First of all: Thank you so much for such a detailed answer! I hope people will find this solution useful because I could honestly not figure this one out even after spending many hours on it.
You were spot on because after I upgraded my ctags and included the custom markdown config in my vimrc, I got this beautiful result.
" Press <F1>, ? for help
▼ My chapter : chapter
▼ My section : section
My sub section : subsection
▼ The last chapter : chapter
▼ The last section : section
The last sub section : subsection
As for compiling ctags, I did take the lazy route and included a deb from Ubuntu 20.10, which installed just fine on my Ubuntu 20.04 LTS base install.
The second option would be to provide a fix for this for all users in the
uctags.vim
file in tagbar. This could be derived from the above output to correct the tagbar definition for the markdown language. I will defer to someone who works with markdown more specifically to implement this as I do not write anything in markdown and so I'm not in the best position to test or validate.
I can run with that change for some time and see if I run into any problems. I hope that ends up being a PR.
diff --git a/autoload/tagbar/types/uctags.vim b/autoload/tagbar/types/uctags.vim
index cc60d79..6f9d27c 100644
--- a/autoload/tagbar/types/uctags.vim
+++ b/autoload/tagbar/types/uctags.vim
@@ -656,13 +656,31 @@ function! tagbar#types#uctags#init(supported_types) abort
let type_markdown = tagbar#prototypes#typeinfo#new()
let type_markdown.ctagstype = 'markdown'
let type_markdown.kinds = [
- \ {'short' : 'c', 'long' : 'h1', 'fold' : 0, 'stl' : 0},
- \ {'short' : 's', 'long' : 'h2', 'fold' : 0, 'stl' : 0},
- \ {'short' : 'S', 'long' : 'h3', 'fold' : 0, 'stl' : 0},
- \ {'short' : 't', 'long' : 'h4', 'fold' : 0, 'stl' : 0},
- \ {'short' : 'T', 'long' : 'h5', 'fold' : 0, 'stl' : 0},
- \ {'short' : 'u', 'long' : 'h6', 'fold' : 0, 'stl' : 0},
+ \ {'short' : 'c', 'long' : 'chapter', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 's', 'long' : 'section', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'S', 'long' : 'subsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 't', 'long' : 'subsubsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'T', 'long' : 'l3subsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'u', 'long' : 'l4subsection', 'fold' : 0, 'stl' : 1},
\ ]
+ let type_markdown.kind2scope = {
+ \ 'c' : 'chapter',
+ \ 's' : 'section',
+ \ 'S' : 'subsection',
+ \ 't' : 'subsubsection',
+ \ 'T' : 'l3subsection',
+ \ 'u' : 'l4subsection',
+ \ }
+ let type_markdown.scope2kind = {
+ \ 'chapter' : 'c',
+ \ 'section' : 's',
+ \ 'subsection' : 'S',
+ \ 'subsubsection' : 't',
+ \ 'l3subsection' : 'T',
+ \ 'l4subsection' : 'u',
+ \ }
+ let type_markdown.sro = '""'
+ let type_markdown.sort = 0
let types.markdown = type_markdown
" Matlab {{{1
let type_matlab = tagbar#prototypes#typeinfo#new()
@@ -861,6 +879,36 @@ function! tagbar#types#uctags#init(supported_types) abort
\ {'short' : 'v', 'long' : 'function variables', 'fold' : 0, 'stl' : 0},
\ ]
let types.r = type_r
+ " ReStructuredText {{{1
+ let type_restructuredtext = tagbar#prototypes#typeinfo#new()
+ let type_restructuredtext.ctagstype = 'restructuredtext'
+ let type_restructuredtext.kinds = [
+ \ {'short' : 'c', 'long' : 'chapter', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 's', 'long' : 'section', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'S', 'long' : 'subsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 't', 'long' : 'subsubsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'T', 'long' : 'l3subsection', 'fold' : 0, 'stl' : 1},
+ \ {'short' : 'u', 'long' : 'l4subsection', 'fold' : 0, 'stl' : 1},
+ \ ]
+ let type_restructuredtext.kind2scope = {
+ \ 'c' : 'chapter',
+ \ 's' : 'section',
+ \ 'S' : 'subsection',
+ \ 't' : 'subsubsection',
+ \ 'T' : 'l3subsection',
+ \ 'u' : 'l4subsection',
+ \ }
+ let type_restructuredtext.scope2kind = {
+ \ 'chapter' : 'c',
+ \ 'section' : 's',
+ \ 'subsection' : 'S',
+ \ 'subsubsection' : 't',
+ \ 'l3subsection' : 'T',
+ \ 'l4subsection' : 'u',
+ \ }
+ let type_restructuredtext.sro = '""'
+ let type_restructuredtext.sort = 0
+ let types.rst = type_restructuredtext
" REXX {{{1
let type_rexx = tagbar#prototypes#typeinfo#new()
let type_rexx.ctagstype = 'rexx'
I would potentially change the header names back to h1...h6 to align with the original naming in https://daringfireball.net/projects/markdown/syntax.text, but for some reason that would not work with the nesting. Needs some more work.
I'd be happy to see this PR'ed too when it works right.
Personally I don't like either set of names shown, but the hN series is least-bad. Using LaTeX-ish names like 'chapter' are confusing to non LaTeX folks and might not even be accurately mapped (90% of my work uses L-2 headings for 'chapter', L-1 headings are parts). The CommonMark spec refrains from calling them anything other than "level X headings". Unfortunately the closest shorthand to that is the h1, h2, hN abbreviations. I don't like the fact that they are 1-to-1 matches for HTML tags which may or may not have the same meaning, but I guess that's better than calling things sections that might be chapters.
From my testing, I think the names of chapter
/ section
/ subsection
and so forth is what ctags returns as the kind of tag. It also looks like matching this is required for tagbar to build the hierarchy properly. When I first attempted it keeping with the h1
/ h2
/ etc, the hierarchy wasn't built correctly. I believe tagbar is actually keying off the output looking for the corresponding kind indicator. So I think this naming will probably be required.
@hholst80 Please do test with it for a bit and feel free to submit a PR for this.
Blag. In that case my beef is with ctags
, not with tagbar
. Okay then so be it.
How can I disable the group-by operation applied by Tagbar?
Input document:
Test.md
Config:
The expected result should be something similar to what ctags emit (explicitly unsorted)
I leave it open that my ctags version is completely incompatible with Tagbar because the following C++ file is interpreted correctly by Tagbar but the suggested ctags command emits nothing.