Mercurial > vim-gutentags
comparison autoload/gutentags.vim @ 202:b50b6d0f82dd
Refactor for Vim8/Neovim job support.
- Refactor all modules' `generate` methods to use a vaguely generic job API
wrapper that works for both Vim8 and Neovim jobs.
- Make the `statusline` method use new `User` autocommands driven by the
job-started/ended callbacks.
- Remove all the lock-file-related stuff.
- Better error/warning messages.
- Move a few things around.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 31 Mar 2018 18:42:54 -0700 |
parents | f7a417234dea |
children | 6e96ddda0fd3 |
comparison
equal
deleted
inserted
replaced
201:b41385032f86 | 202:b50b6d0f82dd |
---|---|
1 " gutentags.vim - Automatic ctags management for Vim | 1 " gutentags.vim - Automatic ctags management for Vim |
2 | 2 |
3 " Utilities {{{ | 3 " Utilities {{{ |
4 | 4 |
5 function! gutentags#chdir(path) | 5 function! gutentags#chdir(path) |
6 if has('nvim') | 6 if has('nvim') |
7 let chdir = haslocaldir() ? 'lcd' : haslocaldir(-1, 0) ? 'tcd' : 'cd' | 7 let chdir = haslocaldir() ? 'lcd' : haslocaldir(-1, 0) ? 'tcd' : 'cd' |
8 else | 8 else |
9 let chdir = haslocaldir() ? 'lcd' : 'cd' | 9 let chdir = haslocaldir() ? 'lcd' : 'cd' |
10 endif | 10 endif |
11 execute chdir a:path | 11 execute chdir a:path |
12 endfunction | 12 endfunction |
13 | 13 |
14 " Throw an exception message. | 14 " Throw an exception message. |
15 function! gutentags#throw(message) | 15 function! gutentags#throw(message) |
16 throw "gutentags: " . a:message | 16 throw "gutentags: " . a:message |
17 endfunction | 17 endfunction |
18 | 18 |
19 " Throw an exception message and set Vim's error message variable. | 19 " Show an error message. |
20 function! gutentags#throwerr(message) | 20 function! gutentags#error(message) |
21 let v:errmsg = "gutentags: " . a:message | 21 let v:errmsg = "gutentags: " . a:message |
22 throw v:errmsg | 22 echoerr v:errmsg |
23 endfunction | |
24 | |
25 " Show a warning message. | |
26 function! gutentags#warning(message) | |
27 echohl WarningMsg | |
28 echom "gutentags: " . a:message | |
29 echohl None | |
23 endfunction | 30 endfunction |
24 | 31 |
25 " Prints a message if debug tracing is enabled. | 32 " Prints a message if debug tracing is enabled. |
26 function! gutentags#trace(message, ...) | 33 function! gutentags#trace(message, ...) |
27 if g:gutentags_trace || (a:0 && a:1) | 34 if g:gutentags_trace || (a:0 && a:1) |
46 endif | 53 endif |
47 endfunction | 54 endfunction |
48 | 55 |
49 " Shell-slashes the path (opposite of `normalizepath`). | 56 " Shell-slashes the path (opposite of `normalizepath`). |
50 function! gutentags#shellslash(path) | 57 function! gutentags#shellslash(path) |
51 if exists('+shellslash') && !&shellslash | 58 if exists('+shellslash') && !&shellslash |
52 return substitute(a:path, '\v\\', '/', 'g') | 59 return substitute(a:path, '\v\\', '/', 'g') |
53 else | 60 else |
54 return a:path | 61 return a:path |
55 endif | 62 endif |
56 endfunction | 63 endfunction |
57 | 64 |
58 " Gets a file path in the correct `plat` folder. | 65 " Gets a file path in the correct `plat` folder. |
59 function! gutentags#get_plat_file(filename) abort | 66 function! gutentags#get_plat_file(filename) abort |
60 return g:gutentags_plat_dir . a:filename . g:gutentags_script_ext | 67 return g:gutentags_plat_dir . a:filename . g:gutentags_script_ext |
63 " Gets a file path in the resource folder. | 70 " Gets a file path in the resource folder. |
64 function! gutentags#get_res_file(filename) abort | 71 function! gutentags#get_res_file(filename) abort |
65 return g:gutentags_res_dir . a:filename | 72 return g:gutentags_res_dir . a:filename |
66 endfunction | 73 endfunction |
67 | 74 |
75 " Generate a path for a given filename in the cache directory. | |
76 function! gutentags#get_cachefile(root_dir, filename) abort | |
77 if gutentags#is_path_rooted(a:filename) | |
78 return a:filename | |
79 endif | |
80 let l:tag_path = gutentags#stripslash(a:root_dir) . '/' . a:filename | |
81 if g:gutentags_cache_dir != "" | |
82 " Put the tag file in the cache dir instead of inside the | |
83 " project root. | |
84 let l:tag_path = g:gutentags_cache_dir . '/' . | |
85 \tr(l:tag_path, '\/: ', '---_') | |
86 let l:tag_path = substitute(l:tag_path, '/\-', '/', '') | |
87 endif | |
88 let l:tag_path = gutentags#normalizepath(l:tag_path) | |
89 return l:tag_path | |
90 endfunction | |
91 | |
92 " Makes sure a given command starts with an executable that's in the PATH. | |
93 function! gutentags#validate_cmd(cmd) abort | |
94 if !empty(a:cmd) && executable(split(a:cmd)[0]) | |
95 return a:cmd | |
96 endif | |
97 return "" | |
98 endfunction | |
99 | |
100 " Makes an appropriate command line for use with `job_start` by converting | |
101 " a list of possibly quoted arguments into a single string on Windows, or | |
102 " into a list of unquoted arguments on Unix/Mac. | |
103 if has('win32') || has('win64') | |
104 function! gutentags#make_args(cmd) abort | |
105 return join(a:cmd, ' ') | |
106 endfunction | |
107 else | |
108 function! gutentags#make_args(cmd) abort | |
109 let l:outcmd = [] | |
110 for cmdarg in a:cmd | |
111 " Thanks Vimscript... you can use negative integers for strings | |
112 " in the slice notation, but not for indexing characters :( | |
113 let l:arglen = strlen(cmdarg) | |
114 if (cmdarg[0] == '"' && cmdarg[l:arglen - 1] == '"') || | |
115 \(cmdarg[0] == "'" && cmdarg[l:arglen - 1] == "'") | |
116 call add(l:outcmd, cmdarg[1:-2]) | |
117 else | |
118 call add(l:outcmd, cmdarg) | |
119 endif | |
120 endfor | |
121 return l:outcmd | |
122 endfunction | |
123 endif | |
124 | |
68 " Returns whether a path is rooted. | 125 " Returns whether a path is rooted. |
69 if has('win32') || has('win64') | 126 if has('win32') || has('win64') |
70 function! gutentags#is_path_rooted(path) abort | 127 function! gutentags#is_path_rooted(path) abort |
71 return len(a:path) >= 2 && ( | 128 return len(a:path) >= 2 && ( |
72 \a:path[0] == '/' || a:path[0] == '\' || a:path[1] == ':') | 129 \a:path[0] == '/' || a:path[0] == '\' || a:path[1] == ':') |
73 endfunction | 130 endfunction |
74 else | 131 else |
75 function! gutentags#is_path_rooted(path) abort | 132 function! gutentags#is_path_rooted(path) abort |
76 return !empty(a:path) && a:path[0] == '/' | 133 return !empty(a:path) && a:path[0] == '/' |
77 endfunction | 134 endfunction |
78 endif | 135 endif |
79 | 136 |
80 " }}} | 137 " }}} |
81 | 138 |
82 " Gutentags Setup {{{ | 139 " Gutentags Setup {{{ |
100 break | 157 break |
101 endif | 158 endif |
102 endfor | 159 endfor |
103 | 160 |
104 let s:known_projects[a:path] = l:result | 161 let s:known_projects[a:path] = l:result |
105 endfunction | |
106 | |
107 function! gutentags#validate_cmd(cmd) abort | |
108 if !empty(a:cmd) && executable(split(a:cmd)[0]) | |
109 return a:cmd | |
110 endif | |
111 return "" | |
112 endfunction | 162 endfunction |
113 | 163 |
114 function! gutentags#get_project_file_list_cmd(path) abort | 164 function! gutentags#get_project_file_list_cmd(path) abort |
115 if type(g:gutentags_file_list_command) == type("") | 165 if type(g:gutentags_file_list_command) == type("") |
116 return gutentags#validate_cmd(g:gutentags_file_list_command) | 166 return gutentags#validate_cmd(g:gutentags_file_list_command) |
179 " Get info on the project we're inside of. | 229 " Get info on the project we're inside of. |
180 function! gutentags#get_project_info(path) abort | 230 function! gutentags#get_project_info(path) abort |
181 return get(s:known_projects, a:path, {}) | 231 return get(s:known_projects, a:path, {}) |
182 endfunction | 232 endfunction |
183 | 233 |
184 " Generate a path for a given filename in the cache directory. | |
185 function! gutentags#get_cachefile(root_dir, filename) abort | |
186 if gutentags#is_path_rooted(a:filename) | |
187 return a:filename | |
188 endif | |
189 let l:tag_path = gutentags#stripslash(a:root_dir) . '/' . a:filename | |
190 if g:gutentags_cache_dir != "" | |
191 " Put the tag file in the cache dir instead of inside the | |
192 " project root. | |
193 let l:tag_path = g:gutentags_cache_dir . '/' . | |
194 \tr(l:tag_path, '\/: ', '---_') | |
195 let l:tag_path = substitute(l:tag_path, '/\-', '/', '') | |
196 endif | |
197 let l:tag_path = gutentags#normalizepath(l:tag_path) | |
198 return l:tag_path | |
199 endfunction | |
200 | |
201 " Setup gutentags for the current buffer. | 234 " Setup gutentags for the current buffer. |
202 function! gutentags#setup_gutentags() abort | 235 function! gutentags#setup_gutentags() abort |
203 if exists('b:gutentags_files') && !g:gutentags_debug | 236 if exists('b:gutentags_files') && !g:gutentags_debug |
204 " This buffer already has gutentags support. | 237 " This buffer already has gutentags support. |
205 return | 238 return |
297 endfor | 330 endfor |
298 endfunction | 331 endfunction |
299 | 332 |
300 " }}} | 333 " }}} |
301 | 334 |
302 " Tags File Management {{{ | 335 " Job Management {{{ |
303 | 336 |
304 " List of queued-up jobs, and in-progress jobs, per module. | 337 " List of queued-up jobs, and in-progress jobs, per module. |
305 let s:update_queue = {} | 338 let s:update_queue = {} |
306 let s:maybe_in_progress = {} | 339 let s:update_in_progress = {} |
307 for module in g:gutentags_modules | 340 for module in g:gutentags_modules |
308 let s:update_queue[module] = [] | 341 let s:update_queue[module] = [] |
309 let s:maybe_in_progress[module] = {} | 342 let s:update_in_progress[module] = [] |
310 endfor | 343 endfor |
311 | 344 |
312 " Make a given file known as being currently generated or updated. | 345 function! gutentags#add_job(module, tags_file, data) abort |
313 function! gutentags#add_progress(module, file) abort | 346 call add(s:update_in_progress[a:module], [a:tags_file, a:data]) |
314 let l:abs_file = fnamemodify(a:file, ':p') | 347 endfunction |
315 let s:maybe_in_progress[a:module][l:abs_file] = localtime() | 348 |
316 endfunction | 349 function! gutentags#find_job_index_by_tags_file(module, tags_file) abort |
317 | 350 let l:idx = -1 |
318 " Get how to execute an external command depending on debug settings. | 351 for upd_info in s:update_in_progress[a:module] |
319 function! gutentags#get_execute_cmd() abort | 352 let l:idx += 1 |
320 if has('win32') | 353 if upd_info[0] == a:tags_file |
321 let l:cmd = '!start ' | 354 return l:idx |
322 if g:gutentags_background_update | 355 endif |
323 let l:cmd .= '/b ' | 356 endfor |
324 endif | 357 return -1 |
325 return l:cmd | 358 endfunction |
359 | |
360 function! gutentags#find_job_index_by_data(module, data) abort | |
361 let l:idx = -1 | |
362 for upd_info in s:update_in_progress[a:module] | |
363 let l:idx += 1 | |
364 if upd_info[1] == a:data | |
365 return l:idx | |
366 endif | |
367 endfor | |
368 return -1 | |
369 endfunction | |
370 | |
371 function! gutentags#get_job_tags_file(module, job_idx) abort | |
372 return s:update_in_progress[a:module][a:job_idx][0] | |
373 endfunction | |
374 | |
375 function! gutentags#get_job_data(module, job_idx) abort | |
376 return s:update_in_progress[a:module][a:job_idx][1] | |
377 endfunction | |
378 | |
379 function! gutentags#remove_job(module, job_idx) abort | |
380 let l:tags_file = s:update_in_progress[a:module][a:job_idx][0] | |
381 call remove(s:update_in_progress[a:module], a:job_idx) | |
382 | |
383 " Run the user callback for finished jobs. | |
384 silent doautocmd User GutentagsUpdated | |
385 | |
386 " See if we had any more updates queued up for this. | |
387 let l:qu_idx = -1 | |
388 for qu_info in s:update_queue[a:module] | |
389 let l:qu_idx += 1 | |
390 if qu_info[0] == l:tags_file | |
391 break | |
392 endif | |
393 endfor | |
394 if l:qu_idx >= 0 | |
395 let l:qu_info = s:update_queue[a:module][l:qu_idx] | |
396 call remove(s:update_queue[a:module], l:qu_idx) | |
397 | |
398 if bufexists(l:qu_info[1]) | |
399 call gutentags#trace("Finished ".a:module." job, ". | |
400 \"running queued update for '".l:tags_file."'.") | |
401 call s:update_tags(l:qu_info[1], a:module, l:qu_info[2], 2) | |
402 else | |
403 call gutentags#trace("Finished ".a:module." job, ". | |
404 \"but skipping queued update for '".l:tags_file."' ". | |
405 \"because originating buffer doesn't exist anymore.") | |
406 endif | |
326 else | 407 else |
327 return '!' | 408 call gutentags#trace("Finished ".a:module." job.") |
328 endif | 409 endif |
329 endfunction | 410 endfunction |
330 | 411 |
331 " Get the suffix for how to execute an external command. | 412 function! gutentags#remove_job_by_data(module, data) abort |
332 function! gutentags#get_execute_cmd_suffix() abort | 413 let l:idx = gutentags#find_job_index_by_data(a:module, a:data) |
333 if has('win32') | 414 call gutentags#remove_job(a:module, l:idx) |
334 return '' | 415 endfunction |
335 else | 416 |
336 return ' &' | 417 " }}} |
337 endif | 418 |
338 endfunction | 419 " Tags File Management {{{ |
339 | 420 |
340 " (Re)Generate the tags file for the current buffer's file. | 421 " (Re)Generate the tags file for the current buffer's file. |
341 function! s:manual_update_tags(bang) abort | 422 function! s:manual_update_tags(bang) abort |
342 let l:bn = bufnr('%') | 423 let l:bn = bufnr('%') |
343 for module in g:gutentags_modules | 424 for module in g:gutentags_modules |
344 call s:update_tags(l:bn, module, a:bang, 0) | 425 call s:update_tags(l:bn, module, a:bang, 0) |
345 endfor | 426 endfor |
346 silent doautocmd User GutentagsUpdated | 427 silent doautocmd User GutentagsUpdating |
347 endfunction | 428 endfunction |
348 | 429 |
349 " (Re)Generate the tags file for a buffer that just go saved. | 430 " (Re)Generate the tags file for a buffer that just go saved. |
350 function! s:write_triggered_update_tags(bufno) abort | 431 function! s:write_triggered_update_tags(bufno) abort |
351 if g:gutentags_enabled && g:gutentags_generate_on_write | 432 if g:gutentags_enabled && g:gutentags_generate_on_write |
352 for module in g:gutentags_modules | 433 for module in g:gutentags_modules |
353 call s:update_tags(a:bufno, module, 0, 2) | 434 call s:update_tags(a:bufno, module, 0, 2) |
354 endfor | 435 endfor |
355 endif | 436 endif |
356 silent doautocmd User GutentagsUpdated | 437 silent doautocmd User GutentagsUpdating |
357 endfunction | 438 endfunction |
358 | 439 |
359 " Update the tags file for the current buffer's file. | 440 " Update the tags file for the current buffer's file. |
360 " write_mode: | 441 " write_mode: |
361 " 0: update the tags file if it exists, generate it otherwise. | 442 " 0: update the tags file if it exists, generate it otherwise. |
370 let l:buf_gutentags_files = getbufvar(a:bufno, 'gutentags_files') | 451 let l:buf_gutentags_files = getbufvar(a:bufno, 'gutentags_files') |
371 let l:tags_file = l:buf_gutentags_files[a:module] | 452 let l:tags_file = l:buf_gutentags_files[a:module] |
372 let l:proj_dir = getbufvar(a:bufno, 'gutentags_root') | 453 let l:proj_dir = getbufvar(a:bufno, 'gutentags_root') |
373 | 454 |
374 " Check that there's not already an update in progress. | 455 " Check that there's not already an update in progress. |
375 let l:lock_file = l:tags_file . '.lock' | 456 let l:in_progress_idx = gutentags#find_job_index_by_tags_file( |
376 if filereadable(l:lock_file) | 457 \a:module, l:tags_file) |
458 if l:in_progress_idx >= 0 | |
377 if a:queue_mode == 2 | 459 if a:queue_mode == 2 |
378 let l:idx = index(s:update_queue[a:module], l:tags_file) | 460 let l:needs_queuing = 1 |
379 if l:idx < 0 | 461 for qu_info in s:update_queue[a:module] |
380 call add(s:update_queue[a:module], l:tags_file) | 462 if qu_info[0] == l:tags_file |
463 let l:needs_queuing = 0 | |
464 break | |
465 endif | |
466 endfor | |
467 if l:needs_queuing | |
468 call add(s:update_queue[a:module], | |
469 \[l:tags_file, a:bufno, a:write_mode]) | |
381 endif | 470 endif |
382 call gutentags#trace("Tag file '" . l:tags_file . | 471 call gutentags#trace("Tag file '" . l:tags_file . |
383 \"' is already being updated. Queuing it up...") | 472 \"' is already being updated. Queuing it up...") |
384 elseif a:queue_mode == 1 | 473 elseif a:queue_mode == 1 |
385 call gutentags#trace("Tag file '" . l:tags_file . | 474 call gutentags#trace("Tag file '" . l:tags_file . |
386 \"' is already being updated. Skipping...") | 475 \"' is already being updated. Skipping...") |
387 elseif a:queue_mode == 0 | 476 elseif a:queue_mode == 0 |
388 echom "gutentags: The tags file is already being updated, " . | 477 echom "gutentags: The tags file is already being updated, " . |
389 \"please try again later." | 478 \"please try again later." |
390 else | 479 else |
391 call gutentags#throwerr("Unknown queue mode: " . a:queue_mode) | 480 call gutentags#throw("Unknown queue mode: " . a:queue_mode) |
392 endif | 481 endif |
482 | |
483 " Don't update the tags right now. | |
393 return | 484 return |
394 endif | 485 endif |
395 | 486 |
396 " Switch to the project root to make the command line smaller, and make | 487 " Switch to the project root to make the command line smaller, and make |
397 " it possible to get the relative path of the filename to parse if we're | 488 " it possible to get the relative path of the filename to parse if we're |
398 " doing an incremental update. | 489 " doing an incremental update. |
399 let l:prev_cwd = getcwd() | 490 let l:prev_cwd = getcwd() |
400 call gutentags#chdir(fnameescape(l:proj_dir)) | 491 call gutentags#chdir(fnameescape(l:proj_dir)) |
401 try | 492 try |
402 call call("gutentags#".a:module."#generate", | 493 call call("gutentags#".a:module."#generate", |
403 \[l:proj_dir, l:tags_file, a:write_mode]) | 494 \[l:proj_dir, l:tags_file, |
495 \ { | |
496 \ 'write_mode': a:write_mode, | |
497 \ }]) | |
404 catch /^gutentags\:/ | 498 catch /^gutentags\:/ |
405 echom "Error while generating ".a:module." file:" | 499 echom "Error while generating ".a:module." file:" |
406 echom v:exception | 500 echom v:exception |
407 finally | 501 finally |
408 " Restore the current directory... | 502 " Restore the current directory... |
426 if a:0 && a:1 | 520 if a:0 && a:1 |
427 let g:gutentags_trace = l:trace_backup | 521 let g:gutentags_trace = l:trace_backup |
428 endif | 522 endif |
429 endfunction | 523 endfunction |
430 | 524 |
431 function! gutentags#delete_lock_files() abort | |
432 if exists('b:gutentags_files') | |
433 for tagfile in values(b:gutentags_files) | |
434 silent call delete(tagfile.'.lock') | |
435 endfor | |
436 endif | |
437 endfunction | |
438 | |
439 function! gutentags#toggletrace(...) | 525 function! gutentags#toggletrace(...) |
440 let g:gutentags_trace = !g:gutentags_trace | 526 let g:gutentags_trace = !g:gutentags_trace |
441 if a:0 > 0 | 527 if a:0 > 0 |
442 let g:gutentags_trace = a:1 | 528 let g:gutentags_trace = a:1 |
443 endif | 529 endif |
459 else | 545 else |
460 echom "gutentags: Now running gutentags for real." | 546 echom "gutentags: Now running gutentags for real." |
461 endif | 547 endif |
462 echom "" | 548 echom "" |
463 endfunction | 549 endfunction |
550 | |
551 function! gutentags#default_io_cb(data) abort | |
552 call gutentags#trace(a:data) | |
553 endfunction | |
554 | |
555 if has('nvim') | |
556 " Neovim job API. | |
557 function! s:nvim_job_exit_wrapper(real_cb, job, exit_code, event_type) abort | |
558 call call(a:real_cb, [a:job, a:exit_code]) | |
559 endfunction | |
560 | |
561 function! s:nvim_job_out_wrapper(real_cb, job, lines, event_type) abort | |
562 call call(a:real_cb, [a:lines]) | |
563 endfunction | |
564 | |
565 function! gutentags#build_default_job_options(module) abort | |
566 let l:job_opts = { | |
567 \'on_exit': function( | |
568 \ '<SID>nvim_job_exit_wrapper', | |
569 \ ['gutentags#'.a:module.'#on_job_exit']), | |
570 \'on_stdout': function( | |
571 \ '<SID>nvim_job_out_wrapper', | |
572 \ ['gutentags#default_io_cb']), | |
573 \'on_stderr': function( | |
574 \ '<SID>nvim_job_out_wrapper', | |
575 \ ['gutentags#default_io_cb']) | |
576 \} | |
577 return l:job_opts | |
578 endfunction | |
579 | |
580 function! gutentags#start_job(cmd, opts) abort | |
581 return jobstart(a:cmd, a:opts) | |
582 endfunction | |
583 else | |
584 " Vim8 job API. | |
585 function! gutentags#build_default_job_options(module) abort | |
586 let l:job_opts = { | |
587 \'exit_cb': 'gutentags#'.a:module.'#on_job_exit' | |
588 \'out_cb': 'gutentags#default_io_cb', | |
589 \'err_cb': 'gutentags#default_io_cb' | |
590 \} | |
591 return l:job_opts | |
592 endfunction | |
593 | |
594 function! gutentags#start_job(cmd, opts) abort | |
595 return job_start(a:cmd, a:opts) | |
596 endfunction | |
597 endif | |
464 | 598 |
465 function! gutentags#inprogress() | 599 function! gutentags#inprogress() |
466 echom "gutentags: generations in progress:" | 600 echom "gutentags: generations in progress:" |
467 for mod_name in keys(s:maybe_in_progress) | 601 for mod_name in keys(s:maybe_in_progress) |
468 for mib in keys(s:maybe_in_progress[mod_name]) | 602 for mib in keys(s:maybe_in_progress[mod_name]) |
490 if !exists('b:gutentags_files') | 624 if !exists('b:gutentags_files') |
491 " This buffer doesn't have gutentags. | 625 " This buffer doesn't have gutentags. |
492 return '' | 626 return '' |
493 endif | 627 endif |
494 | 628 |
495 " Figure out what the user is customizing. | 629 " Find any module that has a job in progress for any of this buffer's |
630 " tags files. | |
631 let l:modules_in_progress = [] | |
632 for [module, tags_file] in items(b:gutentags_files) | |
633 let l:jobidx = gutentags#find_job_index_by_tags_file(module, tags_file) | |
634 if l:jobidx >= 0 | |
635 call add(l:modules_in_progress, module) | |
636 endif | |
637 endfor | |
638 | |
639 " Did we find any module? If not, don't print anything. | |
640 if len(l:modules_in_progress) == 0 | |
641 return '' | |
642 endif | |
643 | |
644 " W00t, stuff is happening! Let's print what. | |
496 let l:gen_msg = 'TAGS' | 645 let l:gen_msg = 'TAGS' |
497 if a:0 > 0 | 646 if a:0 > 0 |
498 let l:gen_msg = a:1 | 647 let l:gen_msg = a:1 |
499 endif | 648 endif |
500 | |
501 " To make this function as fast as possible, we first check whether the | |
502 " current buffer's tags file is 'maybe' being generated. This provides a | |
503 " nice and quick bail out for 99.9% of cases before we need to this the | |
504 " file-system to check the lock file. | |
505 let l:modules_in_progress = [] | |
506 for module in keys(b:gutentags_files) | |
507 let l:abs_tag_file = fnamemodify(b:gutentags_files[module], ':p') | |
508 let l:progress_queue = s:maybe_in_progress[module] | |
509 let l:timestamp = get(l:progress_queue, l:abs_tag_file) | |
510 if l:timestamp == 0 | |
511 continue | |
512 endif | |
513 " It's maybe generating! Check if the lock file is still there... but | |
514 " don't do it too soon after the script was originally launched, because | |
515 " there can be a race condition where we get here just before the script | |
516 " had a chance to write the lock file. | |
517 if (localtime() - l:timestamp) > 1 && | |
518 \!filereadable(l:abs_tag_file . '.lock') | |
519 call remove(l:progress_queue, l:abs_tag_file) | |
520 continue | |
521 endif | |
522 call add(l:modules_in_progress, module) | |
523 endfor | |
524 | |
525 if len(l:modules_in_progress) == 0 | |
526 return '' | |
527 endif | |
528 | |
529 " It's still there! So probably `ctags` is still running... | |
530 " (although there's a chance it crashed, or the script had a problem, and | |
531 " the lock file has been left behind... we could try and run some | |
532 " additional checks here to see if it's legitimately running, and | |
533 " otherwise delete the lock file... maybe in the future...) | |
534 let l:gen_msg .= '['.join(l:modules_in_progress, ',').']' | 649 let l:gen_msg .= '['.join(l:modules_in_progress, ',').']' |
535 return l:gen_msg | 650 return l:gen_msg |
536 endfunction | 651 endfunction |
537 | 652 |
538 " }}} | 653 " }}} |