]>
Commit | Line | Data |
---|---|---|
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 |