1
2"*********************************************************************
3" Note: Cream adds one adjustment (marked with "+++ CREAM" below).
4"*********************************************************************
5
6" File: taglist.vim
7" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
8" Version: 3.3
9" Last Modified: May 26, 2004
10"
11" The "Tag List" plugin is a source code browser plugin for Vim and
12" provides an overview of the structure of source code files and allows
13" you to efficiently browse through source code files for different
14" programming languages.  You can visit the taglist plugin home page for
15" more information:
16"
17"       http://www.geocities.com/yegappan/taglist
18"
19" You can subscribe to the taglist mailing list to post your questions
20" or suggestions for improvement or to report bugs. Visit the following
21" page for subscribing to the mailing list:
22"
23"       http://groups.yahoo.com/group/taglist/
24"
25" For more information about using this plugin, after installing the
26" taglist plugin, use the ":help taglist" command.
27"
28" Installation
29" ------------
30" 1. Download the taglist.zip file and unzip the files to the $HOME/.vim
31"    or the $HOME/vimfiles or the $VIM/vimfiles directory. This should
32"    unzip the following two files (the directory structure should be
33"    preserved):
34"
35"       plugin/taglist.vim - main taglist plugin file
36"       doc/taglist.txt    - documentation (help) file
37"
38"    Refer to the 'add-plugin', 'add-global-plugin' and 'runtimepath'
39"    Vim help pages for more details about installing Vim plugins.
40" 2. Change to the $HOME/.vim/doc or $HOME/vimfiles/doc or
41"    $VIM/doc/vimfiles directory, start Vim and run the ":helptags ."
42"    command to process the taglist help file.
43" 3. Set the Tlist_Ctags_Cmd variable to point to the location of the
44"    exuberant ctags utility (not to the directory) in the .vimrc file.
45" 4. If you are running a terminal/console version of Vim and the
46"    terminal doesn't support changing the window width then set the
47"    'Tlist_Inc_Winwidth' variable to 0 in the .vimrc file.
48" 5. Restart Vim.
49" 6. You can now use the ":Tlist" command to open/close the taglist
50"    window. You can use the ":help taglist" command to get more
51"    information about using the taglist plugin.
52"
53" ****************** Do not modify after this line ************************
54if exists('loaded_taglist') || &cp
55    finish
56endif
57let loaded_taglist=1
58
59" Location of the exuberant ctags tool
60if !exists('Tlist_Ctags_Cmd')
61    if executable('exuberant-ctags')
62        let Tlist_Ctags_Cmd = 'exuberant-ctags'
63    elseif executable('ctags')
64        let Tlist_Ctags_Cmd = 'ctags'
65    elseif executable('tags')
66        let Tlist_Ctags_Cmd = 'tags'
67    else
68		"+++ CREAM:
69		"echomsg 'Taglist: Exuberant ctags not found in PATH. ' .
70		"            \ 'Plugin is not loaded.'
71		"+++
72        finish
73    endif
74endif
75
76" Tag listing sort type - 'name' or 'order'
77if !exists('Tlist_Sort_Type')
78    let Tlist_Sort_Type = 'order'
79endif
80
81" Tag listing window split (horizontal/vertical) control
82if !exists('Tlist_Use_Horiz_Window')
83    let Tlist_Use_Horiz_Window = 0
84endif
85
86" Open the vertically split taglist window on the left or on the right side.
87" This setting is relevant only if Tlist_Use_Horiz_Window is set to zero (i.e.
88" only for vertically split windows)
89if !exists('Tlist_Use_Right_Window')
90    let Tlist_Use_Right_Window = 0
91endif
92
93" Increase Vim window width to display vertically split taglist window.  For
94" MS-Windows version of Vim running in a MS-DOS window, this must be set to 0
95" otherwise the system may hang due to a Vim limitation.
96if !exists('Tlist_Inc_Winwidth')
97    if (has('win16') || has('win95')) && !has('gui_running')
98        let Tlist_Inc_Winwidth = 0
99    else
100        let Tlist_Inc_Winwidth = 1
101    endif
102endif
103
104" Vertically split taglist window width setting
105if !exists('Tlist_WinWidth')
106    let Tlist_WinWidth = 30
107endif
108
109" Horizontally split taglist window height setting
110if !exists('Tlist_WinHeight')
111    let Tlist_WinHeight = 10
112endif
113
114" Automatically open the taglist window on Vim startup
115if !exists('Tlist_Auto_Open')
116    let Tlist_Auto_Open = 0
117endif
118
119" Display tag prototypes or tag names in the taglist window
120if !exists('Tlist_Display_Prototype')
121    let Tlist_Display_Prototype = 0
122endif
123
124" Display tag scopes in the taglist window
125if !exists('Tlist_Display_Tag_Scope')
126    let Tlist_Display_Tag_Scope = 1
127endif
128
129" Use single left mouse click to jump to a tag. By default this is disabled.
130" Only double click using the mouse will be processed.
131if !exists('Tlist_Use_SingleClick')
132    let Tlist_Use_SingleClick = 0
133endif
134
135" Control whether additional help is displayed as part of the taglist or not.
136" Also, controls whether empty lines are used to separate the tag tree.
137if !exists('Tlist_Compact_Format')
138    let Tlist_Compact_Format = 0
139endif
140
141" Exit Vim if only the taglist window is currently open. By default, this is
142" set to zero.
143if !exists('Tlist_Exit_OnlyWindow')
144    let Tlist_Exit_OnlyWindow = 0
145endif
146
147" Automatically close the folds for the non-active files in the taglist window
148if !exists('Tlist_File_Fold_Auto_Close')
149    let Tlist_File_Fold_Auto_Close = 0
150endif
151
152" Automatically highlight the current tag
153if !exists('Tlist_Auto_Highlight_Tag')
154    let Tlist_Auto_Highlight_Tag = 1
155endif
156
157" Process files even when the taglist window is not open
158if !exists('Tlist_Process_File_Always')
159    let Tlist_Process_File_Always = 0
160endif
161
162" Enable fold column to display the folding for the tag tree
163if !exists('Tlist_Enable_Fold_Column')
164    let Tlist_Enable_Fold_Column = 1
165endif
166
167"------------------- end of user configurable options --------------------
168
169" Initialize the taglist plugin local variables for the supported file types
170" and tag types
171
172" assembly language
173let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type'
174
175" aspperl language
176let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable'
177
178" aspvbs language
179let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable'
180
181" awk language
182let s:tlist_def_awk_settings = 'awk;f:function'
183
184" beta language
185let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern'
186
187" c language
188let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' .
189                           \ 'v:variable;f:function'
190
191" c++ language
192let s:tlist_def_cpp_settings = 'c++;v:variable;d:macro;t:typedef;c:class;' .
193                             \ 'n:namespace;g:enum;s:struct;u:union;f:function'
194
195" c# language
196let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' .
197                             \ 'E:event;g:enum;s:struct;i:interface;' .
198                             \ 'p:properties;m:method'
199
200" cobol language
201let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' .
202                               \ 'P:program;s:section'
203
204" eiffel language
205let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature'
206
207" erlang language
208let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function'
209
210" expect (same as tcl) language
211let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure'
212
213" fortran language
214let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' .
215                    \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' .
216                    \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine'
217
218" HTML language
219let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function'
220
221" java language
222let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' .
223                              \ 'f:field;m:method'
224
225" javascript language
226let s:tlist_def_javascript_settings = 'javascript;f:function'
227
228" lisp language
229let s:tlist_def_lisp_settings = 'lisp;f:function'
230
231" lua language
232let s:tlist_def_lua_settings = 'lua;f:function'
233
234" makefiles
235let s:tlist_def_make_settings = 'make;m:macro'
236
237" pascal language
238let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure'
239
240" perl language
241let s:tlist_def_perl_settings = 'perl;p:package;s:subroutine'
242
243" php language
244let s:tlist_def_php_settings = 'php;c:class;f:function'
245
246" python language
247let s:tlist_def_python_settings = 'python;c:class;m:member;f:function'
248
249" rexx language
250let s:tlist_def_rexx_settings = 'rexx;s:subroutine'
251
252" ruby language
253let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' .
254                              \ 'm:singleton method'
255
256" scheme language
257let s:tlist_def_scheme_settings = 'scheme;s:set;f:function'
258
259" shell language
260let s:tlist_def_sh_settings = 'sh;f:function'
261
262" C shell language
263let s:tlist_def_csh_settings = 'sh;f:function'
264
265" Z shell language
266let s:tlist_def_zsh_settings = 'sh;f:function'
267
268" slang language
269let s:tlist_def_slang_settings = 'slang;n:namespace;f:function'
270
271" sml language
272let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' .
273                             \ 'r:structure;t:type;v:value;f:function'
274
275" sql language
276let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' .
277            \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure'
278
279" tcl language
280let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;p:procedure'
281
282" vera language
283let s:tlist_def_vera_settings = 'vera;c:class;d:macro;e:enumerator;' .
284                                \ 'f:function;g:enum;m:member;p:program;' .
285                                \ 'P:prototype;t:task;T:typedef;v:variable;' .
286                                \ 'x:externvar'
287
288"verilog language
289let s:tlist_def_verilog_settings = 'verilog;m:module;P:parameter;r:register;' .
290                                 \ 't:task;w:write;p:port;v:variable;f:function'
291
292" vim language
293let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function'
294
295" yacc language
296let s:tlist_def_yacc_settings = 'yacc;l:label'
297
298"------------------- end of language specific options --------------------
299
300" Vim window size is changed or not
301let s:tlist_winsize_chgd = 0
302" Taglist window is maximized or not
303let s:tlist_win_maximized = 0
304" Number of files displayed in the taglist window
305let s:tlist_file_count = 0
306" Number of filetypes supported by taglist
307let s:tlist_ftype_count = 0
308" Is taglist part of other plugins like winmanager or cream?
309let s:tlist_app_name = "none"
310" Are we displaying brief help text
311let s:tlist_brief_help = 1
312" Do not change the name of the taglist title variable. The winmanager plugin
313" relies on this name to determine the title for the taglist plugin.
314let TagList_title = "__Tag_List__"
315
316" An autocommand is used to refresh the taglist window when entering any
317" buffer. We don't want to refresh the taglist window if we are entering the
318" file window from one of the taglist functions. The 'Tlist_Skip_Refresh'
319" variable is used to skip the refresh of the taglist window and is set
320" and cleared appropriately.
321let s:Tlist_Skip_Refresh = 0
322
323" Tlist_Display_Help()
324function! s:Tlist_Display_Help()
325    if s:tlist_app_name == "winmanager"
326        " To handle a bug in the winmanager plugin, add a space at the
327        " last line
328        call setline('$', ' ')
329    endif
330
331    if s:tlist_brief_help
332        " Add the brief help
333        call append(0, '" Press ? to display help text')
334    else
335        " Add the extensive help
336        call append(0, '" <enter> : Jump to tag definition')
337        call append(1, '" o : Jump to tag definition in new window')
338        call append(2, '" p : Preview the tag definition')
339        call append(3, '" <space> : Display tag prototype')
340        call append(4, '" u : Update tag list')
341        call append(5, '" s : Select sort field')
342        call append(6, '" d : Remove file from taglist')
343        call append(7, '" x : Zoom-out/Zoom-in taglist window')
344        call append(8, '" + : Open a fold')
345        call append(9, '" - : Close a fold')
346        call append(10, '" * : Open all folds')
347        call append(11, '" = : Close all folds')
348        call append(12, '" [[ : Move to the start of previous file')
349        call append(13, '" ]] : Move to the start of next file')
350        call append(14, '" q : Close the taglist window')
351        call append(15, '" ? : Remove help text')
352    endif
353endfunction
354
355" Tlist_Toggle_Help_Text()
356" Toggle taglist plugin help text between the full version and the brief
357" version
358function! s:Tlist_Toggle_Help_Text()
359    if g:Tlist_Compact_Format
360        " In compact display mode, do not display help
361        return
362    endif
363
364    " Include the empty line displayed after the help text
365    let brief_help_size = 1
366    let full_help_size = 16
367
368    setlocal modifiable
369
370    " Set report option to a huge value to prevent informational messages
371    " while deleting the lines
372    let old_report = &report
373    set report=99999
374
375    " Remove the currently highlighted tag. Otherwise, the help text
376    " might be highlighted by mistake
377    match none
378
379    " Toggle between brief and full help text
380    if s:tlist_brief_help
381        let s:tlist_brief_help = 0
382
383        " Remove the previous help
384        exe '1,' . brief_help_size . ' delete _'
385
386        " Adjust the start/end line numbers for the files
387        call s:Tlist_Update_Line_Offsets(0, 1, full_help_size - brief_help_size)
388    else
389        let s:tlist_brief_help = 1
390
391        " Remove the previous help
392        exe '1,' . full_help_size . ' delete _'
393
394        " Adjust the start/end line numbers for the files
395        call s:Tlist_Update_Line_Offsets(0, 0, full_help_size - brief_help_size)
396    endif
397
398    call s:Tlist_Display_Help()
399
400    " Restore the report option
401    let &report = old_report
402
403    setlocal nomodifiable
404endfunction
405
406" Tlist_Warning_Msg()
407" Display a message using WarningMsg highlight group
408function! s:Tlist_Warning_Msg(msg)
409    echohl WarningMsg
410    echomsg a:msg
411    echohl None
412endfunction
413
414" Tlist_Get_File_Index()
415" Return the index of the specified filename
416function! s:Tlist_Get_File_Index(fname)
417    let i = 0
418
419    " Do a linear search
420    while i < s:tlist_file_count
421        if s:tlist_{i}_filename == a:fname
422            return i
423        endif
424        let i = i + 1
425    endwhile
426
427    return -1
428endfunction
429
430" Tlist_Get_File_Index_By_Linenum()
431" Return the index of the filename present in the specified line number
432" Line number refers to the line number in the taglist window
433function! s:Tlist_Get_File_Index_By_Linenum(lnum)
434    let i = 0
435
436    " TODO: Convert this to a binary search
437    while i < s:tlist_file_count
438        if a:lnum >= s:tlist_{i}_start && a:lnum <= s:tlist_{i}_end
439            return i
440        endif
441        let i = i + 1
442    endwhile
443
444    return -1
445endfunction
446
447" Tlist_Skip_File()
448" Check whether tag listing is supported for the specified file
449function! s:Tlist_Skip_File(filename, ftype)
450    " Skip buffers with filetype not set
451    if a:ftype == ''
452        return 1
453    endif
454
455    " Skip files which are not supported by exuberant ctags
456    " First check whether default settings for this filetype are available.
457    " If it is not available, then check whether user specified settings are
458    " available. If both are not available, then don't list the tags for this
459    " filetype
460    let var = 's:tlist_def_' . a:ftype . '_settings'
461    if !exists(var)
462        let var = 'g:tlist_' . a:ftype . '_settings'
463        if !exists(var)
464            return 1
465        endif
466    endif
467
468    " Skip buffers with no names
469    if a:filename == ''
470        return 1
471    endif
472
473    " Skip files which are not readable or files which are not yet stored
474    " to the disk
475    if !filereadable(a:filename)
476        return 1
477    endif
478
479    return 0
480endfunction
481
482" Tlist_FileType_Init
483" Initialize the ctags arguments and tag variable for the specified
484" file type
485function! s:Tlist_FileType_Init(ftype)
486    " If the user didn't specify any settings, then use the default
487    " ctags args. Otherwise, use the settings specified by the user
488    let var = 'g:tlist_' . a:ftype . '_settings'
489    if exists(var)
490        " User specified ctags arguments
491        let settings = {var} . ';'
492    else
493        " Default ctags arguments
494        let var = 's:tlist_def_' . a:ftype . '_settings'
495        if !exists(var)
496            " No default settings for this file type. This filetype is
497            " not supported
498            return 0
499        endif
500        let settings = s:tlist_def_{a:ftype}_settings . ';'
501    endif
502
503    let msg = 'Taglist: Invalid ctags option setting - ' . settings
504
505    " Format of the option that specifies the filetype and ctags arugments:
506    "
507    "       <language_name>;flag1:name1;flag2:name2;flag3:name3
508    "
509
510    " Extract the file type to pass to ctags. This may be different from the
511    " file type detected by Vim
512    let pos = stridx(settings, ';')
513    if pos == -1
514        call s:Tlist_Warning_Msg(msg)
515        return 0
516    endif
517    let ctags_ftype = strpart(settings, 0, pos)
518    if ctags_ftype == ''
519        call s:Tlist_Warning_Msg(msg)
520        return 0
521    endif
522    " Make sure a valid filetype is supplied. If the user didn't specify a
523    " valid filetype, then the ctags option settings may be treated as the
524    " filetype
525    if ctags_ftype =~ ':'
526        call s:Tlist_Warning_Msg(msg)
527        return 0
528    endif
529
530    " Remove the file type from settings
531    let settings = strpart(settings, pos + 1)
532    if settings == ''
533        call s:Tlist_Warning_Msg(msg)
534        return 0
535    endif
536
537    " Process all the specified ctags flags. The format is
538    " flag1:name1;flag2:name2;flag3:name3
539    let ctags_flags = ''
540    let cnt = 0
541    while settings != ''
542        " Extract the flag
543        let pos = stridx(settings, ':')
544        if pos == -1
545            call s:Tlist_Warning_Msg(msg)
546            return 0
547        endif
548        let flag = strpart(settings, 0, pos)
549        if flag == ''
550            call s:Tlist_Warning_Msg(msg)
551            return 0
552        endif
553        " Remove the flag from settings
554        let settings = strpart(settings, pos + 1)
555
556        " Extract the tag type name
557        let pos = stridx(settings, ';')
558        if pos == -1
559            call s:Tlist_Warning_Msg(msg)
560            return 0
561        endif
562        let name = strpart(settings, 0, pos)
563        if name == ''
564            call s:Tlist_Warning_Msg(msg)
565            return 0
566        endif
567        let settings = strpart(settings, pos + 1)
568
569        let cnt = cnt + 1
570
571        let s:tlist_{a:ftype}_{cnt}_name = flag
572        let s:tlist_{a:ftype}_{cnt}_fullname = name
573        let ctags_flags = ctags_flags . flag
574    endwhile
575
576    let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype .
577                            \ ' --' . ctags_ftype . '-types=' . ctags_flags
578    let s:tlist_{a:ftype}_count = cnt
579    let s:tlist_{a:ftype}_ctags_flags = ctags_flags
580
581    " Save the filetype name
582    let s:tlist_ftype_{s:tlist_ftype_count}_name = a:ftype
583    let s:tlist_ftype_count = s:tlist_ftype_count + 1
584
585    return 1
586endfunction
587
588" Tlist_Discard_TagInfo
589" Discard the stored tag information for a file
590function! s:Tlist_Discard_TagInfo(fidx)
591    let ftype = s:tlist_{a:fidx}_filetype
592
593    " Discard information about the tags defined in the file
594    let i = 1
595    while i <= s:tlist_{a:fidx}_tag_count
596        unlet! s:tlist_{a:fidx}_tag_{i}
597        let i = i + 1
598    endwhile
599
600    let s:tlist_{a:fidx}_tag_count = 0
601
602    " Discard information about tags groups by their type
603    let i = 1
604    while i <= s:tlist_{ftype}_count
605        let ttype = s:tlist_{ftype}_{i}_name
606        if s:tlist_{a:fidx}_{ttype} != ''
607            let s:tlist_{a:fidx}_{ttype} = ''
608            let s:tlist_{a:fidx}_{ttype}_start = 0
609            let cnt = s:tlist_{a:fidx}_{ttype}_count
610            let s:tlist_{a:fidx}_{ttype}_count = 0
611            let j = 1
612            while j <= cnt
613                unlet! s:tlist_{a:fidx}_{ttype}_{j}
614                let j = j + 1
615            endwhile
616        endif
617        let i = i + 1
618    endwhile
619endfunction
620
621" Tlist_Update_Line_Offsets
622" Update the line offsets for tags for files starting from start_idx
623" and displayed in the taglist window by the specified offset
624function! s:Tlist_Update_Line_Offsets(start_idx, increment, offset)
625    let i = a:start_idx
626
627    while i < s:tlist_file_count
628        if s:tlist_{i}_visible
629            " Update the start/end line number only if the file is visible
630            if a:increment
631                let s:tlist_{i}_start = s:tlist_{i}_start + a:offset
632                let s:tlist_{i}_end = s:tlist_{i}_end + a:offset
633            else
634                let s:tlist_{i}_start = s:tlist_{i}_start - a:offset
635                let s:tlist_{i}_end = s:tlist_{i}_end - a:offset
636            endif
637        endif
638        let i = i + 1
639    endwhile
640endfunction
641
642" Tlist_Discard_FileInfo
643" Discard the stored information for a file
644function! s:Tlist_Discard_FileInfo(fidx)
645    call s:Tlist_Discard_TagInfo(a:fidx)
646
647    let ftype = s:tlist_{a:fidx}_filetype
648
649    let i = 1
650    while i <= s:tlist_{ftype}_count
651        let ttype = s:tlist_{ftype}_{i}_name
652        unlet! s:tlist_{a:fidx}_{ttype}
653        unlet! s:tlist_{a:fidx}_{ttype}_start
654        unlet! s:tlist_{a:fidx}_{ttype}_count
655        let i = i + 1
656    endwhile
657
658    unlet! s:tlist_{a:fidx}_filename
659    unlet! s:tlist_{a:fidx}_sort_type
660    unlet! s:tlist_{a:fidx}_filetype
661    unlet! s:tlist_{a:fidx}_mtime
662    unlet! s:tlist_{a:fidx}_start
663    unlet! s:tlist_{a:fidx}_end
664    unlet! s:tlist_{a:fidx}_valid
665    unlet! s:tlist_{a:fidx}_visible
666    unlet! s:tlist_{a:fidx}_tag_count
667endfunction
668
669" Tlist_Remove_File_From_Display
670" Remove the specified file from display
671function! s:Tlist_Remove_File_From_Display(fidx)
672    " Remove the tags displayed for the specified file from the window
673    let start = s:tlist_{a:fidx}_start
674    " Include the empty line after the last line also
675    if g:Tlist_Compact_Format
676        let end = s:tlist_{a:fidx}_end
677    else
678        let end = s:tlist_{a:fidx}_end + 1
679    endif
680
681    setlocal modifiable
682
683    exe 'silent! ' . start . ',' . end . 'delete _'
684
685    setlocal nomodifiable
686
687    " Correct the start and end line offsets for all the files following
688    " this file, as the tags for this file are removed
689    call s:Tlist_Update_Line_Offsets(a:fidx + 1, 0, end - start + 1)
690endfunction
691
692" Tlist_Remove_File
693" Remove the file under the cursor or the specified file index
694function! s:Tlist_Remove_File(file_idx)
695    let fidx = a:file_idx
696
697    if fidx == -1
698        let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
699        if fidx == -1
700            return
701        endif
702    endif
703
704    call s:Tlist_Remove_File_From_Display(fidx)
705
706    call s:Tlist_Discard_FileInfo(fidx)
707
708    " Shift all the file variables by one index
709    let i = fidx + 1
710
711    while i < s:tlist_file_count
712        let j = i - 1
713
714        let s:tlist_{j}_filename = s:tlist_{i}_filename
715        let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type
716        let s:tlist_{j}_filetype = s:tlist_{i}_filetype
717        let s:tlist_{j}_mtime = s:tlist_{i}_mtime
718        let s:tlist_{j}_start = s:tlist_{i}_start
719        let s:tlist_{j}_end = s:tlist_{i}_end
720        let s:tlist_{j}_valid = s:tlist_{i}_valid
721        let s:tlist_{j}_visible = s:tlist_{i}_visible
722        let s:tlist_{j}_tag_count = s:tlist_{i}_tag_count
723
724        let k = 1
725        while k <= s:tlist_{j}_tag_count
726            let s:tlist_{j}_tag_{k} = s:tlist_{i}_tag_{k}
727            let k = k + 1
728        endwhile
729
730        let ftype = s:tlist_{i}_filetype
731
732        let k = 1
733        while k <= s:tlist_{ftype}_count
734            let ttype = s:tlist_{ftype}_{k}_name
735            let s:tlist_{j}_{ttype} = s:tlist_{i}_{ttype}
736            let s:tlist_{j}_{ttype}_start = s:tlist_{i}_{ttype}_start
737            let s:tlist_{j}_{ttype}_count = s:tlist_{i}_{ttype}_count
738            if s:tlist_{j}_{ttype} != ''
739                let l = 1
740                while l <= s:tlist_{j}_{ttype}_count
741                    let s:tlist_{j}_{ttype}_{l} = s:tlist_{i}_{ttype}_{l}
742                    let l = l + 1
743                endwhile
744            endif
745            let k = k + 1
746        endwhile
747
748        " As the file and tag information is copied to the new index,
749        " discard the previous information
750        call s:Tlist_Discard_FileInfo(i)
751
752        let i = i + 1
753    endwhile
754
755    " Reduce the number of files displayed
756    let s:tlist_file_count = s:tlist_file_count - 1
757endfunction
758
759
760" Tlist_Open_Window
761" Create a new taglist window. If it is already open, jump to it
762function! s:Tlist_Open_Window()
763    " If used with winmanager don't open windows. Winmanager will handle
764    " the window/buffer management
765    if s:tlist_app_name == "winmanager"
766        return
767    endif
768
769    " If the window is open, jump to it
770    let winnum = bufwinnr(g:TagList_title)
771    if winnum != -1
772        " Jump to the existing window
773        if winnr() != winnum
774            exe winnum . 'wincmd w'
775        endif
776        return
777    endif
778
779    " Create a new window. If user prefers a horizontal window, then open
780    " a horizontally split window. Otherwise open a vertically split
781    " window
782    if g:Tlist_Use_Horiz_Window
783        " Open a horizontally split window
784        let win_dir = 'botright'
785        " Horizontal window height
786        let win_size = g:Tlist_WinHeight
787    else
788        " Open a horizontally split window. Increase the window size, if
789        " needed, to accomodate the new window
790        if g:Tlist_Inc_Winwidth &&
791                    \ &columns < (80 + g:Tlist_WinWidth)
792            " one extra column is needed to include the vertical split
793            let &columns= &columns + (g:Tlist_WinWidth + 1)
794            let s:tlist_winsize_chgd = 1
795        else
796            let s:tlist_winsize_chgd = 0
797        endif
798
799        if g:Tlist_Use_Right_Window
800            " Open the window at the rightmost place
801            let win_dir = 'botright vertical'
802        else
803            " Open the window at the leftmost place
804            let win_dir = 'topleft vertical'
805        endif
806        let win_size = g:Tlist_WinWidth
807    endif
808
809    " If the tag listing temporary buffer already exists, then reuse it.
810    " Otherwise create a new buffer
811    let bufnum = bufnr(g:TagList_title)
812    if bufnum == -1
813        " Create a new buffer
814        let wcmd = g:TagList_title
815    else
816        " Edit the existing buffer
817        let wcmd = '+buffer' . bufnum
818    endif
819
820    " Create the taglist window
821    exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd
822endfunction
823
824" Tlist_Zoom_Window
825" Zoom (maximize/minimize) the taglist window
826function! s:Tlist_Zoom_Window()
827    if s:tlist_win_maximized
828        " Restore the window back to the previous size
829        if g:Tlist_Use_Horiz_Window
830            exe 'resize ' . g:Tlist_WinHeight
831        else
832            exe 'vert resize ' . g:Tlist_WinWidth
833        endif
834        let s:tlist_win_maximized = 0
835    else
836        " Set the window size to the maximum possible without closing other
837        " windows
838        if g:Tlist_Use_Horiz_Window
839            resize
840        else
841            vert resize
842        endif
843        let s:tlist_win_maximized = 1
844    endif
845endfunction
846
847" Tlist_Init_Window
848" Set the default options for the taglist window
849function! s:Tlist_Init_Window()
850    " Define taglist window element highlighting
851    if has('syntax')
852        syntax match TagListComment '^" .*'
853        syntax match TagListFileName '^[^" ].*$'
854        syntax match TagListTitle '^  \S.*$'
855        syntax match TagListTagScope  '\s\[.\{-\}\]$'
856
857        " Define the highlighting only if colors are supported
858        if has('gui_running') || &t_Co > 2
859            " Colors to highlight various taglist window elements
860            " If user defined highlighting group exists, then use them.
861            " Otherwise, use default highlight groups.
862            if hlexists('MyTagListTagName')
863                highlight link TagListTagName MyTagListTagName
864            else
865                highlight link TagListTagName Search
866            endif
867            " Colors to highlight comments and titles
868            if hlexists('MyTagListComment')
869                highlight link TagListComment MyTagListComment
870            else
871                highlight clear TagListComment
872                highlight link TagListComment Comment
873            endif
874            if hlexists('MyTagListTitle')
875                highlight link TagListTitle MyTagListTitle
876            else
877                highlight clear TagListTitle
878                highlight link TagListTitle Title
879            endif
880            if hlexists('MyTagListFileName')
881                highlight link TagListFileName MyTagListFileName
882            else
883                highlight clear TagListFileName
884                highlight link TagListFileName LineNr
885            endif
886            if hlexists('MyTagListTagScope')
887                highlight link TagListTagScope MyTagListTagScope
888            else
889                highlight clear TagListTagScope
890                highlight link TagListTagScope Identifier
891            endif
892        else
893            highlight TagListTagName term=reverse cterm=reverse
894        endif
895    endif
896
897    " Folding related settings
898    if has('folding')
899        setlocal foldenable
900        setlocal foldminlines=0
901        setlocal foldmethod=manual
902        if g:Tlist_Enable_Fold_Column
903            setlocal foldcolumn=3
904        else
905            setlocal foldcolumn=0
906        endif
907        setlocal foldtext=v:folddashes.getline(v:foldstart)
908    endif
909
910    if s:tlist_app_name != "winmanager"
911        " Mark buffer as scratch
912        silent! setlocal buftype=nofile
913
914        if s:tlist_app_name == "none"
915
916            silent! setlocal bufhidden=delete
917
918        endif
919
920        silent! setlocal noswapfile
921        " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted
922        " buffers. So if the taglist buffer is unlisted, multiple taglist
923        " windows will be opened. This bug is fixed in Vim 6.1 and above
924        if v:version >= 601
925            silent! setlocal nobuflisted
926        endif
927    endif
928
929    silent! setlocal nowrap
930
931    " If the 'number' option is set in the source window, it will affect the
932    " taglist window. So forcefully disable 'number' option for the taglist
933    " window
934    silent! setlocal nonumber
935
936    " Create buffer local mappings for jumping to the tags and sorting the list
937    nnoremap <buffer> <silent> <CR> :call <SID>Tlist_Jump_To_Tag(0)<CR>
938    nnoremap <buffer> <silent> o :call <SID>Tlist_Jump_To_Tag(1)<CR>
939    nnoremap <buffer> <silent> p :call <SID>Tlist_Jump_To_Tag(2)<CR>
940    nnoremap <buffer> <silent> <2-LeftMouse> :call <SID>Tlist_Jump_To_Tag(0)<CR>
941    nnoremap <buffer> <silent> s :call <SID>Tlist_Change_Sort()<CR>
942    nnoremap <buffer> <silent> + :silent! foldopen<CR>
943    nnoremap <buffer> <silent> - :silent! foldclose<CR>
944    nnoremap <buffer> <silent> * :silent! %foldopen!<CR>
945    nnoremap <buffer> <silent> = :silent! %foldclose<CR>
946    nnoremap <buffer> <silent> <kPlus> :silent! foldopen<CR>
947    nnoremap <buffer> <silent> <kMinus> :silent! foldclose<CR>
948    nnoremap <buffer> <silent> <kMultiply> :silent! %foldopen!<CR>
949    nnoremap <buffer> <silent> <Space> :call <SID>Tlist_Show_Tag_Prototype()<CR>
950    nnoremap <buffer> <silent> u :call <SID>Tlist_Update_Window()<CR>
951    nnoremap <buffer> <silent> d :call <SID>Tlist_Remove_File(-1)<CR>
952    nnoremap <buffer> <silent> x :call <SID>Tlist_Zoom_Window()<CR>
953    nnoremap <buffer> <silent> [[ :call <SID>Tlist_Move_To_File(-1)<CR>
954    nnoremap <buffer> <silent> ]] :call <SID>Tlist_Move_To_File(1)<CR>
955    nnoremap <buffer> <silent> ? :call <SID>Tlist_Toggle_Help_Text()<CR>
956    nnoremap <buffer> <silent> q :close<CR>
957
958    " Insert mode mappings
959    imap <buffer> <silent> <CR>    <C-b>:call <SID>Tlist_Jump_To_Tag(0)<CR>
960    " Windows needs return
961    imap <buffer> <silent> <Return> <C-b>:call <SID>Tlist_Jump_To_Tag(0)<CR>
962    inoremap <buffer> <silent> o        <C-b>:call <SID>Tlist_Jump_To_Tag(1)<CR>
963    inoremap <buffer> <silent> p        <C-b>:call <SID>Tlist_Jump_To_Tag(2)<CR>
964    imap <buffer> <silent> <2-LeftMouse> <C-b>:call
965                                            \ <SID>Tlist_Jump_To_Tag(0)<CR>
966    inoremap <buffer> <silent> s        <C-b>:call <SID>Tlist_Change_Sort()<CR>
967    inoremap <buffer> <silent> +             <C-b>:silent! foldopen<CR>
968    inoremap <buffer> <silent> -             <C-b>:silent! foldclose<CR>
969    inoremap <buffer> <silent> *             <C-b>:silent! %foldopen!<CR>
970    inoremap <buffer> <silent> =             <C-b>:silent! %foldclose<CR>
971    inoremap <buffer> <silent> <kPlus>       <C-b>:silent! foldopen<CR>
972    inoremap <buffer> <silent> <kMinus>      <C-b>:silent! foldclose<CR>
973    inoremap <buffer> <silent> <kMultiply>   <C-b>:silent! %foldopen!<CR>
974    inoremap <buffer> <silent> <Space>       <C-b>:call
975                                    \ <SID>Tlist_Show_Tag_Prototype()<CR>
976    inoremap <buffer> <silent> u    <C-b>:call <SID>Tlist_Update_Window()<CR>
977    inoremap <buffer> <silent> d    <C-b>:call <SID>Tlist_Remove_File(-1)<CR>
978    inoremap <buffer> <silent> x    <C-b>:call <SID>Tlist_Zoom_Window()<CR>
979    inoremap <buffer> <silent> [[   <C-b>:call <SID>Tlist_Move_To_File(-1)<CR>
980    inoremap <buffer> <silent> ]]   <C-b>:call <SID>Tlist_Move_To_File(1)<CR>
981    inoremap <buffer> <silent> ?    <C-b>:call <SID>Tlist_Toggle_Help_Text()<CR>
982    inoremap <buffer> <silent> q    <C-b>:close<CR>
983
984    " Map single left mouse click if the user wants this functionality
985    if g:Tlist_Use_SingleClick
986    nnoremap <silent> <LeftMouse> <LeftMouse>:if bufname("%") =~ "__Tag_List__"
987                        \ <bar> call <SID>Tlist_Jump_To_Tag(0) <bar> endif <CR>
988    endif
989
990    " Define the taglist autocommands
991    augroup TagListAutoCmds
992        autocmd!
993        " Display the tag prototype for the tag under the cursor.
994        autocmd CursorHold __Tag_List__ call s:Tlist_Show_Tag_Prototype()
995        " Highlight the current tag
996        autocmd CursorHold * silent call <SID>Tlist_Highlight_Tag(
997                                \ fnamemodify(bufname('%'), ':p'), line('.'), 1)
998        " Adjust the Vim window width when taglist window is closed
999        autocmd BufUnload __Tag_List__ call <SID>Tlist_Post_Close_Cleanup()
1000        " Close the fold for this buffer when it's not visible in any window
1001        autocmd BufWinLeave * silent call <SID>Tlist_Update_File_Display(
1002                                \ fnamemodify(expand('<afile>'), ':p'), 1)
1003        " Remove the file from the list when it's buffer is deleted
1004        autocmd BufDelete * silent call <SID>Tlist_Update_File_Display(
1005                                \ fnamemodify(expand('<afile>'), ':p'), 2)
1006        " Exit Vim itself if only the taglist window is present (optional)
1007        autocmd BufEnter __Tag_List__ call <SID>Tlist_Check_Only_Window()
1008        if s:tlist_app_name != "winmanager" && !g:Tlist_Process_File_Always
1009            " Auto refresh the taglist window
1010            autocmd BufEnter * call <SID>Tlist_Refresh()
1011        endif
1012    augroup end
1013endfunction
1014
1015" Tlist_Refresh_Window
1016" Display the tags for all the files in the taglist window
1017function! s:Tlist_Refresh_Window()
1018    " Set report option to a huge value to prevent informational messages
1019    " while deleting the lines
1020    let old_report = &report
1021    set report=99999
1022
1023    " Mark the buffer as modifiable
1024    setlocal modifiable
1025
1026    " Delete the contents of the buffer to the black-hole register
1027    silent! %delete _
1028
1029    if g:Tlist_Compact_Format == 0
1030        " Display help in non-compact mode
1031        call s:Tlist_Display_Help()
1032    endif
1033
1034    " Mark the buffer as not modifiable
1035    setlocal nomodifiable
1036
1037    " Restore the report option
1038    let &report = old_report
1039
1040    " List all the tags for the previously processed files
1041    let i = 0
1042    while i < s:tlist_file_count
1043        " Mark the file as not visible, so that Tlist_Explore_File() will
1044        " display the tags for this file and mark the file as visible
1045        let s:tlist_{i}_visible = 0
1046        call s:Tlist_Explore_File(s:tlist_{i}_filename, s:tlist_{i}_filetype)
1047        let i = i + 1
1048    endwhile
1049
1050    " If Tlist_File_Fold_Auto_Close option is set, then close all the
1051    " folds
1052    if g:Tlist_File_Fold_Auto_Close
1053        if has('folding')
1054            " Close all the folds
1055            silent! %foldclose
1056        endif
1057    endif
1058endfunction
1059
1060" Tlist_Post_Close_Cleanup()
1061" Close the taglist window and adjust the Vim window width
1062function! s:Tlist_Post_Close_Cleanup()
1063    " Mark all the files as not visible
1064    let i = 0
1065    while i < s:tlist_file_count
1066        let s:tlist_{i}_visible = 0
1067        let i = i + 1
1068    endwhile
1069
1070    " Remove the taglist autocommands
1071    silent! autocmd! TagListAutoCmds
1072
1073    " Clear all the highlights
1074    match none
1075
1076    if has('syntax')
1077        silent! syntax clear TagListTitle
1078        silent! syntax clear TagListComment
1079        silent! syntax clear TagListTagScope
1080    endif
1081
1082    " Remove the left mouse click mapping if it was setup initially
1083    if g:Tlist_Use_SingleClick
1084        if hasmapto('<LeftMouse>')
1085            nunmap <LeftMouse>
1086        endif
1087    endif
1088
1089    if s:tlist_app_name != "winmanager"
1090    if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 ||
1091                \ s:tlist_winsize_chgd == 0 ||
1092                \ &columns < (80 + g:Tlist_WinWidth)
1093        " No need to adjust window width if using horizontally split taglist
1094        " window or if columns is less than 101 or if the user chose not to
1095        " adjust the window width
1096    else
1097        " Adjust the Vim window width
1098        let &columns= &columns - (g:Tlist_WinWidth + 1)
1099    endif
1100    endif
1101
1102    " Reset taglist state variables
1103    if s:tlist_app_name == "winmanager"
1104        let s:tlist_app_name = "none"
1105        let s:tlist_window_initialized = 0
1106    endif
1107endfunction
1108
1109" Tlist_Check_Only_Window
1110" Check if only the taglist window is opened currently. If the
1111" Tlist_Exit_OnlyWindow variable is set, then close the taglist window
1112function! s:Tlist_Check_Only_Window()
1113    if g:Tlist_Exit_OnlyWindow
1114        if winbufnr(2) == -1 && bufname(winbufnr(1)) == g:TagList_title
1115            " If only the taglist window is currently open, then the buffer
1116            " number associated with window 2 will be -1.
1117            quit
1118        endif
1119    endif
1120endfunction
1121
1122" Tlist_Explore_File()
1123" List the tags defined in the specified file in a Vim window
1124function! s:Tlist_Explore_File(filename, ftype)
1125    " First check whether the file already exists
1126    let fidx = s:Tlist_Get_File_Index(a:filename)
1127    if fidx != -1
1128        let file_exists = 1
1129    else
1130        let file_exists = 0
1131    endif
1132
1133    if file_exists && s:tlist_{fidx}_visible
1134        " Check whether the file tags are currently valid
1135        if s:tlist_{fidx}_valid
1136            " Goto the first line in the file
1137            exe s:tlist_{fidx}_start
1138
1139            " If the line is inside a fold, open the fold
1140            if has('folding')
1141                exe "silent! " . s:tlist_{fidx}_start . "," .
1142                            \ s:tlist_{fidx}_end . "foldopen!"
1143            endif
1144            return
1145        endif
1146
1147        " Discard and remove the tags for this file from display
1148        call s:Tlist_Discard_TagInfo(fidx)
1149        call s:Tlist_Remove_File_From_Display(fidx)
1150    endif
1151
1152    " Process and generate a list of tags defined in the file
1153    if !file_exists || !s:tlist_{fidx}_valid
1154        let ret_fidx = s:Tlist_Process_File(a:filename, a:ftype)
1155        if ret_fidx == -1
1156            return
1157        endif
1158        let fidx = ret_fidx
1159    endif
1160
1161    " Set report option to a huge value to prevent informational messages
1162    " while adding lines to the taglist window
1163    let old_report = &report
1164    set report=99999
1165
1166    " Mark the buffer as modifiable
1167    setlocal modifiable
1168
1169    " Add new files to the end of the window. For existing files, add them at
1170    " the same line where they were previously present. If the file is not
1171    " visible, then add it at the end
1172    if s:tlist_{fidx}_start == 0 || !s:tlist_{fidx}_visible
1173        if g:Tlist_Compact_Format
1174            let s:tlist_{fidx}_start = line('$')
1175        else
1176            let s:tlist_{fidx}_start = line('$') + 1
1177        endif
1178    endif
1179
1180    let s:tlist_{fidx}_visible = 1
1181
1182    " Goto the line where this file should be placed
1183    if g:Tlist_Compact_Format
1184        exe s:tlist_{fidx}_start
1185    else
1186        exe (s:tlist_{fidx}_start - 1)
1187    endif
1188
1189    let txt = fnamemodify(s:tlist_{fidx}_filename, ':t') . ' (' .
1190                \ fnamemodify(s:tlist_{fidx}_filename, ':p:h') . ')'
1191    if g:Tlist_Compact_Format == 0
1192        silent! put =txt
1193    else
1194        silent! put! =txt
1195        " Move to the next line
1196        exe line('.') + 1
1197    endif
1198    let file_start = s:tlist_{fidx}_start
1199
1200    " Add the tag names grouped by tag type to the buffer with a title
1201    let i = 1
1202    while i <= s:tlist_{a:ftype}_count
1203        let ttype = s:tlist_{a:ftype}_{i}_name
1204        " Add the tag type only if there are tags for that type
1205        if s:tlist_{fidx}_{ttype} != ''
1206            let txt = '  ' . s:tlist_{a:ftype}_{i}_fullname
1207            if g:Tlist_Compact_Format == 0
1208                let ttype_start_lnum = line('.') + 1
1209                silent! put =txt
1210            else
1211                let ttype_start_lnum = line('.')
1212                silent! put! =txt
1213            endif
1214            silent! put =s:tlist_{fidx}_{ttype}
1215
1216            if g:Tlist_Compact_Format
1217                exe (line('.') + s:tlist_{fidx}_{ttype}_count)
1218            endif
1219
1220            let s:tlist_{fidx}_{ttype}_start = ttype_start_lnum - file_start
1221
1222            " create a fold for this tag type
1223            if has('folding')
1224                let fold_start = ttype_start_lnum
1225                let fold_end = fold_start + s:tlist_{fidx}_{ttype}_count
1226                exe fold_start . ',' . fold_end  . 'fold'
1227            endif
1228
1229            if g:Tlist_Compact_Format == 0
1230                silent! put =''
1231            endif
1232        endif
1233        let i = i + 1
1234    endwhile
1235
1236    if s:tlist_{fidx}_tag_count == 0
1237        put =''
1238    endif
1239
1240    let s:tlist_{fidx}_end = line('.') - 1
1241
1242    " Create a fold for the entire file
1243    if has('folding')
1244        exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold'
1245        exe 'silent! ' . s:tlist_{fidx}_start . ',' .
1246                                        \ s:tlist_{fidx}_end . 'foldopen!'
1247    endif
1248
1249    " Goto the starting line for this file,
1250    exe s:tlist_{fidx}_start
1251
1252    if s:tlist_app_name == "winmanager"
1253        " To handle a bug in the winmanager plugin, add a space at the
1254        " last line
1255        call setline('$', ' ')
1256    endif
1257
1258    " Mark the buffer as not modifiable
1259    setlocal nomodifiable
1260
1261    " Restore the report option
1262    let &report = old_report
1263
1264    " Update the start and end line numbers for all the files following this
1265    " file
1266    let start = s:tlist_{fidx}_start
1267    " include the empty line after the last line
1268    if g:Tlist_Compact_Format
1269        let end = s:tlist_{fidx}_end
1270    else
1271        let end = s:tlist_{fidx}_end + 1
1272    endif
1273    call s:Tlist_Update_Line_Offsets(fidx + 1, 1, end - start + 1)
1274
1275    return
1276endfunction
1277
1278" Tlist_Init_File
1279" Initialize the variables for a new file
1280function! s:Tlist_Init_File(filename, ftype)
1281    " Add new files at the end of the list
1282    let fidx = s:tlist_file_count
1283    let s:tlist_file_count = s:tlist_file_count + 1
1284
1285    " Initialize the file variables
1286    let s:tlist_{fidx}_filename = a:filename
1287    let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type
1288    let s:tlist_{fidx}_filetype = a:ftype
1289    let s:tlist_{fidx}_mtime = -1
1290    let s:tlist_{fidx}_start = 0
1291    let s:tlist_{fidx}_end = 0
1292    let s:tlist_{fidx}_valid = 0
1293    let s:tlist_{fidx}_visible = 0
1294    let s:tlist_{fidx}_tag_count = 0
1295
1296    " Initialize the tag type variables
1297    let i = 1
1298    while i <= s:tlist_{a:ftype}_count
1299        let ttype = s:tlist_{a:ftype}_{i}_name
1300        let s:tlist_{fidx}_{ttype} = ''
1301        let s:tlist_{fidx}_{ttype}_start = 0
1302        let s:tlist_{fidx}_{ttype}_count = 0
1303        let i = i + 1
1304    endwhile
1305
1306    return fidx
1307endfunction
1308
1309" Tlist_Process_File
1310" Get the list of tags defined in the specified file and store them
1311" in Vim variables. Returns the file index where the tags are stored.
1312function! s:Tlist_Process_File(filename, ftype)
1313    " Check for valid filename and valid filetype
1314    if a:filename == '' || !filereadable(a:filename) || a:ftype == ''
1315        return -1
1316    endif
1317
1318    " If the tag types for this filetype are not yet created, then create
1319    " them now
1320    let var = 's:tlist_' . a:ftype . '_count'
1321    if !exists(var)
1322        if s:Tlist_FileType_Init(a:ftype) == 0
1323            return -1
1324        endif
1325    endif
1326
1327    " If this file is already processed, then use the cached values
1328    let fidx = s:Tlist_Get_File_Index(a:filename)
1329    if fidx == -1
1330        " First time, this file is loaded
1331        let fidx = s:Tlist_Init_File(a:filename, a:ftype)
1332    else
1333        " File was previously processed. Discard the tag information
1334        call s:Tlist_Discard_TagInfo(fidx)
1335    endif
1336
1337    let s:tlist_{fidx}_valid = 1
1338
1339    " Exuberant ctags arguments to generate a tag list
1340    let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks '
1341
1342    " Form the ctags argument depending on the sort type
1343    if s:tlist_{fidx}_sort_type == 'name'
1344        let ctags_args = ctags_args . ' --sort=yes '
1345    else
1346        let ctags_args = ctags_args . ' --sort=no '
1347    endif
1348
1349    " Add the filetype specific arguments
1350    let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args
1351
1352    " Ctags command to produce output with regexp for locating the tags
1353    let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args
1354    let ctags_cmd = ctags_cmd . ' "' . a:filename . '"'
1355
1356    " In Windows 95, if not using cygwin, disable the 'shellslash'
1357    " option. Otherwise, this will cause problems when running the
1358    " ctags command.
1359    if has("win95") && !has("win32unix")
1360        let myshellslash = &shellslash
1361        set noshellslash
1362    endif
1363
1364    " Run ctags and get the tag list
1365    let cmd_output = system(ctags_cmd)
1366
1367    " Restore the value of the 'shellslash' option.
1368    if has("win95") && !has("win32unix")
1369        let &shellslash = myshellslash
1370    endif
1371
1372    " Handle errors
1373    if v:shell_error && cmd_output != ''
1374        let msg = "Taglist: Failed to generate tags for " . a:filename
1375        call s:Tlist_Warning_Msg(msg)
1376        call s:Tlist_Warning_Msg(cmd_output)
1377        return fidx
1378    endif
1379
1380    " No tags for current file
1381    if cmd_output == ''
1382        call s:Tlist_Warning_Msg('Taglist: No tags found for ' . a:filename)
1383        return fidx
1384    endif
1385
1386    " Store the modification time for the file
1387    let s:tlist_{fidx}_mtime = getftime(a:filename)
1388
1389    " Process the ctags output one line at a time. Separate the tag output
1390    " based on the tag type and store it in the tag type variable
1391    " The format of each line in the ctags output is:
1392    "
1393    "     tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields
1394    "
1395    while cmd_output != ''
1396        " Extract one line at a time
1397        let idx = stridx(cmd_output, "\n")
1398        let one_line = strpart(cmd_output, 0, idx)
1399        " Remove the line from the tags output
1400        let cmd_output = strpart(cmd_output, idx + 1)
1401
1402        if one_line == ''
1403            " Line is not in proper tags format
1404            continue
1405        endif
1406
1407        " Extract the tag type
1408        let ttype = s:Tlist_Extract_Tagtype(one_line)
1409
1410        if ttype == ''
1411            " Line is not in proper tags format
1412            continue
1413        endif
1414
1415        " make sure the tag type is supported
1416        if s:tlist_{a:ftype}_ctags_flags !~# ttype
1417            " Tag type is not supported
1418            continue
1419        endif
1420
1421        " Extract the tag name
1422        if g:Tlist_Display_Prototype == 0
1423            let ttxt = '    ' . strpart(one_line, 0, stridx(one_line, "\t"))
1424
1425            " Add the tag scope, if it is available. Tag scope is the last
1426            " field after the 'line:<num>\t' field
1427            if g:Tlist_Display_Tag_Scope     " only if it is selected
1428                let tag_scope = s:Tlist_Extract_Tag_Scope(one_line)
1429                if tag_scope != ''
1430                    let ttxt = ttxt . ' [' . tag_scope . ']'
1431                endif
1432            endif
1433        else
1434            let ttxt = s:Tlist_Extract_Tag_Prototype(one_line)
1435        endif
1436
1437        " Update the count of this tag type
1438        let cnt = s:tlist_{fidx}_{ttype}_count + 1
1439        let s:tlist_{fidx}_{ttype}_count = cnt
1440
1441        " Add this tag to the tag type variable
1442        let s:tlist_{fidx}_{ttype} = s:tlist_{fidx}_{ttype} . ttxt . "\n"
1443
1444        " Update the total tag count
1445        let s:tlist_{fidx}_tag_count = s:tlist_{fidx}_tag_count + 1
1446        " Store the ctags output line and the tagtype count
1447        let s:tlist_{fidx}_tag_{s:tlist_{fidx}_tag_count} =
1448                                    \ cnt . ':' . one_line
1449        " Store the tag output index
1450        let s:tlist_{fidx}_{ttype}_{cnt} = s:tlist_{fidx}_tag_count
1451    endwhile
1452
1453    return fidx
1454endfunction
1455
1456" Tlist_Update_File_Tags
1457" Update the tags for a file (if needed)
1458function! Tlist_Update_File_Tags(filename, ftype)
1459    " If the file doesn't support tag listing, skip it
1460    if s:Tlist_Skip_File(a:filename, a:ftype)
1461        return
1462    endif
1463
1464    " First check whether the file already exists
1465    let fidx = s:Tlist_Get_File_Index(a:filename)
1466
1467    if fidx != -1 && s:tlist_{fidx}_valid
1468        " File exists and the tags are valid
1469        return
1470    endif
1471
1472    " If the taglist window is opened, update it
1473    let winnum = bufwinnr(g:TagList_title)
1474    if winnum == -1
1475        " Taglist window is not present. Just update the taglist
1476        " and return
1477        call s:Tlist_Process_File(a:filename, a:ftype)
1478    else
1479        " Save the current window number
1480        let save_winnr = winnr()
1481
1482        " Goto the taglist window
1483        call s:Tlist_Open_Window()
1484
1485        " Update the taglist window
1486        call s:Tlist_Explore_File(a:filename, a:ftype)
1487
1488        if winnr() != save_winnr
1489            " Go back to the original window
1490            exe save_winnr . 'wincmd w'
1491        endif
1492    endif
1493endfunction
1494
1495" Tlist_Close_Window
1496" Close the taglist window
1497function! s:Tlist_Close_Window()
1498    " Make sure the taglist window exists
1499    let winnum = bufwinnr(g:TagList_title)
1500    if winnum == -1
1501        call s:Tlist_Warning_Msg('Error: Taglist window is not open')
1502        return
1503    endif
1504
1505    if winnr() == winnum
1506        " Already in the taglist window. Close it and return
1507        if winbufnr(2) != -1
1508            " If a window other than the taglist window is open,
1509            " then only close the taglist window.
1510            close
1511        endif
1512    else
1513        " Goto the taglist window, close it and then come back to the
1514        " original window
1515        let curbufnr = bufnr('%')
1516        exe winnum . 'wincmd w'
1517        close
1518        " Need to jump back to the original window only if we are not
1519        " already in that window
1520        let winnum = bufwinnr(curbufnr)
1521        if winnr() != winnum
1522            exe winnum . 'wincmd w'
1523        endif
1524    endif
1525endfunction
1526
1527" Tlist_Toggle_Window()
1528" Open or close a taglist window
1529function! s:Tlist_Toggle_Window()
1530    let curline = line('.')
1531
1532    " If taglist window is open then close it.
1533    let winnum = bufwinnr(g:TagList_title)
1534    if winnum != -1
1535        call s:Tlist_Close_Window()
1536        return
1537    endif
1538
1539    if s:tlist_app_name == "winmanager"
1540        " Taglist plugin is no longer part of the winmanager app
1541        let s:tlist_app_name = "none"
1542    endif
1543
1544    " Get the filename and filetype for the specified buffer
1545    let curbuf_name = fnamemodify(bufname('%'), ':p')
1546    let curbuf_ftype = getbufvar('%', '&filetype')
1547
1548    " Mark the current window as the desired window to open a file
1549    " when a tag is selcted
1550    let w:tlist_file_window = "yes"
1551
1552    " Open the taglist window
1553    call s:Tlist_Open_Window()
1554
1555    " Initialize the taglist window
1556    call s:Tlist_Init_Window()
1557    call s:Tlist_Refresh_Window()
1558
1559    " Add and list the tags for all the buffers in the bufferlist
1560    let i = 1
1561    while i < bufnr('$')
1562        let fname = fnamemodify(bufname(i), ':p')
1563        let ftype = getbufvar(i, '&filetype')
1564        call s:Tlist_Explore_File(fname, ftype)
1565        let i = i + 1
1566    endwhile
1567
1568    " Highlight the current tag
1569    call s:Tlist_Highlight_Tag(curbuf_name, curline, 1)
1570
1571    " Go back to the original window
1572    let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
1573    let s:Tlist_Skip_Refresh = 1
1574    wincmd p
1575    let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
1576endfunction
1577
1578" Tlist_Extract_Tagtype
1579" Extract the tag type from the tag text
1580function! s:Tlist_Extract_Tagtype(tag_txt)
1581    " The tag type is after the tag prototype field. The prototype field
1582    " ends with the /;"\t string. We add 4 at the end to skip the characters
1583    " in this special string..
1584    let start = strridx(a:tag_txt, '/;"' . "\t") + 4
1585    let end = strridx(a:tag_txt, 'line:') - 1
1586    let ttype = strpart(a:tag_txt, start, end - start)
1587
1588    return ttype
1589endfunction
1590
1591" Tlist_Extract_Tag_Prototype
1592" Extract the tag protoype from the tag text
1593function! s:Tlist_Extract_Tag_Prototype(tag_txt)
1594    let start = stridx(a:tag_txt, '/^') + 2
1595    let end = strridx(a:tag_txt, '/;"' . "\t")
1596    " The search patterns for some tag types doesn't end with
1597    " the ;" character
1598    if a:tag_txt[end - 1] == '$'
1599        let end = end -1
1600    endif
1601    let tag_pat = strpart(a:tag_txt, start, end - start)
1602
1603    " Remove all the leading space characters
1604    let tag_pat = substitute(tag_pat, '\s*', '', '')
1605
1606    return tag_pat
1607endfunction
1608
1609" Tlist_Extract_Tag_Scope
1610" Extract the tag scope from the tag text
1611function! s:Tlist_Extract_Tag_Scope(tag_txt)
1612    let start = strridx(a:tag_txt, 'line:')
1613    let end = strridx(a:tag_txt, "\t")
1614    if end <= start
1615        return ''
1616    endif
1617
1618    let tag_scope = strpart(a:tag_txt, end + 1)
1619    let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1)
1620
1621    return tag_scope
1622endfunction
1623
1624" Tlist_Refresh()
1625" Refresh the taglist
1626function! s:Tlist_Refresh()
1627    " If we are entering the buffer from one of the taglist functions, then
1628    " no need to refresh the taglist window again.
1629    if s:Tlist_Skip_Refresh || (s:tlist_app_name == "winmanager")
1630        return
1631    endif
1632
1633    " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help
1634    if &buftype != ''
1635        return
1636    endif
1637
1638    let filename = fnamemodify(bufname('%'), ':p')
1639    let ftype = &filetype
1640
1641    " If the file doesn't support tag listing, skip it
1642    if s:Tlist_Skip_File(filename, ftype)
1643        return
1644    endif
1645
1646    let curline = line('.')
1647
1648    " Make sure the taglist window is open. Otherwise, no need to refresh
1649    let winnum = bufwinnr(g:TagList_title)
1650    if winnum == -1
1651        if g:Tlist_Process_File_Always
1652            call Tlist_Update_File_Tags(filename, ftype)
1653        endif
1654        return
1655    endif
1656
1657    let fidx = s:Tlist_Get_File_Index(filename)
1658    if fidx != -1
1659        let mtime = getftime(filename)
1660        if s:tlist_{fidx}_mtime != mtime
1661            " Invalidate the tags listed for this file
1662            let s:tlist_{fidx}_valid = 0
1663
1664            " Update the taglist window
1665            call Tlist_Update_File_Tags(s:tlist_{fidx}_filename,
1666                        \ s:tlist_{fidx}_filetype)
1667
1668            " Store the new file modification time
1669            let s:tlist_{fidx}_mtime = mtime
1670        endif
1671
1672        " If the tag listing for the current window is already present, no
1673        " need to refresh it
1674        if !g:Tlist_Auto_Highlight_Tag
1675            return
1676        endif
1677
1678        " Highlight the current tag
1679        call s:Tlist_Highlight_Tag(filename, curline, 1)
1680
1681        return
1682    endif
1683
1684    " Save the current window number
1685    let cur_winnr = winnr()
1686
1687    " Goto the taglist window
1688    call s:Tlist_Open_Window()
1689
1690    if !g:Tlist_Auto_Highlight_Tag
1691        " Save the cursor position
1692        let save_line = line('.')
1693        let save_col = col('.')
1694    endif
1695
1696    " Update the taglist window
1697    call s:Tlist_Explore_File(filename, ftype)
1698
1699    " Highlight the current tag
1700    call s:Tlist_Highlight_Tag(filename, curline, 1)
1701
1702    if !g:Tlist_Auto_Highlight_Tag
1703        " Restore the cursor position
1704        call cursor(save_line, save_col)
1705    endif
1706
1707    " Refresh the taglist window
1708    redraw
1709
1710    if s:tlist_app_name != "winmanager"
1711    " Jump back to the original window
1712    exe cur_winnr . 'wincmd w'
1713    endif
1714endfunction
1715
1716" Tlist_Change_Sort()
1717" Change the sort order of the tag listing
1718function! s:Tlist_Change_Sort()
1719    let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
1720    if fidx == -1
1721        return
1722    endif
1723
1724    " Remove the previous highlighting
1725    match none
1726
1727    let sort_type = s:tlist_{fidx}_sort_type
1728
1729    " Toggle the sort order from 'name' to 'order' and vice versa
1730    if sort_type == 'name'
1731        let s:tlist_{fidx}_sort_type = 'order'
1732    else
1733        let s:tlist_{fidx}_sort_type = 'name'
1734    endif
1735
1736    " Save the current line for later restoration
1737    let curline = '\V\^' . getline('.') . '\$'
1738
1739    " Invalidate the tags listed for this file
1740    let s:tlist_{fidx}_valid = 0
1741
1742    call s:Tlist_Explore_File(s:tlist_{fidx}_filename, s:tlist_{fidx}_filetype)
1743
1744    " Go back to the cursor line before the tag list is sorted
1745    call search(curline, 'w')
1746endfunction
1747
1748" Tlist_Update_Tags()
1749" Update taglist for the current buffer by regenerating the tag list
1750" Contributed by WEN Guopeng.
1751function! s:Tlist_Update_Tags()
1752    if winnr() == bufwinnr(g:TagList_title)
1753        " In the taglist window. Update the current file
1754        call s:Tlist_Update_Window()
1755        return
1756    else
1757        " Not in the taglist window. Update the current buffer
1758        let filename = fnamemodify(bufname('%'), ':p')
1759        let fidx = s:Tlist_Get_File_Index(filename)
1760        if fidx != -1
1761            let s:tlist_{fidx}_valid = 0
1762        endif
1763        call Tlist_Update_File_Tags(filename, &filetype)
1764    endif
1765endfunction
1766
1767" Tlist_Update_Window()
1768" Update the window by regenerating the tag list
1769function! s:Tlist_Update_Window()
1770    let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
1771    if fidx == -1
1772        return
1773    endif
1774
1775    " Remove the previous highlighting
1776    match none
1777
1778    " Save the current line for later restoration
1779    let curline = '\V\^' . getline('.') . '\$'
1780
1781    let s:tlist_{fidx}_valid = 0
1782
1783    " Update the taglist window
1784    call s:Tlist_Explore_File(s:tlist_{fidx}_filename, s:tlist_{fidx}_filetype)
1785
1786    " Go back to the tag line before the list is updated
1787    call search(curline, 'w')
1788endfunction
1789
1790" Tlist_Get_Tag_Index()
1791" Return the tag index for the current line
1792function! s:Tlist_Get_Tag_Index(fidx)
1793    let lnum = line('.')
1794    let ftype = s:tlist_{a:fidx}_filetype
1795
1796    " Determine to which tag type the current line number belongs to using the
1797    " tag type start line number and the number of tags in a tag type
1798    let i = 1
1799    while i <= s:tlist_{ftype}_count
1800        let ttype = s:tlist_{ftype}_{i}_name
1801        let start_lnum = s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_start
1802        let end =  start_lnum + s:tlist_{a:fidx}_{ttype}_count
1803        if lnum >= start_lnum && lnum <= end
1804            break
1805        endif
1806        let i = i + 1
1807    endwhile
1808
1809    " Current line doesn't belong to any of the displayed tag types
1810    if i > s:tlist_{ftype}_count
1811        return 0
1812    endif
1813
1814    " Compute the index into the displayed tags for the tag type
1815    let tidx = lnum - start_lnum
1816    if tidx == 0
1817        return 0
1818    endif
1819
1820    " Get the corresponding tag line and return it
1821    return s:tlist_{a:fidx}_{ttype}_{tidx}
1822endfunction
1823
1824" Tlist_Highlight_Tagline
1825" Higlight the current tagline
1826function! s:Tlist_Highlight_Tagline()
1827    " Clear previously selected name
1828    match none
1829
1830    " Highlight the current selected name
1831    if g:Tlist_Display_Prototype == 0
1832        exe 'match TagListTagName /\%' . line('.') . 'l\s\+\zs.*/'
1833    else
1834        exe 'match TagListTagName /\%' . line('.') . 'l.*/'
1835    endif
1836endfunction
1837
1838" Tlist_Jump_To_Tag()
1839" Jump to the location of the current tag
1840" win_ctrl == 0 - Reuse the existing file window
1841" win_ctrl == 1 - Open a new window
1842" win_ctrl == 2 - Preview the tag
1843function! s:Tlist_Jump_To_Tag(win_ctrl)
1844    " Do not process comment lines and empty lines
1845    let curline = getline('.')
1846    if curline =~ '^\s*$' || curline[0] == '"'
1847        return
1848    endif
1849
1850    " If inside a fold, then don't try to jump to the tag
1851    if foldclosed('.') != -1
1852        return
1853    endif
1854
1855    let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
1856    if fidx == -1
1857        return
1858    endif
1859
1860    " Get the tag output for the current tag
1861    let tidx = s:Tlist_Get_Tag_Index(fidx)
1862    if tidx != 0
1863        let mtxt = s:tlist_{fidx}_tag_{tidx}
1864        let start = stridx(mtxt, '/^') + 2
1865        let end = strridx(mtxt, '/;"' . "\t")
1866        if mtxt[end - 1] == '$'
1867            let end = end - 1
1868        endif
1869        let tagpat = '\V\^' . strpart(mtxt, start, end - start) .
1870                                            \ (mtxt[end] == '$' ? '\$' : '')
1871
1872        " Highlight the tagline
1873        call s:Tlist_Highlight_Tagline()
1874    else
1875        " Selected a line which is not a tag name. Just edit the file
1876        let tagpat = ''
1877    endif
1878
1879    call s:Tlist_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat)
1880endfunction
1881
1882" Tlist_Open_File
1883" Open the specified file in either a new window or an existing window
1884" and place the cursor at the specified tag pattern
1885function! s:Tlist_Open_File(win_ctrl, filename, tagpat)
1886    let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
1887    let s:Tlist_Skip_Refresh = 1
1888
1889    if s:tlist_app_name == "winmanager"
1890        " Let the winmanager edit the file
1891        call WinManagerFileEdit(a:filename, a:win_ctrl)
1892    else
1893    " Goto the window containing the file.  If the window is not there, open a
1894    " new window
1895    let winnum = bufwinnr(a:filename)
1896    if winnum == -1
1897        " Locate the previously used window for opening a file
1898        let fwin_num = 0
1899
1900        let i = 1
1901        while winbufnr(i) != -1
1902            if getwinvar(i, 'tlist_file_window') == "yes"
1903                let fwin_num = i
1904                break
1905            endif
1906            let i = i + 1
1907        endwhile
1908
1909        if fwin_num != 0
1910            " Jump to the file window
1911            exe fwin_num . "wincmd w"
1912
1913            " If the user asked to jump to the tag in a new window, then split
1914            " the existing window into two.
1915            if a:win_ctrl == 1
1916                split
1917            endif
1918            exe "edit " . a:filename
1919        else
1920            " Open a new window
1921            if g:Tlist_Use_Horiz_Window
1922                exe 'leftabove split #' . bufnr(a:filename)
1923                " Go to the taglist window to change the window size to the user
1924                " configured value
1925                wincmd p
1926                exe 'resize ' . g:Tlist_WinHeight
1927                " Go back to the file window
1928                wincmd p
1929            else
1930                " Open the file in a window and skip refreshing the taglist
1931                " window
1932                exe 'rightbelow vertical split #' . bufnr(a:filename)
1933                " Go to the taglist window to change the window size to the user
1934                " configured value
1935                wincmd p
1936                exe 'vertical resize ' . g:Tlist_WinWidth
1937                " Go back to the file window
1938                wincmd p
1939            endif
1940            let w:tlist_file_window = "yes"
1941        endif
1942    else
1943        exe winnum . 'wincmd w'
1944
1945        " If the user asked to jump to the tag in a new window, then split the
1946        " existing window into two.
1947        if a:win_ctrl == 1
1948            split
1949        endif
1950    endif
1951    endif
1952
1953    " Jump to the tag
1954    if a:tagpat != ''
1955        silent call search(a:tagpat, 'w')
1956    endif
1957
1958    " Bring the line to the middle of the window
1959    normal! z.
1960
1961    " If the line is inside a fold, open the fold
1962    if has('folding')
1963        if foldlevel('.') != 0
1964            normal! zv
1965        endif
1966    endif
1967
1968    " If the user selects to preview the tag then jump back to the
1969    " taglist window
1970    if a:win_ctrl == 2
1971        " Go back to the taglist window
1972        let winnum = bufwinnr(g:TagList_title)
1973        exe winnum . 'wincmd w'
1974    endif
1975
1976    let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
1977endfunction
1978
1979" Tlist_Show_Tag_Prototype()
1980" Display the prototype of the tag under the cursor
1981function! s:Tlist_Show_Tag_Prototype()
1982    " If we have already display prototype in the tag window, no need to
1983    " display it in the status line
1984    if g:Tlist_Display_Prototype
1985        return
1986    endif
1987
1988    " Clear the previously displayed line
1989    echo
1990
1991    " Do not process comment lines and empty lines
1992    let curline = getline('.')
1993    if curline =~ '^\s*$' || curline[0] == '"'
1994        return
1995    endif
1996
1997    " If inside a fold, then don't display the prototype
1998    if foldclosed('.') != -1
1999        return
2000    endif
2001
2002    " Get the file index
2003    let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
2004    if fidx == -1
2005        return
2006    endif
2007
2008    " Get the tag output line for the current tag
2009    let tidx = s:Tlist_Get_Tag_Index(fidx)
2010    if tidx == 0
2011        return
2012    endif
2013
2014    let mtxt = s:tlist_{fidx}_tag_{tidx}
2015
2016    " Get the tag search pattern and display it
2017    echo s:Tlist_Extract_Tag_Prototype(mtxt)
2018endfunction
2019
2020" Tlist_Find_Tag_text
2021" Find the tag text given the line number in the source window
2022function! s:Tlist_Find_Tag_text(fidx, linenum)
2023    let sort_type = s:tlist_{a:fidx}_sort_type
2024
2025    let left = 1
2026    let right = s:tlist_{a:fidx}_tag_count
2027
2028    if sort_type == 'order'
2029        " Tag list sorted by order, do a binary search comparing the line
2030        " numbers and pick a tag entry that contains the current line and
2031        " highlight it.  The idea behind this function is taken from the
2032        " ctags.vim script (by Alexey Marinichev) available at the Vim online
2033        " website.
2034
2035        " If the current line is the less than the first tag, then no need to
2036        " search
2037        let txt = s:tlist_{a:fidx}_tag_1
2038        let start = strridx(txt, 'line:') + strlen('line:')
2039        let end = strridx(txt, "\t")
2040        if end < start
2041            let first_lnum = strpart(txt, start) + 0
2042        else
2043            let first_lnum = strpart(txt, start, end - start) + 0
2044        endif
2045
2046        if a:linenum < first_lnum
2047            return ""
2048        endif
2049
2050        while left < right
2051            let middle = (right + left + 1) / 2
2052            let txt = s:tlist_{a:fidx}_tag_{middle}
2053
2054            let start = strridx(txt, 'line:') + strlen('line:')
2055            let end = strridx(txt, "\t")
2056            if end < start
2057                let middle_lnum = strpart(txt, start) + 0
2058            else
2059                let middle_lnum = strpart(txt, start, end - start) + 0
2060            endif
2061
2062            if middle_lnum == a:linenum
2063                let left = middle
2064                break
2065            endif
2066
2067            if middle_lnum > a:linenum
2068                let right = middle - 1
2069            else
2070                let left = middle
2071            endif
2072        endwhile
2073    else
2074        " sorted by name, brute force method (Dave Eggum)
2075        let closest_lnum = 0
2076        let final_left = 0
2077        while left < right
2078            let txt = s:tlist_{a:fidx}_tag_{left}
2079
2080            let start = strridx(txt, 'line:') + strlen('line:')
2081            let end = strridx(txt, "\t")
2082            if end < start
2083                let lnum = strpart(txt, start) + 0
2084            else
2085                let lnum = strpart(txt, start, end - start) + 0
2086            endif
2087
2088            if lnum < a:linenum && lnum > closest_lnum
2089                let closest_lnum = lnum
2090                let final_left = left
2091            elseif lnum == a:linenum
2092                let closest_lnum = lnum
2093                break
2094            else
2095                let left = left + 1
2096            endif
2097        endwhile
2098        if closest_lnum == 0
2099            return ""
2100        endif
2101        if left == right
2102            let left = final_left
2103        endif
2104    endif
2105
2106    return s:tlist_{a:fidx}_tag_{left}
2107endfunction
2108
2109" Tlist_Highlight_Tag()
2110" Highlight the current tag
2111" cntx == 1, Called by the taglist plugin itself
2112" cntx == 2, Forced by the user through the TlistSync command
2113function! s:Tlist_Highlight_Tag(filename, curline, cntx)
2114    " Highlight the current tag only if the user configured the
2115    " taglist plugin to do so or if the user explictly invoked the
2116    " command to highlight the current tag.
2117    if !g:Tlist_Auto_Highlight_Tag && a:cntx == 1
2118        return
2119    endif
2120
2121    if a:filename == ''
2122        return
2123    endif
2124
2125    " Make sure the taglist window is present
2126    let winnum = bufwinnr(g:TagList_title)
2127    if winnum == -1
2128        call s:Tlist_Warning_Msg('Error: Taglist window is not open')
2129        return
2130    endif
2131
2132    let fidx = s:Tlist_Get_File_Index(a:filename)
2133    if fidx == -1
2134        return
2135    endif
2136
2137    " If there are no tags for this file, then no need to proceed further
2138    if s:tlist_{fidx}_tag_count == 0
2139        return
2140    endif
2141
2142    " If part of winmanager then disable winmanager autocommands
2143    if s:tlist_app_name == "winmanager"
2144        call WinManagerSuspendAUs()
2145    endif
2146
2147    " Save the original window number
2148    let org_winnr = winnr()
2149
2150    if org_winnr == winnum
2151        let in_taglist_window = 1
2152    else
2153        let in_taglist_window = 0
2154    endif
2155
2156    " Go to the taglist window
2157    if !in_taglist_window
2158        exe winnum . 'wincmd w'
2159    endif
2160
2161    " Clear previously selected name
2162    match none
2163
2164    let tag_txt = s:Tlist_Find_Tag_text(fidx, a:curline)
2165    if tag_txt == ""
2166        " Make sure the current tag line is visible in the taglist window.
2167        " Calling the winline() function makes the line visible.  Don't know
2168        " of a better way to achieve this.
2169        let cur_lnum = line('.')
2170
2171        if cur_lnum < s:tlist_{fidx}_start || cur_lnum > s:tlist_{fidx}_end
2172            " Move the cursor to the beginning of the file
2173            exe s:tlist_{fidx}_start
2174        endif
2175
2176        if has('folding')
2177            normal! zv
2178        endif
2179
2180        call winline()
2181
2182        if !in_taglist_window
2183            let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
2184            let s:Tlist_Skip_Refresh = 1
2185            exe org_winnr . 'wincmd w'
2186            let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
2187        endif
2188        if s:tlist_app_name == "winmanager"
2189            call WinManagerResumeAUs()
2190        endif
2191        return
2192    endif
2193
2194    " Extract the tag type
2195    let ttype = s:Tlist_Extract_Tagtype(tag_txt)
2196
2197    " Extract the tag offset
2198    let offset = strpart(tag_txt, 0, stridx(tag_txt, ':')) + 0
2199
2200    " Compute the line number
2201    let lnum = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_start + offset
2202
2203    " Goto the line containing the tag
2204    exe lnum
2205
2206    " Open the fold
2207    if has('folding')
2208        normal! zv
2209    endif
2210
2211    " Make sure the current tag line is visible in the taglist window.
2212    " Calling the winline() function makes the line visible.  Don't know
2213    " of a better way to achieve this.
2214    call winline()
2215
2216    " Highlight the tag name
2217    call s:Tlist_Highlight_Tagline()
2218
2219    " Go back to the original window
2220    if !in_taglist_window
2221        let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
2222        let s:Tlist_Skip_Refresh = 1
2223        exe org_winnr . 'wincmd w'
2224        let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
2225    endif
2226
2227    if s:tlist_app_name == "winmanager"
2228        call WinManagerResumeAUs()
2229    endif
2230
2231    return
2232endfunction
2233
2234" Tlist_Get_Tag_Prototype_By_Line
2235" Get the prototype for the tag on or before the specified line number in the
2236" current buffer
2237function! Tlist_Get_Tag_Prototype_By_Line(...)
2238    if a:0 == 0
2239        " Arguments are not supplied. Use the current buffer name
2240        " and line number
2241        let filename = bufname('%')
2242        let linenr = line('.')
2243    elseif a:0 == 2
2244        " Filename and line number are specified
2245        let filename = a:1
2246        let linenr = a:2
2247        if linenr !~ '\d\+'
2248            " Invalid line number
2249            return ""
2250        endif
2251    else
2252        " Sufficient arguments are not supplied
2253        let msg =  'Usage: Tlist_Get_Tag_Prototype_By_Line <filename> ' .
2254                                \ '<line_number>'
2255        call s:Tlist_Warning_Msg(msg)
2256        return ""
2257    endif
2258
2259    " Expand the file to a fully qualified name
2260    let filename = fnamemodify(filename, ':p')
2261    if filename == ''
2262        return ""
2263    endif
2264
2265    let fidx = s:Tlist_Get_File_Index(filename)
2266    if fidx == -1
2267        return ""
2268    endif
2269
2270    " If there are no tags for this file, then no need to proceed further
2271    if s:tlist_{fidx}_tag_count == 0
2272        return ""
2273    endif
2274
2275    " Get the tag text using the line number
2276    let tag_txt = s:Tlist_Find_Tag_text(fidx, linenr)
2277    if tag_txt == ""
2278        return ""
2279    endif
2280
2281    " Extract the tag search pattern and return it
2282    return s:Tlist_Extract_Tag_Prototype(tag_txt)
2283endfunction
2284
2285" Tlist_Get_Tagname_By_Line
2286" Get the tag name on or before the specified line number in the
2287" current buffer
2288function! Tlist_Get_Tagname_By_Line(...)
2289    if a:0 == 0
2290        " Arguments are not supplied. Use the current buffer name
2291        " and line number
2292        let filename = bufname('%')
2293        let linenr = line('.')
2294    elseif a:0 == 2
2295        " Filename and line number are specified
2296        let filename = a:1
2297        let linenr = a:2
2298        if linenr !~ '\d\+'
2299            " Invalid line number
2300            return ""
2301        endif
2302    else
2303        " Sufficient arguments are not supplied
2304        let msg =  'Usage: Tlist_Get_Tagname_By_Line <filename> <line_number>'
2305        call s:Tlist_Warning_Msg(msg)
2306        return ""
2307    endif
2308
2309    " Make sure the current file has a name
2310    let filename = fnamemodify(filename, ':p')
2311    if filename == ''
2312        return ""
2313    endif
2314
2315    let fidx = s:Tlist_Get_File_Index(filename)
2316    if fidx == -1
2317        return ""
2318    endif
2319
2320    " If there are no tags for this file, then no need to proceed further
2321    if s:tlist_{fidx}_tag_count == 0
2322        return ""
2323    endif
2324
2325    " Get the tag name using the line number
2326    let tag_txt = s:Tlist_Find_Tag_text(fidx, linenr)
2327    if tag_txt == ""
2328        return ""
2329    endif
2330
2331    " Remove the line number at the beginning
2332    let start = stridx(tag_txt, ':')  + 1
2333
2334    " Extract the tag name and return it
2335    return strpart(tag_txt, start, stridx(tag_txt, "\t") - start)
2336endfunction
2337
2338" Tlist_Move_To_File
2339" Move the cursor to the beginning of the current file or the next file
2340" or the previous file in the taglist window
2341" dir == -1, move to start of current or previous function
2342" dir == 1, move to start of next function
2343function! s:Tlist_Move_To_File(dir)
2344    if foldlevel('.') == 0
2345        " Cursor is on a non-folded line (it is not in any of the files)
2346        " Move it to a folded line
2347        if a:dir == -1
2348            normal! zk
2349        else
2350            " While moving down to the start of the next fold,
2351            " no need to do go to the start of the next file.
2352            normal! zj
2353            return
2354        endif
2355    endif
2356
2357    let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.'))
2358    if fidx == -1
2359        return
2360    endif
2361
2362    let cur_lnum = line('.')
2363
2364    if a:dir == -1
2365        if cur_lnum > s:tlist_{fidx}_start
2366            " Move to the beginning of the current file
2367            exe s:tlist_{fidx}_start
2368            return
2369        endif
2370
2371        if fidx == 0
2372            " At the first file, can't move to previous file
2373            return
2374        endif
2375
2376        " Otherwise, move to the beginning of the previous file
2377        let fidx = fidx - 1
2378        exe s:tlist_{fidx}_start
2379        return
2380    else
2381        let fidx = fidx + 1
2382
2383        if fidx == s:tlist_file_count
2384            " At the last file, can't move to the next file
2385            return
2386        endif
2387
2388        " Otherwise, move to the beginning of the next file
2389        exe s:tlist_{fidx}_start
2390        return
2391    endif
2392endfunction
2393
2394" Tlist_Session_Load
2395" Load a taglist session (information about all the displayed files
2396" and the tags) from the specified file
2397function! s:Tlist_Session_Load(...)
2398    if a:0 == 0 || a:1 == ''
2399        call s:Tlist_Warning_Msg('Usage: TlistSessionLoad <filename>')
2400        return
2401    endif
2402
2403    let sessionfile = a:1
2404
2405    if !filereadable(sessionfile)
2406        let msg = 'Taglist: Error - Unable to open file ' . sessionfile
2407        call s:Tlist_Warning_Msg(msg)
2408        return
2409    endif
2410
2411    " Mark the current window as the file window
2412    if bufname('%') !~ g:TagList_title
2413        let w:tlist_file_window = "yes"
2414    endif
2415
2416    " Open to the taglist window
2417    call s:Tlist_Open_Window()
2418
2419    " Source the session file
2420    exe 'source ' . sessionfile
2421
2422    let new_file_count = g:tlist_file_count
2423    unlet! g:tlist_file_count
2424
2425    let i = 0
2426    while i < new_file_count
2427        let ftype = g:tlist_{i}_filetype
2428        unlet! g:tlist_{i}_filetype
2429
2430        if !exists("s:tlist_" . ftype . "_count")
2431            if s:Tlist_FileType_Init(ftype) == 0
2432                let i = i + 1
2433                continue
2434            endif
2435        endif
2436
2437        let fname = g:tlist_{i}_filename
2438        unlet! g:tlist_{i}_filename
2439
2440        let fidx = s:Tlist_Get_File_Index(fname)
2441        if fidx != -1
2442            let s:tlist_{fidx}_visible = 0
2443            let i = i + 1
2444            continue
2445        endif
2446
2447        let fidx = s:Tlist_Init_File(fname, ftype)
2448
2449        let s:tlist_{fidx}_filename = fname
2450
2451        let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type
2452        unlet! g:tlist_{i}_sort_type
2453
2454        let s:tlist_{fidx}_filetype = ftype
2455        let s:tlist_{fidx}_mtime = getftime(fname)
2456
2457        let s:tlist_{fidx}_start = 0
2458        let s:tlist_{fidx}_end = 0
2459
2460        let s:tlist_{fidx}_valid = 1
2461        " Mark the file as not visible, so that Tlist_Init_Window() function
2462        " will display the tags for this file
2463        let s:tlist_{fidx}_visible = 0
2464
2465        let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count
2466        unlet! g:tlist_{i}_tag_count
2467
2468        let j = 1
2469        while j <= s:tlist_{fidx}_tag_count
2470            let s:tlist_{fidx}_tag_{j} = g:tlist_{i}_tag_{j}
2471            unlet! g:tlist_{i}_tag_{j}
2472            let j = j + 1
2473        endwhile
2474
2475        let j = 1
2476        while j <= s:tlist_{ftype}_count
2477            let ttype = s:tlist_{ftype}_{j}_name
2478
2479            if exists('g:tlist_' . i . '_' . ttype)
2480                let s:tlist_{fidx}_{ttype} = g:tlist_{i}_{ttype}
2481                unlet! g:tlist_{i}_{ttype}
2482                let s:tlist_{fidx}_{ttype}_start = 0
2483                let s:tlist_{fidx}_{ttype}_count = g:tlist_{i}_{ttype}_count
2484                unlet! g:tlist_{i}_{ttype}_count
2485
2486                let k = 1
2487                while k <= s:tlist_{fidx}_{ttype}_count
2488                    let s:tlist_{fidx}_{ttype}_{k} = g:tlist_{i}_{ttype}_{k}
2489                    unlet! g:tlist_{i}_{ttype}_{k}
2490                    let k = k + 1
2491                endwhile
2492            else
2493                let s:tlist_{fidx}_{ttype} = ''
2494                let s:tlist_{fidx}_{ttype}_start = 0
2495                let s:tlist_{fidx}_{ttype}_count = 0
2496            endif
2497
2498            let j = j + 1
2499        endwhile
2500
2501        let i = i + 1
2502    endwhile
2503
2504    " Initialize the taglist window
2505    call s:Tlist_Init_Window()
2506    call s:Tlist_Refresh_Window()
2507
2508    if s:tlist_file_count > 0
2509        " Jump to the beginning of the first file
2510        call cursor(s:tlist_0_start, 1)
2511    endif
2512endfunction
2513
2514" Tlist_Session_Save
2515" Save a taglist session (information about all the displayed files
2516" and the tags) into the specified file
2517function! s:Tlist_Session_Save(...)
2518    if a:0 == 0 || a:1 == ''
2519        call s:Tlist_Warning_Msg('Usage: TlistSessionSave <filename>')
2520        return
2521    endif
2522
2523    let sessionfile = a:1
2524
2525    if s:tlist_file_count == 0
2526        " There is nothing to save
2527        call s:Tlist_Warning_Msg('Warning: Taglist is empty. Nothing to save.')
2528        return
2529    endif
2530
2531    if filereadable(sessionfile)
2532        let ans = input("Do you want to overwrite " . sessionfile . " (Y/N)?")
2533        if ans !=? 'y'
2534            return
2535        endif
2536
2537        echo "\n"
2538    endif
2539
2540    exe 'redir! > ' . sessionfile
2541
2542    silent! echo '" Taglist session file. This file is auto-generated.'
2543    silent! echo '" File information'
2544    silent! echo 'let tlist_file_count = ' . s:tlist_file_count
2545
2546    let i = 0
2547
2548    while i < s:tlist_file_count
2549        " Store information about the file
2550        silent! echo 'let tlist_' . i . "_filename = '" .
2551                                            \ s:tlist_{i}_filename . "'"
2552        silent! echo 'let tlist_' . i . '_sort_type = "' .
2553                                                \ s:tlist_{i}_sort_type . '"'
2554        silent! echo 'let tlist_' . i . '_filetype = "' .
2555                                            \ s:tlist_{i}_filetype . '"'
2556        silent! echo 'let tlist_' . i . '_tag_count = ' .
2557                                                        \ s:tlist_{i}_tag_count
2558        " Store information about all the tags
2559        let j = 1
2560        while j <= s:tlist_{i}_tag_count
2561            let txt = escape(s:tlist_{i}_tag_{j}, '"\\')
2562            silent! echo 'let tlist_' . i . '_tag_' . j . ' = "' . txt . '"'
2563            let j = j + 1
2564        endwhile
2565
2566        " Store information about all the tags grouped by their type
2567        let ftype = s:tlist_{i}_filetype
2568        let j = 1
2569        while j <= s:tlist_{ftype}_count
2570            let ttype = s:tlist_{ftype}_{j}_name
2571            if s:tlist_{i}_{ttype}_count != 0
2572                let txt = substitute(s:tlist_{i}_{ttype}, "\n", "\\\\n", "g")
2573                silent! echo 'let tlist_' . i . '_' . ttype . ' = "' .
2574                                                \ txt . '"'
2575                silent! echo 'let tlist_' . i . '_' . ttype . '_count = ' .
2576                                                     \ s:tlist_{i}_{ttype}_count
2577                let k = 1
2578                while k <= s:tlist_{i}_{ttype}_count
2579                    silent! echo 'let tlist_' . i . '_' . ttype . '_' . k .
2580                                \ ' = ' . s:tlist_{i}_{ttype}_{k}
2581                    let k = k + 1
2582                endwhile
2583            endif
2584            let j = j + 1
2585        endwhile
2586
2587        silent! echo
2588
2589        let i = i + 1
2590    endwhile
2591
2592    redir END
2593endfunction
2594
2595" Tlist_Update_File_Display
2596" Update a file displayed in the taglist window.
2597" action == 1, Close the fold for the file
2598" action == 2, Remove the file from the taglist window
2599function! s:Tlist_Update_File_Display(filename, action)
2600    " Make sure a valid filename is supplied
2601    if a:filename == ''
2602        return
2603    endif
2604
2605    " Make sure the taglist window is present
2606    let winnum = bufwinnr(g:TagList_title)
2607    if winnum == -1
2608        call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open')
2609        return
2610    endif
2611
2612    " Save the original window number
2613    let org_winnr = winnr()
2614    if org_winnr == winnum
2615        let in_taglist_window = 1
2616    else
2617        let in_taglist_window = 0
2618    endif
2619
2620    " Go to the taglist window
2621    if !in_taglist_window
2622        exe winnum . 'wincmd w'
2623    endif
2624
2625    " Get tag list index of the specified file
2626    let idx = s:Tlist_Get_File_Index(a:filename)
2627    if idx != -1
2628        " Save the cursor position
2629        let save_lnum = line('.')
2630
2631        " Perform the requested action on the file
2632        if a:action == 1
2633            " Close the fold for the file
2634
2635            if g:Tlist_File_Fold_Auto_Close
2636                " Close the fold for the file
2637                if has('folding')
2638                    exe "silent! " . s:tlist_{idx}_start . "," .
2639                                \ s:tlist_{idx}_end . "foldclose"
2640                endif
2641            endif
2642        elseif a:action == 2
2643            " Remove the file from the list
2644            call s:Tlist_Remove_File(idx)
2645        endif
2646
2647        " Move the cursor to the original location
2648        exe save_lnum
2649    endif
2650
2651    " Go back to the original window
2652    if !in_taglist_window
2653        let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh
2654        let s:Tlist_Skip_Refresh = 1
2655        exe org_winnr . 'wincmd w'
2656        let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh
2657    endif
2658endfunction
2659
2660" Define the taglist autocommand to automatically open the taglist window on
2661" Vim startup
2662if g:Tlist_Auto_Open
2663    autocmd VimEnter * nested Tlist
2664endif
2665
2666" Refresh the taglist
2667if g:Tlist_Process_File_Always
2668    autocmd BufEnter * call <SID>Tlist_Refresh()
2669endif
2670
2671" Define the user commands to manage the taglist window
2672command! -nargs=0 Tlist call s:Tlist_Toggle_Window()
2673command! -nargs=0 TlistClose call s:Tlist_Close_Window()
2674command! -nargs=0 TlistUpdate call s:Tlist_Update_Tags()
2675command! -nargs=0 TlistSync call s:Tlist_Highlight_Tag(
2676                            \ fnamemodify(bufname('%'), ':p'), line('.'), 2)
2677command! -nargs=* -complete=buffer TlistShowPrototype
2678            \ echo Tlist_Get_Tag_Prototype_By_Line(<f-args>)
2679command! -nargs=* -complete=buffer TlistShowTag
2680            \ echo Tlist_Get_Tagname_By_Line(<f-args>)
2681command! -nargs=* -complete=file TlistSessionLoad
2682            \ call s:Tlist_Session_Load(<q-args>)
2683command! -nargs=* -complete=file TlistSessionSave
2684            \ call s:Tlist_Session_Save(<q-args>)
2685
2686" Tlist_Set_App
2687" Set the name of the external plugin/application to which taglist
2688" belongs.
2689" Taglist plugin is part of another plugin like cream or winmanager.
2690function! Tlist_Set_App(name)
2691    if a:name == ""
2692        return
2693    endif
2694
2695    let s:tlist_app_name = a:name
2696endfunction
2697
2698" Winmanager integration
2699
2700" Initialization required for integration with winmanager
2701function! TagList_Start()
2702    " If current buffer is not taglist buffer, then don't proceed
2703    if bufname('%') != '__Tag_List__'
2704        return
2705    endif
2706
2707    call Tlist_Set_App("winmanager")
2708
2709    " Get the current filename from the winmanager plugin
2710    let bufnum = WinManagerGetLastEditedFile()
2711    if bufnum != -1
2712        let filename = fnamemodify(bufname(bufnum), ':p')
2713        let ftype = getbufvar(bufnum, '&filetype')
2714    endif
2715
2716    " Initialize the taglist window, if it is not already initialized
2717    if !exists("s:tlist_window_initialized") || !s:tlist_window_initialized
2718        call s:Tlist_Init_Window()
2719        call s:Tlist_Refresh_Window()
2720        let s:tlist_window_initialized = 1
2721    endif
2722
2723    " Open the taglist window
2724    if bufnum != -1
2725        call s:Tlist_Explore_File(filename, ftype)
2726    endif
2727endfunction
2728
2729function! TagList_IsValid()
2730    return 0
2731endfunction
2732
2733function! TagList_WrapUp()
2734    return 0
2735endfunction
2736