commit e9b9608c00deccd51a6baac8e52e74d731b0ac67 Author: Yegappan Lakshmanan Date: Fri Apr 25 00:00:00 2003 +0000 Version 2.5 Added support for highlighting the tag scope. Added support for displaying namespaces in C++ files. Updated the comments. diff --git a/README b/README new file mode 100644 index 0000000..56bf86f --- /dev/null +++ b/README @@ -0,0 +1,18 @@ +This is a mirror of http://www.vim.org/scripts/script.php?script_id=273 + +The "Tag List" plugin is a source code browser plugin for Vim and +provides an overview of the structure of source code files and allows +you to efficiently browse through source code files for different +programming languages. You can visit the taglist plugin home page for +more information: + + http://vim-taglist.sourceforge.net + +You can subscribe to the taglist mailing list to post your questions +or suggestions for improvement or to report bugs. Visit the following +page for subscribing to the mailing list: + + http://groups.yahoo.com/group/taglist/ + +For more information about using this plugin, after installing the +taglist plugin, use the ":help taglist" command. diff --git a/plugin/taglist.vim b/plugin/taglist.vim new file mode 100644 index 0000000..52054bf --- /dev/null +++ b/plugin/taglist.vim @@ -0,0 +1,1954 @@ +" File: taglist.vim +" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +" Version: 2.5 +" Last Modified: April 26, 2003 +" +" Overview +" -------- +" The "Tag List" plugin provides an overview of the structure of source code +" files and allows you to efficiently browse through source code files in +" different programming languages. The "Tag List" plugin provides the +" following features: +" +" 1. Opens a vertically/horizontally split Vim window with a list of tags +" (functions, classes, structures, variables, etc) defined in the current +" file. +" 2. Groups the tags by their type and displays them in a foldable tree. +" 3. Automatically updates the taglist window as you switch between +" files/buffers. +" 4. When a tag name is selected from the taglist window, positions the cursor +" at the definition of the tag in the source file +" 5. Automatically highlights the current tag name. +" 6. Can display the prototype of a tag from the taglist window. +" 7. Displays the scope of a tag. +" 8. Can optionally use the tag prototype instead of the tag name. +" 9. The tag list can be sorted either by name or by line number. +" 10. Supports the following language files: Assembly, ASP, Awk, Beta, C, C++, +" C#, Cobol, Eiffel, Erlang, Fortran, HTML, Java, Javascript, Lisp, Lua, +" Make, Pascal, Perl, PHP, Python, Rexx, Ruby, Scheme, Shell, Slang, SML, +" Sql, TCL, Verilog, Vim and Yacc. +" 11. Runs in all the platforms where the exuberant ctags utility and Vim are +" supported (this includes MS-Windows and Unix based systems). +" 12. Runs in both console/terminal and GUI versions of Vim. +" 13. The ctags output for a file is cached to speed up displaying the taglist +" window. +" 14. Works with the winmanager plugin. Using the winmanager plugin, you can +" use Vim plugins like the file explorer, buffer explorer and the taglist +" plugin at the same time like an IDE. +" 15. Can be easily extended to support new languages. Support for existing +" languages can be modified easily. +" +" To see the screenshots of the taglist plugin in different environments, +" visit the following page: +" +" http://www.geocities.com/yegappan/taglist/screenshots.html +" +" This plugin relies on the exuberant ctags utility to dynamically generate +" the tag listing. You can download the exuberant ctags utility from +" +" http://ctags.sourceforge.net +" +" The exuberant ctags utility must be installed in your system to use this +" plugin. You should use exuberant ctags version 5.0 and above. This plugin +" doesn't use or create a tags file and there is no need to create a tags file +" to use this plugin. +" +" This plugin relies on the Vim "filetype" detection mechanism to determine +" the type of the current file. You have to turn on the Vim filetype detection +" by adding the following line to your .vimrc file: +" +" filetype on +" +" This plugin will not work in 'compatible' mode. Make sure the 'compatible' +" option is not set. This plugin will not work if you run Vim in the +" restricted mode (using the -Z command-line argument). This plugin also +" assumes that the system() Vim function is supported. +" +" Installation +" ------------ +" 1. Copy the taglist.vim plugin to the $HOME/.vim/plugin directory. Refer to +" ':help add-plugin', ':help add-global-plugin' and ':help runtimepath' for +" more details about Vim plugins. +" 2. Set the Tlist_Ctags_Cmd variable to point to the location of the +" exuberant ctags utility (not to the directory). +" 3. If you are running a terminal/console version of Vim and the terminal +" doesn't support changing the window width then set the Tlist_Inc_Winwidth +" variable to 0. +" 4. Restart Vim. +" 5. You can use the ":Tlist" command to open/close the taglist window. +" +" Usage +" ----- +" You can open the taglist window by using the ":Tlist" command. Invoking this +" command will toggle (open or close) the taglist window. You can map a key to +" invoke this command. For example, the following command creates a normal +" mode mapping for the key to open or close the taglist window. +" +" nnoremap :Tlist +" +" Add the above mapping to your ~/.vimrc file. You can also open the taglist +" window on startup using the following command line: +" +" $ vim +Tlist +" +" You can close the taglist window from the taglist window by pressing 'q' or +" using the Vim ":q" command. You can also use any of the Vim window commands +" to close the taglist window. Invoking the ":Tlist" command when the taglist +" window is opened, will close the taglist window. +" +" As you switch between source files, the taglist window will be automatically +" updated with the tag listing for the current source file. The tag names +" will grouped by their type (variable, function, class, etc). For tags with +" scope information (like class members, structures inside structures, etc), +" the scope information will be displayed in square brackets "[]" after the +" tagname. +" +" The tag names will be displayed as a foldable tree using the Vim folding +" support. You can collapse the tree using the '-' key or using the Vim zc +" fold command. You can open the tree using the '+' key or using the Vim zo +" fold command. You can open all the fold using the '*' key or using the Vim +" zR fold command You can also use the mouse to open/close the folds. +" +" You can select a tag either by pressing the key or by double +" clicking the tag name using the mouse. You can configure the taglist plugin +" by setting the 'Tlist_Use_SingleClick' variable to jump to a tag on a single +" mouse click. +" +" This plugin will automatically highlight the name of the current tag. The +" tag name will be highlighted after 'updatetime' milliseconds. The default +" value for this Vim option is 4 seconds. You can also use the ":TlistSync" +" command to force the highlighting of the current tag. You can map a key to +" invoke this command. For example, the following command creates a normal +" mapping for the key to highlight the current tag name. +" +" nnoremap :TlistSync +" +" Add the above mapping to your ~/.vimrc file. +" +" If you place the cursor on a tag name in the "Tag List" window, then the tag +" prototype will be displayed at the Vim status line after 'updatetime' +" milliseconds. The default value for the 'updatetime' Vim option is 4 +" seconds. You can also press the space bar to display the prototype of the +" tag under the cursor. +" +" By default, the tag list will be sorted by the order in which the tags +" appear in the file. You can sort the tags either by name or by order by +" pressing the "s" key in the taglist window. +" +" You can press the 'x' key in the taglist window to maximize the taglist +" window width/height. The window will be maximized to the maximum possible +" width/height without closing the other existing windows. You can again press +" 'x' to restore the taglist window to the default width/height. +" +" You can press the '?' key to display help information about using the +" taglist window. If you again press the '?' key, the help information will be +" removed. +" +" The following table lists the description of the keys that you can use +" in the taglist window. +" +" Key Description +" +" Jump to the location where the tag under cursor is +" defined. +" o Jump to the location where the tag under cursor is +" defined in a new window. +" Display the prototype of the tag under the cursor. +" u Update the tags listed in the taglist window +" s Change the sort order of the tags (by name or by order) +" x Zoom-in or Zoom-out the taglist window +" + Open a fold +" - Close a fold +" * Open all folds +" q Close the taglist window +" ? Display help +" +" +" You can use the ":TlistShowPrototype" command to display the prototype of +" a function in the specified line number. For example, +" +" :TlistShowPrototype 50 +" +" If the line number is not supplied, this command will display the prototype +" of the current function. +" +" You can also use the taglist plugin with the winmanager plugin. This will +" allow you to use the file explorer, buffer explorer and the taglist plugin +" at the same time in different windows. To use the taglist plugin with the +" winmanager plugin, set 'TagList' in the 'winManagerWindowLayout' variable. +" For example, to use the file explorer plugin and the taglist plugin at the +" same time, use the following setting: +" +" let winManagerWindowLayout = 'FileExplorer|TagList' +" +" Configuration +" ------------- +" By changing the following variables you can configure the behavior of this +" plugin. Set the following variables in your .vimrc file using the 'let' +" command. +" +" This plugin uses the Tlist_Ctags_Cmd variable to locate the ctags utility. +" By default, this is set to ctags. Set this variable to point to the location +" of the ctags utility in your system. Note that this variable should point to +" the fully qualified exuberant ctags location and NOT to the directory in +" which exuberant ctags is installed. +" +" let Tlist_Ctags_Cmd = 'd:\tools\ctags.exe' +" let Tlist_Ctags_Cmd = '/usr/local/bin/ctags' +" +" By default, the tag names will be listed in the order in which they are +" defined in the file. You can alphabetically sort the tag names by pressing +" the "s" key in the taglist window. You can also change the default order by +" setting the variable Tlist_Sort_Type to "name" or "order": +" +" let Tlist_Sort_Type = "name" +" +" Be default, the tag names will be listed in a vertically split window. If +" you prefer a horizontally split window, then set the +" 'Tlist_Use_Horiz_Window' variable to 1. If you are running MS-Windows +" version of Vim in a MS-DOS command window, then you should use a +" horizontally split window instead of a vertically split window. Also, if +" you are using an older version of xterm in a Unix system that doesn't +" support changing the xterm window width, you should use a horizontally split +" window. +" +" let Tlist_Use_Horiz_Window = 1 +" +" By default, the vertically split taglist window will appear on the left hand +" side. If you prefer to open the window on the right hand side, you can set +" the Tlist_Use_Right_Window variable to one: +" +" let Tlist_Use_Right_Window = 1 +" +" To automatically open the taglist window, when you start Vim, you can set +" the Tlist_Auto_Open variable to 1. By default, this variable is set to 0 and +" the taglist window will not be opened automatically on Vim startup. +" +" let Tlist_Auto_Open = 1 +" +" By default, only the tag name will be displayed in the taglist window. If +" you like to see tag prototypes instead of names, set the +" Tlist_Display_Prototype variable to 1. By default, this variable is set to 0 +" and only tag names will be displayed. +" +" let Tlist_Display_Prototype = 1 +" +" The default width of the vertically split taglist window will be 30. This +" can be changed by modifying the Tlist_WinWidth variable: +" +" let Tlist_WinWidth = 20 +" +" Note that the value of the 'winwidth' option setting determines the minimum +" width of the current window. If you set the 'Tlist_WinWidth' variable to a +" value less than that of the 'winwidth' option setting, then Vim will use the +" value of the 'winwidth' option. +" +" By default, when the width of the window is less than 100 and a new taglist +" window is opened vertically, then the window width will be increased by the +" value set in the Tlist_WinWidth variable to accommodate the new window. The +" value of this variable is used only if you are using a vertically split +" taglist window. If your terminal doesn't support changing the window width +" from Vim (older version of xterm running in a Unix system) or if you see any +" weird problems in the screen due to the change in the window width or if you +" prefer not to adjust the window width then set the 'Tlist_Inc_Winwidth' +" variable to 0. CAUTION: If you are using the MS-Windows version of Vim in a +" MS-DOS command window then you must set this variable to 0, otherwise the +" system may hang due to a Vim limitation (explained in :help win32-problems) +" +" let Tlist_Inc_Winwidth = 0 +" +" By default, when you double click on the tag name using the left mouse +" button, the cursor will be positioned at the definition of the tag. You +" can set the Tlist_Use_SingleClick variable to one to jump to a tag when +" you single click on the tag name using the mouse. By default this variable +" is set to zero. +" +" let Tlist_Use_SingleClick = 1 +" +" Due to a bug in Vim, if you set Tlist_Use_SingleClick to one and try to +" resize the taglist window using the mouse, then Vim will crash. The fix for +" this bug will be available in the next version of Vim. In the meantime, +" instead of resizing the taglist window using the mouse, you can use normal +" Vim window resizing commands to resize the taglist window. +" +" By default, the taglist window will contain text that display the name of +" the file, sort order information and the key to press to get help. Also, +" empty lines will be used to separate different groups of tags. If you +" don't need these information, you can set the Tlist_Compact_Format variable +" to one to get a compact display. +" +" let Tlist_Compact_Format = 1 +" +" Extending +" --------- +" You can extend exuberant ctags to add support for new languages. For more +" information, visit the following page +" +" http://ctags.sourceforge.net/EXTENDING.html +" +" You can extend the taglist plugin to add support for new languages or modify +" the support for an already supported language by setting the following +" variables in the .vimrc file. +" +" To modify the support for an already supported language, you have to set the +" tlist_xxx_settings variable. Replace xxx with the Vim filetype name. To +" determine the filetype name used by Vim for a file, use the command +" +" :set filetype +" +" The format of the value set in the tlist_xxx_settings variable is +" +" ;flag1:name1;flag2:name2;flag3:name3 +" +" The different fields are separated by the ';' character. The first field +" 'language_name' is the name used by exuberant ctags. This name can be +" different from the file type name used by Vim. For example, for C++, the +" language name used by ctags is 'c++' but the filetype name used by Vim is +" 'cpp'. The remaining fields follow the format "flag:name". The sub-field +" 'flag' is the language specific flag used by exuberant ctags to generate the +" corresponding tags. For example, for the C language, to list only the +" functions, the 'f' flag should be used. For more information about the flags +" supported by exuberant ctags for a particular language, read the help text +" from the 'ctags --help' comand. The sub-field 'name' specifies the title +" text to use for displaying the tags of a particular type. For example, +" 'name' can be set to 'functions'. +" +" For example, to list only the classes and functions defined in a C++ +" language file, add the following lines to your .vimrc file +" +" let tlist_cpp_settings = 'c++;c:class;f:function' +" +" In the above setting, 'cpp' is the Vim filetype name and 'c++' is the name +" used by the exuberant ctags tool. 'c' and 'f' are the flags passed to +" exuberant ctags to list classes and functions. +" +" For example, to display only functions defined in a C file and to use "My +" Functions" as the title for the function group, use +" +" let tlist_c_settings = 'c;f:My Functions' +" +" To add support for a new language, set the tlist_xxx_settings variable +" appropriately as described above. +" +" ****************** Do not modify after this line ************************ +if exists('loaded_taglist') || &cp + finish +endif +let loaded_taglist=1 + +" Location of the exuberant ctags tool +if !exists('Tlist_Ctags_Cmd') + let Tlist_Ctags_Cmd = 'ctags' +endif + +" Tag listing sort type - 'name' or 'order' +if !exists('Tlist_Sort_Type') + let Tlist_Sort_Type = 'order' +endif + +" Tag listing window split (horizontal/vertical) control +if !exists('Tlist_Use_Horiz_Window') + let Tlist_Use_Horiz_Window = 0 +endif + +" Open the vertically split taglist window on the left or on the right side. +" This setting is relevant only if Tlist_Use_Horiz_Window is set to zero (i.e. +" only for vertically split windows) +if !exists('Tlist_Use_Right_Window') + let Tlist_Use_Right_Window = 0 +endif + +" Increase Vim window width to display vertically split taglist window. For +" MS-Windows version of Vim running in a MS-DOS window, this must be set to 0 +" otherwise the system may hang due to a Vim limitation. +if !exists('Tlist_Inc_Winwidth') + if (has('win16') || has('win95')) && !has('gui_running') + let Tlist_Inc_Winwidth = 0 + else + let Tlist_Inc_Winwidth = 1 + endif +endif + +" Vertically split taglist window width setting +if !exists('Tlist_WinWidth') + let Tlist_WinWidth = 30 +endif + +" Horizontally split taglist window height setting +if !exists('Tlist_WinHeight') + let Tlist_WinHeight = 10 +endif + +" Automatically open the taglist window on Vim startup +if !exists('Tlist_Auto_Open') + let Tlist_Auto_Open = 0 +endif + +" Display tag prototypes or tag names in the taglist window +if !exists('Tlist_Display_Prototype') + let Tlist_Display_Prototype = 0 +endif + +" Use single left mouse click to jump to a tag. By default this is disabled. +" Only double click using the mouse will be processed. +if !exists('Tlist_Use_SingleClick') + let Tlist_Use_SingleClick = 0 +endif + +" Control whether additional help is displayed as part of the taglist or not. +" Also, controls whether empty lines are used to separate the tag tree. +if !exists('Tlist_Compact_Format') + let Tlist_Compact_Format = 0 +endif + +" assembly language +let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type' + +" aspperl language +let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable' + +" aspvbs language +let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable' + +" awk language +let s:tlist_def_awk_settings = 'awk;f:function' + +" beta language +let s:tlist_def_beta_settings = 'beta;f:fragment;s:pattern;v:virtual' + +" c language +let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' . + \ 'v:variable;f:function' + +" c++ language +let s:tlist_def_cpp_settings = 'c++;v:variable;d:macro;t:typedef;c:class;' . + \ 'n:namespace;g:enum;s:struct;u:union;f:function' + +" c# language +let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' . + \ 'E:event;g:enum;s:struct;i:interface;' . + \ 'p:properties;m:method' + +" cobol language +let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' . + \ 'P:program;s:section' + +" eiffel language +let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature' + +" erlang language +let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function' + +" fortran language +let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' . + \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' . + \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine' + +" HTML language +let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function' + +" java language +let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' . + \ 'f:field;m:method' + +" javascript language +let s:tlist_def_javascript_settings = 'javascript;f:function' + +" lisp language +let s:tlist_def_lisp_settings = 'lisp;f:function' + +" lua language +let s:tlist_def_lua_settings = 'lua;f:function' + +" makefiles +let s:tlist_def_make_settings = 'make;m:macro' + +" pascal language +let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure' + +" perl language +let s:tlist_def_perl_settings = 'perl;p:package;s:subroutine' + +" php language +let s:tlist_def_php_settings = 'php;c:class;f:function' + +" python language +let s:tlist_def_python_settings = 'python;c:class;m:member;f:function' + +" rexx language +let s:tlist_def_rexx_settings = 'rexx;s:subroutine' + +" ruby language +let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' . + \ 'm:singleton method' + +" scheme language +let s:tlist_def_scheme_settings = 'scheme;s:set;f:function' + +" shell language +let s:tlist_def_sh_settings = 'sh;f:function' + +" C shell language +let s:tlist_def_csh_settings = 'sh;f:function' + +" Z shell language +let s:tlist_def_zsh_settings = 'sh;f:function' + +" slang language +let s:tlist_def_slang_settings = 'slang;n:namespace;f:function' + +" sml language +let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' . + \ 'r:structure;t:type;v:value;f:function' + +" sql language +let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' . + \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure' + +" tcl language +let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;p:procedure' + +"verilog language +let s:tlist_def_verilog_settings = 'verilog;m:module;P:parameter;r:register;' . + \ 't:task;w:write;p:port;v:variable;f:function' + +" vim language +let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function' + +" yacc language +let s:tlist_def_yacc_settings = 'yacc;l:label' + +" Initialize the taglist script local variables for the supported file types +" and tag types +let s:tlist_winsize_chgd = 0 +let s:tlist_win_maximized = 0 +let s:tlist_part_of_winmanager = 0 +" Do not change the name of the taglist title variable. The winmanager plugin +" relies on this name to determine the title for the taglist plugin. +let TagList_title = "__Tag_List__" + +function! s:Tlist_Show_Help() + if g:Tlist_Compact_Format == 1 + " In compact display mode, do not display help + return + endif + if exists("s:tlist_show_help") && s:tlist_show_help == 1 + let s:tlist_show_help = 0 + else + let s:tlist_show_help = 1 + endif + + call s:Tlist_Open_Window() + + call s:Tlist_Init_Window(b:tlist_bufnum) + + " Update the taglist window + call s:Tlist_Explore_File(b:tlist_bufnum) +endfunction + +" An autocommand is used to refresh the taglist window when entering any +" buffer. We don't want to refresh the taglist window if we are entering the +" file window from one of the taglist functions. The 'Tlist_Skip_Refresh' +" variable is used to skip the refresh of the taglist window +let s:Tlist_Skip_Refresh = 0 + +function! s:Tlist_Warning_Msg(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +" Tlist_Skip_Buffer() +" Check whether tag listing is supported for the specified buffer. +function! s:Tlist_Skip_Buffer(bufnum) + " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help + if getbufvar(a:bufnum, '&buftype') != '' + return 1 + endif + + let ftype = getbufvar(a:bufnum, '&filetype') + + " Skip buffers with filetype not set + if ftype == '' + return 1 + endif + + " Skip files which are not supported by exuberant ctags + " First check whether default settings for this filetype are available. + " If it is not available, then check whether user specified settings are + " available. If both are not available, then don't list the tags for this + " filetype + let var = 's:tlist_def_' . ftype . '_settings' + if !exists(var) + let var = 'g:tlist_' . ftype . '_settings' + if !exists(var) + return 1 + endif + endif + + let filename = fnamemodify(bufname(a:bufnum), '%:p') + + " Skip buffers with no names + if filename == '' + return 1 + endif + + " Skip files which are not readable or files which are not yet stored + " to the disk + if !filereadable(filename) + return 1 + endif + + return 0 +endfunction + +" Tlist_FileType_Init +" Initialize the ctags arguments and tag variable for the specified +" file type +function! s:Tlist_FileType_Init(ftype) + " If the user didn't specify any settings, then use the default + " ctags args. Otherwise, use the settings specified by the user + let var = 'g:tlist_' . a:ftype . '_settings' + if exists(var) + " User specified ctags arguments + let settings = {var} . ';' + else + " Default ctags arguments + let var = 's:tlist_def_' . a:ftype . '_settings' + if !exists(var) + " No default settings for this file type. This filetype is + " not supported + return 0 + endif + let settings = s:tlist_def_{a:ftype}_settings . ';' + endif + + let msg = 'Invalid ctags option setting - ' . settings + + " Extract the file type to pass to ctags. This can be different from the + " file type detected by Vim + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let ctags_ftype = strpart(settings, 0, pos) + if ctags_ftype == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Make sure a valid filetype is supplied. If the user didn't specify a + " valid filetype, then the ctags option settings may be treated as the + " filetype + if ctags_ftype =~ ':' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Remove the file type from settings + let settings = strpart(settings, pos + 1) + if settings == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Process all the specified ctags flags. The format is + " flag1:name1;flag2:name2;flag3:name3 + let ctags_flags = '' + let cnt = 0 + while settings != '' + " Extract the flag + let pos = stridx(settings, ':') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let flag = strpart(settings, 0, pos) + if flag == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Remove the flag from settings + let settings = strpart(settings, pos + 1) + + " Extract the tag type name + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let name = strpart(settings, 0, pos) + if name == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let settings = strpart(settings, pos + 1) + + let cnt = cnt + 1 + + let s:tlist_{a:ftype}_{cnt}_name = flag + let s:tlist_{a:ftype}_{cnt}_fullname = name + let ctags_flags = ctags_flags . flag + endwhile + + let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype . + \ ' --' . ctags_ftype . '-types=' . ctags_flags + let s:tlist_{a:ftype}_count = cnt + let s:tlist_{a:ftype}_ctags_flags = ctags_flags + + return 1 +endfunction + +" Tlist_Cleanup() +" Cleanup all the taglist window variables. +function! s:Tlist_Cleanup() + if has('syntax') + silent! syntax clear TagListTitle + endif + match none + + if exists('b:tlist_ftype') && b:tlist_ftype != '' + let count_var_name = 's:tlist_' . b:tlist_ftype . '_count' + if exists(count_var_name) + let old_ftype = b:tlist_ftype + let i = 1 + while i <= s:tlist_{old_ftype}_count + let ttype = s:tlist_{old_ftype}_{i}_name + let j = 1 + let var_name = 'b:tlist_' . old_ftype . '_' . ttype . '_count' + if exists(var_name) + let cnt = b:tlist_{old_ftype}_{ttype}_count + else + let cnt = 0 + endif + while j <= cnt + unlet! b:tlist_{old_ftype}_{ttype}_{j} + let j = j + 1 + endwhile + unlet! b:tlist_{old_ftype}_{ttype}_count + unlet! b:tlist_{old_ftype}_{ttype}_start + let i = i + 1 + endwhile + endif + endif + + " Clean up all the variables containing the tags output + if exists('b:tlist_tag_count') + while b:tlist_tag_count > 0 + unlet! b:tlist_tag_{b:tlist_tag_count} + let b:tlist_tag_count = b:tlist_tag_count - 1 + endwhile + endif + + unlet! b:tlist_bufnum + unlet! b:tlist_bufname + unlet! b:tlist_ftype +endfunction + +" Tlist_Open_Window +" Create a new taglist window. If it is already open, clear it +function! s:Tlist_Open_Window() + " If used with winmanager don't open windows. Winmanager will handle + " the window/buffer management + if s:tlist_part_of_winmanager + return + endif + + " Cleanup the taglist window listing, if the window is open + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Jump to the existing window + if winnr() != winnum + exe winnum . 'wincmd w' + endif + else + " Create a new window. If user prefers a horizontal window, then open + " a horizontally split window. Otherwise open a vertically split + " window + if g:Tlist_Use_Horiz_Window == 1 + " If a single window is used for all files, then open the tag + " listing window at the very bottom + let win_dir = 'botright' + " Horizontal window height + let win_size = g:Tlist_WinHeight + else + " Increase the window size, if needed, to accomodate the new + " window + if g:Tlist_Inc_Winwidth == 1 && + \ &columns < (80 + g:Tlist_WinWidth) + " one extra column is needed to include the vertical split + let &columns= &columns + (g:Tlist_WinWidth + 1) + let s:tlist_winsize_chgd = 1 + else + let s:tlist_winsize_chgd = 0 + endif + + " Open the window at the leftmost place + if g:Tlist_Use_Right_Window == 1 + let win_dir = 'botright vertical' + else + let win_dir = 'topleft vertical' + endif + let win_size = g:Tlist_WinWidth + endif + + " If the tag listing temporary buffer already exists, then reuse it. + " Otherwise create a new buffer + let bufnum = bufnr(g:TagList_title) + if bufnum == -1 + " Create a new buffer + let wcmd = g:TagList_title + else + " Edit the existing buffer + let wcmd = '+buffer' . bufnum + endif + + " Create the taglist window + exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd + endif +endfunction + +" Tlist_Zoom_Window +" Zoom (maximize/minimize) the taglist window +function! s:Tlist_Zoom_Window() + if s:tlist_win_maximized == 1 + if g:Tlist_Use_Horiz_Window == 1 + exe 'resize ' . g:Tlist_WinHeight + else + exe 'vert resize ' . g:Tlist_WinWidth + endif + let s:tlist_win_maximized = 0 + else + " Set the window size to the maximum possible without closing other + " windows + if g:Tlist_Use_Horiz_Window == 1 + resize + else + vert resize + endif + let s:tlist_win_maximized = 1 + endif +endfunction + +" Tlist_Init_Window +" Set the default options for the taglist window +function! s:Tlist_Init_Window(bufnum) + " Set report option to a huge value to prevent informations messages + " while deleting the lines + let old_report = &report + set report=99999 + + " Mark the buffer as modifiable + setlocal modifiable + + " Delete the contents of the buffer to the black-hole register + silent! %delete _ + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " Clean up all the old variables used for the last filetype + call Tlist_Cleanup() + + let filename = fnamemodify(bufname(a:bufnum), ':p') + + " Set the sort type. First time, use the global setting. After that use + " the previous setting + let b:tlist_sort_type = getbufvar(a:bufnum, 'tlist_sort_type') + if b:tlist_sort_type == '' + let b:tlist_sort_type = g:Tlist_Sort_Type + endif + + let b:tlist_tag_count = 0 + let b:tlist_bufnum = a:bufnum + let b:tlist_bufname = fnamemodify(bufname(a:bufnum), ':p') + let b:tlist_ftype = getbufvar(a:bufnum, '&filetype') + + " Mark the buffer as modifiable + setlocal modifiable + + if s:tlist_part_of_winmanager + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + if g:Tlist_Compact_Format == 0 + if exists("s:tlist_show_help") && s:tlist_show_help == 1 + call append(0, '" : Jump to tag definition') + call append(1, '" o : Jump to tag definition in new window') + call append(2, '" : Display tag prototype') + call append(3, '" u : Update tag list') + call append(4, '" s : Select sort field') + call append(5, '" x : Zoom-out/Zoom-in taglist window') + call append(6, '" + : Open a fold') + call append(7, '" - : Close a fold') + call append(8, '" * : Open all folds') + call append(9, '" q : Close the taglist window') + call append(10, '" ? : Remove help text') + call append(11, '" Sorted by ' . b:tlist_sort_type) + call append(12, '"= ' . fnamemodify(filename, ':t') . ' (' . + \ fnamemodify(filename, ':p:h') . ')') + else + call append(0, '" Press ? to display help text') + call append(1, '" Sorted by ' . b:tlist_sort_type) + call append(2, '"= ' . fnamemodify(filename, ':t') . ' (' . + \ fnamemodify(filename, ':p:h') . ')') + endif + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Highlight the comments + if has('syntax') + syntax match TagListComment '^" .*' + syntax match TagListSortBy '^" Sorted by .*' + syntax match TagListCurDir '^"= .*' + syntax match TagScope '\s\[.\{-\}\]$' + + " Colors used to highlight the selected tag name + highlight clear TagName + if has('gui_running') || &t_Co > 2 + highlight link TagName Search + else + highlight TagName term=reverse cterm=reverse + endif + + " Colors to highlight comments and titles + highlight clear TagListComment + highlight link TagListComment Comment + highlight clear TagListTitle + highlight link TagListTitle Title + highlight clear TagListSortBy + highlight link TagListSortBy String + highlight clear TagListCurDir + highlight link TagListCurDir Statement + highlight clear TagScope + highlight link TagScope Identifier + endif + + " Folding related settings + if has('folding') + setlocal foldenable + setlocal foldmethod=manual + setlocal foldcolumn=2 + setlocal foldtext=v:folddashes.getline(v:foldstart) + endif + + if !s:tlist_part_of_winmanager + " Mark buffer as scratch + silent! setlocal buftype=nofile + silent! setlocal bufhidden=delete + silent! setlocal noswapfile + " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted + " buffers. So if the taglist buffer is unlisted, multiple taglist + " windows will be opened. This bug is fixed in Vim 6.1 and above + if v:version >= 601 + silent! setlocal nobuflisted + endif + endif + + silent! setlocal nowrap + + " If the 'number' option is set in the source window, it will affect the + " taglist window. So forcefully disable 'number' option for the taglist + " window + silent! setlocal nonumber + + " Create buffer local mappings for jumping to the tags and sorting the list + nnoremap :call Tlist_Jump_To_Tag(0) + nnoremap o :call Tlist_Jump_To_Tag(1) + nnoremap <2-LeftMouse> :call Tlist_Jump_To_Tag(0) + nnoremap s :call Tlist_Change_Sort() + nnoremap + :silent! foldopen + nnoremap - :silent! foldclose + nnoremap * :silent! %foldopen! + nnoremap :silent! foldopen + nnoremap :silent! foldclose + nnoremap :silent! %foldopen! + nnoremap :call Tlist_Show_Tag_Prototype() + nnoremap u :call Tlist_Update_Window() + nnoremap x :call Tlist_Zoom_Window() + nnoremap ? :call Tlist_Show_Help() + nnoremap q :close + + " Insert mode mappings + inoremap :call Tlist_Jump_To_Tag(0) + " Windows needs return + inoremap :call Tlist_Jump_To_Tag(0) + inoremap o :call Tlist_Jump_To_Tag(1) + inoremap <2-LeftMouse> :call + \ Tlist_Jump_To_Tag(0) + inoremap s :call Tlist_Change_Sort() + inoremap + :silent! foldopen + inoremap - :silent! foldclose + inoremap * :silent! %foldopen! + inoremap :silent! foldopen + inoremap :silent! foldclose + inoremap :silent! %foldopen! + inoremap :call + \ Tlist_Show_Tag_Prototype() + inoremap u :call Tlist_Update_Window() + inoremap ? :call Tlist_Show_Help() + inoremap q :close + + + " Map single left mouse click if the user wants this functionality + if g:Tlist_Use_SingleClick == 1 + nnoremap :if bufname("%") =~ "__Tag_List__" + \ call Tlist_Jump_To_Tag(0) endif + endif + + " Define the autocommand to highlight the current tag + augroup TagListAutoCmds + autocmd! + " Display the tag prototype for the tag under the cursor. + autocmd CursorHold __Tag_List__ call s:Tlist_Show_Tag_Prototype() + " Highlight the current tag + autocmd CursorHold * silent call Tlist_Highlight_Tag(bufnr('%'), + \ line('.')) + autocmd BufUnload __Tag_List__ call Tlist_Close_Window() + if !s:tlist_part_of_winmanager + " Adjust the Vim window width when taglist window is closed + " Auto refresh the taglisting window + autocmd BufEnter * call Tlist_Refresh_Window() + endif + augroup end +endfunction + +" Tlist_Close_Window() +" Close the taglist window and adjust the Vim window width +function! s:Tlist_Close_Window() + " Remove the autocommands for the taglist window + silent! autocmd! TagListAutoCmds + + " Clear all the highlights + match none + + if has('syntax') + silent! syntax clear TagListTitle + endif + + " Remove the left mouse click mapping if it was setup initially + if g:Tlist_Use_SingleClick == 1 + if hasmapto('') + nunmap + endif + endif + + if !s:tlist_part_of_winmanager + if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 || + \ s:tlist_winsize_chgd == 0 || + \ &columns < (80 + g:Tlist_WinWidth) + " No need to adjust window width if horizontally split tag listing + " window or if columns is less than 101 or if the user chose not to + " adjust the window width + else + " Adjust the Vim window width + let &columns= &columns - (g:Tlist_WinWidth + 1) + endif + endif +endfunction + +" Tlist_Explore_File() +" List the tags defined in the specified file in a Vim window +function! s:Tlist_Explore_File(bufnum) + " Get the filename and file type + let filename = fnamemodify(bufname(a:bufnum), ':p') + let ftype = getbufvar(a:bufnum, '&filetype') + + " Check for valid filename and valid filetype + if filename == '' || !filereadable(filename) || ftype == '' + return + endif + + " If the tag types for this filetype are not yet created, then create + " them now + let var = 's:tlist_' . ftype . '_count' + if !exists(var) + if s:Tlist_FileType_Init(ftype) == 0 + return + endif + endif + + " If the cached ctags output exists for the specified buffer, then use it. + " Otherwise run ctags to get the output + let valid_cache = getbufvar(a:bufnum, 'tlist_valid_cache') + if valid_cache != '' + " Load the cached processed tags output from the buffer local + " variables + let b:tlist_tag_count = getbufvar(a:bufnum, 'tlist_tag_count') + 0 + let i = 1 + while i <= b:tlist_tag_count + let var_name = 'tlist_tag_' . i + let b:tlist_tag_{i} = getbufvar(a:bufnum, var_name) + let i = i + 1 + endwhile + + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let var_name = 'tlist_' . ttype . '_start' + let b:tlist_{ftype}_{ttype}_start = + \ getbufvar(a:bufnum, var_name) + 0 + let var_name = 'tlist_' . ttype . '_count' + let cnt = getbufvar(a:bufnum, var_name) + 0 + let b:tlist_{ftype}_{ttype}_count = cnt + let var_name = 'tlist_' . ttype + let l:tlist_{ftype}_{ttype} = getbufvar(a:bufnum, var_name) + let j = 1 + while j <= cnt + let var_name = 'tlist_' . ttype . '_' . j + let b:tlist_{ftype}_{ttype}_{j} = getbufvar(a:bufnum, var_name) + let j = j + 1 + endwhile + let i = i + 1 + endwhile + else + " Exuberant ctags arguments to generate a tag list + let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks ' + + " Form the ctags argument depending on the sort type + if b:tlist_sort_type == 'name' + let ctags_args = ctags_args . ' --sort=yes ' + else + let ctags_args = ctags_args . ' --sort=no ' + endif + + " Add the filetype specific arguments + let ctags_args = ctags_args . ' ' . s:tlist_{ftype}_ctags_args + + " Ctags command to produce output with regexp for locating the tags + let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args + let ctags_cmd = ctags_cmd . ' "' . filename . '"' + + " In Windows 95, if not using cygwin, disable the 'shellslash' + " option. Otherwise, this will cause problems when running the + " ctags command. + if has("win95") && !has("win32unix") + let myshellslash = &shellslash + set noshellslash + endif + + " Run ctags and get the tag list + let cmd_output = system(ctags_cmd) + + " Restore the value of the 'shellslash' option. + if has("win95") && !has("win32unix") + let &shellslash = myshellslash + endif + + " Cache the ctags output with a buffer local variable + call setbufvar(a:bufnum, 'tlist_valid_cache', 'Yes') + call setbufvar(a:bufnum, 'tlist_sort_type', b:tlist_sort_type) + + " Handle errors + if v:shell_error && cmd_output != '' + call s:Tlist_Warning_Msg(cmd_output) + return + endif + + " No tags for current file + if cmd_output == '' + call s:Tlist_Warning_Msg('No tags found for ' . filename) + return + endif + + " Initialize variables for the new filetype + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let b:tlist_{ftype}_{ttype}_start = 0 + let b:tlist_{ftype}_{ttype}_count = 0 + let l:tlist_{ftype}_{ttype} = '' + let i = i + 1 + endwhile + + " Process the ctags output one line at a time. Separate the tag output + " based on the tag type and store it in the tag type variable + while cmd_output != '' + " Extract one line at a time + let one_line = strpart(cmd_output, 0, stridx(cmd_output, "\n")) + " Remove the line from the tags output + let cmd_output = strpart(cmd_output, stridx(cmd_output, "\n") + 1) + + if one_line == '' + " Line is not in proper tags format + continue + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(one_line) + + if ttype == '' + " Line is not in proper tags format + continue + endif + + " make sure the tag type is supported + if s:tlist_{ftype}_ctags_flags !~# ttype + continue + endif + + " Extract the tag name + if g:Tlist_Display_Prototype == 0 + let ttxt = ' ' . strpart(one_line, 0, stridx(one_line, "\t")) + + " Add the tag scope, if it is available. Tag scope is the last + " field after the 'line:\t' field + let start = strridx(one_line, 'line:') + let end = strridx(one_line, "\t") + if end > start + let tscope = strpart(one_line, end + 1) + let tscope = strpart(tscope, stridx(tscope, ':') + 1) + if tscope != '' + let ttxt = ttxt . ' [' . tscope . ']' + endif + endif + else + let start = stridx(one_line, '/^') + 2 + let end = strridx(one_line, '/;"' . "\t") + if one_line[end - 1] == '$' + let end = end -1 + endif + let ttxt = strpart(one_line, start, end - start) + endif + + " Update the count of this tag type + let cnt = b:tlist_{ftype}_{ttype}_count + 1 + let b:tlist_{ftype}_{ttype}_count = cnt + + " Add this tag to the tag type variable + let l:tlist_{ftype}_{ttype} = l:tlist_{ftype}_{ttype} . ttxt . "\n" + + " Update the total tag count + let b:tlist_tag_count = b:tlist_tag_count + 1 + let b:tlist_tag_{b:tlist_tag_count} = cnt . ':' . one_line + + let b:tlist_{ftype}_{ttype}_{cnt} = b:tlist_tag_count + endwhile + + " Cache the processed tags output using buffer local variables + call setbufvar(a:bufnum, 'tlist_tag_count', b:tlist_tag_count) + let i = 1 + while i <= b:tlist_tag_count + let var_name = 'tlist_tag_' . i + call setbufvar(a:bufnum, var_name, b:tlist_tag_{i}) + let i = i + 1 + endwhile + + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let var_name = 'tlist_' . ttype . '_start' + call setbufvar(a:bufnum, var_name, b:tlist_{ftype}_{ttype}_start) + let cnt = b:tlist_{ftype}_{ttype}_count + let var_name = 'tlist_' . ttype . '_count' + call setbufvar(a:bufnum, var_name, cnt) + let var_name = 'tlist_' . ttype + call setbufvar(a:bufnum, var_name, l:tlist_{ftype}_{ttype}) + let j = 1 + while j <= cnt + let var_name = 'tlist_' . ttype . '_' . j + call setbufvar(a:bufnum, var_name, b:tlist_{ftype}_{ttype}_{j}) + let j = j + 1 + endwhile + let i = i + 1 + endwhile + endif + + " If we are using a hidden taglist window, then no need to update the + " taglist window + if exists("g:taglist_hidden") + return + endif + + " Set report option to a huge value to prevent informational messages + " while adding lines to the taglist window + let old_report = &report + set report=99999 + + " Mark the buffer as modifiable + setlocal modifiable + + " Add the tag names grouped by tag type to the buffer with a title + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + " Add the tag type only if there are tags for that type + if l:tlist_{ftype}_{ttype} != '' + if g:Tlist_Compact_Format == 0 + let b:tlist_{ftype}_{ttype}_start = line('.') + 1 + silent! put =s:tlist_{ftype}_{i}_fullname + else + let b:tlist_{ftype}_{ttype}_start = line('.') + silent! put! =s:tlist_{ftype}_{i}_fullname + endif + silent! put =l:tlist_{ftype}_{ttype} + + " create a fold for this tag type + if has('folding') + let fold_start = b:tlist_{ftype}_{ttype}_start + let fold_end = fold_start + b:tlist_{ftype}_{ttype}_count + exe fold_start . ',' . fold_end . 'fold' + endif + + " Syntax highlight the tag type names + if has('syntax') + exe 'syntax match TagListTitle /\%' . + \ b:tlist_{ftype}_{ttype}_start . 'l.*/' + endif + " Separate the tag types with a empty line + normal! G + if g:Tlist_Compact_Format == 0 + silent! put ='' + endif + endif + let i = i + 1 + endwhile + + if s:tlist_part_of_winmanager + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " Initially open all the folds + if has('folding') + silent! %foldopen! + endif + + " Goto the first line in the buffer + go + + return +endfunction + +" Tlist_Toggle_Window() +" Open or close a taglist window +function! s:Tlist_Toggle_Window(bufnum) + let curline = line('.') + + " If taglist window is open then close it. + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + if winnr() == winnum + " Already in the taglist window. Close it and return + close + else + " Goto the taglist window, close it and then come back to the + " original window + let curbufnr = bufnr('%') + exe winnum . 'wincmd w' + close + " Need to jump back to the original window only if we are not + " already in that window + let winnum = bufwinnr(curbufnr) + if winnr() != winnum + exe winnum . 'wincmd w' + endif + endif + return + endif + + let s:tlist_part_of_winmanager = 0 + + " Open the taglist window + call s:Tlist_Open_Window() + + " Initialize the taglist window + call s:Tlist_Init_Window(a:bufnum) + + " List the tags defined in a file + call s:Tlist_Explore_File(a:bufnum) + + " Highlight the current tag + call s:Tlist_Highlight_Tag(a:bufnum, curline) + + " Go back to the original window + let s:Tlist_Skip_Refresh = 1 + wincmd p + let s:Tlist_Skip_Refresh = 0 +endfunction + +" Tlist_Extract_Tagtype +" Extract the tag type from the tag text +function! s:Tlist_Extract_Tagtype(tag_txt) + " The tag type is after the tag prototype field. The prototype field + " ends with the /;"\t string. We add 4 at the end to skip the characters + " in this special string.. + let start = strridx(a:tag_txt, '/;"' . "\t") + 4 + let end = strridx(a:tag_txt, 'line:') - 1 + let ttype = strpart(a:tag_txt, start, end - start) + + return ttype +endfunction + +" Tlist_Extract_Tag_Prototype +" Extract the tag protoype from the tag text +function! s:Tlist_Extract_Tag_Prototype(tag_txt) + let start = stridx(a:tag_txt, '/^') + 2 + let end = strridx(a:tag_txt, '/;"' . "\t") + if a:tag_txt[end - 1] == '$' + let end = end -1 + endif + let tag_pat = strpart(a:tag_txt, start, end - start) + + " Remove all the leading space characters + let tag_pat = matchstr(tag_pat, '^\s*\zs.*') + + return tag_pat +endfunction + +" Tlist_Refresh_Window() +" Refresh the taglist window +function! s:Tlist_Refresh_Window() + " We are entering the buffer from one of the taglist functions. So no need + " to refresh the taglist window again + if s:Tlist_Skip_Refresh == 1 + return + endif + + " If the buffer doesn't support tag listing, skip it + if s:Tlist_Skip_Buffer(bufnr('%')) + return + endif + + let filename = expand('%:p') + + let curline = line('.') + + " Make sure the taglist window is open. Otherwise, no need to refresh + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + return + endif + + let bno = bufnr(g:TagList_title) + + let cur_bufnr = bufnr('%') + + " If the tag listing for the current window is already present, no need to + " refresh it + if getbufvar(bno, 'tlist_bufnum') == cur_bufnr && + \ getbufvar(bno, 'tlist_bufname') == filename + return + endif + + " Save the current window number + let cur_winnr = winnr() + + call s:Tlist_Open_Window() + + call s:Tlist_Init_Window(cur_bufnr) + + " Update the taglist window + call s:Tlist_Explore_File(cur_bufnr) + + " Highlight the current tag + call s:Tlist_Highlight_Tag(cur_bufnr, curline) + + " Refresh the taglist window + redraw + + if !s:tlist_part_of_winmanager + " Jump back to the original window + exe cur_winnr . 'wincmd w' + endif +endfunction + +" Tlist_Change_Sort() +" Change the sort order of the tag listing +function! s:Tlist_Change_Sort() + if !exists('b:tlist_bufnum') || !exists('b:tlist_ftype') + return + endif + + let sort_type = getbufvar(b:tlist_bufnum, 'tlist_sort_type') + + " Toggle the sort order from 'name' to 'order' and vice versa + if sort_type == 'name' + call setbufvar(b:tlist_bufnum, 'tlist_sort_type', 'order') + else + call setbufvar(b:tlist_bufnum, 'tlist_sort_type', 'name') + endif + + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + " Clear out the cached taglist information + call setbufvar(b:tlist_bufnum, 'tlist_valid_cache', '') + + call s:Tlist_Open_Window() + + call s:Tlist_Init_Window(b:tlist_bufnum) + + call s:Tlist_Explore_File(b:tlist_bufnum) + + " Go back to the tag line before the list is sorted + call search(curline, 'w') +endfunction + +" Tlist_Update_Window() +" Update the window by regenerating the tag list +function! s:Tlist_Update_Window() + if !exists('b:tlist_bufnum') || !exists('b:tlist_ftype') + return + endif + + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + " Clear out the cached taglist information + call setbufvar(b:tlist_bufnum, 'tlist_valid_cache', '') + + call s:Tlist_Open_Window() + + call s:Tlist_Init_Window(b:tlist_bufnum) + + " Update the taglist window + call s:Tlist_Explore_File(b:tlist_bufnum) + + " Go back to the tag line before the list is sorted + call search(curline, 'w') +endfunction + +" Tlist_Get_Tag_Linenr() +" Return the tag line for the current line +function! s:Tlist_Get_Tag_Linenr() + if !exists('b:tlist_ftype') + return 0 + endif + + let lnum = line('.') + let ftype = b:tlist_ftype + + " Determine to which tag type the current line number belongs to using the + " tag type start line number and the number of tags in a tag type + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let end = b:tlist_{ftype}_{ttype}_start + b:tlist_{ftype}_{ttype}_count + if lnum >= b:tlist_{ftype}_{ttype}_start && lnum <= end + break + endif + let i = i + 1 + endwhile + + " Current line doesn't belong to any of the displayed tag types + if i > s:tlist_{ftype}_count + return 0 + endif + + " Compute the offset into the displayed tags for the tag type + let offset = lnum - b:tlist_{ftype}_{ttype}_start + if offset == 0 + return 0 + endif + + " Get the corresponding tag line and return it + return b:tlist_{ftype}_{ttype}_{offset} +endfunction + +function! s:Tlist_Highlight_Tagline() + " Clear previously selected name + match none + + " Highlight the current selected name + if g:Tlist_Display_Prototype == 0 + exe 'match TagName /\%' . line('.') . 'l\s\+\zs.*/' + else + exe 'match TagName /\%' . line('.') . 'l.*/' + endif +endfunction + +" Tlist_Jump_To_Tag() +" Jump to the location of the current tag +function! s:Tlist_Jump_To_Tag(new_window) + " Do not process comment lines and empty lines + let curline = getline('.') + if curline == '' || curline[0] == '"' + return + endif + + " If inside a fold, then don't try to jump to the tag + if foldclosed('.') != -1 + return + endif + + " Get the tag output for the current tag + let lnum = s:Tlist_Get_Tag_Linenr() + if lnum == 0 + return + endif + + let mtxt = b:tlist_tag_{lnum} + let start = stridx(mtxt, '/^') + 2 + let end = strridx(mtxt, '/;"' . "\t") + if mtxt[end - 1] == '$' + let end = end - 1 + endif + let tagpat = '\V\^' . strpart(mtxt, start, end - start) . + \ (mtxt[end] == '$' ? '\$' : '') + + " Highlight the tagline + call s:Tlist_Highlight_Tagline() + + let s:Tlist_Skip_Refresh = 1 + + if s:tlist_part_of_winmanager + call WinManagerFileEdit(bufname(b:tlist_bufnum), a:new_window) + else + " Goto the window containing the file. If the window is not there, open a + " new window + let winnum = bufwinnr(b:tlist_bufnum) + if winnum == -1 + if g:Tlist_Use_Horiz_Window == 1 + exe 'leftabove split #' . b:tlist_bufnum + " Go to the taglist window to change the window size to the user + " configured value + wincmd p + exe 'resize ' . g:Tlist_WinHeight + " Go back to the file window + wincmd p + else + " Open the file in a window and skip refreshing the taglist window + exe 'rightbelow vertical split #' . b:tlist_bufnum + " Go to the taglist window to change the window size to the user + " configured value + wincmd p + exe 'vertical resize ' . g:Tlist_WinWidth + " Go back to the file window + wincmd p + endif + else + exe winnum . 'wincmd w' + + " If the user asked to jump to the tag in a new window, then split the + " existing window into two. + if a:new_window + split + endif + endif + endif + + " Jump to the tag + silent call search(tagpat, 'w') + + " Bring the line to the middle of the window + normal! z. + + " If the line is inside a fold, open the fold + if has('folding') + if foldlevel('.') != 0 + normal zo + endif + endif + + let s:Tlist_Skip_Refresh = 0 +endfunction + +" Tlist_Show_Tag_Prototype() +" Display the prototype of the tag under the cursor +function! s:Tlist_Show_Tag_Prototype() + " If we have already display prototype in the tag window, no need to + " display it in the status line + if g:Tlist_Display_Prototype == 1 + return + endif + + " Clear the previously displayed line + echo + + " Do not process comment lines and empty lines + let curline = getline('.') + if curline == '' || curline[0] == '"' + return + endif + + " If inside a fold, then don't display the prototype + if foldclosed('.') != -1 + return + endif + + " Get the tag output line for the current tag + let lnum = s:Tlist_Get_Tag_Linenr() + if lnum == 0 + return + endif + + let mtxt = b:tlist_tag_{lnum} + + " Get the tag search pattern and display it + echo s:Tlist_Extract_Tag_Prototype(mtxt) +endfunction + +" Tlist_Locate_Tag_Text +" Locate the tag text given the line number in the source window +function! s:Tlist_Locate_Tag_Text(sort_type, linenum) + let left = 1 + let right = b:tlist_tag_count + + if a:sort_type == 'order' + " Tag list sorted by order, do a binary search comparing the line + " numbers + + " If the current line is the less than the first tag, then no need to + " search + let txt = b:tlist_tag_1 + let start = strridx(txt, 'line:') + strlen('line:') + let end = strridx(txt, "\t") + if end < start + let first_lnum = strpart(txt, start) + 0 + else + let first_lnum = strpart(txt, start, end - start) + 0 + endif + + if a:linenum < first_lnum + return "" + endif + + while left < right + let middle = (right + left + 1) / 2 + let txt = b:tlist_tag_{middle} + + let start = strridx(txt, 'line:') + strlen('line:') + let end = strridx(txt, "\t") + if end < start + let middle_lnum = strpart(txt, start) + 0 + else + let middle_lnum = strpart(txt, start, end - start) + 0 + endif + + if middle_lnum == a:linenum + let left = middle + break + endif + + if middle_lnum > a:linenum + let right = middle - 1 + else + let left = middle + endif + endwhile + else + " sorted by name, brute force method (Dave Eggum) + let closest_lnum = 0 + let final_left = 0 + while left < right + let txt = b:tlist_tag_{left} + + let start = strridx(txt, 'line:') + strlen('line:') + let end = strridx(txt, "\t") + if end < start + let lnum = strpart(txt, start) + 0 + else + let lnum = strpart(txt, start, end - start) + 0 + endif + + if lnum < a:linenum && lnum > closest_lnum + let closest_lnum = lnum + let final_left = left + elseif lnum == a:linenum + let closest_lnum = lnum + break + else + let left = left + 1 + endif + endwhile + if closest_lnum == 0 + return "" + endif + if left == right + let left = final_left + endif + endif + + return b:tlist_tag_{left} +endfunction + +" Tlist_Highlight_Tag() +" Do a binary search in the array of tag names and pick a tag entry that +" contains the current line and highlight it. The idea behind this function +" is taken from the ctags.vim script available at the Vim online website. +function! s:Tlist_Highlight_Tag(bufnum, curline) + let filename = fnamemodify(bufname(a:bufnum), ':p') + if filename == '' + return + endif + + " Make sure the taglist window is present + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + return + endif + + let bno = bufnr(g:TagList_title) + + " Make sure we have the tag listing for the current file + if getbufvar(bno, 'tlist_bufnum') != a:bufnum + return + endif + + " If there are no tags for this file, then no need to proceed further + if getbufvar(bno, 'tlist_tag_count') == 0 + return + endif + + " If part of winmanager then disable winmanager autocommands + if s:tlist_part_of_winmanager + call WinManagerSuspendAUs() + endif + + " Save the original window number + let org_winnr = winnr() + + if org_winnr == winnum + let in_taglist_window = 1 + else + let in_taglist_window = 0 + endif + + " Go to the taglist window + if !in_taglist_window + exe winnum . 'wincmd w' + endif + + " Clear previously selected name + match none + + let tag_txt = s:Tlist_Locate_Tag_Text(getbufvar(bno, 'tlist_sort_type'), + \ a:curline) + if tag_txt == "" + if !in_taglist_window + let s:Tlist_Skip_Refresh = 1 + exe org_winnr . 'wincmd w' + let s:Tlist_Skip_Refresh = 0 + endif + if s:tlist_part_of_winmanager + call WinManagerResumeAUs() + endif + return + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(tag_txt) + + " Extract the tag offset + let offset = strpart(tag_txt, 0, stridx(tag_txt, ':')) + 0 + + " Compute the line number + let lnum = b:tlist_{b:tlist_ftype}_{ttype}_start + offset + + " Goto the line containing the tag + exe lnum + + " Open the fold + if has('folding') + silent! .foldopen + endif + + " Call winline() to make sure the target line is visible in the taglist + " window. This is a side effect of calling winline(). Don't know of a + " better way to achieve this. + call winline() + + " Highlight the tag name + call s:Tlist_Highlight_Tagline() + + " Go back to the original window + if !in_taglist_window + let s:Tlist_Skip_Refresh = 1 + exe org_winnr . 'wincmd w' + let s:Tlist_Skip_Refresh = 0 + endif + + if s:tlist_part_of_winmanager + call WinManagerResumeAUs() + endif + + return +endfunction + +" Tlist_Get_Tag_Prototype_By_Line +function! s:Tlist_Get_Tag_Prototype_By_Line(linenum) + " Make sure the current file has a name + let filename = fnamemodify(bufname("%"), ':p') + if filename == '' + return "" + endif + + let linenr = a:linenum + if linenr == "" + " Default is the current line + let linenr = line('.') + endif + + " If there are no tags for this file, then no need to proceed further + if !exists("b:tlist_tag_count") || b:tlist_tag_count == 0 + return "" + endif + + " Get the tag text using the line number + let tag_txt = s:Tlist_Locate_Tag_Text(b:tlist_sort_type, linenr) + if tag_txt == "" + return "" + endif + + " Extract the tag search pattern and return it + return s:Tlist_Extract_Tag_Prototype(tag_txt) +endfunction + +" Define tag listing autocommand to automatically open the taglist window on +" Vim startup +if g:Tlist_Auto_Open + autocmd VimEnter * nested Tlist +endif + +" Define the 'Tlist' and 'TlistSync' user commands to open/close taglist +" window +command! -nargs=0 Tlist call s:Tlist_Toggle_Window(bufnr('%')) +command! -nargs=0 TlistSync call s:Tlist_Highlight_Tag(bufnr('%'), line('.')) +command! -nargs=? TlistShowPrototype echo s:Tlist_Get_Tag_Prototype_By_Line() + +" Winmanager integration + +" Initialization required for integration with winmanager +function! TagList_Start() + let s:tlist_part_of_winmanager = 1 + + if bufname('%') != '__Tag_List__' + return + endif + + let bufnum = WinManagerGetLastEditedFile() + + " If the tags for the buffer is already listed, then no need to do + " anything + if exists('b:tlist_bufnum') && bufnum == b:tlist_bufnum + return + endif + + call s:Tlist_Init_Window(bufnum) + + " Open the taglist window + call s:Tlist_Explore_File(bufnum) +endfunction + +function! TagList_IsValid() + return 0 +endfunction + +function! TagList_WrapUp() + return 0 +endfunction