1
1
" Vim script
2
2
" Author: Peter Odding <peter@peterodding.com>
3
- " Last Change: June 13 , 2011
3
+ " Last Change: June 14 , 2011
4
4
" URL: http://peterodding.com/code/vim/easytags/
5
5
6
- let s: script = expand ( ' <sfile>:p:~ ' )
6
+ let s: script = ' easytags.vim '
7
7
8
8
" Public interface through (automatic) commands. {{{1
9
9
@@ -94,8 +94,6 @@ function! xolox#easytags#update(silent, filter_tags, filenames) " {{{2
94
94
return 1
95
95
catch
96
96
call xolox#misc#msg#warn (" %s: %s (at %s)" , s: script , v: exception , v: throwpoint )
97
- finally
98
- unlet s: cached_filenames
99
97
endtry
100
98
endfunction
101
99
@@ -169,73 +167,67 @@ function! s:prep_cmdline(cfile, tagsfile, firstrun, arguments) " {{{3
169
167
endfunction
170
168
171
169
function ! s: run_ctags (starttime, cfile , tagsfile, firstrun, cmdline) " {{{3
172
- let output = []
170
+ let lines = []
173
171
if a: cmdline != ' '
174
172
call xolox#misc#msg#debug (" %s: Executing %s" , s: script , a: cmdline )
175
173
try
176
- let output = xolox#shell#execute (a: cmdline , 1 )
174
+ let lines = xolox#shell#execute (a: cmdline , 1 )
177
175
catch /^Vim\%((\a\+)\)\=:E117/
178
176
" Ignore missing shell.vim plug-in.
179
- let output = split ( system (a: cmdline), " \n " )
177
+ let output = system (a: cmdline )
180
178
if v: shell_error
181
179
let msg = " Failed to update tags file %s: %s!"
182
- throw printf (msg, fnamemodify (a: tagsfile , ' :~' ), strtrans (join ( output, " \n " ) ))
180
+ throw printf (msg, fnamemodify (a: tagsfile , ' :~' ), strtrans (output))
183
181
endif
182
+ let lines = split (output, " \n " )
184
183
endtry
185
184
if a: firstrun
186
185
if a: cfile != ' '
187
- call xolox#easytags#add_tagged_file (a: cfile )
188
186
call xolox#misc#timer#stop (" %s: Created tags for %s in %s." , s: script , expand (' %:p:~' ), a: starttime )
189
187
else
190
188
call xolox#misc#timer#stop (" %s: Created tags in %s." , s: script , a: starttime )
191
189
endif
192
190
endif
193
191
endif
194
- return output
192
+ return xolox#easytags#parse_entries ( lines )
195
193
endfunction
196
194
197
195
function ! s: filter_merge_tags (filter_tags, tagsfile, output) " {{{3
198
196
let [headers, entries] = xolox#easytags#read_tagsfile (a: tagsfile )
199
- call s: set_tagged_files (entries)
200
197
let filters = []
198
+ " Filter old tags that are to be replaced with the tags in {output}.
201
199
let tagged_files = s: find_tagged_files (a: output )
202
200
if ! empty (tagged_files)
203
- call add (filters, ' !has_key(tagged_files, s:canonicalize(get( v:val, 1) ))' )
201
+ call add (filters, ' !has_key(tagged_files, s:canonicalize(v:val[1] ))' )
204
202
endif
203
+ " Filter tags for non-existing files?
205
204
if a: filter_tags
206
- call add (filters, ' filereadable(get( v:val, 1 ))' )
205
+ call add (filters, ' filereadable(v:val[1] ))' )
207
206
endif
208
207
let num_old_entries = len (entries)
209
208
if ! empty (filters)
209
+ " Apply the filters.
210
210
call filter (entries, join (filters, ' && ' ))
211
211
endif
212
212
let num_filtered = num_old_entries - len (entries)
213
+ " Merge old/new tags and write tags file.
213
214
call extend (entries, a: output )
214
215
if ! xolox#easytags#write_tagsfile (a: tagsfile , headers, entries)
215
216
let msg = " Failed to write filtered tags file %s!"
216
217
throw printf (msg, fnamemodify (a: tagsfile , ' :~' ))
217
218
endif
219
+ " We've already read the tags file, might as well cache the tagged files :-)
220
+ let fname = s: canonicalize (a: tagsfile )
221
+ call s: cache_tagged_files_in (fname, getftime (fname), entries)
218
222
return num_filtered
219
223
endfunction
220
224
221
- function ! s: find_tagged_files (new_entries) " {{{3
222
- " FIXME Don't parse tags files in multiple places!
225
+ function ! s: find_tagged_files (entries) " {{{3
223
226
let tagged_files = {}
224
- for entry in a: new_entries
225
- if type (entry) == type ([])
226
- let filename = entry[1 ]
227
- else
228
- if match (entry, ' ^[^\t]\+\t[^\t]\+\t.\+$' ) == -1
229
- " Never corrupt the tags file by merging an invalid line
230
- " (probably an error message) with the existing tags!
231
- throw " Exuberant Ctags returned invalid data: " . strtrans (entry)
232
- endif
233
- let filename = matchstr (entry, ' ^[^\t]\+\t\zs[^\t]\+' )
234
- endif
227
+ for entry in a: entries
228
+ let filename = s: canonicalize (entry[1 ])
235
229
if ! has_key (tagged_files, filename)
236
- let filename = s: canonicalize (filename)
237
230
let tagged_files[filename] = 1
238
- call xolox#easytags#add_tagged_file (filename)
239
231
endif
240
232
endfor
241
233
return tagged_files
@@ -292,10 +284,10 @@ endfunction
292
284
293
285
function ! xolox#easytags#by_filetype (undo ) " {{{2
294
286
try
295
- let s: cached_filenames = {}
296
287
if empty (g: easytags_by_filetype )
297
288
throw " Please set g:easytags_by_filetype before running :TagsByFileType!"
298
289
endif
290
+ let s: cached_filenames = {}
299
291
let global_tagsfile = expand (g: easytags_file )
300
292
let disabled_tagsfile = global_tagsfile . ' .disabled'
301
293
if ! a: undo
@@ -316,8 +308,6 @@ function! xolox#easytags#by_filetype(undo) " {{{2
316
308
endif
317
309
catch
318
310
call xolox#misc#msg#warn (" %s: %s (at %s)" , s: script , v: exception , v: throwpoint )
319
- finally
320
- unlet s: cached_filenames
321
311
endtry
322
312
endfunction
323
313
@@ -379,21 +369,29 @@ function! xolox#easytags#read_tagsfile(tagsfile) " {{{2
379
369
" I'm not sure whether this is by design or an implementation detail but
380
370
" it's possible for the "!_TAG_FILE_SORTED" header to appear after one or
381
371
" more tags and Vim will apparently still use the header! For this reason
382
- " the xolox#easytags#write_tagsfile() function should also recognize it, otherwise
383
- " Vim might complain with "E432: Tags file not sorted".
372
+ " the xolox#easytags#write_tagsfile() function should also recognize it,
373
+ " otherwise Vim might complain with "E432: Tags file not sorted".
384
374
let headers = []
385
375
let entries = []
386
- let pattern = ' ^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$'
387
376
for line in readfile (a: tagsfile )
388
377
if line = ~# ' ^!_TAG_'
389
378
call add (headers, line )
390
379
else
391
- call add (entries, matchlist (line , pattern)[ 1 : 3 ] )
380
+ call add (entries, xolox#easytags#parse_entry (line ) )
392
381
endif
393
382
endfor
394
383
return [headers, entries]
395
384
endfunction
396
385
386
+ function ! xolox#easytags#parse_entry (line ) " {{{2
387
+ return matchlist (a: line , ' ^\([^\t]\+\)\t\([^\t]\+\)\t\(.\+\)$' )[1 :3 ]
388
+ endfunction
389
+
390
+ function ! xolox#easytags#parse_entries (lines ) " {{{2
391
+ call map (a: lines , ' xolox#easytags#parse_entry(v:val)' )
392
+ return a: lines
393
+ endfunction
394
+
397
395
function ! xolox#easytags#write_tagsfile (tagsfile, headers, entries) " {{{2
398
396
" This function always sorts the tags file but understands "foldcase".
399
397
let sort_order = 1
@@ -429,14 +427,43 @@ function! s:join_entry(value)
429
427
endfunction
430
428
431
429
function ! xolox#easytags#file_has_tags (filename) " {{{2
430
+ " Check whether the given source file occurs in one of the tags files known
431
+ " to Vim. This function might not always give the right answer because of
432
+ " caching, but for the intended purpose that's no problem: When editing an
433
+ " existing file which has no tags defined the plug-in will run Exuberant
434
+ " Ctags to update the tags, *unless the file has already been tagged*.
432
435
call s: cache_tagged_files ()
433
436
return has_key (s: tagged_files , s: resolve (a: filename ))
434
437
endfunction
435
438
436
- function ! xolox#easytags#add_tagged_file (filename) " {{{2
437
- call s: cache_tagged_files ()
438
- let filename = s: resolve (a: filename )
439
- let s: tagged_files [filename] = 1
439
+ if ! exists (' s:tagged_files' )
440
+ let s: tagged_files = {}
441
+ let s: known_tagfiles = {}
442
+ endif
443
+
444
+ function ! s: cache_tagged_files () " {{{3
445
+ if empty (s: tagged_files )
446
+ " Initialize the cache of tagged files on first use. After initialization
447
+ " we'll only update the cache when we're reading a tags file from disk for
448
+ " other purposes anyway (so the cache doesn't introduce too much overhead).
449
+ let starttime = xolox#misc#timer#start ()
450
+ for tagsfile in tagfiles ()
451
+ let fname = s: canonicalize (tagsfile)
452
+ let ftime = getftime (fname)
453
+ if get (s: known_tagfiles , fname, 0 ) != ftime
454
+ let [headers, entries] = xolox#easytags#read_tagsfile (fname)
455
+ call s: cache_tagged_files_in (fname, ftime, entries)
456
+ endif
457
+ endfor
458
+ call xolox#misc#timer#stop (" %s: Initialized cache of tagged files in %s" , s: script , starttime)
459
+ endif
460
+ endfunction
461
+
462
+ function ! s: cache_tagged_files_in (fname, ftime, entries) " {{{3
463
+ for entry in a: entries
464
+ let s: tagged_files [s: canonicalize (entry[1 ])] = 1
465
+ endfor
466
+ let s: known_tagfiles [a: fname ] = a: ftime
440
467
endfunction
441
468
442
469
function ! xolox#easytags#get_tagsfile () " {{{2
@@ -532,30 +559,7 @@ function! s:canonicalize(filename) " {{{2
532
559
endif
533
560
endfunction
534
561
535
- function ! s: cache_tagged_files () " {{{2
536
- if ! exists (' s:tagged_files' )
537
- let tagsfile = xolox#easytags#get_tagsfile ()
538
- try
539
- let [headers, entries] = xolox#easytags#read_tagsfile (tagsfile)
540
- call s: set_tagged_files (entries)
541
- catch /\<E484\>/
542
- " Ignore missing tags file.
543
- call s: set_tagged_files ([])
544
- endtry
545
- endif
546
- endfunction
547
-
548
- function ! s: set_tagged_files (entries) " {{{2
549
- " TODO use taglist() instead of readfile() so that all tag files are
550
- " automatically used :-)
551
- let s: tagged_files = {}
552
- for entry in a: entries
553
- let filename = get (entry, 1 , ' ' )
554
- if filename != ' '
555
- let s: tagged_files [s: resolve (filename)] = 1
556
- endif
557
- endfor
558
- endfunction
562
+ let s: cached_filenames = {}
559
563
560
564
" Built-in file type & tag kind definitions. {{{1
561
565
0 commit comments