]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/autoload/tagbar.vim
Relative line numbers for vim
[rbdr/dotfiles] / vim / autoload / tagbar.vim
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
21 scriptencoding utf-8
22
23 " Initialization {{{1
24
25 " Basic init {{{2
26
27 if !exists('g:tagbar_ctags_bin')
28 if executable('ctags-exuberant')
29 let g:tagbar_ctags_bin = 'ctags-exuberant'
30 elseif executable('exuberant-ctags')
31 let g:tagbar_ctags_bin = 'exuberant-ctags'
32 elseif executable('exctags')
33 let g:tagbar_ctags_bin = 'exctags'
34 elseif has('macunix') && executable('/usr/local/bin/ctags')
35 " Homebrew default location
36 let g:tagbar_ctags_bin = '/usr/local/bin/ctags'
37 elseif has('macunix') && executable('/opt/local/bin/ctags')
38 " Macports default location
39 let g:tagbar_ctags_bin = '/opt/local/bin/ctags'
40 elseif executable('ctags')
41 let g:tagbar_ctags_bin = 'ctags'
42 elseif executable('ctags.exe')
43 let g:tagbar_ctags_bin = 'ctags.exe'
44 elseif executable('tags')
45 let g:tagbar_ctags_bin = 'tags'
46 else
47 echomsg 'Tagbar: Exuberant ctags not found, skipping plugin'
48 finish
49 endif
50 else
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
64 endif
65
66 redir => s:ftype_out
67 silent filetype
68 redir END
69 if s:ftype_out !~# 'detection:ON'
70 echomsg 'Tagbar: Filetype detection is turned off, skipping plugin'
71 unlet s:ftype_out
72 finish
73 endif
74 unlet s:ftype_out
75
76 let s:icon_closed = g:tagbar_iconchars[0]
77 let s:icon_open = g:tagbar_iconchars[1]
78
79 let s:type_init_done = 0
80 let s:autocommands_done = 0
81 let s:checked_ctags = 0
82 let s:window_expanded = 0
83
84 let s:access_symbols = {
85 \ 'public' : '+',
86 \ 'protected' : '#',
87 \ 'private' : '-'
88 \ }
89
90 let g:loaded_tagbar = 1
91
92 let s:last_highlight_tline = 0
93 let s:debug = 0
94 let s:debug_file = ''
95
96 " s:Init() {{{2
97 function! 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
107 endfunction
108
109 " s:InitTypes() {{{2
110 function! 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
756 endfunction
757
758 " s:GetUserTypeDefs() {{{2
759 function! 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
809 endfunction
810
811 " s:RestoreSession() {{{2
812 " Properly restore Tagbar after a session got loaded
813 function! 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
839 endfunction
840
841 " s:MapKeys() {{{2
842 function! 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>
884 endfunction
885
886 " s:CreateAutocommands() {{{2
887 function! 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
907 endfunction
908
909 " s:CheckForExCtags() {{{2
910 " Test whether the ctags binary is actually Exuberant Ctags and not GNU ctags
911 " (or something else)
912 function! s:CheckForExCtags()
913 call s:LogDebugMessage('Checking for Exuberant Ctags')
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
952 endfunction
953
954 " s:CheckExCtagsVersion() {{{2
955 function! 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)
967 endfunction
968
969 " s:CheckFTCtags() {{{2
970 function! 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 ''
985 endfunction
986
987 " Prototypes {{{1
988 " Base tag {{{2
989 let s:BaseTag = {}
990
991 " s:BaseTag._init() {{{3
992 function! 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 = {}
1002 endfunction
1003
1004 " s:BaseTag.isNormalTag() {{{3
1005 function! s:BaseTag.isNormalTag() dict
1006 return 0
1007 endfunction
1008
1009 " s:BaseTag.isPseudoTag() {{{3
1010 function! s:BaseTag.isPseudoTag() dict
1011 return 0
1012 endfunction
1013
1014 " s:BaseTag.isKindheader() {{{3
1015 function! s:BaseTag.isKindheader() dict
1016 return 0
1017 endfunction
1018
1019 " s:BaseTag.getPrototype() {{{3
1020 function! s:BaseTag.getPrototype() dict
1021 return ''
1022 endfunction
1023
1024 " s:BaseTag._getPrefix() {{{3
1025 function! 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
1044 endfunction
1045
1046 " s:BaseTag.initFoldState() {{{3
1047 function! 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
1062 endfunction
1063
1064 " s:BaseTag.getClosedParentTline() {{{3
1065 function! 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
1079 endfunction
1080
1081 " s:BaseTag.isFoldable() {{{3
1082 function! s:BaseTag.isFoldable() dict
1083 return has_key(self, 'children') && !empty(self.children)
1084 endfunction
1085
1086 " s:BaseTag.isFolded() {{{3
1087 function! s:BaseTag.isFolded() dict
1088 return self.fileinfo.tagfolds[self.fields.kind][self.fullpath]
1089 endfunction
1090
1091 " s:BaseTag.openFold() {{{3
1092 function! s:BaseTag.openFold() dict
1093 if self.isFoldable()
1094 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = 0
1095 endif
1096 endfunction
1097
1098 " s:BaseTag.closeFold() {{{3
1099 function! 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
1118 endfunction
1119
1120 " s:BaseTag.setFolded() {{{3
1121 function! s:BaseTag.setFolded(folded) dict
1122 let self.fileinfo.tagfolds[self.fields.kind][self.fullpath] = a:folded
1123 endfunction
1124
1125 " s:BaseTag.openParents() {{{3
1126 function! 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
1133 endfunction
1134
1135 " Normal tag {{{2
1136 let s:NormalTag = copy(s:BaseTag)
1137
1138 " s:NormalTag.New() {{{3
1139 function! s:NormalTag.New(name) dict
1140 let newobj = copy(self)
1141
1142 call newobj._init(a:name)
1143
1144 return newobj
1145 endfunction
1146
1147 " s:NormalTag.isNormalTag() {{{3
1148 function! s:NormalTag.isNormalTag() dict
1149 return 1
1150 endfunction
1151
1152 " s:NormalTag.str() {{{3
1153 function! 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"
1166 endfunction
1167
1168 " s:NormalTag.getPrototype() {{{3
1169 function! s:NormalTag.getPrototype() dict
1170 return self.prototype
1171 endfunction
1172
1173 " Pseudo tag {{{2
1174 let s:PseudoTag = copy(s:BaseTag)
1175
1176 " s:PseudoTag.New() {{{3
1177 function! s:PseudoTag.New(name) dict
1178 let newobj = copy(self)
1179
1180 call newobj._init(a:name)
1181
1182 return newobj
1183 endfunction
1184
1185 " s:PseudoTag.isPseudoTag() {{{3
1186 function! s:PseudoTag.isPseudoTag() dict
1187 return 1
1188 endfunction
1189
1190 " s:PseudoTag.str() {{{3
1191 function! 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
1201 endfunction
1202
1203 " Kind header {{{2
1204 let s:KindheaderTag = copy(s:BaseTag)
1205
1206 " s:KindheaderTag.New() {{{3
1207 function! s:KindheaderTag.New(name) dict
1208 let newobj = copy(self)
1209
1210 call newobj._init(a:name)
1211
1212 return newobj
1213 endfunction
1214
1215 " s:KindheaderTag.isKindheader() {{{3
1216 function! s:KindheaderTag.isKindheader() dict
1217 return 1
1218 endfunction
1219
1220 " s:KindheaderTag.getPrototype() {{{3
1221 function! s:KindheaderTag.getPrototype() dict
1222 return self.name . ': ' .
1223 \ self.numtags . ' ' . (self.numtags > 1 ? 'tags' : 'tag')
1224 endfunction
1225
1226 " s:KindheaderTag.isFoldable() {{{3
1227 function! s:KindheaderTag.isFoldable() dict
1228 return 1
1229 endfunction
1230
1231 " s:KindheaderTag.isFolded() {{{3
1232 function! s:KindheaderTag.isFolded() dict
1233 return self.fileinfo.kindfolds[self.short]
1234 endfunction
1235
1236 " s:KindheaderTag.openFold() {{{3
1237 function! s:KindheaderTag.openFold() dict
1238 let self.fileinfo.kindfolds[self.short] = 0
1239 endfunction
1240
1241 " s:KindheaderTag.closeFold() {{{3
1242 function! s:KindheaderTag.closeFold() dict
1243 let self.fileinfo.kindfolds[self.short] = 1
1244 return line('.')
1245 endfunction
1246
1247 " s:KindheaderTag.toggleFold() {{{3
1248 function! s:KindheaderTag.toggleFold() dict
1249 let fileinfo = s:known_files.getCurrent()
1250
1251 let fileinfo.kindfolds[self.short] = !fileinfo.kindfolds[self.short]
1252 endfunction
1253
1254 " File info {{{2
1255 let s:FileInfo = {}
1256
1257 " s:FileInfo.New() {{{3
1258 function! 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
1300 endfunction
1301
1302 " s:FileInfo.reset() {{{3
1303 " Reset stuff that gets regenerated while processing a file and save the old
1304 " tag folds
1305 function! 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
1318 endfunction
1319
1320 " s:FileInfo.clearOldFolds() {{{3
1321 function! s:FileInfo.clearOldFolds() dict
1322 if exists('self._tagfolds_old')
1323 unlet self._tagfolds_old
1324 endif
1325 endfunction
1326
1327 " s:FileInfo.sortTags() {{{3
1328 function! s:FileInfo.sortTags() dict
1329 if has_key(s:compare_typeinfo, 'sort')
1330 if s:compare_typeinfo.sort
1331 call s:SortTags(self.tags, 's:CompareByKind')
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
1340 endfunction
1341
1342 " s:FileInfo.openKindFold() {{{3
1343 function! s:FileInfo.openKindFold(kind) dict
1344 let self.kindfolds[a:kind.short] = 0
1345 endfunction
1346
1347 " s:FileInfo.closeKindFold() {{{3
1348 function! s:FileInfo.closeKindFold(kind) dict
1349 let self.kindfolds[a:kind.short] = 1
1350 endfunction
1351
1352 " Known files {{{2
1353 let s:known_files = {
1354 \ '_current' : {},
1355 \ '_files' : {}
1356 \ }
1357
1358 " s:known_files.getCurrent() {{{3
1359 function! s:known_files.getCurrent() dict
1360 return self._current
1361 endfunction
1362
1363 " s:known_files.setCurrent() {{{3
1364 function! s:known_files.setCurrent(fileinfo) dict
1365 let self._current = a:fileinfo
1366 endfunction
1367
1368 " s:known_files.get() {{{3
1369 function! s:known_files.get(fname) dict
1370 return get(self._files, a:fname, {})
1371 endfunction
1372
1373 " s:known_files.put() {{{3
1374 " Optional second argument is the filename
1375 function! 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
1382 endfunction
1383
1384 " s:known_files.has() {{{3
1385 function! s:known_files.has(fname) dict
1386 return has_key(self._files, a:fname)
1387 endfunction
1388
1389 " s:known_files.rm() {{{3
1390 function! s:known_files.rm(fname) dict
1391 if s:known_files.has(a:fname)
1392 call remove(self._files, a:fname)
1393 endif
1394 endfunction
1395
1396 " Window management {{{1
1397 " s:ToggleWindow() {{{2
1398 function! s:ToggleWindow()
1399 let tagbarwinnr = bufwinnr("__Tagbar__")
1400 if tagbarwinnr != -1
1401 call s:CloseWindow()
1402 return
1403 endif
1404
1405 call s:OpenWindow('')
1406 endfunction
1407
1408 " s:OpenWindow() {{{2
1409 function! s:OpenWindow(flags)
1410 let autofocus = a:flags =~# 'f'
1411 let jump = a:flags =~# 'j'
1412 let autoclose = a:flags =~# 'c'
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
1453 endfunction
1454
1455 " s:InitWindow() {{{2
1456 function! s:InitWindow(autoclose)
1457 setlocal noreadonly " in case the "view" mode is used
1458 setlocal buftype=nofile
1459 setlocal bufhidden=hide
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
1517 endfunction
1518
1519 " s:CloseWindow() {{{2
1520 function! 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
1561 endfunction
1562
1563 " s:ZoomWindow() {{{2
1564 function! 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
1572 endfunction
1573
1574 " Tag processing {{{1
1575 " s:ProcessFile() {{{2
1576 " Execute ctags and put the information into a 'FileInfo' object
1577 function! s:ProcessFile(fname, ftype)
1578 call s:LogDebugMessage('ProcessFile called on ' . a:fname)
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)
1682 endfunction
1683
1684 " s:ExecuteCtagsOnFile() {{{2
1685 function! 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
1749 endfunction
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
1756 function! 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
1822 endfunction
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.
1833 function! 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
1919 endfunction
1920
1921 " s:ProcessPseudoTag() {{{2
1922 function! 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
1940 endfunction
1941
1942 " s:ProcessPseudoChildren() {{{2
1943 function! s:ProcessPseudoChildren(tags, tag, depth, typeinfo, fileinfo)
1944 for childtag in a:tag.children
1945 let childtag.parent = a:tag
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
1966 endfunction
1967
1968 " s:CreatePseudoTag() {{{2
1969 function! 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
2001 endfunction
2002
2003 " Sorting {{{1
2004 " s:SortTags() {{{2
2005 function! 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
2013 endfunction
2014
2015 " s:CompareByKind() {{{2
2016 function! 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
2045 endfunction
2046
2047 " s:CompareByLine() {{{2
2048 function! s:CompareByLine(tag1, tag2)
2049 return a:tag1.fields.line - a:tag2.fields.line
2050 endfunction
2051
2052 " s:ToggleSort() {{{2
2053 function! 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
2076 endfunction
2077
2078 " Display {{{1
2079 " s:RenderContent() {{{2
2080 function! 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
2166 endfunction
2167
2168 " s:PrintKinds() {{{2
2169 function! 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
2266 endfunction
2267
2268 " s:PrintTag() {{{2
2269 function! s:PrintTag(tag, depth, fileinfo, typeinfo)
2270 " Print tag indented according to depth
2271 silent put =repeat(' ', a:depth * 2) . a:tag.str()
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
2298 endfunction
2299
2300 " s:PrintHelp() {{{2
2301 function! 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
2326 endfunction
2327
2328 " s:RenderKeepView() {{{2
2329 " The gist of this function was taken from NERDTree by Martin Grenfell.
2330 function! 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
2352 endfunction
2353
2354 " User actions {{{1
2355 " s:HighlightTag() {{{2
2356 function! 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
2413 endfunction
2414
2415 " s:JumpToTag() {{{2
2416 function! 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
2506 endfunction
2507
2508 " s:ShowPrototype() {{{2
2509 function! s:ShowPrototype()
2510 let taginfo = s:GetTagInfo(line('.'), 1)
2511
2512 if empty(taginfo)
2513 return
2514 endif
2515
2516 echo taginfo.getPrototype()
2517 endfunction
2518
2519 " s:ToggleHelp() {{{2
2520 function! 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
2530 endfunction
2531
2532 " s:GotoNextToplevelTag() {{{2
2533 function! 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
2560 endfunction
2561
2562 " Folding {{{1
2563 " s:OpenFold() {{{2
2564 function! 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()
2580 endfunction
2581
2582 " s:CloseFold() {{{2
2583 function! 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)
2601 endfunction
2602
2603 " s:ToggleFold() {{{2
2604 function! 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)
2632 endfunction
2633
2634 " s:SetFoldLevel() {{{2
2635 function! 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()
2664 endfunction
2665
2666 " s:SetFoldLevelRecursive() {{{2
2667 " Apply foldlevel to normal tags
2668 function! 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
2680 endfunction
2681
2682 " s:OpenParents() {{{2
2683 function! 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()
2695 endfunction
2696
2697 " Helper functions {{{1
2698 " s:CleanUp() {{{2
2699 function! s:CleanUp()
2700 silent autocmd! TagbarAutoCmds
2701
2702 unlet s:is_maximized
2703 unlet s:compare_typeinfo
2704 unlet s:short_help
2705 endfunction
2706
2707 " s:CleanupFileinfo() {{{2
2708 function! s:CleanupFileinfo(fname)
2709 call s:known_files.rm(a:fname)
2710 endfunction
2711
2712 " s:QuitIfOnlyWindow() {{{2
2713 function! s:QuitIfOnlyWindow()
2714 " Before quitting Vim, delete the tagbar buffer so that
2715 " the '0 mark is correctly set to the previous buffer.
2716 if winbufnr(2) == -1
2717 " Check if there is more than one tab page
2718 if tabpagenr('$') == 1
2719 bdelete
2720 quit
2721 else
2722 close
2723 endif
2724 endif
2725 endfunction
2726
2727 " s:AutoUpdate() {{{2
2728 function! 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')
2783 endfunction
2784
2785 " s:IsValidFile() {{{2
2786 function! 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
2803 endfunction
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
2809 function! s:EscapeCtagsCmd(ctags_bin, args, ...)
2810 call s:LogDebugMessage('EscapeCtagsCmd called')
2811 call s:LogDebugMessage('ctags_bin: ' . a:ctags_bin)
2812 call s:LogDebugMessage('ctags_args: ' . a:args)
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
2848 endfunction
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
2854 function! 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
2879 endfunction
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.
2885 function! 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
2911 endfunction
2912
2913 " s:GetNearbyTag() {{{2
2914 " Get the tag info for a file near the cursor in the current file
2915 function! s:GetNearbyTag()
2916 let fileinfo = s:known_files.getCurrent()
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
2934 endfunction
2935
2936 " s:CheckMouseClick() {{{2
2937 function! 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
2948 endfunction
2949
2950 " s:DetermineFiletype() {{{2
2951 function! s:DetectFiletype(bufnr)
2952 " Filetype has already been detected for loaded buffers, but not
2953 " necessarily for unloaded ones
2954 let ftype = getbufvar(a:bufnr, '&filetype')
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
2979 endfunction
2980
2981
2982 " TagbarBalloonExpr() {{{2
2983 function! TagbarBalloonExpr()
2984 let taginfo = s:GetTagInfo(v:beval_lnum, 1)
2985
2986 if empty(taginfo)
2987 return
2988 endif
2989
2990 return taginfo.getPrototype()
2991 endfunction
2992
2993 " TagbarGenerateStatusline() {{{2
2994 function! 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
3007 endfunction
3008
3009 " Debugging {{{1
3010 " s:StartDebug() {{{2
3011 function! 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
3030 endfunction
3031
3032 " s:StopDebug() {{{2
3033 function! s:StopDebug()
3034 let s:debug = 0
3035 let s:debug_file = ''
3036 endfunction
3037
3038 " s:LogDebugMessage() {{{2
3039 function! 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
3045 endfunction
3046
3047 " Autoload functions {{{1
3048 function! tagbar#ToggleWindow()
3049 call s:ToggleWindow()
3050 endfunction
3051
3052 function! tagbar#OpenWindow(...)
3053 let flags = a:0 > 0 ? a:1 : ''
3054 call s:OpenWindow(flags)
3055 endfunction
3056
3057 function! tagbar#CloseWindow()
3058 call s:CloseWindow()
3059 endfunction
3060
3061 function! tagbar#SetFoldLevel(...)
3062 call s:SetFoldLevel(a:1)
3063 endfunction
3064
3065 function! tagbar#OpenParents()
3066 call s:OpenParents()
3067 endfunction
3068
3069 function! tagbar#StartDebug(...)
3070 let filename = a:0 > 0 ? a:1 : ''
3071 call s:StartDebug(filename)
3072 endfunction
3073
3074 function! tagbar#StopDebug()
3075 call s:StopDebug()
3076 endfunction
3077
3078 function! tagbar#RestoreSession()
3079 call s:RestoreSession()
3080 endfunction
3081
3082 " Automatically open Tagbar if one of the open buffers contains a supported
3083 " file
3084 function! 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
3096 endfunction
3097
3098 " Modeline {{{1
3099 " vim: ts=8 sw=4 sts=4 et foldenable foldmethod=marker foldcolumn=1