]> git.r.bdr.sh - rbdr/dotfiles/blame - vim/autoload/tagbar.vim
A whole bunch of new additions to the submodules
[rbdr/dotfiles] / vim / autoload / tagbar.vim
CommitLineData
0d23b6e5
BB
1" ============================================================================
2" File: tagbar.vim
3" Description: List the current file's tags in a sidebar, ordered by class etc
4" Author: Jan Larres <jan@majutsushi.net>
5" Licence: Vim licence
6" Website: http://majutsushi.github.com/tagbar/
7" Version: 2.3
8" Note: This plugin was heavily inspired by the 'Taglist' plugin by
9" Yegappan Lakshmanan and uses a small amount of code from it.
10"
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" ============================================================================
20
21scriptencoding utf-8
22
23" Initialization {{{1
24
25" Basic init {{{2
26
27if !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'
46 else
47 echomsg 'Tagbar: Exuberant ctags not found, skipping plugin'
48 finish
49 endif
50else
51 " reset 'wildignore' temporarily in case *.exe is included in it
52 let wildignore_save = &wildignore
53 set wildignore&
54
55 let g:tagbar_ctags_bin = expand(g:tagbar_ctags_bin)
56
57 let &wildignore = wildignore_save
58
59 if !executable(g:tagbar_ctags_bin)
60 echomsg 'Tagbar: Exuberant ctags not found in specified place,'
61 \ 'skipping plugin'
62 finish
63 endif
64endif
65
66redir => s:ftype_out
67silent filetype
68redir END
69if s:ftype_out !~# 'detection:ON'
70 echomsg 'Tagbar: Filetype detection is turned off, skipping plugin'
71 unlet s:ftype_out
72 finish
73endif
74unlet s:ftype_out
75
76let s:icon_closed = g:tagbar_iconchars[0]
77let s:icon_open = g:tagbar_iconchars[1]
78
79let s:type_init_done = 0
80let s:autocommands_done = 0
81let s:checked_ctags = 0
82let s:window_expanded = 0
83
84let s:access_symbols = {
85 \ 'public' : '+',
86 \ 'protected' : '#',
87 \ 'private' : '-'
88\ }
89
90let g:loaded_tagbar = 1
91
92let s:last_highlight_tline = 0
93let s:debug = 0
94let s:debug_file = ''
95
96" s:Init() {{{2
97function! s:Init()
98 if !s:type_init_done
99 call s:InitTypes()
100 endif
101
102 if !s:checked_ctags
103 if !s:CheckForExCtags()
104 return
105 endif
106 endif
107endfunction
108
109" s:InitTypes() {{{2
110function! s:InitTypes()
111 call s:LogDebugMessage('Initializing types')
112
113 let s:known_types = {}
114
115 " Ant {{{3
116 let type_ant = {}
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}
121 \ ]
122 let s:known_types.ant = type_ant
123 " Asm {{{3
124 let type_asm = {}
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}
131 \ ]
132 let s:known_types.asm = type_asm
133 " ASP {{{3
134 let type_aspvbs = {}
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}
142 \ ]
143 let s:known_types.aspvbs = type_aspvbs
144 " Awk {{{3
145 let type_awk = {}
146 let type_awk.ctagstype = 'awk'
147 let type_awk.kinds = [
148 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
149 \ ]
150 let s:known_types.awk = type_awk
151 " Basic {{{3
152 let type_basic = {}
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}
161 \ ]
162 let s:known_types.basic = type_basic
163 " BETA {{{3
164 let type_beta = {}
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}
170 \ ]
171 let s:known_types.beta = type_beta
172 " C {{{3
173 let type_c = {}
174 let type_c.ctagstype = 'c'
175 let type_c.kinds = [
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}
186 \ ]
187 let type_c.sro = '::'
188 let type_c.kind2scope = {
189 \ 'g' : 'enum',
190 \ 's' : 'struct',
191 \ 'u' : 'union'
192 \ }
193 let type_c.scope2kind = {
194 \ 'enum' : 'g',
195 \ 'struct' : 's',
196 \ 'union' : 'u'
197 \ }
198 let s:known_types.c = type_c
199 " C++ {{{3
200 let type_cpp = {}
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}
215 \ ]
216 let type_cpp.sro = '::'
217 let type_cpp.kind2scope = {
218 \ 'g' : 'enum',
219 \ 'n' : 'namespace',
220 \ 'c' : 'class',
221 \ 's' : 'struct',
222 \ 'u' : 'union'
223 \ }
224 let type_cpp.scope2kind = {
225 \ 'enum' : 'g',
226 \ 'namespace' : 'n',
227 \ 'class' : 'c',
228 \ 'struct' : 's',
229 \ 'union' : 'u'
230 \ }
231 let s:known_types.cpp = type_cpp
232 " C# {{{3
233 let type_cs = {}
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}
248 \ ]
249 let type_cs.sro = '.'
250 let type_cs.kind2scope = {
251 \ 'n' : 'namespace',
252 \ 'i' : 'interface',
253 \ 'c' : 'class',
254 \ 's' : 'struct',
255 \ 'g' : 'enum'
256 \ }
257 let type_cs.scope2kind = {
258 \ 'namespace' : 'n',
259 \ 'interface' : 'i',
260 \ 'class' : 'c',
261 \ 'struct' : 's',
262 \ 'enum' : 'g'
263 \ }
264 let s:known_types.cs = type_cs
265 " COBOL {{{3
266 let type_cobol = {}
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}
275 \ ]
276 let s:known_types.cobol = type_cobol
277 " DOS Batch {{{3
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}
283 \ ]
284 let s:known_types.dosbatch = type_dosbatch
285 " Eiffel {{{3
286 let type_eiffel = {}
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}
291 \ ]
292 let type_eiffel.sro = '.' " Not sure, is nesting even possible?
293 let type_eiffel.kind2scope = {
294 \ 'c' : 'class',
295 \ 'f' : 'feature'
296 \ }
297 let type_eiffel.scope2kind = {
298 \ 'class' : 'c',
299 \ 'feature' : 'f'
300 \ }
301 let s:known_types.eiffel = type_eiffel
302 " Erlang {{{3
303 let type_erlang = {}
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}
310 \ ]
311 let type_erlang.sro = '.' " Not sure, is nesting even possible?
312 let type_erlang.kind2scope = {
313 \ 'm' : 'module'
314 \ }
315 let type_erlang.scope2kind = {
316 \ 'module' : 'm'
317 \ }
318 let s:known_types.erlang = type_erlang
319 " Flex {{{3
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!
324 let type_mxml = {}
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}
333 \ ]
334 let type_mxml.sro = '.'
335 let type_mxml.kind2scope = {
336 \ 'c' : 'class'
337 \ }
338 let type_mxml.scope2kind = {
339 \ 'class' : 'c'
340 \ }
341 let s:known_types.mxml = type_mxml
342 " Fortran {{{3
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}
358 \ ]
359 let type_fortran.sro = '.' " Not sure, is nesting even possible?
360 let type_fortran.kind2scope = {
361 \ 'm' : 'module',
362 \ 'p' : 'program',
363 \ 'f' : 'function',
364 \ 's' : 'subroutine'
365 \ }
366 let type_fortran.scope2kind = {
367 \ 'module' : 'm',
368 \ 'program' : 'p',
369 \ 'function' : 'f',
370 \ 'subroutine' : 's'
371 \ }
372 let s:known_types.fortran = type_fortran
373 " HTML {{{3
374 let type_html = {}
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}
379 \ ]
380 let s:known_types.html = type_html
381 " Java {{{3
382 let type_java = {}
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}
392 \ ]
393 let type_java.sro = '.'
394 let type_java.kind2scope = {
395 \ 'g' : 'enum',
396 \ 'i' : 'interface',
397 \ 'c' : 'class'
398 \ }
399 let type_java.scope2kind = {
400 \ 'enum' : 'g',
401 \ 'interface' : 'i',
402 \ 'class' : 'c'
403 \ }
404 let s:known_types.java = type_java
405 " JavaScript {{{3
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')
413 if jsctags != ''
414 let type_javascript.kinds = [
415 \ {'short' : 'v', 'long' : 'variables', 'fold' : 0},
416 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
417 \ ]
418 let type_javascript.sro = '.'
419 let type_javascript.kind2scope = {
420 \ 'v' : 'namespace',
421 \ 'f' : 'namespace'
422 \ }
423 let type_javascript.scope2kind = {
424 \ 'namespace' : 'v'
425 \ }
426 let type_javascript.ctagsbin = jsctags
427 let type_javascript.ctagsargs = '-f -'
428 else
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}
435 \ ]
436 endif
437 let s:known_types.javascript = type_javascript
438 " Lisp {{{3
439 let type_lisp = {}
440 let type_lisp.ctagstype = 'lisp'
441 let type_lisp.kinds = [
442 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
443 \ ]
444 let s:known_types.lisp = type_lisp
445 " Lua {{{3
446 let type_lua = {}
447 let type_lua.ctagstype = 'lua'
448 let type_lua.kinds = [
449 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
450 \ ]
451 let s:known_types.lua = type_lua
452 " Make {{{3
453 let type_make = {}
454 let type_make.ctagstype = 'make'
455 let type_make.kinds = [
456 \ {'short' : 'm', 'long' : 'macros', 'fold' : 0}
457 \ ]
458 let s:known_types.make = type_make
459 " Matlab {{{3
460 let type_matlab = {}
461 let type_matlab.ctagstype = 'matlab'
462 let type_matlab.kinds = [
463 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
464 \ ]
465 let s:known_types.matlab = type_matlab
466 " Ocaml {{{3
467 let type_ocaml = {}
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}
479 \ ]
480 let type_ocaml.sro = '.' " Not sure, is nesting even possible?
481 let type_ocaml.kind2scope = {
482 \ 'M' : 'Module',
483 \ 'c' : 'class',
484 \ 't' : 'type'
485 \ }
486 let type_ocaml.scope2kind = {
487 \ 'Module' : 'M',
488 \ 'class' : 'c',
489 \ 'type' : 't'
490 \ }
491 let s:known_types.ocaml = type_ocaml
492 " Pascal {{{3
493 let type_pascal = {}
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}
498 \ ]
499 let s:known_types.pascal = type_pascal
500 " Perl {{{3
501 let type_perl = {}
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}
509 \ ]
510 let s:known_types.perl = type_perl
511 " PHP {{{3
512 let type_php = {}
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}
521 \ ]
522 let s:known_types.php = type_php
523 " Python {{{3
524 let type_python = {}
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}
532 \ ]
533 let type_python.sro = '.'
534 let type_python.kind2scope = {
535 \ 'c' : 'class',
536 \ 'f' : 'function',
537 \ 'm' : 'function'
538 \ }
539 let type_python.scope2kind = {
540 \ 'class' : 'c',
541 \ 'function' : 'f'
542 \ }
543 let s:known_types.python = type_python
544 " REXX {{{3
545 let type_rexx = {}
546 let type_rexx.ctagstype = 'rexx'
547 let type_rexx.kinds = [
548 \ {'short' : 's', 'long' : 'subroutines', 'fold' : 0}
549 \ ]
550 let s:known_types.rexx = type_rexx
551 " Ruby {{{3
552 let type_ruby = {}
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}
559 \ ]
560 let type_ruby.sro = '.'
561 let type_ruby.kind2scope = {
562 \ 'c' : 'class',
563 \ 'm' : 'class'
564 \ }
565 let type_ruby.scope2kind = {
566 \ 'class' : 'c'
567 \ }
568 let s:known_types.ruby = type_ruby
569 " Scheme {{{3
570 let type_scheme = {}
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}
575 \ ]
576 let s:known_types.scheme = type_scheme
577 " Shell script {{{3
578 let type_sh = {}
579 let type_sh.ctagstype = 'sh'
580 let type_sh.kinds = [
581 \ {'short' : 'f', 'long' : 'functions', 'fold' : 0}
582 \ ]
583 let s:known_types.sh = type_sh
584 let s:known_types.csh = type_sh
585 let s:known_types.zsh = type_sh
586 " SLang {{{3
587 let type_slang = {}
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}
592 \ ]
593 let s:known_types.slang = type_slang
594 " SML {{{3
595 let type_sml = {}
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}
605 \ ]
606 let s:known_types.sml = type_sml
607 " SQL {{{3
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
610 " welcome!
611 let type_sql = {}
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}
633 \ ]
634 let s:known_types.sql = type_sql
635 " Tcl {{{3
636 let type_tcl = {}
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}
642 \ ]
643 let s:known_types.tcl = type_tcl
644 " LaTeX {{{3
645 let type_tex = {}
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}
655 \ ]
656 let s:known_types.tex = type_tex
657 " Vera {{{3
658 " Why are variables 'virtual'?
659 let type_vera = {}
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}
672 \ ]
673 let type_vera.sro = '.' " Nesting doesn't seem to be possible
674 let type_vera.kind2scope = {
675 \ 'g' : 'enum',
676 \ 'c' : 'class',
677 \ 'v' : 'virtual'
678 \ }
679 let type_vera.scope2kind = {
680 \ 'enum' : 'g',
681 \ 'class' : 'c',
682 \ 'virtual' : 'v'
683 \ }
684 let s:known_types.vera = type_vera
685 " Verilog {{{3
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}
697 \ ]
698 let s:known_types.verilog = type_verilog
699 " VHDL {{{3
700 " The VHDL ctags parser unfortunately doesn't generate proper scopes
701 let type_vhdl = {}
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}
712 \ ]
713 let s:known_types.vhdl = type_vhdl
714 " Vim {{{3
715 let type_vim = {}
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}
723 \ ]
724 let s:known_types.vim = type_vim
725 " YACC {{{3
726 let type_yacc = {}
727 let type_yacc.ctagstype = 'yacc'
728 let type_yacc.kinds = [
729 \ {'short' : 'l', 'long' : 'labels', 'fold' : 0}
730 \ ]
731 let s:known_types.yacc = type_yacc
732 " }}}3
733
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
739 else
740 call extend(s:known_types[key], value)
741 endif
742 endfor
743
744 " Create a dictionary of the kind order for fast
745 " access in sorting functions
746 for type in values(s:known_types)
747 let i = 0
748 let type.kinddict = {}
749 for kind in type.kinds
750 let type.kinddict[kind.short] = i
751 let i += 1
752 endfor
753 endfor
754
755 let s:type_init_done = 1
756endfunction
757
758" s:GetUserTypeDefs() {{{2
759function! s:GetUserTypeDefs()
760 call s:LogDebugMessage('Initializing user types')
761
762 redir => defs
763 silent execute 'let g:'
764 redir END
765
766 let deflist = split(defs, '\n')
767 call map(deflist, 'substitute(v:val, ''^\S\+\zs.*'', "", "")')
768 call filter(deflist, 'v:val =~ "^tagbar_type_"')
769
770 let defdict = {}
771 for defstr in deflist
772 let type = substitute(defstr, '^tagbar_type_', '', '')
773 execute 'let defdict["' . type . '"] = g:' . defstr
774 endfor
775
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
782 let def.kinds = []
783 for kind in 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]
788 else
789 let kinddict.fold = 0
790 endif
791 call add(def.kinds, kinddict)
792 endfor
793 endif
794
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
799 endfor
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
804 endfor
805 endif
806 endfor
807
808 return defdict
809endfunction
810
811" s:RestoreSession() {{{2
812" Properly restore Tagbar after a session got loaded
813function! s:RestoreSession()
814 call s:LogDebugMessage('Restoring session')
815
816 let tagbarwinnr = bufwinnr('__Tagbar__')
817 if tagbarwinnr == -1
818 " Tagbar wasn't open in the saved session, nothing to do
819 return
820 else
821 let in_tagbar = 1
822 if winnr() != tagbarwinnr
823 execute tagbarwinnr . 'wincmd w'
824 let in_tagbar = 0
825 endif
826 endif
827
828 call s:Init()
829
830 call s:InitWindow(g:tagbar_autoclose)
831
832 " Leave the Tagbar window and come back so the update event gets triggered
833 wincmd p
834 execute tagbarwinnr . 'wincmd w'
835
836 if !in_tagbar
837 wincmd p
838 endif
839endfunction
840
841" s:MapKeys() {{{2
842function! s:MapKeys()
843 call s:LogDebugMessage('Mapping keys')
844
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>
849
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>
854
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>
858
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>
867
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>
874
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>
879
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>
884endfunction
885
886" s:CreateAutocommands() {{{2
887function! s:CreateAutocommands()
888 call s:LogDebugMessage('Creating autocommands')
889
890 augroup TagbarAutoCmds
891 autocmd!
892 autocmd BufEnter __Tagbar__ nested call s:QuitIfOnlyWindow()
893 autocmd BufUnload __Tagbar__ call s:CleanUp()
894 autocmd CursorHold __Tagbar__ call s:ShowPrototype()
895
896 autocmd BufWritePost *
897 \ if line('$') < g:tagbar_updateonsave_maxlines |
898 \ call s:AutoUpdate(fnamemodify(expand('<afile>'), ':p')) |
899 \ endif
900 autocmd BufEnter,CursorHold,FileType * call
901 \ s:AutoUpdate(fnamemodify(expand('<afile>'), ':p'))
902 autocmd BufDelete * call
903 \ s:CleanupFileinfo(fnamemodify(expand('<afile>'), ':p'))
904 augroup END
905
906 let s:autocommands_done = 1
907endfunction
908
909" s:CheckForExCtags() {{{2
910" Test whether the ctags binary is actually Exuberant Ctags and not GNU ctags
911" (or something else)
912function! s:CheckForExCtags()
913 call s:LogDebugMessage('Checking for Exuberant Ctags')
914
915 let ctags_cmd = s:EscapeCtagsCmd(g:tagbar_ctags_bin, '--version')
916 if ctags_cmd == ''
917 return
918 endif
919
920 let ctags_output = s:ExecuteCtags(ctags_cmd)
921
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')
932 echomsg line
933 endfor
934 endif
935 return 0
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')
944 echomsg line
945 endfor
946 endif
947 return 0
948 else
949 let s:checked_ctags = 1
950 return 1
951 endif
952endfunction
953
954" s:CheckExCtagsVersion() {{{2
955function! s:CheckExCtagsVersion(output)
956 call s:LogDebugMessage('Checking Exuberant Ctags version')
957
958 if a:output =~ 'Exuberant Ctags Development'
959 return 1
960 endif
961
962 let matchlist = matchlist(a:output, '\vExuberant Ctags (\d+)\.(\d+)')
963 let major = matchlist[1]
964 let minor = matchlist[2]
965
966 return major >= 6 || (major == 5 && minor >= 5)
967endfunction
968
969" s:CheckFTCtags() {{{2
970function! s:CheckFTCtags(bin, ftype)
971 if executable(a:bin)
972 return a:bin
973 endif
974
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
979 else
980 return ''
981 endif
982 endif
983
984 return ''
985endfunction
986
987" Prototypes {{{1
988" Base tag {{{2
989let s:BaseTag = {}
990
991" s:BaseTag._init() {{{3
992function! s:BaseTag._init(name) dict
993 let self.name = a:name
994 let self.fields = {}
995 let self.fields.line = 0
996 let self.path = ''
997 let self.fullpath = a:name
998 let self.depth = 0
999 let self.parent = {}
1000 let self.tline = -1
1001 let self.fileinfo = {}
1002endfunction
1003
1004" s:BaseTag.isNormalTag() {{{3
1005function! s:BaseTag.isNormalTag() dict
1006 return 0
1007endfunction
1008
1009" s:BaseTag.isPseudoTag() {{{3
1010function! s:BaseTag.isPseudoTag() dict
1011 return 0
1012endfunction
1013
1014" s:BaseTag.isKindheader() {{{3
1015function! s:BaseTag.isKindheader() dict
1016 return 0
1017endfunction
1018
1019" s:BaseTag.getPrototype() {{{3
1020function! s:BaseTag.getPrototype() dict
1021 return ''
1022endfunction
1023
1024" s:BaseTag._getPrefix() {{{3
1025function! s:BaseTag._getPrefix() dict
1026 let fileinfo = self.fileinfo
1027
1028 if has_key(self, 'children') && !empty(self.children)
1029 if fileinfo.tagfolds[self.fields.kind][self.fullpath]
1030 let prefix = s:icon_closed
1031 else
1032 let prefix = s:icon_open
1033 endif
1034 else
1035 let prefix = ' '
1036 endif
1037 if has_key(self.fields, 'access')
1038 let prefix .= get(s:access_symbols, self.fields.access, ' ')
1039 else
1040 let prefix .= ' '
1041 endif
1042
1043 return prefix
1044endfunction
1045
1046" s:BaseTag.initFoldState() {{{3
1047function! s:BaseTag.initFoldState() dict
1048 let fileinfo = self.fileinfo
1049
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
1053 " old fold state
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
1058 else
1059 let fileinfo.tagfolds[self.fields.kind][self.fullpath] =
1060 \ fileinfo.kindfolds[self.fields.kind]
1061 endif
1062endfunction
1063
1064" s:BaseTag.getClosedParentTline() {{{3
1065function! s:BaseTag.getClosedParentTline() dict
1066 let tagline = self.tline
1067 let fileinfo = self.fileinfo
1068
1069 let parent = self.parent
1070 while !empty(parent)
1071 if parent.isFolded()
1072 let tagline = parent.tline
1073 break
1074 endif
1075 let parent = parent.parent
1076 endwhile
1077
1078 return tagline
1079endfunction
1080
1081" s:BaseTag.isFoldable() {{{3
1082function! s:BaseTag.isFoldable() dict
1083 return has_key(self, 'children') && !empty(self.children)
1084endfunction
1085
1086" s:BaseTag.isFolded() {{{3
1087function! s:BaseTag.isFolded() dict
1088 return self.fileinfo.tagfolds[self.fields.kind][self.fullpath]
1089endfunction
1090
1091" s:BaseTag.openFold() {{{3
1092function! s:BaseTag.openFold() dict
1093 if self.isFoldable()
1094 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 0
1095 endif
1096endfunction
1097
1098" s:BaseTag.closeFold() {{{3
1099function! s:BaseTag.closeFold() dict
1100 let newline = line('.')
1101
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
1115 endif
1116
1117 return newline
1118endfunction
1119
1120" s:BaseTag.setFolded() {{{3
1121function! s:BaseTag.setFolded(folded) dict
1122 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = a:folded
1123endfunction
1124
1125" s:BaseTag.openParents() {{{3
1126function! s:BaseTag.openParents() dict
1127 let parent = self.parent
1128
1129 while !empty(parent)
1130 call parent.openFold()
1131 let parent = parent.parent
1132 endwhile
1133endfunction
1134
1135" Normal tag {{{2
1136let s:NormalTag = copy(s:BaseTag)
1137
1138" s:NormalTag.New() {{{3
1139function! s:NormalTag.New(name) dict
1140 let newobj = copy(self)
1141
1142 call newobj._init(a:name)
1143
1144 return newobj
1145endfunction
1146
1147" s:NormalTag.isNormalTag() {{{3
1148function! s:NormalTag.isNormalTag() dict
1149 return 1
1150endfunction
1151
1152" s:NormalTag.str() {{{3
1153function! s:NormalTag.str() dict
1154 let fileinfo = self.fileinfo
1155 let typeinfo = s:known_types[fileinfo.ftype]
1156
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]
1163 endif
1164
1165 return self._getPrefix() . self.name . suffix . "\n"
1166endfunction
1167
1168" s:NormalTag.getPrototype() {{{3
1169function! s:NormalTag.getPrototype() dict
1170 return self.prototype
1171endfunction
1172
1173" Pseudo tag {{{2
1174let s:PseudoTag = copy(s:BaseTag)
1175
1176" s:PseudoTag.New() {{{3
1177function! s:PseudoTag.New(name) dict
1178 let newobj = copy(self)
1179
1180 call newobj._init(a:name)
1181
1182 return newobj
1183endfunction
1184
1185" s:PseudoTag.isPseudoTag() {{{3
1186function! s:PseudoTag.isPseudoTag() dict
1187 return 1
1188endfunction
1189
1190" s:PseudoTag.str() {{{3
1191function! s:PseudoTag.str() dict
1192 let fileinfo = self.fileinfo
1193 let typeinfo = s:known_types[fileinfo.ftype]
1194
1195 let suffix = get(self.fields, 'signature', '')
1196 if has_key(typeinfo.kind2scope, self.fields.kind)
1197 let suffix .= ' : ' . typeinfo.kind2scope[self.fields.kind]
1198 endif
1199
1200 return self._getPrefix() . self.name . '*' . suffix
1201endfunction
1202
1203" Kind header {{{2
1204let s:KindheaderTag = copy(s:BaseTag)
1205
1206" s:KindheaderTag.New() {{{3
1207function! s:KindheaderTag.New(name) dict
1208 let newobj = copy(self)
1209
1210 call newobj._init(a:name)
1211
1212 return newobj
1213endfunction
1214
1215" s:KindheaderTag.isKindheader() {{{3
1216function! s:KindheaderTag.isKindheader() dict
1217 return 1
1218endfunction
1219
1220" s:KindheaderTag.getPrototype() {{{3
1221function! s:KindheaderTag.getPrototype() dict
1222 return self.name . ': ' .
1223 \ self.numtags . ' ' . (self.numtags > 1 ? 'tags' : 'tag')
1224endfunction
1225
1226" s:KindheaderTag.isFoldable() {{{3
1227function! s:KindheaderTag.isFoldable() dict
1228 return 1
1229endfunction
1230
1231" s:KindheaderTag.isFolded() {{{3
1232function! s:KindheaderTag.isFolded() dict
1233 return self.fileinfo.kindfolds[self.short]
1234endfunction
1235
1236" s:KindheaderTag.openFold() {{{3
1237function! s:KindheaderTag.openFold() dict
1238 let self.fileinfo.kindfolds[self.short] = 0
1239endfunction
1240
1241" s:KindheaderTag.closeFold() {{{3
1242function! s:KindheaderTag.closeFold() dict
1243 let self.fileinfo.kindfolds[self.short] = 1
1244 return line('.')
1245endfunction
1246
1247" s:KindheaderTag.toggleFold() {{{3
1248function! s:KindheaderTag.toggleFold() dict
1249 let fileinfo = s:known_files.getCurrent()
1250
1251 let fileinfo.kindfolds[self.short] = !fileinfo.kindfolds[self.short]
1252endfunction
1253
1254" File info {{{2
1255let s:FileInfo = {}
1256
1257" s:FileInfo.New() {{{3
1258function! s:FileInfo.New(fname, ftype) dict
1259 let newobj = copy(self)
1260
1261 " The complete file path
1262 let newobj.fpath = a:fname
1263
1264 " File modification time
1265 let newobj.mtime = getftime(a:fname)
1266
1267 " The vim file type
1268 let newobj.ftype = a:ftype
1269
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 = []
1273
1274 " Dictionary of the tags, indexed by line number in the file
1275 let newobj.fline = {}
1276
1277 " Dictionary of the tags, indexed by line number in the tagbar
1278 let newobj.tline = {}
1279
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
1287 endfor
1288
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] = {}
1294 endfor
1295
1296 " The current foldlevel of the file
1297 let newobj.foldlevel = g:tagbar_foldlevel
1298
1299 return newobj
1300endfunction
1301
1302" s:FileInfo.reset() {{{3
1303" Reset stuff that gets regenerated while processing a file and save the old
1304" tag folds
1305function! s:FileInfo.reset() dict
1306 let self.mtime = getftime(self.fpath)
1307 let self.tags = []
1308 let self.fline = {}
1309 let self.tline = {}
1310
1311 let self._tagfolds_old = self.tagfolds
1312 let self.tagfolds = {}
1313
1314 let typeinfo = s:known_types[self.ftype]
1315 for kind in typeinfo.kinds
1316 let self.tagfolds[kind.short] = {}
1317 endfor
1318endfunction
1319
1320" s:FileInfo.clearOldFolds() {{{3
1321function! s:FileInfo.clearOldFolds() dict
1322 if exists('self._tagfolds_old')
1323 unlet self._tagfolds_old
1324 endif
1325endfunction
1326
1327" s:FileInfo.sortTags() {{{3
1328function! 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')
1332 else
1333 call s:SortTags(self.tags, 's:CompareByLine')
1334 endif
1335 elseif g:tagbar_sort
1336 call s:SortTags(self.tags, 's:CompareByKind')
1337 else
1338 call s:SortTags(self.tags, 's:CompareByLine')
1339 endif
1340endfunction
1341
1342" s:FileInfo.openKindFold() {{{3
1343function! s:FileInfo.openKindFold(kind) dict
1344 let self.kindfolds[a:kind.short] = 0
1345endfunction
1346
1347" s:FileInfo.closeKindFold() {{{3
1348function! s:FileInfo.closeKindFold(kind) dict
1349 let self.kindfolds[a:kind.short] = 1
1350endfunction
1351
1352" Known files {{{2
1353let s:known_files = {
1354 \ '_current' : {},
1355 \ '_files' : {}
1356\ }
1357
1358" s:known_files.getCurrent() {{{3
1359function! s:known_files.getCurrent() dict
1360 return self._current
1361endfunction
1362
1363" s:known_files.setCurrent() {{{3
1364function! s:known_files.setCurrent(fileinfo) dict
1365 let self._current = a:fileinfo
1366endfunction
1367
1368" s:known_files.get() {{{3
1369function! s:known_files.get(fname) dict
1370 return get(self._files, a:fname, {})
1371endfunction
1372
1373" s:known_files.put() {{{3
1374" Optional second argument is the filename
1375function! s:known_files.put(fileinfo, ...) dict
1376 if a:0 == 1
1377 let self._files[a:1] = a:fileinfo
1378 else
1379 let fname = a:fileinfo.fpath
1380 let self._files[fname] = a:fileinfo
1381 endif
1382endfunction
1383
1384" s:known_files.has() {{{3
1385function! s:known_files.has(fname) dict
1386 return has_key(self._files, a:fname)
1387endfunction
1388
1389" s:known_files.rm() {{{3
1390function! s:known_files.rm(fname) dict
1391 if s:known_files.has(a:fname)
1392 call remove(self._files, a:fname)
1393 endif
1394endfunction
1395
1396" Window management {{{1
1397" s:ToggleWindow() {{{2
1398function! s:ToggleWindow()
1399 let tagbarwinnr = bufwinnr("__Tagbar__")
1400 if tagbarwinnr != -1
1401 call s:CloseWindow()
1402 return
1403 endif
1404
1405 call s:OpenWindow('')
1406endfunction
1407
1408" s:OpenWindow() {{{2
1409function! s:OpenWindow(flags)
1410 let autofocus = a:flags =~# 'f'
1411 let jump = a:flags =~# 'j'
1412 let autoclose = a:flags =~# 'c'
1413
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'
1420 if autoclose
1421 let w:autoclose = autoclose
1422 endif
1423 endif
1424 return
1425 endif
1426
1427 call s:Init()
1428
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
1433 endif
1434
1435 let eventignore_save = &eventignore
1436 set eventignore=all
1437
1438 let openpos = g:tagbar_left ? 'topleft vertical ' : 'botright vertical '
1439 exe 'silent keepalt ' . openpos . g:tagbar_width . 'split ' . '__Tagbar__'
1440
1441 let &eventignore = eventignore_save
1442
1443 call s:InitWindow(autoclose)
1444
1445 wincmd p
1446
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'
1452 endif
1453endfunction
1454
1455" s:InitWindow() {{{2
1456function! s:InitWindow(autoclose)
1457 setlocal noreadonly " in case the "view" mode is used
1458 setlocal buftype=nofile
1459 setlocal bufhidden=hide
1460 setlocal noswapfile
1461 setlocal nobuflisted
1462 setlocal nomodifiable
1463 setlocal filetype=tagbar
1464 setlocal nolist
1465 setlocal nonumber
1466 setlocal nowrap
1467 setlocal winfixwidth
1468 setlocal textwidth=0
1469 setlocal nocursorline
1470 setlocal nocursorcolumn
1471
1472 if exists('+relativenumber')
1473 setlocal norelativenumber
1474 endif
1475
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&
1482 setlocal foldexpr&
1483
1484 " Earlier versions have a bug in local, evaluated statuslines
1485 if v:version > 701 || (v:version == 701 && has('patch097'))
1486 setlocal statusline=%!TagbarGenerateStatusline()
1487 else
1488 setlocal statusline=Tagbar
1489 endif
1490
1491 " Script-local variable needed since compare functions can't
1492 " take extra arguments
1493 let s:compare_typeinfo = {}
1494
1495 let s:is_maximized = 0
1496 let s:short_help = 1
1497
1498 let w:autoclose = a:autoclose
1499
1500 if has('balloon_eval')
1501 setlocal balloonexpr=TagbarBalloonExpr()
1502 set ballooneval
1503 endif
1504
1505 let cpoptions_save = &cpoptions
1506 set cpoptions&vim
1507
1508 if !hasmapto('JumpToTag', 'n')
1509 call s:MapKeys()
1510 endif
1511
1512 if !s:autocommands_done
1513 call s:CreateAutocommands()
1514 endif
1515
1516 let &cpoptions = cpoptions_save
1517endfunction
1518
1519" s:CloseWindow() {{{2
1520function! s:CloseWindow()
1521 let tagbarwinnr = bufwinnr('__Tagbar__')
1522 if tagbarwinnr == -1
1523 return
1524 endif
1525
1526 let tagbarbufnr = winbufnr(tagbarwinnr)
1527
1528 if winnr() == tagbarwinnr
1529 if winbufnr(2) != -1
1530 " Other windows are open, only close the tagbar one
1531 close
1532 wincmd p
1533 endif
1534 else
1535 " Go to the tagbar window, close it and then come back to the
1536 " original window
1537 let curbufnr = bufnr('%')
1538 execute tagbarwinnr . 'wincmd w'
1539 close
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'
1545 endif
1546 endif
1547
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
1551 let tablist = []
1552 for i in range(tabpagenr('$'))
1553 call extend(tablist, tabpagebuflist(i + 1))
1554 endfor
1555
1556 if index(tablist, tagbarbufnr) == -1
1557 let &columns -= g:tagbar_width + 1
1558 let s:window_expanded = 0
1559 endif
1560 endif
1561endfunction
1562
1563" s:ZoomWindow() {{{2
1564function! s:ZoomWindow()
1565 if s:is_maximized
1566 execute 'vert resize ' . g:tagbar_width
1567 let s:is_maximized = 0
1568 else
1569 vert resize
1570 let s:is_maximized = 1
1571 endif
1572endfunction
1573
1574" Tag processing {{{1
1575" s:ProcessFile() {{{2
1576" Execute ctags and put the information into a 'FileInfo' object
1577function! s:ProcessFile(fname, ftype)
1578 call s:LogDebugMessage('ProcessFile called on ' . a:fname)
1579
1580 if !s:IsValidFile(a:fname, a:ftype)
1581 call s:LogDebugMessage('Not a valid file, returning')
1582 return
1583 endif
1584
1585 let ctags_output = s:ExecuteCtagsOnFile(a:fname, a:ftype)
1586
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
1590 " shown once
1591 call s:known_files.put({}, a:fname)
1592 return
1593 elseif ctags_output == ''
1594 call s:LogDebugMessage('Ctags output empty')
1595 return
1596 endif
1597
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()
1603 else
1604 let fileinfo = s:FileInfo.New(a:fname, a:ftype)
1605 endif
1606
1607 let typeinfo = s:known_types[a:ftype]
1608
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
1613 " skip comments
1614 if line =~# '^!_TAG_'
1615 continue
1616 endif
1617
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)
1623 endif
1624 endfor
1625
1626 " Process scoped tags
1627 let processedtags = []
1628 if has_key(typeinfo, 'kind2scope')
1629 call s:LogDebugMessage('Processing scoped tags')
1630
1631 let scopedtags = []
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 . ')')
1636
1637 call s:AddScopedTags(scopedtags, processedtags, {}, 0,
1638 \ typeinfo, fileinfo)
1639
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.'
1644 endif
1645 endif
1646 call s:LogDebugMessage('Number of top-level tags: ' . len(processedtags))
1647
1648 " Create a placeholder tag for the 'kind' header for folding purposes
1649 for kind in typeinfo.kinds
1650
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))
1655
1656 if empty(curtags)
1657 continue
1658 endif
1659
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
1664
1665 for tag in curtags
1666 let tag.parent = kindtag
1667 endfor
1668 endfor
1669
1670 if !empty(processedtags)
1671 call extend(fileinfo.tags, processedtags)
1672 endif
1673
1674 " Clear old folding information from previous file version to prevent leaks
1675 call fileinfo.clearOldFolds()
1676
1677 " Sort the tags
1678 let s:compare_typeinfo = typeinfo
1679 call fileinfo.sortTags()
1680
1681 call s:known_files.put(fileinfo)
1682endfunction
1683
1684" s:ExecuteCtagsOnFile() {{{2
1685function! s:ExecuteCtagsOnFile(fname, ftype)
1686 call s:LogDebugMessage('ExecuteCtagsOnFile called on ' . a:fname)
1687
1688 let typeinfo = s:known_types[a:ftype]
1689
1690 if has_key(typeinfo, 'ctagsargs')
1691 let ctags_args = ' ' . typeinfo.ctagsargs . ' '
1692 else
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 '
1699
1700 " Include extra type definitions
1701 if has_key(typeinfo, 'deffile')
1702 let ctags_args .= ' --options=' . typeinfo.deffile . ' '
1703 endif
1704
1705 let ctags_type = typeinfo.ctagstype
1706
1707 let ctags_kinds = ''
1708 for kind in typeinfo.kinds
1709 let ctags_kinds .= kind.short
1710 endfor
1711
1712 let ctags_args .= ' --language-force=' . ctags_type .
1713 \ ' --' . ctags_type . '-kinds=' . ctags_kinds . ' '
1714 endif
1715
1716 if has_key(typeinfo, 'ctagsbin')
1717 " reset 'wildignore' temporarily in case *.exe is included in it
1718 let wildignore_save = &wildignore
1719 set wildignore&
1720 let ctags_bin = expand(typeinfo.ctagsbin)
1721 let &wildignore = wildignore_save
1722 else
1723 let ctags_bin = g:tagbar_ctags_bin
1724 endif
1725
1726 let ctags_cmd = s:EscapeCtagsCmd(ctags_bin, ctags_args, a:fname)
1727 if ctags_cmd == ''
1728 return ''
1729 endif
1730
1731 let ctags_output = s:ExecuteCtags(ctags_cmd)
1732
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')
1741 echomsg line
1742 endfor
1743 endif
1744 return -1
1745 endif
1746
1747 call s:LogDebugMessage('Ctags executed successfully')
1748 return ctags_output
1749endfunction
1750
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
1756function! s:ParseTagline(part1, part2, typeinfo, fileinfo)
1757 let basic_info = split(a:part1, '\t')
1758
1759 let taginfo = s:NormalTag.New(basic_info[0])
1760 let taginfo.file = basic_info[1]
1761
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] ==# '$'
1768 let end -= 1
1769 let dollar = '\$'
1770 else
1771 let dollar = ''
1772 endif
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
1778
1779 let fields = split(a:part2, '\t')
1780 let taginfo.fields.kind = remove(fields, 0)
1781 for field in fields
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)
1786 if len(val) > 0
1787 let taginfo.fields[key] = val
1788 endif
1789 endfor
1790 " Needed for jsctags
1791 if has_key(taginfo.fields, 'lineno')
1792 let taginfo.fields.line = taginfo.fields.lineno
1793 endif
1794
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]
1801
1802 let taginfo.fullpath = taginfo.path . a:typeinfo.sro .
1803 \ taginfo.name
1804 break
1805 endif
1806 endfor
1807 let taginfo.depth = len(split(taginfo.path, '\V' . a:typeinfo.sro))
1808 endif
1809
1810 let taginfo.fileinfo = a:fileinfo
1811
1812 " Needed for folding
1813 try
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''.'
1819 endtry
1820
1821 return taginfo
1822endfunction
1823
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
1832" too much.
1833function! s:AddScopedTags(tags, processedtags, parent, depth,
1834 \ typeinfo, fileinfo)
1835 if !empty(a:parent)
1836 let curpath = a:parent.fullpath
1837 let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
1838 else
1839 let curpath = ''
1840 let pscope = ''
1841 endif
1842
1843 let is_cur_tag = 'v:val.depth == a:depth'
1844
1845 if !empty(curpath)
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)'
1853 endif
1854
1855 let curtags = filter(copy(a:tags), is_cur_tag)
1856
1857 if !empty(curtags)
1858 call filter(a:tags, '!(' . is_cur_tag . ')')
1859
1860 let realtags = []
1861 let pseudotags = []
1862
1863 while !empty(curtags)
1864 let tag = remove(curtags, 0)
1865
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)
1871
1872 call add(pseudotags, pseudotag)
1873 else
1874 call add(realtags, tag)
1875 endif
1876 endwhile
1877
1878 " Recursively add the children of the tags on the current level
1879 for tag in realtags
1880 let tag.parent = a:parent
1881
1882 if !has_key(a:typeinfo.kind2scope, tag.fields.kind)
1883 continue
1884 endif
1885
1886 if !has_key(tag, 'children')
1887 let tag.children = []
1888 endif
1889
1890 call s:AddScopedTags(a:tags, tag.children, tag, a:depth + 1,
1891 \ a:typeinfo, a:fileinfo)
1892 endfor
1893 call extend(a:processedtags, realtags)
1894
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,
1899 \ a:fileinfo)
1900 endfor
1901 call extend(a:processedtags, pseudotags)
1902 endif
1903
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'
1907
1908 if !empty(curpath)
1909 let is_grandchild .=
1910 \ ' && match(v:val.path, ''\V\^\C'' . curpath . a:typeinfo.sro) == 0'
1911 endif
1912
1913 let grandchildren = filter(copy(a:tags), is_grandchild)
1914
1915 if !empty(grandchildren)
1916 call s:AddScopedTags(a:tags, a:processedtags, a:parent, a:depth + 1,
1917 \ a:typeinfo, a:fileinfo)
1918 endif
1919endfunction
1920
1921" s:ProcessPseudoTag() {{{2
1922function! s:ProcessPseudoTag(curtags, tag, parent, typeinfo, fileinfo)
1923 let curpath = !empty(a:parent) ? a:parent.fullpath : ''
1924
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]
1930
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)
1937 endif
1938
1939 return pseudotag
1940endfunction
1941
1942" s:ProcessPseudoChildren() {{{2
1943function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo, fileinfo)
1944 for childtag in a:tag.children
1945 let childtag.parent = a:tag
1946
1947 if !has_key(a:typeinfo.kind2scope, childtag.fields.kind)
1948 continue
1949 endif
1950
1951 if !has_key(childtag, 'children')
1952 let childtag.children = []
1953 endif
1954
1955 call s:AddScopedTags(a:tags, childtag.children, childtag, a:depth + 1,
1956 \ a:typeinfo, a:fileinfo)
1957 endfor
1958
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)
1965 endif
1966endfunction
1967
1968" s:CreatePseudoTag() {{{2
1969function! s:CreatePseudoTag(name, parent, scope, typeinfo, fileinfo)
1970 if !empty(a:parent)
1971 let curpath = a:parent.fullpath
1972 let pscope = a:typeinfo.kind2scope[a:parent.fields.kind]
1973 else
1974 let curpath = ''
1975 let pscope = ''
1976 endif
1977
1978 let pseudotag = s:PseudoTag.New(a:name)
1979 let pseudotag.fields.kind = a:typeinfo.scope2kind[a:scope]
1980
1981 let parentscope = substitute(curpath, a:name . '$', '', '')
1982 let parentscope = substitute(parentscope,
1983 \ '\V\^' . a:typeinfo.sro . '\$', '', '')
1984
1985 if pscope != ''
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
1991 endif
1992 let pseudotag.depth = len(split(pseudotag.path, '\V' . a:typeinfo.sro))
1993
1994 let pseudotag.parent = a:parent
1995
1996 let pseudotag.fileinfo = a:fileinfo
1997
1998 call pseudotag.initFoldState()
1999
2000 return pseudotag
2001endfunction
2002
2003" Sorting {{{1
2004" s:SortTags() {{{2
2005function! s:SortTags(tags, comparemethod)
2006 call sort(a:tags, a:comparemethod)
2007
2008 for tag in a:tags
2009 if has_key(tag, 'children')
2010 call s:SortTags(tag.children, a:comparemethod)
2011 endif
2012 endfor
2013endfunction
2014
2015" s:CompareByKind() {{{2
2016function! s:CompareByKind(tag1, tag2)
2017 let typeinfo = s:compare_typeinfo
2018
2019 if typeinfo.kinddict[a:tag1.fields.kind] <#
2020 \ typeinfo.kinddict[a:tag2.fields.kind]
2021 return -1
2022 elseif typeinfo.kinddict[a:tag1.fields.kind] >#
2023 \ typeinfo.kinddict[a:tag2.fields.kind]
2024 return 1
2025 else
2026 " Ignore '~' prefix for C++ destructors to sort them directly under
2027 " the constructors
2028 if a:tag1.name[0] ==# '~'
2029 let name1 = a:tag1.name[1:]
2030 else
2031 let name1 = a:tag1.name
2032 endif
2033 if a:tag2.name[0] ==# '~'
2034 let name2 = a:tag2.name[1:]
2035 else
2036 let name2 = a:tag2.name
2037 endif
2038
2039 if name1 <=# name2
2040 return -1
2041 else
2042 return 1
2043 endif
2044 endif
2045endfunction
2046
2047" s:CompareByLine() {{{2
2048function! s:CompareByLine(tag1, tag2)
2049 return a:tag1.fields.line - a:tag2.fields.line
2050endfunction
2051
2052" s:ToggleSort() {{{2
2053function! s:ToggleSort()
2054 let fileinfo = s:known_files.getCurrent()
2055 if empty(fileinfo)
2056 return
2057 endif
2058
2059 let curline = line('.')
2060
2061 match none
2062
2063 let s:compare_typeinfo = s:known_types[fileinfo.ftype]
2064
2065 if has_key(s:compare_typeinfo, 'sort')
2066 let s:compare_typeinfo.sort = !s:compare_typeinfo.sort
2067 else
2068 let g:tagbar_sort = !g:tagbar_sort
2069 endif
2070
2071 call fileinfo.sortTags()
2072
2073 call s:RenderContent()
2074
2075 execute curline
2076endfunction
2077
2078" Display {{{1
2079" s:RenderContent() {{{2
2080function! s:RenderContent(...)
2081 call s:LogDebugMessage('RenderContent called')
2082
2083 if a:0 == 1
2084 let fileinfo = a:1
2085 else
2086 let fileinfo = s:known_files.getCurrent()
2087 endif
2088
2089 if empty(fileinfo)
2090 call s:LogDebugMessage('Empty fileinfo, returning')
2091 return
2092 endif
2093
2094 let tagbarwinnr = bufwinnr('__Tagbar__')
2095
2096 if &filetype == 'tagbar'
2097 let in_tagbar = 1
2098 else
2099 let in_tagbar = 0
2100 let prevwinnr = winnr()
2101 execute tagbarwinnr . 'wincmd w'
2102 endif
2103
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')
2111 endif
2112
2113 let lazyredraw_save = &lazyredraw
2114 set lazyredraw
2115 let eventignore_save = &eventignore
2116 set eventignore=all
2117
2118 setlocal modifiable
2119
2120 silent %delete _
2121
2122 call s:PrintHelp()
2123
2124 let typeinfo = s:known_types[fileinfo.ftype]
2125
2126 " Print tags
2127 call s:PrintKinds(typeinfo, fileinfo)
2128
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 _'
2133 else
2134 break
2135 endif
2136 endfor
2137
2138 setlocal nomodifiable
2139
2140 if !empty(s:known_files.getCurrent()) &&
2141 \ fileinfo.fpath ==# s:known_files.getCurrent().fpath
2142 let scrolloff_save = &scrolloff
2143 set scrolloff=0
2144
2145 call cursor(topline, 1)
2146 normal! zt
2147 call cursor(saveline, savecol)
2148
2149 let &scrolloff = scrolloff_save
2150 else
2151 " Make sure as much of the Tagbar content as possible is shown in the
2152 " window by jumping to the top after drawing
2153 execute 1
2154 call winline()
2155
2156 " Invalidate highlight cache from old file
2157 let s:last_highlight_tline = 0
2158 endif
2159
2160 let &lazyredraw = lazyredraw_save
2161 let &eventignore = eventignore_save
2162
2163 if !in_tagbar
2164 execute prevwinnr . 'wincmd w'
2165 endif
2166endfunction
2167
2168" s:PrintKinds() {{{2
2169function! s:PrintKinds(typeinfo, fileinfo)
2170 call s:LogDebugMessage('PrintKinds called')
2171
2172 let first_tag = 1
2173
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))
2179
2180 if empty(curtags)
2181 continue
2182 endif
2183
2184 if has_key(a:typeinfo, 'kind2scope') &&
2185 \ has_key(a:typeinfo.kind2scope, kind.short)
2186 " Scoped tags
2187 for tag in curtags
2188 if g:tagbar_compact && first_tag && s:short_help
2189 silent 0put =tag.str()
2190 else
2191 silent put =tag.str()
2192 endif
2193
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
2199
2200 " Print children
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
2210 endif
2211 for childtag in childtags
2212 call s:PrintTag(childtag, 1,
2213 \ a:fileinfo, a:typeinfo)
2214 endfor
2215 endif
2216 endfor
2217 endif
2218
2219 if !g:tagbar_compact
2220 silent put _
2221 endif
2222
2223 let first_tag = 0
2224 endfor
2225 else
2226 " Non-scoped tags
2227 let kindtag = curtags[0].parent
2228
2229 if kindtag.isFolded()
2230 let foldmarker = s:icon_closed
2231 else
2232 let foldmarker = s:icon_open
2233 endif
2234
2235 if g:tagbar_compact && first_tag && s:short_help
2236 silent 0put =foldmarker . ' ' . kind.long
2237 else
2238 silent put =foldmarker . ' ' . kind.long
2239 endif
2240
2241 let curline = line('.')
2242 let kindtag.tline = curline
2243 let a:fileinfo.tline[curline] = kindtag
2244
2245 if !kindtag.isFolded()
2246 for tag in curtags
2247 let str = tag.str()
2248 silent put =' ' . str
2249
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
2255 let tag.depth = 1
2256 endfor
2257 endif
2258
2259 if !g:tagbar_compact
2260 silent put _
2261 endif
2262
2263 let first_tag = 0
2264 endif
2265 endfor
2266endfunction
2267
2268" s:PrintTag() {{{2
2269function! s:PrintTag(tag, depth, fileinfo, typeinfo)
2270 " Print tag indented according to depth
2271 silent put =repeat(' ', a:depth * 2) . a:tag.str()
2272
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
2278
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
2290 endif
2291 for childtag in childtags
2292 call s:PrintTag(childtag, a:depth + 1,
2293 \ a:fileinfo, a:typeinfo)
2294 endfor
2295 endif
2296 endfor
2297 endif
2298endfunction
2299
2300" s:PrintHelp() {{{2
2301function! s:PrintHelp()
2302 if !g:tagbar_compact && s:short_help
2303 silent 0put ='\" Press <F1> for help'
2304 silent put _
2305 elseif !s:short_help
2306 silent 0put ='\" Tagbar keybindings'
2307 silent put ='\"'
2308 silent put ='\" --------- General ---------'
2309 silent put ='\" <Enter> : Jump to tag definition'
2310 silent put ='\" <Space> : Display tag prototype'
2311 silent put ='\"'
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'
2318 silent put ='\"'
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'
2324 silent put _
2325 endif
2326endfunction
2327
2328" s:RenderKeepView() {{{2
2329" The gist of this function was taken from NERDTree by Martin Grenfell.
2330function! s:RenderKeepView(...)
2331 if a:0 == 1
2332 let line = a:1
2333 else
2334 let line = line('.')
2335 endif
2336
2337 let curcol = col('.')
2338 let topline = line('w0')
2339
2340 call s:RenderContent()
2341
2342 let scrolloff_save = &scrolloff
2343 set scrolloff=0
2344
2345 call cursor(topline, 1)
2346 normal! zt
2347 call cursor(line, curcol)
2348
2349 let &scrolloff = scrolloff_save
2350
2351 redraw
2352endfunction
2353
2354" User actions {{{1
2355" s:HighlightTag() {{{2
2356function! s:HighlightTag()
2357 let tagline = 0
2358
2359 let tag = s:GetNearbyTag()
2360 if !empty(tag)
2361 let tagline = tag.tline
2362 endif
2363
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
2366 " the mouse.
2367 if tagline == s:last_highlight_tline
2368 return
2369 else
2370 let s:last_highlight_tline = tagline
2371 endif
2372
2373 let eventignore_save = &eventignore
2374 set eventignore=all
2375
2376 let tagbarwinnr = bufwinnr('__Tagbar__')
2377 let prevwinnr = winnr()
2378 execute tagbarwinnr . 'wincmd w'
2379
2380 match none
2381
2382 " No tag above cursor position so don't do anything
2383 if tagline == 0
2384 execute prevwinnr . 'wincmd w'
2385 let &eventignore = eventignore_save
2386 redraw
2387 return
2388 endif
2389
2390 if g:tagbar_autoshowtag
2391 call s:OpenParents(tag)
2392 endif
2393
2394 " Check whether the tag is inside a closed fold and highlight the parent
2395 " instead in that case
2396 let tagline = tag.getClosedParentTline()
2397
2398 " Go to the line containing the tag
2399 execute tagline
2400
2401 " Make sure the tag is visible in the window
2402 call winline()
2403
2404 let foldpat = '[' . s:icon_open . s:icon_closed . ' ]'
2405 let pattern = '/^\%' . tagline . 'l\s*' . foldpat . '[-+# ]\zs[^( ]\+\ze/'
2406 execute 'match TagbarHighlight ' . pattern
2407
2408 execute prevwinnr . 'wincmd w'
2409
2410 let &eventignore = eventignore_save
2411
2412 redraw
2413endfunction
2414
2415" s:JumpToTag() {{{2
2416function! s:JumpToTag(stay_in_tagbar)
2417 let taginfo = s:GetTagInfo(line('.'), 1)
2418
2419 let autoclose = w:autoclose
2420
2421 if empty(taginfo) || has_key(taginfo, 'numtags')
2422 return
2423 endif
2424
2425 let tagbarwinnr = winnr()
2426
2427 let eventignore_save = &eventignore
2428 set eventignore=all
2429
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
2433 wincmd p
2434 let filebufnr = bufnr(taginfo.fileinfo.fpath)
2435 if bufnr('%') != filebufnr
2436 let filewinnr = bufwinnr(filebufnr)
2437 if filewinnr != -1
2438 execute filewinnr . 'wincmd w'
2439 else
2440 for i in range(1, winnr('$'))
2441 execute i . 'wincmd w'
2442 if &buftype == ''
2443 execute 'buffer ' . filebufnr
2444 break
2445 endif
2446 endfor
2447 endif
2448 " To make ctrl-w_p work we switch between the Tagbar window and the
2449 " correct window once
2450 execute tagbarwinnr . 'wincmd w'
2451 wincmd p
2452 endif
2453
2454 " Mark current position so it can be jumped back to
2455 mark '
2456
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
2461
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
2465 let interval = 1
2466 let forward = 1
2467 while search(taginfo.pattern, 'W' . forward ? '' : 'b') == 0
2468 if !forward
2469 if interval > line('$')
2470 break
2471 else
2472 let interval = interval * 2
2473 endif
2474 endif
2475 let forward = !forward
2476 endwhile
2477 endif
2478
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
2485 endif
2486
2487 " Center the tag in the window
2488 normal! z.
2489
2490 if foldclosed('.') != -1
2491 .foldopen!
2492 endif
2493
2494 redraw
2495
2496 let &eventignore = eventignore_save
2497
2498 if a:stay_in_tagbar
2499 call s:HighlightTag()
2500 execute tagbarwinnr . 'wincmd w'
2501 elseif g:tagbar_autoclose || autoclose
2502 call s:CloseWindow()
2503 else
2504 call s:HighlightTag()
2505 endif
2506endfunction
2507
2508" s:ShowPrototype() {{{2
2509function! s:ShowPrototype()
2510 let taginfo = s:GetTagInfo(line('.'), 1)
2511
2512 if empty(taginfo)
2513 return
2514 endif
2515
2516 echo taginfo.getPrototype()
2517endfunction
2518
2519" s:ToggleHelp() {{{2
2520function! s:ToggleHelp()
2521 let s:short_help = !s:short_help
2522
2523 " Prevent highlighting from being off after adding/removing the help text
2524 match none
2525
2526 call s:RenderContent()
2527
2528 execute 1
2529 redraw
2530endfunction
2531
2532" s:GotoNextToplevelTag() {{{2
2533function! s:GotoNextToplevelTag(direction)
2534 let curlinenr = line('.')
2535 let newlinenr = line('.')
2536
2537 if a:direction == 1
2538 let range = range(line('.') + 1, line('$'))
2539 else
2540 let range = range(line('.') - 1, 1, -1)
2541 endif
2542
2543 for tmplinenr in range
2544 let taginfo = s:GetTagInfo(tmplinenr, 0)
2545
2546 if empty(taginfo)
2547 continue
2548 elseif empty(taginfo.parent)
2549 let newlinenr = tmplinenr
2550 break
2551 endif
2552 endfor
2553
2554 if curlinenr != newlinenr
2555 execute newlinenr
2556 call winline()
2557 endif
2558
2559 redraw
2560endfunction
2561
2562" Folding {{{1
2563" s:OpenFold() {{{2
2564function! s:OpenFold()
2565 let fileinfo = s:known_files.getCurrent()
2566 if empty(fileinfo)
2567 return
2568 endif
2569
2570 let curline = line('.')
2571
2572 let tag = s:GetTagInfo(curline, 0)
2573 if empty(tag)
2574 return
2575 endif
2576
2577 call tag.openFold()
2578
2579 call s:RenderKeepView()
2580endfunction
2581
2582" s:CloseFold() {{{2
2583function! s:CloseFold()
2584 let fileinfo = s:known_files.getCurrent()
2585 if empty(fileinfo)
2586 return
2587 endif
2588
2589 match none
2590
2591 let curline = line('.')
2592
2593 let curtag = s:GetTagInfo(curline, 0)
2594 if empty(curtag)
2595 return
2596 endif
2597
2598 let newline = curtag.closeFold()
2599
2600 call s:RenderKeepView(newline)
2601endfunction
2602
2603" s:ToggleFold() {{{2
2604function! s:ToggleFold()
2605 let fileinfo = s:known_files.getCurrent()
2606 if empty(fileinfo)
2607 return
2608 endif
2609
2610 match none
2611
2612 let curtag = s:GetTagInfo(line('.'), 0)
2613 if empty(curtag)
2614 return
2615 endif
2616
2617 let newline = line('.')
2618
2619 if curtag.isKindheader()
2620 call curtag.toggleFold()
2621 elseif curtag.isFoldable()
2622 if curtag.isFolded()
2623 call curtag.openFold()
2624 else
2625 let newline = curtag.closeFold()
2626 endif
2627 else
2628 let newline = curtag.closeFold()
2629 endif
2630
2631 call s:RenderKeepView(newline)
2632endfunction
2633
2634" s:SetFoldLevel() {{{2
2635function! s:SetFoldLevel(level)
2636 if a:level < 0
2637 echoerr 'Foldlevel can''t be negative'
2638 return
2639 endif
2640
2641 let fileinfo = s:known_files.getCurrent()
2642 if empty(fileinfo)
2643 return
2644 endif
2645
2646 call s:SetFoldLevelRecursive(fileinfo, fileinfo.tags, a:level)
2647
2648 let typeinfo = s:known_types[fileinfo.ftype]
2649
2650 " Apply foldlevel to 'kind's
2651 if a:level == 0
2652 for kind in typeinfo.kinds
2653 call fileinfo.closeKindFold(kind)
2654 endfor
2655 else
2656 for kind in typeinfo.kinds
2657 call fileinfo.openKindFold(kind)
2658 endfor
2659 endif
2660
2661 let fileinfo.foldlevel = a:level
2662
2663 call s:RenderContent()
2664endfunction
2665
2666" s:SetFoldLevelRecursive() {{{2
2667" Apply foldlevel to normal tags
2668function! s:SetFoldLevelRecursive(fileinfo, tags, level)
2669 for tag in a:tags
2670 if tag.depth >= a:level
2671 call tag.setFolded(1)
2672 else
2673 call tag.setFolded(0)
2674 endif
2675
2676 if has_key(tag, 'children')
2677 call s:SetFoldLevelRecursive(a:fileinfo, tag.children, a:level)
2678 endif
2679 endfor
2680endfunction
2681
2682" s:OpenParents() {{{2
2683function! s:OpenParents(...)
2684 let tagline = 0
2685
2686 if a:0 == 1
2687 let tag = a:1
2688 else
2689 let tag = s:GetNearbyTag()
2690 endif
2691
2692 call tag.openParents()
2693
2694 call s:RenderKeepView()
2695endfunction
2696
2697" Helper functions {{{1
2698" s:CleanUp() {{{2
2699function! s:CleanUp()
2700 silent autocmd! TagbarAutoCmds
2701
2702 unlet s:is_maximized
2703 unlet s:compare_typeinfo
2704 unlet s:short_help
2705endfunction
2706
2707" s:CleanupFileinfo() {{{2
2708function! s:CleanupFileinfo(fname)
2709 call s:known_files.rm(a:fname)
2710endfunction
2711
2712" s:QuitIfOnlyWindow() {{{2
2713function! 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
2719 bdelete
2720 quit
2721 else
2722 close
2723 endif
2724 endif
2725endfunction
2726
2727" s:AutoUpdate() {{{2
2728function! s:AutoUpdate(fname)
2729 call s:LogDebugMessage('AutoUpdate called on ' . a:fname)
2730
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')
2735 return
2736 endif
2737
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)
2742
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')
2746 return
2747 endif
2748
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)
2756 endif
2757 elseif !s:known_files.has(a:fname)
2758 call s:LogDebugMessage('Unknown file, processing ' . a:fname)
2759 call s:ProcessFile(a:fname, ftype)
2760 endif
2761
2762 let fileinfo = s:known_files.get(a:fname)
2763
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
2766 if empty(fileinfo)
2767 call s:LogDebugMessage('fileinfo empty after processing: ' . a:fname)
2768 return
2769 endif
2770
2771 " Display the tagbar content
2772 call s:RenderContent(fileinfo)
2773
2774 " Call setCurrent after rendering so RenderContent can check whether the
2775 " same file is redisplayed
2776 if !empty(fileinfo)
2777 call s:LogDebugMessage('Setting current file to ' . a:fname)
2778 call s:known_files.setCurrent(fileinfo)
2779 endif
2780
2781 call s:HighlightTag()
2782 call s:LogDebugMessage('AutoUpdate finished successfully')
2783endfunction
2784
2785" s:IsValidFile() {{{2
2786function! s:IsValidFile(fname, ftype)
2787 if a:fname == '' || a:ftype == ''
2788 call s:LogDebugMessage('Empty filename or type')
2789 return 0
2790 endif
2791
2792 if !filereadable(a:fname)
2793 call s:LogDebugMessage('File not readable')
2794 return 0
2795 endif
2796
2797 if !has_key(s:known_types, a:ftype)
2798 call s:LogDebugMessage('Unsupported filetype: ' . a:ftype)
2799 return 0
2800 endif
2801
2802 return 1
2803endfunction
2804
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
2809function! 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)
2813
2814 if exists('+shellslash')
2815 let shellslash_save = &shellslash
2816 set noshellslash
2817 endif
2818
2819 if a:0 == 1
2820 let fname = shellescape(a:1)
2821 else
2822 let fname = ''
2823 endif
2824
2825 let ctags_cmd = shellescape(a:ctags_bin) . ' ' . a:args . ' ' . fname
2826
2827 if exists('+shellslash')
2828 let &shellslash = shellslash_save
2829 endif
2830
2831 " Needed for cases where 'encoding' is different from the system's
2832 " encoding
2833 if g:tagbar_systemenc != &encoding
2834 let ctags_cmd = iconv(ctags_cmd, &encoding, g:tagbar_systemenc)
2835 elseif $LANG != ''
2836 let ctags_cmd = iconv(ctags_cmd, &encoding, $LANG)
2837 endif
2838
2839 call s:LogDebugMessage('Escaped ctags command: ' . ctags_cmd)
2840
2841 if 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.'
2845 endif
2846
2847 return ctags_cmd
2848endfunction
2849
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
2854function! s:ExecuteCtags(ctags_cmd)
2855 if exists('+shellslash')
2856 let shellslash_save = &shellslash
2857 set noshellslash
2858 endif
2859
2860 if &shell =~ 'cmd\.exe'
2861 let shellxquote_save = &shellxquote
2862 set shellxquote=\"
2863 let shellcmdflag_save = &shellcmdflag
2864 set shellcmdflag=/s\ /c
2865 endif
2866
2867 let ctags_output = system(a:ctags_cmd)
2868
2869 if &shell =~ 'cmd\.exe'
2870 let &shellxquote = shellxquote_save
2871 let &shellcmdflag = shellcmdflag_save
2872 endif
2873
2874 if exists('+shellslash')
2875 let &shellslash = shellslash_save
2876 endif
2877
2878 return ctags_output
2879endfunction
2880
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.
2885function! s:GetTagInfo(linenr, ignorepseudo)
2886 let fileinfo = s:known_files.getCurrent()
2887
2888 if empty(fileinfo)
2889 return {}
2890 endif
2891
2892 " Don't do anything in empty and comment lines
2893 let curline = getline(a:linenr)
2894 if curline =~ '^\s*$' || curline[0] == '"'
2895 return {}
2896 endif
2897
2898 " Check if there is a tag on the current line
2899 if !has_key(fileinfo.tline, a:linenr)
2900 return {}
2901 endif
2902
2903 let taginfo = fileinfo.tline[a:linenr]
2904
2905 " Check if the current tag is not a pseudo-tag
2906 if a:ignorepseudo && taginfo.isPseudoTag()
2907 return {}
2908 endif
2909
2910 return taginfo
2911endfunction
2912
2913" s:GetNearbyTag() {{{2
2914" Get the tag info for a file near the cursor in the current file
2915function! s:GetNearbyTag()
2916 let fileinfo = s:known_files.getCurrent()
2917
2918 let curline = line('.')
2919 let tag = {}
2920
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]
2929 break
2930 endif
2931 endfor
2932
2933 return tag
2934endfunction
2935
2936" s:CheckMouseClick() {{{2
2937function! s:CheckMouseClick()
2938 let line = getline('.')
2939 let curcol = col('.')
2940
2941 if (match(line, s:icon_open . '[-+ ]') + 1) == curcol
2942 call s:CloseFold()
2943 elseif (match(line, s:icon_closed . '[-+ ]') + 1) == curcol
2944 call s:OpenFold()
2945 elseif g:tagbar_singleclick
2946 call s:JumpToTag(0)
2947 endif
2948endfunction
2949
2950" s:DetermineFiletype() {{{2
2951function! 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')
2955
2956 if bufloaded(a:bufnr)
2957 return ftype
2958 endif
2959
2960 if ftype != ''
2961 return ftype
2962 endif
2963
2964 " Unloaded buffer with non-detected filetype, need to detect filetype
2965 " manually
2966 let bufname = bufname(a:bufnr)
2967
2968 let eventignore_save = &eventignore
2969 set eventignore=FileType
2970 let filetype_save = &filetype
2971
2972 exe 'doautocmd filetypedetect BufRead ' . bufname
2973 let ftype = &filetype
2974
2975 let &filetype = filetype_save
2976 let &eventignore = eventignore_save
2977
2978 return ftype
2979endfunction
2980
2981
2982" TagbarBalloonExpr() {{{2
2983function! TagbarBalloonExpr()
2984 let taginfo = s:GetTagInfo(v:beval_lnum, 1)
2985
2986 if empty(taginfo)
2987 return
2988 endif
2989
2990 return taginfo.getPrototype()
2991endfunction
2992
2993" TagbarGenerateStatusline() {{{2
2994function! TagbarGenerateStatusline()
2995 if g:tagbar_sort
2996 let text = '[Name]'
2997 else
2998 let text = '[Order]'
2999 endif
3000
3001 if !empty(s:known_files.getCurrent())
3002 let filename = fnamemodify(s:known_files.getCurrent().fpath, ':t')
3003 let text .= ' ' . filename
3004 endif
3005
3006 return text
3007endfunction
3008
3009" Debugging {{{1
3010" s:StartDebug() {{{2
3011function! s:StartDebug(filename)
3012 if empty(a:filename)
3013 let s:debug_file = 'tagbardebug.log'
3014 else
3015 let s:debug_file = a:filename
3016 endif
3017
3018 " Empty log file
3019 exe 'redir! > ' . s:debug_file
3020 redir END
3021
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 = ''
3026 return
3027 endif
3028
3029 let s:debug = 1
3030endfunction
3031
3032" s:StopDebug() {{{2
3033function! s:StopDebug()
3034 let s:debug = 0
3035 let s:debug_file = ''
3036endfunction
3037
3038" s:LogDebugMessage() {{{2
3039function! s:LogDebugMessage(msg)
3040 if s:debug
3041 exe 'redir >> ' . s:debug_file
3042 silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n"
3043 redir END
3044 endif
3045endfunction
3046
3047" Autoload functions {{{1
3048function! tagbar#ToggleWindow()
3049 call s:ToggleWindow()
3050endfunction
3051
3052function! tagbar#OpenWindow(...)
3053 let flags = a:0 > 0 ? a:1 : ''
3054 call s:OpenWindow(flags)
3055endfunction
3056
3057function! tagbar#CloseWindow()
3058 call s:CloseWindow()
3059endfunction
3060
3061function! tagbar#SetFoldLevel(...)
3062 call s:SetFoldLevel(a:1)
3063endfunction
3064
3065function! tagbar#OpenParents()
3066 call s:OpenParents()
3067endfunction
3068
3069function! tagbar#StartDebug(...)
3070 let filename = a:0 > 0 ? a:1 : ''
3071 call s:StartDebug(filename)
3072endfunction
3073
3074function! tagbar#StopDebug()
3075 call s:StopDebug()
3076endfunction
3077
3078function! tagbar#RestoreSession()
3079 call s:RestoreSession()
3080endfunction
3081
3082" Automatically open Tagbar if one of the open buffers contains a supported
3083" file
3084function! tagbar#autoopen()
3085 call s:Init()
3086
3087 for bufnr in range(1, bufnr('$'))
3088 if buflisted(bufnr)
3089 let ftype = s:DetectFiletype(bufnr)
3090 if s:IsValidFile(bufname(bufnr), ftype)
3091 call s:OpenWindow('')
3092 return
3093 endif
3094 endif
3095 endfor
3096endfunction
3097
3098" Modeline {{{1
3099" vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1