comparison plugin/autotags.vim @ 3:60adce96ac2d

Lots of changes: * Add Unix script for tag file generation. * Add status-line indicator. * More options to customize Autotags behaviour. * Use 'wildignore' to exclude things from ctags. * Generate tag file in a project missing it.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 20 Jul 2014 14:25:09 -0700
parents a3a37124558b
children 12f4f50f4d3a
comparison
equal deleted inserted replaced
2:d073ca33c5b8 3:60adce96ac2d
15 echom "Reloaded autotags." 15 echom "Reloaded autotags."
16 endif 16 endif
17 let g:loaded_autotags = 1 17 let g:loaded_autotags = 1
18 18
19 if !exists('g:autotags_trace') 19 if !exists('g:autotags_trace')
20 let g:autotags_trace = 1 20 let g:autotags_trace = 0
21 endif 21 endif
22 22
23 if !exists('g:autotags_fake') 23 if !exists('g:autotags_fake')
24 let g:autotags_fake = 0 24 let g:autotags_fake = 0
25 endif 25 endif
42 42
43 if !exists('g:autotags_project_root') 43 if !exists('g:autotags_project_root')
44 let g:autotags_project_root = [] 44 let g:autotags_project_root = []
45 endif 45 endif
46 let g:autotags_project_root += ['.git', '.hg', '.bzr', '_darcs'] 46 let g:autotags_project_root += ['.git', '.hg', '.bzr', '_darcs']
47
48 if !exists('g:autotags_exclude')
49 let g:autotags_exclude = []
50 endif
51
52 if !exists('g:autotags_generate_on_missing')
53 let g:autotags_generate_on_missing = 1
54 endif
55
56 if !exists('g:autotags_generate_on_write')
57 let g:autotags_generate_on_write = 1
58 endif
59
60 if !exists('g:autotags_auto_set_tags')
61 let g:autotags_auto_set_tags = 1
62 endif
47 63
48 " }}} 64 " }}}
49 65
50 " Utilities {{{ 66 " Utilities {{{
51 67
109 call s:throw("Can't figure out what tag file to use for: " . a:path) 125 call s:throw("Can't figure out what tag file to use for: " . a:path)
110 endfunction 126 endfunction
111 127
112 " Setup autotags for the current buffer. 128 " Setup autotags for the current buffer.
113 function! s:setup_autotags() abort 129 function! s:setup_autotags() abort
130 if exists('b:autotags_file') && !g:autotags_debug
131 " This buffer already has autotags support.
132 return
133 endif
134
135 " Try and file what tags file we should manage.
114 call s:trace("Scanning buffer '" . bufname('%') . "' for autotags setup...") 136 call s:trace("Scanning buffer '" . bufname('%') . "' for autotags setup...")
115 if exists('b:autotags_file')
116 return
117 endif
118 try 137 try
119 let b:autotags_file = s:get_tagfile_for(expand('%:h')) 138 let b:autotags_file = s:get_tagfile_for(expand('%:h'))
120 catch /^autotags\:/ 139 catch /^autotags\:/
140 call s:trace("Can't figure out what tag file to use... no autotags support.")
121 return 141 return
122 endtry 142 endtry
123 143
144 " We know what tags file to manage! Now set things up.
124 call s:trace("Setting autotags for buffer '" . bufname('%') . "' with tagfile: " . b:autotags_file) 145 call s:trace("Setting autotags for buffer '" . bufname('%') . "' with tagfile: " . b:autotags_file)
125 146
147 " Set the tags file for Vim to use.
148 if g:autotags_auto_set_tags
149 execute 'setlocal tags^=' . b:autotags_file
150 endif
151
152 " Autocommands for updating the tags on save.
126 let l:bn = bufnr('%') 153 let l:bn = bufnr('%')
127 execute 'augroup autotags_buffer_' . l:bn 154 execute 'augroup autotags_buffer_' . l:bn
128 execute ' autocmd!' 155 execute ' autocmd!'
129 execute ' autocmd BufWritePost <buffer=' . l:bn . '> if g:autotags_enabled|call s:update_tags(0, 1)|endif' 156 execute ' autocmd BufWritePost <buffer=' . l:bn . '> call s:write_triggered_update_tags()'
130 execute 'augroup end' 157 execute 'augroup end'
131 158
159 " Miscellaneous commands.
132 command! -buffer -bang AutotagsUpdate :call s:manual_update_tags(<bang>0) 160 command! -buffer -bang AutotagsUpdate :call s:manual_update_tags(<bang>0)
161
162 " If the tags file doesn't exist, start generating it now.
163 if g:autotags_generate_on_missing && !filereadable(b:autotags_file)
164 call s:trace("Generating missing tags file: " . b:autotags_file)
165 call s:update_tags(1, 0)
166 endif
133 endfunction 167 endfunction
134 168
135 augroup autotags_detect 169 augroup autotags_detect
136 autocmd! 170 autocmd!
137 autocmd BufNewFile,BufReadPost * call s:setup_autotags() 171 autocmd BufNewFile,BufReadPost * call s:setup_autotags()
146 if has('win32') 180 if has('win32')
147 let s:runner_exe = expand('<sfile>:h:h') . '\plat\win32\update_tags.cmd' 181 let s:runner_exe = expand('<sfile>:h:h') . '\plat\win32\update_tags.cmd'
148 endif 182 endif
149 183
150 let s:update_queue = [] 184 let s:update_queue = []
185 let s:maybe_in_progress = []
151 186
152 " Get how to execute an external command depending on debug settings. 187 " Get how to execute an external command depending on debug settings.
153 function! s:get_execute_cmd() abort 188 function! s:get_execute_cmd() abort
154 if has('win32') 189 if has('win32')
155 let l:cmd = '!start ' 190 let l:cmd = '!start '
160 else 195 else
161 return '!' 196 return '!'
162 endif 197 endif
163 endfunction 198 endfunction
164 199
200 " Get the suffix for how to execute an external command.
201 function! s:get_execute_cmd_suffix() abort
202 if has('win32')
203 return ''
204 else
205 return ' &'
206 endif
207 endfunction
208
165 " (Re)Generate the tags file for the current buffer's file. 209 " (Re)Generate the tags file for the current buffer's file.
166 function! s:manual_update_tags(bang) abort 210 function! s:manual_update_tags(bang) abort
167 call s:update_tags(a:bang, 0) 211 call s:update_tags(a:bang, 0)
212 endfunction
213
214 " (Re)Generate the tags file for a buffer that just go saved.
215 function! s:write_triggered_update_tags() abort
216 if g:autotags_enabled && g:autotags_generate_on_write
217 call s:update_tags(0, 1)
218 endif
168 endfunction 219 endfunction
169 220
170 " Update the tags file for the current buffer's file. 221 " Update the tags file for the current buffer's file.
171 " write_mode: 222 " write_mode:
172 " 0: update the tags file if it exists, generate it otherwise. 223 " 0: update the tags file if it exists, generate it otherwise.
178 " 229 "
179 " An additional argument specifies where to write the tags file. If nothing 230 " An additional argument specifies where to write the tags file. If nothing
180 " is specified, it will go to the autotags-defined file. 231 " is specified, it will go to the autotags-defined file.
181 function! s:update_tags(write_mode, queue_mode, ...) abort 232 function! s:update_tags(write_mode, queue_mode, ...) abort
182 " Figure out where to save. 233 " Figure out where to save.
183 let l:tags_file = 0
184 if a:0 == 1 234 if a:0 == 1
185 let l:tags_file = a:1 235 let l:tags_file = a:1
186 else 236 else
187 let l:tags_file = b:autotags_file 237 let l:tags_file = b:autotags_file
188 endif 238 endif
212 execute "chdir " . l:work_dir 262 execute "chdir " . l:work_dir
213 263
214 try 264 try
215 " Build the command line. 265 " Build the command line.
216 let l:cmd = s:get_execute_cmd() . s:runner_exe 266 let l:cmd = s:get_execute_cmd() . s:runner_exe
217 let l:cmd .= ' --exe "' . g:autotags_executable . '"' 267 let l:cmd .= ' -e "' . g:autotags_executable . '"'
218 let l:cmd .= ' --tags "' . fnamemodify(l:tags_file, ':t') . '"' 268 let l:cmd .= ' -t "' . fnamemodify(l:tags_file, ':t') . '"'
219 if a:write_mode == 0 && filereadable(l:tags_file) 269 if a:write_mode == 0 && filereadable(l:tags_file)
220 " CTags specifies paths relative to the tags file with a `./` 270 " CTags specifies paths relative to the tags file with a `./`
221 " prefix, so we need to specify the same prefix otherwise it will 271 " prefix, so we need to specify the same prefix otherwise it will
222 " think those are different files and we'll end up with duplicate 272 " think those are different files and we'll end up with duplicate
223 " entries. 273 " entries.
224 let l:rel_path = s:normalizepath('./' . expand('%:.')) 274 let l:rel_path = s:normalizepath('./' . expand('%:.'))
225 let l:cmd .= ' --source "' . l:rel_path . '"' 275 let l:cmd .= ' -s "' . l:rel_path . '"'
226 endif 276 endif
277 for ign in split(&wildignore, ',')
278 let l:cmd .= ' -x ' . ign
279 endfor
280 for exc in g:autotags_exclude
281 let l:cmd .= ' -x ' . exc
282 endfor
227 if g:autotags_trace 283 if g:autotags_trace
228 let l:cmd .= ' --log "' . fnamemodify(l:tags_file, ':t') . '.log"' 284 if has('win32')
285 let l:cmd .= ' -l "' . fnamemodify(l:tags_file, ':t') . '.log"'
286 else
287 let l:cmd .= ' > "' . fnamemodify(l:tags_file, ':t') . '.log"'
288 endif
229 endif 289 endif
290 let l:cmd .= s:get_execute_cmd_suffix()
291
292 " Run the background process.
230 call s:trace("Running: " . l:cmd) 293 call s:trace("Running: " . l:cmd)
231 call s:trace("In: " . l:work_dir) 294 call s:trace("In: " . l:work_dir)
232 if !g:autotags_fake 295 if !g:autotags_fake
296 " Flag this tags file as being in progress
297 call add(s:maybe_in_progress, fnamemodify(l:tags_file, ':p'))
298
233 if !g:autotags_trace 299 if !g:autotags_trace
234 silent execute l:cmd 300 silent execute l:cmd
235 else 301 else
236 execute l:cmd 302 execute l:cmd
237 endif 303 endif
255 321
256 command! -bang -nargs=1 -complete=file AutotagsGenerate :call s:generate_tags(<bang>0, <f-args>) 322 command! -bang -nargs=1 -complete=file AutotagsGenerate :call s:generate_tags(<bang>0, <f-args>)
257 323
258 " }}} 324 " }}}
259 325
260 " Toggles {{{ 326 " Toggles and Miscellaneous Commands {{{
261 327
262 command! AutotagsToggleEnabled :let g:autotags_enabled=!g:autotags_enabled 328 command! AutotagsToggleEnabled :let g:autotags_enabled=!g:autotags_enabled
263 command! AutotagsToggleTrace :call autotags#trace() 329 command! AutotagsToggleTrace :call autotags#trace()
264 command! AutotagsToggleFake :call autotags#fake() 330 command! AutotagsToggleFake :call autotags#fake()
265 command! AutotagsUnlock :call delete(b:autotags_file . '.lock') 331 command! AutotagsUnlock :call delete(b:autotags_file . '.lock')
306 echom "autotags: Now running autotags for real." 372 echom "autotags: Now running autotags for real."
307 endif 373 endif
308 echom "" 374 echom ""
309 endfunction 375 endfunction
310 376
311 " }}} 377 function! autotags#inprogress()
312 378 echom "autotags: generations in progress:"
379 for mip in s:maybe_in_progress
380 echom mip
381 endfor
382 echom ""
383 endfunction
384
385 " }}}
386
387 " Statusline Functions {{{
388
389 " Prints whether a tag file is being generated right now for the current
390 " buffer in the status line.
391 "
392 " Arguments can be passed:
393 " - args 1 and 2 are the prefix and suffix, respectively, of whatever output,
394 " if any, is going to be produced.
395 " (defaults to empty strings)
396 " - arg 3 is the text to be shown if tags are currently being generated.
397 " (defaults to 'TAGS')
398 "
399 function! autotags#statusline(...) abort
400 if !exists('b:autotags_file')
401 " This buffer doesn't have autotags.
402 return ''
403 endif
404
405 " Figure out what the user is customizing.
406 let l:gen_msg = 'TAGS'
407 if a:0 >= 0
408 let l:gen_msg = a:1
409 endif
410
411 " To make this function as fast as possible, we first check whether the
412 " current buffer's tags file is 'maybe' being generated. This provides a
413 " nice and quick bail out for 99.9% of cases before we need to this the
414 " file-system to check the lock file.
415 let l:abs_tag_file = fnamemodify(b:autotags_file, ':p')
416 let l:found = index(s:maybe_in_progress, l:abs_tag_file)
417 if l:found < 0
418 return ''
419 endif
420 " It's maybe generating! Check if the lock file is still there.
421 if !filereadable(l:abs_tag_file . '.lock')
422 call remove(s:maybe_in_progress, l:found)
423 return ''
424 endif
425 " It's still there! So probably `ctags` is still running...
426 " (although there's a chance it crashed, or the script had a problem, and
427 " the lock file has been left behind... we could try and run some
428 " additional checks here to see if it's legitimately running, and
429 " otherwise delete the lock file... maybe in the future...)
430 return l:gen_msg
431 endfunction
432
433 " }}}
434