Skip to content

Instantly share code, notes, and snippets.

@jeffdeville
Created February 3, 2014 14:48
Show Gist options
  • Select an option

  • Save jeffdeville/8785116 to your computer and use it in GitHub Desktop.

Select an option

Save jeffdeville/8785116 to your computer and use it in GitHub Desktop.
Keep your tags up to date automatically. Tweaked from https://github.com/jjasonclark/dotvim/blob/master/bundle/autotag/autotag.vim to use ripper-tags
you need ripper-tags for this to work.
`gem install ripper-tags`
then just drop the .vim script in
" Increment the number below for a dynamic #include guard
let s:autotag_vim_version=1
if exists("g:autotag_vim_version_sourced")
if s:autotag_vim_version == g:autotag_vim_version_sourced
finish
endif
endif
let g:autotag_vim_version_sourced=s:autotag_vim_version
" This file supplies automatic tag regeneration when saving files
" There's a problem with ctags when run with -a (append)
" ctags doesn't remove entries for the supplied source file that no longer exist
" so this script (implemented in python) finds a tags file for the file vim has
" just saved, removes all entries for that source file and *then* runs ctags -a
if has("python")
python << EEOOFF
import os
import string
import os.path
import fileinput
import sys
import vim
import time
import logging
from collections import defaultdict
# global vim config variables used (all are g:autotag<name>):
# name purpose
# maxTagsFileSize a cap on what size tag file to strip etc
# ExcludeSuffixes suffixes to not ctags on
# VerbosityLevel logging verbosity (as in Python logging module)
# CtagsCmd name of ctags command
# TagsFile name of tags file to look for
# Disabled Disable autotag (enable by setting to any non-blank value)
# StopAt stop looking for a tags file (and make one) at this directory (defaults to $HOME)
vim_global_defaults = dict(maxTagsFileSize = 1024*1024*7,
ExcludeSuffixes = "tml.xml.text.txt",
VerbosityLevel = logging.WARNING,
CtagsCmd = "ripper-tags",
TagsFile = "tags",
Disabled = 0,
StopAt = 0)
# Just in case the ViM build you're using doesn't have subprocess
if sys.version < '2.4':
def do_cmd(cmd, cwd):
old_cwd=os.getcwd()
os.chdir(cwd)
(ch_in, ch_out) = os.popen2(cmd)
for line in ch_out:
pass
os.chdir(old_cwd)
import traceback
def format_exc():
return ''.join(traceback.format_exception(*list(sys.exc_info())))
else:
import subprocess
def do_cmd(cmd, cwd):
p = subprocess.Popen(cmd, shell=True, stdout=None, stderr=None, cwd=cwd)
from traceback import format_exc
def vim_global(name, kind = string):
ret = vim_global_defaults.get(name, None)
try:
v = "g:autotag%s" % name
exists = (vim.eval("exists('%s')" % v) == "1")
if exists:
ret = vim.eval(v)
else:
if isinstance(ret, int):
vim.command("let %s=%s" % (v, ret))
else:
vim.command("let %s=\"%s\"" % (v, ret))
finally:
if kind == bool:
ret = (ret not in [0, "0"])
elif kind == int:
ret = int(ret)
elif kind == string:
pass
return ret
class VimAppendHandler(logging.Handler):
def __init__(self, name):
logging.Handler.__init__(self)
self.__name = name
self.__formatter = logging.Formatter()
def __findBuffer(self):
for b in vim.buffers:
if b and b.name and b.name.endswith(self.__name):
return b
def emit(self, record):
b = self.__findBuffer()
if b:
b.append(self.__formatter.format(record))
def makeAndAddHandler(logger, name):
ret = VimAppendHandler(name)
logger.addHandler(ret)
return ret
class AutoTag:
MAXTAGSFILESIZE = long(vim_global("maxTagsFileSize"))
DEBUG_NAME = "autotag_debug"
LOGGER = logging.getLogger(DEBUG_NAME)
HANDLER = makeAndAddHandler(LOGGER, DEBUG_NAME)
@staticmethod
def setVerbosity():
try:
level = int(vim_global("VerbosityLevel"))
except:
level = vim_global_defaults["VerbosityLevel"]
AutoTag.LOGGER.setLevel(level)
def __init__(self):
self.tags = defaultdict(list)
self.excludesuffix = [ "." + s for s in vim_global("ExcludeSuffixes").split(".") ]
AutoTag.setVerbosity()
self.sep_used_by_ctags = '/'
self.ctags_cmd = vim_global("CtagsCmd")
self.tags_file = str(vim_global("TagsFile"))
self.count = 0
self.stop_at = vim_global("StopAt")
def findTagFile(self, source):
AutoTag.LOGGER.info('source = "%s"', source)
( drive, file ) = os.path.splitdrive(source)
ret = None
while file:
file = os.path.dirname(file)
AutoTag.LOGGER.info('drive = "%s", file = "%s"', drive, file)
tagsDir = os.path.join(drive, file)
tagsFile = os.path.join(tagsDir, self.tags_file)
AutoTag.LOGGER.info('tagsFile "%s"', tagsFile)
if os.path.isfile(tagsFile):
st = os.stat(tagsFile)
if st:
size = getattr(st, 'st_size', None)
if size is None:
AutoTag.LOGGER.warn("Could not stat tags file %s", tagsFile)
break
if size > AutoTag.MAXTAGSFILESIZE:
AutoTag.LOGGER.info("Ignoring too big tags file %s", tagsFile)
break
ret = (file, tagsFile)
break
elif tagsDir and tagsDir == self.stop_at:
AutoTag.LOGGER.info("Reached %s. Making one %s" % (self.stop_at, tagsFile))
open(tagsFile, 'wb').close()
ret = (file, tagsFile)
break
elif not file or file == os.sep or file == "//" or file == "\\\\":
AutoTag.LOGGER.info('bail (file = "%s")' % (file, ))
break
return ret
def addSource(self, source):
if not source:
AutoTag.LOGGER.warn('No source')
return
if os.path.basename(source) == self.tags_file:
AutoTag.LOGGER.info("Ignoring tags file %s", self.tags_file)
return
(base, suff) = os.path.splitext(source)
if suff in self.excludesuffix:
AutoTag.LOGGER.info("Ignoring excluded suffix %s for file %s", source, suff)
return
found = self.findTagFile(source)
if found:
tagsDir, tagsFile = found
relativeSource = source[len(tagsDir):]
if relativeSource[0] == os.sep:
relativeSource = relativeSource[1:]
if os.sep != self.sep_used_by_ctags:
relativeSource = string.replace(relativeSource, os.sep, self.sep_used_by_ctags)
self.tags[(tagsDir, tagsFile)].append(relativeSource)
def goodTag(self, line, excluded):
if line[0] == '!':
return True
else:
f = string.split(line, '\t')
AutoTag.LOGGER.log(1, "read tags line:%s", str(f))
if len(f) > 3 and f[1] not in excluded:
return True
return False
def stripTags(self, tagsFile, sources):
AutoTag.LOGGER.info("Stripping tags for %s from tags file %s", ",".join(sources), tagsFile)
backup = ".SAFE"
input = fileinput.FileInput(files=tagsFile, inplace=True, backup=backup)
try:
for l in input:
l = l.strip()
if self.goodTag(l, sources):
print l
finally:
input.close()
try:
os.unlink(tagsFile + backup)
except StandardError:
pass
def updateTagsFile(self, tagsDir, tagsFile, sources):
self.stripTags(tagsFile, sources)
if self.tags_file:
cmd = "%s -f %s -a " % (self.ctags_cmd, self.tags_file)
else:
cmd = "%s -a " % (self.ctags_cmd,)
for source in sources:
if os.path.isfile(os.path.join(tagsDir, source)):
cmd += " '%s'" % source
AutoTag.LOGGER.log(1, "%s: %s", tagsDir, cmd)
do_cmd(cmd, tagsDir)
def rebuildTagFiles(self):
for ((tagsDir, tagsFile), sources) in self.tags.items():
self.updateTagsFile(tagsDir, tagsFile, sources)
EEOOFF
function! AutoTag()
python << EEOOFF
try:
if not vim_global("Disabled", bool):
at = AutoTag()
at.addSource(vim.eval("expand(\"%:p\")"))
at.rebuildTagFiles()
except:
logging.warning(format_exc())
EEOOFF
if exists(":TlistUpdate")
TlistUpdate
endif
endfunction
function! AutoTagDebug()
new
file autotag_debug
setlocal buftype=nowrite
setlocal bufhidden=delete
setlocal noswapfile
normal 
endfunction
augroup autotag
au!
autocmd BufWritePost,FileWritePost * call AutoTag ()
augroup END
endif " has("python")
" vim:shiftwidth=3:ts=3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment