diff --git a/doc/taglist.txt b/doc/taglist.txt index cb5eaa7..0886348 100644 --- a/doc/taglist.txt +++ b/doc/taglist.txt @@ -2,7 +2,7 @@ Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) For Vim version 6.0 and above -Last change: 2004 April 24 +Last change: 2004 June 1 1. Overview |taglist-intro| 2. Taglist on the internet |taglist-internet| @@ -11,9 +11,10 @@ Last change: 2004 April 24 5. Usage |taglist-using| 6. Configuration |taglist-configure| 7. Commands |taglist-commands| -8. Extending |taglist-extend| -9. FAQ |taglist-faq| -10. Todo |taglist-todo| +8. Global functions |taglist-functions| +9. Extending |taglist-extend| +10. FAQ |taglist-faq| +11. Todo |taglist-todo| ============================================================================== *taglist-intro* @@ -145,7 +146,11 @@ Add the above mapping to your ~/.vimrc file. You can also open the taglist window on startup using the following command line: > $ vim +Tlist + < +When the taglist window is opened for the first time, all the files in the +buffer list are processed and the tags for those files are displayed. + Closing the taglist window~ 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 @@ -309,6 +314,8 @@ in the taglist window. - Close a fold * Open all folds = Close all folds + [[ Jump to the beginning of the previous file + ]] Jump to the beginning of the next file q Close the taglist window ? Display help @@ -337,11 +344,16 @@ of these variables. Tlist_Ctags_Cmd~ The 'Tlist_Ctags_Cmd' variable specifies the location of the ctags utility. -By default, this variable 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. > - +The exuberant ctags tool is installed under different names in different +installations. When the taglist plugin starts up, it checks for the names +exuberant-ctags, ctags and tags in the PATH environment variable. If any one +of the named executable is found, then Tlist_Ctags_Cmd is set to that name. +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. +If the exuberant ctags tool is not found in either PATH or in the specified +location, then the taglist plugin will not be loaded. +> let Tlist_Ctags_Cmd = 'd:\tools\ctags.exe' let Tlist_Ctags_Cmd = '/usr/local/bin/ctags' < @@ -399,8 +411,8 @@ this variable is set to 1 and the tag scopes will be displayed. > let Tlist_Display_Tag_Scope = 0 < Tlist_WinWidth~ -The default width of the vertically split taglist window will be 30. This can -be changed by modifying the 'Tlist_WinWidth' variable: > +The default width of the vertically split taglist window is 30. This can be +changed by modifying the 'Tlist_WinWidth' variable: > let Tlist_WinWidth = 20 < @@ -424,6 +436,12 @@ system may hang due to a Vim limitation (explained in :help win32-problems) > let Tlist_Inc_Winwidth = 0 < +Tlist_WinHeight~ +The default height of the horizontally split taglist window is 10. This can be +changed by modifying the 'Tlist_WinHeight' variable: > + + let Tlist_WinHeight = 20 +< Tlist_Use_SingleClick~ 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 @@ -467,6 +485,13 @@ in a Vim window, the corresponding tags tree will be opened. > let Tlist_File_Fold_Auto_Close = 1 < +Tlist_Enable_Fold_Column~ +By default, the tags tree includes a fold column. If you wish to disable this +(for example, if you are working with a narrow Vim window or terminal), set +the Tlist_Enable_Fold_Column variable to 0. +> + let Tlist_Enable_Fold_Column = 1 +< Tlist_Auto_Highlight_Tag~ By default, the taglist plugin will highlight the current tag in the taglist window. If you want to disable the highlighting of the current tag, then you @@ -496,10 +521,13 @@ The taglist plugin provides the following ex-mode commands: Tlist~ Open or close (toggle) the taglist window. Opens the taglist window, if the window is not opened currently. Closes the taglist window, if the taglist -window is already opened. +window is already opened. When the taglist window is opened for the first +time, all the files in the buffer list are processed and the tags are +displayed in the taglist window. TlistClose~ -Close the taglist window. Can be used from any one of the Vim windows. +Close the taglist window. This command can be used from any one of the Vim +windows. TlistUpdate~ Update the tags for the current buffer. When a file is changed and saved to @@ -538,9 +566,71 @@ TlistSessionLoad~ Load information about files and tags stored from the specified session file and update the taglist window with those files. +============================================================================== + *taglist-functions* +8. Global functions~ + +The taglist plugin function provides several global functions that can be +invoked from other Vim plugins to interact with the taglist plugin. These +functions are described below. + +Tlist_Update_File_Tags()~ +The Tlist_Update_File_Tags() function updates the tags for the specified file. +The second argument specifies the Vim filetype for the specified file. If the +taglist plugin has not processed the specified file previously, then the +exuberant ctags tool is invoked to generate the tags for the file. + +The syntax for this function is +> + Tlist_Update_File_Tags(filename, filetype) +< +Tlist_Get_Tag_Prototype_By_Line()~ +The Tlist_Get_Tag_Prototype_By_Line() function returns the prototype of the +tag at or below the specified line number in the specified file. If the +filename and line number are not specified, then the current buffer name and +the current line number are used. + +The syntax for this function is +> + Tlist_Get_Tag_Prototype_By_Line([filename, linenumber]) +< +Tlist_Get_Tagname_By_Line()~ +The Tlist_Get_Tagname_By_Line() function returns the name of the tag at or +below the specified line number in the specified file. If the filename and +line number are not specified, then the current buffer name and the current +line number are used. + +The syntax for this function is +> + Tlist_Get_Tagname_By_Line([filename, linenumber]) +< +This function can be used to display the current tag name in the status line +or in the title bar. For example, the following 'statusline' setting will +display the current tag name in the status line: +> + set statusline=%<%f\ %h%m%r%{Tlist_Get_Tagname_By_Line()}%=%-14.(%l,%c%V%)\ %P +< +Note that the above 'statusline' setting will work only after the file is +processed by the taglist plugin. For this, you have to either open the taglist +window or you have to set the 'Tlist_Process_File_Always' option. + +Tlist_Set_App()~ +The Tlist_Set_App() function sets the name of the plugin that controls the +taglist plugin window and buffer. For example, the winmanager plugin or the +cream package use the taglist plugin and control the taglist window and +buffer. These two plugins invoke this function and specify the name as +"winmanager" or "cream" respectively. By default, the taglist plugin is a +standalone plugin and controls the taglist window and buffer. If the taglist +window is controlled by an external plugin, then the appname should be set +appropriately. + +The syntax for this function is +> + Tlist_Set_App(appname) +< ============================================================================== *taglist-extend* -8. Extending~ +9. Extending~ The taglist plugin supports all the languages supported by the exuberant ctags tool, which includes the following languages: Assembly ASP, Awk, Beta, C, C++, @@ -621,7 +711,7 @@ flags b, c and l respectively. ============================================================================== *taglist-faq* -9. Frequently Asked Questions~ +10. Frequently Asked Questions~ Q. The taglist plugin doesn't work. The taglist window is empty and the tags defined in a file are not displayed. @@ -652,6 +742,11 @@ A. Are you using Vim version 6.0 and above? The taglist plugin relies on the utility with the path to the file, if the incorrect slashes are used, then you will see errors. + Are you using a Unix shell in a MS-Windows environment? For example, + the Unix shell from the MKS-toolkit. Do you have the SHELL environment + set to point to this shell? You can try resetting the SHELL environment + variable. + Is your filetype supported by the exuberant ctags utility? The file types supported by the exuberant ctags utility is listed in the ctags help. If a file type is not supported, you have to extend exuberant ctags. @@ -671,11 +766,11 @@ A. The taglist plugin uses the search pattern generated by the exuberant ctags pattern, the cursor may be positioned at the incorrect location. Q. I have made some modifications to my file and introduced new - functions/classes/variables. I have not yet saved my file. -A. The taglist plugin is not displaying the new tags when I update the taglist - window. The exuberant ctags utility will process only files that are - present in the disk. To list the tags defined in a file, you have to save - the file and then update the taglist window. + functions/classes/variables. I have not yet saved my file. The taglist + plugin is not displaying the new tags when I update the taglist window. +A. The exuberant ctags utility will process only files that are present in the + disk. To list the tags defined in a file, you have to save the file and + then update the taglist window. Q. I have created a ctags file using the exuberant ctags utility for my source tree. How do I configure the taglist plugin to use this tags file? @@ -685,7 +780,7 @@ A. The taglist plugin doesn't use the tags file generated by exuberant ctags. ============================================================================== *taglist-todo* -10. Todo~ +11. Todo~ 1. Support for displaying tags in a modified (not-yet-saved) file. 2. Group tags according to the scope and display them. For example, diff --git a/plugin/taglist.vim b/plugin/taglist.vim index adfb1e8..c45a510 100644 --- a/plugin/taglist.vim +++ b/plugin/taglist.vim @@ -1,7 +1,7 @@ " File: taglist.vim " Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) -" Version: 3.2 -" Last Modified: April 25, 2004 +" Version: 3.3 +" Last Modified: May 26, 2004 " " 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 @@ -53,7 +53,17 @@ let loaded_taglist=1 " Location of the exuberant ctags tool if !exists('Tlist_Ctags_Cmd') - let Tlist_Ctags_Cmd = 'ctags' + if executable('exuberant-ctags') + let Tlist_Ctags_Cmd = 'exuberant-ctags' + elseif executable('ctags') + let Tlist_Ctags_Cmd = 'ctags' + elseif executable('tags') + let Tlist_Ctags_Cmd = 'tags' + else + echomsg 'Taglist: Exuberant ctags not found in PATH. ' . + \ 'Plugin is not loaded.' + finish + endif endif " Tag listing sort type - 'name' or 'order' @@ -142,6 +152,11 @@ if !exists('Tlist_Process_File_Always') let Tlist_Process_File_Always = 0 endif +" Enable fold column to display the folding for the tag tree +if !exists('Tlist_Enable_Fold_Column') + let Tlist_Enable_Fold_Column = 1 +endif + "------------------- end of user configurable options -------------------- " Initialize the taglist plugin local variables for the supported file types @@ -186,7 +201,7 @@ let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature' let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function' " expect (same as tcl) language -let s:tlist_def_expect_settings = 'expect;c:class;f:method;p:procedure' +let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure' " fortran language let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' . @@ -283,10 +298,8 @@ let s:tlist_win_maximized = 0 let s:tlist_file_count = 0 " Number of filetypes supported by taglist let s:tlist_ftype_count = 0 -" Current active file index -let s:tlist_cur_fidx = -1 -" Is taglist part of the winmanager plugin -let s:tlist_part_of_winmanager = 0 +" Is taglist part of other plugins like winmanager or cream? +let s:tlist_app_name = "none" " Are we displaying brief help text let s:tlist_brief_help = 1 " Do not change the name of the taglist title variable. The winmanager plugin @@ -302,7 +315,7 @@ let s:Tlist_Skip_Refresh = 0 " Tlist_Display_Help() function! s:Tlist_Display_Help() - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" " To handle a bug in the winmanager plugin, add a space at the " last line call setline('$', ' ') @@ -325,8 +338,10 @@ function! s:Tlist_Display_Help() call append(9, '" - : Close a fold') call append(10, '" * : Open all folds') call append(11, '" = : Close all folds') - call append(12, '" q : Close the taglist window') - call append(13, '" ? : Remove help text') + call append(12, '" [[ : Move to the start of previous file') + call append(13, '" ]] : Move to the start of next file') + call append(14, '" q : Close the taglist window') + call append(15, '" ? : Remove help text') endif endfunction @@ -341,7 +356,7 @@ function! s:Tlist_Toggle_Help_Text() " Include the empty line displayed after the help text let brief_help_size = 1 - let full_help_size = 14 + let full_help_size = 16 setlocal modifiable @@ -407,6 +422,7 @@ endfunction " Tlist_Get_File_Index_By_Linenum() " Return the index of the filename present in the specified line number +" Line number refers to the line number in the taglist window function! s:Tlist_Get_File_Index_By_Linenum(lnum) let i = 0 @@ -477,7 +493,7 @@ function! s:Tlist_FileType_Init(ftype) let settings = s:tlist_def_{a:ftype}_settings . ';' endif - let msg = 'Invalid ctags option setting - ' . settings + let msg = 'Taglist: Invalid ctags option setting - ' . settings " Format of the option that specifies the filetype and ctags arugments: " @@ -635,6 +651,7 @@ function! s:Tlist_Discard_FileInfo(fidx) unlet! s:tlist_{a:fidx}_filename unlet! s:tlist_{a:fidx}_sort_type unlet! s:tlist_{a:fidx}_filetype + unlet! s:tlist_{a:fidx}_mtime unlet! s:tlist_{a:fidx}_start unlet! s:tlist_{a:fidx}_end unlet! s:tlist_{a:fidx}_valid @@ -667,19 +684,17 @@ endfunction " Tlist_Remove_File " Remove the file under the cursor or the specified file index -function! s:Tlist_Remove_File(file_idx, update_display) - if a:file_idx == -1 +function! s:Tlist_Remove_File(file_idx) + let fidx = a:file_idx + + if fidx == -1 let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.')) if fidx == -1 return endif - else - let fidx = a:file_idx endif - if a:update_display - call s:Tlist_Remove_File_From_Display(fidx) - endif + call s:Tlist_Remove_File_From_Display(fidx) call s:Tlist_Discard_FileInfo(fidx) @@ -692,6 +707,7 @@ function! s:Tlist_Remove_File(file_idx, update_display) let s:tlist_{j}_filename = s:tlist_{i}_filename let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type let s:tlist_{j}_filetype = s:tlist_{i}_filetype + let s:tlist_{j}_mtime = s:tlist_{i}_mtime let s:tlist_{j}_start = s:tlist_{i}_start let s:tlist_{j}_end = s:tlist_{i}_end let s:tlist_{j}_valid = s:tlist_{i}_valid @@ -722,6 +738,8 @@ function! s:Tlist_Remove_File(file_idx, update_display) let k = k + 1 endwhile + " As the file and tag information is copied to the new index, + " discard the previous information call s:Tlist_Discard_FileInfo(i) let i = i + 1 @@ -733,66 +751,67 @@ endfunction " Tlist_Open_Window -" Create a new taglist window. If it is already open, clear it +" Create a new taglist window. If it is already open, jump to 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 + if s:tlist_app_name == "winmanager" return endif - " Cleanup the taglist window listing, if the window is open + " If the window is open, jump to it 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 - " Open a horizontally split window - let win_dir = 'botright' - " Horizontal window height - let win_size = g:Tlist_WinHeight - else - " Open a horizontally split window. Increase the window size, if - " needed, to accomodate the new window - if g:Tlist_Inc_Winwidth && - \ &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 - - if g:Tlist_Use_Right_Window - " Open the window at the rightmost place - let win_dir = 'botright vertical' - else - " Open the window at the leftmost place - 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 + return endif + + " 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 + " Open a horizontally split window + let win_dir = 'botright' + " Horizontal window height + let win_size = g:Tlist_WinHeight + else + " Open a horizontally split window. Increase the window size, if + " needed, to accomodate the new window + if g:Tlist_Inc_Winwidth && + \ &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 + + if g:Tlist_Use_Right_Window + " Open the window at the rightmost place + let win_dir = 'botright vertical' + else + " Open the window at the leftmost place + 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 endfunction " Tlist_Zoom_Window @@ -821,28 +840,6 @@ endfunction " Tlist_Init_Window " Set the default options for the taglist window function! s:Tlist_Init_Window() - " Set report option to a huge value to prevent informational 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 _ - - if g:Tlist_Compact_Format == 0 - " Display help in non-compact mode - call s:Tlist_Display_Help() - endif - - " Mark the buffer as not modifiable - setlocal nomodifiable - - " Restore the report option - let &report = old_report - " Define taglist window element highlighting if has('syntax') syntax match TagListComment '^" .*' @@ -895,21 +892,27 @@ function! s:Tlist_Init_Window() setlocal foldenable setlocal foldminlines=0 setlocal foldmethod=manual - setlocal foldcolumn=3 + if g:Tlist_Enable_Fold_Column + setlocal foldcolumn=3 + else + setlocal foldcolumn=0 + endif 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 + if s:tlist_app_name != "winmanager" + " Mark buffer as scratch + silent! setlocal buftype=nofile + if s:tlist_app_name == "none" + silent! setlocal bufhidden=delete + endif + 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 @@ -934,8 +937,10 @@ function! s:Tlist_Init_Window() nnoremap :silent! %foldopen! nnoremap :call Tlist_Show_Tag_Prototype() nnoremap u :call Tlist_Update_Window() - nnoremap d :call Tlist_Remove_File(-1, 1) + nnoremap d :call Tlist_Remove_File(-1) nnoremap x :call Tlist_Zoom_Window() + nnoremap [[ :call Tlist_Move_To_File(-1) + nnoremap ]] :call Tlist_Move_To_File(1) nnoremap ? :call Tlist_Toggle_Help_Text() nnoremap q :close @@ -958,8 +963,10 @@ function! s:Tlist_Init_Window() inoremap :call \ Tlist_Show_Tag_Prototype() inoremap u :call Tlist_Update_Window() - inoremap d :call Tlist_Remove_File(-1, 1) + inoremap d :call Tlist_Remove_File(-1) inoremap x :call Tlist_Zoom_Window() + inoremap [[ :call Tlist_Move_To_File(-1) + inoremap ]] :call Tlist_Move_To_File(1) inoremap ? :call Tlist_Toggle_Help_Text() inoremap q :close @@ -987,11 +994,37 @@ function! s:Tlist_Init_Window() \ fnamemodify(expand(''), ':p'), 2) " Exit Vim itself if only the taglist window is present (optional) autocmd BufEnter __Tag_List__ call Tlist_Check_Only_Window() - if !s:tlist_part_of_winmanager && !g:Tlist_Process_File_Always + if s:tlist_app_name != "winmanager" && !g:Tlist_Process_File_Always " Auto refresh the taglist window autocmd BufEnter * call Tlist_Refresh() endif augroup end +endfunction + +" Tlist_Refresh_Window +" Display the tags for all the files in the taglist window +function! s:Tlist_Refresh_Window() + " Set report option to a huge value to prevent informational 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 _ + + if g:Tlist_Compact_Format == 0 + " Display help in non-compact mode + call s:Tlist_Display_Help() + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report " List all the tags for the previously processed files let i = 0 @@ -1011,7 +1044,6 @@ function! s:Tlist_Init_Window() silent! %foldclose endif endif - endfunction " Tlist_Post_Close_Cleanup() @@ -1043,7 +1075,7 @@ function! s:Tlist_Post_Close_Cleanup() endif endif - if !s:tlist_part_of_winmanager + if s:tlist_app_name != "winmanager" if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 || \ s:tlist_winsize_chgd == 0 || \ &columns < (80 + g:Tlist_WinWidth) @@ -1057,9 +1089,8 @@ function! s:Tlist_Post_Close_Cleanup() endif " Reset taglist state variables - let s:tlist_cur_fidx = -1 - if s:tlist_part_of_winmanager - let s:tlist_part_of_winmanager = 0 + if s:tlist_app_name == "winmanager" + let s:tlist_app_name = "none" let s:tlist_window_initialized = 0 endif endfunction @@ -1091,9 +1122,6 @@ function! s:Tlist_Explore_File(filename, ftype) if file_exists && s:tlist_{fidx}_visible " Check whether the file tags are currently valid if s:tlist_{fidx}_valid - " Make the selected file as the current file - let s:tlist_cur_fidx = fidx - " Goto the first line in the file exe s:tlist_{fidx}_start @@ -1119,9 +1147,6 @@ function! s:Tlist_Explore_File(filename, ftype) let fidx = ret_fidx endif - " Make the selected file as the current file - let s:tlist_cur_fidx = fidx - " Set report option to a huge value to prevent informational messages " while adding lines to the taglist window let old_report = &report @@ -1213,7 +1238,7 @@ function! s:Tlist_Explore_File(filename, ftype) " Goto the starting line for this file, exe s:tlist_{fidx}_start - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" " To handle a bug in the winmanager plugin, add a space at the " last line call setline('$', ' ') @@ -1250,6 +1275,7 @@ function! s:Tlist_Init_File(filename, ftype) let s:tlist_{fidx}_filename = a:filename let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type let s:tlist_{fidx}_filetype = a:ftype + let s:tlist_{fidx}_mtime = -1 let s:tlist_{fidx}_start = 0 let s:tlist_{fidx}_end = 0 let s:tlist_{fidx}_valid = 0 @@ -1292,6 +1318,9 @@ function! s:Tlist_Process_File(filename, ftype) if fidx == -1 " First time, this file is loaded let fidx = s:Tlist_Init_File(a:filename, a:ftype) + else + " File was previously processed. Discard the tag information + call s:Tlist_Discard_TagInfo(fidx) endif let s:tlist_{fidx}_valid = 1 @@ -1331,16 +1360,21 @@ function! s:Tlist_Process_File(filename, ftype) " Handle errors if v:shell_error && cmd_output != '' + let msg = "Taglist: Failed to generate tags for " . a:filename + call s:Tlist_Warning_Msg(msg) call s:Tlist_Warning_Msg(cmd_output) return fidx endif " No tags for current file if cmd_output == '' - call s:Tlist_Warning_Msg('No tags found for ' . a:filename) + call s:Tlist_Warning_Msg('Taglist: No tags found for ' . a:filename) return fidx endif + " Store the modification time for the file + let s:tlist_{fidx}_mtime = getftime(a:filename) + " 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 " The format of each line in the ctags output is: @@ -1349,9 +1383,10 @@ function! s:Tlist_Process_File(filename, ftype) " while cmd_output != '' " Extract one line at a time - let one_line = strpart(cmd_output, 0, stridx(cmd_output, "\n")) + let idx = stridx(cmd_output, "\n") + let one_line = strpart(cmd_output, 0, idx) " Remove the line from the tags output - let cmd_output = strpart(cmd_output, stridx(cmd_output, "\n") + 1) + let cmd_output = strpart(cmd_output, idx + 1) if one_line == '' " Line is not in proper tags format @@ -1368,6 +1403,7 @@ function! s:Tlist_Process_File(filename, ftype) " make sure the tag type is supported if s:tlist_{a:ftype}_ctags_flags !~# ttype + " Tag type is not supported continue endif @@ -1378,25 +1414,13 @@ function! s:Tlist_Process_File(filename, ftype) " Add the tag scope, if it is available. Tag scope is the last " field after the 'line:\t' field if g:Tlist_Display_Tag_Scope " only if it is selected - 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 + let tag_scope = s:Tlist_Extract_Tag_Scope(one_line) + if tag_scope != '' + let ttxt = ttxt . ' [' . tag_scope . ']' endif endif else - let start = stridx(one_line, '/^') + 2 - let end = strridx(one_line, '/;"' . "\t") - " The search patterns for some tag types doesn't end with - " the ;" character - if one_line[end - 1] == '$' - let end = end -1 - endif - let ttxt = strpart(one_line, start, end - start) + let ttxt = s:Tlist_Extract_Tag_Prototype(one_line) endif " Update the count of this tag type @@ -1420,17 +1444,41 @@ endfunction " Tlist_Update_File_Tags " Update the tags for a file (if needed) -function s:Tlist_Update_File_Tags(filename, ftype) - " First check whether the file already exists - let fidx = s:Tlist_Get_File_Index(a:filename) - - " File exists and the tags are valid - if fidx != -1 && s:tlist_{fidx}_valid +function! Tlist_Update_File_Tags(filename, ftype) + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(a:filename, a:ftype) return endif - " Process and generate a list of tags defined in the file - call s:Tlist_Process_File(a:filename, a:ftype) + " First check whether the file already exists + let fidx = s:Tlist_Get_File_Index(a:filename) + + if fidx != -1 && s:tlist_{fidx}_valid + " File exists and the tags are valid + return + endif + + " If the taglist window is opened, update it + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + " Taglist window is not present. Just update the taglist + " and return + call s:Tlist_Process_File(a:filename, a:ftype) + else + " Save the current window number + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Open_Window() + + " Update the taglist window + call s:Tlist_Explore_File(a:filename, a:ftype) + + if winnr() != save_winnr + " Go back to the original window + exe save_winnr . 'wincmd w' + endif + endif endfunction " Tlist_Close_Window @@ -1467,7 +1515,7 @@ endfunction " Tlist_Toggle_Window() " Open or close a taglist window -function! s:Tlist_Toggle_Window(bufnum) +function! s:Tlist_Toggle_Window() let curline = line('.') " If taglist window is open then close it. @@ -1477,12 +1525,14 @@ function! s:Tlist_Toggle_Window(bufnum) return endif - " We are not part of winmanager plugin - let s:tlist_part_of_winmanager = 0 + if s:tlist_app_name == "winmanager" + " Taglist plugin is no longer part of the winmanager app + let s:tlist_app_name = "none" + endif " Get the filename and filetype for the specified buffer - let filename = fnamemodify(bufname(a:bufnum), ':p') - let ftype = getbufvar(a:bufnum, '&filetype') + let curbuf_name = fnamemodify(bufname('%'), ':p') + let curbuf_ftype = getbufvar('%', '&filetype') " Mark the current window as the desired window to open a file " when a tag is selcted @@ -1493,12 +1543,19 @@ function! s:Tlist_Toggle_Window(bufnum) " Initialize the taglist window call s:Tlist_Init_Window() + call s:Tlist_Refresh_Window() - " List the tags defined in the file - call s:Tlist_Explore_File(filename, ftype) + " Add and list the tags for all the buffers in the bufferlist + let i = 1 + while i < bufnr('$') + let fname = fnamemodify(bufname(i), ':p') + let ftype = getbufvar(i, '&filetype') + call s:Tlist_Explore_File(fname, ftype) + let i = i + 1 + endwhile " Highlight the current tag - call s:Tlist_Highlight_Tag(filename, curline, 1) + call s:Tlist_Highlight_Tag(curbuf_name, curline, 1) " Go back to the original window let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh @@ -1525,25 +1582,32 @@ endfunction function! s:Tlist_Extract_Tag_Prototype(tag_txt) let start = stridx(a:tag_txt, '/^') + 2 let end = strridx(a:tag_txt, '/;"' . "\t") + " The search patterns for some tag types doesn't end with + " the ;" character 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.*') + let tag_pat = substitute(tag_pat, '\s*', '', '') return tag_pat endfunction -" Tlist_Add_File() -" Add a new file to the taglist -function! s:Tlist_Add_File(filename, ftype) - " Goto the taglist window - call s:Tlist_Open_Window() +" Tlist_Extract_Tag_Scope +" Extract the tag scope from the tag text +function! s:Tlist_Extract_Tag_Scope(tag_txt) + let start = strridx(a:tag_txt, 'line:') + let end = strridx(a:tag_txt, "\t") + if end <= start + return '' + endif - " Update the taglist window - call s:Tlist_Explore_File(a:filename, a:ftype) + let tag_scope = strpart(a:tag_txt, end + 1) + let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1) + + return tag_scope endfunction " Tlist_Refresh() @@ -1551,13 +1615,12 @@ endfunction function! s:Tlist_Refresh() " If we are entering the buffer from one of the taglist functions, then " no need to refresh the taglist window again. - if s:Tlist_Skip_Refresh || s:tlist_part_of_winmanager + if s:Tlist_Skip_Refresh || (s:tlist_app_name == "winmanager") return endif " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help if &buftype != '' - let s:tlist_cur_fidx = -1 return endif @@ -1566,7 +1629,6 @@ function! s:Tlist_Refresh() " If the file doesn't support tag listing, skip it if s:Tlist_Skip_File(filename, ftype) - let s:tlist_cur_fidx = -1 return endif @@ -1576,25 +1638,36 @@ function! s:Tlist_Refresh() let winnum = bufwinnr(g:TagList_title) if winnum == -1 if g:Tlist_Process_File_Always - call s:Tlist_Update_File_Tags(filename, ftype) + call Tlist_Update_File_Tags(filename, ftype) endif return endif let fidx = s:Tlist_Get_File_Index(filename) if fidx != -1 + let mtime = getftime(filename) + if s:tlist_{fidx}_mtime != mtime + " Invalidate the tags listed for this file + let s:tlist_{fidx}_valid = 0 + + " Update the taglist window + call Tlist_Update_File_Tags(s:tlist_{fidx}_filename, + \ s:tlist_{fidx}_filetype) + + " Store the new file modification time + let s:tlist_{fidx}_mtime = mtime + endif + " If the tag listing for the current window is already present, no " need to refresh it if !g:Tlist_Auto_Highlight_Tag return endif - if s:tlist_cur_fidx == fidx - " Highlight the current tag - call s:Tlist_Highlight_Tag(filename, curline, 1) + " Highlight the current tag + call s:Tlist_Highlight_Tag(filename, curline, 1) - return - endif + return endif " Save the current window number @@ -1623,7 +1696,7 @@ function! s:Tlist_Refresh() " Refresh the taglist window redraw - if !s:tlist_part_of_winmanager + if s:tlist_app_name != "winmanager" " Jump back to the original window exe cur_winnr . 'wincmd w' endif @@ -1632,15 +1705,14 @@ endfunction " Tlist_Change_Sort() " Change the sort order of the tag listing function! s:Tlist_Change_Sort() - if s:tlist_cur_fidx == -1 + let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 return endif " Remove the previous highlighting match none - let fidx = s:tlist_cur_fidx - let sort_type = s:tlist_{fidx}_sort_type " Toggle the sort order from 'name' to 'order' and vice versa @@ -1658,7 +1730,7 @@ function! s:Tlist_Change_Sort() call s:Tlist_Explore_File(s:tlist_{fidx}_filename, s:tlist_{fidx}_filetype) - " Go back to the tag line before the list is sorted + " Go back to the cursor line before the tag list is sorted call search(curline, 'w') endfunction @@ -1666,39 +1738,19 @@ endfunction " Update taglist for the current buffer by regenerating the tag list " Contributed by WEN Guopeng. function! s:Tlist_Update_Tags() - " If taglist window is not open, show an error message: - let winnum = bufwinnr(g:TagList_title) - if winnum == -1 - call s:Tlist_Warning_Msg('Error: Taglist window is not open') - return 0 - endif - - " Update the tag list window only if it's open - if winnr() == winnum - " Already in the taglist window, simply update the window content + if winnr() == bufwinnr(g:TagList_title) + " In the taglist window. Update the current file call s:Tlist_Update_Window() + return else - " First check the current buffer is modified or not: - if &modified - let msg = "No write since last change, tag list may be inaccurate" - call s:Tlist_Warning_Msg(msg) - endif - - " Goto the taglist window, update it and get back to the original - " window: - let curbufnr = bufnr('%') - exe winnum . 'wincmd w' - call s:Tlist_Update_Window() - - " 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' + " Not in the taglist window. Update the current buffer + let filename = fnamemodify(bufname('%'), ':p') + let fidx = s:Tlist_Get_File_Index(filename) + if fidx != -1 + let s:tlist_{fidx}_valid = 0 endif + call Tlist_Update_File_Tags(filename, &filetype) endif - - return 1 endfunction " Tlist_Update_Window() @@ -1720,7 +1772,7 @@ function! s:Tlist_Update_Window() " Update the taglist window call s:Tlist_Explore_File(s:tlist_{fidx}_filename, s:tlist_{fidx}_filetype) - " Go back to the tag line before the list is sorted + " Go back to the tag line before the list is updated call search(curline, 'w') endfunction @@ -1794,15 +1846,9 @@ function! s:Tlist_Jump_To_Tag(win_ctrl) return endif - if fidx != s:tlist_cur_fidx - " Selected a tag from some other file. Switch to editing this file - let s:tlist_cur_fidx = fidx - endif - " Get the tag output for the current tag let tidx = s:Tlist_Get_Tag_Index(fidx) if tidx != 0 - let fidx = s:tlist_cur_fidx let mtxt = s:tlist_{fidx}_tag_{tidx} let start = stridx(mtxt, '/^') + 2 let end = strridx(mtxt, '/;"' . "\t") @@ -1829,7 +1875,7 @@ function! s:Tlist_Open_File(win_ctrl, filename, tagpat) let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh let s:Tlist_Skip_Refresh = 1 - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" " Let the winmanager edit the file call WinManagerFileEdit(a:filename, a:win_ctrl) else @@ -2083,7 +2129,7 @@ function! s:Tlist_Highlight_Tag(filename, curline, cntx) endif " If part of winmanager then disable winmanager autocommands - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" call WinManagerSuspendAUs() endif @@ -2128,7 +2174,7 @@ function! s:Tlist_Highlight_Tag(filename, curline, cntx) exe org_winnr . 'wincmd w' let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh endif - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" call WinManagerResumeAUs() endif return @@ -2167,7 +2213,7 @@ function! s:Tlist_Highlight_Tag(filename, curline, cntx) let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh endif - if s:tlist_part_of_winmanager + if s:tlist_app_name == "winmanager" call WinManagerResumeAUs() endif @@ -2177,27 +2223,42 @@ endfunction " Tlist_Get_Tag_Prototype_By_Line " Get the prototype for the tag on or before the specified line number in the " current buffer -function! Tlist_Get_Tag_Prototype_By_Line(linenum) - " Make sure the current file has a name - let filename = fnamemodify(bufname("%"), ':p') +function! Tlist_Get_Tag_Prototype_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tag_Prototype_By_Line ' . + \ '' + call s:Tlist_Warning_Msg(msg) + return "" + endif + + " Expand the file to a fully qualified name + let filename = fnamemodify(filename, ':p') if filename == '' return "" endif let fidx = s:Tlist_Get_File_Index(filename) if fidx == -1 - return + return "" endif " If there are no tags for this file, then no need to proceed further if s:tlist_{fidx}_tag_count == 0 - return - endif - - let linenr = a:linenum - if linenr == "" - " Default is the current line - let linenr = line('.') + return "" endif " Get the tag text using the line number @@ -2213,27 +2274,41 @@ endfunction " Tlist_Get_Tagname_By_Line " Get the tag name on or before the specified line number in the " current buffer -function! Tlist_Get_Tagname_By_Line(linenum) +function! Tlist_Get_Tagname_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tagname_By_Line ' + call s:Tlist_Warning_Msg(msg) + return "" + endif + " Make sure the current file has a name - let filename = fnamemodify(bufname("%"), ':p') + let filename = fnamemodify(filename, ':p') if filename == '' return "" endif let fidx = s:Tlist_Get_File_Index(filename) if fidx == -1 - return + return "" endif " If there are no tags for this file, then no need to proceed further if s:tlist_{fidx}_tag_count == 0 - return - endif - - let linenr = a:linenum - if linenr == "" - " Default is the current line - let linenr = line('.') + return "" endif " Get the tag name using the line number @@ -2249,6 +2324,62 @@ function! Tlist_Get_Tagname_By_Line(linenum) return strpart(tag_txt, start, stridx(tag_txt, "\t") - start) endfunction +" Tlist_Move_To_File +" Move the cursor to the beginning of the current file or the next file +" or the previous file in the taglist window +" dir == -1, move to start of current or previous function +" dir == 1, move to start of next function +function! s:Tlist_Move_To_File(dir) + if foldlevel('.') == 0 + " Cursor is on a non-folded line (it is not in any of the files) + " Move it to a folded line + if a:dir == -1 + normal! zk + else + " While moving down to the start of the next fold, + " no need to do go to the start of the next file. + normal! zj + return + endif + endif + + let fidx = s:Tlist_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + let cur_lnum = line('.') + + if a:dir == -1 + if cur_lnum > s:tlist_{fidx}_start + " Move to the beginning of the current file + exe s:tlist_{fidx}_start + return + endif + + if fidx == 0 + " At the first file, can't move to previous file + return + endif + + " Otherwise, move to the beginning of the previous file + let fidx = fidx - 1 + exe s:tlist_{fidx}_start + return + else + let fidx = fidx + 1 + + if fidx == s:tlist_file_count + " At the last file, can't move to the next file + return + endif + + " Otherwise, move to the beginning of the next file + exe s:tlist_{fidx}_start + return + endif +endfunction + " Tlist_Session_Load " Load a taglist session (information about all the displayed files " and the tags) from the specified file @@ -2261,7 +2392,8 @@ function! s:Tlist_Session_Load(...) let sessionfile = a:1 if !filereadable(sessionfile) - call s:Tlist_Warning_Msg('Error: Unable to open file ' . sessionfile) + let msg = 'Taglist: Error - Unable to open file ' . sessionfile + call s:Tlist_Warning_Msg(msg) return endif @@ -2309,6 +2441,7 @@ function! s:Tlist_Session_Load(...) unlet! g:tlist_{i}_sort_type let s:tlist_{fidx}_filetype = ftype + let s:tlist_{fidx}_mtime = getftime(fname) let s:tlist_{fidx}_start = 0 let s:tlist_{fidx}_end = 0 @@ -2359,6 +2492,7 @@ function! s:Tlist_Session_Load(...) " Initialize the taglist window call s:Tlist_Init_Window() + call s:Tlist_Refresh_Window() if s:tlist_file_count > 0 " Jump to the beginning of the first file @@ -2460,7 +2594,7 @@ function! s:Tlist_Update_File_Display(filename, action) " Make sure the taglist window is present let winnum = bufwinnr(g:TagList_title) if winnum == -1 - call s:Tlist_Warning_Msg('Error: Taglist window is not open') + call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open') return endif @@ -2496,7 +2630,7 @@ function! s:Tlist_Update_File_Display(filename, action) endif elseif a:action == 2 " Remove the file from the list - call s:Tlist_Remove_File(idx, 1) + call s:Tlist_Remove_File(idx) endif " Move the cursor to the original location @@ -2524,44 +2658,54 @@ if g:Tlist_Process_File_Always endif " Define the user commands to manage the taglist window -command! -nargs=0 Tlist call s:Tlist_Toggle_Window(bufnr('%')) +command! -nargs=0 Tlist call s:Tlist_Toggle_Window() command! -nargs=0 TlistClose call s:Tlist_Close_Window() command! -nargs=0 TlistUpdate call s:Tlist_Update_Tags() command! -nargs=0 TlistSync call s:Tlist_Highlight_Tag( \ fnamemodify(bufname('%'), ':p'), line('.'), 2) -command! -nargs=? TlistShowPrototype echo Tlist_Get_Tag_Prototype_By_Line() -command! -nargs=? TlistShowTag echo Tlist_Get_Tagname_By_Line() -command! -nargs=* -complete=file TlistSessionLoad call s:Tlist_Session_Load() -command! -nargs=* -complete=file TlistSessionSave call s:Tlist_Session_Save() +command! -nargs=* -complete=buffer TlistShowPrototype + \ echo Tlist_Get_Tag_Prototype_By_Line() +command! -nargs=* -complete=buffer TlistShowTag + \ echo Tlist_Get_Tagname_By_Line() +command! -nargs=* -complete=file TlistSessionLoad + \ call s:Tlist_Session_Load() +command! -nargs=* -complete=file TlistSessionSave + \ call s:Tlist_Session_Save() + +" Tlist_Set_App +" Set the name of the external plugin/application to which taglist +" belongs. +" Taglist plugin is part of another plugin like cream or winmanager. +function! Tlist_Set_App(name) + if a:name == "" + return + endif + + let s:tlist_app_name = a:name +endfunction " Winmanager integration " Initialization required for integration with winmanager function! TagList_Start() - let s:tlist_part_of_winmanager = 1 - + " If current buffer is not taglist buffer, then don't proceed if bufname('%') != '__Tag_List__' return endif + call Tlist_Set_App("winmanager") + " Get the current filename from the winmanager plugin let bufnum = WinManagerGetLastEditedFile() if bufnum != -1 let filename = fnamemodify(bufname(bufnum), ':p') - let fidx = s:Tlist_Get_File_Index(filename) - - if fidx != -1 && fidx == s:tlist_cur_fidx - " If the tags for the buffer is already listed, then no need to do - " anything - return - endif - let ftype = getbufvar(bufnum, '&filetype') endif " Initialize the taglist window, if it is not already initialized if !exists("s:tlist_window_initialized") || !s:tlist_window_initialized call s:Tlist_Init_Window() + call s:Tlist_Refresh_Window() let s:tlist_window_initialized = 1 endif