Use a darker shade of blue for listchars characters.
[profile.git] / .vim / plugin / taglist.vim
1 " File: taglist.vim
2 " Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
3 " Version: 4.5
4 " Last Modified: September 21, 2007
5 " Copyright: Copyright (C) 2002-2007 Yegappan Lakshmanan
6 "            Permission is hereby granted to use and distribute this code,
7 "            with or without modifications, provided that this copyright
8 "            notice is copied with it. Like anything else that's free,
9 "            taglist.vim is provided *as is* and comes with no warranty of any
10 "            kind, either expressed or implied. In no event will the copyright
11 "            holder be liable for any damamges resulting from the use of this
12 "            software.
13 "
14 " The "Tag List" plugin is a source code browser plugin for Vim and provides
15 " an overview of the structure of the programming language files and allows
16 " you to efficiently browse through source code files for different
17 " programming languages.  You can visit the taglist plugin home page for more
18 " information:
19 "
20 "       http://vim-taglist.sourceforge.net
21 "
22 " You can subscribe to the taglist mailing list to post your questions
23 " or suggestions for improvement or to report bugs. Visit the following
24 " page for subscribing to the mailing list:
25 "
26 "       http://groups.yahoo.com/group/taglist/
27 "
28 " For more information about using this plugin, after installing the
29 " taglist plugin, use the ":help taglist" command.
30 "
31 " Installation
32 " ------------
33 " 1. Download the taglist.zip file and unzip the files to the $HOME/.vim
34 "    or the $HOME/vimfiles or the $VIM/vimfiles directory. This should
35 "    unzip the following two files (the directory structure should be
36 "    preserved):
37 "
38 "       plugin/taglist.vim - main taglist plugin file
39 "       doc/taglist.txt    - documentation (help) file
40 "
41 "    Refer to the 'add-plugin', 'add-global-plugin' and 'runtimepath'
42 "    Vim help pages for more details about installing Vim plugins.
43 " 2. Change to the $HOME/.vim/doc or $HOME/vimfiles/doc or
44 "    $VIM/vimfiles/doc directory, start Vim and run the ":helptags ."
45 "    command to process the taglist help file.
46 " 3. If the exuberant ctags utility is not present in your PATH, then set the
47 "    Tlist_Ctags_Cmd variable to point to the location of the exuberant ctags
48 "    utility (not to the directory) in the .vimrc file.
49 " 4. If you are running a terminal/console version of Vim and the
50 "    terminal doesn't support changing the window width then set the
51 "    'Tlist_Inc_Winwidth' variable to 0 in the .vimrc file.
52 " 5. Restart Vim.
53 " 6. You can now use the ":TlistToggle" command to open/close the taglist
54 "    window. You can use the ":help taglist" command to get more
55 "    information about using the taglist plugin.
56 "
57 " ****************** Do not modify after this line ************************
58
59 " Line continuation used here
60 let s:cpo_save = &cpo
61 set cpo&vim
62
63 if !exists('loaded_taglist')
64     " First time loading the taglist plugin
65     "
66     " To speed up the loading of Vim, the taglist plugin uses autoload
67     " mechanism to load the taglist functions.
68     " Only define the configuration variables, user commands and some
69     " auto-commands and finish sourcing the file
70
71     " The taglist plugin requires the built-in Vim system() function. If this
72     " function is not available, then don't load the plugin.
73     if !exists('*system')
74         echomsg 'Taglist: Vim system() built-in function is not available. ' .
75                     \ 'Plugin is not loaded.'
76         let loaded_taglist = 'no'
77         let &cpo = s:cpo_save
78         finish
79     endif
80
81     " Location of the exuberant ctags tool
82     if !exists('Tlist_Ctags_Cmd')
83         if executable('exuberant-ctags')
84             " On Debian Linux, exuberant ctags is installed
85             " as exuberant-ctags
86             let Tlist_Ctags_Cmd = 'exuberant-ctags'
87         elseif executable('exctags')
88             " On Free-BSD, exuberant ctags is installed as exctags
89             let Tlist_Ctags_Cmd = 'exctags'
90         elseif executable('ctags')
91             let Tlist_Ctags_Cmd = 'ctags'
92         elseif executable('ctags.exe')
93             let Tlist_Ctags_Cmd = 'ctags.exe'
94         elseif executable('tags')
95             let Tlist_Ctags_Cmd = 'tags'
96         else
97             "echomsg 'Taglist: Exuberant ctags (http://ctags.sf.net) ' .
98             "            \ 'not found in PATH. Plugin is not loaded.'
99             " Skip loading the plugin
100             let loaded_taglist = 'no'
101             let &cpo = s:cpo_save
102             finish
103         endif
104     endif
105
106
107     " Automatically open the taglist window on Vim startup
108     if !exists('Tlist_Auto_Open')
109         let Tlist_Auto_Open = 0
110     endif
111
112     " When the taglist window is toggle opened, move the cursor to the
113     " taglist window
114     if !exists('Tlist_GainFocus_On_ToggleOpen')
115         let Tlist_GainFocus_On_ToggleOpen = 0
116     endif
117
118     " Process files even when the taglist window is not open
119     if !exists('Tlist_Process_File_Always')
120         let Tlist_Process_File_Always = 0
121     endif
122
123     if !exists('Tlist_Show_Menu')
124         let Tlist_Show_Menu = 0
125     endif
126
127     " Tag listing sort type - 'name' or 'order'
128     if !exists('Tlist_Sort_Type')
129         let Tlist_Sort_Type = 'order'
130     endif
131
132     " Tag listing window split (horizontal/vertical) control
133     if !exists('Tlist_Use_Horiz_Window')
134         let Tlist_Use_Horiz_Window = 0
135     endif
136
137     " Open the vertically split taglist window on the left or on the right
138     " side.  This setting is relevant only if Tlist_Use_Horiz_Window is set to
139     " zero (i.e.  only for vertically split windows)
140     if !exists('Tlist_Use_Right_Window')
141         let Tlist_Use_Right_Window = 0
142     endif
143
144     " Increase Vim window width to display vertically split taglist window.
145     " For MS-Windows version of Vim running in a MS-DOS window, this must be
146     " set to 0 otherwise the system may hang due to a Vim limitation.
147     if !exists('Tlist_Inc_Winwidth')
148         if (has('win16') || has('win95')) && !has('gui_running')
149             let Tlist_Inc_Winwidth = 0
150         else
151             let Tlist_Inc_Winwidth = 1
152         endif
153     endif
154
155     " Vertically split taglist window width setting
156     if !exists('Tlist_WinWidth')
157         let Tlist_WinWidth = 30
158     endif
159
160     " Horizontally split taglist window height setting
161     if !exists('Tlist_WinHeight')
162         let Tlist_WinHeight = 10
163     endif
164
165     " Display tag prototypes or tag names in the taglist window
166     if !exists('Tlist_Display_Prototype')
167         let Tlist_Display_Prototype = 0
168     endif
169
170     " Display tag scopes in the taglist window
171     if !exists('Tlist_Display_Tag_Scope')
172         let Tlist_Display_Tag_Scope = 1
173     endif
174
175     " Use single left mouse click to jump to a tag. By default this is disabled.
176     " Only double click using the mouse will be processed.
177     if !exists('Tlist_Use_SingleClick')
178         let Tlist_Use_SingleClick = 0
179     endif
180
181     " Control whether additional help is displayed as part of the taglist or
182     " not.  Also, controls whether empty lines are used to separate the tag
183     " tree.
184     if !exists('Tlist_Compact_Format')
185         let Tlist_Compact_Format = 0
186     endif
187
188     " Exit Vim if only the taglist window is currently open. By default, this is
189     " set to zero.
190     if !exists('Tlist_Exit_OnlyWindow')
191         let Tlist_Exit_OnlyWindow = 0
192     endif
193
194     " Automatically close the folds for the non-active files in the taglist
195     " window
196     if !exists('Tlist_File_Fold_Auto_Close')
197         let Tlist_File_Fold_Auto_Close = 0
198     endif
199
200     " Close the taglist window when a tag is selected
201     if !exists('Tlist_Close_On_Select')
202         let Tlist_Close_On_Select = 0
203     endif
204
205     " Automatically update the taglist window to display tags for newly
206     " edited files
207     if !exists('Tlist_Auto_Update')
208         let Tlist_Auto_Update = 1
209     endif
210
211     " Automatically highlight the current tag
212     if !exists('Tlist_Auto_Highlight_Tag')
213         let Tlist_Auto_Highlight_Tag = 1
214     endif
215     
216     " Automatically highlight the current tag on entering a buffer
217     if !exists('Tlist_Highlight_Tag_On_BufEnter')
218         let Tlist_Highlight_Tag_On_BufEnter = 1
219     endif
220
221     " Enable fold column to display the folding for the tag tree
222     if !exists('Tlist_Enable_Fold_Column')
223         let Tlist_Enable_Fold_Column = 1
224     endif
225
226     " Display the tags for only one file in the taglist window
227     if !exists('Tlist_Show_One_File')
228         let Tlist_Show_One_File = 0
229     endif
230
231     if !exists('Tlist_Max_Submenu_Items')
232         let Tlist_Max_Submenu_Items = 20
233     endif
234
235     if !exists('Tlist_Max_Tag_Length')
236         let Tlist_Max_Tag_Length = 10
237     endif
238
239     " Do not change the name of the taglist title variable. The winmanager
240     " plugin relies on this name to determine the title for the taglist
241     " plugin.
242     let TagList_title = "__Tag_List__"
243
244     " Taglist debug messages
245     let s:tlist_msg = ''
246
247     " Define the taglist autocommand to automatically open the taglist window
248     " on Vim startup
249     if g:Tlist_Auto_Open
250         autocmd VimEnter * nested call s:Tlist_Window_Check_Auto_Open()
251     endif
252
253     " Refresh the taglist
254     if g:Tlist_Process_File_Always
255         autocmd BufEnter * call s:Tlist_Refresh()
256     endif
257
258     if g:Tlist_Show_Menu
259         autocmd GUIEnter * call s:Tlist_Menu_Init()
260     endif
261
262     " When the taglist buffer is created when loading a Vim session file,
263     " the taglist buffer needs to be initialized. The BufFilePost event
264     " is used to handle this case.
265     autocmd BufFilePost __Tag_List__ call s:Tlist_Vim_Session_Load()
266
267     " Define the user commands to manage the taglist window
268     command! -nargs=0 -bar TlistToggle call s:Tlist_Window_Toggle()
269     command! -nargs=0 -bar TlistOpen call s:Tlist_Window_Open()
270     " For backwards compatiblity define the Tlist command
271     command! -nargs=0 -bar Tlist TlistToggle
272     command! -nargs=+ -complete=file TlistAddFiles
273                 \  call s:Tlist_Add_Files(<f-args>)
274     command! -nargs=+ -complete=dir TlistAddFilesRecursive
275                 \ call s:Tlist_Add_Files_Recursive(<f-args>)
276     command! -nargs=0 -bar TlistClose call s:Tlist_Window_Close()
277     command! -nargs=0 -bar TlistUpdate call s:Tlist_Update_Current_File()
278     command! -nargs=0 -bar TlistHighlightTag call s:Tlist_Window_Highlight_Tag(
279                         \ fnamemodify(bufname('%'), ':p'), line('.'), 2, 1)
280     " For backwards compatiblity define the TlistSync command
281     command! -nargs=0 -bar TlistSync TlistHighlightTag
282     command! -nargs=* -complete=buffer TlistShowPrototype
283                 \ echo Tlist_Get_Tag_Prototype_By_Line(<f-args>)
284     command! -nargs=* -complete=buffer TlistShowTag
285                 \ echo Tlist_Get_Tagname_By_Line(<f-args>)
286     command! -nargs=* -complete=file TlistSessionLoad
287                 \ call s:Tlist_Session_Load(<q-args>)
288     command! -nargs=* -complete=file TlistSessionSave
289                 \ call s:Tlist_Session_Save(<q-args>)
290     command! -bar TlistLock let Tlist_Auto_Update=0
291     command! -bar TlistUnlock let Tlist_Auto_Update=1
292
293     " Commands for enabling/disabling debug and to display debug messages
294     command! -nargs=? -complete=file -bar TlistDebug
295                 \ call s:Tlist_Debug_Enable(<q-args>)
296     command! -nargs=0 -bar TlistUndebug  call s:Tlist_Debug_Disable()
297     command! -nargs=0 -bar TlistMessages call s:Tlist_Debug_Show()
298
299     " Define autocommands to autoload the taglist plugin when needed.
300
301     " Trick to get the current script ID
302     map <SID>xx <SID>xx
303     let s:tlist_sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$',
304                                 \ '\1', '')
305     unmap <SID>xx
306
307     exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_* source ' .
308                 \ escape(expand('<sfile>'), ' ')
309     exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Window_* source ' .
310                 \ escape(expand('<sfile>'), ' ')
311     exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Menu_* source ' .
312                 \ escape(expand('<sfile>'), ' ')
313     exe 'autocmd FuncUndefined Tlist_* source ' .
314                 \ escape(expand('<sfile>'), ' ')
315     exe 'autocmd FuncUndefined TagList_* source ' .
316                 \ escape(expand('<sfile>'), ' ')
317
318     let loaded_taglist = 'fast_load_done'
319
320     if g:Tlist_Show_Menu && has('gui_running')
321         call s:Tlist_Menu_Init()
322     endif
323
324     " restore 'cpo'
325     let &cpo = s:cpo_save
326     finish
327 endif
328
329 if !exists('s:tlist_sid')
330     " Two or more versions of taglist plugin are installed. Don't
331     " load this version of the plugin.
332     finish
333 endif
334
335 unlet! s:tlist_sid
336
337 if loaded_taglist != 'fast_load_done'
338     " restore 'cpo'
339     let &cpo = s:cpo_save
340     finish
341 endif
342
343 " Taglist plugin functionality is available
344 let loaded_taglist = 'available'
345
346 "------------------- end of user configurable options --------------------
347
348 " Default language specific settings for supported file types and tag types
349 "
350 " Variable name format:
351 "
352 "       s:tlist_def_{vim_ftype}_settings
353
354 " vim_ftype - Filetype detected by Vim
355 "
356 " Value format:
357 "
358 "       <ctags_ftype>;<flag>:<name>;<flag>:<name>;...
359 "
360 " ctags_ftype - File type supported by exuberant ctags
361 " flag        - Flag supported by exuberant ctags to generate a tag type
362 " name        - Name of the tag type used in the taglist window to display the
363 "               tags of this type
364 "
365
366 " assembly language
367 let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type'
368
369 " aspperl language
370 let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable'
371
372 " aspvbs language
373 let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable'
374
375 " awk language
376 let s:tlist_def_awk_settings = 'awk;f:function'
377
378 " beta language
379 let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern'
380
381 " c language
382 let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' .
383                            \ 'v:variable;f:function'
384
385 " c++ language
386 let s:tlist_def_cpp_settings = 'c++;n:namespace;v:variable;d:macro;t:typedef;' .
387                              \ 'c:class;g:enum;s:struct;u:union;f:function'
388
389 " c# language
390 let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' .
391                              \ 'E:event;g:enum;s:struct;i:interface;' .
392                              \ 'p:properties;m:method'
393
394 " cobol language
395 let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' .
396                                \ 'P:program;s:section'
397
398 " eiffel language
399 let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature'
400
401 " erlang language
402 let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function'
403
404 " expect (same as tcl) language
405 let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure'
406
407 " fortran language
408 let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' .
409                     \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' .
410                     \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine'
411
412 " HTML language
413 let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function'
414
415 " java language
416 let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' .
417                               \ 'f:field;m:method'
418
419 " javascript language
420 let s:tlist_def_javascript_settings = 'javascript;f:function'
421
422 " lisp language
423 let s:tlist_def_lisp_settings = 'lisp;f:function'
424
425 " lua language
426 let s:tlist_def_lua_settings = 'lua;f:function'
427
428 " makefiles
429 let s:tlist_def_make_settings = 'make;m:macro'
430
431 " pascal language
432 let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure'
433
434 " perl language
435 let s:tlist_def_perl_settings = 'perl;c:constant;l:label;p:package;s:subroutine'
436
437 " php language
438 let s:tlist_def_php_settings = 'php;c:class;d:constant;v:variable;f:function'
439
440 " python language
441 let s:tlist_def_python_settings = 'python;c:class;m:member;f:function'
442
443 " rexx language
444 let s:tlist_def_rexx_settings = 'rexx;s:subroutine'
445
446 " ruby language
447 let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' .
448                               \ 'm:singleton method'
449
450 " scheme language
451 let s:tlist_def_scheme_settings = 'scheme;s:set;f:function'
452
453 " shell language
454 let s:tlist_def_sh_settings = 'sh;f:function'
455
456 " C shell language
457 let s:tlist_def_csh_settings = 'sh;f:function'
458
459 " Z shell language
460 let s:tlist_def_zsh_settings = 'sh;f:function'
461
462 " slang language
463 let s:tlist_def_slang_settings = 'slang;n:namespace;f:function'
464
465 " sml language
466 let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' .
467                              \ 'r:structure;t:type;v:value;f:function'
468
469 " sql language
470 let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' .
471             \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure'
472
473 " tcl language
474 let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;m:method;p:procedure'
475
476 " vera language
477 let s:tlist_def_vera_settings = 'vera;c:class;d:macro;e:enumerator;' .
478                                 \ 'f:function;g:enum;m:member;p:program;' .
479                                 \ 'P:prototype;t:task;T:typedef;v:variable;' .
480                                 \ 'x:externvar'
481
482 "verilog language
483 let s:tlist_def_verilog_settings = 'verilog;m:module;c:constant;P:parameter;' .
484             \ 'e:event;r:register;t:task;w:write;p:port;v:variable;f:function'
485
486 " vim language
487 let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function'
488
489 " yacc language
490 let s:tlist_def_yacc_settings = 'yacc;l:label'
491
492 "------------------- end of language specific options --------------------
493
494 " Vim window size is changed by the taglist plugin or not
495 let s:tlist_winsize_chgd = -1
496 " Taglist window is maximized or not
497 let s:tlist_win_maximized = 0
498 " Name of files in the taglist
499 let s:tlist_file_names=''
500 " Number of files in the taglist
501 let s:tlist_file_count = 0
502 " Number of filetypes supported by taglist
503 let s:tlist_ftype_count = 0
504 " Is taglist part of other plugins like winmanager or cream?
505 let s:tlist_app_name = "none"
506 " Are we displaying brief help text
507 let s:tlist_brief_help = 1
508 " List of files removed on user request
509 let s:tlist_removed_flist = ""
510 " Index of current file displayed in the taglist window
511 let s:tlist_cur_file_idx = -1
512 " Taglist menu is empty or not
513 let s:tlist_menu_empty = 1
514
515 " An autocommand is used to refresh the taglist window when entering any
516 " buffer. We don't want to refresh the taglist window if we are entering the
517 " file window from one of the taglist functions. The 'Tlist_Skip_Refresh'
518 " variable is used to skip the refresh of the taglist window and is set
519 " and cleared appropriately.
520 let s:Tlist_Skip_Refresh = 0
521
522 " Tlist_Window_Display_Help()
523 function! s:Tlist_Window_Display_Help()
524     if s:tlist_app_name == "winmanager"
525         " To handle a bug in the winmanager plugin, add a space at the
526         " last line
527         call setline('$', ' ')
528     endif
529
530     if s:tlist_brief_help
531         " Add the brief help
532         call append(0, '" Press <F1> to display help text')
533     else
534         " Add the extensive help
535         call append(0, '" <enter> : Jump to tag definition')
536         call append(1, '" o : Jump to tag definition in new window')
537         call append(2, '" p : Preview the tag definition')
538         call append(3, '" <space> : Display tag prototype')
539         call append(4, '" u : Update tag list')
540         call append(5, '" s : Select sort field')
541         call append(6, '" d : Remove file from taglist')
542         call append(7, '" x : Zoom-out/Zoom-in taglist window')
543         call append(8, '" + : Open a fold')
544         call append(9, '" - : Close a fold')
545         call append(10, '" * : Open all folds')
546         call append(11, '" = : Close all folds')
547         call append(12, '" [[ : Move to the start of previous file')
548         call append(13, '" ]] : Move to the start of next file')
549         call append(14, '" q : Close the taglist window')
550         call append(15, '" <F1> : Remove help text')
551     endif
552 endfunction
553
554 " Tlist_Window_Toggle_Help_Text()
555 " Toggle taglist plugin help text between the full version and the brief
556 " version
557 function! s:Tlist_Window_Toggle_Help_Text()
558     if g:Tlist_Compact_Format
559         " In compact display mode, do not display help
560         return
561     endif
562
563     " Include the empty line displayed after the help text
564     let brief_help_size = 1
565     let full_help_size = 16
566
567     setlocal modifiable
568
569     " Set report option to a huge value to prevent informational messages
570     " while deleting the lines
571     let old_report = &report
572     set report=99999
573
574     " Remove the currently highlighted tag. Otherwise, the help text
575     " might be highlighted by mistake
576     match none
577
578     " Toggle between brief and full help text
579     if s:tlist_brief_help
580         let s:tlist_brief_help = 0
581
582         " Remove the previous help
583         exe '1,' . brief_help_size . ' delete _'
584
585         " Adjust the start/end line numbers for the files
586         call s:Tlist_Window_Update_Line_Offsets(0, 1, full_help_size - brief_help_size)
587     else
588         let s:tlist_brief_help = 1
589
590         " Remove the previous help
591         exe '1,' . full_help_size . ' delete _'
592
593         " Adjust the start/end line numbers for the files
594         call s:Tlist_Window_Update_Line_Offsets(0, 0, full_help_size - brief_help_size)
595     endif
596
597     call s:Tlist_Window_Display_Help()
598
599     " Restore the report option
600     let &report = old_report
601
602     setlocal nomodifiable
603 endfunction
604
605 " Taglist debug support
606 let s:tlist_debug = 0
607
608 " File for storing the debug messages
609 let s:tlist_debug_file = ''
610
611 " Tlist_Debug_Enable
612 " Enable logging of taglist debug messages.
613 function! s:Tlist_Debug_Enable(...)
614     let s:tlist_debug = 1
615
616     " Check whether a valid file name is supplied.
617     if a:1 != ''
618         let s:tlist_debug_file = fnamemodify(a:1, ':p')
619
620         " Empty the log file
621         exe 'redir! > ' . s:tlist_debug_file
622         redir END
623
624         " Check whether the log file is present/created
625         if !filewritable(s:tlist_debug_file)
626             call s:Tlist_Warning_Msg('Taglist: Unable to create log file '
627                         \ . s:tlist_debug_file)
628             let s:tlist_debug_file = ''
629         endif
630     endif
631 endfunction
632
633 " Tlist_Debug_Disable
634 " Disable logging of taglist debug messages.
635 function! s:Tlist_Debug_Disable(...)
636     let s:tlist_debug = 0
637     let s:tlist_debug_file = ''
638 endfunction
639
640 " Tlist_Debug_Show
641 " Display the taglist debug messages in a new window
642 function! s:Tlist_Debug_Show()
643     if s:tlist_msg == ''
644         call s:Tlist_Warning_Msg('Taglist: No debug messages')
645         return
646     endif
647
648     " Open a new window to display the taglist debug messages
649     new taglist_debug.txt
650     " Delete all the lines (if the buffer already exists)
651     silent! %delete _
652     " Add the messages
653     silent! put =s:tlist_msg
654     " Move the cursor to the first line
655     normal! gg
656 endfunction
657
658 " Tlist_Log_Msg
659 " Log the supplied debug message along with the time
660 function! s:Tlist_Log_Msg(msg)
661     if s:tlist_debug
662         if s:tlist_debug_file != ''
663             exe 'redir >> ' . s:tlist_debug_file
664             silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n"
665             redir END
666         else
667             " Log the message into a variable
668             " Retain only the last 3000 characters
669             let len = strlen(s:tlist_msg)
670             if len > 3000
671                 let s:tlist_msg = strpart(s:tlist_msg, len - 3000)
672             endif
673             let s:tlist_msg = s:tlist_msg . strftime('%H:%M:%S') . ': ' . 
674                         \ a:msg . "\n"
675         endif
676     endif
677 endfunction
678
679 " Tlist_Warning_Msg()
680 " Display a message using WarningMsg highlight group
681 function! s:Tlist_Warning_Msg(msg)
682     echohl WarningMsg
683     echomsg a:msg
684     echohl None
685 endfunction
686
687 " Last returned file index for file name lookup.
688 " Used to speed up file lookup
689 let s:tlist_file_name_idx_cache = -1
690
691 " Tlist_Get_File_Index()
692 " Return the index of the specified filename
693 function! s:Tlist_Get_File_Index(fname)
694     if s:tlist_file_count == 0 || a:fname == ''
695         return -1
696     endif
697
698     " If the new filename is same as the last accessed filename, then
699     " return that index
700     if s:tlist_file_name_idx_cache != -1 &&
701                 \ s:tlist_file_name_idx_cache < s:tlist_file_count
702         if s:tlist_{s:tlist_file_name_idx_cache}_filename == a:fname
703             " Same as the last accessed file
704             return s:tlist_file_name_idx_cache
705         endif
706     endif
707
708     " First, check whether the filename is present
709     let s_fname = a:fname . "\n"
710     let i = stridx(s:tlist_file_names, s_fname)
711     if i == -1
712         let s:tlist_file_name_idx_cache = -1
713         return -1
714     endif
715
716     " Second, compute the file name index
717     let nl_txt = substitute(strpart(s:tlist_file_names, 0, i), "[^\n]", '', 'g')
718     let s:tlist_file_name_idx_cache = strlen(nl_txt)
719     return s:tlist_file_name_idx_cache
720 endfunction
721
722 " Last returned file index for line number lookup.
723 " Used to speed up file lookup
724 let s:tlist_file_lnum_idx_cache = -1
725
726 " Tlist_Window_Get_File_Index_By_Linenum()
727 " Return the index of the filename present in the specified line number
728 " Line number refers to the line number in the taglist window
729 function! s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
730     call s:Tlist_Log_Msg('Tlist_Window_Get_File_Index_By_Linenum (' . a:lnum . ')')
731
732     " First try to see whether the new line number is within the range
733     " of the last returned file
734     if s:tlist_file_lnum_idx_cache != -1 &&
735                 \ s:tlist_file_lnum_idx_cache < s:tlist_file_count
736         if a:lnum >= s:tlist_{s:tlist_file_lnum_idx_cache}_start &&
737                     \ a:lnum <= s:tlist_{s:tlist_file_lnum_idx_cache}_end
738             return s:tlist_file_lnum_idx_cache
739         endif
740     endif
741
742     let fidx = -1
743
744     if g:Tlist_Show_One_File
745         " Displaying only one file in the taglist window. Check whether
746         " the line is within the tags displayed for that file
747         if s:tlist_cur_file_idx != -1
748             if a:lnum >= s:tlist_{s:tlist_cur_file_idx}_start
749                         \ && a:lnum <= s:tlist_{s:tlist_cur_file_idx}_end
750                 let fidx = s:tlist_cur_file_idx
751             endif
752
753         endif
754     else
755         " Do a binary search in the taglist
756         let left = 0
757         let right = s:tlist_file_count - 1
758
759         while left < right
760             let mid = (left + right) / 2
761
762             if a:lnum >= s:tlist_{mid}_start && a:lnum <= s:tlist_{mid}_end
763                 let s:tlist_file_lnum_idx_cache = mid
764                 return mid
765             endif
766
767             if a:lnum < s:tlist_{mid}_start
768                 let right = mid - 1
769             else
770                 let left = mid + 1
771             endif
772         endwhile
773
774         if left >= 0 && left < s:tlist_file_count
775                     \ && a:lnum >= s:tlist_{left}_start
776                     \ && a:lnum <= s:tlist_{left}_end
777             let fidx = left
778         endif
779     endif
780
781     let s:tlist_file_lnum_idx_cache = fidx
782
783     return fidx
784 endfunction
785
786 " Tlist_Exe_Cmd_No_Acmds
787 " Execute the specified Ex command after disabling autocommands
788 function! s:Tlist_Exe_Cmd_No_Acmds(cmd)
789     let old_eventignore = &eventignore
790     set eventignore=all
791     exe a:cmd
792     let &eventignore = old_eventignore
793 endfunction
794
795 " Tlist_Skip_File()
796 " Check whether tag listing is supported for the specified file
797 function! s:Tlist_Skip_File(filename, ftype)
798     " Skip buffers with no names and buffers with filetype not set
799     if a:filename == '' || a:ftype == ''
800         return 1
801     endif
802
803     " Skip files which are not supported by exuberant ctags
804     " First check whether default settings for this filetype are available.
805     " If it is not available, then check whether user specified settings are
806     " available. If both are not available, then don't list the tags for this
807     " filetype
808     let var = 's:tlist_def_' . a:ftype . '_settings'
809     if !exists(var)
810         let var = 'g:tlist_' . a:ftype . '_settings'
811         if !exists(var)
812             return 1
813         endif
814     endif
815
816     " Skip files which are not readable or files which are not yet stored
817     " to the disk
818     if !filereadable(a:filename)
819         return 1
820     endif
821
822     return 0
823 endfunction
824
825 " Tlist_User_Removed_File
826 " Returns 1 if a file is removed by a user from the taglist
827 function! s:Tlist_User_Removed_File(filename)
828     return stridx(s:tlist_removed_flist, a:filename . "\n") != -1
829 endfunction
830
831 " Tlist_Update_Remove_List
832 " Update the list of user removed files from the taglist
833 " add == 1, add the file to the removed list
834 " add == 0, delete the file from the removed list
835 function! s:Tlist_Update_Remove_List(filename, add)
836     if a:add
837         let s:tlist_removed_flist = s:tlist_removed_flist . a:filename . "\n"
838     else
839         let idx = stridx(s:tlist_removed_flist, a:filename . "\n")
840         let text_before = strpart(s:tlist_removed_flist, 0, idx)
841         let rem_text = strpart(s:tlist_removed_flist, idx)
842         let next_idx = stridx(rem_text, "\n")
843         let text_after = strpart(rem_text, next_idx + 1)
844
845         let s:tlist_removed_flist = text_before . text_after
846     endif
847 endfunction
848
849 " Tlist_FileType_Init
850 " Initialize the ctags arguments and tag variable for the specified
851 " file type
852 function! s:Tlist_FileType_Init(ftype)
853     call s:Tlist_Log_Msg('Tlist_FileType_Init (' . a:ftype . ')')
854     " If the user didn't specify any settings, then use the default
855     " ctags args. Otherwise, use the settings specified by the user
856     let var = 'g:tlist_' . a:ftype . '_settings'
857     if exists(var)
858         " User specified ctags arguments
859         let settings = {var} . ';'
860     else
861         " Default ctags arguments
862         let var = 's:tlist_def_' . a:ftype . '_settings'
863         if !exists(var)
864             " No default settings for this file type. This filetype is
865             " not supported
866             return 0
867         endif
868         let settings = s:tlist_def_{a:ftype}_settings . ';'
869     endif
870
871     let msg = 'Taglist: Invalid ctags option setting - ' . settings
872
873     " Format of the option that specifies the filetype and ctags arugments:
874     "
875     "       <language_name>;flag1:name1;flag2:name2;flag3:name3
876     "
877
878     " Extract the file type to pass to ctags. This may be different from the
879     " file type detected by Vim
880     let pos = stridx(settings, ';')
881     if pos == -1
882         call s:Tlist_Warning_Msg(msg)
883         return 0
884     endif
885     let ctags_ftype = strpart(settings, 0, pos)
886     if ctags_ftype == ''
887         call s:Tlist_Warning_Msg(msg)
888         return 0
889     endif
890     " Make sure a valid filetype is supplied. If the user didn't specify a
891     " valid filetype, then the ctags option settings may be treated as the
892     " filetype
893     if ctags_ftype =~ ':'
894         call s:Tlist_Warning_Msg(msg)
895         return 0
896     endif
897
898     " Remove the file type from settings
899     let settings = strpart(settings, pos + 1)
900     if settings == ''
901         call s:Tlist_Warning_Msg(msg)
902         return 0
903     endif
904
905     " Process all the specified ctags flags. The format is
906     " flag1:name1;flag2:name2;flag3:name3
907     let ctags_flags = ''
908     let cnt = 0
909     while settings != ''
910         " Extract the flag
911         let pos = stridx(settings, ':')
912         if pos == -1
913             call s:Tlist_Warning_Msg(msg)
914             return 0
915         endif
916         let flag = strpart(settings, 0, pos)
917         if flag == ''
918             call s:Tlist_Warning_Msg(msg)
919             return 0
920         endif
921         " Remove the flag from settings
922         let settings = strpart(settings, pos + 1)
923
924         " Extract the tag type name
925         let pos = stridx(settings, ';')
926         if pos == -1
927             call s:Tlist_Warning_Msg(msg)
928             return 0
929         endif
930         let name = strpart(settings, 0, pos)
931         if name == ''
932             call s:Tlist_Warning_Msg(msg)
933             return 0
934         endif
935         let settings = strpart(settings, pos + 1)
936
937         let cnt = cnt + 1
938
939         let s:tlist_{a:ftype}_{cnt}_name = flag
940         let s:tlist_{a:ftype}_{cnt}_fullname = name
941         let ctags_flags = ctags_flags . flag
942     endwhile
943
944     let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype .
945                             \ ' --' . ctags_ftype . '-types=' . ctags_flags
946     let s:tlist_{a:ftype}_count = cnt
947     let s:tlist_{a:ftype}_ctags_flags = ctags_flags
948
949     " Save the filetype name
950     let s:tlist_ftype_{s:tlist_ftype_count}_name = a:ftype
951     let s:tlist_ftype_count = s:tlist_ftype_count + 1
952
953     return 1
954 endfunction
955
956 " Tlist_Detect_Filetype
957 " Determine the filetype for the specified file using the filetypedetect
958 " autocmd.
959 function! s:Tlist_Detect_Filetype(fname)
960     " Ignore the filetype autocommands
961     let old_eventignore = &eventignore
962     set eventignore=FileType
963
964     " Save the 'filetype', as this will be changed temporarily
965     let old_filetype = &filetype
966
967     " Run the filetypedetect group of autocommands to determine
968     " the filetype
969     exe 'doautocmd filetypedetect BufRead ' . a:fname
970
971     " Save the detected filetype
972     let ftype = &filetype
973
974     " Restore the previous state
975     let &filetype = old_filetype
976     let &eventignore = old_eventignore
977
978     return ftype
979 endfunction
980
981 " Tlist_Get_Buffer_Filetype
982 " Get the filetype for the specified buffer
983 function! s:Tlist_Get_Buffer_Filetype(bnum)
984     let buf_ft = getbufvar(a:bnum, '&filetype')
985
986     if bufloaded(a:bnum)
987         " For loaded buffers, the 'filetype' is already determined
988         return buf_ft
989     endif
990
991     " For unloaded buffers, if the 'filetype' option is set, return it
992     if buf_ft != ''
993         return buf_ft
994     endif
995
996     " Skip non-existent buffers
997     if !bufexists(a:bnum)
998         return ''
999     endif
1000
1001     " For buffers whose filetype is not yet determined, try to determine
1002     " the filetype
1003     let bname = bufname(a:bnum)
1004
1005     return s:Tlist_Detect_Filetype(bname)
1006 endfunction
1007
1008 " Tlist_Discard_TagInfo
1009 " Discard the stored tag information for a file
1010 function! s:Tlist_Discard_TagInfo(fidx)
1011     call s:Tlist_Log_Msg('Tlist_Discard_TagInfo (' .
1012                 \ s:tlist_{a:fidx}_filename . ')')
1013     let ftype = s:tlist_{a:fidx}_filetype
1014
1015     " Discard information about the tags defined in the file
1016     let i = 1
1017     while i <= s:tlist_{a:fidx}_tag_count
1018         let fidx_i = 's:tlist_' . a:fidx . '_' . i
1019         unlet! {fidx_i}_tag
1020         unlet! {fidx_i}_tag_name
1021         unlet! {fidx_i}_tag_type
1022         unlet! {fidx_i}_ttype_idx
1023         unlet! {fidx_i}_tag_proto
1024         unlet! {fidx_i}_tag_searchpat
1025         unlet! {fidx_i}_tag_linenum
1026         let i = i + 1
1027     endwhile
1028
1029     let s:tlist_{a:fidx}_tag_count = 0
1030
1031     " Discard information about tag type groups
1032     let i = 1
1033     while i <= s:tlist_{ftype}_count
1034         let ttype = s:tlist_{ftype}_{i}_name
1035         if s:tlist_{a:fidx}_{ttype} != ''
1036             let fidx_ttype = 's:tlist_' . a:fidx . '_' . ttype
1037             let {fidx_ttype} = ''
1038             let {fidx_ttype}_offset = 0
1039             let cnt = {fidx_ttype}_count
1040             let {fidx_ttype}_count = 0
1041             let j = 1
1042             while j <= cnt
1043                 unlet! {fidx_ttype}_{j}
1044                 let j = j + 1
1045             endwhile
1046         endif
1047         let i = i + 1
1048     endwhile
1049
1050     " Discard the stored menu command also
1051     let s:tlist_{a:fidx}_menu_cmd = ''
1052 endfunction
1053
1054 " Tlist_Window_Update_Line_Offsets
1055 " Update the line offsets for tags for files starting from start_idx
1056 " and displayed in the taglist window by the specified offset
1057 function! s:Tlist_Window_Update_Line_Offsets(start_idx, increment, offset)
1058     let i = a:start_idx
1059
1060     while i < s:tlist_file_count
1061         if s:tlist_{i}_visible
1062             " Update the start/end line number only if the file is visible
1063             if a:increment
1064                 let s:tlist_{i}_start = s:tlist_{i}_start + a:offset
1065                 let s:tlist_{i}_end = s:tlist_{i}_end + a:offset
1066             else
1067                 let s:tlist_{i}_start = s:tlist_{i}_start - a:offset
1068                 let s:tlist_{i}_end = s:tlist_{i}_end - a:offset
1069             endif
1070         endif
1071         let i = i + 1
1072     endwhile
1073 endfunction
1074
1075 " Tlist_Discard_FileInfo
1076 " Discard the stored information for a file
1077 function! s:Tlist_Discard_FileInfo(fidx)
1078     call s:Tlist_Log_Msg('Tlist_Discard_FileInfo (' .
1079                 \ s:tlist_{a:fidx}_filename . ')')
1080     call s:Tlist_Discard_TagInfo(a:fidx)
1081
1082     let ftype = s:tlist_{a:fidx}_filetype
1083
1084     let i = 1
1085     while i <= s:tlist_{ftype}_count
1086         let ttype = s:tlist_{ftype}_{i}_name
1087         unlet! s:tlist_{a:fidx}_{ttype}
1088         unlet! s:tlist_{a:fidx}_{ttype}_offset
1089         unlet! s:tlist_{a:fidx}_{ttype}_count
1090         let i = i + 1
1091     endwhile
1092
1093     unlet! s:tlist_{a:fidx}_filename
1094     unlet! s:tlist_{a:fidx}_sort_type
1095     unlet! s:tlist_{a:fidx}_filetype
1096     unlet! s:tlist_{a:fidx}_mtime
1097     unlet! s:tlist_{a:fidx}_start
1098     unlet! s:tlist_{a:fidx}_end
1099     unlet! s:tlist_{a:fidx}_valid
1100     unlet! s:tlist_{a:fidx}_visible
1101     unlet! s:tlist_{a:fidx}_tag_count
1102     unlet! s:tlist_{a:fidx}_menu_cmd
1103 endfunction
1104
1105 " Tlist_Window_Remove_File_From_Display
1106 " Remove the specified file from display
1107 function! s:Tlist_Window_Remove_File_From_Display(fidx)
1108     call s:Tlist_Log_Msg('Tlist_Window_Remove_File_From_Display (' .
1109                 \ s:tlist_{a:fidx}_filename . ')')
1110     " If the file is not visible then no need to remove it
1111     if !s:tlist_{a:fidx}_visible
1112         return
1113     endif
1114
1115     " Remove the tags displayed for the specified file from the window
1116     let start = s:tlist_{a:fidx}_start
1117     " Include the empty line after the last line also
1118     if g:Tlist_Compact_Format
1119         let end = s:tlist_{a:fidx}_end
1120     else
1121         let end = s:tlist_{a:fidx}_end + 1
1122     endif
1123
1124     setlocal modifiable
1125     exe 'silent! ' . start . ',' . end . 'delete _'
1126     setlocal nomodifiable
1127
1128     " Correct the start and end line offsets for all the files following
1129     " this file, as the tags for this file are removed
1130     call s:Tlist_Window_Update_Line_Offsets(a:fidx + 1, 0, end - start + 1)
1131 endfunction
1132
1133 " Tlist_Remove_File
1134 " Remove the file under the cursor or the specified file index
1135 " user_request - User requested to remove the file from taglist
1136 function! s:Tlist_Remove_File(file_idx, user_request)
1137     let fidx = a:file_idx
1138
1139     if fidx == -1
1140         let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
1141         if fidx == -1
1142             return
1143         endif
1144     endif
1145     call s:Tlist_Log_Msg('Tlist_Remove_File (' .
1146                 \ s:tlist_{fidx}_filename . ', ' . a:user_request . ')')
1147
1148     let save_winnr = winnr()
1149     let winnum = bufwinnr(g:TagList_title)
1150     if winnum != -1
1151         " Taglist window is open, remove the file from display
1152
1153         if save_winnr != winnum
1154             let old_eventignore = &eventignore
1155             set eventignore=all
1156             exe winnum . 'wincmd w'
1157         endif
1158
1159         call s:Tlist_Window_Remove_File_From_Display(fidx)
1160
1161         if save_winnr != winnum
1162             exe save_winnr . 'wincmd w'
1163             let &eventignore = old_eventignore
1164         endif
1165     endif
1166
1167     let fname = s:tlist_{fidx}_filename
1168
1169     if a:user_request
1170         " As the user requested to remove the file from taglist,
1171         " add it to the removed list
1172         call s:Tlist_Update_Remove_List(fname, 1)
1173     endif
1174
1175     " Remove the file name from the taglist list of filenames
1176     let idx = stridx(s:tlist_file_names, fname . "\n")
1177     let text_before = strpart(s:tlist_file_names, 0, idx)
1178     let rem_text = strpart(s:tlist_file_names, idx)
1179     let next_idx = stridx(rem_text, "\n")
1180     let text_after = strpart(rem_text, next_idx + 1)
1181     let s:tlist_file_names = text_before . text_after
1182
1183     call s:Tlist_Discard_FileInfo(fidx)
1184
1185     " Shift all the file variables by one index
1186     let i = fidx + 1
1187
1188     while i < s:tlist_file_count
1189         let j = i - 1
1190
1191         let s:tlist_{j}_filename = s:tlist_{i}_filename
1192         let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type
1193         let s:tlist_{j}_filetype = s:tlist_{i}_filetype
1194         let s:tlist_{j}_mtime = s:tlist_{i}_mtime
1195         let s:tlist_{j}_start = s:tlist_{i}_start
1196         let s:tlist_{j}_end = s:tlist_{i}_end
1197         let s:tlist_{j}_valid = s:tlist_{i}_valid
1198         let s:tlist_{j}_visible = s:tlist_{i}_visible
1199         let s:tlist_{j}_tag_count = s:tlist_{i}_tag_count
1200         let s:tlist_{j}_menu_cmd = s:tlist_{i}_menu_cmd
1201
1202         let k = 1
1203         while k <= s:tlist_{j}_tag_count
1204             let s:tlist_{j}_{k}_tag = s:tlist_{i}_{k}_tag
1205             let s:tlist_{j}_{k}_tag_name = s:tlist_{i}_{k}_tag_name
1206             let s:tlist_{j}_{k}_tag_type = s:Tlist_Get_Tag_Type_By_Tag(i, k)
1207             let s:tlist_{j}_{k}_ttype_idx = s:tlist_{i}_{k}_ttype_idx
1208             let s:tlist_{j}_{k}_tag_proto = s:Tlist_Get_Tag_Prototype(i, k)
1209             let s:tlist_{j}_{k}_tag_searchpat = s:Tlist_Get_Tag_SearchPat(i, k)
1210             let s:tlist_{j}_{k}_tag_linenum = s:Tlist_Get_Tag_Linenum(i, k)
1211             let k = k + 1
1212         endwhile
1213
1214         let ftype = s:tlist_{i}_filetype
1215
1216         let k = 1
1217         while k <= s:tlist_{ftype}_count
1218             let ttype = s:tlist_{ftype}_{k}_name
1219             let s:tlist_{j}_{ttype} = s:tlist_{i}_{ttype}
1220             let s:tlist_{j}_{ttype}_offset = s:tlist_{i}_{ttype}_offset
1221             let s:tlist_{j}_{ttype}_count = s:tlist_{i}_{ttype}_count
1222             if s:tlist_{j}_{ttype} != ''
1223                 let l = 1
1224                 while l <= s:tlist_{j}_{ttype}_count
1225                     let s:tlist_{j}_{ttype}_{l} = s:tlist_{i}_{ttype}_{l}
1226                     let l = l + 1
1227                 endwhile
1228             endif
1229             let k = k + 1
1230         endwhile
1231
1232         " As the file and tag information is copied to the new index,
1233         " discard the previous information
1234         call s:Tlist_Discard_FileInfo(i)
1235
1236         let i = i + 1
1237     endwhile
1238
1239     " Reduce the number of files displayed
1240     let s:tlist_file_count = s:tlist_file_count - 1
1241
1242     if g:Tlist_Show_One_File
1243         " If the tags for only one file is displayed and if we just
1244         " now removed that file, then invalidate the current file idx
1245         if s:tlist_cur_file_idx == fidx
1246             let s:tlist_cur_file_idx = -1
1247         endif
1248     endif
1249 endfunction
1250
1251 " Tlist_Window_Goto_Window
1252 " Goto the taglist window
1253 function! s:Tlist_Window_Goto_Window()
1254     let winnum = bufwinnr(g:TagList_title)
1255     if winnum != -1
1256         if winnr() != winnum
1257             call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w')
1258         endif
1259     endif
1260 endfunction
1261
1262 " Tlist_Window_Create
1263 " Create a new taglist window. If it is already open, jump to it
1264 function! s:Tlist_Window_Create()
1265     call s:Tlist_Log_Msg('Tlist_Window_Create()')
1266     " If the window is open, jump to it
1267     let winnum = bufwinnr(g:TagList_title)
1268     if winnum != -1
1269         " Jump to the existing window
1270         if winnr() != winnum
1271             exe winnum . 'wincmd w'
1272         endif
1273         return
1274     endif
1275
1276     " If used with winmanager don't open windows. Winmanager will handle
1277     " the window/buffer management
1278     if s:tlist_app_name == "winmanager"
1279         return
1280     endif
1281
1282     " Create a new window. If user prefers a horizontal window, then open
1283     " a horizontally split window. Otherwise open a vertically split
1284     " window
1285     if g:Tlist_Use_Horiz_Window
1286         " Open a horizontally split window
1287         let win_dir = 'botright'
1288         " Horizontal window height
1289         let win_size = g:Tlist_WinHeight
1290     else
1291         if s:tlist_winsize_chgd == -1
1292             " Open a vertically split window. Increase the window size, if
1293             " needed, to accomodate the new window
1294             if g:Tlist_Inc_Winwidth &&
1295                         \ &columns < (80 + g:Tlist_WinWidth)
1296                 " Save the original window position
1297                 let s:tlist_pre_winx = getwinposx()
1298                 let s:tlist_pre_winy = getwinposy()
1299
1300                 " one extra column is needed to include the vertical split
1301                 let &columns= &columns + g:Tlist_WinWidth + 1
1302
1303                 let s:tlist_winsize_chgd = 1
1304             else
1305                 let s:tlist_winsize_chgd = 0
1306             endif
1307         endif
1308
1309         if g:Tlist_Use_Right_Window
1310             " Open the window at the rightmost place
1311             let win_dir = 'botright vertical'
1312         else
1313             " Open the window at the leftmost place
1314             let win_dir = 'topleft vertical'
1315         endif
1316         let win_size = g:Tlist_WinWidth
1317     endif
1318
1319     " If the tag listing temporary buffer already exists, then reuse it.
1320     " Otherwise create a new buffer
1321     let bufnum = bufnr(g:TagList_title)
1322     if bufnum == -1
1323         " Create a new buffer
1324         let wcmd = g:TagList_title
1325     else
1326         " Edit the existing buffer
1327         let wcmd = '+buffer' . bufnum
1328     endif
1329
1330     " Create the taglist window
1331     exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd
1332
1333     " Save the new window position
1334     let s:tlist_winx = getwinposx()
1335     let s:tlist_winy = getwinposy()
1336
1337     " Initialize the taglist window
1338     call s:Tlist_Window_Init()
1339 endfunction
1340
1341 " Tlist_Window_Zoom
1342 " Zoom (maximize/minimize) the taglist window
1343 function! s:Tlist_Window_Zoom()
1344     if s:tlist_win_maximized
1345         " Restore the window back to the previous size
1346         if g:Tlist_Use_Horiz_Window
1347             exe 'resize ' . g:Tlist_WinHeight
1348         else
1349             exe 'vert resize ' . g:Tlist_WinWidth
1350         endif
1351         let s:tlist_win_maximized = 0
1352     else
1353         " Set the window size to the maximum possible without closing other
1354         " windows
1355         if g:Tlist_Use_Horiz_Window
1356             resize
1357         else
1358             vert resize
1359         endif
1360         let s:tlist_win_maximized = 1
1361     endif
1362 endfunction
1363
1364 " Tlist_Ballon_Expr
1365 " When the mouse cursor is over a tag in the taglist window, display the
1366 " tag prototype (balloon)
1367 function! Tlist_Ballon_Expr()
1368     " Get the file index
1369     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(v:beval_lnum)
1370     if fidx == -1
1371         return ''
1372     endif
1373
1374     " Get the tag output line for the current tag
1375     let tidx = s:Tlist_Window_Get_Tag_Index(fidx, v:beval_lnum)
1376     if tidx == 0
1377         return ''
1378     endif
1379
1380     " Get the tag search pattern and display it
1381     return s:Tlist_Get_Tag_Prototype(fidx, tidx)
1382 endfunction
1383
1384 " Tlist_Window_Check_Width
1385 " Check the width of the taglist window. For horizontally split windows, the
1386 " 'winfixheight' option is used to fix the height of the window. For
1387 " vertically split windows, Vim doesn't support the 'winfixwidth' option. So
1388 " need to handle window width changes from this function.
1389 function! s:Tlist_Window_Check_Width()
1390     let tlist_winnr = bufwinnr(g:TagList_title)
1391     if tlist_winnr == -1
1392         return
1393     endif
1394
1395     let width = winwidth(tlist_winnr)
1396     if width != g:Tlist_WinWidth
1397         call s:Tlist_Log_Msg("Tlist_Window_Check_Width: Changing window " .
1398                     \ "width from " . width . " to " . g:Tlist_WinWidth)
1399         let save_winnr = winnr()
1400         if save_winnr != tlist_winnr
1401             call s:Tlist_Exe_Cmd_No_Acmds(tlist_winnr . 'wincmd w')
1402         endif
1403         exe 'vert resize ' . g:Tlist_WinWidth
1404         if save_winnr != tlist_winnr
1405             call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
1406         endif
1407     endif
1408 endfunction
1409
1410 " Tlist_Window_Exit_Only_Window
1411 " If the 'Tlist_Exit_OnlyWindow' option is set, then exit Vim if only the
1412 " taglist window is present.
1413 function! s:Tlist_Window_Exit_Only_Window()
1414     " Before quitting Vim, delete the taglist buffer so that
1415     " the '0 mark is correctly set to the previous buffer.
1416     if v:version < 700
1417         if winbufnr(2) == -1
1418             bdelete
1419             quit
1420         endif
1421     else
1422         if winbufnr(2) == -1
1423             if tabpagenr('$') == 1
1424                 " Only one tag page is present
1425                 bdelete
1426                 quit
1427             else
1428                 " More than one tab page is present. Close only the current
1429                 " tab page
1430                 close
1431             endif
1432         endif
1433     endif
1434 endfunction
1435
1436 " Tlist_Window_Init
1437 " Set the default options for the taglist window
1438 function! s:Tlist_Window_Init()
1439     call s:Tlist_Log_Msg('Tlist_Window_Init()')
1440
1441     " The 'readonly' option should not be set for the taglist buffer.
1442     " If Vim is started as "view/gview" or if the ":view" command is
1443     " used, then the 'readonly' option is set for all the buffers.
1444     " Unset it for the taglist buffer
1445     setlocal noreadonly
1446
1447     " Set the taglist buffer filetype to taglist
1448     setlocal filetype=taglist
1449
1450     " Define taglist window element highlighting
1451     syntax match TagListComment '^" .*'
1452     syntax match TagListFileName '^[^" ].*$'
1453     syntax match TagListTitle '^  \S.*$'
1454     syntax match TagListTagScope  '\s\[.\{-\}\]$'
1455
1456     " Define the highlighting only if colors are supported
1457     if has('gui_running') || &t_Co > 2
1458         " Colors to highlight various taglist window elements
1459         " If user defined highlighting group exists, then use them.
1460         " Otherwise, use default highlight groups.
1461         if hlexists('MyTagListTagName')
1462             highlight link TagListTagName MyTagListTagName
1463         else
1464             highlight default link TagListTagName Search
1465         endif
1466         " Colors to highlight comments and titles
1467         if hlexists('MyTagListComment')
1468             highlight link TagListComment MyTagListComment
1469         else
1470             highlight clear TagListComment
1471             highlight default link TagListComment Comment
1472         endif
1473         if hlexists('MyTagListTitle')
1474             highlight link TagListTitle MyTagListTitle
1475         else
1476             highlight clear TagListTitle
1477             highlight default link TagListTitle Title
1478         endif
1479         if hlexists('MyTagListFileName')
1480             highlight link TagListFileName MyTagListFileName
1481         else
1482             highlight clear TagListFileName
1483             highlight default TagListFileName guibg=Grey ctermbg=darkgray
1484                         \ guifg=white ctermfg=white
1485         endif
1486         if hlexists('MyTagListTagScope')
1487             highlight link TagListTagScope MyTagListTagScope
1488         else
1489             highlight clear TagListTagScope
1490             highlight default link TagListTagScope Identifier
1491         endif
1492     else
1493         highlight default TagListTagName term=reverse cterm=reverse
1494     endif
1495
1496     " Folding related settings
1497     setlocal foldenable
1498     setlocal foldminlines=0
1499     setlocal foldmethod=manual
1500     setlocal foldlevel=9999
1501     if g:Tlist_Enable_Fold_Column
1502         setlocal foldcolumn=3
1503     else
1504         setlocal foldcolumn=0
1505     endif
1506     setlocal foldtext=v:folddashes.getline(v:foldstart)
1507
1508     if s:tlist_app_name != "winmanager"
1509         " Mark buffer as scratch
1510         silent! setlocal buftype=nofile
1511         if s:tlist_app_name == "none"
1512             silent! setlocal bufhidden=delete
1513         endif
1514         silent! setlocal noswapfile
1515         " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted
1516         " buffers. So if the taglist buffer is unlisted, multiple taglist
1517         " windows will be opened. This bug is fixed in Vim 6.1 and above
1518         if v:version >= 601
1519             silent! setlocal nobuflisted
1520         endif
1521     endif
1522
1523     silent! setlocal nowrap
1524
1525     " If the 'number' option is set in the source window, it will affect the
1526     " taglist window. So forcefully disable 'number' option for the taglist
1527     " window
1528     silent! setlocal nonumber
1529
1530     " Use fixed height when horizontally split window is used
1531     if g:Tlist_Use_Horiz_Window
1532         if v:version >= 602
1533             set winfixheight
1534         endif
1535     endif
1536     if !g:Tlist_Use_Horiz_Window && v:version >= 700
1537         set winfixwidth
1538     endif
1539
1540     " Setup balloon evaluation to display tag prototype
1541     if v:version >= 700 && has('balloon_eval')
1542         setlocal balloonexpr=Tlist_Ballon_Expr()
1543         set ballooneval
1544     endif
1545
1546     " Setup the cpoptions properly for the maps to work
1547     let old_cpoptions = &cpoptions
1548     set cpoptions&vim
1549
1550     " Create buffer local mappings for jumping to the tags and sorting the list
1551     nnoremap <buffer> <silent> <CR>
1552                 \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
1553     nnoremap <buffer> <silent> o
1554                 \ :call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR>
1555     nnoremap <buffer> <silent> p
1556                 \ :call <SID>Tlist_Window_Jump_To_Tag('preview')<CR>
1557     nnoremap <buffer> <silent> P
1558                 \ :call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR>
1559     if v:version >= 700
1560     nnoremap <buffer> <silent> t
1561                 \ :call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR>
1562     nnoremap <buffer> <silent> <C-t>
1563                 \ :call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR>
1564     endif
1565     nnoremap <buffer> <silent> <2-LeftMouse>
1566                 \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
1567     nnoremap <buffer> <silent> s
1568                 \ :call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR>
1569     nnoremap <buffer> <silent> + :silent! foldopen<CR>
1570     nnoremap <buffer> <silent> - :silent! foldclose<CR>
1571     nnoremap <buffer> <silent> * :silent! %foldopen!<CR>
1572     nnoremap <buffer> <silent> = :silent! %foldclose<CR>
1573     nnoremap <buffer> <silent> <kPlus> :silent! foldopen<CR>
1574     nnoremap <buffer> <silent> <kMinus> :silent! foldclose<CR>
1575     nnoremap <buffer> <silent> <kMultiply> :silent! %foldopen!<CR>
1576     nnoremap <buffer> <silent> <Space> :call <SID>Tlist_Window_Show_Info()<CR>
1577     nnoremap <buffer> <silent> u :call <SID>Tlist_Window_Update_File()<CR>
1578     nnoremap <buffer> <silent> d :call <SID>Tlist_Remove_File(-1, 1)<CR>
1579     nnoremap <buffer> <silent> x :call <SID>Tlist_Window_Zoom()<CR>
1580     nnoremap <buffer> <silent> [[ :call <SID>Tlist_Window_Move_To_File(-1)<CR>
1581     nnoremap <buffer> <silent> <BS> :call <SID>Tlist_Window_Move_To_File(-1)<CR>
1582     nnoremap <buffer> <silent> ]] :call <SID>Tlist_Window_Move_To_File(1)<CR>
1583     nnoremap <buffer> <silent> <Tab> :call <SID>Tlist_Window_Move_To_File(1)<CR>
1584     nnoremap <buffer> <silent> <F1> :call <SID>Tlist_Window_Toggle_Help_Text()<CR>
1585     nnoremap <buffer> <silent> q :close<CR>
1586
1587     " Insert mode mappings
1588     inoremap <buffer> <silent> <CR>
1589                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
1590     " Windows needs return
1591     inoremap <buffer> <silent> <Return>
1592                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
1593     inoremap <buffer> <silent> o
1594                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR>
1595     inoremap <buffer> <silent> p
1596                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('preview')<CR>
1597     inoremap <buffer> <silent> P
1598                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR>
1599     if v:version >= 700
1600     inoremap <buffer> <silent> t
1601                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR>
1602     inoremap <buffer> <silent> <C-t>
1603                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR>
1604     endif
1605     inoremap <buffer> <silent> <2-LeftMouse>
1606                 \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR>
1607     inoremap <buffer> <silent> s
1608                 \ <C-o>:call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR>
1609     inoremap <buffer> <silent> +             <C-o>:silent! foldopen<CR>
1610     inoremap <buffer> <silent> -             <C-o>:silent! foldclose<CR>
1611     inoremap <buffer> <silent> *             <C-o>:silent! %foldopen!<CR>
1612     inoremap <buffer> <silent> =             <C-o>:silent! %foldclose<CR>
1613     inoremap <buffer> <silent> <kPlus>       <C-o>:silent! foldopen<CR>
1614     inoremap <buffer> <silent> <kMinus>      <C-o>:silent! foldclose<CR>
1615     inoremap <buffer> <silent> <kMultiply>   <C-o>:silent! %foldopen!<CR>
1616     inoremap <buffer> <silent> <Space>       <C-o>:call
1617                                     \ <SID>Tlist_Window_Show_Info()<CR>
1618     inoremap <buffer> <silent> u
1619                             \ <C-o>:call <SID>Tlist_Window_Update_File()<CR>
1620     inoremap <buffer> <silent> d    <C-o>:call <SID>Tlist_Remove_File(-1, 1)<CR>
1621     inoremap <buffer> <silent> x    <C-o>:call <SID>Tlist_Window_Zoom()<CR>
1622     inoremap <buffer> <silent> [[   <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR>
1623     inoremap <buffer> <silent> <BS> <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR>
1624     inoremap <buffer> <silent> ]]   <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR>
1625     inoremap <buffer> <silent> <Tab> <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR>
1626     inoremap <buffer> <silent> <F1>  <C-o>:call <SID>Tlist_Window_Toggle_Help_Text()<CR>
1627     inoremap <buffer> <silent> q    <C-o>:close<CR>
1628
1629     " Map single left mouse click if the user wants this functionality
1630     if g:Tlist_Use_SingleClick == 1
1631         " Contributed by Bindu Wavell
1632         " attempt to perform single click mapping, it would be much
1633         " nicer if we could nnoremap <buffer> ... however vim does
1634         " not fire the <buffer> <leftmouse> when you use the mouse
1635         " to enter a buffer.
1636         let clickmap = ':if bufname("%") =~ "__Tag_List__" <bar> ' .
1637                     \ 'call <SID>Tlist_Window_Jump_To_Tag("useopen") ' .
1638                     \ '<bar> endif <CR>'
1639         if maparg('<leftmouse>', 'n') == ''
1640             " no mapping for leftmouse
1641             exe ':nnoremap <silent> <leftmouse> <leftmouse>' . clickmap
1642         else
1643             " we have a mapping
1644             let mapcmd = ':nnoremap <silent> <leftmouse> <leftmouse>'
1645             let mapcmd = mapcmd . substitute(substitute(
1646                         \ maparg('<leftmouse>', 'n'), '|', '<bar>', 'g'),
1647                         \ '\c^<leftmouse>', '', '')
1648             let mapcmd = mapcmd . clickmap
1649             exe mapcmd
1650         endif
1651     endif
1652
1653     " Define the taglist autocommands
1654     augroup TagListAutoCmds
1655         autocmd!
1656         " Display the tag prototype for the tag under the cursor.
1657         autocmd CursorHold __Tag_List__ call s:Tlist_Window_Show_Info()
1658         " Highlight the current tag periodically
1659         autocmd CursorHold * silent call s:Tlist_Window_Highlight_Tag(
1660                             \ fnamemodify(bufname('%'), ':p'), line('.'), 1, 0)
1661
1662         " Adjust the Vim window width when taglist window is closed
1663         autocmd BufUnload __Tag_List__ call s:Tlist_Post_Close_Cleanup()
1664         " Close the fold for this buffer when leaving the buffer
1665         if g:Tlist_File_Fold_Auto_Close
1666             autocmd BufEnter * silent
1667                 \ call s:Tlist_Window_Open_File_Fold(expand('<abuf>'))
1668         endif
1669         " Exit Vim itself if only the taglist window is present (optional)
1670         if g:Tlist_Exit_OnlyWindow
1671             autocmd BufEnter __Tag_List__ nested
1672                         \ call s:Tlist_Window_Exit_Only_Window()
1673         endif
1674         if s:tlist_app_name != "winmanager" &&
1675                     \ !g:Tlist_Process_File_Always &&
1676                     \ (!has('gui_running') || !g:Tlist_Show_Menu)
1677             " Auto refresh the taglist window
1678             autocmd BufEnter * call s:Tlist_Refresh()
1679         endif
1680
1681         if !g:Tlist_Use_Horiz_Window
1682             if v:version < 700
1683                 autocmd WinEnter * call s:Tlist_Window_Check_Width()
1684             endif
1685         endif
1686         if v:version >= 700
1687             autocmd TabEnter * silent call s:Tlist_Refresh_Folds()
1688         endif
1689     augroup end
1690
1691     " Restore the previous cpoptions settings
1692     let &cpoptions = old_cpoptions
1693 endfunction
1694
1695 " Tlist_Window_Refresh
1696 " Display the tags for all the files in the taglist window
1697 function! s:Tlist_Window_Refresh()
1698     call s:Tlist_Log_Msg('Tlist_Window_Refresh()')
1699     " Set report option to a huge value to prevent informational messages
1700     " while deleting the lines
1701     let old_report = &report
1702     set report=99999
1703
1704     " Mark the buffer as modifiable
1705     setlocal modifiable
1706
1707     " Delete the contents of the buffer to the black-hole register
1708     silent! %delete _
1709
1710     " As we have cleared the taglist window, mark all the files
1711     " as not visible
1712     let i = 0
1713     while i < s:tlist_file_count
1714         let s:tlist_{i}_visible = 0
1715         let i = i + 1
1716     endwhile
1717
1718     if g:Tlist_Compact_Format == 0
1719         " Display help in non-compact mode
1720         call s:Tlist_Window_Display_Help()
1721     endif
1722
1723     " Mark the buffer as not modifiable
1724     setlocal nomodifiable
1725
1726     " Restore the report option
1727     let &report = old_report
1728
1729     " If the tags for only one file should be displayed in the taglist
1730     " window, then no need to add the tags here. The bufenter autocommand
1731     " will add the tags for that file.
1732     if g:Tlist_Show_One_File
1733         return
1734     endif
1735
1736     " List all the tags for the previously processed files
1737     " Do this only if taglist is configured to display tags for more than
1738     " one file. Otherwise, when Tlist_Show_One_File is configured,
1739     " tags for the wrong file will be displayed.
1740     let i = 0
1741     while i < s:tlist_file_count
1742         call s:Tlist_Window_Refresh_File(s:tlist_{i}_filename,
1743                     \ s:tlist_{i}_filetype)
1744         let i = i + 1
1745     endwhile
1746
1747     if g:Tlist_Auto_Update
1748         " Add and list the tags for all buffers in the Vim buffer list
1749         let i = 1
1750         let last_bufnum = bufnr('$')
1751         while i <= last_bufnum
1752             if buflisted(i)
1753                 let fname = fnamemodify(bufname(i), ':p')
1754                 let ftype = s:Tlist_Get_Buffer_Filetype(i)
1755                 " If the file doesn't support tag listing, skip it
1756                 if !s:Tlist_Skip_File(fname, ftype)
1757                     call s:Tlist_Window_Refresh_File(fname, ftype)
1758                 endif
1759             endif
1760             let i = i + 1
1761         endwhile
1762     endif
1763
1764     " If Tlist_File_Fold_Auto_Close option is set, then close all the folds
1765     if g:Tlist_File_Fold_Auto_Close
1766         " Close all the folds
1767         silent! %foldclose
1768     endif
1769
1770     " Move the cursor to the top of the taglist window
1771     normal! gg
1772 endfunction
1773
1774 " Tlist_Post_Close_Cleanup()
1775 " Close the taglist window and adjust the Vim window width
1776 function! s:Tlist_Post_Close_Cleanup()
1777     call s:Tlist_Log_Msg('Tlist_Post_Close_Cleanup()')
1778     " Mark all the files as not visible
1779     let i = 0
1780     while i < s:tlist_file_count
1781         let s:tlist_{i}_visible = 0
1782         let i = i + 1
1783     endwhile
1784
1785     " Remove the taglist autocommands
1786     silent! autocmd! TagListAutoCmds
1787
1788     " Clear all the highlights
1789     match none
1790
1791     silent! syntax clear TagListTitle
1792     silent! syntax clear TagListComment
1793     silent! syntax clear TagListTagScope
1794
1795     " Remove the left mouse click mapping if it was setup initially
1796     if g:Tlist_Use_SingleClick
1797         if hasmapto('<LeftMouse>')
1798             nunmap <LeftMouse>
1799         endif
1800     endif
1801
1802     if s:tlist_app_name != "winmanager"
1803     if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 ||
1804                 \ s:tlist_winsize_chgd != 1 ||
1805                 \ &columns < (80 + g:Tlist_WinWidth)
1806         " No need to adjust window width if using horizontally split taglist
1807         " window or if columns is less than 101 or if the user chose not to
1808         " adjust the window width
1809     else
1810         " If the user didn't manually move the window, then restore the window
1811         " position to the pre-taglist position
1812         if s:tlist_pre_winx != -1 && s:tlist_pre_winy != -1 &&
1813                     \ getwinposx() == s:tlist_winx &&
1814                     \ getwinposy() == s:tlist_winy
1815             exe 'winpos ' . s:tlist_pre_winx . ' ' . s:tlist_pre_winy
1816         endif
1817
1818         " Adjust the Vim window width
1819         let &columns= &columns - (g:Tlist_WinWidth + 1)
1820     endif
1821     endif
1822
1823     let s:tlist_winsize_chgd = -1
1824
1825     " Reset taglist state variables
1826     if s:tlist_app_name == "winmanager"
1827         let s:tlist_app_name = "none"
1828     endif
1829     let s:tlist_window_initialized = 0
1830 endfunction
1831
1832 " Tlist_Window_Refresh_File()
1833 " List the tags defined in the specified file in a Vim window
1834 function! s:Tlist_Window_Refresh_File(filename, ftype)
1835     call s:Tlist_Log_Msg('Tlist_Window_Refresh_File (' . a:filename . ')')
1836     " First check whether the file already exists
1837     let fidx = s:Tlist_Get_File_Index(a:filename)
1838     if fidx != -1
1839         let file_listed = 1
1840     else
1841         let file_listed = 0
1842     endif
1843
1844     if !file_listed
1845         " Check whether this file is removed based on user request
1846         " If it is, then don't display the tags for this file
1847         if s:Tlist_User_Removed_File(a:filename)
1848             return
1849         endif
1850     endif
1851
1852     if file_listed && s:tlist_{fidx}_visible
1853         " Check whether the file tags are currently valid
1854         if s:tlist_{fidx}_valid
1855             " Goto the first line in the file
1856             exe s:tlist_{fidx}_start
1857
1858             " If the line is inside a fold, open the fold
1859             if foldclosed('.') != -1
1860                 exe "silent! " . s:tlist_{fidx}_start . "," .
1861                             \ s:tlist_{fidx}_end . "foldopen!"
1862             endif
1863             return
1864         endif
1865
1866         " Discard and remove the tags for this file from display
1867         call s:Tlist_Discard_TagInfo(fidx)
1868         call s:Tlist_Window_Remove_File_From_Display(fidx)
1869     endif
1870
1871     " Process and generate a list of tags defined in the file
1872     if !file_listed || !s:tlist_{fidx}_valid
1873         let ret_fidx = s:Tlist_Process_File(a:filename, a:ftype)
1874         if ret_fidx == -1
1875             return
1876         endif
1877         let fidx = ret_fidx
1878     endif
1879
1880     " Set report option to a huge value to prevent informational messages
1881     " while adding lines to the taglist window
1882     let old_report = &report
1883     set report=99999
1884
1885     if g:Tlist_Show_One_File
1886         " Remove the previous file
1887         if s:tlist_cur_file_idx != -1
1888             call s:Tlist_Window_Remove_File_From_Display(s:tlist_cur_file_idx)
1889             let s:tlist_{s:tlist_cur_file_idx}_visible = 0
1890             let s:tlist_{s:tlist_cur_file_idx}_start = 0
1891             let s:tlist_{s:tlist_cur_file_idx}_end = 0
1892         endif
1893         let s:tlist_cur_file_idx = fidx
1894     endif
1895
1896     " Mark the buffer as modifiable
1897     setlocal modifiable
1898
1899     " Add new files to the end of the window. For existing files, add them at
1900     " the same line where they were previously present. If the file is not
1901     " visible, then add it at the end
1902     if s:tlist_{fidx}_start == 0 || !s:tlist_{fidx}_visible
1903         if g:Tlist_Compact_Format
1904             let s:tlist_{fidx}_start = line('$')
1905         else
1906             let s:tlist_{fidx}_start = line('$') + 1
1907         endif
1908     endif
1909
1910     let s:tlist_{fidx}_visible = 1
1911
1912     " Goto the line where this file should be placed
1913     if g:Tlist_Compact_Format
1914         exe s:tlist_{fidx}_start
1915     else
1916         exe s:tlist_{fidx}_start - 1
1917     endif
1918
1919     let txt = fnamemodify(s:tlist_{fidx}_filename, ':t') . ' (' .
1920                 \ fnamemodify(s:tlist_{fidx}_filename, ':p:h') . ')'
1921     if g:Tlist_Compact_Format == 0
1922         silent! put =txt
1923     else
1924         silent! put! =txt
1925         " Move to the next line
1926         exe line('.') + 1
1927     endif
1928     let file_start = s:tlist_{fidx}_start
1929
1930     " Add the tag names grouped by tag type to the buffer with a title
1931     let i = 1
1932     let ttype_cnt = s:tlist_{a:ftype}_count
1933     while i <= ttype_cnt
1934         let ttype = s:tlist_{a:ftype}_{i}_name
1935         " Add the tag type only if there are tags for that type
1936         let fidx_ttype = 's:tlist_' . fidx . '_' . ttype
1937         let ttype_txt = {fidx_ttype}
1938         if ttype_txt != ''
1939             let txt = '  ' . s:tlist_{a:ftype}_{i}_fullname
1940             if g:Tlist_Compact_Format == 0
1941                 let ttype_start_lnum = line('.') + 1
1942                 silent! put =txt
1943             else
1944                 let ttype_start_lnum = line('.')
1945                 silent! put! =txt
1946             endif
1947             silent! put =ttype_txt
1948
1949             let {fidx_ttype}_offset = ttype_start_lnum - file_start
1950
1951             " create a fold for this tag type
1952             let fold_start = ttype_start_lnum
1953             let fold_end = fold_start + {fidx_ttype}_count
1954             exe fold_start . ',' . fold_end  . 'fold'
1955
1956             " Adjust the cursor position
1957             if g:Tlist_Compact_Format == 0
1958                 exe ttype_start_lnum + {fidx_ttype}_count
1959             else
1960                 exe ttype_start_lnum + {fidx_ttype}_count + 1
1961             endif
1962
1963             if g:Tlist_Compact_Format == 0
1964                 " Separate the tag types by a empty line
1965                 silent! put =''
1966             endif
1967         endif
1968         let i = i + 1
1969     endwhile
1970
1971     if s:tlist_{fidx}_tag_count == 0
1972         if g:Tlist_Compact_Format == 0
1973             silent! put =''
1974         endif
1975     endif
1976
1977     let s:tlist_{fidx}_end = line('.') - 1
1978
1979     " Create a fold for the entire file
1980     exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold'
1981     exe 'silent! ' . s:tlist_{fidx}_start . ',' .
1982                 \ s:tlist_{fidx}_end . 'foldopen!'
1983
1984     " Goto the starting line for this file,
1985     exe s:tlist_{fidx}_start
1986
1987     if s:tlist_app_name == "winmanager"
1988         " To handle a bug in the winmanager plugin, add a space at the
1989         " last line
1990         call setline('$', ' ')
1991     endif
1992
1993     " Mark the buffer as not modifiable
1994     setlocal nomodifiable
1995
1996     " Restore the report option
1997     let &report = old_report
1998
1999     " Update the start and end line numbers for all the files following this
2000     " file
2001     let start = s:tlist_{fidx}_start
2002     " include the empty line after the last line
2003     if g:Tlist_Compact_Format
2004         let end = s:tlist_{fidx}_end
2005     else
2006         let end = s:tlist_{fidx}_end + 1
2007     endif
2008     call s:Tlist_Window_Update_Line_Offsets(fidx + 1, 1, end - start + 1)
2009
2010     " Now that we have updated the taglist window, update the tags
2011     " menu (if present)
2012     if g:Tlist_Show_Menu
2013         call s:Tlist_Menu_Update_File(1)
2014     endif
2015 endfunction
2016
2017 " Tlist_Init_File
2018 " Initialize the variables for a new file
2019 function! s:Tlist_Init_File(filename, ftype)
2020     call s:Tlist_Log_Msg('Tlist_Init_File (' . a:filename . ')')
2021     " Add new files at the end of the list
2022     let fidx = s:tlist_file_count
2023     let s:tlist_file_count = s:tlist_file_count + 1
2024     " Add the new file name to the taglist list of file names
2025     let s:tlist_file_names = s:tlist_file_names . a:filename . "\n"
2026
2027     " Initialize the file variables
2028     let s:tlist_{fidx}_filename = a:filename
2029     let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type
2030     let s:tlist_{fidx}_filetype = a:ftype
2031     let s:tlist_{fidx}_mtime = -1
2032     let s:tlist_{fidx}_start = 0
2033     let s:tlist_{fidx}_end = 0
2034     let s:tlist_{fidx}_valid = 0
2035     let s:tlist_{fidx}_visible = 0
2036     let s:tlist_{fidx}_tag_count = 0
2037     let s:tlist_{fidx}_menu_cmd = ''
2038
2039     " Initialize the tag type variables
2040     let i = 1
2041     while i <= s:tlist_{a:ftype}_count
2042         let ttype = s:tlist_{a:ftype}_{i}_name
2043         let s:tlist_{fidx}_{ttype} = ''
2044         let s:tlist_{fidx}_{ttype}_offset = 0
2045         let s:tlist_{fidx}_{ttype}_count = 0
2046         let i = i + 1
2047     endwhile
2048
2049     return fidx
2050 endfunction
2051
2052 " Tlist_Get_Tag_Type_By_Tag
2053 " Return the tag type for the specified tag index
2054 function! s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx)
2055     let ttype_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_type'
2056
2057     " Already parsed and have the tag name
2058     if exists(ttype_var)
2059         return {ttype_var}
2060     endif
2061
2062     let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
2063     let {ttype_var} = s:Tlist_Extract_Tagtype(tag_line)
2064
2065     return {ttype_var}
2066 endfunction
2067
2068 " Tlist_Get_Tag_Prototype
2069 function! s:Tlist_Get_Tag_Prototype(fidx, tidx)
2070     let tproto_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_proto'
2071
2072     " Already parsed and have the tag prototype
2073     if exists(tproto_var)
2074         return {tproto_var}
2075     endif
2076
2077     " Parse and extract the tag prototype
2078     let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
2079     let start = stridx(tag_line, '/^') + 2
2080     let end = stridx(tag_line, '/;"' . "\t")
2081     if tag_line[end - 1] == '$'
2082         let end = end -1
2083     endif
2084     let tag_proto = strpart(tag_line, start, end - start)
2085     let {tproto_var} = substitute(tag_proto, '\s*', '', '')
2086
2087     return {tproto_var}
2088 endfunction
2089
2090 " Tlist_Get_Tag_SearchPat
2091 function! s:Tlist_Get_Tag_SearchPat(fidx, tidx)
2092     let tpat_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_searchpat'
2093
2094     " Already parsed and have the tag search pattern
2095     if exists(tpat_var)
2096         return {tpat_var}
2097     endif
2098
2099     " Parse and extract the tag search pattern
2100     let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
2101     let start = stridx(tag_line, '/^') + 2
2102     let end = stridx(tag_line, '/;"' . "\t")
2103     if tag_line[end - 1] == '$'
2104         let end = end -1
2105     endif
2106     let {tpat_var} = '\V\^' . strpart(tag_line, start, end - start) .
2107                         \ (tag_line[end] == '$' ? '\$' : '')
2108
2109     return {tpat_var}
2110 endfunction
2111
2112 " Tlist_Get_Tag_Linenum
2113 " Return the tag line number, given the tag index
2114 function! s:Tlist_Get_Tag_Linenum(fidx, tidx)
2115     let tline_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_linenum'
2116
2117     " Already parsed and have the tag line number
2118     if exists(tline_var)
2119         return {tline_var}
2120     endif
2121
2122     " Parse and extract the tag line number
2123     let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
2124     let start = strridx(tag_line, 'line:') + 5
2125     let end = strridx(tag_line, "\t")
2126     if end < start
2127         let {tline_var} = strpart(tag_line, start) + 0
2128     else
2129         let {tline_var} = strpart(tag_line, start, end - start) + 0
2130     endif
2131
2132     return {tline_var}
2133 endfunction
2134
2135 " Tlist_Parse_Tagline
2136 " Parse a tag line from the ctags output. Separate the tag output based on the
2137 " tag type and store it in the tag type variable.
2138 " The format of each line in the ctags output is:
2139 "
2140 "     tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields
2141 "
2142 function! s:Tlist_Parse_Tagline(tag_line)
2143     if a:tag_line == ''
2144         " Skip empty lines
2145         return
2146     endif
2147
2148     " Extract the tag type
2149     let ttype = s:Tlist_Extract_Tagtype(a:tag_line)
2150
2151     " Make sure the tag type is a valid and supported one
2152     if ttype == '' || stridx(s:ctags_flags, ttype) == -1
2153         " Line is not in proper tags format or Tag type is not supported
2154         return
2155     endif
2156
2157     " Update the total tag count
2158     let s:tidx = s:tidx + 1
2159
2160     " The following variables are used to optimize this code.  Vim is slow in
2161     " using curly brace names. To reduce the amount of processing needed, the
2162     " curly brace variables are pre-processed here
2163     let fidx_tidx = 's:tlist_' . s:fidx . '_' . s:tidx
2164     let fidx_ttype = 's:tlist_' . s:fidx . '_' . ttype
2165
2166     " Update the count of this tag type
2167     let ttype_idx = {fidx_ttype}_count + 1
2168     let {fidx_ttype}_count = ttype_idx
2169
2170     " Store the ctags output for this tag
2171     let {fidx_tidx}_tag = a:tag_line
2172
2173     " Store the tag index and the tag type index (back pointers)
2174     let {fidx_ttype}_{ttype_idx} = s:tidx
2175     let {fidx_tidx}_ttype_idx = ttype_idx
2176
2177     " Extract the tag name
2178     let tag_name = strpart(a:tag_line, 0, stridx(a:tag_line, "\t"))
2179
2180     " Extract the tag scope/prototype
2181     if g:Tlist_Display_Prototype
2182         let ttxt = '    ' . s:Tlist_Get_Tag_Prototype(s:fidx, s:tidx)
2183     else
2184         let ttxt = '    ' . tag_name
2185
2186         " Add the tag scope, if it is available and is configured. Tag
2187         " scope is the last field after the 'line:<num>\t' field
2188         if g:Tlist_Display_Tag_Scope
2189             let tag_scope = s:Tlist_Extract_Tag_Scope(a:tag_line)
2190             if tag_scope != ''
2191                 let ttxt = ttxt . ' [' . tag_scope . ']'
2192             endif
2193         endif
2194     endif
2195
2196     " Add this tag to the tag type variable
2197     let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
2198
2199     " Save the tag name
2200     let {fidx_tidx}_tag_name = tag_name
2201 endfunction
2202
2203 " Tlist_Process_File
2204 " Get the list of tags defined in the specified file and store them
2205 " in Vim variables. Returns the file index where the tags are stored.
2206 function! s:Tlist_Process_File(filename, ftype)
2207     call s:Tlist_Log_Msg('Tlist_Process_File (' . a:filename . ', ' .
2208                 \ a:ftype . ')')
2209     " Check whether this file is supported
2210     if s:Tlist_Skip_File(a:filename, a:ftype)
2211         return -1
2212     endif
2213
2214     " If the tag types for this filetype are not yet created, then create
2215     " them now
2216     let var = 's:tlist_' . a:ftype . '_count'
2217     if !exists(var)
2218         if s:Tlist_FileType_Init(a:ftype) == 0
2219             return -1
2220         endif
2221     endif
2222
2223     " If this file is already processed, then use the cached values
2224     let fidx = s:Tlist_Get_File_Index(a:filename)
2225     if fidx == -1
2226         " First time, this file is loaded
2227         let fidx = s:Tlist_Init_File(a:filename, a:ftype)
2228     else
2229         " File was previously processed. Discard the tag information
2230         call s:Tlist_Discard_TagInfo(fidx)
2231     endif
2232
2233     let s:tlist_{fidx}_valid = 1
2234
2235     " Exuberant ctags arguments to generate a tag list
2236     let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks '
2237
2238     " Form the ctags argument depending on the sort type
2239     if s:tlist_{fidx}_sort_type == 'name'
2240         let ctags_args = ctags_args . '--sort=yes'
2241     else
2242         let ctags_args = ctags_args . '--sort=no'
2243     endif
2244
2245     " Add the filetype specific arguments
2246     let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args
2247
2248     " Ctags command to produce output with regexp for locating the tags
2249     let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args
2250     let ctags_cmd = ctags_cmd . ' "' . a:filename . '"'
2251
2252     if &shellxquote == '"'
2253         " Double-quotes within double-quotes will not work in the
2254         " command-line.If the 'shellxquote' option is set to double-quotes,
2255         " then escape the double-quotes in the ctags command-line.
2256         let ctags_cmd = escape(ctags_cmd, '"')
2257     endif
2258
2259     " In Windows 95, if not using cygwin, disable the 'shellslash'
2260     " option. Otherwise, this will cause problems when running the
2261     " ctags command.
2262     if has('win95') && !has('win32unix')
2263         let old_shellslash = &shellslash
2264         set noshellslash
2265     endif
2266
2267     if has('win32') && !has('win32unix') && !has('win95')
2268                 \ && (&shell =~ 'cmd.exe')
2269         " Windows does not correctly deal with commands that have more than 1
2270         " set of double quotes.  It will strip them all resulting in:
2271         " 'C:\Program' is not recognized as an internal or external command
2272         " operable program or batch file.  To work around this, place the
2273         " command inside a batch file and call the batch file.
2274         " Do this only on Win2K, WinXP and above.
2275         " Contributed by: David Fishburn.
2276         let s:taglist_tempfile = fnamemodify(tempname(), ':h') .
2277                     \ '\taglist.cmd'
2278         exe 'redir! > ' . s:taglist_tempfile
2279         silent echo ctags_cmd
2280         redir END
2281
2282         call s:Tlist_Log_Msg('Cmd inside batch file: ' . ctags_cmd)
2283         let ctags_cmd = '"' . s:taglist_tempfile . '"'
2284     endif
2285
2286     call s:Tlist_Log_Msg('Cmd: ' . ctags_cmd)
2287
2288     " Run ctags and get the tag list
2289     let cmd_output = system(ctags_cmd)
2290
2291     " Restore the value of the 'shellslash' option.
2292     if has('win95') && !has('win32unix')
2293         let &shellslash = old_shellslash
2294     endif
2295
2296     if exists('s:taglist_tempfile')
2297         " Delete the temporary cmd file created on MS-Windows
2298         call delete(s:taglist_tempfile)
2299     endif
2300
2301     " Handle errors
2302     if v:shell_error
2303         let msg = "Taglist: Failed to generate tags for " . a:filename
2304         call s:Tlist_Warning_Msg(msg)
2305         if cmd_output != ''
2306             call s:Tlist_Warning_Msg(cmd_output)
2307         endif
2308         return fidx
2309     endif
2310
2311     " Store the modification time for the file
2312     let s:tlist_{fidx}_mtime = getftime(a:filename)
2313
2314     " No tags for current file
2315     if cmd_output == ''
2316         call s:Tlist_Log_Msg('No tags defined in ' . a:filename)
2317         return fidx
2318     endif
2319
2320     call s:Tlist_Log_Msg('Generated tags information for ' . a:filename)
2321
2322     if v:version > 601
2323         " The following script local variables are used by the
2324         " Tlist_Parse_Tagline() function.
2325         let s:ctags_flags = s:tlist_{a:ftype}_ctags_flags
2326         let s:fidx = fidx
2327         let s:tidx = 0
2328
2329         " Process the ctags output one line at a time.  The substitute()
2330         " command is used to parse the tag lines instead of using the
2331         " matchstr()/stridx()/strpart() functions for performance reason
2332         call substitute(cmd_output, "\\([^\n]\\+\\)\n",
2333                     \ '\=s:Tlist_Parse_Tagline(submatch(1))', 'g')
2334
2335         " Save the number of tags for this file
2336         let s:tlist_{fidx}_tag_count = s:tidx
2337
2338         " The following script local variables are no longer needed
2339         unlet! s:ctags_flags
2340         unlet! s:tidx
2341         unlet! s:fidx
2342     else
2343         " Due to a bug in Vim earlier than version 6.1,
2344         " we cannot use substitute() to parse the ctags output.
2345         " Instead the slow str*() functions are used
2346         let ctags_flags = s:tlist_{a:ftype}_ctags_flags
2347         let tidx = 0
2348
2349         while cmd_output != ''
2350             " Extract one line at a time
2351             let idx = stridx(cmd_output, "\n")
2352             let one_line = strpart(cmd_output, 0, idx)
2353             " Remove the line from the tags output
2354             let cmd_output = strpart(cmd_output, idx + 1)
2355
2356             if one_line == ''
2357                 " Line is not in proper tags format
2358                 continue
2359             endif
2360
2361             " Extract the tag type
2362             let ttype = s:Tlist_Extract_Tagtype(one_line)
2363
2364             " Make sure the tag type is a valid and supported one
2365             if ttype == '' || stridx(ctags_flags, ttype) == -1
2366                 " Line is not in proper tags format or Tag type is not
2367                 " supported
2368                 continue
2369             endif
2370
2371             " Update the total tag count
2372             let tidx = tidx + 1
2373
2374             " The following variables are used to optimize this code.  Vim is
2375             " slow in using curly brace names. To reduce the amount of
2376             " processing needed, the curly brace variables are pre-processed
2377             " here
2378             let fidx_tidx = 's:tlist_' . fidx . '_' . tidx
2379             let fidx_ttype = 's:tlist_' . fidx . '_' . ttype
2380
2381             " Update the count of this tag type
2382             let ttype_idx = {fidx_ttype}_count + 1
2383             let {fidx_ttype}_count = ttype_idx
2384
2385             " Store the ctags output for this tag
2386             let {fidx_tidx}_tag = one_line
2387
2388             " Store the tag index and the tag type index (back pointers)
2389             let {fidx_ttype}_{ttype_idx} = tidx
2390             let {fidx_tidx}_ttype_idx = ttype_idx
2391
2392             " Extract the tag name
2393             let tag_name = strpart(one_line, 0, stridx(one_line, "\t"))
2394
2395             " Extract the tag scope/prototype
2396             if g:Tlist_Display_Prototype
2397                 let ttxt = '    ' . s:Tlist_Get_Tag_Prototype(fidx, tidx)
2398             else
2399                 let ttxt = '    ' . tag_name
2400
2401                 " Add the tag scope, if it is available and is configured. Tag
2402                 " scope is the last field after the 'line:<num>\t' field
2403                 if g:Tlist_Display_Tag_Scope
2404                     let tag_scope = s:Tlist_Extract_Tag_Scope(one_line)
2405                     if tag_scope != ''
2406                         let ttxt = ttxt . ' [' . tag_scope . ']'
2407                     endif
2408                 endif
2409             endif
2410
2411             " Add this tag to the tag type variable
2412             let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
2413
2414             " Save the tag name
2415             let {fidx_tidx}_tag_name = tag_name
2416         endwhile
2417
2418         " Save the number of tags for this file
2419         let s:tlist_{fidx}_tag_count = tidx
2420     endif
2421
2422     call s:Tlist_Log_Msg('Processed ' . s:tlist_{fidx}_tag_count . 
2423                 \ ' tags in ' . a:filename)
2424
2425     return fidx
2426 endfunction
2427
2428 " Tlist_Update_File
2429 " Update the tags for a file (if needed)
2430 function! Tlist_Update_File(filename, ftype)
2431     call s:Tlist_Log_Msg('Tlist_Update_File (' . a:filename . ')')
2432     " If the file doesn't support tag listing, skip it
2433     if s:Tlist_Skip_File(a:filename, a:ftype)
2434         return
2435     endif
2436
2437     " Convert the file name to a full path
2438     let fname = fnamemodify(a:filename, ':p')
2439
2440     " First check whether the file already exists
2441     let fidx = s:Tlist_Get_File_Index(fname)
2442
2443     if fidx != -1 && s:tlist_{fidx}_valid
2444         " File exists and the tags are valid
2445         " Check whether the file was modified after the last tags update
2446         " If it is modified, then update the tags
2447         if s:tlist_{fidx}_mtime == getftime(fname)
2448             return
2449         endif
2450     else
2451         " If the tags were removed previously based on a user request,
2452         " as we are going to update the tags (based on the user request),
2453         " remove the filename from the deleted list
2454         call s:Tlist_Update_Remove_List(fname, 0)
2455     endif
2456
2457     " If the taglist window is opened, update it
2458     let winnum = bufwinnr(g:TagList_title)
2459     if winnum == -1
2460         " Taglist window is not present. Just update the taglist
2461         " and return
2462         call s:Tlist_Process_File(fname, a:ftype)
2463     else
2464         if g:Tlist_Show_One_File && s:tlist_cur_file_idx != -1
2465             " If tags for only one file are displayed and we are not
2466             " updating the tags for that file, then no need to
2467             " refresh the taglist window. Otherwise, the taglist
2468             " window should be updated.
2469             if s:tlist_{s:tlist_cur_file_idx}_filename != fname
2470                 call s:Tlist_Process_File(fname, a:ftype)
2471                 return
2472             endif
2473         endif
2474
2475         " Save the current window number
2476         let save_winnr = winnr()
2477
2478         " Goto the taglist window
2479         call s:Tlist_Window_Goto_Window()
2480
2481         " Save the cursor position
2482         let save_line = line('.')
2483         let save_col = col('.')
2484
2485         " Update the taglist window
2486         call s:Tlist_Window_Refresh_File(fname, a:ftype)
2487
2488         " Restore the cursor position
2489         if v:version >= 601
2490             call cursor(save_line, save_col)
2491         else
2492             exe save_line
2493             exe 'normal! ' . save_col . '|'
2494         endif
2495
2496         if winnr() != save_winnr
2497             " Go back to the original window
2498             call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w')
2499         endif
2500     endif
2501
2502     " Update the taglist menu
2503     if g:Tlist_Show_Menu
2504         call s:Tlist_Menu_Update_File(1)
2505     endif
2506 endfunction
2507
2508 " Tlist_Window_Close
2509 " Close the taglist window
2510 function! s:Tlist_Window_Close()
2511     call s:Tlist_Log_Msg('Tlist_Window_Close()')
2512     " Make sure the taglist window exists
2513     let winnum = bufwinnr(g:TagList_title)
2514     if winnum == -1
2515         call s:Tlist_Warning_Msg('Error: Taglist window is not open')
2516         return
2517     endif
2518
2519     if winnr() == winnum
2520         " Already in the taglist window. Close it and return
2521         if winbufnr(2) != -1
2522             " If a window other than the taglist window is open,
2523             " then only close the taglist window.
2524             close
2525         endif
2526     else
2527         " Goto the taglist window, close it and then come back to the
2528         " original window
2529         let curbufnr = bufnr('%')
2530         exe winnum . 'wincmd w'
2531         close
2532         " Need to jump back to the original window only if we are not
2533         " already in that window
2534         let winnum = bufwinnr(curbufnr)
2535         if winnr() != winnum
2536             exe winnum . 'wincmd w'
2537         endif
2538     endif
2539 endfunction
2540
2541 " Tlist_Window_Mark_File_Window
2542 " Mark the current window as the file window to use when jumping to a tag.
2543 " Only if the current window is a non-plugin, non-preview and non-taglist
2544 " window
2545 function! s:Tlist_Window_Mark_File_Window()
2546     if getbufvar('%', '&buftype') == '' && !&previewwindow
2547         let w:tlist_file_window = "yes"
2548     endif
2549 endfunction
2550
2551 " Tlist_Window_Open
2552 " Open and refresh the taglist window
2553 function! s:Tlist_Window_Open()
2554     call s:Tlist_Log_Msg('Tlist_Window_Open()')
2555     " If the window is open, jump to it
2556     let winnum = bufwinnr(g:TagList_title)
2557     if winnum != -1
2558         " Jump to the existing window
2559         if winnr() != winnum
2560             exe winnum . 'wincmd w'
2561         endif
2562         return
2563     endif
2564
2565     if s:tlist_app_name == "winmanager"
2566         " Taglist plugin is no longer part of the winmanager app
2567         let s:tlist_app_name = "none"
2568     endif
2569
2570     " Get the filename and filetype for the specified buffer
2571     let curbuf_name = fnamemodify(bufname('%'), ':p')
2572     let curbuf_ftype = s:Tlist_Get_Buffer_Filetype('%')
2573     let cur_lnum = line('.')
2574
2575     " Mark the current window as the desired window to open a file when a tag
2576     " is selected.
2577     call s:Tlist_Window_Mark_File_Window()
2578
2579     " Open the taglist window
2580     call s:Tlist_Window_Create()
2581
2582     call s:Tlist_Window_Refresh()
2583
2584     if g:Tlist_Show_One_File
2585         " Add only the current buffer and file
2586         "
2587         " If the file doesn't support tag listing, skip it
2588         if !s:Tlist_Skip_File(curbuf_name, curbuf_ftype)
2589             call s:Tlist_Window_Refresh_File(curbuf_name, curbuf_ftype)
2590         endif
2591     endif
2592
2593     if g:Tlist_File_Fold_Auto_Close
2594         " Open the fold for the current file, as all the folds in
2595         " the taglist window are closed
2596         let fidx = s:Tlist_Get_File_Index(curbuf_name)
2597         if fidx != -1
2598             exe "silent! " . s:tlist_{fidx}_start . "," .
2599                         \ s:tlist_{fidx}_end . "foldopen!"
2600         endif
2601     endif
2602
2603     " Highlight the current tag
2604     call s:Tlist_Window_Highlight_Tag(curbuf_name, cur_lnum, 1, 1)
2605 endfunction
2606
2607 " Tlist_Window_Toggle()
2608 " Open or close a taglist window
2609 function! s:Tlist_Window_Toggle()
2610     call s:Tlist_Log_Msg('Tlist_Window_Toggle()')
2611     " If taglist window is open then close it.
2612     let winnum = bufwinnr(g:TagList_title)
2613     if winnum != -1
2614         call s:Tlist_Window_Close()
2615         return
2616     endif
2617
2618     call s:Tlist_Window_Open()
2619
2620     " Go back to the original window, if Tlist_GainFocus_On_ToggleOpen is not
2621     " set
2622     if !g:Tlist_GainFocus_On_ToggleOpen
2623         call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
2624     endif
2625
2626     " Update the taglist menu
2627     if g:Tlist_Show_Menu
2628         call s:Tlist_Menu_Update_File(0)
2629     endif
2630 endfunction
2631
2632 " Tlist_Process_Filelist
2633 " Process multiple files. Each filename is separated by "\n"
2634 " Returns the number of processed files
2635 function! s:Tlist_Process_Filelist(file_names)
2636     let flist = a:file_names
2637
2638     " Enable lazy screen updates
2639     let old_lazyredraw = &lazyredraw
2640     set lazyredraw
2641
2642     " Keep track of the number of processed files
2643     let fcnt = 0
2644
2645     " Process one file at a time
2646     while flist != ''
2647         let nl_idx = stridx(flist, "\n")
2648         let one_file = strpart(flist, 0, nl_idx)
2649
2650         " Remove the filename from the list
2651         let flist = strpart(flist, nl_idx + 1)
2652
2653         if one_file == ''
2654             continue
2655         endif
2656
2657         " Skip directories
2658         if isdirectory(one_file)
2659             continue
2660         endif
2661
2662         let ftype = s:Tlist_Detect_Filetype(one_file)
2663
2664         echon "\r                                                              "
2665         echon "\rProcessing tags for " . fnamemodify(one_file, ':p:t')
2666
2667         let fcnt = fcnt + 1
2668
2669         call Tlist_Update_File(one_file, ftype)
2670     endwhile
2671
2672     " Clear the displayed informational messages
2673     echon "\r                                                            "
2674
2675     " Restore the previous state
2676     let &lazyredraw = old_lazyredraw
2677
2678     return fcnt
2679 endfunction
2680
2681 " Tlist_Process_Dir
2682 " Process the files in a directory matching the specified pattern
2683 function! s:Tlist_Process_Dir(dir_name, pat)
2684     let flist = glob(a:dir_name . '/' . a:pat) . "\n"
2685
2686     let fcnt = s:Tlist_Process_Filelist(flist)
2687
2688     let len = strlen(a:dir_name)
2689     if a:dir_name[len - 1] == '\' || a:dir_name[len - 1] == '/'
2690         let glob_expr = a:dir_name . '*'
2691     else
2692         let glob_expr = a:dir_name . '/*'
2693     endif
2694     let all_files = glob(glob_expr) . "\n"
2695
2696     while all_files != ''
2697         let nl_idx = stridx(all_files, "\n")
2698         let one_file = strpart(all_files, 0, nl_idx)
2699
2700         let all_files = strpart(all_files, nl_idx + 1)
2701         if one_file == ''
2702             continue
2703         endif
2704
2705         " Skip non-directory names
2706         if !isdirectory(one_file)
2707             continue
2708         endif
2709
2710         echon "\r                                                              "
2711         echon "\rProcessing files in directory " . fnamemodify(one_file, ':t')
2712         let fcnt = fcnt + s:Tlist_Process_Dir(one_file, a:pat)
2713     endwhile
2714
2715     return fcnt
2716 endfunction
2717
2718 " Tlist_Add_Files_Recursive
2719 " Add files recursively from a directory
2720 function! s:Tlist_Add_Files_Recursive(dir, ...)
2721     let dir_name = fnamemodify(a:dir, ':p')
2722     if !isdirectory(dir_name)
2723         call s:Tlist_Warning_Msg('Error: ' . dir_name . ' is not a directory')
2724         return
2725     endif
2726
2727     if a:0 == 1
2728         " User specified file pattern
2729         let pat = a:1
2730     else
2731         " Default file pattern
2732         let pat = '*'
2733     endif
2734
2735     echon "\r                                                              "
2736     echon "\rProcessing files in directory " . fnamemodify(dir_name, ':t')
2737     let fcnt = s:Tlist_Process_Dir(dir_name, pat)
2738
2739     echon "\rAdded " . fcnt . " files to the taglist"
2740 endfunction
2741
2742 " Tlist_Add_Files
2743 " Add the specified list of files to the taglist
2744 function! s:Tlist_Add_Files(...)
2745     let flist = ''
2746     let i = 1
2747
2748     " Get all the files matching the file patterns supplied as argument
2749     while i <= a:0
2750         let flist = flist . glob(a:{i}) . "\n"
2751         let i = i + 1
2752     endwhile
2753
2754     if flist == ''
2755         call s:Tlist_Warning_Msg('Error: No matching files are found')
2756         return
2757     endif
2758
2759     let fcnt = s:Tlist_Process_Filelist(flist)
2760     echon "\rAdded " . fcnt . " files to the taglist"
2761 endfunction
2762
2763 " Tlist_Extract_Tagtype
2764 " Extract the tag type from the tag text
2765 function! s:Tlist_Extract_Tagtype(tag_line)
2766     " The tag type is after the tag prototype field. The prototype field
2767     " ends with the /;"\t string. We add 4 at the end to skip the characters
2768     " in this special string..
2769     let start = strridx(a:tag_line, '/;"' . "\t") + 4
2770     let end = strridx(a:tag_line, 'line:') - 1
2771     let ttype = strpart(a:tag_line, start, end - start)
2772
2773     return ttype
2774 endfunction
2775
2776 " Tlist_Extract_Tag_Scope
2777 " Extract the tag scope from the tag text
2778 function! s:Tlist_Extract_Tag_Scope(tag_line)
2779     let start = strridx(a:tag_line, 'line:')
2780     let end = strridx(a:tag_line, "\t")
2781     if end <= start
2782         return ''
2783     endif
2784
2785     let tag_scope = strpart(a:tag_line, end + 1)
2786     let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1)
2787
2788     return tag_scope
2789 endfunction
2790
2791 " Tlist_Refresh()
2792 " Refresh the taglist
2793 function! s:Tlist_Refresh()
2794     call s:Tlist_Log_Msg('Tlist_Refresh (Skip_Refresh = ' .
2795                 \ s:Tlist_Skip_Refresh . ', ' . bufname('%') . ')')
2796     " If we are entering the buffer from one of the taglist functions, then
2797     " no need to refresh the taglist window again.
2798     if s:Tlist_Skip_Refresh
2799         " We still need to update the taglist menu
2800         if g:Tlist_Show_Menu
2801             call s:Tlist_Menu_Update_File(0)
2802         endif
2803         return
2804     endif
2805
2806     " If part of the winmanager plugin and not configured to process
2807     " tags always and not configured to display the tags menu, then return
2808     if (s:tlist_app_name == 'winmanager') && !g:Tlist_Process_File_Always
2809                 \ && !g:Tlist_Show_Menu
2810         return
2811     endif
2812
2813     " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
2814     if &buftype != ''
2815         return
2816     endif
2817
2818     let filename = fnamemodify(bufname('%'), ':p')
2819     let ftype = s:Tlist_Get_Buffer_Filetype('%')
2820
2821     " If the file doesn't support tag listing, skip it
2822     if s:Tlist_Skip_File(filename, ftype)
2823         return
2824     endif
2825
2826     let tlist_win = bufwinnr(g:TagList_title)
2827
2828     " If the taglist window is not opened and not configured to process
2829     " tags always and not displaying the tags menu, then return
2830     if tlist_win == -1 && !g:Tlist_Process_File_Always && !g:Tlist_Show_Menu
2831         return
2832     endif
2833
2834     let fidx = s:Tlist_Get_File_Index(filename)
2835     if fidx == -1
2836         " Check whether this file is removed based on user request
2837         " If it is, then don't display the tags for this file
2838         if s:Tlist_User_Removed_File(filename)
2839             return
2840         endif
2841
2842         " If the taglist should not be auto updated, then return
2843         if !g:Tlist_Auto_Update
2844             return
2845         endif
2846     endif
2847
2848     let cur_lnum = line('.')
2849
2850     if fidx == -1
2851         " Update the tags for the file
2852         let fidx = s:Tlist_Process_File(filename, ftype)
2853     else
2854         let mtime = getftime(filename)
2855         if s:tlist_{fidx}_mtime != mtime
2856             " Invalidate the tags listed for this file
2857             let s:tlist_{fidx}_valid = 0
2858
2859             " Update the taglist and the window
2860             call Tlist_Update_File(filename, ftype)
2861
2862             " Store the new file modification time
2863             let s:tlist_{fidx}_mtime = mtime
2864         endif
2865     endif
2866
2867     " Update the taglist window
2868     if tlist_win != -1
2869         " Disable screen updates
2870         let old_lazyredraw = &lazyredraw
2871         set nolazyredraw
2872
2873         " Save the current window number
2874         let save_winnr = winnr()
2875
2876         " Goto the taglist window
2877         call s:Tlist_Window_Goto_Window()
2878
2879         if !g:Tlist_Auto_Highlight_Tag || !g:Tlist_Highlight_Tag_On_BufEnter
2880             " Save the cursor position
2881             let save_line = line('.')
2882             let save_col = col('.')
2883         endif
2884
2885         " Update the taglist window
2886         call s:Tlist_Window_Refresh_File(filename, ftype)
2887
2888         " Open the fold for the file
2889         exe "silent! " . s:tlist_{fidx}_start . "," .
2890                     \ s:tlist_{fidx}_end . "foldopen!"
2891
2892         if g:Tlist_Highlight_Tag_On_BufEnter && g:Tlist_Auto_Highlight_Tag
2893             if g:Tlist_Show_One_File && s:tlist_cur_file_idx != fidx
2894                 " If displaying tags for only one file in the taglist
2895                 " window and about to display the tags for a new file,
2896                 " then center the current tag line for the new file
2897                 let center_tag_line = 1
2898             else
2899                 let center_tag_line = 0
2900             endif
2901
2902             " Highlight the current tag
2903             call s:Tlist_Window_Highlight_Tag(filename, cur_lnum, 1, center_tag_line)
2904         else
2905             " Restore the cursor position
2906             if v:version >= 601
2907                 call cursor(save_line, save_col)
2908             else
2909                 exe save_line
2910                 exe 'normal! ' . save_col . '|'
2911             endif
2912         endif
2913
2914         " Jump back to the original window
2915         if save_winnr != winnr()
2916             call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w')
2917         endif
2918
2919         " Restore screen updates
2920         let &lazyredraw = old_lazyredraw
2921     endif
2922
2923     " Update the taglist menu
2924     if g:Tlist_Show_Menu
2925         call s:Tlist_Menu_Update_File(0)
2926     endif
2927 endfunction
2928
2929 " Tlist_Change_Sort()
2930 " Change the sort order of the tag listing
2931 " caller == 'cmd', command used in the taglist window
2932 " caller == 'menu', taglist menu
2933 " action == 'toggle', toggle sort from name to order and vice versa
2934 " action == 'set', set the sort order to sort_type
2935 function! s:Tlist_Change_Sort(caller, action, sort_type)
2936     call s:Tlist_Log_Msg('Tlist_Change_Sort (caller = ' . a:caller .
2937             \ ', action = ' . a:action . ', sort_type = ' . a:sort_type . ')')
2938     if a:caller == 'cmd'
2939         let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
2940         if fidx == -1
2941             return
2942         endif
2943
2944         " Remove the previous highlighting
2945         match none
2946     elseif a:caller == 'menu'
2947         let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
2948         if fidx == -1
2949             return
2950         endif
2951     endif
2952
2953     if a:action == 'toggle'
2954         let sort_type = s:tlist_{fidx}_sort_type
2955
2956         " Toggle the sort order from 'name' to 'order' and vice versa
2957         if sort_type == 'name'
2958             let s:tlist_{fidx}_sort_type = 'order'
2959         else
2960             let s:tlist_{fidx}_sort_type = 'name'
2961         endif
2962     else
2963         let s:tlist_{fidx}_sort_type = a:sort_type
2964     endif
2965
2966     " Invalidate the tags listed for this file
2967     let s:tlist_{fidx}_valid = 0
2968
2969     if a:caller  == 'cmd'
2970         " Save the current line for later restoration
2971         let curline = '\V\^' . getline('.') . '\$'
2972
2973         call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
2974                     \   s:tlist_{fidx}_filetype)
2975
2976         exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
2977
2978         " Go back to the cursor line before the tag list is sorted
2979         call search(curline, 'w')
2980
2981         call s:Tlist_Menu_Update_File(1)
2982     else
2983         call s:Tlist_Menu_Remove_File()
2984
2985         call s:Tlist_Refresh()
2986     endif
2987 endfunction
2988
2989 " Tlist_Update_Current_File()
2990 " Update taglist for the current buffer by regenerating the tag list
2991 " Contributed by WEN Guopeng.
2992 function! s:Tlist_Update_Current_File()
2993     call s:Tlist_Log_Msg('Tlist_Update_Current_File()')
2994     if winnr() == bufwinnr(g:TagList_title)
2995         " In the taglist window. Update the current file
2996         call s:Tlist_Window_Update_File()
2997     else
2998         " Not in the taglist window. Update the current buffer
2999         let filename = fnamemodify(bufname('%'), ':p')
3000         let fidx = s:Tlist_Get_File_Index(filename)
3001         if fidx != -1
3002             let s:tlist_{fidx}_valid = 0
3003         endif
3004         let ft = s:Tlist_Get_Buffer_Filetype('%')
3005         call Tlist_Update_File(filename, ft)
3006     endif
3007 endfunction
3008
3009 " Tlist_Window_Update_File()
3010 " Update the tags displayed in the taglist window
3011 function! s:Tlist_Window_Update_File()
3012     call s:Tlist_Log_Msg('Tlist_Window_Update_File()')
3013     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
3014     if fidx == -1
3015         return
3016     endif
3017
3018     " Remove the previous highlighting
3019     match none
3020
3021     " Save the current line for later restoration
3022     let curline = '\V\^' . getline('.') . '\$'
3023
3024     let s:tlist_{fidx}_valid = 0
3025
3026     " Update the taglist window
3027     call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
3028                 \ s:tlist_{fidx}_filetype)
3029
3030     exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
3031
3032     " Go back to the tag line before the list is updated
3033     call search(curline, 'w')
3034 endfunction
3035
3036 " Tlist_Window_Get_Tag_Type_By_Linenum()
3037 " Return the tag type index for the specified line in the taglist window
3038 function! s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum)
3039     let ftype = s:tlist_{a:fidx}_filetype
3040
3041     " Determine to which tag type the current line number belongs to using the
3042     " tag type start line number and the number of tags in a tag type
3043     let i = 1
3044     while i <= s:tlist_{ftype}_count
3045         let ttype = s:tlist_{ftype}_{i}_name
3046         let start_lnum =
3047                     \ s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset
3048         let end =  start_lnum + s:tlist_{a:fidx}_{ttype}_count
3049         if a:lnum >= start_lnum && a:lnum <= end
3050             break
3051         endif
3052         let i = i + 1
3053     endwhile
3054
3055     " Current line doesn't belong to any of the displayed tag types
3056     if i > s:tlist_{ftype}_count
3057         return ''
3058     endif
3059
3060     return ttype
3061 endfunction
3062
3063 " Tlist_Window_Get_Tag_Index()
3064 " Return the tag index for the specified line in the taglist window
3065 function! s:Tlist_Window_Get_Tag_Index(fidx, lnum)
3066     let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(a:fidx, a:lnum)
3067
3068     " Current line doesn't belong to any of the displayed tag types
3069     if ttype == ''
3070         return 0
3071     endif
3072
3073     " Compute the index into the displayed tags for the tag type
3074     let ttype_lnum = s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset
3075     let tidx = a:lnum - ttype_lnum
3076     if tidx == 0
3077         return 0
3078     endif
3079
3080     " Get the corresponding tag line and return it
3081     return s:tlist_{a:fidx}_{ttype}_{tidx}
3082 endfunction
3083
3084 " Tlist_Window_Highlight_Line
3085 " Highlight the current line
3086 function! s:Tlist_Window_Highlight_Line()
3087     " Clear previously selected name
3088     match none
3089
3090     " Highlight the current line
3091     if g:Tlist_Display_Prototype == 0
3092         let pat = '/\%' . line('.') . 'l\s\+\zs.*/'
3093     else
3094         let pat = '/\%' . line('.') . 'l.*/'
3095     endif
3096
3097     exe 'match TagListTagName ' . pat
3098 endfunction
3099
3100 " Tlist_Window_Open_File
3101 " Open the specified file in either a new window or an existing window
3102 " and place the cursor at the specified tag pattern
3103 function! s:Tlist_Window_Open_File(win_ctrl, filename, tagpat)
3104     call s:Tlist_Log_Msg('Tlist_Window_Open_File (' . a:filename . ',' .
3105                 \ a:win_ctrl . ')')
3106     let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
3107     let s:Tlist_Skip_Refresh = 1
3108
3109     if s:tlist_app_name == "winmanager"
3110         " Let the winmanager edit the file
3111         call WinManagerFileEdit(a:filename, a:win_ctrl == 'newwin')
3112     else
3113
3114     if a:win_ctrl == 'newtab'
3115         " Create a new tab
3116         exe 'tabnew ' . escape(a:filename, ' ')
3117         " Open the taglist window in the new tab
3118         call s:Tlist_Window_Open()
3119     endif
3120
3121     if a:win_ctrl == 'checktab'
3122         " Check whether the file is present in any of the tabs.
3123         " If the file is present in the current tab, then use the
3124         " current tab.
3125         if bufwinnr(a:filename) != -1
3126             let file_present_in_tab = 1
3127             let i = tabpagenr()
3128         else
3129             let i = 1
3130             let bnum = bufnr(a:filename)
3131             let file_present_in_tab = 0
3132             while i <= tabpagenr('$')
3133                 if index(tabpagebuflist(i), bnum) != -1
3134                     let file_present_in_tab = 1
3135                     break
3136                 endif
3137                 let i += 1
3138             endwhile
3139         endif
3140
3141         if file_present_in_tab
3142             " Goto the tab containing the file
3143             exe 'tabnext ' . i
3144         else
3145             " Open a new tab
3146             exe 'tabnew ' . escape(a:filename, ' ')
3147
3148             " Open the taglist window
3149             call s:Tlist_Window_Open()
3150         endif
3151     endif
3152
3153     let winnum = -1
3154     if a:win_ctrl == 'prevwin'
3155         " Open the file in the previous window, if it is usable
3156         let cur_win = winnr()
3157         wincmd p
3158         if &buftype == '' && !&previewwindow
3159             exe "edit " . escape(a:filename, ' ')
3160             let winnum = winnr()
3161         else
3162             " Previous window is not usable
3163             exe cur_win . 'wincmd w'
3164         endif
3165     endif
3166
3167     " Goto the window containing the file.  If the window is not there, open a
3168     " new window
3169     if winnum == -1
3170         let winnum = bufwinnr(a:filename)
3171     endif
3172
3173     if winnum == -1
3174         " Locate the previously used window for opening a file
3175         let fwin_num = 0
3176         let first_usable_win = 0
3177
3178         let i = 1
3179         let bnum = winbufnr(i)
3180         while bnum != -1
3181             if getwinvar(i, 'tlist_file_window') == 'yes'
3182                 let fwin_num = i
3183                 break
3184             endif
3185             if first_usable_win == 0 &&
3186                         \ getbufvar(bnum, '&buftype') == '' &&
3187                         \ !getwinvar(i, '&previewwindow')
3188                 " First non-taglist, non-plugin and non-preview window
3189                 let first_usable_win = i
3190             endif
3191             let i = i + 1
3192             let bnum = winbufnr(i)
3193         endwhile
3194
3195         " If a previously used window is not found, then use the first
3196         " non-taglist window
3197         if fwin_num == 0
3198             let fwin_num = first_usable_win
3199         endif
3200
3201         if fwin_num != 0
3202             " Jump to the file window
3203             exe fwin_num . "wincmd w"
3204
3205             " If the user asked to jump to the tag in a new window, then split
3206             " the existing window into two.
3207             if a:win_ctrl == 'newwin'
3208                 split
3209             endif
3210             exe "edit " . escape(a:filename, ' ')
3211         else
3212             " Open a new window
3213             if g:Tlist_Use_Horiz_Window
3214                 exe 'leftabove split ' . escape(a:filename, ' ')
3215             else
3216                 if winbufnr(2) == -1
3217                     " Only the taglist window is present
3218                     if g:Tlist_Use_Right_Window
3219                         exe 'leftabove vertical split ' .
3220                                     \ escape(a:filename, ' ')
3221                     else
3222                         exe 'rightbelow vertical split ' .
3223                                     \ escape(a:filename, ' ')
3224                     endif
3225
3226                     " Go to the taglist window to change the window size to
3227                     " the user configured value
3228                     call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
3229                     if g:Tlist_Use_Horiz_Window
3230                         exe 'resize ' . g:Tlist_WinHeight
3231                     else
3232                         exe 'vertical resize ' . g:Tlist_WinWidth
3233                     endif
3234                     " Go back to the file window
3235                     call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
3236                 else
3237                     " A plugin or help window is also present
3238                     wincmd w
3239                     exe 'leftabove split ' . escape(a:filename, ' ')
3240                 endif
3241             endif
3242         endif
3243         " Mark the window, so that it can be reused.
3244         call s:Tlist_Window_Mark_File_Window()
3245     else
3246         if v:version >= 700
3247             " If the file is opened in more than one window, then check
3248             " whether the last accessed window has the selected file.
3249             " If it does, then use that window.
3250             let lastwin_bufnum = winbufnr(winnr('#'))
3251             if bufnr(a:filename) == lastwin_bufnum
3252                 let winnum = winnr('#')
3253             endif
3254         endif
3255         exe winnum . 'wincmd w'
3256
3257         " If the user asked to jump to the tag in a new window, then split the
3258         " existing window into two.
3259         if a:win_ctrl == 'newwin'
3260             split
3261         endif
3262     endif
3263     endif
3264
3265     " Jump to the tag
3266     if a:tagpat != ''
3267         " Add the current cursor position to the jump list, so that user can
3268         " jump back using the ' and ` marks.
3269         mark '
3270         silent call search(a:tagpat, 'w')
3271
3272         " Bring the line to the middle of the window
3273         normal! z.
3274
3275         " If the line is inside a fold, open the fold
3276         if foldclosed('.') != -1
3277             .foldopen
3278         endif
3279     endif
3280
3281     " If the user selects to preview the tag then jump back to the
3282     " taglist window
3283     if a:win_ctrl == 'preview'
3284         " Go back to the taglist window
3285         let winnum = bufwinnr(g:TagList_title)
3286         exe winnum . 'wincmd w'
3287     else
3288         " If the user has selected to close the taglist window, when a
3289         " tag is selected, close the taglist  window
3290         if g:Tlist_Close_On_Select
3291             call s:Tlist_Window_Goto_Window()
3292             close
3293
3294             " Go back to the window displaying the selected file
3295             let wnum = bufwinnr(a:filename)
3296             if wnum != -1 && wnum != winnr()
3297                 call s:Tlist_Exe_Cmd_No_Acmds(wnum . 'wincmd w')
3298             endif
3299         endif
3300     endif
3301
3302     let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
3303 endfunction
3304
3305 " Tlist_Window_Jump_To_Tag()
3306 " Jump to the location of the current tag
3307 " win_ctrl == useopen - Reuse the existing file window
3308 " win_ctrl == newwin - Open a new window
3309 " win_ctrl == preview - Preview the tag
3310 " win_ctrl == prevwin - Open in previous window
3311 " win_ctrl == newtab - Open in new tab
3312 function! s:Tlist_Window_Jump_To_Tag(win_ctrl)
3313     call s:Tlist_Log_Msg('Tlist_Window_Jump_To_Tag(' . a:win_ctrl . ')')
3314     " Do not process comment lines and empty lines
3315     let curline = getline('.')
3316     if curline =~ '^\s*$' || curline[0] == '"'
3317         return
3318     endif
3319
3320     " If inside a closed fold, then use the first line of the fold
3321     " and jump to the file.
3322     let lnum = foldclosed('.')
3323     if lnum == -1
3324         " Jump to the selected tag or file
3325         let lnum = line('.')
3326     else
3327         " Open the closed fold
3328         .foldopen!
3329     endif
3330
3331     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
3332     if fidx == -1
3333         return
3334     endif
3335
3336     " Get the tag output for the current tag
3337     let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum)
3338     if tidx != 0
3339         let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, tidx)
3340
3341         " Highlight the tagline
3342         call s:Tlist_Window_Highlight_Line()
3343     else
3344         " Selected a line which is not a tag name. Just edit the file
3345         let tagpat = ''
3346     endif
3347
3348     call s:Tlist_Window_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat)
3349 endfunction
3350
3351 " Tlist_Window_Show_Info()
3352 " Display information about the entry under the cursor
3353 function! s:Tlist_Window_Show_Info()
3354     call s:Tlist_Log_Msg('Tlist_Window_Show_Info()')
3355
3356     " Clear the previously displayed line
3357     echo
3358
3359     " Do not process comment lines and empty lines
3360     let curline = getline('.')
3361     if curline =~ '^\s*$' || curline[0] == '"'
3362         return
3363     endif
3364
3365     " If inside a fold, then don't display the prototype
3366     if foldclosed('.') != -1
3367         return
3368     endif
3369
3370     let lnum = line('.')
3371
3372     " Get the file index
3373     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
3374     if fidx == -1
3375         return
3376     endif
3377
3378     if lnum == s:tlist_{fidx}_start
3379         " Cursor is on a file name
3380         let fname = s:tlist_{fidx}_filename
3381         if strlen(fname) > 50
3382             let fname = fnamemodify(fname, ':t')
3383         endif
3384         echo fname . ', Filetype=' . s:tlist_{fidx}_filetype .
3385                     \  ', Tag count=' . s:tlist_{fidx}_tag_count
3386         return
3387     endif
3388
3389     " Get the tag output line for the current tag
3390     let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum)
3391     if tidx == 0
3392         " Cursor is on a tag type
3393         let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum)
3394         if ttype == ''
3395             return
3396         endif
3397
3398         let ttype_name = ''
3399
3400         let ftype = s:tlist_{fidx}_filetype
3401         let i = 1
3402         while i <= s:tlist_{ftype}_count
3403             if ttype == s:tlist_{ftype}_{i}_name
3404                 let ttype_name = s:tlist_{ftype}_{i}_fullname
3405                 break
3406             endif
3407             let i = i + 1
3408         endwhile
3409
3410         echo 'Tag type=' . ttype_name .
3411                     \ ', Tag count=' . s:tlist_{fidx}_{ttype}_count
3412         return
3413     endif
3414
3415     " Get the tag search pattern and display it
3416     echo s:Tlist_Get_Tag_Prototype(fidx, tidx)
3417 endfunction
3418
3419 " Tlist_Find_Nearest_Tag_Idx
3420 " Find the tag idx nearest to the supplied line number
3421 " Returns -1, if a tag couldn't be found for the specified line number
3422 function! s:Tlist_Find_Nearest_Tag_Idx(fidx, linenum)
3423     let sort_type = s:tlist_{a:fidx}_sort_type
3424
3425     let left = 1
3426     let right = s:tlist_{a:fidx}_tag_count
3427
3428     if sort_type == 'order'
3429         " Tags sorted by order, use a binary search.
3430         " The idea behind this function is taken from the ctags.vim script (by
3431         " Alexey Marinichev) available at the Vim online website.
3432
3433         " If the current line is the less than the first tag, then no need to
3434         " search
3435         let first_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, 1)
3436
3437         if a:linenum < first_lnum
3438             return -1
3439         endif
3440
3441         while left < right
3442             let middle = (right + left + 1) / 2
3443             let middle_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, middle)
3444
3445             if middle_lnum == a:linenum
3446                 let left = middle
3447                 break
3448             endif
3449
3450             if middle_lnum > a:linenum
3451                 let right = middle - 1
3452             else
3453                 let left = middle
3454             endif
3455         endwhile
3456     else
3457         " Tags sorted by name, use a linear search. (contributed by Dave
3458         " Eggum).
3459         " Look for a tag with a line number less than or equal to the supplied
3460         " line number. If multiple tags are found, then use the tag with the
3461         " line number closest to the supplied line number. IOW, use the tag
3462         " with the highest line number.
3463         let closest_lnum = 0
3464         let final_left = 0
3465         while left <= right
3466             let lnum = s:Tlist_Get_Tag_Linenum(a:fidx, left)
3467
3468             if lnum < a:linenum && lnum > closest_lnum
3469                 let closest_lnum = lnum
3470                 let final_left = left
3471             elseif lnum == a:linenum
3472                 let closest_lnum = lnum
3473                 let final_left = left
3474                 break
3475             else
3476                 let left = left + 1
3477             endif
3478         endwhile
3479         if closest_lnum == 0
3480             return -1
3481         endif
3482         if left >= right
3483             let left = final_left
3484         endif
3485     endif
3486
3487     return left
3488 endfunction
3489
3490 " Tlist_Window_Highlight_Tag()
3491 " Highlight the current tag
3492 " cntx == 1, Called by the taglist plugin itself
3493 " cntx == 2, Forced by the user through the TlistHighlightTag command
3494 " center = 1, move the tag line to the center of the taglist window
3495 function! s:Tlist_Window_Highlight_Tag(filename, cur_lnum, cntx, center)
3496     " Highlight the current tag only if the user configured the
3497     " taglist plugin to do so or if the user explictly invoked the
3498     " command to highlight the current tag.
3499     if !g:Tlist_Auto_Highlight_Tag && a:cntx == 1
3500         return
3501     endif
3502
3503     if a:filename == ''
3504         return
3505     endif
3506
3507     " Make sure the taglist window is present
3508     let winnum = bufwinnr(g:TagList_title)
3509     if winnum == -1
3510         call s:Tlist_Warning_Msg('Error: Taglist window is not open')
3511         return
3512     endif
3513
3514     let fidx = s:Tlist_Get_File_Index(a:filename)
3515     if fidx == -1
3516         return
3517     endif
3518
3519     " If the file is currently not displayed in the taglist window, then retrn
3520     if !s:tlist_{fidx}_visible
3521         return
3522     endif
3523
3524     " If there are no tags for this file, then no need to proceed further
3525     if s:tlist_{fidx}_tag_count == 0
3526         return
3527     endif
3528
3529     " Ignore all autocommands
3530     let old_ei = &eventignore
3531     set eventignore=all
3532
3533     " Save the original window number
3534     let org_winnr = winnr()
3535
3536     if org_winnr == winnum
3537         let in_taglist_window = 1
3538     else
3539         let in_taglist_window = 0
3540     endif
3541
3542     " Go to the taglist window
3543     if !in_taglist_window
3544         exe winnum . 'wincmd w'
3545     endif
3546
3547     " Clear previously selected name
3548     match none
3549
3550     let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, a:cur_lnum)
3551     if tidx == -1
3552         " Make sure the current tag line is visible in the taglist window.
3553         " Calling the winline() function makes the line visible.  Don't know
3554         " of a better way to achieve this.
3555         let lnum = line('.')
3556
3557         if lnum < s:tlist_{fidx}_start || lnum > s:tlist_{fidx}_end
3558             " Move the cursor to the beginning of the file
3559             exe s:tlist_{fidx}_start
3560         endif
3561
3562         if foldclosed('.') != -1
3563             .foldopen
3564         endif
3565
3566         call winline()
3567
3568         if !in_taglist_window
3569             exe org_winnr . 'wincmd w'
3570         endif
3571
3572         " Restore the autocommands
3573         let &eventignore = old_ei
3574         return
3575     endif
3576
3577     " Extract the tag type
3578     let ttype = s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx)
3579
3580     " Compute the line number
3581     " Start of file + Start of tag type + offset
3582     let lnum = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset +
3583                 \ s:tlist_{fidx}_{tidx}_ttype_idx
3584
3585     " Goto the line containing the tag
3586     exe lnum
3587
3588     " Open the fold
3589     if foldclosed('.') != -1
3590         .foldopen
3591     endif
3592
3593     if a:center
3594         " Move the tag line to the center of the taglist window
3595         normal! z.
3596     else
3597         " Make sure the current tag line is visible in the taglist window.
3598         " Calling the winline() function makes the line visible.  Don't know
3599         " of a better way to achieve this.
3600         call winline()
3601     endif
3602
3603     " Highlight the tag name
3604     call s:Tlist_Window_Highlight_Line()
3605
3606     " Go back to the original window
3607     if !in_taglist_window
3608         exe org_winnr . 'wincmd w'
3609     endif
3610
3611     " Restore the autocommands
3612     let &eventignore = old_ei
3613     return
3614 endfunction
3615
3616 " Tlist_Get_Tag_Prototype_By_Line
3617 " Get the prototype for the tag on or before the specified line number in the
3618 " current buffer
3619 function! Tlist_Get_Tag_Prototype_By_Line(...)
3620     if a:0 == 0
3621         " Arguments are not supplied. Use the current buffer name
3622         " and line number
3623         let filename = bufname('%')
3624         let linenr = line('.')
3625     elseif a:0 == 2
3626         " Filename and line number are specified
3627         let filename = a:1
3628         let linenr = a:2
3629         if linenr !~ '\d\+'
3630             " Invalid line number
3631             return ""
3632         endif
3633     else
3634         " Sufficient arguments are not supplied
3635         let msg =  'Usage: Tlist_Get_Tag_Prototype_By_Line <filename> ' .
3636                                 \ '<line_number>'
3637         call s:Tlist_Warning_Msg(msg)
3638         return ""
3639     endif
3640
3641     " Expand the file to a fully qualified name
3642     let filename = fnamemodify(filename, ':p')
3643     if filename == ''
3644         return ""
3645     endif
3646
3647     let fidx = s:Tlist_Get_File_Index(filename)
3648     if fidx == -1
3649         return ""
3650     endif
3651
3652     " If there are no tags for this file, then no need to proceed further
3653     if s:tlist_{fidx}_tag_count == 0
3654         return ""
3655     endif
3656
3657     " Get the tag text using the line number
3658     let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr)
3659     if tidx == -1
3660         return ""
3661     endif
3662
3663     return s:Tlist_Get_Tag_Prototype(fidx, tidx)
3664 endfunction
3665
3666 " Tlist_Get_Tagname_By_Line
3667 " Get the tag name on or before the specified line number in the
3668 " current buffer
3669 function! Tlist_Get_Tagname_By_Line(...)
3670     if a:0 == 0
3671         " Arguments are not supplied. Use the current buffer name
3672         " and line number
3673         let filename = bufname('%')
3674         let linenr = line('.')
3675     elseif a:0 == 2
3676         " Filename and line number are specified
3677         let filename = a:1
3678         let linenr = a:2
3679         if linenr !~ '\d\+'
3680             " Invalid line number
3681             return ""
3682         endif
3683     else
3684         " Sufficient arguments are not supplied
3685         let msg =  'Usage: Tlist_Get_Tagname_By_Line <filename> <line_number>'
3686         call s:Tlist_Warning_Msg(msg)
3687         return ""
3688     endif
3689
3690     " Make sure the current file has a name
3691     let filename = fnamemodify(filename, ':p')
3692     if filename == ''
3693         return ""
3694     endif
3695
3696     let fidx = s:Tlist_Get_File_Index(filename)
3697     if fidx == -1
3698         return ""
3699     endif
3700
3701     " If there are no tags for this file, then no need to proceed further
3702     if s:tlist_{fidx}_tag_count == 0
3703         return ""
3704     endif
3705
3706     " Get the tag name using the line number
3707     let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr)
3708     if tidx == -1
3709         return ""
3710     endif
3711
3712     return s:tlist_{fidx}_{tidx}_tag_name
3713 endfunction
3714
3715 " Tlist_Window_Move_To_File
3716 " Move the cursor to the beginning of the current file or the next file
3717 " or the previous file in the taglist window
3718 " dir == -1, move to start of current or previous function
3719 " dir == 1, move to start of next function
3720 function! s:Tlist_Window_Move_To_File(dir)
3721     if foldlevel('.') == 0
3722         " Cursor is on a non-folded line (it is not in any of the files)
3723         " Move it to a folded line
3724         if a:dir == -1
3725             normal! zk
3726         else
3727             " While moving down to the start of the next fold,
3728             " no need to do go to the start of the next file.
3729             normal! zj
3730             return
3731         endif
3732     endif
3733
3734     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
3735     if fidx == -1
3736         return
3737     endif
3738
3739     let cur_lnum = line('.')
3740
3741     if a:dir == -1
3742         if cur_lnum > s:tlist_{fidx}_start
3743             " Move to the beginning of the current file
3744             exe s:tlist_{fidx}_start
3745             return
3746         endif
3747
3748         if fidx != 0
3749             " Move to the beginning of the previous file
3750             let fidx = fidx - 1
3751         else
3752             " Cursor is at the first file, wrap around to the last file
3753             let fidx = s:tlist_file_count - 1
3754         endif
3755
3756         exe s:tlist_{fidx}_start
3757         return
3758     else
3759         " Move to the beginning of the next file
3760         let fidx = fidx + 1
3761
3762         if fidx >= s:tlist_file_count
3763             " Cursor is at the last file, wrap around to the first file
3764             let fidx = 0
3765         endif
3766
3767         if s:tlist_{fidx}_start != 0
3768             exe s:tlist_{fidx}_start
3769         endif
3770         return
3771     endif
3772 endfunction
3773
3774 " Tlist_Session_Load
3775 " Load a taglist session (information about all the displayed files
3776 " and the tags) from the specified file
3777 function! s:Tlist_Session_Load(...)
3778     if a:0 == 0 || a:1 == ''
3779         call s:Tlist_Warning_Msg('Usage: TlistSessionLoad <filename>')
3780         return
3781     endif
3782
3783     let sessionfile = a:1
3784
3785     if !filereadable(sessionfile)
3786         let msg = 'Taglist: Error - Unable to open file ' . sessionfile
3787         call s:Tlist_Warning_Msg(msg)
3788         return
3789     endif
3790
3791     " Mark the current window as the file window
3792     call s:Tlist_Window_Mark_File_Window()
3793
3794     " Source the session file
3795     exe 'source ' . sessionfile
3796
3797     let new_file_count = g:tlist_file_count
3798     unlet! g:tlist_file_count
3799
3800     let i = 0
3801     while i < new_file_count
3802         let ftype = g:tlist_{i}_filetype
3803         unlet! g:tlist_{i}_filetype
3804
3805         if !exists('s:tlist_' . ftype . '_count')
3806             if s:Tlist_FileType_Init(ftype) == 0
3807                 let i = i + 1
3808                 continue
3809             endif
3810         endif
3811
3812         let fname = g:tlist_{i}_filename
3813         unlet! g:tlist_{i}_filename
3814
3815         let fidx = s:Tlist_Get_File_Index(fname)
3816         if fidx != -1
3817             let s:tlist_{fidx}_visible = 0
3818             let i = i + 1
3819             continue
3820         else
3821             " As we are loading the tags from the session file, if this
3822             " file was previously deleted by the user, now we need to
3823             " add it back. So remove the file from the deleted list.
3824             call s:Tlist_Update_Remove_List(fname, 0)
3825         endif
3826
3827         let fidx = s:Tlist_Init_File(fname, ftype)
3828
3829         let s:tlist_{fidx}_filename = fname
3830
3831         let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type
3832         unlet! g:tlist_{i}_sort_type
3833
3834         let s:tlist_{fidx}_filetype = ftype
3835         let s:tlist_{fidx}_mtime = getftime(fname)
3836
3837         let s:tlist_{fidx}_start = 0
3838         let s:tlist_{fidx}_end = 0
3839
3840         let s:tlist_{fidx}_valid = 1
3841
3842         let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count
3843         unlet! g:tlist_{i}_tag_count
3844
3845         let j = 1
3846         while j <= s:tlist_{fidx}_tag_count
3847             let s:tlist_{fidx}_{j}_tag = g:tlist_{i}_{j}_tag
3848             let s:tlist_{fidx}_{j}_tag_name = g:tlist_{i}_{j}_tag_name
3849             let s:tlist_{fidx}_{j}_ttype_idx = g:tlist_{i}_{j}_ttype_idx
3850             unlet! g:tlist_{i}_{j}_tag
3851             unlet! g:tlist_{i}_{j}_tag_name
3852             unlet! g:tlist_{i}_{j}_ttype_idx
3853             let j = j + 1
3854         endwhile
3855
3856         let j = 1
3857         while j <= s:tlist_{ftype}_count
3858             let ttype = s:tlist_{ftype}_{j}_name
3859
3860             if exists('g:tlist_' . i . '_' . ttype)
3861                 let s:tlist_{fidx}_{ttype} = g:tlist_{i}_{ttype}
3862                 unlet! g:tlist_{i}_{ttype}
3863                 let s:tlist_{fidx}_{ttype}_offset = 0
3864                 let s:tlist_{fidx}_{ttype}_count = g:tlist_{i}_{ttype}_count
3865                 unlet! g:tlist_{i}_{ttype}_count
3866
3867                 let k = 1
3868                 while k <= s:tlist_{fidx}_{ttype}_count
3869                     let s:tlist_{fidx}_{ttype}_{k} = g:tlist_{i}_{ttype}_{k}
3870                     unlet! g:tlist_{i}_{ttype}_{k}
3871                     let k = k + 1
3872                 endwhile
3873             else
3874                 let s:tlist_{fidx}_{ttype} = ''
3875                 let s:tlist_{fidx}_{ttype}_offset = 0
3876                 let s:tlist_{fidx}_{ttype}_count = 0
3877             endif
3878
3879             let j = j + 1
3880         endwhile
3881
3882         let i = i + 1
3883     endwhile
3884
3885     " If the taglist window is open, then update it
3886     let winnum = bufwinnr(g:TagList_title)
3887     if winnum != -1
3888         let save_winnr = winnr()
3889
3890         " Goto the taglist window
3891         call s:Tlist_Window_Goto_Window()
3892
3893         " Refresh the taglist window
3894         call s:Tlist_Window_Refresh()
3895
3896         " Go back to the original window
3897         if save_winnr != winnr()
3898             call s:Tlist_Exe_Cmd_No_Acmds('wincmd p')
3899         endif
3900     endif
3901 endfunction
3902
3903 " Tlist_Session_Save
3904 " Save a taglist session (information about all the displayed files
3905 " and the tags) into the specified file
3906 function! s:Tlist_Session_Save(...)
3907     if a:0 == 0 || a:1 == ''
3908         call s:Tlist_Warning_Msg('Usage: TlistSessionSave <filename>')
3909         return
3910     endif
3911
3912     let sessionfile = a:1
3913
3914     if s:tlist_file_count == 0
3915         " There is nothing to save
3916         call s:Tlist_Warning_Msg('Warning: Taglist is empty. Nothing to save.')
3917         return
3918     endif
3919
3920     if filereadable(sessionfile)
3921         let ans = input('Do you want to overwrite ' . sessionfile . ' (Y/N)?')
3922         if ans !=? 'y'
3923             return
3924         endif
3925
3926         echo "\n"
3927     endif
3928
3929     let old_verbose = &verbose
3930     set verbose&vim
3931
3932     exe 'redir! > ' . sessionfile
3933
3934     silent! echo '" Taglist session file. This file is auto-generated.'
3935     silent! echo '" File information'
3936     silent! echo 'let tlist_file_count = ' . s:tlist_file_count
3937
3938     let i = 0
3939
3940     while i < s:tlist_file_count
3941         " Store information about the file
3942         silent! echo 'let tlist_' . i . "_filename = '" .
3943                                             \ s:tlist_{i}_filename . "'"
3944         silent! echo 'let tlist_' . i . '_sort_type = "' .
3945                                                 \ s:tlist_{i}_sort_type . '"'
3946         silent! echo 'let tlist_' . i . '_filetype = "' .
3947                                             \ s:tlist_{i}_filetype . '"'
3948         silent! echo 'let tlist_' . i . '_tag_count = ' .
3949                                                         \ s:tlist_{i}_tag_count
3950         " Store information about all the tags
3951         let j = 1
3952         while j <= s:tlist_{i}_tag_count
3953             let txt = escape(s:tlist_{i}_{j}_tag, '"\\')
3954             silent! echo 'let tlist_' . i . '_' . j . '_tag = "' . txt . '"'
3955             silent! echo 'let tlist_' . i . '_' . j . '_tag_name = "' .
3956                         \ s:tlist_{i}_{j}_tag_name . '"'
3957             silent! echo 'let tlist_' . i . '_' . j . '_ttype_idx' . ' = ' .
3958                         \ s:tlist_{i}_{j}_ttype_idx
3959             let j = j + 1
3960         endwhile
3961
3962         " Store information about all the tags grouped by their type
3963         let ftype = s:tlist_{i}_filetype
3964         let j = 1
3965         while j <= s:tlist_{ftype}_count
3966             let ttype = s:tlist_{ftype}_{j}_name
3967             if s:tlist_{i}_{ttype}_count != 0
3968                 let txt = escape(s:tlist_{i}_{ttype}, '"\')
3969                 let txt = substitute(txt, "\n", "\\\\n", 'g')
3970                 silent! echo 'let tlist_' . i . '_' . ttype . ' = "' .
3971                                                 \ txt . '"'
3972                 silent! echo 'let tlist_' . i . '_' . ttype . '_count = ' .
3973                                                      \ s:tlist_{i}_{ttype}_count
3974                 let k = 1
3975                 while k <= s:tlist_{i}_{ttype}_count
3976                     silent! echo 'let tlist_' . i . '_' . ttype . '_' . k .
3977                                 \ ' = ' . s:tlist_{i}_{ttype}_{k}
3978                     let k = k + 1
3979                 endwhile
3980             endif
3981             let j = j + 1
3982         endwhile
3983
3984         silent! echo
3985
3986         let i = i + 1
3987     endwhile
3988
3989     redir END
3990
3991     let &verbose = old_verbose
3992 endfunction
3993
3994 " Tlist_Buffer_Removed
3995 " A buffer is removed from the Vim buffer list. Remove the tags defined
3996 " for that file
3997 function! s:Tlist_Buffer_Removed(filename)
3998     call s:Tlist_Log_Msg('Tlist_Buffer_Removed (' . a:filename .  ')')
3999
4000     " Make sure a valid filename is supplied
4001     if a:filename == ''
4002         return
4003     endif
4004
4005     " Get tag list index of the specified file
4006     let fidx = s:Tlist_Get_File_Index(a:filename)
4007     if fidx == -1
4008         " File not present in the taglist
4009         return
4010     endif
4011
4012     " Remove the file from the list
4013     call s:Tlist_Remove_File(fidx, 0)
4014 endfunction
4015
4016 " When a buffer is deleted, remove the file from the taglist
4017 autocmd BufDelete * silent call s:Tlist_Buffer_Removed(expand('<afile>:p'))
4018
4019 " Tlist_Window_Open_File_Fold
4020 " Open the fold for the specified file and close the fold for all the
4021 " other files
4022 function! s:Tlist_Window_Open_File_Fold(acmd_bufnr)
4023     call s:Tlist_Log_Msg('Tlist_Window_Open_File_Fold (' . a:acmd_bufnr . ')')
4024
4025     " Make sure the taglist window is present
4026     let winnum = bufwinnr(g:TagList_title)
4027     if winnum == -1
4028         call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open')
4029         return
4030     endif
4031
4032     " Save the original window number
4033     let org_winnr = winnr()
4034     if org_winnr == winnum
4035         let in_taglist_window = 1
4036     else
4037         let in_taglist_window = 0
4038     endif
4039
4040     if in_taglist_window
4041         " When entering the taglist window, no need to update the folds
4042         return
4043     endif
4044
4045     " Go to the taglist window
4046     if !in_taglist_window
4047         call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w')
4048     endif
4049
4050     " Close all the folds
4051     silent! %foldclose
4052
4053     " Get tag list index of the specified file
4054     let fname = fnamemodify(bufname(a:acmd_bufnr + 0), ':p')
4055     if filereadable(fname)
4056         let fidx = s:Tlist_Get_File_Index(fname)
4057         if fidx != -1
4058             " Open the fold for the file
4059             exe "silent! " . s:tlist_{fidx}_start . "," .
4060                         \ s:tlist_{fidx}_end . "foldopen"
4061         endif
4062     endif
4063
4064     " Go back to the original window
4065     if !in_taglist_window
4066         call s:Tlist_Exe_Cmd_No_Acmds(org_winnr . 'wincmd w')
4067     endif
4068 endfunction
4069
4070 " Tlist_Window_Check_Auto_Open
4071 " Open the taglist window automatically on Vim startup.
4072 " Open the window only when files present in any of the Vim windows support
4073 " tags.
4074 function! s:Tlist_Window_Check_Auto_Open()
4075     let open_window = 0
4076
4077     let i = 1
4078     let buf_num = winbufnr(i)
4079     while buf_num != -1
4080         let filename = fnamemodify(bufname(buf_num), ':p')
4081         let ft = s:Tlist_Get_Buffer_Filetype(buf_num)
4082         if !s:Tlist_Skip_File(filename, ft)
4083             let open_window = 1
4084             break
4085         endif
4086         let i = i + 1
4087         let buf_num = winbufnr(i)
4088     endwhile
4089
4090     if open_window
4091         call s:Tlist_Window_Toggle()
4092     endif
4093 endfunction
4094
4095 " Tlist_Refresh_Folds
4096 " Remove and create the folds for all the files displayed in the taglist
4097 " window. Used after entering a tab. If this is not done, then the folds
4098 " are not properly created for taglist windows displayed in multiple tabs.
4099 function! s:Tlist_Refresh_Folds()
4100     let winnum = bufwinnr(g:TagList_title)
4101     if winnum == -1
4102         return
4103     endif
4104
4105     let save_wnum = winnr()
4106     exe winnum . 'wincmd w'
4107
4108     " First remove all the existing folds
4109     normal! zE
4110
4111     " Create the folds for each in the tag list
4112     let fidx = 0
4113     while fidx < s:tlist_file_count
4114         let ftype = s:tlist_{fidx}_filetype
4115
4116         " Create the folds for each tag type in a file
4117         let j = 1
4118         while j <= s:tlist_{ftype}_count
4119             let ttype = s:tlist_{ftype}_{j}_name
4120             if s:tlist_{fidx}_{ttype}_count
4121                 let s = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset
4122                 let e = s + s:tlist_{fidx}_{ttype}_count
4123                 exe s . ',' . e . 'fold'
4124             endif
4125             let j = j + 1
4126         endwhile
4127
4128         exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold'
4129         exe 'silent! ' . s:tlist_{fidx}_start . ',' .
4130                     \ s:tlist_{fidx}_end . 'foldopen!'
4131         let fidx = fidx + 1
4132     endwhile
4133
4134     exe save_wnum . 'wincmd w'
4135 endfunction
4136
4137 function! s:Tlist_Menu_Add_Base_Menu()
4138     call s:Tlist_Log_Msg('Adding the base menu')
4139
4140     " Add the menu
4141     anoremenu <silent> T&ags.Refresh\ menu :call <SID>Tlist_Menu_Refresh()<CR>
4142     anoremenu <silent> T&ags.Sort\ menu\ by.Name
4143                     \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR>
4144     anoremenu <silent> T&ags.Sort\ menu\ by.Order
4145                     \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR>
4146     anoremenu T&ags.-SEP1-           :
4147
4148     if &mousemodel =~ 'popup'
4149         anoremenu <silent> PopUp.T&ags.Refresh\ menu
4150                     \ :call <SID>Tlist_Menu_Refresh()<CR>
4151         anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Name
4152                   \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR>
4153         anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Order
4154                   \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR>
4155         anoremenu PopUp.T&ags.-SEP1-           :
4156     endif
4157 endfunction
4158
4159 let s:menu_char_prefix =
4160             \ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
4161
4162 " Tlist_Menu_Get_Tag_Type_Cmd
4163 " Get the menu command for the specified tag type
4164 " fidx - File type index
4165 " ftype - File Type
4166 " add_ttype_name - To add or not to add the tag type name to the menu entries
4167 " ttype_idx - Tag type index
4168 function! s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, ttype_idx)
4169     " Curly brace variable name optimization
4170     let ftype_ttype_idx = a:ftype . '_' . a:ttype_idx
4171
4172     let ttype = s:tlist_{ftype_ttype_idx}_name
4173     if a:add_ttype_name
4174         " If the tag type name contains space characters, escape it. This
4175         " will be used to create the menu entries.
4176         let ttype_fullname = escape(s:tlist_{ftype_ttype_idx}_fullname, ' ')
4177     endif
4178
4179     " Curly brace variable name optimization
4180     let fidx_ttype = a:fidx . '_' . ttype
4181
4182     " Number of tag entries for this tag type
4183     let tcnt = s:tlist_{fidx_ttype}_count
4184     if tcnt == 0 " No entries for this tag type
4185         return ''
4186     endif
4187
4188     let mcmd = ''
4189
4190     " Create the menu items for the tags.
4191     " Depending on the number of tags of this type, split the menu into
4192     " multiple sub-menus, if needed.
4193     if tcnt > g:Tlist_Max_Submenu_Items
4194         let j = 1
4195         while j <= tcnt
4196             let final_index = j + g:Tlist_Max_Submenu_Items - 1
4197             if final_index > tcnt
4198                 let final_index = tcnt
4199             endif
4200
4201             " Extract the first and last tag name and form the
4202             " sub-menu name
4203             let tidx = s:tlist_{fidx_ttype}_{j}
4204             let first_tag = s:tlist_{a:fidx}_{tidx}_tag_name
4205
4206             let tidx = s:tlist_{fidx_ttype}_{final_index}
4207             let last_tag = s:tlist_{a:fidx}_{tidx}_tag_name
4208
4209             " Truncate the names, if they are greater than the
4210             " max length
4211             let first_tag = strpart(first_tag, 0, g:Tlist_Max_Tag_Length)
4212             let last_tag = strpart(last_tag, 0, g:Tlist_Max_Tag_Length)
4213
4214             " Form the menu command prefix
4215             let m_prefix = 'anoremenu <silent> T\&ags.'
4216             if a:add_ttype_name
4217                 let m_prefix = m_prefix . ttype_fullname . '.'
4218             endif
4219             let m_prefix = m_prefix . first_tag . '\.\.\.' . last_tag . '.'
4220
4221             " Character prefix used to number the menu items (hotkey)
4222             let m_prefix_idx = 0
4223
4224             while j <= final_index
4225                 let tidx = s:tlist_{fidx_ttype}_{j}
4226
4227                 let tname = s:tlist_{a:fidx}_{tidx}_tag_name
4228
4229                 let mcmd = mcmd . m_prefix . '\&' .
4230                             \ s:menu_char_prefix[m_prefix_idx] . '\.' .
4231                             \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' .
4232                             \ tidx . ')<CR>|'
4233
4234                 let m_prefix_idx = m_prefix_idx + 1
4235                 let j = j + 1
4236             endwhile
4237         endwhile
4238     else
4239         " Character prefix used to number the menu items (hotkey)
4240         let m_prefix_idx = 0
4241
4242         let m_prefix = 'anoremenu <silent> T\&ags.'
4243         if a:add_ttype_name
4244             let m_prefix = m_prefix . ttype_fullname . '.'
4245         endif
4246         let j = 1
4247         while j <= tcnt
4248             let tidx = s:tlist_{fidx_ttype}_{j}
4249
4250             let tname = s:tlist_{a:fidx}_{tidx}_tag_name
4251
4252             let mcmd = mcmd . m_prefix . '\&' .
4253                         \ s:menu_char_prefix[m_prefix_idx] . '\.' .
4254                         \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . tidx
4255                         \ . ')<CR>|'
4256
4257             let m_prefix_idx = m_prefix_idx + 1
4258             let j = j + 1
4259         endwhile
4260     endif
4261
4262     return mcmd
4263 endfunction
4264
4265 " Update the taglist menu with the tags for the specified file
4266 function! s:Tlist_Menu_File_Refresh(fidx)
4267     call s:Tlist_Log_Msg('Refreshing the tag menu for ' . s:tlist_{a:fidx}_filename)
4268     " The 'B' flag is needed in the 'cpoptions' option
4269     let old_cpoptions = &cpoptions
4270     set cpoptions&vim
4271
4272     exe s:tlist_{a:fidx}_menu_cmd
4273
4274     " Update the popup menu (if enabled)
4275     if &mousemodel =~ 'popup'
4276         let cmd = substitute(s:tlist_{a:fidx}_menu_cmd, ' T\\&ags\.',
4277                                         \ ' PopUp.T\\\&ags.', "g")
4278         exe cmd
4279     endif
4280
4281     " The taglist menu is not empty now
4282     let s:tlist_menu_empty = 0
4283
4284     " Restore the 'cpoptions' settings
4285     let &cpoptions = old_cpoptions
4286 endfunction
4287
4288 " Tlist_Menu_Update_File
4289 " Add the taglist menu
4290 function! s:Tlist_Menu_Update_File(clear_menu)
4291     if !has('gui_running')
4292         " Not running in GUI mode
4293         return
4294     endif
4295
4296     call s:Tlist_Log_Msg('Updating the tag menu, clear_menu = ' . a:clear_menu)
4297
4298     " Remove the tags menu
4299     if a:clear_menu
4300         call s:Tlist_Menu_Remove_File()
4301
4302     endif
4303
4304     " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
4305     if &buftype != ''
4306         return
4307     endif
4308
4309     let filename = fnamemodify(bufname('%'), ':p')
4310     let ftype = s:Tlist_Get_Buffer_Filetype('%')
4311
4312     " If the file doesn't support tag listing, skip it
4313     if s:Tlist_Skip_File(filename, ftype)
4314         return
4315     endif
4316
4317     let fidx = s:Tlist_Get_File_Index(filename)
4318     if fidx == -1 || !s:tlist_{fidx}_valid
4319         " Check whether this file is removed based on user request
4320         " If it is, then don't display the tags for this file
4321         if s:Tlist_User_Removed_File(filename)
4322             return
4323         endif
4324
4325         " Process the tags for the file
4326         let fidx = s:Tlist_Process_File(filename, ftype)
4327         if fidx == -1
4328             return
4329         endif
4330     endif
4331
4332     let fname = escape(fnamemodify(bufname('%'), ':t'), '.')
4333     if fname != ''
4334         exe 'anoremenu T&ags.' .  fname . ' <Nop>'
4335         anoremenu T&ags.-SEP2-           :
4336     endif
4337
4338     if !s:tlist_{fidx}_tag_count
4339         return
4340     endif
4341
4342     if s:tlist_{fidx}_menu_cmd != ''
4343         " Update the menu with the cached command
4344         call s:Tlist_Menu_File_Refresh(fidx)
4345
4346         return
4347     endif
4348
4349     " We are going to add entries to the tags menu, so the menu won't be
4350     " empty
4351     let s:tlist_menu_empty = 0
4352
4353     let cmd = ''
4354
4355     " Determine whether the tag type name needs to be added to the menu
4356     " If more than one tag type is present in the taglisting for a file,
4357     " then the tag type name needs to be present
4358     let add_ttype_name = -1
4359     let i = 1
4360     while i <= s:tlist_{ftype}_count && add_ttype_name < 1
4361         let ttype = s:tlist_{ftype}_{i}_name
4362         if s:tlist_{fidx}_{ttype}_count
4363             let add_ttype_name = add_ttype_name + 1
4364         endif
4365         let i = i + 1
4366     endwhile
4367
4368     " Process the tags by the tag type and get the menu command
4369     let i = 1
4370     while i <= s:tlist_{ftype}_count
4371         let mcmd = s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, i)
4372         if mcmd != ''
4373             let cmd = cmd . mcmd
4374         endif
4375
4376         let i = i + 1
4377     endwhile
4378
4379     " Cache the menu command for reuse
4380     let s:tlist_{fidx}_menu_cmd = cmd
4381
4382     " Update the menu
4383     call s:Tlist_Menu_File_Refresh(fidx)
4384 endfunction
4385
4386 " Tlist_Menu_Remove_File
4387 " Remove the tags displayed in the tags menu
4388 function! s:Tlist_Menu_Remove_File()
4389     if !has('gui_running') || s:tlist_menu_empty
4390         return
4391     endif
4392
4393     call s:Tlist_Log_Msg('Removing the tags menu for a file')
4394
4395     " Cleanup the Tags menu
4396     silent! unmenu T&ags
4397     if &mousemodel =~ 'popup'
4398         silent! unmenu PopUp.T&ags
4399     endif
4400
4401     " Add a dummy menu item to retain teared off menu
4402     noremenu T&ags.Dummy l
4403
4404     silent! unmenu! T&ags
4405     if &mousemodel =~ 'popup'
4406         silent! unmenu! PopUp.T&ags
4407     endif
4408
4409     call s:Tlist_Menu_Add_Base_Menu()
4410
4411     " Remove the dummy menu item
4412     unmenu T&ags.Dummy
4413
4414     let s:tlist_menu_empty = 1
4415 endfunction
4416
4417 " Tlist_Menu_Refresh
4418 " Refresh the taglist menu
4419 function! s:Tlist_Menu_Refresh()
4420     call s:Tlist_Log_Msg('Refreshing the tags menu')
4421     let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
4422     if fidx != -1
4423         " Invalidate the cached menu command
4424         let s:tlist_{fidx}_menu_cmd = ''
4425     endif
4426
4427     " Update the taglist, menu and window
4428     call s:Tlist_Update_Current_File()
4429 endfunction
4430
4431 " Tlist_Menu_Jump_To_Tag
4432 " Jump to the selected tag
4433 function! s:Tlist_Menu_Jump_To_Tag(tidx)
4434     let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p'))
4435     if fidx == -1
4436         return
4437     endif
4438
4439     let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, a:tidx)
4440     if tagpat == ''
4441         return
4442     endif
4443
4444     " Add the current cursor position to the jump list, so that user can
4445     " jump back using the ' and ` marks.
4446     mark '
4447
4448     silent call search(tagpat, 'w')
4449
4450     " Bring the line to the middle of the window
4451     normal! z.
4452
4453     " If the line is inside a fold, open the fold
4454     if foldclosed('.') != -1
4455         .foldopen
4456     endif
4457 endfunction
4458
4459 " Tlist_Menu_Init
4460 " Initialize the taglist menu
4461 function! s:Tlist_Menu_Init()
4462     call s:Tlist_Menu_Add_Base_Menu()
4463
4464     " Automatically add the tags defined in the current file to the menu
4465     augroup TagListMenuCmds
4466         autocmd!
4467
4468         if !g:Tlist_Process_File_Always
4469             autocmd BufEnter * call s:Tlist_Refresh()
4470         endif
4471         autocmd BufLeave * call s:Tlist_Menu_Remove_File()
4472     augroup end
4473
4474     call s:Tlist_Menu_Update_File(0)
4475 endfunction
4476
4477 " Tlist_Vim_Session_Load
4478 " Initialize the taglist window/buffer, which is created when loading
4479 " a Vim session file.
4480 function! s:Tlist_Vim_Session_Load()
4481     call s:Tlist_Log_Msg('Tlist_Vim_Session_Load')
4482
4483     " Initialize the taglist window
4484     call s:Tlist_Window_Init()
4485
4486     " Refresh the taglist window
4487     call s:Tlist_Window_Refresh()
4488 endfunction
4489
4490 " Tlist_Set_App
4491 " Set the name of the external plugin/application to which taglist
4492 " belongs.
4493 " Taglist plugin is part of another plugin like cream or winmanager.
4494 function! Tlist_Set_App(name)
4495     if a:name == ""
4496         return
4497     endif
4498
4499     let s:tlist_app_name = a:name
4500 endfunction
4501
4502 " Winmanager integration
4503
4504 " Initialization required for integration with winmanager
4505 function! TagList_Start()
4506     " If current buffer is not taglist buffer, then don't proceed
4507     if bufname('%') != '__Tag_List__'
4508         return
4509     endif
4510
4511     call Tlist_Set_App('winmanager')
4512
4513     " Get the current filename from the winmanager plugin
4514     let bufnum = WinManagerGetLastEditedFile()
4515     if bufnum != -1
4516         let filename = fnamemodify(bufname(bufnum), ':p')
4517         let ftype = s:Tlist_Get_Buffer_Filetype(bufnum)
4518     endif
4519
4520     " Initialize the taglist window, if it is not already initialized
4521     if !exists('s:tlist_window_initialized') || !s:tlist_window_initialized
4522         call s:Tlist_Window_Init()
4523         call s:Tlist_Window_Refresh()
4524         let s:tlist_window_initialized = 1
4525     endif
4526
4527     " Update the taglist window
4528     if bufnum != -1
4529         if !s:Tlist_Skip_File(filename, ftype) && g:Tlist_Auto_Update
4530             call s:Tlist_Window_Refresh_File(filename, ftype)
4531         endif
4532     endif
4533 endfunction
4534
4535 function! TagList_IsValid()
4536     return 0
4537 endfunction
4538
4539 function! TagList_WrapUp()
4540     return 0
4541 endfunction
4542
4543 " restore 'cpo'
4544 let &cpo = s:cpo_save
4545 unlet s:cpo_save
4546