Mercurial > vim-gutentags
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 |