Mercurial > vim-gutentags
view plugin/autotags.vim @ 1:b137f6c2a7e7
Don't include tag files, duh.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 20 Jul 2014 14:20:19 -0700 |
parents | a3a37124558b |
children | 60adce96ac2d |
line wrap: on
line source
" autotags.vim - Automatic ctags management for Vim " Maintainer: Ludovic Chabant <http://ludovic.chabant.com> " Version: 0.0.1 " Globals {{{ if !exists('g:autotags_debug') let g:autotags_debug = 0 endif if (exists('g:loaded_autotags') || &cp) && !g:autotags_debug finish endif if (exists('g:loaded_autotags') && g:autotags_debug) echom "Reloaded autotags." endif let g:loaded_autotags = 1 if !exists('g:autotags_trace') let g:autotags_trace = 1 endif if !exists('g:autotags_fake') let g:autotags_fake = 0 endif if !exists('g:autotags_background_update') let g:autotags_background_update = 1 endif if !exists('g:autotags_enabled') let g:autotags_enabled = 1 endif if !exists('g:autotags_executable') let g:autotags_executable = 'ctags' endif if !exists('g:autotags_tagfile') let g:autotags_tagfile = 'tags' endif if !exists('g:autotags_project_root') let g:autotags_project_root = [] endif let g:autotags_project_root += ['.git', '.hg', '.bzr', '_darcs'] " }}} " Utilities {{{ " Throw an exception message. function! s:throw(message) let v:errmsg = "autotags: " . a:message throw v:errmsg endfunction " Prints a message if debug tracing is enabled. function! s:trace(message, ...) if g:autotags_trace || (a:0 && a:1) let l:message = "autotags: " . 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 " }}} " Autotags Setup {{{ " Finds the tag file path for the given current directory " (typically the directory of the file being edited) function! s:get_tagfile_for(path) abort let l:path = s:stripslash(a:path) let l:previous_path = "" while l:path != l:previous_path for root in g:autotags_project_root if getftype(l:path . '/' . root) != "" return simplify(fnamemodify(l:path, ':p') . g:autotags_tagfile) 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 " Setup autotags for the current buffer. function! s:setup_autotags() abort call s:trace("Scanning buffer '" . bufname('%') . "' for autotags setup...") if exists('b:autotags_file') return endif try let b:autotags_file = s:get_tagfile_for(expand('%:h')) catch /^autotags\:/ return endtry call s:trace("Setting autotags for buffer '" . bufname('%') . "' with tagfile: " . b:autotags_file) let l:bn = bufnr('%') execute 'augroup autotags_buffer_' . l:bn execute ' autocmd!' execute ' autocmd BufWritePost <buffer=' . l:bn . '> if g:autotags_enabled|call s:update_tags(0, 1)|endif' execute 'augroup end' command! -buffer -bang AutotagsUpdate :call s:manual_update_tags(<bang>0) endfunction augroup autotags_detect autocmd! autocmd BufNewFile,BufReadPost * call s:setup_autotags() autocmd VimEnter * if expand('<amatch>')==''|call s:setup_autotags()|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 = [] " 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:autotags_background_update let l:cmd .= '/b ' endif return l:cmd 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 " 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 autotags-defined file. function! s:update_tags(write_mode, queue_mode, ...) abort " Figure out where to save. let l:tags_file = 0 if a:0 == 1 let l:tags_file = a:1 else let l:tags_file = b:autotags_file 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 "autotags: 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 .= ' --exe "' . g:autotags_executable . '"' let l:cmd .= ' --tags "' . fnamemodify(l:tags_file, ':t') . '"' if a:write_mode == 0 && filereadable(l:tags_file) " CTags specifies paths relative to the tags file with a `./` " prefix, so we need to specify the same prefix otherwise it will " think those are different files and we'll end up with duplicate " entries. let l:rel_path = s:normalizepath('./' . expand('%:.')) let l:cmd .= ' --source "' . l:rel_path . '"' endif if g:autotags_trace let l:cmd .= ' --log "' . fnamemodify(l:tags_file, ':t') . '.log"' endif call s:trace("Running: " . l:cmd) call s:trace("In: " . l:work_dir) if !g:autotags_fake if !g:autotags_trace silent execute l:cmd else execute l:cmd endif else call s:trace("(fake... not actually running)") endif call s:trace("") finally " Restore the current directory... execute "chdir " . 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 AutotagsGenerate :call s:generate_tags(<bang>0, <f-args>) " }}} " Toggles {{{ command! AutotagsToggleEnabled :let g:autotags_enabled=!g:autotags_enabled command! AutotagsToggleTrace :call autotags#trace() command! AutotagsToggleFake :call autotags#fake() command! AutotagsUnlock :call delete(b:autotags_file . '.lock') " }}} " Autoload Functions {{{ function! autotags#rescan(...) if exists('b:autotags_file') unlet b:autotags_file endif if a:0 && a:1 let l:trace_backup = g:autotags_trace let l:autotags_trace = 1 endif call s:setup_autotags() if a:0 && a:1 let g:autotags_trace = l:trace_backup endif endfunction function! autotags#trace(...) let g:autotags_trace = !g:autotags_trace if a:0 > 0 let g:autotags_trace = a:1 endif if g:autotags_trace echom "autotags: Tracing is enabled." else echom "autotags: Tracing is disabled." endif echom "" endfunction function! autotags#fake(...) let g:autotags_fake = !g:autotags_fake if a:0 > 0 let g:autotags_fake = a:1 endif if g:autotags_fake echom "autotags: Now faking autotags." else echom "autotags: Now running autotags for real." endif echom "" endfunction " }}}