aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Odding <peter@peterodding.com>2011-06-14 00:29:49 +0200
committerPeter Odding <peter@peterodding.com>2011-06-14 00:29:49 +0200
commitca9a4d8847db5aed86d94c0223914aca64029ab3 (patch)
tree5d1d426e5c4f17771a7ac95c056b6ea384bbf060
parent1194a6b8a2e21db24fe051c0679d17b8a6878ebf (diff)
downloadvim-easytags-ca9a4d8847db5aed86d94c0223914aca64029ab3.tar.gz
Fix caching of tagged files (for file type specific tags files)
* The easytags plug-in caches known tagged files so it doesn't have to run :UpdateTags whenever you edit an existing file. The previous implementation was based on the assumption of one global tags file so wasn't compatible with the concept of file type specific tags files. This should now be fixed. * Previously the plug-in worked with a combination of parsed and unparsed tags file entries which made the code confusing. I've now cleaned this up so that the plug-in only keeps one type of data in memory. * Moved resetting of s:cached_filenames from the end to the start of the functions that call s:canonicalize() to avoid caching invalid data. PS. I've benchmarked two cache_tagged_files() implementations, one using taglist('.'), the other calling xolox#easytags#read_tagsfile() on each tags file reported by the tagfiles() function. It turns out that taglist('.') is very slow, which explains why I went with the code that calls xolox#easytags#read_tagsfile() in a loop.
-rw-r--r--autoload/xolox/easytags.vim130
-rw-r--r--plugin/easytags.vim4
2 files changed, 69 insertions, 65 deletions
diff --git a/autoload/xolox/easytags.vim b/autoload/xolox/easytags.vim
index b879458..9388d0b 100644
--- a/autoload/xolox/easytags.vim
+++ b/autoload/xolox/easytags.vim
@@ -1,9 +1,9 @@
" Vim script
" Author: Peter Odding <peter@peterodding.com>
-" Last Change: June 13, 2011
+" Last Change: June 14, 2011
" URL: http://peterodding.com/code/vim/easytags/
-let s:script = expand('<sfile>:p:~')
+let s:script = 'easytags.vim'
" Public interface through (automatic) commands. {{{1
@@ -94,8 +94,6 @@ function! xolox#easytags#update(silent, filter_tags, filenames) " {{{2
return 1
catch
call xolox#misc#msg#warn("%s: %s (at %s)", s:script, v:exception, v:throwpoint)
- finally
- unlet s:cached_filenames
endtry
endfunction
@@ -169,73 +167,67 @@ function! s:prep_cmdline(cfile, tagsfile, firstrun, arguments) " {{{3
endfunction
function! s:run_ctags(starttime, cfile, tagsfile, firstrun, cmdline) " {{{3
- let output = []
+ let lines = []
if a:cmdline != ''
call xolox#misc#msg#debug("%s: Executing %s", s:script, a:cmdline)
try
- let output = xolox#shell#execute(a:cmdline, 1)
+ let lines = xolox#shell#execute(a:cmdline, 1)
catch /^Vim\%((\a\+)\)\=:E117/
" Ignore missing shell.vim plug-in.
- let output = split(system(a:cmdline), "\n")
+ let output = system(a:cmdline)
if v:shell_error
let msg = "Failed to update tags file %s: %s!"
- throw printf(msg, fnamemodify(a:tagsfile, ':~'), strtrans(join(output, "\n")))
+ throw printf(msg, fnamemodify(a:tagsfile, ':~'), strtrans(output))
endif
+ let lines = split(output, "\n")
endtry
if a:firstrun
if a:cfile != ''
- call xolox#easytags#add_tagged_file(a:cfile)
call xolox#misc#timer#stop("%s: Created tags for %s in %s.", s:script, expand('%:p:~'), a:starttime)
else
call xolox#misc#timer#stop("%s: Created tags in %s.", s:script, a:starttime)
endif
endif
endif
- return output
+ return xolox#easytags#parse_entries(lines)
endfunction
function! s:filter_merge_tags(filter_tags, tagsfile, output) " {{{3
let [headers, entries] = xolox#easytags#read_tagsfile(a:tagsfile)
- call s:set_tagged_files(entries)
let filters = []
+ " Filter old tags that are to be replaced with the tags in {output}.
let tagged_files = s:find_tagged_files(a:output)
if !empty(tagged_files)
- call add(filters, '!has_key(tagged_files, s:canonicalize(get(v:val, 1)))')
+ call add(filters, '!has_key(tagged_files, s:canonicalize(v:val[1]))')
endif
+ " Filter tags for non-existing files?
if a:filter_tags
- call add(filters, 'filereadable(get(v:val, 1))')
+ call add(filters, 'filereadable(v:val[1]))')
endif
let num_old_entries = len(entries)
if !empty(filters)
+ " Apply the filters.
call filter(entries, join(filters, ' && '))
endif
let num_filtered = num_old_entries - len(entries)
+ " Merge old/new tags and write tags file.
call extend(entries, a:output)
if !xolox#easytags#write_tagsfile(a:tagsfile, headers, entries)
let msg = "Failed to write filtered tags file %s!"
throw printf(msg, fnamemodify(a:tagsfile, ':~'))
endif
+ " We've already read the tags file, might as well cache the tagged files :-)
+ let fname = s:canonicalize(a:tagsfile)
+ call s:cache_tagged_files_in(fname, getftime(fname), entries)
return num_filtered
endfunction
-function! s:find_tagged_files(new_entries) " {{{3
- " FIXME Don't parse tags files in multiple places!
+function! s:find_tagged_files(entries) " {{{3
let tagged_files = {}
- for entry in a:new_entries
- if type(entry) == type([])
- let filename = entry[1]
- else
- if match(entry, '^[^\t]\+\t[^\t]\+\t.\+$') == -1
- " Never corrupt the tags file by merging an invalid line
- " (probably an error message) with the existing tags!
- throw "Exuberant Ctags returned invalid data: " . strtrans(entry)
- endif
- let filename = matchstr(entry, '^[^\t]\+\t\zs[^\t]\+')
- endif
+ for entry in a:entries
+ let filename = s:canonicalize(entry[1])
if !has_key(tagged_files, filename)
- let filename = s:canonicalize(filename)
let tagged_files[filename] = 1
- call xolox#easytags#add_tagged_file(filename)
endif
endfor
return tagged_files
@@ -292,10 +284,10 @@ endfunction
function! xolox#easytags#by_filetype(undo) " {{{2
try
- let s:cached_filenames = {}
if empty(g:easytags_by_filetype)
throw "Please set g:easytags_by_filetype before running :TagsByFileType!"
endif
+ let s:cached_filenames = {}
let global_tagsfile = expand(g:easytags_file)
let disabled_tagsfile = global_tagsfile . '.disabled'
if !a:undo
@@ -316,8 +308,6 @@ function! xolox#easytags#by_filetype(undo) " {{{2
endif
catch
call xolox#misc#msg#warn("%s: %s (at %s)", s:script, v:exception, v:throwpoint)
- finally
- unlet s:cached_filenames
endtry
endfunction
@@ -379,21 +369,29 @@ function! xolox#easytags#read_tagsfile(tagsfile) " {{{2
" I'm not sure whether this is by design or an implementation detail but
" it's possible for the "!_TAG_FILE_SORTED" header to appear after one or
" more tags and Vim will apparently still use the header! For this reason
- " the xolox#easytags#write_tagsfile() function should also recognize it, otherwise
- " Vim might complain with "E432: Tags file not sorted".
+ " the xolox#easytags#write_tagsfile() function should also recognize it,
+ " otherwise Vim might complain with "E432: Tags file not sorted".
let headers = []
let entries = []
- let pattern = '^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$'
for line in readfile(a:tagsfile)
if line =~# '^!_TAG_'
call add(headers, line)
else
- call add(entries, matchlist(line, pattern)[1:3])
+ call add(entries, xolox#easytags#parse_entry(line))
endif
endfor
return [headers, entries]
endfunction
+function! xolox#easytags#parse_entry(line) " {{{2
+ return matchlist(a:line, '^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$')[1:3]
+endfunction
+
+function! xolox#easytags#parse_entries(lines) " {{{2
+ call map(a:lines, 'xolox#easytags#parse_entry(v:val)')
+ return a:lines
+endfunction
+
function! xolox#easytags#write_tagsfile(tagsfile, headers, entries) " {{{2
" This function always sorts the tags file but understands "foldcase".
let sort_order = 1
@@ -429,14 +427,43 @@ function! s:join_entry(value)
endfunction
function! xolox#easytags#file_has_tags(filename) " {{{2
+ " Check whether the given source file occurs in one of the tags files known
+ " to Vim. This function might not always give the right answer because of
+ " caching, but for the intended purpose that's no problem: When editing an
+ " existing file which has no tags defined the plug-in will run Exuberant
+ " Ctags to update the tags, *unless the file has already been tagged*.
call s:cache_tagged_files()
return has_key(s:tagged_files, s:resolve(a:filename))
endfunction
-function! xolox#easytags#add_tagged_file(filename) " {{{2
- call s:cache_tagged_files()
- let filename = s:resolve(a:filename)
- let s:tagged_files[filename] = 1
+if !exists('s:tagged_files')
+ let s:tagged_files = {}
+ let s:known_tagfiles = {}
+endif
+
+function! s:cache_tagged_files() " {{{3
+ if empty(s:tagged_files)
+ " Initialize the cache of tagged files on first use. After initialization
+ " we'll only update the cache when we're reading a tags file from disk for
+ " other purposes anyway (so the cache doesn't introduce too much overhead).
+ let starttime = xolox#misc#timer#start()
+ for tagsfile in tagfiles()
+ let fname = s:canonicalize(tagsfile)
+ let ftime = getftime(fname)
+ if get(s:known_tagfiles, fname, 0) != ftime
+ let [headers, entries] = xolox#easytags#read_tagsfile(fname)
+ call s:cache_tagged_files_in(fname, ftime, entries)
+ endif
+ endfor
+ call xolox#misc#timer#stop("%s: Initialized cache of tagged files in %s", s:script, starttime)
+ endif
+endfunction
+
+function! s:cache_tagged_files_in(fname, ftime, entries) " {{{3
+ for entry in a:entries
+ let s:tagged_files[s:canonicalize(entry[1])] = 1
+ endfor
+ let s:known_tagfiles[a:fname] = a:ftime
endfunction
function! xolox#easytags#get_tagsfile() " {{{2
@@ -532,30 +559,7 @@ function! s:canonicalize(filename) " {{{2
endif
endfunction
-function! s:cache_tagged_files() " {{{2
- if !exists('s:tagged_files')
- let tagsfile = xolox#easytags#get_tagsfile()
- try
- let [headers, entries] = xolox#easytags#read_tagsfile(tagsfile)
- call s:set_tagged_files(entries)
- catch /\<E484\>/
- " Ignore missing tags file.
- call s:set_tagged_files([])
- endtry
- endif
-endfunction
-
-function! s:set_tagged_files(entries) " {{{2
- " TODO use taglist() instead of readfile() so that all tag files are
- " automatically used :-)
- let s:tagged_files = {}
- for entry in a:entries
- let filename = get(entry, 1, '')
- if filename != ''
- let s:tagged_files[s:resolve(filename)] = 1
- endif
- endfor
-endfunction
+let s:cached_filenames = {}
" Built-in file type & tag kind definitions. {{{1
diff --git a/plugin/easytags.vim b/plugin/easytags.vim
index 9697967..433544d 100644
--- a/plugin/easytags.vim
+++ b/plugin/easytags.vim
@@ -1,10 +1,10 @@
" Vim plug-in
" Author: Peter Odding <peter@peterodding.com>
-" Last Change: June 13, 2011
+" Last Change: June 14, 2011
" URL: http://peterodding.com/code/vim/easytags/
" Requires: Exuberant Ctags (http://ctags.sf.net)
" License: MIT
-" Version: 2.3.1
+" Version: 2.3.2
" Support for automatic update using the GLVS plug-in.
" GetLatestVimScripts: 3114 1 :AutoInstall: easytags.zip