1 " ============================================================================
3 " Description: List the current file's tags in a sidebar, ordered by class etc
4 " Author: Jan Larres <jan@majutsushi.net>
6 " Website: http://majutsushi.github.com/tagbar/
8 " Note: This plugin was heavily inspired by the 'Taglist' plugin by
9 " Yegappan Lakshmanan and uses a small amount of code from it.
11 " Original taglist copyright notice:
12 " Permission is hereby granted to use and distribute this code,
13 " with or without modifications, provided that this copyright
14 " notice is copied with it. Like anything else that's free,
15 " taglist.vim is provided *as is* and comes with no warranty of
16 " any kind, either expressed or implied. In no event will the
17 " copyright holder be liable for any damamges resulting from the
18 " use of this software.
19 " ============================================================================
27 if !exists('g:tagbar_ctags_bin')
28 if executable('ctags-exuberant')
29 let g:tagbar_ctags_bin = 'ctags-exuberant'
30 elseif executable('exuberant-ctags')
31 let g:tagbar_ctags_bin = 'exuberant-ctags'
32 elseif executable('exctags')
33 let g:tagbar_ctags_bin = 'exctags'
34 elseif has('macunix') && executable('/usr/local/bin/ctags')
35 " Homebrew default location
36 let g:tagbar_ctags_bin = '/usr/local/bin/ctags'
37 elseif has('macunix') && executable('/opt/local/bin/ctags')
38 " Macports default location
39 let g:tagbar_ctags_bin = '/opt/local/bin/ctags'
40 elseif executable('ctags')
41 let g:tagbar_ctags_bin = 'ctags'
42 elseif executable('ctags.exe')
43 let g:tagbar_ctags_bin = 'ctags.exe'
44 elseif executable('tags')
45 let g:tagbar_ctags_bin = 'tags'
47 echomsg 'Tagbar: Exuberant ctags not found, skipping plugin'
51 " reset 'wildignore' temporarily in case *.exe is included in it
52 let wildignore_save = &wildignore
55 let g:tagbar_ctags_bin = expand(g:tagbar_ctags_bin)
57 let &wildignore = wildignore_save
59 if !executable(g:tagbar_ctags_bin)
60 echomsg 'Tagbar: Exuberant ctags not found in specified place,'
69 if s:ftype_out !~# 'detection:ON'
70 echomsg 'Tagbar: Filetype detection is turned off, skipping plugin'
76 let s:icon_closed = g:tagbar_iconchars[0]
77 let s:icon_open = g:tagbar_iconchars[1]
79 let s:type_init_done = 0
80 let s:autocommands_done = 0
81 let s:checked_ctags = 0
82 let s:window_expanded = 0
84 let s:access_symbols = {
90 let g:loaded_tagbar = 1
92 let s:last_highlight_tline = 0
103 if !s:CheckForExCtags()
110 function! s:InitTypes()
111 call s:LogDebugMessage('Initializing types')
113 let s:known_types = {}
117 let type_ant.ctagstype = 'ant'
118 let type_ant.kinds = [
119 \ {'short' : 'p', 'long' : 'projects', 'fold' : 0},
120 \ {'short' : 't', 'long' : 'targets', 'fold' : 0}
122 let s:known_types.ant = type_ant
125 let type_asm.ctagstype = 'asm'
126 let type_asm.kinds = [
127 \ {'short' : 'm', 'long' : 'macros', 'fold' : 0},
128 \ {'short' : 't', 'long' : 'types', 'fold' : 0},
129 \ {'short' : 'd', 'long' : 'defines', 'fold' : 0},
130 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0}
132 let s:known_types.asm = type_asm
135 let type_aspvbs.ctagstype = 'asp'
136 let type_aspvbs.kinds = [
137 \ {'short' : 'd', 'long' : 'constants', 'fold' : 0},
138 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
139 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
140 \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0},
141 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
143 let s:known_types.aspvbs = type_aspvbs
146 let type_awk.ctagstype = 'awk'
147 let type_awk.kinds = [
148 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
150 let s:known_types.awk = type_awk
153 let type_basic.ctagstype = 'basic'
154 let type_basic.kinds = [
155 \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
156 \ {'short' : 'g', 'long' : 'enumerations', 'fold' : 0},
157 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
158 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
159 \ {'short' : 't', 'long' : 'types', 'fold' : 0},
160 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
162 let s:known_types.basic = type_basic
165 let type_beta.ctagstype = 'beta'
166 let type_beta.kinds = [
167 \ {'short' : 'f', 'long' : 'fragments', 'fold' : 0},
168 \ {'short' : 's', 'long' : 'slots', 'fold' : 0},
169 \ {'short' : 'v', 'long' : 'patterns', 'fold' : 0}
171 let s:known_types.beta = type_beta
174 let type_c.ctagstype = 'c'
176 \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
177 \ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1},
178 \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
179 \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
180 \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
181 \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
182 \ {'short' : 'u', 'long' : 'unions', 'fold' : 0},
183 \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
184 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
185 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
187 let type_c.sro = '::'
188 let type_c.kind2scope = {
193 let type_c.scope2kind = {
198 let s:known_types.c = type_c
201 let type_cpp.ctagstype = 'c++'
202 let type_cpp.kinds = [
203 \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
204 \ {'short' : 'p', 'long' : 'prototypes', 'fold' : 1},
205 \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
206 \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
207 \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
208 \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
209 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
210 \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
211 \ {'short' : 'u', 'long' : 'unions', 'fold' : 0},
212 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
213 \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
214 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
216 let type_cpp.sro = '::'
217 let type_cpp.kind2scope = {
224 let type_cpp.scope2kind = {
231 let s:known_types.cpp = type_cpp
234 let type_cs.ctagstype = 'c#'
235 let type_cs.kinds = [
236 \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
237 \ {'short' : 'f', 'long' : 'fields', 'fold' : 0},
238 \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
239 \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
240 \ {'short' : 't', 'long' : 'typedefs', 'fold' : 0},
241 \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
242 \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
243 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
244 \ {'short' : 's', 'long' : 'structs', 'fold' : 0},
245 \ {'short' : 'E', 'long' : 'events', 'fold' : 0},
246 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
247 \ {'short' : 'p', 'long' : 'properties', 'fold' : 0}
249 let type_cs.sro = '.'
250 let type_cs.kind2scope = {
257 let type_cs.scope2kind = {
264 let s:known_types.cs = type_cs
267 let type_cobol.ctagstype = 'cobol'
268 let type_cobol.kinds = [
269 \ {'short' : 'd', 'long' : 'data items', 'fold' : 0},
270 \ {'short' : 'f', 'long' : 'file descriptions', 'fold' : 0},
271 \ {'short' : 'g', 'long' : 'group items', 'fold' : 0},
272 \ {'short' : 'p', 'long' : 'paragraphs', 'fold' : 0},
273 \ {'short' : 'P', 'long' : 'program ids', 'fold' : 0},
274 \ {'short' : 's', 'long' : 'sections', 'fold' : 0}
276 let s:known_types.cobol = type_cobol
278 let type_dosbatch = {}
279 let type_dosbatch.ctagstype = 'dosbatch'
280 let type_dosbatch.kinds = [
281 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
282 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
284 let s:known_types.dosbatch = type_dosbatch
287 let type_eiffel.ctagstype = 'eiffel'
288 let type_eiffel.kinds = [
289 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
290 \ {'short' : 'f', 'long' : 'features', 'fold' : 0}
292 let type_eiffel.sro = '.' " Not sure, is nesting even possible?
293 let type_eiffel.kind2scope = {
297 let type_eiffel.scope2kind = {
301 let s:known_types.eiffel = type_eiffel
304 let type_erlang.ctagstype = 'erlang'
305 let type_erlang.kinds = [
306 \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
307 \ {'short' : 'd', 'long' : 'macro definitions', 'fold' : 0},
308 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
309 \ {'short' : 'r', 'long' : 'record definitions', 'fold' : 0}
311 let type_erlang.sro = '.' " Not sure, is nesting even possible?
312 let type_erlang.kind2scope = {
315 let type_erlang.scope2kind = {
318 let s:known_types.erlang = type_erlang
320 " Vim doesn't support Flex out of the box, this is based on rough
321 " guesses and probably requires
322 " http://www.vim.org/scripts/script.php?script_id=2909
323 " Improvements welcome!
325 let type_mxml.ctagstype = 'flex'
326 let type_mxml.kinds = [
327 \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
328 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
329 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
330 \ {'short' : 'p', 'long' : 'properties', 'fold' : 0},
331 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
332 \ {'short' : 'x', 'long' : 'mxtags', 'fold' : 0}
334 let type_mxml.sro = '.'
335 let type_mxml.kind2scope = {
338 let type_mxml.scope2kind = {
341 let s:known_types.mxml = type_mxml
343 let type_fortran = {}
344 let type_fortran.ctagstype = 'fortran'
345 let type_fortran.kinds = [
346 \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
347 \ {'short' : 'p', 'long' : 'programs', 'fold' : 0},
348 \ {'short' : 'k', 'long' : 'components', 'fold' : 0},
349 \ {'short' : 't', 'long' : 'derived types and structures', 'fold' : 0},
350 \ {'short' : 'c', 'long' : 'common blocks', 'fold' : 0},
351 \ {'short' : 'b', 'long' : 'block data', 'fold' : 0},
352 \ {'short' : 'e', 'long' : 'entry points', 'fold' : 0},
353 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
354 \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0},
355 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
356 \ {'short' : 'n', 'long' : 'namelists', 'fold' : 0},
357 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
359 let type_fortran.sro = '.' " Not sure, is nesting even possible?
360 let type_fortran.kind2scope = {
366 let type_fortran.scope2kind = {
372 let s:known_types.fortran = type_fortran
375 let type_html.ctagstype = 'html'
376 let type_html.kinds = [
377 \ {'short' : 'f', 'long' : 'JavaScript funtions', 'fold' : 0},
378 \ {'short' : 'a', 'long' : 'named anchors', 'fold' : 0}
380 let s:known_types.html = type_html
383 let type_java.ctagstype = 'java'
384 let type_java.kinds = [
385 \ {'short' : 'p', 'long' : 'packages', 'fold' : 1},
386 \ {'short' : 'f', 'long' : 'fields', 'fold' : 0},
387 \ {'short' : 'g', 'long' : 'enum types', 'fold' : 0},
388 \ {'short' : 'e', 'long' : 'enum constants', 'fold' : 0},
389 \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
390 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
391 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0}
393 let type_java.sro = '.'
394 let type_java.kind2scope = {
399 let type_java.scope2kind = {
404 let s:known_types.java = type_java
406 " JavaScript is weird -- it does have scopes, but ctags doesn't seem to
407 " properly generate the information for them, instead it simply uses the
408 " complete name. So ctags has to be fixed before I can do anything here.
409 " Alternatively jsctags/doctorjs will be used if available.
410 let type_javascript = {}
411 let type_javascript.ctagstype = 'javascript'
412 let jsctags = s:CheckFTCtags('jsctags', 'javascript')
414 let type_javascript.kinds = [
415 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
416 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
418 let type_javascript.sro = '.'
419 let type_javascript.kind2scope = {
423 let type_javascript.scope2kind = {
426 let type_javascript.ctagsbin = jsctags
427 let type_javascript.ctagsargs = '-f -'
429 let type_javascript.kinds = [
430 \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
431 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
432 \ {'short' : 'p', 'long' : 'properties', 'fold' : 0},
433 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
434 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
437 let s:known_types.javascript = type_javascript
440 let type_lisp.ctagstype = 'lisp'
441 let type_lisp.kinds = [
442 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
444 let s:known_types.lisp = type_lisp
447 let type_lua.ctagstype = 'lua'
448 let type_lua.kinds = [
449 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
451 let s:known_types.lua = type_lua
454 let type_make.ctagstype = 'make'
455 let type_make.kinds = [
456 \ {'short' : 'm', 'long' : 'macros', 'fold' : 0}
458 let s:known_types.make = type_make
461 let type_matlab.ctagstype = 'matlab'
462 let type_matlab.kinds = [
463 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
465 let s:known_types.matlab = type_matlab
468 let type_ocaml.ctagstype = 'ocaml'
469 let type_ocaml.kinds = [
470 \ {'short' : 'M', 'long' : 'modules or functors', 'fold' : 0},
471 \ {'short' : 'v', 'long' : 'global variables', 'fold' : 0},
472 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
473 \ {'short' : 'C', 'long' : 'constructors', 'fold' : 0},
474 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
475 \ {'short' : 'e', 'long' : 'exceptions', 'fold' : 0},
476 \ {'short' : 't', 'long' : 'type names', 'fold' : 0},
477 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
478 \ {'short' : 'r', 'long' : 'structure fields', 'fold' : 0}
480 let type_ocaml.sro = '.' " Not sure, is nesting even possible?
481 let type_ocaml.kind2scope = {
486 let type_ocaml.scope2kind = {
491 let s:known_types.ocaml = type_ocaml
494 let type_pascal.ctagstype = 'pascal'
495 let type_pascal.kinds = [
496 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
497 \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
499 let s:known_types.pascal = type_pascal
502 let type_perl.ctagstype = 'perl'
503 let type_perl.kinds = [
504 \ {'short' : 'p', 'long' : 'packages', 'fold' : 1},
505 \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
506 \ {'short' : 'f', 'long' : 'formats', 'fold' : 0},
507 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0},
508 \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0}
510 let s:known_types.perl = type_perl
513 let type_php.ctagstype = 'php'
514 let type_php.kinds = [
515 \ {'short' : 'i', 'long' : 'interfaces', 'fold' : 0},
516 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
517 \ {'short' : 'd', 'long' : 'constant definitions', 'fold' : 0},
518 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
519 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
520 \ {'short' : 'j', 'long' : 'javascript functions', 'fold' : 0}
522 let s:known_types.php = type_php
525 let type_python.ctagstype = 'python'
526 let type_python.kinds = [
527 \ {'short' : 'i', 'long' : 'imports', 'fold' : 1},
528 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
529 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
530 \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
531 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0}
533 let type_python.sro = '.'
534 let type_python.kind2scope = {
539 let type_python.scope2kind = {
543 let s:known_types.python = type_python
546 let type_rexx.ctagstype = 'rexx'
547 let type_rexx.kinds = [
548 \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0}
550 let s:known_types.rexx = type_rexx
553 let type_ruby.ctagstype = 'ruby'
554 let type_ruby.kinds = [
555 \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
556 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
557 \ {'short' : 'f', 'long' : 'methods', 'fold' : 0},
558 \ {'short' : 'F', 'long' : 'singleton methods', 'fold' : 0}
560 let type_ruby.sro = '.'
561 let type_ruby.kind2scope = {
565 let type_ruby.scope2kind = {
568 let s:known_types.ruby = type_ruby
571 let type_scheme.ctagstype = 'scheme'
572 let type_scheme.kinds = [
573 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
574 \ {'short' : 's', 'long' : 'sets', 'fold' : 0}
576 let s:known_types.scheme = type_scheme
579 let type_sh.ctagstype = 'sh'
580 let type_sh.kinds = [
581 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
583 let s:known_types.sh = type_sh
584 let s:known_types.csh = type_sh
585 let s:known_types.zsh = type_sh
588 let type_slang.ctagstype = 'slang'
589 let type_slang.kinds = [
590 \ {'short' : 'n', 'long' : 'namespaces', 'fold' : 0},
591 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
593 let s:known_types.slang = type_slang
596 let type_sml.ctagstype = 'sml'
597 let type_sml.kinds = [
598 \ {'short' : 'e', 'long' : 'exception declarations', 'fold' : 0},
599 \ {'short' : 'f', 'long' : 'function definitions', 'fold' : 0},
600 \ {'short' : 'c', 'long' : 'functor definitions', 'fold' : 0},
601 \ {'short' : 's', 'long' : 'signature declarations', 'fold' : 0},
602 \ {'short' : 'r', 'long' : 'structure declarations', 'fold' : 0},
603 \ {'short' : 't', 'long' : 'type definitions', 'fold' : 0},
604 \ {'short' : 'v', 'long' : 'value bindings', 'fold' : 0}
606 let s:known_types.sml = type_sml
608 " The SQL ctags parser seems to be buggy for me, so this just uses the
609 " normal kinds even though scopes should be available. Improvements
612 let type_sql.ctagstype = 'sql'
613 let type_sql.kinds = [
614 \ {'short' : 'P', 'long' : 'packages', 'fold' : 1},
615 \ {'short' : 'c', 'long' : 'cursors', 'fold' : 0},
616 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
617 \ {'short' : 'F', 'long' : 'record fields', 'fold' : 0},
618 \ {'short' : 'L', 'long' : 'block label', 'fold' : 0},
619 \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0},
620 \ {'short' : 's', 'long' : 'subtypes', 'fold' : 0},
621 \ {'short' : 't', 'long' : 'tables', 'fold' : 0},
622 \ {'short' : 'T', 'long' : 'triggers', 'fold' : 0},
623 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
624 \ {'short' : 'i', 'long' : 'indexes', 'fold' : 0},
625 \ {'short' : 'e', 'long' : 'events', 'fold' : 0},
626 \ {'short' : 'U', 'long' : 'publications', 'fold' : 0},
627 \ {'short' : 'R', 'long' : 'services', 'fold' : 0},
628 \ {'short' : 'D', 'long' : 'domains', 'fold' : 0},
629 \ {'short' : 'V', 'long' : 'views', 'fold' : 0},
630 \ {'short' : 'n', 'long' : 'synonyms', 'fold' : 0},
631 \ {'short' : 'x', 'long' : 'MobiLink Table Scripts', 'fold' : 0},
632 \ {'short' : 'y', 'long' : 'MobiLink Conn Scripts', 'fold' : 0}
634 let s:known_types.sql = type_sql
637 let type_tcl.ctagstype = 'tcl'
638 let type_tcl.kinds = [
639 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
640 \ {'short' : 'm', 'long' : 'methods', 'fold' : 0},
641 \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
643 let s:known_types.tcl = type_tcl
646 let type_tex.ctagstype = 'tex'
647 let type_tex.kinds = [
648 \ {'short' : 'p', 'long' : 'parts', 'fold' : 0},
649 \ {'short' : 'c', 'long' : 'chapters', 'fold' : 0},
650 \ {'short' : 's', 'long' : 'sections', 'fold' : 0},
651 \ {'short' : 'u', 'long' : 'subsections', 'fold' : 0},
652 \ {'short' : 'b', 'long' : 'subsubsections', 'fold' : 0},
653 \ {'short' : 'P', 'long' : 'paragraphs', 'fold' : 0},
654 \ {'short' : 'G', 'long' : 'subparagraphs', 'fold' : 0}
656 let s:known_types.tex = type_tex
658 " Why are variables 'virtual'?
660 let type_vera.ctagstype = 'vera'
661 let type_vera.kinds = [
662 \ {'short' : 'd', 'long' : 'macros', 'fold' : 1},
663 \ {'short' : 'g', 'long' : 'enums', 'fold' : 0},
664 \ {'short' : 'T', 'long' : 'typedefs', 'fold' : 0},
665 \ {'short' : 'c', 'long' : 'classes', 'fold' : 0},
666 \ {'short' : 'e', 'long' : 'enumerators', 'fold' : 0},
667 \ {'short' : 'm', 'long' : 'members', 'fold' : 0},
668 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
669 \ {'short' : 't', 'long' : 'tasks', 'fold' : 0},
670 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
671 \ {'short' : 'p', 'long' : 'programs', 'fold' : 0}
673 let type_vera.sro = '.' " Nesting doesn't seem to be possible
674 let type_vera.kind2scope = {
679 let type_vera.scope2kind = {
684 let s:known_types.vera = type_vera
686 let type_verilog = {}
687 let type_verilog.ctagstype = 'verilog'
688 let type_verilog.kinds = [
689 \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
690 \ {'short' : 'e', 'long' : 'events', 'fold' : 0},
691 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
692 \ {'short' : 'm', 'long' : 'modules', 'fold' : 0},
693 \ {'short' : 'n', 'long' : 'net data types', 'fold' : 0},
694 \ {'short' : 'p', 'long' : 'ports', 'fold' : 0},
695 \ {'short' : 'r', 'long' : 'register data types', 'fold' : 0},
696 \ {'short' : 't', 'long' : 'tasks', 'fold' : 0}
698 let s:known_types.verilog = type_verilog
700 " The VHDL ctags parser unfortunately doesn't generate proper scopes
702 let type_vhdl.ctagstype = 'vhdl'
703 let type_vhdl.kinds = [
704 \ {'short' : 'P', 'long' : 'packages', 'fold' : 1},
705 \ {'short' : 'c', 'long' : 'constants', 'fold' : 0},
706 \ {'short' : 't', 'long' : 'types', 'fold' : 0},
707 \ {'short' : 'T', 'long' : 'subtypes', 'fold' : 0},
708 \ {'short' : 'r', 'long' : 'records', 'fold' : 0},
709 \ {'short' : 'e', 'long' : 'entities', 'fold' : 0},
710 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
711 \ {'short' : 'p', 'long' : 'procedures', 'fold' : 0}
713 let s:known_types.vhdl = type_vhdl
716 let type_vim.ctagstype = 'vim'
717 let type_vim.kinds = [
718 \ {'short' : 'v', 'long' : 'variables', 'fold' : 1},
719 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0},
720 \ {'short' : 'a', 'long' : 'autocommand groups', 'fold' : 1},
721 \ {'short' : 'c', 'long' : 'commands', 'fold' : 0},
722 \ {'short' : 'm', 'long' : 'maps', 'fold' : 1}
724 let s:known_types.vim = type_vim
727 let type_yacc.ctagstype = 'yacc'
728 let type_yacc.kinds = [
729 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0}
731 let s:known_types.yacc = type_yacc
734 let user_defs = s:GetUserTypeDefs()
735 for [key, value] in items(user_defs)
736 if !has_key(s:known_types, key) ||
737 \ (has_key(value, 'replace') && value.replace)
738 let s:known_types[key] = value
740 call extend(s:known_types[key], value)
744 " Create a dictionary of the kind order for fast
745 " access in sorting functions
746 for type in values(s:known_types)
748 let type.kinddict = {}
749 for kind in type.kinds
750 let type.kinddict[kind.short] = i
755 let s:type_init_done = 1
758 " s:GetUserTypeDefs() {{{2
759 function! s:GetUserTypeDefs()
760 call s:LogDebugMessage('Initializing user types')
763 silent execute 'let g:'
766 let deflist = split(defs, '\n')
767 call map(deflist, 'substitute(v:val, ''^\S\+\zs.*'', "", "")')
768 call filter(deflist, 'v:val =~ "^tagbar_type_"')
771 for defstr in deflist
772 let type = substitute(defstr, '^tagbar_type_', '', '')
773 execute 'let defdict["' . type . '"] = g:' . defstr
776 " If the user only specified one of kind2scope and scope2kind use it to
777 " generate the other one
778 " Also, transform the 'kind' definitions into dictionary format
779 for def in values(defdict)
780 if has_key(def, 'kinds')
781 let kinds = def.kinds
784 let kindlist = split(kind, ':')
785 let kinddict = {'short' : kindlist[0], 'long' : kindlist[1]}
786 if len(kindlist) == 3
787 let kinddict.fold = kindlist[2]
789 let kinddict.fold = 0
791 call add(def.kinds, kinddict)
795 if has_key(def, 'kind2scope') && !has_key(def, 'scope2kind')
796 let def.scope2kind = {}
797 for [key, value] in items(def.kind2scope)
798 let def.scope2kind[value] = key
800 elseif has_key(def, 'scope2kind') && !has_key(def, 'kind2scope')
801 let def.kind2scope = {}
802 for [key, value] in items(def.scope2kind)
803 let def.kind2scope[value] = key
811 " s:RestoreSession() {{{2
812 " Properly restore Tagbar after a session got loaded
813 function! s:RestoreSession()
814 call s:LogDebugMessage('Restoring session')
816 let tagbarwinnr = bufwinnr('__Tagbar__')
818 " Tagbar wasn't open in the saved session, nothing to do
822 if winnr() != tagbarwinnr
823 execute tagbarwinnr . 'wincmd w'
830 call s:InitWindow(g:tagbar_autoclose)
832 " Leave the Tagbar window and come back so the update event gets triggered
834 execute tagbarwinnr . 'wincmd w'
842 function! s:MapKeys()
843 call s:LogDebugMessage('Mapping keys')
845 nnoremap <script> <silent> <buffer> <2-LeftMouse>
846 \ :call <SID>JumpToTag(0)<CR>
847 nnoremap <script> <silent> <buffer> <LeftRelease>
848 \ <LeftRelease>:call <SID>CheckMouseClick()<CR>
850 inoremap <script> <silent> <buffer> <2-LeftMouse>
851 \ <C-o>:call <SID>JumpToTag(0)<CR>
852 inoremap <script> <silent> <buffer> <LeftRelease>
853 \ <LeftRelease><C-o>:call <SID>CheckMouseClick()<CR>
855 nnoremap <script> <silent> <buffer> <CR> :call <SID>JumpToTag(0)<CR>
856 nnoremap <script> <silent> <buffer> p :call <SID>JumpToTag(1)<CR>
857 nnoremap <script> <silent> <buffer> <Space> :call <SID>ShowPrototype()<CR>
859 nnoremap <script> <silent> <buffer> + :call <SID>OpenFold()<CR>
860 nnoremap <script> <silent> <buffer> <kPlus> :call <SID>OpenFold()<CR>
861 nnoremap <script> <silent> <buffer> zo :call <SID>OpenFold()<CR>
862 nnoremap <script> <silent> <buffer> - :call <SID>CloseFold()<CR>
863 nnoremap <script> <silent> <buffer> <kMinus> :call <SID>CloseFold()<CR>
864 nnoremap <script> <silent> <buffer> zc :call <SID>CloseFold()<CR>
865 nnoremap <script> <silent> <buffer> o :call <SID>ToggleFold()<CR>
866 nnoremap <script> <silent> <buffer> za :call <SID>ToggleFold()<CR>
868 nnoremap <script> <silent> <buffer> * :call <SID>SetFoldLevel(99)<CR>
869 nnoremap <script> <silent> <buffer> <kMultiply>
870 \ :call <SID>SetFoldLevel(99)<CR>
871 nnoremap <script> <silent> <buffer> zR :call <SID>SetFoldLevel(99)<CR>
872 nnoremap <script> <silent> <buffer> = :call <SID>SetFoldLevel(0)<CR>
873 nnoremap <script> <silent> <buffer> zM :call <SID>SetFoldLevel(0)<CR>
875 nnoremap <script> <silent> <buffer> <C-N>
876 \ :call <SID>GotoNextToplevelTag(1)<CR>
877 nnoremap <script> <silent> <buffer> <C-P>
878 \ :call <SID>GotoNextToplevelTag(-1)<CR>
880 nnoremap <script> <silent> <buffer> s :call <SID>ToggleSort()<CR>
881 nnoremap <script> <silent> <buffer> x :call <SID>ZoomWindow()<CR>
882 nnoremap <script> <silent> <buffer> q :call <SID>CloseWindow()<CR>
883 nnoremap <script> <silent> <buffer> <F1> :call <SID>ToggleHelp()<CR>
886 " s:CreateAutocommands() {{{2
887 function! s:CreateAutocommands()
888 call s:LogDebugMessage('Creating autocommands')
890 augroup TagbarAutoCmds
892 autocmd BufEnter __Tagbar__ nested call s:QuitIfOnlyWindow()
893 autocmd BufUnload __Tagbar__ call s:CleanUp()
894 autocmd CursorHold __Tagbar__ call s:ShowPrototype()
896 autocmd BufWritePost *
897 \ if line('$') < g:tagbar_updateonsave_maxlines |
898 \ call s:AutoUpdate(fnamemodify(expand('<afile>'), ':p')) |
900 autocmd BufEnter,CursorHold,FileType * call
901 \ s:AutoUpdate(fnamemodify(expand('<afile>'), ':p'))
902 autocmd BufDelete * call
903 \ s:CleanupFileinfo(fnamemodify(expand('<afile>'), ':p'))
906 let s:autocommands_done = 1
909 " s:CheckForExCtags() {{{2
910 " Test whether the ctags binary is actually Exuberant Ctags and not GNU ctags
911 " (or something else)
912 function! s:CheckForExCtags()
913 call s:LogDebugMessage('Checking for Exuberant Ctags')
915 let ctags_cmd = s:EscapeCtagsCmd(g:tagbar_ctags_bin, '--version')
920 let ctags_output = s:ExecuteCtags(ctags_cmd)
922 if v:shell_error || ctags_output !~# 'Exuberant Ctags'
923 echoerr 'Tagbar: Ctags doesn''t seem to be Exuberant Ctags!'
924 echomsg 'GNU ctags will NOT WORK.'
925 \ 'Please download Exuberant Ctags from ctags.sourceforge.net'
926 \ 'and install it in a directory in your $PATH'
927 \ 'or set g:tagbar_ctags_bin.'
928 echomsg 'Executed command: "' . ctags_cmd . '"'
929 if !empty(ctags_output)
930 echomsg 'Command output:'
931 for line in split(ctags_output, '\n')
936 elseif !s:CheckExCtagsVersion(ctags_output)
937 echoerr 'Tagbar: Exuberant Ctags is too old!'
938 echomsg 'You need at least version 5.5 for Tagbar to work.'
939 \ 'Please download a newer version from ctags.sourceforge.net.'
940 echomsg 'Executed command: "' . ctags_cmd . '"'
941 if !empty(ctags_output)
942 echomsg 'Command output:'
943 for line in split(ctags_output, '\n')
949 let s:checked_ctags = 1
954 " s:CheckExCtagsVersion() {{{2
955 function! s:CheckExCtagsVersion(output)
956 call s:LogDebugMessage('Checking Exuberant Ctags version')
958 if a:output =~ 'Exuberant Ctags Development'
962 let matchlist = matchlist(a:output, '\vExuberant Ctags (\d+)\.(\d+)')
963 let major = matchlist[1]
964 let minor = matchlist[2]
966 return major >= 6 || (major == 5 && minor >= 5)
969 " s:CheckFTCtags() {{{2
970 function! s:CheckFTCtags(bin, ftype)
975 if exists('g:tagbar_type_' . a:ftype)
976 execute 'let userdef = ' . 'g:tagbar_type_' . a:ftype
977 if has_key(userdef, 'ctagsbin')
978 return userdef.ctagsbin
991 " s:BaseTag._init() {{{3
992 function! s:BaseTag._init(name) dict
993 let self.name = a:name
995 let self.fields.line = 0
997 let self.fullpath = a:name
1001 let self.fileinfo = {}
1004 " s:BaseTag.isNormalTag() {{{3
1005 function! s:BaseTag.isNormalTag() dict
1009 " s:BaseTag.isPseudoTag() {{{3
1010 function! s:BaseTag.isPseudoTag() dict
1014 " s:BaseTag.isKindheader() {{{3
1015 function! s:BaseTag.isKindheader() dict
1019 " s:BaseTag.getPrototype() {{{3
1020 function! s:BaseTag.getPrototype() dict
1024 " s:BaseTag._getPrefix() {{{3
1025 function! s:BaseTag._getPrefix() dict
1026 let fileinfo = self.fileinfo
1028 if has_key(self, 'children') && !empty(self.children)
1029 if fileinfo.tagfolds[self.fields.kind][self.fullpath]
1030 let prefix = s:icon_closed
1032 let prefix = s:icon_open
1037 if has_key(self.fields, 'access')
1038 let prefix .= get(s:access_symbols, self.fields.access, ' ')
1046 " s:BaseTag.initFoldState() {{{3
1047 function! s:BaseTag.initFoldState() dict
1048 let fileinfo = self.fileinfo
1050 if s:known_files.has(fileinfo.fpath) &&
1051 \ has_key(fileinfo._tagfolds_old[self.fields.kind], self.fullpath)
1052 " The file has been updated and the tag was there before, so copy its
1054 let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
1055 \ fileinfo._tagfolds_old[self.fields.kind][self.fullpath]
1056 elseif self.depth >= fileinfo.foldlevel
1057 let fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
1059 let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
1060 \ fileinfo.kindfolds[self.fields.kind]
1064 " s:BaseTag.getClosedParentTline() {{{3
1065 function! s:BaseTag.getClosedParentTline() dict
1066 let tagline = self.tline
1067 let fileinfo = self.fileinfo
1069 let parent = self.parent
1070 while !empty(parent)
1071 if parent.isFolded()
1072 let tagline = parent.tline
1075 let parent = parent.parent
1081 " s:BaseTag.isFoldable() {{{3
1082 function! s:BaseTag.isFoldable() dict
1083 return has_key(self, 'children') && !empty(self.children)
1086 " s:BaseTag.isFolded() {{{3
1087 function! s:BaseTag.isFolded() dict
1088 return self.fileinfo.tagfolds[self.fields.kind][self.fullpath]
1091 " s:BaseTag.openFold() {{{3
1092 function! s:BaseTag.openFold() dict
1093 if self.isFoldable()
1094 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 0
1098 " s:BaseTag.closeFold() {{{3
1099 function! s:BaseTag.closeFold() dict
1100 let newline = line('.')
1102 if !empty(self.parent) && self.parent.isKindheader()
1103 " Tag is child of generic 'kind'
1104 call self.parent.closeFold()
1105 let newline = self.parent.tline
1106 elseif self.isFoldable() && !self.isFolded()
1107 " Tag is parent of a scope and is not folded
1108 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 1
1109 let newline = self.tline
1110 elseif !empty(self.parent)
1111 " Tag is normal child, so close parent
1112 let parent = self.parent
1113 let self.fileinfo.tagfolds[parent.fields.kind][parent.fullpath] = 1
1114 let newline = parent.tline
1120 " s:BaseTag.setFolded() {{{3
1121 function! s:BaseTag.setFolded(folded) dict
1122 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = a:folded
1125 " s:BaseTag.openParents() {{{3
1126 function! s:BaseTag.openParents() dict
1127 let parent = self.parent
1129 while !empty(parent)
1130 call parent.openFold()
1131 let parent = parent.parent
1136 let s:NormalTag = copy(s:BaseTag)
1138 " s:NormalTag.New() {{{3
1139 function! s:NormalTag.New(name) dict
1140 let newobj = copy(self)
1142 call newobj._init(a:name)
1147 " s:NormalTag.isNormalTag() {{{3
1148 function! s:NormalTag.isNormalTag() dict
1152 " s:NormalTag.str() {{{3
1153 function! s:NormalTag.str() dict
1154 let fileinfo = self.fileinfo
1155 let typeinfo = s:known_types[fileinfo.ftype]
1157 let suffix = get(self.fields, 'signature', '')
1158 if has_key(self.fields, 'type')
1159 let suffix .= ' : ' . self.fields.type
1160 elseif has_key(typeinfo, 'kind2scope') &&
1161 \ has_key(typeinfo.kind2scope, self.fields.kind)
1162 let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
1165 return self._getPrefix() . self.name . suffix . "\n"
1168 " s:NormalTag.getPrototype() {{{3
1169 function! s:NormalTag.getPrototype() dict
1170 return self.prototype
1174 let s:PseudoTag = copy(s:BaseTag)
1176 " s:PseudoTag.New() {{{3
1177 function! s:PseudoTag.New(name) dict
1178 let newobj = copy(self)
1180 call newobj._init(a:name)
1185 " s:PseudoTag.isPseudoTag() {{{3
1186 function! s:PseudoTag.isPseudoTag() dict
1190 " s:PseudoTag.str() {{{3
1191 function! s:PseudoTag.str() dict
1192 let fileinfo = self.fileinfo
1193 let typeinfo = s:known_types[fileinfo.ftype]
1195 let suffix = get(self.fields, 'signature', '')
1196 if has_key(typeinfo.kind2scope, self.fields.kind)
1197 let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
1200 return self._getPrefix() . self.name . '*' . suffix
1204 let s:KindheaderTag = copy(s:BaseTag)
1206 " s:KindheaderTag.New() {{{3
1207 function! s:KindheaderTag.New(name) dict
1208 let newobj = copy(self)
1210 call newobj._init(a:name)
1215 " s:KindheaderTag.isKindheader() {{{3
1216 function! s:KindheaderTag.isKindheader() dict
1220 " s:KindheaderTag.getPrototype() {{{3
1221 function! s:KindheaderTag.getPrototype() dict
1222 return self.name . ': ' .
1223 \ self.numtags . ' ' . (self.numtags > 1 ? 'tags' : 'tag')
1226 " s:KindheaderTag.isFoldable() {{{3
1227 function! s:KindheaderTag.isFoldable() dict
1231 " s:KindheaderTag.isFolded() {{{3
1232 function! s:KindheaderTag.isFolded() dict
1233 return self.fileinfo.kindfolds[self.short]
1236 " s:KindheaderTag.openFold() {{{3
1237 function! s:KindheaderTag.openFold() dict
1238 let self.fileinfo.kindfolds[self.short] = 0
1241 " s:KindheaderTag.closeFold() {{{3
1242 function! s:KindheaderTag.closeFold() dict
1243 let self.fileinfo.kindfolds[self.short] = 1
1247 " s:KindheaderTag.toggleFold() {{{3
1248 function! s:KindheaderTag.toggleFold() dict
1249 let fileinfo = s:known_files.getCurrent()
1251 let fileinfo.kindfolds[self.short] = !fileinfo.kindfolds[self.short]
1257 " s:FileInfo.New() {{{3
1258 function! s:FileInfo.New(fname, ftype) dict
1259 let newobj = copy(self)
1261 " The complete file path
1262 let newobj.fpath = a:fname
1264 " File modification time
1265 let newobj.mtime = getftime(a:fname)
1268 let newobj.ftype = a:ftype
1270 " List of the tags that are present in the file, sorted according to the
1271 " value of 'g:tagbar_sort'
1272 let newobj.tags = []
1274 " Dictionary of the tags, indexed by line number in the file
1275 let newobj.fline = {}
1277 " Dictionary of the tags, indexed by line number in the tagbar
1278 let newobj.tline = {}
1280 " Dictionary of the folding state of 'kind's, indexed by short name
1281 let newobj.kindfolds = {}
1282 let typeinfo = s:known_types[a:ftype]
1283 " copy the default fold state from the type info
1284 for kind in typeinfo.kinds
1285 let newobj.kindfolds[kind.short] =
1286 \ g:tagbar_foldlevel == 0 ? 1 : kind.fold
1289 " Dictionary of dictionaries of the folding state of individual tags,
1290 " indexed by kind and full path
1291 let newobj.tagfolds = {}
1292 for kind in typeinfo.kinds
1293 let newobj.tagfolds[kind.short] = {}
1296 " The current foldlevel of the file
1297 let newobj.foldlevel = g:tagbar_foldlevel
1302 " s:FileInfo.reset() {{{3
1303 " Reset stuff that gets regenerated while processing a file and save the old
1305 function! s:FileInfo.reset() dict
1306 let self.mtime = getftime(self.fpath)
1311 let self._tagfolds_old = self.tagfolds
1312 let self.tagfolds = {}
1314 let typeinfo = s:known_types[self.ftype]
1315 for kind in typeinfo.kinds
1316 let self.tagfolds[kind.short] = {}
1320 " s:FileInfo.clearOldFolds() {{{3
1321 function! s:FileInfo.clearOldFolds() dict
1322 if exists('self._tagfolds_old')
1323 unlet self._tagfolds_old
1327 " s:FileInfo.sortTags() {{{3
1328 function! s:FileInfo.sortTags() dict
1329 if has_key(s:compare_typeinfo, 'sort')
1330 if s:compare_typeinfo.sort
1331 call s:SortTags(self.tags, 's:CompareByKind')
1333 call s:SortTags(self.tags, 's:CompareByLine')
1335 elseif g:tagbar_sort
1336 call s:SortTags(self.tags, 's:CompareByKind')
1338 call s:SortTags(self.tags, 's:CompareByLine')
1342 " s:FileInfo.openKindFold() {{{3
1343 function! s:FileInfo.openKindFold(kind) dict
1344 let self.kindfolds[a:kind.short] = 0
1347 " s:FileInfo.closeKindFold() {{{3
1348 function! s:FileInfo.closeKindFold(kind) dict
1349 let self.kindfolds[a:kind.short] = 1
1353 let s:known_files = {
1358 " s:known_files.getCurrent() {{{3
1359 function! s:known_files.getCurrent() dict
1360 return self._current
1363 " s:known_files.setCurrent() {{{3
1364 function! s:known_files.setCurrent(fileinfo) dict
1365 let self._current = a:fileinfo
1368 " s:known_files.get() {{{3
1369 function! s:known_files.get(fname) dict
1370 return get(self._files, a:fname, {})
1373 " s:known_files.put() {{{3
1374 " Optional second argument is the filename
1375 function! s:known_files.put(fileinfo, ...) dict
1377 let self._files[a:1] = a:fileinfo
1379 let fname = a:fileinfo.fpath
1380 let self._files[fname] = a:fileinfo
1384 " s:known_files.has() {{{3
1385 function! s:known_files.has(fname) dict
1386 return has_key(self._files, a:fname)
1389 " s:known_files.rm() {{{3
1390 function! s:known_files.rm(fname) dict
1391 if s:known_files.has(a:fname)
1392 call remove(self._files, a:fname)
1396 " Window management {{{1
1397 " s:ToggleWindow() {{{2
1398 function! s:ToggleWindow()
1399 let tagbarwinnr = bufwinnr("__Tagbar__")
1400 if tagbarwinnr != -1
1401 call s:CloseWindow()
1405 call s:OpenWindow('')
1408 " s:OpenWindow() {{{2
1409 function! s:OpenWindow(flags)
1410 let autofocus = a:flags =~# 'f'
1411 let jump = a:flags =~# 'j'
1412 let autoclose = a:flags =~# 'c'
1414 " If the tagbar window is already open check jump flag
1415 " Also set the autoclose flag if requested
1416 let tagbarwinnr = bufwinnr('__Tagbar__')
1417 if tagbarwinnr != -1
1418 if winnr() != tagbarwinnr && jump
1419 execute tagbarwinnr . 'wincmd w'
1421 let w:autoclose = autoclose
1429 " Expand the Vim window to accomodate for the Tagbar window if requested
1430 if g:tagbar_expand && !s:window_expanded && has('gui_running')
1431 let &columns += g:tagbar_width + 1
1432 let s:window_expanded = 1
1435 let eventignore_save = &eventignore
1438 let openpos = g:tagbar_left ? 'topleft vertical ' : 'botright vertical '
1439 exe 'silent keepalt ' . openpos . g:tagbar_width . 'split ' . '__Tagbar__'
1441 let &eventignore = eventignore_save
1443 call s:InitWindow(autoclose)
1447 " Jump back to the tagbar window if autoclose or autofocus is set. Can't
1448 " just stay in it since it wouldn't trigger the update event
1449 if g:tagbar_autoclose || autofocus || g:tagbar_autofocus
1450 let tagbarwinnr = bufwinnr('__Tagbar__')
1451 execute tagbarwinnr . 'wincmd w'
1455 " s:InitWindow() {{{2
1456 function! s:InitWindow(autoclose)
1457 setlocal noreadonly " in case the "view" mode is used
1458 setlocal buftype=nofile
1459 setlocal bufhidden=hide
1461 setlocal nobuflisted
1462 setlocal nomodifiable
1463 setlocal filetype=tagbar
1467 setlocal winfixwidth
1468 setlocal textwidth=0
1469 setlocal nocursorline
1470 setlocal nocursorcolumn
1472 if exists('+relativenumber')
1473 setlocal norelativenumber
1476 setlocal nofoldenable
1477 setlocal foldcolumn=0
1478 " Reset fold settings in case a plugin set them globally to something
1479 " expensive. Apparently 'foldexpr' gets executed even if 'foldenable' is
1480 " off, and then for every appended line (like with :put).
1481 setlocal foldmethod&
1484 " Earlier versions have a bug in local, evaluated statuslines
1485 if v:version > 701 || (v:version == 701 && has('patch097'))
1486 setlocal statusline=%!TagbarGenerateStatusline()
1488 setlocal statusline=Tagbar
1491 " Script-local variable needed since compare functions can't
1492 " take extra arguments
1493 let s:compare_typeinfo = {}
1495 let s:is_maximized = 0
1496 let s:short_help = 1
1498 let w:autoclose = a:autoclose
1500 if has('balloon_eval')
1501 setlocal balloonexpr=TagbarBalloonExpr()
1505 let cpoptions_save = &cpoptions
1508 if !hasmapto('JumpToTag', 'n')
1512 if !s:autocommands_done
1513 call s:CreateAutocommands()
1516 let &cpoptions = cpoptions_save
1519 " s:CloseWindow() {{{2
1520 function! s:CloseWindow()
1521 let tagbarwinnr = bufwinnr('__Tagbar__')
1522 if tagbarwinnr == -1
1526 let tagbarbufnr = winbufnr(tagbarwinnr)
1528 if winnr() == tagbarwinnr
1529 if winbufnr(2) != -1
1530 " Other windows are open, only close the tagbar one
1535 " Go to the tagbar window, close it and then come back to the
1537 let curbufnr = bufnr('%')
1538 execute tagbarwinnr . 'wincmd w'
1540 " Need to jump back to the original window only if we are not
1541 " already in that window
1542 let winnum = bufwinnr(curbufnr)
1543 if winnr() != winnum
1544 exe winnum . 'wincmd w'
1548 " If the Vim window has been expanded, and Tagbar is not open in any other
1549 " tabpages, shrink the window again
1550 if s:window_expanded
1552 for i in range(tabpagenr('$'))
1553 call extend(tablist, tabpagebuflist(i + 1))
1556 if index(tablist, tagbarbufnr) == -1
1557 let &columns -= g:tagbar_width + 1
1558 let s:window_expanded = 0
1563 " s:ZoomWindow() {{{2
1564 function! s:ZoomWindow()
1566 execute 'vert resize ' . g:tagbar_width
1567 let s:is_maximized = 0
1570 let s:is_maximized = 1
1574 " Tag processing {{{1
1575 " s:ProcessFile() {{{2
1576 " Execute ctags and put the information into a 'FileInfo' object
1577 function! s:ProcessFile(fname, ftype)
1578 call s:LogDebugMessage('ProcessFile called on ' . a:fname)
1580 if !s:IsValidFile(a:fname, a:ftype)
1581 call s:LogDebugMessage('Not a valid file, returning')
1585 let ctags_output = s:ExecuteCtagsOnFile(a:fname, a:ftype)
1587 if ctags_output == -1
1588 call s:LogDebugMessage('Ctags error when processing file')
1589 " put an empty entry into known_files so the error message is only
1591 call s:known_files.put({}, a:fname)
1593 elseif ctags_output == ''
1594 call s:LogDebugMessage('Ctags output empty')
1598 " If the file has only been updated preserve the fold states, otherwise
1599 " create a new entry
1600 if s:known_files.has(a:fname)
1601 let fileinfo = s:known_files.get(a:fname)
1602 call fileinfo.reset()
1604 let fileinfo = s:FileInfo.New(a:fname, a:ftype)
1607 let typeinfo = s:known_types[a:ftype]
1609 " Parse the ctags output lines
1610 call s:LogDebugMessage('Parsing ctags output')
1611 let rawtaglist = split(ctags_output, '\n\+')
1612 for line in rawtaglist
1614 if line =~# '^!_TAG_'
1618 let parts = split(line, ';"')
1619 if len(parts) == 2 " Is a valid tag line
1620 let taginfo = s:ParseTagline(parts[0], parts[1], typeinfo, fileinfo)
1621 let fileinfo.fline[taginfo.fields.line] = taginfo
1622 call add(fileinfo.tags, taginfo)
1626 " Process scoped tags
1627 let processedtags = []
1628 if has_key(typeinfo, 'kind2scope')
1629 call s:LogDebugMessage('Processing scoped tags')
1632 let is_scoped = 'has_key(typeinfo.kind2scope, v:val.fields.kind) ||
1633 \ has_key(v:val, "scope")'
1634 let scopedtags += filter(copy(fileinfo.tags), is_scoped)
1635 call filter(fileinfo.tags, '!(' . is_scoped . ')')
1637 call s:AddScopedTags(scopedtags, processedtags, {}, 0,
1638 \ typeinfo, fileinfo)
1640 if !empty(scopedtags)
1641 echoerr 'Tagbar: ''scopedtags'' not empty after processing,'
1642 \ 'this should never happen!'
1643 \ 'Please contact the script maintainer with an example.'
1646 call s:LogDebugMessage('Number of top-level tags: ' . len(processedtags))
1648 " Create a placeholder tag for the 'kind' header for folding purposes
1649 for kind in typeinfo.kinds
1651 let curtags = filter(copy(fileinfo.tags),
1652 \ 'v:val.fields.kind ==# kind.short')
1653 call s:LogDebugMessage('Processing kind: ' . kind.short .
1654 \ ', number of tags: ' . len(curtags))
1660 let kindtag = s:KindheaderTag.New(kind.long)
1661 let kindtag.short = kind.short
1662 let kindtag.numtags = len(curtags)
1663 let kindtag.fileinfo = fileinfo
1666 let tag.parent = kindtag
1670 if !empty(processedtags)
1671 call extend(fileinfo.tags, processedtags)
1674 " Clear old folding information from previous file version to prevent leaks
1675 call fileinfo.clearOldFolds()
1678 let s:compare_typeinfo = typeinfo
1679 call fileinfo.sortTags()
1681 call s:known_files.put(fileinfo)
1684 " s:ExecuteCtagsOnFile() {{{2
1685 function! s:ExecuteCtagsOnFile(fname, ftype)
1686 call s:LogDebugMessage('ExecuteCtagsOnFile called on ' . a:fname)
1688 let typeinfo = s:known_types[a:ftype]
1690 if has_key(typeinfo, 'ctagsargs')
1691 let ctags_args = ' ' . typeinfo.ctagsargs . ' '
1693 let ctags_args = ' -f - '
1694 let ctags_args .= ' --format=2 '
1695 let ctags_args .= ' --excmd=pattern '
1696 let ctags_args .= ' --fields=nksSa '
1697 let ctags_args .= ' --extra= '
1698 let ctags_args .= ' --sort=yes '
1700 " Include extra type definitions
1701 if has_key(typeinfo, 'deffile')
1702 let ctags_args .= ' --options=' . typeinfo.deffile . ' '
1705 let ctags_type = typeinfo.ctagstype
1707 let ctags_kinds = ''
1708 for kind in typeinfo.kinds
1709 let ctags_kinds .= kind.short
1712 let ctags_args .= ' --language-force=' . ctags_type .
1713 \ ' --' . ctags_type . '-kinds=' . ctags_kinds . ' '
1716 if has_key(typeinfo, 'ctagsbin')
1717 " reset 'wildignore' temporarily in case *.exe is included in it
1718 let wildignore_save = &wildignore
1720 let ctags_bin = expand(typeinfo.ctagsbin)
1721 let &wildignore = wildignore_save
1723 let ctags_bin = g:tagbar_ctags_bin
1726 let ctags_cmd = s:EscapeCtagsCmd(ctags_bin, ctags_args, a:fname)
1731 let ctags_output = s:ExecuteCtags(ctags_cmd)
1733 if v:shell_error || ctags_output =~ 'Warning: cannot open source file'
1734 echoerr 'Tagbar: Could not execute ctags for ' . a:fname . '!'
1735 echomsg 'Executed command: "' . ctags_cmd . '"'
1736 if !empty(ctags_output)
1737 call s:LogDebugMessage('Command output:')
1738 call s:LogDebugMessage(ctags_output)
1739 echomsg 'Command output:'
1740 for line in split(ctags_output, '\n')
1747 call s:LogDebugMessage('Ctags executed successfully')
1751 " s:ParseTagline() {{{2
1752 " Structure of a tag line:
1753 " tagname<TAB>filename<TAB>expattern;"fields
1754 " fields: <TAB>name:value
1755 " fields that are always present: kind, line
1756 function! s:ParseTagline(part1, part2, typeinfo, fileinfo)
1757 let basic_info = split(a:part1, '\t')
1759 let taginfo = s:NormalTag.New(basic_info[0])
1760 let taginfo.file = basic_info[1]
1762 " the pattern can contain tabs and thus may have been split up, so join
1763 " the rest of the items together again
1764 let pattern = join(basic_info[2:], "\t")
1765 let start = 2 " skip the slash and the ^
1766 let end = strlen(pattern) - 1
1767 if pattern[end - 1] ==# '$'
1773 let pattern = strpart(pattern, start, end - start)
1774 let taginfo.pattern = '\V\^\C' . pattern . dollar
1775 let prototype = substitute(pattern, '^[[:space:]]\+', '', '')
1776 let prototype = substitute(prototype, '[[:space:]]\+$', '', '')
1777 let taginfo.prototype = prototype
1779 let fields = split(a:part2, '\t')
1780 let taginfo.fields.kind = remove(fields, 0)
1782 " can't use split() since the value can contain ':'
1783 let delimit = stridx(field, ':')
1784 let key = strpart(field, 0, delimit)
1785 let val = strpart(field, delimit + 1)
1787 let taginfo.fields[key] = val
1790 " Needed for jsctags
1791 if has_key(taginfo.fields, 'lineno')
1792 let taginfo.fields.line = taginfo.fields.lineno
1795 " Make some information easier accessible
1796 if has_key(a:typeinfo, 'scope2kind')
1797 for scope in keys(a:typeinfo.scope2kind)
1798 if has_key(taginfo.fields, scope)
1799 let taginfo.scope = scope
1800 let taginfo.path = taginfo.fields[scope]
1802 let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
1807 let taginfo.depth = len(split(taginfo.path, '\V' . a:typeinfo.sro))
1810 let taginfo.fileinfo = a:fileinfo
1812 " Needed for folding
1814 call taginfo.initFoldState()
1815 catch /^Vim(\a\+):E716:/ " 'Key not present in Dictionary'
1816 " The tag has a 'kind' that doesn't exist in the type definition
1817 echoerr 'Your ctags and Tagbar configurations are out of sync!'
1818 \ 'Please read '':help tagbar-extend''.'
1824 " s:AddScopedTags() {{{2
1825 " Recursively process tags. Unfortunately there is a problem: not all tags in
1826 " a hierarchy are actually there. For example, in C++ a class can be defined
1827 " in a header file and implemented in a .cpp file (so the class itself doesn't
1828 " appear in the .cpp file and thus doesn't generate a tag). Another example
1829 " are anonymous structures like namespaces, structs, enums, and unions, that
1830 " also don't get a tag themselves. These tags are thus called 'pseudo-tags' in
1831 " Tagbar. Properly parsing them is quite tricky, so try not to think about it
1833 function! s:AddScopedTags(tags, processedtags, parent, depth,
1834 \ typeinfo, fileinfo)
1836 let curpath = a:parent.fullpath
1837 let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
1843 let is_cur_tag = 'v:val.depth == a:depth'
1846 " Check whether the tag is either a direct child at the current depth
1847 " or at least a proper grandchild with pseudo-tags in between. If it
1848 " is a direct child also check for matching scope.
1849 let is_cur_tag .= ' &&
1850 \ (v:val.path ==# curpath ||
1851 \ match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0) &&
1852 \ (v:val.path ==# curpath ? (v:val.scope ==# pscope) : 1)'
1855 let curtags = filter(copy(a:tags), is_cur_tag)
1858 call filter(a:tags, '!(' . is_cur_tag . ')')
1863 while !empty(curtags)
1864 let tag = remove(curtags, 0)
1866 if tag.path != curpath
1867 " tag is child of a pseudo-tag, so create a new pseudo-tag and
1868 " add all its children to it
1869 let pseudotag = s:ProcessPseudoTag(curtags, tag, a:parent,
1870 \ a:typeinfo, a:fileinfo)
1872 call add(pseudotags, pseudotag)
1874 call add(realtags, tag)
1878 " Recursively add the children of the tags on the current level
1880 let tag.parent = a:parent
1882 if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
1886 if !has_key(tag, 'children')
1887 let tag.children = []
1890 call s:AddScopedTags(a:tags, tag.children, tag, a:depth + 1,
1891 \ a:typeinfo, a:fileinfo)
1893 call extend(a:processedtags, realtags)
1895 " Recursively add the children of the tags that are children of the
1896 " pseudo-tags on the current level
1897 for tag in pseudotags
1898 call s:ProcessPseudoChildren(a:tags, tag, a:depth, a:typeinfo,
1901 call extend(a:processedtags, pseudotags)
1904 " Now we have to check if there are any pseudo-tags at the current level
1905 " so we have to check for real tags at a lower level, i.e. grandchildren
1906 let is_grandchild = 'v:val.depth > a:depth'
1909 let is_grandchild .=
1910 \ ' && match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0'
1913 let grandchildren = filter(copy(a:tags), is_grandchild)
1915 if !empty(grandchildren)
1916 call s:AddScopedTags(a:tags, a:processedtags, a:parent, a:depth + 1,
1917 \ a:typeinfo, a:fileinfo)
1921 " s:ProcessPseudoTag() {{{2
1922 function! s:ProcessPseudoTag(curtags, tag, parent, typeinfo, fileinfo)
1923 let curpath = !empty(a:parent) ? a:parent.fullpath : ''
1925 let pseudoname = substitute(a:tag.path, curpath, '', '')
1926 let pseudoname = substitute(pseudoname, '\V\^' . a:typeinfo.sro, '', '')
1927 let pseudotag = s:CreatePseudoTag(pseudoname, a:parent, a:tag.scope,
1928 \ a:typeinfo, a:fileinfo)
1929 let pseudotag.children = [a:tag]
1931 " get all the other (direct) children of the current pseudo-tag
1932 let ispseudochild = 'v:val.path ==# a:tag.path && v:val.scope ==# a:tag.scope'
1933 let pseudochildren = filter(copy(a:curtags), ispseudochild)
1934 if !empty(pseudochildren)
1935 call filter(a:curtags, '!(' . ispseudochild . ')')
1936 call extend(pseudotag.children, pseudochildren)
1942 " s:ProcessPseudoChildren() {{{2
1943 function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo, fileinfo)
1944 for childtag in a:tag.children
1945 let childtag.parent = a:tag
1947 if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
1951 if !has_key(childtag, 'children')
1952 let childtag.children = []
1955 call s:AddScopedTags(a:tags, childtag.children, childtag, a:depth + 1,
1956 \ a:typeinfo, a:fileinfo)
1959 let is_grandchild = 'v:val.depth > a:depth &&
1960 \ match(v:val.path, ''^\C'' . a:tag.fullpath) == 0'
1961 let grandchildren = filter(copy(a:tags), is_grandchild)
1962 if !empty(grandchildren)
1963 call s:AddScopedTags(a:tags, a:tag.children, a:tag, a:depth + 1,
1964 \ a:typeinfo, a:fileinfo)
1968 " s:CreatePseudoTag() {{{2
1969 function! s:CreatePseudoTag(name, parent, scope, typeinfo, fileinfo)
1971 let curpath = a:parent.fullpath
1972 let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
1978 let pseudotag = s:PseudoTag.New(a:name)
1979 let pseudotag.fields.kind = a:typeinfo.scope2kind[a:scope]
1981 let parentscope = substitute(curpath, a:name . '$', '', '')
1982 let parentscope = substitute(parentscope,
1983 \ '\V\^' . a:typeinfo.sro . '\$', '', '')
1986 let pseudotag.fields[pscope] = parentscope
1987 let pseudotag.scope = pscope
1988 let pseudotag.path = parentscope
1989 let pseudotag.fullpath =
1990 \ pseudotag.path . a:typeinfo.sro . pseudotag.name
1992 let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
1994 let pseudotag.parent = a:parent
1996 let pseudotag.fileinfo = a:fileinfo
1998 call pseudotag.initFoldState()
2005 function! s:SortTags(tags, comparemethod)
2006 call sort(a:tags, a:comparemethod)
2009 if has_key(tag, 'children')
2010 call s:SortTags(tag.children, a:comparemethod)
2015 " s:CompareByKind() {{{2
2016 function! s:CompareByKind(tag1, tag2)
2017 let typeinfo = s:compare_typeinfo
2019 if typeinfo.kinddict[a:tag1.fields.kind] <#
2020 \ typeinfo.kinddict[a:tag2.fields.kind]
2022 elseif typeinfo.kinddict[a:tag1.fields.kind] >#
2023 \ typeinfo.kinddict[a:tag2.fields.kind]
2026 " Ignore '~' prefix for C++ destructors to sort them directly under
2028 if a:tag1.name[0] ==# '~'
2029 let name1 = a:tag1.name[1:]
2031 let name1 = a:tag1.name
2033 if a:tag2.name[0] ==# '~'
2034 let name2 = a:tag2.name[1:]
2036 let name2 = a:tag2.name
2047 " s:CompareByLine() {{{2
2048 function! s:CompareByLine(tag1, tag2)
2049 return a:tag1.fields.line - a:tag2.fields.line
2052 " s:ToggleSort() {{{2
2053 function! s:ToggleSort()
2054 let fileinfo = s:known_files.getCurrent()
2059 let curline = line('.')
2063 let s:compare_typeinfo = s:known_types[fileinfo.ftype]
2065 if has_key(s:compare_typeinfo, 'sort')
2066 let s:compare_typeinfo.sort = !s:compare_typeinfo.sort
2068 let g:tagbar_sort = !g:tagbar_sort
2071 call fileinfo.sortTags()
2073 call s:RenderContent()
2079 " s:RenderContent() {{{2
2080 function! s:RenderContent(...)
2081 call s:LogDebugMessage('RenderContent called')
2086 let fileinfo = s:known_files.getCurrent()
2090 call s:LogDebugMessage('Empty fileinfo, returning')
2094 let tagbarwinnr = bufwinnr('__Tagbar__')
2096 if &filetype == 'tagbar'
2100 let prevwinnr = winnr()
2101 execute tagbarwinnr . 'wincmd w'
2104 if !empty(s:known_files.getCurrent()) &&
2105 \ fileinfo.fpath ==# s:known_files.getCurrent().fpath
2106 " We're redisplaying the same file, so save the view
2107 call s:LogDebugMessage('Redisplaying file' . fileinfo.fpath)
2108 let saveline = line('.')
2109 let savecol = col('.')
2110 let topline = line('w0')
2113 let lazyredraw_save = &lazyredraw
2115 let eventignore_save = &eventignore
2124 let typeinfo = s:known_types[fileinfo.ftype]
2127 call s:PrintKinds(typeinfo, fileinfo)
2129 " Delete empty lines at the end of the buffer
2130 for linenr in range(line('$'), 1, -1)
2131 if getline(linenr) =~ '^$'
2132 execute 'silent ' . linenr . 'delete _'
2138 setlocal nomodifiable
2140 if !empty(s:known_files.getCurrent()) &&
2141 \ fileinfo.fpath ==# s:known_files.getCurrent().fpath
2142 let scrolloff_save = &scrolloff
2145 call cursor(topline, 1)
2147 call cursor(saveline, savecol)
2149 let &scrolloff = scrolloff_save
2151 " Make sure as much of the Tagbar content as possible is shown in the
2152 " window by jumping to the top after drawing
2156 " Invalidate highlight cache from old file
2157 let s:last_highlight_tline = 0
2160 let &lazyredraw = lazyredraw_save
2161 let &eventignore = eventignore_save
2164 execute prevwinnr . 'wincmd w'
2168 " s:PrintKinds() {{{2
2169 function! s:PrintKinds(typeinfo, fileinfo)
2170 call s:LogDebugMessage('PrintKinds called')
2174 for kind in a:typeinfo.kinds
2175 let curtags = filter(copy(a:fileinfo.tags),
2176 \ 'v:val.fields.kind ==# kind.short')
2177 call s:LogDebugMessage('Printing kind: ' . kind.short .
2178 \ ', number of (top-level) tags: ' . len(curtags))
2184 if has_key(a:typeinfo, 'kind2scope') &&
2185 \ has_key(a:typeinfo.kind2scope, kind.short)
2188 if g:tagbar_compact && first_tag && s:short_help
2189 silent 0put =tag.str()
2191 silent put =tag.str()
2194 " Save the current tagbar line in the tag for easy
2195 " highlighting access
2196 let curline = line('.')
2197 let tag.tline = curline
2198 let a:fileinfo.tline[curline] = tag
2201 if tag.isFoldable() && !tag.isFolded()
2202 for ckind in a:typeinfo.kinds
2203 let childtags = filter(copy(tag.children),
2204 \ 'v:val.fields.kind ==# ckind.short')
2205 if len(childtags) > 0
2206 " Print 'kind' header of following children
2207 if !has_key(a:typeinfo.kind2scope, ckind.short)
2208 silent put =' [' . ckind.long . ']'
2209 let a:fileinfo.tline[line('.')] = tag
2211 for childtag in childtags
2212 call s:PrintTag(childtag, 1,
2213 \ a:fileinfo, a:typeinfo)
2219 if !g:tagbar_compact
2227 let kindtag = curtags[0].parent
2229 if kindtag.isFolded()
2230 let foldmarker = s:icon_closed
2232 let foldmarker = s:icon_open
2235 if g:tagbar_compact && first_tag && s:short_help
2236 silent 0put =foldmarker . ' ' . kind.long
2238 silent put =foldmarker . ' ' . kind.long
2241 let curline = line('.')
2242 let kindtag.tline = curline
2243 let a:fileinfo.tline[curline] = kindtag
2245 if !kindtag.isFolded()
2248 silent put =' ' . str
2250 " Save the current tagbar line in the tag for easy
2251 " highlighting access
2252 let curline = line('.')
2253 let tag.tline = curline
2254 let a:fileinfo.tline[curline] = tag
2259 if !g:tagbar_compact
2269 function! s:PrintTag(tag, depth, fileinfo, typeinfo)
2270 " Print tag indented according to depth
2271 silent put =repeat(' ', a:depth * 2) . a:tag.str()
2273 " Save the current tagbar line in the tag for easy
2274 " highlighting access
2275 let curline = line('.')
2276 let a:tag.tline = curline
2277 let a:fileinfo.tline[curline] = a:tag
2279 " Recursively print children
2280 if a:tag.isFoldable() && !a:tag.isFolded()
2281 for ckind in a:typeinfo.kinds
2282 let childtags = filter(copy(a:tag.children),
2283 \ 'v:val.fields.kind ==# ckind.short')
2284 if len(childtags) > 0
2285 " Print 'kind' header of following children
2286 if !has_key(a:typeinfo.kind2scope, ckind.short)
2287 silent put =' ' . repeat(' ', a:depth * 2) .
2288 \ '[' . ckind.long . ']'
2289 let a:fileinfo.tline[line('.')] = a:tag
2291 for childtag in childtags
2292 call s:PrintTag(childtag, a:depth + 1,
2293 \ a:fileinfo, a:typeinfo)
2300 " s:PrintHelp() {{{2
2301 function! s:PrintHelp()
2302 if !g:tagbar_compact && s:short_help
2303 silent 0put ='\" Press <F1> for help'
2305 elseif !s:short_help
2306 silent 0put ='\" Tagbar keybindings'
2308 silent put ='\" --------- General ---------'
2309 silent put ='\" <Enter> : Jump to tag definition'
2310 silent put ='\" <Space> : Display tag prototype'
2312 silent put ='\" ---------- Folds ----------'
2313 silent put ='\" +, zo : Open fold'
2314 silent put ='\" -, zc : Close fold'
2315 silent put ='\" o, za : Toggle fold'
2316 silent put ='\" *, zR : Open all folds'
2317 silent put ='\" =, zM : Close all folds'
2319 silent put ='\" ---------- Misc -----------'
2320 silent put ='\" s : Toggle sort'
2321 silent put ='\" x : Zoom window in/out'
2322 silent put ='\" q : Close window'
2323 silent put ='\" <F1> : Remove help'
2328 " s:RenderKeepView() {{{2
2329 " The gist of this function was taken from NERDTree by Martin Grenfell.
2330 function! s:RenderKeepView(...)
2334 let line = line('.')
2337 let curcol = col('.')
2338 let topline = line('w0')
2340 call s:RenderContent()
2342 let scrolloff_save = &scrolloff
2345 call cursor(topline, 1)
2347 call cursor(line, curcol)
2349 let &scrolloff = scrolloff_save
2355 " s:HighlightTag() {{{2
2356 function! s:HighlightTag()
2359 let tag = s:GetNearbyTag()
2361 let tagline = tag.tline
2364 " Don't highlight the tag again if it's the same one as last time.
2365 " This prevents the Tagbar window from jumping back after scrolling with
2367 if tagline == s:last_highlight_tline
2370 let s:last_highlight_tline = tagline
2373 let eventignore_save = &eventignore
2376 let tagbarwinnr = bufwinnr('__Tagbar__')
2377 let prevwinnr = winnr()
2378 execute tagbarwinnr . 'wincmd w'
2382 " No tag above cursor position so don't do anything
2384 execute prevwinnr . 'wincmd w'
2385 let &eventignore = eventignore_save
2390 if g:tagbar_autoshowtag
2391 call s:OpenParents(tag)
2394 " Check whether the tag is inside a closed fold and highlight the parent
2395 " instead in that case
2396 let tagline = tag.getClosedParentTline()
2398 " Go to the line containing the tag
2401 " Make sure the tag is visible in the window
2404 let foldpat = '[' . s:icon_open . s:icon_closed . ' ]'
2405 let pattern = '/^\%' . tagline . 'l\s*' . foldpat . '[-+# ]\zs[^( ]\+\ze/'
2406 execute 'match TagbarHighlight ' . pattern
2408 execute prevwinnr . 'wincmd w'
2410 let &eventignore = eventignore_save
2415 " s:JumpToTag() {{{2
2416 function! s:JumpToTag(stay_in_tagbar)
2417 let taginfo = s:GetTagInfo(line('.'), 1)
2419 let autoclose = w:autoclose
2421 if empty(taginfo) || has_key(taginfo, 'numtags')
2425 let tagbarwinnr = winnr()
2427 let eventignore_save = &eventignore
2430 " This elaborate construct will try to switch to the correct
2431 " buffer/window; if the buffer isn't currently shown in a window it will
2432 " open it in the first window with a non-special buffer in it
2434 let filebufnr = bufnr(taginfo.fileinfo.fpath)
2435 if bufnr('%') != filebufnr
2436 let filewinnr = bufwinnr(filebufnr)
2438 execute filewinnr . 'wincmd w'
2440 for i in range(1, winnr('$'))
2441 execute i . 'wincmd w'
2443 execute 'buffer ' . filebufnr
2448 " To make ctrl-w_p work we switch between the Tagbar window and the
2449 " correct window once
2450 execute tagbarwinnr . 'wincmd w'
2454 " Mark current position so it can be jumped back to
2457 " Jump to the line where the tag is defined. Don't use the search pattern
2458 " since it doesn't take the scope into account and thus can fail if tags
2459 " with the same name are defined in different scopes (e.g. classes)
2460 execute taginfo.fields.line
2462 " If the file has been changed but not saved, the tag may not be on the
2463 " saved line anymore, so search for it in the vicinity of the saved line
2464 if match(getline('.'), taginfo.pattern) == -1
2467 while search(taginfo.pattern, 'W' . forward ? '' : 'b') == 0
2469 if interval > line('$')
2472 let interval = interval * 2
2475 let forward = !forward
2479 " If the tag is on a different line after unsaved changes update the tag
2480 " and file infos/objects
2481 let curline = line('.')
2482 if taginfo.fields.line != curline
2483 let taginfo.fields.line = curline
2484 let taginfo.fileinfo.fline[curline] = taginfo
2487 " Center the tag in the window
2490 if foldclosed('.') != -1
2496 let &eventignore = eventignore_save
2499 call s:HighlightTag()
2500 execute tagbarwinnr . 'wincmd w'
2501 elseif g:tagbar_autoclose || autoclose
2502 call s:CloseWindow()
2504 call s:HighlightTag()
2508 " s:ShowPrototype() {{{2
2509 function! s:ShowPrototype()
2510 let taginfo = s:GetTagInfo(line('.'), 1)
2516 echo taginfo.getPrototype()
2519 " s:ToggleHelp() {{{2
2520 function! s:ToggleHelp()
2521 let s:short_help = !s:short_help
2523 " Prevent highlighting from being off after adding/removing the help text
2526 call s:RenderContent()
2532 " s:GotoNextToplevelTag() {{{2
2533 function! s:GotoNextToplevelTag(direction)
2534 let curlinenr = line('.')
2535 let newlinenr = line('.')
2538 let range = range(line('.') + 1, line('$'))
2540 let range = range(line('.') - 1, 1, -1)
2543 for tmplinenr in range
2544 let taginfo = s:GetTagInfo(tmplinenr, 0)
2548 elseif empty(taginfo.parent)
2549 let newlinenr = tmplinenr
2554 if curlinenr != newlinenr
2564 function! s:OpenFold()
2565 let fileinfo = s:known_files.getCurrent()
2570 let curline = line('.')
2572 let tag = s:GetTagInfo(curline, 0)
2579 call s:RenderKeepView()
2582 " s:CloseFold() {{{2
2583 function! s:CloseFold()
2584 let fileinfo = s:known_files.getCurrent()
2591 let curline = line('.')
2593 let curtag = s:GetTagInfo(curline, 0)
2598 let newline = curtag.closeFold()
2600 call s:RenderKeepView(newline)
2603 " s:ToggleFold() {{{2
2604 function! s:ToggleFold()
2605 let fileinfo = s:known_files.getCurrent()
2612 let curtag = s:GetTagInfo(line('.'), 0)
2617 let newline = line('.')
2619 if curtag.isKindheader()
2620 call curtag.toggleFold()
2621 elseif curtag.isFoldable()
2622 if curtag.isFolded()
2623 call curtag.openFold()
2625 let newline = curtag.closeFold()
2628 let newline = curtag.closeFold()
2631 call s:RenderKeepView(newline)
2634 " s:SetFoldLevel() {{{2
2635 function! s:SetFoldLevel(level)
2637 echoerr 'Foldlevel can''t be negative'
2641 let fileinfo = s:known_files.getCurrent()
2646 call s:SetFoldLevelRecursive(fileinfo, fileinfo.tags, a:level)
2648 let typeinfo = s:known_types[fileinfo.ftype]
2650 " Apply foldlevel to 'kind's
2652 for kind in typeinfo.kinds
2653 call fileinfo.closeKindFold(kind)
2656 for kind in typeinfo.kinds
2657 call fileinfo.openKindFold(kind)
2661 let fileinfo.foldlevel = a:level
2663 call s:RenderContent()
2666 " s:SetFoldLevelRecursive() {{{2
2667 " Apply foldlevel to normal tags
2668 function! s:SetFoldLevelRecursive(fileinfo, tags, level)
2670 if tag.depth >= a:level
2671 call tag.setFolded(1)
2673 call tag.setFolded(0)
2676 if has_key(tag, 'children')
2677 call s:SetFoldLevelRecursive(a:fileinfo, tag.children, a:level)
2682 " s:OpenParents() {{{2
2683 function! s:OpenParents(...)
2689 let tag = s:GetNearbyTag()
2692 call tag.openParents()
2694 call s:RenderKeepView()
2697 " Helper functions {{{1
2699 function! s:CleanUp()
2700 silent autocmd! TagbarAutoCmds
2702 unlet s:is_maximized
2703 unlet s:compare_typeinfo
2707 " s:CleanupFileinfo() {{{2
2708 function! s:CleanupFileinfo(fname)
2709 call s:known_files.rm(a:fname)
2712 " s:QuitIfOnlyWindow() {{{2
2713 function! s:QuitIfOnlyWindow()
2714 " Before quitting Vim, delete the tagbar buffer so that
2715 " the '0 mark is correctly set to the previous buffer.
2716 if winbufnr(2) == -1
2717 " Check if there is more than one tab page
2718 if tabpagenr('$') == 1
2727 " s:AutoUpdate() {{{2
2728 function! s:AutoUpdate(fname)
2729 call s:LogDebugMessage('AutoUpdate called on ' . a:fname)
2731 " Don't do anything if tagbar is not open or if we're in the tagbar window
2732 let tagbarwinnr = bufwinnr('__Tagbar__')
2733 if tagbarwinnr == -1 || &filetype == 'tagbar'
2734 call s:LogDebugMessage('Tagbar window not open or in Tagbar window')
2738 " Only consider the main filetype in cases like 'python.django'
2739 let ftype = get(split(&filetype, '\.'), 0, '')
2740 call s:LogDebugMessage('Vim filetype: ' . &filetype .
2741 \ ', sanitized filetype: ' . ftype)
2743 " Don't do anything if the file isn't supported
2744 if !s:IsValidFile(a:fname, ftype)
2745 call s:LogDebugMessage('Not a valid file, stopping processing')
2749 " Process the file if it's unknown or the information is outdated
2750 " Also test for entries that exist but are empty, which will be the case
2751 " if there was an error during the ctags execution
2752 if s:known_files.has(a:fname) && !empty(s:known_files.get(a:fname))
2753 if s:known_files.get(a:fname).mtime != getftime(a:fname)
2754 call s:LogDebugMessage('Filedata outdated, updating ' . a:fname)
2755 call s:ProcessFile(a:fname, ftype)
2757 elseif !s:known_files.has(a:fname)
2758 call s:LogDebugMessage('Unknown file, processing ' . a:fname)
2759 call s:ProcessFile(a:fname, ftype)
2762 let fileinfo = s:known_files.get(a:fname)
2764 " If we don't have an entry for the file by now something must have gone
2765 " wrong, so don't change the tagbar content
2767 call s:LogDebugMessage('fileinfo empty after processing: ' . a:fname)
2771 " Display the tagbar content
2772 call s:RenderContent(fileinfo)
2774 " Call setCurrent after rendering so RenderContent can check whether the
2775 " same file is redisplayed
2777 call s:LogDebugMessage('Setting current file to ' . a:fname)
2778 call s:known_files.setCurrent(fileinfo)
2781 call s:HighlightTag()
2782 call s:LogDebugMessage('AutoUpdate finished successfully')
2785 " s:IsValidFile() {{{2
2786 function! s:IsValidFile(fname, ftype)
2787 if a:fname == '' || a:ftype == ''
2788 call s:LogDebugMessage('Empty filename or type')
2792 if !filereadable(a:fname)
2793 call s:LogDebugMessage('File not readable')
2797 if !has_key(s:known_types, a:ftype)
2798 call s:LogDebugMessage('Unsupported filetype: ' . a:ftype)
2805 " s:EscapeCtagsCmd() {{{2
2806 " Assemble the ctags command line in a way that all problematic characters are
2807 " properly escaped and converted to the system's encoding
2808 " Optional third parameter is a file name to run ctags on
2809 function! s:EscapeCtagsCmd(ctags_bin, args, ...)
2810 call s:LogDebugMessage('EscapeCtagsCmd called')
2811 call s:LogDebugMessage('ctags_bin: ' . a:ctags_bin)
2812 call s:LogDebugMessage('ctags_args: ' . a:args)
2814 if exists('+shellslash')
2815 let shellslash_save = &shellslash
2820 let fname = shellescape(a:1)
2825 let ctags_cmd = shellescape(a:ctags_bin) . ' ' . a:args . ' ' . fname
2827 if exists('+shellslash')
2828 let &shellslash = shellslash_save
2831 " Needed for cases where 'encoding' is different from the system's
2833 if g:tagbar_systemenc != &encoding
2834 let ctags_cmd = iconv(ctags_cmd, &encoding, g:tagbar_systemenc)
2836 let ctags_cmd = iconv(ctags_cmd, &encoding, $LANG)
2839 call s:LogDebugMessage('Escaped ctags command: ' . ctags_cmd)
2842 echoerr 'Tagbar: Encoding conversion failed!'
2843 \ 'Please make sure your system is set up correctly'
2844 \ 'and that Vim is compiled with the "iconv" feature.'
2850 " s:ExecuteCtags() {{{2
2851 " Execute ctags with necessary shell settings
2852 " Partially based on the discussion at
2853 " http://vim.1045645.n5.nabble.com/bad-default-shellxquote-in-Widows-td1208284.html
2854 function! s:ExecuteCtags(ctags_cmd)
2855 if exists('+shellslash')
2856 let shellslash_save = &shellslash
2860 if &shell =~ 'cmd\.exe'
2861 let shellxquote_save = &shellxquote
2863 let shellcmdflag_save = &shellcmdflag
2864 set shellcmdflag=/s\ /c
2867 let ctags_output = system(a:ctags_cmd)
2869 if &shell =~ 'cmd\.exe'
2870 let &shellxquote = shellxquote_save
2871 let &shellcmdflag = shellcmdflag_save
2874 if exists('+shellslash')
2875 let &shellslash = shellslash_save
2881 " s:GetTagInfo() {{{2
2882 " Return the info dictionary of the tag on the specified line. If the line
2883 " does not contain a valid tag (for example because it is empty or only
2884 " contains a pseudo-tag) return an empty dictionary.
2885 function! s:GetTagInfo(linenr, ignorepseudo)
2886 let fileinfo = s:known_files.getCurrent()
2892 " Don't do anything in empty and comment lines
2893 let curline = getline(a:linenr)
2894 if curline =~ '^\s*$' || curline[0] == '"'
2898 " Check if there is a tag on the current line
2899 if !has_key(fileinfo.tline, a:linenr)
2903 let taginfo = fileinfo.tline[a:linenr]
2905 " Check if the current tag is not a pseudo-tag
2906 if a:ignorepseudo && taginfo.isPseudoTag()
2913 " s:GetNearbyTag() {{{2
2914 " Get the tag info for a file near the cursor in the current file
2915 function! s:GetNearbyTag()
2916 let fileinfo = s:known_files.getCurrent()
2918 let curline = line('.')
2921 " If a tag appears in a file more than once (for example namespaces in
2922 " C++) only one of them has a 'tline' entry and can thus be highlighted.
2923 " The only way to solve this would be to go over the whole tag list again,
2924 " making everything slower. Since this should be a rare occurence and
2925 " highlighting isn't /that/ important ignore it for now.
2926 for line in range(curline, 1, -1)
2927 if has_key(fileinfo.fline, line)
2928 let tag = fileinfo.fline[line]
2936 " s:CheckMouseClick() {{{2
2937 function! s:CheckMouseClick()
2938 let line = getline('.')
2939 let curcol = col('.')
2941 if (match(line, s:icon_open . '[-+ ]') + 1) == curcol
2943 elseif (match(line, s:icon_closed . '[-+ ]') + 1) == curcol
2945 elseif g:tagbar_singleclick
2950 " s:DetermineFiletype() {{{2
2951 function! s:DetectFiletype(bufnr)
2952 " Filetype has already been detected for loaded buffers, but not
2953 " necessarily for unloaded ones
2954 let ftype = getbufvar(a:bufnr, '&filetype')
2956 if bufloaded(a:bufnr)
2964 " Unloaded buffer with non-detected filetype, need to detect filetype
2966 let bufname = bufname(a:bufnr)
2968 let eventignore_save = &eventignore
2969 set eventignore=FileType
2970 let filetype_save = &filetype
2972 exe 'doautocmd filetypedetect BufRead ' . bufname
2973 let ftype = &filetype
2975 let &filetype = filetype_save
2976 let &eventignore = eventignore_save
2982 " TagbarBalloonExpr() {{{2
2983 function! TagbarBalloonExpr()
2984 let taginfo = s:GetTagInfo(v:beval_lnum, 1)
2990 return taginfo.getPrototype()
2993 " TagbarGenerateStatusline() {{{2
2994 function! TagbarGenerateStatusline()
2998 let text = '[Order]'
3001 if !empty(s:known_files.getCurrent())
3002 let filename = fnamemodify(s:known_files.getCurrent().fpath, ':t')
3003 let text .= ' ' . filename
3010 " s:StartDebug() {{{2
3011 function! s:StartDebug(filename)
3012 if empty(a:filename)
3013 let s:debug_file = 'tagbardebug.log'
3015 let s:debug_file = a:filename
3019 exe 'redir! > ' . s:debug_file
3022 " Check whether the log file could be created
3023 if !filewritable(s:debug_file)
3024 echomsg 'Tagbar: Unable to create log file ' . s:debug_file
3025 let s:debug_file = ''
3032 " s:StopDebug() {{{2
3033 function! s:StopDebug()
3035 let s:debug_file = ''
3038 " s:LogDebugMessage() {{{2
3039 function! s:LogDebugMessage(msg)
3041 exe 'redir >> ' . s:debug_file
3042 silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n"
3047 " Autoload functions {{{1
3048 function! tagbar#ToggleWindow()
3049 call s:ToggleWindow()
3052 function! tagbar#OpenWindow(...)
3053 let flags = a:0 > 0 ? a:1 : ''
3054 call s:OpenWindow(flags)
3057 function! tagbar#CloseWindow()
3058 call s:CloseWindow()
3061 function! tagbar#SetFoldLevel(...)
3062 call s:SetFoldLevel(a:1)
3065 function! tagbar#OpenParents()
3066 call s:OpenParents()
3069 function! tagbar#StartDebug(...)
3070 let filename = a:0 > 0 ? a:1 : ''
3071 call s:StartDebug(filename)
3074 function! tagbar#StopDebug()
3078 function! tagbar#RestoreSession()
3079 call s:RestoreSession()
3082 " Automatically open Tagbar if one of the open buffers contains a supported
3084 function! tagbar#autoopen()
3087 for bufnr in range(1, bufnr('$'))
3089 let ftype = s:DetectFiletype(bufnr)
3090 if s:IsValidFile(bufname(bufnr), ftype)
3091 call s:OpenWindow('')
3099 " vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1