Skip to content
Browse files

Version 2.1

Improved the startup time and memory usage.  Automatically turn the status line on when displaying the tag name in the status line.  Both of those features were contributed by Keith Reynolds.  Changed the types of tags displayed for C and vim files.  Added updating of the ctags database when a file is written.  Mostly fixed a problem where the script caused the cursor to not remember its column position as it was moved vertically.  Details on the script page.
  • Loading branch information...
1 parent ecc69df commit bef1afe325a95b7479c1018f032e42e75335673f @ghgary ghgary committed with
Showing with 215 additions and 66 deletions.
  1. +215 −66 plugin/ctags.vim
View
281 plugin/ctags.vim
@@ -1,9 +1,11 @@
" ctags.vim: Display function name in the title bar and/or status line.
" Author: Alexey Marinichev <lyosha-vim@lyosha.no-ip.org>
-" Contributor: Gary Johnson <garyjohn@spk.agilent.com>
-" Last Change: 2003-04-02 21:22:14
-" Version: 2.0
-" URL: http://vim.sourceforge.net/scripts/script.php?script_id=12
+" Maintainer: Gary Johnson <garyjohn@spk.agilent.com>
+" Contributor: Keith Reynolds
+" Last Change: 2003-11-26 00:23:22
+" Version: 2.1
+" URL(1.0): http://vim.sourceforge.net/scripts/script.php?script_id=12
+" URL(>=2.0): http://vim.sourceforge.net/scripts/script.php?script_id=610
" DETAILED DESCRIPTION:
" This script uses exuberant ctags to build the list of tags for the current
@@ -18,11 +20,16 @@
"
" Function GetTagName takes line number argument and returns the tag name.
"
-" Function SetStatusline sets the value of 'statusline'.
+" Function SetTagDisplay sets the values of 'statusline' and
+" 'titlestring'.
"
" Function TagName returns the cached tag name.
"
" INSTALL DETAILS:
+" This script requires exuberant ctags to be installed on your system. If
+" it is not already installed, you can obtain the source from
+" http://ctags.sourceforge.net/.
+"
" Before sourcing the script do:
" let g:ctags_path='/path/to/ctags'
" let g:ctags_args='-I __declspec+'
@@ -31,6 +38,16 @@
" let g:ctags_statusline=1 " To show tag name in status line.
" let generate_tags=1 " To start automatically when a supported
" " file is opened.
+"
+" The configuration variables (g:ctags_*) may also be changed after the
+" plugin is loaded. Setting g:ctags_title or g:ctags_statusline to 0
+" after the plugin is loaded will stop the updating of 'titlestring' or
+" 'statusline' but will not clear those options as doing so could
+" interfere with a user's setting of those options, for example in a
+" filetype plugin. To restore the default appearance of either of those
+" options, simply execute ":set titlestring=" or ":set statusline=" as
+" desired.
+"
" :CTAGS command starts the script.
" Exit quickly when already loaded.
@@ -45,51 +62,105 @@ let loaded_ctags = 1
let s:save_cpo = &cpo
set cpo&vim
+" s:ruler contains a copy of the 'ruler' setting so that 'statusline' can
+" be updated when the user changes 'ruler'. It is set to an invalid value
+" initially so that 'statusline' will be set the first time 'ruler' is
+" checked.
+"
+let s:ruler = ''
+
+" The last tag name displayed in the 'titlestring'.
+"
+let s:title_tag_name = ''
+
+" Set default values for all configuration variables not already
+" specified by the user.
+
if !exists("ctags_path")
- "let g:ctags_path='ctags'
- "let g:ctags_args=''
- let g:ctags_path=$VIM.'/ctags/ctags'
- let g:ctags_args='-I __declspec+'
+ " Version 1.0 and 2.0 default:
+ "let g:ctags_path=$VIM.'/ctags/ctags'
+
+ " Version 2.1 default:
+ let g:ctags_path='ctags' " Use first 'ctags' in PATH.
+endif
+
+if !exists("ctags_args")
+ " Version 1.0 and 2.0 default:
+ "let g:ctags_args='-I __declspec+'
+
+ " Version 2.1 default:
+ let g:ctags_args='--c-types=cfgsu --vim-types=f --if0=yes'
+endif
+
+if !exists("generate_tags")
+ let generate_tags = 0
endif
-" If user doesn't specify either g:ctags_title or g:ctags_statusline,
-" revert to the original behavior, which was equivalent to g:ctags_title.
+" By default, the ctags list is regenerated whenever the buffer is
+" written. Allow the user to disable this behavior by setting
+" g:ctags_regenerate = 0 if, for example, this becomes a performance
+" problem.
+"
+if !exists("g:ctags_regenerate")
+ let g:ctags_regenerate = 1
+endif
+
+" If the user doesn't specify either g:ctags_title or g:ctags_statusline,
+" revert to the original behavior, which was equivalent to g:ctags_title =
+" 1, g:ctags_statusline = 0.
"
if !exists("g:ctags_title") && !exists("g:ctags_statusline")
- let g:ctags_title=1
+ let g:ctags_title = 1
+ let g:ctags_statusline = 0
+endif
+
+if !exists("g:ctags_title")
+ let g:ctags_title = 0
+endif
+
+if !exists("g:ctags_statusline")
+ let g:ctags_statusline = 0
endif
command! CTAGS let generate_tags=1|call GenerateTags()
autocmd BufEnter *.c,*.cpp,*.h,*.py,*.vim
-\ if exists('generate_tags')
+\ if g:ctags_statusline != 0
+\ | let s:save_laststatus = &laststatus
+\ | set laststatus=2
+\ | endif
+\ | if generate_tags != 0
\ && !exists('b:lines')
\ && filereadable(expand("<afile>"))
\ | call GenerateTags()
\ | endif
-set updatetime=500
+autocmd BufLeave *.c,*.cpp,*.h,*.py,*.vim
+\ if exists("s:save_laststatus")
+\ | let &laststatus = s:save_laststatus
+\ | unlet s:save_laststatus
+\ | endif
-if exists("g:ctags_title")
- autocmd CursorHold *
- \ if exists('generate_tags')
- \ | let &titlestring='%t%( %M%)%( (%{expand("%:~:.:h")})%)%( %a%)%='.GetTagName(line("."))
- \ | endif
-endif
+" Update the tags list whenever the buffer is written.
+"
+autocmd BufWritePost *.c,*.cpp,*.h,*.py,*.vim
+\ if (generate_tags != 0) && (g:ctags_regenerate != 0)
+\ | call GenerateTags()
+\ | endif
-if exists("g:ctags_statusline")
- autocmd CursorHold *
- \ if exists('generate_tags')
- \ | call s:SetStatusline()
- \ | endif
-endif
+set updatetime=500
+
+autocmd CursorHold *
+\ if generate_tags != 0
+\ | call s:SetTagDisplay()
+\ | endif
"set titlestring=%t%(\ %M%)%(\ (%{expand(\"%:~:.:h\")})%)%(\ %a%)%=%(tag:\ %-{GetTagName(line("."))}%)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-" No changes should be reqired below (unless there are bugs).
+" No changes should be required below (unless there are bugs).
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if version < 600
@@ -105,38 +176,92 @@ endif
let g:ctags_obligatory_args = '-n --sort=no -o -'
let g:ctags_pattern="^\\(.\\{-}\\)\t.\\{-}\t\\(\\d*\\).*"
-" This function builds an array of tag names. b:lines contains line numbers;
-" b:l<number> is the tag value for the line <number>.
-function! GenerateTags()
- let ctags = system(g:ctags_path.' '.g:ctags_args.' '.g:ctags_obligatory_args.' "'.expand('%').'"')
-
- " b:length is one greater than the length of maximum line number.
- let b:length = 8
- let b:lines = ''
-
- " strlen(spaces) must be at least b:length.
- let spaces = ' '
- let i = 1
- let len = strlen(ctags)
-
- while strlen(ctags) > 0
- let one_tag = strpart(ctags, 0, Stridx(ctags, "\n"))
- let tag_name = substitute(one_tag, g:ctags_pattern, '\1', '')
- let tag_line_number = substitute(one_tag, g:ctags_pattern, '\2', '')
- execute "let b:l".tag_line_number . " = '".tag_name."'"
- let b:lines = strpart(b:lines.tag_line_number.spaces, 0, b:length*i)
- let i = i+1
-
- " vim 5.x insists that strpart takes 3 arguments.
- let ctags = strpart(ctags, Stridx(ctags, "\n")+1, len)
- endwhile
-
- let b:lines = b:lines."9999999"
-endfunction
+" This function builds an array of tag indices. b:tags contains all
+" the tag names in the file, separated by newlines. b:lines contains
+" the source file line numbers of lines that contain a tag, followed
+" by the index into b:tags of the name of the tag. The b:lines array
+" is sorted, so a binary search can be used to find the appropriate
+" tagged line for a given source file line number. b:length contains
+" the length of a number (line number or index) in b:lines.
+"
+" There are two versions of this function: if vim has been compiled
+" with perl support, a fast perl version is used; otherwise a native
+" version that is somewhat slower is used.
+"
+if has('perl')
+ function! GenerateTags()
+ perl << PERL_EOF
+ $max_num = "9999999";
+ $length = length($max_num);
+ $lines = "";
+ $tags = "";
+ $index = 0;
+
+ $command = VIM::Eval("g:ctags_path");
+ $command .= " " . VIM::Eval("g:ctags_args");
+ $command .= " " . VIM::Eval("g:ctags_obligatory_args");
+ $command .= " " . VIM::Eval("expand('%')");
+
+ open (CTAGS, $command . "|") or die $!;
+
+ while (<CTAGS>)
+ {
+ s/^(.+?)\t.*?\t(\d*);.*$/\1\t\2/;
+ my ($tag_name, $tag_line_num) = split /\t/;
+ $tags .= $tag_name . "\n";
+ $lines .= sprintf("%-*d", $length, $tag_line_num);
+ $lines .= sprintf("%-*d", $length, $index);
+ $index += length($tag_name) + 1;
+ }
+
+ close (CTAGS);
+
+ $lines .= $max_num;
+ $lines .= $max_num;
+
+ VIM::DoCommand("let b:tags = '$tags'");
+ VIM::DoCommand("let b:length = $length");
+ VIM::DoCommand("let b:lines = '$lines'");
+PERL_EOF
+ endfunction
+else
+ function! GenerateTags()
+ let ctags = system(g:ctags_path.' '.g:ctags_args.' '.g:ctags_obligatory_args.' "'.expand('%').'"')
+
+ let max_num = "9999999"
+ let b:length = strlen(max_num)
+ let b:lines = ''
+ let b:tags = ''
+
+ " strlen(spaces) must be at least b:length.
+ let spaces = ' '
+ let len = strlen(ctags)
+ let index = 0
+ let offset = 0
+
+ while offset < len
+ let one_tag = matchstr(ctags, "[^\n]*", offset)
+ let tag_name = substitute(one_tag, g:ctags_pattern, '\1', '')
+ let tag_line_num = substitute(one_tag, g:ctags_pattern, '\2', '')
+ let b:lines = b:lines . strpart(tag_line_num.spaces, 0, b:length)
+ let b:lines = b:lines . strpart(index.spaces, 0, b:length)
+ let b:tags = b:tags . tag_name . "\n"
+ let index = index + strlen(tag_name) + 1
+ let offset = offset + strlen(one_tag) + 1
+ endwhile
+
+ let b:lines = b:lines . max_num
+ let b:lines = b:lines . max_num
+ endfunction
+endif
" This function returns the tag name for given index.
function! GetLine(i)
- return strpart(b:lines, a:i*b:length, b:length)+0
+ return strpart(b:lines, a:i*b:length*2, b:length)+0
+endfunction
+
+function! GetIndex(i)
+ return strpart(b:lines, a:i*(b:length*2)+b:length, b:length)+0
endfunction
" This function does binary search in the array of tag names and returns
@@ -147,7 +272,7 @@ function! GetTagName(curline)
endif
let left = 0
- let right = strlen(b:lines)/b:length
+ let right = strlen(b:lines)/(b:length*2)
if a:curline < GetLine(left)
return ""
@@ -169,27 +294,51 @@ function! GetTagName(curline)
endif
endwhile
- exe "let ret=b:l".GetLine(left)
+ let index = GetIndex(left)
+
+ if index < strlen(b:tags)
+ let ret = matchstr(b:tags, "[^\n]*", index)
+ endif
+
return ret
endfunction
-" This function sets the value of 'statusline'.
-function! s:SetStatusline()
- let w:tag_name = GetTagName(line("."))
- if &ruler
- let &statusline='%<%f %(%h%m%r %)%=%{TagName()} %-15.15(%l,%c%V%)%P'
+" This function sets the values of 'statusline' and 'titlestring'.
+"
+" Avoid setting 'statusline' (or any other option) unless the tag name has
+" changed since doing so will force a screen redraw and will cause the
+" desired cursor column to be reset. Losing the desired cursor column is
+" really annoying when editing a file type for which ctags aren't even
+" supported. (This may be done differently when we make the autocommands
+" smarter about the current file type.)
+"
+function! s:SetTagDisplay()
+ let l:tag_name = GetTagName(line("."))
+ if !exists('w:tag_name')
+ let w:tag_name = ''
+ endif
+ if (g:ctags_statusline != 0) && ((l:tag_name != w:tag_name) || (&ruler != s:ruler))
+ let w:tag_name = l:tag_name
+ let s:ruler = &ruler
+ if &ruler
+ let &statusline='%<%f %(%h%m%r %)%=%{TagName()} %-15.15(%l,%c%V%)%P'
" Equivalent to default status
" line with 'ruler' set:
"
" '%<%f %h%m%r%=%-15.15(%l,%c%V%)%P'
- else
- let &statusline='%<%f %(%h%m%r %)%=%{TagName()}'
- endif
+ else
+ let &statusline='%<%f %(%h%m%r %)%=%{TagName()}'
+ endif
" The %(%) pair around the "%h%m%r "
" is there to suppress the extra
" space between the file name and
" the function name when those
" elements are null.
+ endif
+ if (g:ctags_title != 0) && (l:tag_name != s:title_tag_name)
+ let s:title_tag_name = l:tag_name
+ let &titlestring='%t%( %M%)%( (%{expand("%:~:.:h")})%)%( %a%)%='.s:title_tag_name
+ endif
endfunction
" This function returns the value of w:tag_name if it exists, otherwise

0 comments on commit bef1afe

Please sign in to comment.
Something went wrong with that request. Please try again.