Mercurial > vim-gutentags
changeset 41:99328cb71e75
Refactor Gutentags so functionality can be implemented in "modules".
The first module is Ctags generation, obviously.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 20 Feb 2015 14:13:51 -0800 |
parents | 8b3c611a4d3b |
children | 5c1baa6007d8 |
files | autoload/gutentags.vim autoload/gutentags/ctags.vim plugin/gutentags.vim |
diffstat | 3 files changed, 498 insertions(+), 411 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/autoload/gutentags.vim Fri Feb 20 14:13:51 2015 -0800 @@ -0,0 +1,376 @@ +" gutentags.vim - Automatic ctags management for Vim + +" Utilities {{{ + +" Throw an exception message. +function! gutentags#throw(message) + let v:errmsg = "gutentags: " . a:message + throw v:errmsg +endfunction + +" Prints a message if debug tracing is enabled. +function! gutentags#trace(message, ...) + if g:gutentags_trace || (a:0 && a:1) + let l:message = "gutentags: " . a:message + echom l:message + endif +endfunction + +" Strips the ending slash in a path. +function! gutentags#stripslash(path) + return fnamemodify(a:path, ':s?[/\\]$??') +endfunction + +" Normalizes the slashes in a path. +function! gutentags#normalizepath(path) + if exists('+shellslash') && &shellslash + return substitute(a:path, '\v/', '\\', 'g') + elseif has('win32') + return substitute(a:path, '\v/', '\\', 'g') + else + return a:path + endif +endfunction + +" Shell-slashes the path (opposite of `normalizepath`). +function! gutentags#shellslash(path) + if exists('+shellslash') && !&shellslash + return substitute(a:path, '\v\\', '/', 'g') + else + return a:path + endif +endfunction + +" Gets a file path in the correct `plat` folder. +function! gutentags#get_plat_file(filename) abort + return g:gutentags_plat_dir . a:filename . g:gutentags_script_ext +endfunction + +" }}} + +" Gutentags Setup {{{ + +let s:known_files = [] + +" Finds the first directory with a project marker by walking up from the given +" file path. +function! gutentags#get_project_root(path) abort + let l:path = gutentags#stripslash(a:path) + let l:previous_path = "" + let l:markers = g:gutentags_project_root[:] + if exists('g:ctrlp_root_markers') + let l:markers += g:ctrlp_root_markers + endif + while l:path != l:previous_path + for root in g:gutentags_project_root + if getftype(l:path . '/' . root) != "" + let l:proj_dir = simplify(fnamemodify(l:path, ':p')) + return gutentags#stripslash(l:proj_dir) + endif + endfor + let l:previous_path = l:path + let l:path = fnamemodify(l:path, ':h') + endwhile + call gutentags#throw("Can't figure out what tag file to use for: " . a:path) +endfunction + +" Generate a path for a given filename in the cache directory. +function! gutentags#get_cachefile(root_dir, filename) abort + let l:tag_path = gutentags#stripslash(a:root_dir) . '/' . a:filename + if g:gutentags_cache_dir != "" + " Put the tag file in the cache dir instead of inside the + " projet root. + let l:tag_path = g:gutentags_cache_dir . '/' . + \tr(l:tag_path, '\/:', '---') + let l:tag_path = substitute(l:tag_path, '/\-', '/', '') + endif + let l:tag_path = gutentags#normalizepath(l:tag_path) + return l:tag_path +endfunction + +" Setup gutentags for the current buffer. +function! gutentags#setup_gutentags() abort + if exists('b:gutentags_files') && !g:gutentags_debug + " This buffer already has gutentags support. + return + endif + + " Try and find what tags file we should manage. + call gutentags#trace("Scanning buffer '" . bufname('%') . "' for gutentags setup...") + try + let b:gutentags_root = gutentags#get_project_root(expand('%:h')) + let b:gutentags_files = {} + for module in g:gutentags_modules + call call("gutentags#".module."#init", [b:gutentags_root]) + endfor + catch /^gutentags\:/ + call gutentags#trace("Can't figure out what tag file to use... no gutentags support.") + return + endtry + + " We know what tags file to manage! Now set things up. + call gutentags#trace("Setting gutentags for buffer '" . bufname('%')) + + " Autocommands for updating the tags on save. + let l:bn = bufnr('%') + execute 'augroup gutentags_buffer_' . l:bn + execute ' autocmd!' + execute ' autocmd BufWritePost <buffer=' . l:bn . '> call s:write_triggered_update_tags()' + execute 'augroup end' + + " Miscellaneous commands. + command! -buffer -bang GutentagsUpdate :call s:manual_update_tags(<bang>0) + + " Add these tags files to the known tags files. + for module in keys(b:gutentags_files) + let l:tagfile = b:gutentags_files[module] + let l:found = index(s:known_files, l:tagfile) + if l:found < 0 + call add(s:known_files, l:tagfile) + + " Generate this new file depending on settings and stuff. + if g:gutentags_generate_on_missing && !filereadable(l:tagfile) + call gutentags#trace("Generating missing tags file: " . l:tagfile) + call s:update_tags(module, 1, 0) + elseif g:gutentags_generate_on_new + call gutentags#trace("Generating tags file: " . l:tagfile) + call s:update_tags(module, 1, 0) + endif + endif + endfor +endfunction + +" }}} + +" Tags File Management {{{ + +" List of queued-up jobs, and in-progress jobs, per module. +let s:update_queue = {} +let s:maybe_in_progress = {} +for module in g:gutentags_modules + let s:update_queue[module] = [] + let s:maybe_in_progress[module] = {} +endfor + +" Make a given file known as being currently generated or updated. +function! gutentags#add_progress(module, file) abort + let s:maybe_in_progress[a:module][a:file] = localtime() +endfunction + +" Get how to execute an external command depending on debug settings. +function! gutentags#get_execute_cmd() abort + if has('win32') + let l:cmd = '!start ' + if g:gutentags_background_update + let l:cmd .= '/b ' + endif + return l:cmd + else + return '!' + endif +endfunction + +" Get the suffix for how to execute an external command. +function! gutentags#get_execute_cmd_suffix() abort + if has('win32') + return '' + else + return ' &' + endif +endfunction + +" (Re)Generate the tags file for the current buffer's file. +function! s:manual_update_tags(module, bang) abort + for module in g:gutentags_modules + call s:update_tags(module, a:bang, 0) + endfor +endfunction + +" (Re)Generate the tags file for a buffer that just go saved. +function! s:write_triggered_update_tags() abort + if g:gutentags_enabled && g:gutentags_generate_on_write + for module in g:gutentags_modules + call s:update_tags(module, 0, 1) + endfor + endif +endfunction + +" Update the tags file for the current buffer's file. +" write_mode: +" 0: update the tags file if it exists, generate it otherwise. +" 1: always generate (overwrite) the tags file. +" +" queue_mode: +" 0: if an update is already in progress, report it and abort. +" 1: if an update is already in progress, queue another one. +" +" An additional argument specifies where to write the tags file. If nothing +" is specified, it will go to the gutentags-defined file. +function! s:update_tags(module, write_mode, queue_mode, ...) abort + " Figure out where to save. + if a:0 == 1 + let l:tags_file = a:1 + let l:proj_dir = fnamemodify(a:1, ':h') + else + let l:tags_file = b:gutentags_files[a:module] + let l:proj_dir = b:gutentags_root + endif + + " Check that there's not already an update in progress. + let l:lock_file = l:tags_file . '.lock' + if filereadable(l:lock_file) + if a:queue_mode == 1 + let l:idx = index(s:update_queue[a:module], l:tags_file) + if l:idx < 0 + call add(s:update_queue[a:module], l:tags_file) + endif + call gutentags#trace("Tag file '" . l:tags_file . + \"' is already being updated. Queuing it up...") + call gutentags#trace("") + else + echom "gutentags: The tags file is already being updated, " . + \"please try again later." + echom "" + endif + return + endif + + " Switch to the project root to make the command line smaller, and make + " it possible to get the relative path of the filename to parse if we're + " doing an incremental update. + let l:prev_cwd = getcwd() + execute "chdir " . fnameescape(l:proj_dir) + try + call call("gutentags#".a:module."#generate", + \[l:proj_dir, l:tags_file, a:write_mode]) + finally + " Restore the current directory... + execute "chdir " . fnameescape(l:prev_cwd) + endtry +endfunction + +" }}} + +" Manual Tagfile Generation {{{ + +function! s:generate_tags(bang, ...) abort + call s:update_tags(1, 0, a:1) +endfunction + +command! -bang -nargs=1 -complete=file GutentagsGenerate :call s:generate_tags(<bang>0, <f-args>) + +" }}} + +" Utility Functions {{{ + +function! gutentags#rescan(...) + if exists('b:gutentags_files') + unlet b:gutentags_files + endif + if a:0 && a:1 + let l:trace_backup = g:gutentags_trace + let l:gutentags_trace = 1 + endif + call s:setup_gutentags() + if a:0 && a:1 + let g:gutentags_trace = l:trace_backup + endif +endfunction + +function! gutentags#toggletrace(...) + let g:gutentags_trace = !g:gutentags_trace + if a:0 > 0 + let g:gutentags_trace = a:1 + endif + if g:gutentags_trace + echom "gutentags: Tracing is enabled." + else + echom "gutentags: Tracing is disabled." + endif + echom "" +endfunction + +function! gutentags#fake(...) + let g:gutentags_fake = !g:gutentags_fake + if a:0 > 0 + let g:gutentags_fake = a:1 + endif + if g:gutentags_fake + echom "gutentags: Now faking gutentags." + else + echom "gutentags: Now running gutentags for real." + endif + echom "" +endfunction + +function! gutentags#inprogress() + echom "gutentags: generations in progress:" + for mip in keys(s:maybe_in_progress) + echom mip + endfor + echom "" +endfunction + +" }}} + +" Statusline Functions {{{ + +" Prints whether a tag file is being generated right now for the current +" buffer in the status line. +" +" Arguments can be passed: +" - args 1 and 2 are the prefix and suffix, respectively, of whatever output, +" if any, is going to be produced. +" (defaults to empty strings) +" - arg 3 is the text to be shown if tags are currently being generated. +" (defaults to 'TAGS') + +function! gutentags#statusline(...) abort + if !exists('b:gutentags_files') + " This buffer doesn't have gutentags. + return '' + endif + + " Figure out what the user is customizing. + let l:gen_msg = 'TAGS' + if a:0 > 0 + let l:gen_msg = a:1 + endif + + " To make this function as fast as possible, we first check whether the + " current buffer's tags file is 'maybe' being generated. This provides a + " nice and quick bail out for 99.9% of cases before we need to this the + " file-system to check the lock file. + let l:modules_in_progress = [] + for module in keys(b:gutentags_files) + let l:abs_tag_file = fnamemodify(b:gutentags_files[module], ':p') + let l:progress_queue = s:maybe_in_progress[module] + let l:timestamp = get(l:progress_queue, l:abs_tag_file) + if l:timestamp == 0 + return '' + endif + " It's maybe generating! Check if the lock file is still there... but + " don't do it too soon after the script was originally launched, because + " there can be a race condition where we get here just before the script + " had a chance to write the lock file. + if (localtime() - l:timestamp) > 1 && + \!filereadable(l:abs_tag_file . '.lock') + call remove(l:progress_queue, l:abs_tag_file) + return '' + endif + call add(l:modules_in_progress, module) + endfor + + " It's still there! So probably `ctags` is still running... + " (although there's a chance it crashed, or the script had a problem, and + " the lock file has been left behind... we could try and run some + " additional checks here to see if it's legitimately running, and + " otherwise delete the lock file... maybe in the future...) + if len(g:gutentags_modules) > 1 + let l:gen_msg .= '['.join(l:modules_in_progress, ',').']' + endif + return l:gen_msg +endfunction + +" }}} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/autoload/gutentags/ctags.vim Fri Feb 20 14:13:51 2015 -0800 @@ -0,0 +1,104 @@ +" Ctags module for Gutentags + +" Global Options {{{ + +if !exists('g:gutentags_ctags_executable') + let g:gutentags_executable = 'ctags' +endif + +if !exists('g:gutentags_ctags_options_file') + let g:gutentags_ctags_options_file = '' +endif + +if !exists('g:gutentags_tagfile') + let g:gutentags_tagfile = 'tags' +endif + +if !exists('g:gutentags_auto_set_tags') + let g:gutentags_auto_set_tags = 1 +endif + +" }}} + +" Gutentags Module Interface {{{ + +let s:runner_exe = gutentags#get_plat_file('update_tags') + +function! gutentags#ctags#init(project_root) abort + " Figure out the path to the tags file. + let b:gutentags_files['ctags'] = gutentags#get_cachefile( + \a:project_root, g:gutentags_tagfile) + + " Set the tags file for Vim to use. + if g:gutentags_auto_set_tags + execute 'setlocal tags^=' . fnameescape(b:gutentags_files['ctags']) + endif +endfunction + +function! gutentags#ctags#generate(proj_dir, tags_file, write_mode) abort + " Get to the tags file directory because ctags is finicky about + " these things. + let l:prev_cwd = getcwd() + let l:work_dir = fnamemodify(a:tags_file, ':h') + execute "chdir " . fnameescape(l:work_dir) + + try + " Build the command line. + let l:cmd = gutentags#get_execute_cmd() . s:runner_exe + let l:cmd .= ' -e "' . g:gutentags_executable . '"' + let l:cmd .= ' -t "' . a:tags_file . '"' + let l:cmd .= ' -p "' . a:proj_dir . '"' + if a:write_mode == 0 && filereadable(a:tags_file) + let l:full_path = expand('%:p') + let l:cmd .= ' -s "' . l:full_path . '"' + endif + for ign in split(&wildignore, ',') + let l:cmd .= ' -x ' . '"' . ign . '"' + endfor + for exc in g:gutentags_exclude + let l:cmd .= ' -x ' . '"' . exc . '"' + endfor + if g:gutentags_pause_after_update + let l:cmd .= ' -c' + endif + if len(g:gutentags_options_file) + let l:cmd .= ' -o "' . g:gutentags_options_file . '"' + endif + if g:gutentags_trace + if has('win32') + let l:cmd .= ' -l "' . a:tags_file . '.log"' + else + let l:cmd .= ' > "' . a:tags_file . '.log" 2>&1' + endif + else + if !has('win32') + let l:cmd .= ' > /dev/null 2>&1' + endif + endif + let l:cmd .= gutentags#get_execute_cmd_suffix() + + call gutentags#trace("Running: " . l:cmd) + call gutentags#trace("In: " . getcwd()) + if !g:gutentags_fake + " Run the background process. + if !g:gutentags_trace + silent execute l:cmd + else + execute l:cmd + endif + + " Flag this tags file as being in progress + let l:full_tags_file = fnamemodify(a:tags_file, ':p') + call gutentags#add_progress('ctags', l:full_tags_file) + else + call gutentags#trace("(fake... not actually running)") + endif + call gutentags#trace("") + finally + " Restore the previous working directory. + execute "chdir " . fnameescape(l:prev_cwd) + endtry +endfunction + +" }}} +
--- a/plugin/gutentags.vim Sat Jan 10 20:45:49 2015 -0800 +++ b/plugin/gutentags.vim Fri Feb 20 14:13:51 2015 -0800 @@ -36,12 +36,8 @@ let g:gutentags_enabled = 1 endif -if !exists('g:gutentags_executable') - let g:gutentags_executable = 'ctags' -endif - -if !exists('g:gutentags_tagfile') - let g:gutentags_tagfile = 'tags' +if !exists('g:gutentags_modules') + let g:gutentags_modules = ['ctags'] endif if !exists('g:gutentags_project_root') @@ -49,10 +45,6 @@ endif let g:gutentags_project_root += ['.git', '.hg', '.svn', '.bzr', '_darcs'] -if !exists('g:gutentags_options_file') - let g:gutentags_options_file = '' -endif - if !exists('g:gutentags_exclude') let g:gutentags_exclude = [] endif @@ -69,10 +61,6 @@ let g:gutentags_generate_on_write = 1 endif -if !exists('g:gutentags_auto_set_tags') - let g:gutentags_auto_set_tags = 1 -endif - if !exists('g:gutentags_cache_dir') let g:gutentags_cache_dir = '' else @@ -83,314 +71,37 @@ call mkdir(g:gutentags_cache_dir, 'p') endif -" }}} - -" Utilities {{{ - -" Throw an exception message. -function! s:throw(message) - let v:errmsg = "gutentags: " . a:message - throw v:errmsg -endfunction - -" Prints a message if debug tracing is enabled. -function! s:trace(message, ...) - if g:gutentags_trace || (a:0 && a:1) - let l:message = "gutentags: " . a:message - echom l:message - endif -endfunction - -" Strips the ending slash in a path. -function! s:stripslash(path) - return fnamemodify(a:path, ':s?[/\\]$??') -endfunction - -" Normalizes the slashes in a path. -function! s:normalizepath(path) - if exists('+shellslash') && &shellslash - return substitute(a:path, '\v/', '\\', 'g') - elseif has('win32') - return substitute(a:path, '\v/', '\\', 'g') - else - return a:path - endif -endfunction - -" Shell-slashes the path (opposite of `normalizepath`). -function! s:shellslash(path) - if exists('+shellslash') && !&shellslash - return substitute(a:path, '\v\\', '/', 'g') - else - return a:path - endif -endfunction +if has('win32') + let g:gutentags_plat_dir = expand('<sfile>:h:h:p') . "\\plat\\win32\\" + let g:gutentags_script_ext = '.cmd' +else + let g:gutentags_plat_dir = expand('<sfile>:h:h:p') . '/plat/unix/' + let g:gutentags_script_ext = '.sh' +endif " }}} " Gutentags Setup {{{ -let s:known_tagfiles = [] - -" Finds the first directory with a project marker by walking up from the given -" file path. -function! s:get_project_root(path) abort - let l:path = s:stripslash(a:path) - let l:previous_path = "" - let l:markers = g:gutentags_project_root[:] - if exists('g:ctrlp_root_markers') - let l:markers += g:ctrlp_root_markers - endif - while l:path != l:previous_path - for root in g:gutentags_project_root - if getftype(l:path . '/' . root) != "" - let l:proj_dir = simplify(fnamemodify(l:path, ':p')) - return s:stripslash(l:proj_dir) - endif - endfor - let l:previous_path = l:path - let l:path = fnamemodify(l:path, ':h') - endwhile - call s:throw("Can't figure out what tag file to use for: " . a:path) -endfunction - -" Get the tag filename for a given project root. -function! s:get_tagfile(root_dir) abort - let l:tag_path = s:stripslash(a:root_dir) . '/' . g:gutentags_tagfile - if g:gutentags_cache_dir != "" - " Put the tag file in the cache dir instead of inside the - " projet root. - let l:tag_path = g:gutentags_cache_dir . '/' . - \tr(l:tag_path, '\/:', '---') - let l:tag_path = substitute(l:tag_path, '/\-', '/', '') - endif - let l:tag_path = s:normalizepath(l:tag_path) - return l:tag_path -endfunction - -" Setup gutentags for the current buffer. -function! s:setup_gutentags() abort - if exists('b:gutentags_file') && !g:gutentags_debug - " This buffer already has gutentags support. - return - endif - - " Try and find what tags file we should manage. - call s:trace("Scanning buffer '" . bufname('%') . "' for gutentags setup...") - try - let b:gutentags_root = s:get_project_root(expand('%:h')) - let b:gutentags_file = s:get_tagfile(b:gutentags_root) - catch /^gutentags\:/ - call s:trace("Can't figure out what tag file to use... no gutentags support.") - return - endtry - - " We know what tags file to manage! Now set things up. - call s:trace("Setting gutentags for buffer '" . bufname('%') . "' with tagfile: " . b:gutentags_file) - - " Set the tags file for Vim to use. - if g:gutentags_auto_set_tags - execute 'setlocal tags^=' . fnameescape(b:gutentags_file) - endif - - " Autocommands for updating the tags on save. - let l:bn = bufnr('%') - execute 'augroup gutentags_buffer_' . l:bn - execute ' autocmd!' - execute ' autocmd BufWritePost <buffer=' . l:bn . '> call s:write_triggered_update_tags()' - execute 'augroup end' - - " Miscellaneous commands. - command! -buffer -bang GutentagsUpdate :call s:manual_update_tags(<bang>0) - - " Add this tags file to the known tags files if it wasn't there already. - let l:found = index(s:known_tagfiles, b:gutentags_file) - if l:found < 0 - call add(s:known_tagfiles, b:gutentags_file) - - " Generate this new file depending on settings and stuff. - if g:gutentags_generate_on_missing && !filereadable(b:gutentags_file) - call s:trace("Generating missing tags file: " . b:gutentags_file) - call s:update_tags(1, 0) - elseif g:gutentags_generate_on_new - call s:trace("Generating tags file: " . b:gutentags_file) - call s:update_tags(1, 0) - endif - endif -endfunction - augroup gutentags_detect autocmd! - autocmd BufNewFile,BufReadPost * call s:setup_gutentags() - autocmd VimEnter * if expand('<amatch>')==''|call s:setup_gutentags()|endif + autocmd BufNewFile,BufReadPost * call gutentags#setup_gutentags() + autocmd VimEnter * if expand('<amatch>')==''|call gutentags#setup_gutentags()|endif augroup end " }}} -" Tags File Management {{{ - -let s:runner_exe = expand('<sfile>:h:h') . '/plat/unix/update_tags.sh' -if has('win32') - let s:runner_exe = expand('<sfile>:h:h') . '\plat\win32\update_tags.cmd' -endif - -let s:update_queue = [] -let s:maybe_in_progress = {} - -" Get how to execute an external command depending on debug settings. -function! s:get_execute_cmd() abort - if has('win32') - let l:cmd = '!start ' - if g:gutentags_background_update - let l:cmd .= '/b ' - endif - return l:cmd - else - return '!' - endif -endfunction - -" Get the suffix for how to execute an external command. -function! s:get_execute_cmd_suffix() abort - if has('win32') - return '' - else - return ' &' - endif -endfunction - -" (Re)Generate the tags file for the current buffer's file. -function! s:manual_update_tags(bang) abort - call s:update_tags(a:bang, 0) -endfunction - -" (Re)Generate the tags file for a buffer that just go saved. -function! s:write_triggered_update_tags() abort - if g:gutentags_enabled && g:gutentags_generate_on_write - call s:update_tags(0, 1) - endif -endfunction - -" Update the tags file for the current buffer's file. -" write_mode: -" 0: update the tags file if it exists, generate it otherwise. -" 1: always generate (overwrite) the tags file. -" -" queue_mode: -" 0: if an update is already in progress, report it and abort. -" 1: if an update is already in progress, queue another one. -" -" An additional argument specifies where to write the tags file. If nothing -" is specified, it will go to the gutentags-defined file. -function! s:update_tags(write_mode, queue_mode, ...) abort - " Figure out where to save. - if a:0 == 1 - let l:tags_file = a:1 - let l:proj_dir = fnamemodify(a:1, ':h') - else - let l:tags_file = b:gutentags_file - let l:proj_dir = b:gutentags_root - endif - - " Check that there's not already an update in progress. - let l:lock_file = l:tags_file . '.lock' - if filereadable(l:lock_file) - if a:queue_mode == 1 - let l:idx = index(s:update_queue, l:tags_file) - if l:idx < 0 - call add(s:update_queue, l:tags_file) - endif - call s:trace("Tag file '" . l:tags_file . "' is already being updated. Queuing it up...") - call s:trace("") - else - echom "gutentags: The tags file is already being updated, please try again later." - echom "" - endif - return - endif - - " Switch to the project root to make the command line smaller, and make - " it possible to get the relative path of the filename to parse if we're - " doing an incremental update. - let l:prev_cwd = getcwd() - let l:work_dir = fnamemodify(l:tags_file, ':h') - execute "chdir " . l:work_dir - - try - " Build the command line. - let l:cmd = s:get_execute_cmd() . s:runner_exe - let l:cmd .= ' -e "' . g:gutentags_executable . '"' - let l:cmd .= ' -t "' . l:tags_file . '"' - let l:cmd .= ' -p "' . l:proj_dir . '"' - if a:write_mode == 0 && filereadable(l:tags_file) - let l:full_path = expand('%:p') - let l:cmd .= ' -s "' . l:full_path . '"' - endif - for ign in split(&wildignore, ',') - let l:cmd .= ' -x ' . '"' . ign . '"' - endfor - for exc in g:gutentags_exclude - let l:cmd .= ' -x ' . '"' . exc . '"' - endfor - if g:gutentags_pause_after_update - let l:cmd .= ' -c' - endif - if len(g:gutentags_options_file) - let l:cmd .= ' -o "' . g:gutentags_options_file . '"' - endif - if g:gutentags_trace - if has('win32') - let l:cmd .= ' -l "' . l:tags_file . '.log"' - else - let l:cmd .= ' > "' . l:tags_file . '.log" 2>&1' - endif - else - if !has('win32') - let l:cmd .= ' > /dev/null 2>&1' - endif - endif - let l:cmd .= s:get_execute_cmd_suffix() - - call s:trace("Running: " . l:cmd) - call s:trace("In: " . l:work_dir) - if !g:gutentags_fake - " Run the background process. - if !g:gutentags_trace - silent execute l:cmd - else - execute l:cmd - endif - - " Flag this tags file as being in progress - let l:full_tags_file = fnamemodify(l:tags_file, ':p') - let s:maybe_in_progress[l:full_tags_file] = localtime() - else - call s:trace("(fake... not actually running)") - endif - call s:trace("") - finally - " Restore the current directory... - execute "chdir " . fnameescape(l:prev_cwd) - endtry -endfunction - -" }}} - -" Manual Tagfile Generation {{{ - -function! s:generate_tags(bang, ...) abort - call s:update_tags(1, 0, a:1) -endfunction - -command! -bang -nargs=1 -complete=file GutentagsGenerate :call s:generate_tags(<bang>0, <f-args>) - -" }}} - " Toggles and Miscellaneous Commands {{{ +function! s:delete_lock_files() abort + for tagfile in values(b:gutentags_files) + silent call delete(tagfile.'.lock') + endfor +endfunction + command! GutentagsToggleEnabled :let g:gutentags_enabled=!g:gutentags_enabled command! GutentagsToggleTrace :call gutentags#trace() -command! GutentagsUnlock :call delete(b:gutentags_file . '.lock') +command! GutentagsUnlock :call s:delete_lock_files() if g:gutentags_debug command! GutentagsToggleFake :call gutentags#fake() @@ -398,107 +109,3 @@ " }}} -" Autoload Functions {{{ - -function! gutentags#rescan(...) - if exists('b:gutentags_file') - unlet b:gutentags_file - endif - if a:0 && a:1 - let l:trace_backup = g:gutentags_trace - let l:gutentags_trace = 1 - endif - call s:setup_gutentags() - if a:0 && a:1 - let g:gutentags_trace = l:trace_backup - endif -endfunction - -function! gutentags#trace(...) - let g:gutentags_trace = !g:gutentags_trace - if a:0 > 0 - let g:gutentags_trace = a:1 - endif - if g:gutentags_trace - echom "gutentags: Tracing is enabled." - else - echom "gutentags: Tracing is disabled." - endif - echom "" -endfunction - -function! gutentags#fake(...) - let g:gutentags_fake = !g:gutentags_fake - if a:0 > 0 - let g:gutentags_fake = a:1 - endif - if g:gutentags_fake - echom "gutentags: Now faking gutentags." - else - echom "gutentags: Now running gutentags for real." - endif - echom "" -endfunction - -function! gutentags#inprogress() - echom "gutentags: generations in progress:" - for mip in keys(s:maybe_in_progress) - echom mip - endfor - echom "" -endfunction - -" }}} - -" Statusline Functions {{{ - -" Prints whether a tag file is being generated right now for the current -" buffer in the status line. -" -" Arguments can be passed: -" - args 1 and 2 are the prefix and suffix, respectively, of whatever output, -" if any, is going to be produced. -" (defaults to empty strings) -" - arg 3 is the text to be shown if tags are currently being generated. -" (defaults to 'TAGS') - -function! gutentags#statusline(...) abort - if !exists('b:gutentags_file') - " This buffer doesn't have gutentags. - return '' - endif - - " Figure out what the user is customizing. - let l:gen_msg = 'TAGS' - if a:0 > 0 - let l:gen_msg = a:1 - endif - - " To make this function as fast as possible, we first check whether the - " current buffer's tags file is 'maybe' being generated. This provides a - " nice and quick bail out for 99.9% of cases before we need to this the - " file-system to check the lock file. - let l:abs_tag_file = fnamemodify(b:gutentags_file, ':p') - let l:timestamp = get(s:maybe_in_progress, l:abs_tag_file) - if l:timestamp == 0 - return '' - endif - " It's maybe generating! Check if the lock file is still there... but - " don't do it too soon after the script was originally launched, because - " there can be a race condition where we get here just before the script - " had a chance to write the lock file. - if (localtime() - l:timestamp) > 1 && - \!filereadable(l:abs_tag_file . '.lock') - call remove(s:maybe_in_progress, l:abs_tag_file) - return '' - endif - " It's still there! So probably `ctags` is still running... - " (although there's a chance it crashed, or the script had a problem, and - " the lock file has been left behind... we could try and run some - " additional checks here to see if it's legitimately running, and - " otherwise delete the lock file... maybe in the future...) - return l:gen_msg -endfunction - -" }}} -