Mercurial > vim-gutentags
changeset 0:a3a37124558b
Initial commit.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Thu, 17 Jul 2014 17:26:48 -0700 |
parents | |
children | b137f6c2a7e7 |
files | doc/autotags.txt plat/unix/update_tags.sh plat/win32/update_tags.cmd plugin/autotags.vim plugin/tags |
diffstat | 4 files changed, 440 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/autotags.txt Thu Jul 17 17:26:48 2014 -0700 @@ -0,0 +1,4 @@ + +TODO + + vim:tw=78:et:ft=help:norl:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plat/win32/update_tags.cmd Thu Jul 17 17:26:48 2014 -0700 @@ -0,0 +1,98 @@ +@echo off +setlocal EnableExtensions EnableDelayedExpansion + +rem ========================================== +rem PARSE ARGUMENTS +rem ========================================== + +set CTAGS_EXE=ctags +set TAGS_FILE=tags +set UPDATED_SOURCE= +set PAUSE_BEFORE_EXIT=0 +set LOG_FILE= + +:ParseArgs +if [%1]==[] goto :DoneParseArgs +if [%1]==[--exe] ( + set CTAGS_EXE=%~2 + shift + goto :LoopParseArgs +) +if [%1]==[--tags] ( + set TAGS_FILE=%~2 + shift + goto :LoopParseArgs +) +if [%1]==[--source] ( + set UPDATED_SOURCE=%~2 + shift + goto :LoopParseArgs +) +if [%1]==[--pause] ( + set PAUSE_BEFORE_EXIT=1 + goto :LoopParseArgs +) +if [%1]==[--log] ( + set LOG_FILE=%~2 + shift + goto :LoopParseArgs +) +echo Invalid Argument: %1 +goto :Usage + +:LoopParseArgs +shift +goto :ParseArgs + +:DoneParseArgs + + +rem ========================================== +rem GENERATE TAGS +rem ========================================== + +set CTAGS_ARGS= +if [%LOG_FILE%]==[] set LOG_FILE=CON + +echo Locking tags file... > %LOG_FILE% +echo locked > "%TAGS_FILE%.lock" + +if exist "%TAGS_FILE%" ( + if not [%UPDATED_SOURCE%]==[] ( + echo Removing references to: %UPDATED_SOURCE% >> %LOG_FILE% + echo type "%TAGS_FILE%" ^| findstr /V /C:"%UPDATED_SOURCE%" ^> "%TAGS_FILE%.filter" >> %LOG_FILE% + findstr /V /C:"%UPDATED_SOURCE%" "%TAGS_FILE%" > "%TAGS_FILE%.filter" + echo move /Y "%TAGS_FILE%.filter" "%TAGS_FILE%" >> %LOG_FILE% + move /Y "%TAGS_FILE%.filter" "%TAGS_FILE%" + set CTAGS_ARGS=--append %UPDATED_SOURCE% + ) +) + +echo Running ctags >> %LOG_FILE% +echo "%CTAGS_EXE%" -R -f "%TAGS_FILE%" %CTAGS_ARGS% >> %LOG_FILE% +"%CTAGS_EXE%" -R -f "%TAGS_FILE%" %CTAGS_ARGS% + +echo Unlocking tags file... >> %LOG_FILE% +del /F "%TAGS_FILE%.lock" + +echo Done. >> %LOG_FILE% +if [%PAUSE_BEFORE_EXIT%]==[1] ( + pause +) + +goto :EOF + + +rem ========================================== +rem USAGE +rem ========================================== + +:Usage +echo Usage: +echo %~n0 ^<options^> +echo. +echo --exe [exe=ctags]: The ctags executable to run +echo --tags [file=tags]: The path to the ctags file to update +echo --source [file=]: The path to the source file that needs updating +echo. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/autotags.vim Thu Jul 17 17:26:48 2014 -0700 @@ -0,0 +1,312 @@ +" 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 + +" }}} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/tags Thu Jul 17 17:26:48 2014 -0700 @@ -0,0 +1,26 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.8 // +autotags#rescan .\autotags.vim /^function! autotags#rescan(...)$/;" f +autotags_detect .\autotags.vim /^augroup autotags_detect$/;" a +autotags_main .\autotags.vim /^augroup autotags_main$/;" a +do_setup_autotags .\autotags.vim /^function! s:do_setup_autotags() abort$/;" f +g:autotags_debug .\autotags.vim /^ let g:autotags_debug = 0$/;" v +g:autotags_enabled .\autotags.vim /^ let g:autotags_enabled = 1$/;" v +g:autotags_executable .\autotags.vim /^ let g:autotags_executable = 'ctags'$/;" v +g:autotags_project_root .\autotags.vim /^ let g:autotags_project_root = ['.git', '.hg', '.bzr', '_darcs']$/;" v +g:autotags_tagfile .\autotags.vim /^ let g:autotags_tagfile = 'tags'$/;" v +g:autotags_trace .\autotags.vim /^ let g:autotags_trace = 1$/;" v +g:loaded_autotags .\autotags.vim /^let g:loaded_autotags = 1$/;" v +generate_tags .\autotags.vim /^function! s:generate_tags() abort$/;" f +get_tagfile_for .\autotags.vim /^function! s:get_tagfile_for(path) abort$/;" f +s:execute_cmd .\autotags.vim /^ let s:execute_cmd = '!start \/b '$/;" v +s:execute_cmd .\autotags.vim /^let s:execute_cmd = '!'$/;" v +setup_autotags .\autotags.vim /^function! s:setup_autotags() abort$/;" f +stripslash .\autotags.vim /^function! s:stripslash(path)$/;" f +throw .\autotags.vim /^function! s:throw(message)$/;" f +trace .\autotags.vim /^function! s:trace(message, ...)$/;" f +update_tags .\autotags.vim /^function! s:update_tags() abort$/;" f