Rainbow
[my-vim-dotfolder.git] / plugin / taglist.vim
blob59901f64fa0999e7884e7b71ed751debbe68fd07
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.
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:
20 "       http://vim-taglist.sourceforge.net
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:
26 "       http://groups.yahoo.com/group/taglist/
28 " For more information about using this plugin, after installing the
29 " taglist plugin, use the ":help taglist" command.
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):
38 "       plugin/taglist.vim - main taglist plugin file
39 "       doc/taglist.txt    - documentation (help) file
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.
57 " ****************** Do not modify after this line ************************
59 " Line continuation used here
60 let s:cpo_save = &cpo
61 set cpo&vim
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
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
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
107     " Automatically open the taglist window on Vim startup
108     if !exists('Tlist_Auto_Open')
109         let Tlist_Auto_Open = 0
110     endif
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
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
123     if !exists('Tlist_Show_Menu')
124         let Tlist_Show_Menu = 0
125     endif
127     " Tag listing sort type - 'name' or 'order'
128     if !exists('Tlist_Sort_Type')
129         let Tlist_Sort_Type = 'order'
130     endif
132     " Tag listing window split (horizontal/vertical) control
133     if !exists('Tlist_Use_Horiz_Window')
134         let Tlist_Use_Horiz_Window = 0
135     endif
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
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
155     " Vertically split taglist window width setting
156     if !exists('Tlist_WinWidth')
157         let Tlist_WinWidth = 30
158     endif
160     " Horizontally split taglist window height setting
161     if !exists('Tlist_WinHeight')
162         let Tlist_WinHeight = 10
163     endif
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
170     " Display tag scopes in the taglist window
171     if !exists('Tlist_Display_Tag_Scope')
172         let Tlist_Display_Tag_Scope = 1
173     endif
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
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
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
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
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
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
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
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
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
231     if !exists('Tlist_Max_Submenu_Items')
232         let Tlist_Max_Submenu_Items = 20
233     endif
235     if !exists('Tlist_Max_Tag_Length')
236         let Tlist_Max_Tag_Length = 10
237     endif
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__"
244     " Taglist debug messages
245     let s:tlist_msg = ''
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
253     " Refresh the taglist
254     if g:Tlist_Process_File_Always
255         autocmd BufEnter * call s:Tlist_Refresh()
256     endif
258     if g:Tlist_Show_Menu
259         autocmd GUIEnter * call s:Tlist_Menu_Init()
260     endif
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()
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
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()
299     " Define autocommands to autoload the taglist plugin when needed.
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
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>'), ' ')
318     let loaded_taglist = 'fast_load_done'
320     if g:Tlist_Show_Menu && has('gui_running')
321         call s:Tlist_Menu_Init()
322     endif
324     " restore 'cpo'
325     let &cpo = s:cpo_save
326     finish
327 endif
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
335 unlet! s:tlist_sid
337 if loaded_taglist != 'fast_load_done'
338     " restore 'cpo'
339     let &cpo = s:cpo_save
340     finish
341 endif
343 " Taglist plugin functionality is available
344 let loaded_taglist = 'available'
346 "------------------- end of user configurable options --------------------
348 " Default language specific settings for supported file types and tag types
350 " Variable name format:
352 "       s:tlist_def_{vim_ftype}_settings
354 " vim_ftype - Filetype detected by Vim
356 " Value format:
358 "       <ctags_ftype>;<flag>:<name>;<flag>:<name>;...
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
366 " assembly language
367 let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type'
369 " aspperl language
370 let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable'
372 " aspvbs language
373 let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable'
375 " awk language
376 let s:tlist_def_awk_settings = 'awk;f:function'
378 " beta language
379 let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern'
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'
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'
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'
394 " cobol language
395 let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' .
396                                \ 'P:program;s:section'
398 " eiffel language
399 let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature'
401 " erlang language
402 let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function'
404 " expect (same as tcl) language
405 let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure'
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'
412 " HTML language
413 let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function'
415 " java language
416 let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' .
417                               \ 'f:field;m:method'
419 " javascript language
420 let s:tlist_def_javascript_settings = 'javascript;f:function'
422 " lisp language
423 let s:tlist_def_lisp_settings = 'lisp;f:function'
425 " lua language
426 let s:tlist_def_lua_settings = 'lua;f:function'
428 " makefiles
429 let s:tlist_def_make_settings = 'make;m:macro'
431 " pascal language
432 let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure'
434 " perl language
435 let s:tlist_def_perl_settings = 'perl;c:constant;l:label;p:package;s:subroutine'
437 " php language
438 let s:tlist_def_php_settings = 'php;c:class;d:constant;v:variable;f:function'
440 " python language
441 let s:tlist_def_python_settings = 'python;c:class;m:member;f:function'
443 " rexx language
444 let s:tlist_def_rexx_settings = 'rexx;s:subroutine'
446 " ruby language
447 let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' .
448                               \ 'm:singleton method'
450 " scheme language
451 let s:tlist_def_scheme_settings = 'scheme;s:set;f:function'
453 " shell language
454 let s:tlist_def_sh_settings = 'sh;f:function'
456 " C shell language
457 let s:tlist_def_csh_settings = 'sh;f:function'
459 " Z shell language
460 let s:tlist_def_zsh_settings = 'sh;f:function'
462 " slang language
463 let s:tlist_def_slang_settings = 'slang;n:namespace;f:function'
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'
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'
473 " tcl language
474 let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;m:method;p:procedure'
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'
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'
486 " vim language
487 let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function'
489 " yacc language
490 let s:tlist_def_yacc_settings = 'yacc;l:label'
492 "------------------- end of language specific options --------------------
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
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
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
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
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
563     " Include the empty line displayed after the help text
564     let brief_help_size = 1
565     let full_help_size = 16
567     setlocal modifiable
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
574     " Remove the currently highlighted tag. Otherwise, the help text
575     " might be highlighted by mistake
576     match none
578     " Toggle between brief and full help text
579     if s:tlist_brief_help
580         let s:tlist_brief_help = 0
582         " Remove the previous help
583         exe '1,' . brief_help_size . ' delete _'
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
590         " Remove the previous help
591         exe '1,' . full_help_size . ' delete _'
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
597     call s:Tlist_Window_Display_Help()
599     " Restore the report option
600     let &report = old_report
602     setlocal nomodifiable
603 endfunction
605 " Taglist debug support
606 let s:tlist_debug = 0
608 " File for storing the debug messages
609 let s:tlist_debug_file = ''
611 " Tlist_Debug_Enable
612 " Enable logging of taglist debug messages.
613 function! s:Tlist_Debug_Enable(...)
614     let s:tlist_debug = 1
616     " Check whether a valid file name is supplied.
617     if a:1 != ''
618         let s:tlist_debug_file = fnamemodify(a:1, ':p')
620         " Empty the log file
621         exe 'redir! > ' . s:tlist_debug_file
622         redir END
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
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
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
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
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
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
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
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
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
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
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
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
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 . ')')
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
742     let fidx = -1
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
753         endif
754     else
755         " Do a binary search in the taglist
756         let left = 0
757         let right = s:tlist_file_count - 1
759         while left < right
760             let mid = (left + right) / 2
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
767             if a:lnum < s:tlist_{mid}_start
768                 let right = mid - 1
769             else
770                 let left = mid + 1
771             endif
772         endwhile
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
781     let s:tlist_file_lnum_idx_cache = fidx
783     return fidx
784 endfunction
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
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
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
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
822     return 0
823 endfunction
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
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)
845         let s:tlist_removed_flist = text_before . text_after
846     endif
847 endfunction
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
871     let msg = 'Taglist: Invalid ctags option setting - ' . settings
873     " Format of the option that specifies the filetype and ctags arugments:
874     "
875     "       <language_name>;flag1:name1;flag2:name2;flag3:name3
876     "
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
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
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)
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)
937         let cnt = cnt + 1
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
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
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
953     return 1
954 endfunction
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
964     " Save the 'filetype', as this will be changed temporarily
965     let old_filetype = &filetype
967     " Run the filetypedetect group of autocommands to determine
968     " the filetype
969     exe 'doautocmd filetypedetect BufRead ' . a:fname
971     " Save the detected filetype
972     let ftype = &filetype
974     " Restore the previous state
975     let &filetype = old_filetype
976     let &eventignore = old_eventignore
978     return ftype
979 endfunction
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')
986     if bufloaded(a:bnum)
987         " For loaded buffers, the 'filetype' is already determined
988         return buf_ft
989     endif
991     " For unloaded buffers, if the 'filetype' option is set, return it
992     if buf_ft != ''
993         return buf_ft
994     endif
996     " Skip non-existent buffers
997     if !bufexists(a:bnum)
998         return ''
999     endif
1001     " For buffers whose filetype is not yet determined, try to determine
1002     " the filetype
1003     let bname = bufname(a:bnum)
1005     return s:Tlist_Detect_Filetype(bname)
1006 endfunction
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
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
1029     let s:tlist_{a:fidx}_tag_count = 0
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
1050     " Discard the stored menu command also
1051     let s:tlist_{a:fidx}_menu_cmd = ''
1052 endfunction
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
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
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)
1082     let ftype = s:tlist_{a:fidx}_filetype
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
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
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
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
1124     setlocal modifiable
1125     exe 'silent! ' . start . ',' . end . 'delete _'
1126     setlocal nomodifiable
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
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
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 . ')')
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
1153         if save_winnr != winnum
1154             let old_eventignore = &eventignore
1155             set eventignore=all
1156             exe winnum . 'wincmd w'
1157         endif
1159         call s:Tlist_Window_Remove_File_From_Display(fidx)
1161         if save_winnr != winnum
1162             exe save_winnr . 'wincmd w'
1163             let &eventignore = old_eventignore
1164         endif
1165     endif
1167     let fname = s:tlist_{fidx}_filename
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
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
1183     call s:Tlist_Discard_FileInfo(fidx)
1185     " Shift all the file variables by one index
1186     let i = fidx + 1
1188     while i < s:tlist_file_count
1189         let j = i - 1
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
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
1214         let ftype = s:tlist_{i}_filetype
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
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)
1236         let i = i + 1
1237     endwhile
1239     " Reduce the number of files displayed
1240     let s:tlist_file_count = s:tlist_file_count - 1
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
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
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
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
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()
1300                 " one extra column is needed to include the vertical split
1301                 let &columns= &columns + g:Tlist_WinWidth + 1
1303                 let s:tlist_winsize_chgd = 1
1304             else
1305                 let s:tlist_winsize_chgd = 0
1306             endif
1307         endif
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
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
1330     " Create the taglist window
1331     exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd
1333     " Save the new window position
1334     let s:tlist_winx = getwinposx()
1335     let s:tlist_winy = getwinposy()
1337     " Initialize the taglist window
1338     call s:Tlist_Window_Init()
1339 endfunction
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
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
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
1380     " Get the tag search pattern and display it
1381     return s:Tlist_Get_Tag_Prototype(fidx, tidx)
1382 endfunction
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
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
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
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()')
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
1447     " Set the taglist buffer filetype to taglist
1448     setlocal filetype=taglist
1450     " Define taglist window element highlighting
1451     syntax match TagListComment '^" .*'
1452     syntax match TagListFileName '^[^" ].*$'
1453     syntax match TagListTitle '^  \S.*$'
1454     syntax match TagListTagScope  '\s\[.\{-\}\]$'
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
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)
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
1523     silent! setlocal nowrap
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
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
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
1546     " Setup the cpoptions properly for the maps to work
1547     let old_cpoptions = &cpoptions
1548     set cpoptions&vim
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>
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>
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
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)
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
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
1691     " Restore the previous cpoptions settings
1692     let &cpoptions = old_cpoptions
1693 endfunction
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
1704     " Mark the buffer as modifiable
1705     setlocal modifiable
1707     " Delete the contents of the buffer to the black-hole register
1708     silent! %delete _
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
1718     if g:Tlist_Compact_Format == 0
1719         " Display help in non-compact mode
1720         call s:Tlist_Window_Display_Help()
1721     endif
1723     " Mark the buffer as not modifiable
1724     setlocal nomodifiable
1726     " Restore the report option
1727     let &report = old_report
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
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
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
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
1770     " Move the cursor to the top of the taglist window
1771     normal! gg
1772 endfunction
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
1785     " Remove the taglist autocommands
1786     silent! autocmd! TagListAutoCmds
1788     " Clear all the highlights
1789     match none
1791     silent! syntax clear TagListTitle
1792     silent! syntax clear TagListComment
1793     silent! syntax clear TagListTagScope
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
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
1818         " Adjust the Vim window width
1819         let &columns= &columns - (g:Tlist_WinWidth + 1)
1820     endif
1821     endif
1823     let s:tlist_winsize_chgd = -1
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
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
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
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
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
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
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
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
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
1896     " Mark the buffer as modifiable
1897     setlocal modifiable
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
1910     let s:tlist_{fidx}_visible = 1
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
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
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
1949             let {fidx_ttype}_offset = ttype_start_lnum - file_start
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'
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
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
1971     if s:tlist_{fidx}_tag_count == 0
1972         if g:Tlist_Compact_Format == 0
1973             silent! put =''
1974         endif
1975     endif
1977     let s:tlist_{fidx}_end = line('.') - 1
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!'
1984     " Goto the starting line for this file,
1985     exe s:tlist_{fidx}_start
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
1993     " Mark the buffer as not modifiable
1994     setlocal nomodifiable
1996     " Restore the report option
1997     let &report = old_report
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)
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
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"
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 = ''
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
2049     return fidx
2050 endfunction
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'
2057     " Already parsed and have the tag name
2058     if exists(ttype_var)
2059         return {ttype_var}
2060     endif
2062     let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag
2063     let {ttype_var} = s:Tlist_Extract_Tagtype(tag_line)
2065     return {ttype_var}
2066 endfunction
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'
2072     " Already parsed and have the tag prototype
2073     if exists(tproto_var)
2074         return {tproto_var}
2075     endif
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*', '', '')
2087     return {tproto_var}
2088 endfunction
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'
2094     " Already parsed and have the tag search pattern
2095     if exists(tpat_var)
2096         return {tpat_var}
2097     endif
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] == '$' ? '\$' : '')
2109     return {tpat_var}
2110 endfunction
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'
2117     " Already parsed and have the tag line number
2118     if exists(tline_var)
2119         return {tline_var}
2120     endif
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
2132     return {tline_var}
2133 endfunction
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:
2140 "     tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields
2142 function! s:Tlist_Parse_Tagline(tag_line)
2143     if a:tag_line == ''
2144         " Skip empty lines
2145         return
2146     endif
2148     " Extract the tag type
2149     let ttype = s:Tlist_Extract_Tagtype(a:tag_line)
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
2157     " Update the total tag count
2158     let s:tidx = s:tidx + 1
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
2166     " Update the count of this tag type
2167     let ttype_idx = {fidx_ttype}_count + 1
2168     let {fidx_ttype}_count = ttype_idx
2170     " Store the ctags output for this tag
2171     let {fidx_tidx}_tag = a:tag_line
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
2177     " Extract the tag name
2178     let tag_name = strpart(a:tag_line, 0, stridx(a:tag_line, "\t"))
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
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
2196     " Add this tag to the tag type variable
2197     let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
2199     " Save the tag name
2200     let {fidx_tidx}_tag_name = tag_name
2201 endfunction
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
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
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
2233     let s:tlist_{fidx}_valid = 1
2235     " Exuberant ctags arguments to generate a tag list
2236     let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks '
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
2245     " Add the filetype specific arguments
2246     let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args
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 . '"'
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
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
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
2282         call s:Tlist_Log_Msg('Cmd inside batch file: ' . ctags_cmd)
2283         let ctags_cmd = '"' . s:taglist_tempfile . '"'
2284     endif
2286     call s:Tlist_Log_Msg('Cmd: ' . ctags_cmd)
2288     " Run ctags and get the tag list
2289     let cmd_output = system(ctags_cmd)
2291     " Restore the value of the 'shellslash' option.
2292     if has('win95') && !has('win32unix')
2293         let &shellslash = old_shellslash
2294     endif
2296     if exists('s:taglist_tempfile')
2297         " Delete the temporary cmd file created on MS-Windows
2298         call delete(s:taglist_tempfile)
2299     endif
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
2311     " Store the modification time for the file
2312     let s:tlist_{fidx}_mtime = getftime(a:filename)
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
2320     call s:Tlist_Log_Msg('Generated tags information for ' . a:filename)
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
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')
2335         " Save the number of tags for this file
2336         let s:tlist_{fidx}_tag_count = s:tidx
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
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)
2356             if one_line == ''
2357                 " Line is not in proper tags format
2358                 continue
2359             endif
2361             " Extract the tag type
2362             let ttype = s:Tlist_Extract_Tagtype(one_line)
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
2371             " Update the total tag count
2372             let tidx = tidx + 1
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
2381             " Update the count of this tag type
2382             let ttype_idx = {fidx_ttype}_count + 1
2383             let {fidx_ttype}_count = ttype_idx
2385             " Store the ctags output for this tag
2386             let {fidx_tidx}_tag = one_line
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
2392             " Extract the tag name
2393             let tag_name = strpart(one_line, 0, stridx(one_line, "\t"))
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
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
2411             " Add this tag to the tag type variable
2412             let {fidx_ttype} = {fidx_ttype} . ttxt . "\n"
2414             " Save the tag name
2415             let {fidx_tidx}_tag_name = tag_name
2416         endwhile
2418         " Save the number of tags for this file
2419         let s:tlist_{fidx}_tag_count = tidx
2420     endif
2422     call s:Tlist_Log_Msg('Processed ' . s:tlist_{fidx}_tag_count . 
2423                 \ ' tags in ' . a:filename)
2425     return fidx
2426 endfunction
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
2437     " Convert the file name to a full path
2438     let fname = fnamemodify(a:filename, ':p')
2440     " First check whether the file already exists
2441     let fidx = s:Tlist_Get_File_Index(fname)
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
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
2475         " Save the current window number
2476         let save_winnr = winnr()
2478         " Goto the taglist window
2479         call s:Tlist_Window_Goto_Window()
2481         " Save the cursor position
2482         let save_line = line('.')
2483         let save_col = col('.')
2485         " Update the taglist window
2486         call s:Tlist_Window_Refresh_File(fname, a:ftype)
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
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
2502     " Update the taglist menu
2503     if g:Tlist_Show_Menu
2504         call s:Tlist_Menu_Update_File(1)
2505     endif
2506 endfunction
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
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
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
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
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
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('.')
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()
2579     " Open the taglist window
2580     call s:Tlist_Window_Create()
2582     call s:Tlist_Window_Refresh()
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
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
2603     " Highlight the current tag
2604     call s:Tlist_Window_Highlight_Tag(curbuf_name, cur_lnum, 1, 1)
2605 endfunction
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
2618     call s:Tlist_Window_Open()
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
2626     " Update the taglist menu
2627     if g:Tlist_Show_Menu
2628         call s:Tlist_Menu_Update_File(0)
2629     endif
2630 endfunction
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
2638     " Enable lazy screen updates
2639     let old_lazyredraw = &lazyredraw
2640     set lazyredraw
2642     " Keep track of the number of processed files
2643     let fcnt = 0
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)
2650         " Remove the filename from the list
2651         let flist = strpart(flist, nl_idx + 1)
2653         if one_file == ''
2654             continue
2655         endif
2657         " Skip directories
2658         if isdirectory(one_file)
2659             continue
2660         endif
2662         let ftype = s:Tlist_Detect_Filetype(one_file)
2664         echon "\r                                                              "
2665         echon "\rProcessing tags for " . fnamemodify(one_file, ':p:t')
2667         let fcnt = fcnt + 1
2669         call Tlist_Update_File(one_file, ftype)
2670     endwhile
2672     " Clear the displayed informational messages
2673     echon "\r                                                            "
2675     " Restore the previous state
2676     let &lazyredraw = old_lazyredraw
2678     return fcnt
2679 endfunction
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"
2686     let fcnt = s:Tlist_Process_Filelist(flist)
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"
2696     while all_files != ''
2697         let nl_idx = stridx(all_files, "\n")
2698         let one_file = strpart(all_files, 0, nl_idx)
2700         let all_files = strpart(all_files, nl_idx + 1)
2701         if one_file == ''
2702             continue
2703         endif
2705         " Skip non-directory names
2706         if !isdirectory(one_file)
2707             continue
2708         endif
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
2715     return fcnt
2716 endfunction
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
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
2735     echon "\r                                                              "
2736     echon "\rProcessing files in directory " . fnamemodify(dir_name, ':t')
2737     let fcnt = s:Tlist_Process_Dir(dir_name, pat)
2739     echon "\rAdded " . fcnt . " files to the taglist"
2740 endfunction
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
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
2754     if flist == ''
2755         call s:Tlist_Warning_Msg('Error: No matching files are found')
2756         return
2757     endif
2759     let fcnt = s:Tlist_Process_Filelist(flist)
2760     echon "\rAdded " . fcnt . " files to the taglist"
2761 endfunction
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)
2773     return ttype
2774 endfunction
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
2785     let tag_scope = strpart(a:tag_line, end + 1)
2786     let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1)
2788     return tag_scope
2789 endfunction
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
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
2813     " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
2814     if &buftype != ''
2815         return
2816     endif
2818     let filename = fnamemodify(bufname('%'), ':p')
2819     let ftype = s:Tlist_Get_Buffer_Filetype('%')
2821     " If the file doesn't support tag listing, skip it
2822     if s:Tlist_Skip_File(filename, ftype)
2823         return
2824     endif
2826     let tlist_win = bufwinnr(g:TagList_title)
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
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
2842         " If the taglist should not be auto updated, then return
2843         if !g:Tlist_Auto_Update
2844             return
2845         endif
2846     endif
2848     let cur_lnum = line('.')
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
2859             " Update the taglist and the window
2860             call Tlist_Update_File(filename, ftype)
2862             " Store the new file modification time
2863             let s:tlist_{fidx}_mtime = mtime
2864         endif
2865     endif
2867     " Update the taglist window
2868     if tlist_win != -1
2869         " Disable screen updates
2870         let old_lazyredraw = &lazyredraw
2871         set nolazyredraw
2873         " Save the current window number
2874         let save_winnr = winnr()
2876         " Goto the taglist window
2877         call s:Tlist_Window_Goto_Window()
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
2885         " Update the taglist window
2886         call s:Tlist_Window_Refresh_File(filename, ftype)
2888         " Open the fold for the file
2889         exe "silent! " . s:tlist_{fidx}_start . "," .
2890                     \ s:tlist_{fidx}_end . "foldopen!"
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
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
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
2919         " Restore screen updates
2920         let &lazyredraw = old_lazyredraw
2921     endif
2923     " Update the taglist menu
2924     if g:Tlist_Show_Menu
2925         call s:Tlist_Menu_Update_File(0)
2926     endif
2927 endfunction
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
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
2953     if a:action == 'toggle'
2954         let sort_type = s:tlist_{fidx}_sort_type
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
2966     " Invalidate the tags listed for this file
2967     let s:tlist_{fidx}_valid = 0
2969     if a:caller  == 'cmd'
2970         " Save the current line for later restoration
2971         let curline = '\V\^' . getline('.') . '\$'
2973         call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
2974                     \   s:tlist_{fidx}_filetype)
2976         exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
2978         " Go back to the cursor line before the tag list is sorted
2979         call search(curline, 'w')
2981         call s:Tlist_Menu_Update_File(1)
2982     else
2983         call s:Tlist_Menu_Remove_File()
2985         call s:Tlist_Refresh()
2986     endif
2987 endfunction
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
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
3018     " Remove the previous highlighting
3019     match none
3021     " Save the current line for later restoration
3022     let curline = '\V\^' . getline('.') . '\$'
3024     let s:tlist_{fidx}_valid = 0
3026     " Update the taglist window
3027     call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename,
3028                 \ s:tlist_{fidx}_filetype)
3030     exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!'
3032     " Go back to the tag line before the list is updated
3033     call search(curline, 'w')
3034 endfunction
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
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
3055     " Current line doesn't belong to any of the displayed tag types
3056     if i > s:tlist_{ftype}_count
3057         return ''
3058     endif
3060     return ttype
3061 endfunction
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)
3068     " Current line doesn't belong to any of the displayed tag types
3069     if ttype == ''
3070         return 0
3071     endif
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
3080     " Get the corresponding tag line and return it
3081     return s:tlist_{a:fidx}_{ttype}_{tidx}
3082 endfunction
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
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
3097     exe 'match TagListTagName ' . pat
3098 endfunction
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
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
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
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
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, ' ')
3148             " Open the taglist window
3149             call s:Tlist_Window_Open()
3150         endif
3151     endif
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
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
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
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
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
3201         if fwin_num != 0
3202             " Jump to the file window
3203             exe fwin_num . "wincmd w"
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
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'
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
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')
3272         " Bring the line to the middle of the window
3273         normal! z.
3275         " If the line is inside a fold, open the fold
3276         if foldclosed('.') != -1
3277             .foldopen
3278         endif
3279     endif
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
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
3302     let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
3303 endfunction
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
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
3331     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum)
3332     if fidx == -1
3333         return
3334     endif
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)
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
3348     call s:Tlist_Window_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat)
3349 endfunction
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()')
3356     " Clear the previously displayed line
3357     echo
3359     " Do not process comment lines and empty lines
3360     let curline = getline('.')
3361     if curline =~ '^\s*$' || curline[0] == '"'
3362         return
3363     endif
3365     " If inside a fold, then don't display the prototype
3366     if foldclosed('.') != -1
3367         return
3368     endif
3370     let lnum = line('.')
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
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
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
3398         let ttype_name = ''
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
3410         echo 'Tag type=' . ttype_name .
3411                     \ ', Tag count=' . s:tlist_{fidx}_{ttype}_count
3412         return
3413     endif
3415     " Get the tag search pattern and display it
3416     echo s:Tlist_Get_Tag_Prototype(fidx, tidx)
3417 endfunction
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
3425     let left = 1
3426     let right = s:tlist_{a:fidx}_tag_count
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.
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)
3437         if a:linenum < first_lnum
3438             return -1
3439         endif
3441         while left < right
3442             let middle = (right + left + 1) / 2
3443             let middle_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, middle)
3445             if middle_lnum == a:linenum
3446                 let left = middle
3447                 break
3448             endif
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)
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
3487     return left
3488 endfunction
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
3503     if a:filename == ''
3504         return
3505     endif
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
3514     let fidx = s:Tlist_Get_File_Index(a:filename)
3515     if fidx == -1
3516         return
3517     endif
3519     " If the file is currently not displayed in the taglist window, then retrn
3520     if !s:tlist_{fidx}_visible
3521         return
3522     endif
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
3529     " Ignore all autocommands
3530     let old_ei = &eventignore
3531     set eventignore=all
3533     " Save the original window number
3534     let org_winnr = winnr()
3536     if org_winnr == winnum
3537         let in_taglist_window = 1
3538     else
3539         let in_taglist_window = 0
3540     endif
3542     " Go to the taglist window
3543     if !in_taglist_window
3544         exe winnum . 'wincmd w'
3545     endif
3547     " Clear previously selected name
3548     match none
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('.')
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
3562         if foldclosed('.') != -1
3563             .foldopen
3564         endif
3566         call winline()
3568         if !in_taglist_window
3569             exe org_winnr . 'wincmd w'
3570         endif
3572         " Restore the autocommands
3573         let &eventignore = old_ei
3574         return
3575     endif
3577     " Extract the tag type
3578     let ttype = s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx)
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
3585     " Goto the line containing the tag
3586     exe lnum
3588     " Open the fold
3589     if foldclosed('.') != -1
3590         .foldopen
3591     endif
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
3603     " Highlight the tag name
3604     call s:Tlist_Window_Highlight_Line()
3606     " Go back to the original window
3607     if !in_taglist_window
3608         exe org_winnr . 'wincmd w'
3609     endif
3611     " Restore the autocommands
3612     let &eventignore = old_ei
3613     return
3614 endfunction
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
3641     " Expand the file to a fully qualified name
3642     let filename = fnamemodify(filename, ':p')
3643     if filename == ''
3644         return ""
3645     endif
3647     let fidx = s:Tlist_Get_File_Index(filename)
3648     if fidx == -1
3649         return ""
3650     endif
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
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
3663     return s:Tlist_Get_Tag_Prototype(fidx, tidx)
3664 endfunction
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
3690     " Make sure the current file has a name
3691     let filename = fnamemodify(filename, ':p')
3692     if filename == ''
3693         return ""
3694     endif
3696     let fidx = s:Tlist_Get_File_Index(filename)
3697     if fidx == -1
3698         return ""
3699     endif
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
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
3712     return s:tlist_{fidx}_{tidx}_tag_name
3713 endfunction
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
3734     let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.'))
3735     if fidx == -1
3736         return
3737     endif
3739     let cur_lnum = line('.')
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
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
3756         exe s:tlist_{fidx}_start
3757         return
3758     else
3759         " Move to the beginning of the next file
3760         let fidx = fidx + 1
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
3767         if s:tlist_{fidx}_start != 0
3768             exe s:tlist_{fidx}_start
3769         endif
3770         return
3771     endif
3772 endfunction
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
3783     let sessionfile = a:1
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
3791     " Mark the current window as the file window
3792     call s:Tlist_Window_Mark_File_Window()
3794     " Source the session file
3795     exe 'source ' . sessionfile
3797     let new_file_count = g:tlist_file_count
3798     unlet! g:tlist_file_count
3800     let i = 0
3801     while i < new_file_count
3802         let ftype = g:tlist_{i}_filetype
3803         unlet! g:tlist_{i}_filetype
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
3812         let fname = g:tlist_{i}_filename
3813         unlet! g:tlist_{i}_filename
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
3827         let fidx = s:Tlist_Init_File(fname, ftype)
3829         let s:tlist_{fidx}_filename = fname
3831         let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type
3832         unlet! g:tlist_{i}_sort_type
3834         let s:tlist_{fidx}_filetype = ftype
3835         let s:tlist_{fidx}_mtime = getftime(fname)
3837         let s:tlist_{fidx}_start = 0
3838         let s:tlist_{fidx}_end = 0
3840         let s:tlist_{fidx}_valid = 1
3842         let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count
3843         unlet! g:tlist_{i}_tag_count
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
3856         let j = 1
3857         while j <= s:tlist_{ftype}_count
3858             let ttype = s:tlist_{ftype}_{j}_name
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
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
3879             let j = j + 1
3880         endwhile
3882         let i = i + 1
3883     endwhile
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()
3890         " Goto the taglist window
3891         call s:Tlist_Window_Goto_Window()
3893         " Refresh the taglist window
3894         call s:Tlist_Window_Refresh()
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
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
3912     let sessionfile = a:1
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
3920     if filereadable(sessionfile)
3921         let ans = input('Do you want to overwrite ' . sessionfile . ' (Y/N)?')
3922         if ans !=? 'y'
3923             return
3924         endif
3926         echo "\n"
3927     endif
3929     let old_verbose = &verbose
3930     set verbose&vim
3932     exe 'redir! > ' . sessionfile
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
3938     let i = 0
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
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
3984         silent! echo
3986         let i = i + 1
3987     endwhile
3989     redir END
3991     let &verbose = old_verbose
3992 endfunction
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 .  ')')
4000     " Make sure a valid filename is supplied
4001     if a:filename == ''
4002         return
4003     endif
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
4012     " Remove the file from the list
4013     call s:Tlist_Remove_File(fidx, 0)
4014 endfunction
4016 " When a buffer is deleted, remove the file from the taglist
4017 autocmd BufDelete * silent call s:Tlist_Buffer_Removed(expand('<afile>:p'))
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 . ')')
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
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
4040     if in_taglist_window
4041         " When entering the taglist window, no need to update the folds
4042         return
4043     endif
4045     " Go to the taglist window
4046     if !in_taglist_window
4047         call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w')
4048     endif
4050     " Close all the folds
4051     silent! %foldclose
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
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
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
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
4090     if open_window
4091         call s:Tlist_Window_Toggle()
4092     endif
4093 endfunction
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
4105     let save_wnum = winnr()
4106     exe winnum . 'wincmd w'
4108     " First remove all the existing folds
4109     normal! zE
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
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
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
4134     exe save_wnum . 'wincmd w'
4135 endfunction
4137 function! s:Tlist_Menu_Add_Base_Menu()
4138     call s:Tlist_Log_Msg('Adding the base menu')
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-           :
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
4159 let s:menu_char_prefix =
4160             \ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
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
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
4179     " Curly brace variable name optimization
4180     let fidx_ttype = a:fidx . '_' . ttype
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
4188     let mcmd = ''
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
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
4206             let tidx = s:tlist_{fidx_ttype}_{final_index}
4207             let last_tag = s:tlist_{a:fidx}_{tidx}_tag_name
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)
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 . '.'
4221             " Character prefix used to number the menu items (hotkey)
4222             let m_prefix_idx = 0
4224             while j <= final_index
4225                 let tidx = s:tlist_{fidx_ttype}_{j}
4227                 let tname = s:tlist_{a:fidx}_{tidx}_tag_name
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>|'
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
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}
4250             let tname = s:tlist_{a:fidx}_{tidx}_tag_name
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>|'
4257             let m_prefix_idx = m_prefix_idx + 1
4258             let j = j + 1
4259         endwhile
4260     endif
4262     return mcmd
4263 endfunction
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
4272     exe s:tlist_{a:fidx}_menu_cmd
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
4281     " The taglist menu is not empty now
4282     let s:tlist_menu_empty = 0
4284     " Restore the 'cpoptions' settings
4285     let &cpoptions = old_cpoptions
4286 endfunction
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
4296     call s:Tlist_Log_Msg('Updating the tag menu, clear_menu = ' . a:clear_menu)
4298     " Remove the tags menu
4299     if a:clear_menu
4300         call s:Tlist_Menu_Remove_File()
4302     endif
4304     " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
4305     if &buftype != ''
4306         return
4307     endif
4309     let filename = fnamemodify(bufname('%'), ':p')
4310     let ftype = s:Tlist_Get_Buffer_Filetype('%')
4312     " If the file doesn't support tag listing, skip it
4313     if s:Tlist_Skip_File(filename, ftype)
4314         return
4315     endif
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
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
4332     let fname = escape(fnamemodify(bufname('%'), ':t'), '.')
4333     if fname != ''
4334         exe 'anoremenu T&ags.' .  fname . ' <Nop>'
4335         anoremenu T&ags.-SEP2-           :
4336     endif
4338     if !s:tlist_{fidx}_tag_count
4339         return
4340     endif
4342     if s:tlist_{fidx}_menu_cmd != ''
4343         " Update the menu with the cached command
4344         call s:Tlist_Menu_File_Refresh(fidx)
4346         return
4347     endif
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
4353     let cmd = ''
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
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
4376         let i = i + 1
4377     endwhile
4379     " Cache the menu command for reuse
4380     let s:tlist_{fidx}_menu_cmd = cmd
4382     " Update the menu
4383     call s:Tlist_Menu_File_Refresh(fidx)
4384 endfunction
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
4393     call s:Tlist_Log_Msg('Removing the tags menu for a file')
4395     " Cleanup the Tags menu
4396     silent! unmenu T&ags
4397     if &mousemodel =~ 'popup'
4398         silent! unmenu PopUp.T&ags
4399     endif
4401     " Add a dummy menu item to retain teared off menu
4402     noremenu T&ags.Dummy l
4404     silent! unmenu! T&ags
4405     if &mousemodel =~ 'popup'
4406         silent! unmenu! PopUp.T&ags
4407     endif
4409     call s:Tlist_Menu_Add_Base_Menu()
4411     " Remove the dummy menu item
4412     unmenu T&ags.Dummy
4414     let s:tlist_menu_empty = 1
4415 endfunction
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
4427     " Update the taglist, menu and window
4428     call s:Tlist_Update_Current_File()
4429 endfunction
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
4439     let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, a:tidx)
4440     if tagpat == ''
4441         return
4442     endif
4444     " Add the current cursor position to the jump list, so that user can
4445     " jump back using the ' and ` marks.
4446     mark '
4448     silent call search(tagpat, 'w')
4450     " Bring the line to the middle of the window
4451     normal! z.
4453     " If the line is inside a fold, open the fold
4454     if foldclosed('.') != -1
4455         .foldopen
4456     endif
4457 endfunction
4459 " Tlist_Menu_Init
4460 " Initialize the taglist menu
4461 function! s:Tlist_Menu_Init()
4462     call s:Tlist_Menu_Add_Base_Menu()
4464     " Automatically add the tags defined in the current file to the menu
4465     augroup TagListMenuCmds
4466         autocmd!
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
4474     call s:Tlist_Menu_Update_File(0)
4475 endfunction
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')
4483     " Initialize the taglist window
4484     call s:Tlist_Window_Init()
4486     " Refresh the taglist window
4487     call s:Tlist_Window_Refresh()
4488 endfunction
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
4499     let s:tlist_app_name = a:name
4500 endfunction
4502 " Winmanager integration
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
4511     call Tlist_Set_App('winmanager')
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
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
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
4535 function! TagList_IsValid()
4536     return 0
4537 endfunction
4539 function! TagList_WrapUp()
4540     return 0
4541 endfunction
4543 " restore 'cpo'
4544 let &cpo = s:cpo_save
4545 unlet s:cpo_save