# HG changeset patch # User Christian Ebert # Date 1168775812 -3600 # Node ID 9e4cbe64fb4fcb6faf38432c7fb9c3537b30d8d7 # Parent 9f70f953dd3bd96fa9364c1b2120bd2439b8b653 Implement custom keyword map templates in hgrc More expensive than hardcoded keywords. But, of course, more flexible. ATM templates can only be set in hgrc, and perhaps this is enough. diff -r 9f70f953dd3b -r 9e4cbe64fb4f hgkw/keyword.py --- a/hgkw/keyword.py Sat Jan 13 13:27:13 2007 +0100 +++ b/hgkw/keyword.py Sun Jan 14 12:56:52 2007 +0100 @@ -11,38 +11,55 @@ files (like LaTeX packages), that are mostly addressed to an audience not running a version control system. -Supported $keywords$ and their $keyword: substition $ are: +You can either use the default cvs-like keywords or provide your +own in hgrc. + +Default $keywords$ and their $keyword: substition $ are: Revision: changeset id - Author: short username + Author: username Date: %Y/%m/%d %H:%M:%S [UTC] RCSFile: basename,v Source: /path/to/basename,v - Id: basename,v csetid %Y/%m/%d %H:%M:%S shortname - Header: /path/to/basename,v csetid %Y/%m/%d %H:%M:%S shortname + Id: basename,v csetid %Y/%m/%d %H:%M:%S username + Header: /path/to/basename,v csetid %Y/%m/%d %H:%M:%S username Simple setup in hgrc: # enable extension - hgext.keyword = - # or, if script not in hgext folder: - # hgext.keyword = /full/path/to/script + hgext.keyword = /full/path/to/script + # or, if script in hgext folder: + # hgext.keyword = # filename patterns for expansion are configured in this section [keyword] **.py = expand ... + # in case you prefer you own keyword maps over the cvs-like defaults: + [keywordmaps] + hgdate = {date|rfc822date} + lastlog = {desc} + checked in by = {author} + ... ''' -from mercurial import context, util +from mercurial import cmdutil, templater, util import os.path, re, sys, time -re_kw = re.compile( - r'\$(Id|Header|Author|Date|Revision|RCSFile|Source)[^$]*?\$') +deftemplates = { + 'Revision': '{node|short}', + 'Author': '{author|user}', + 'Date': '{date|utcdate}', + 'RCSFile': '{file|basename},v', + 'Source': '{root}/{file},v', + 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', + 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', + } -def kwexpand(mobj, kwfctx): - '''Called by kwfilelog.read and pretxnkw. - Expands keywords according to file context.''' - return '$%s: %s $' % (mobj.group(1), kwfctx.expand(mobj.group(1))) +def utcdate(date): + '''Returns hgdate in cvs-like UTC format.''' + return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) + +templater.common_filters['utcdate'] = utcdate def kwfmatches(ui, repo, files): '''Selects candidates for keyword substitution @@ -60,41 +77,38 @@ break return candidates -def utcdate(date): - '''Returns hgdate in cvs-like UTC format.''' - return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) - -class kwfilectx(context.filectx): +class kwtemplater(object): ''' - Provides keyword expansion functions based on file context. + Sets up keyword templates, corresponding keyword regex, and + provides keyword expansion function. ''' - def __init__(self, repo, path, changeid=None, fileid=None, filelog=None): - context.filectx.__init__(self, repo, path, changeid, fileid, filelog) - def Revision(self): - return str(self.changectx()) - def Author(self): - return util.shortuser(self.user()) - def Date(self): - return utcdate(self.date()) - def RCSFile(self): - return os.path.basename(self._path)+',v' - def Source(self): - return self._repo.wjoin(self._path)+',v' - def Header(self): - return ' '.join( - [self.Source(), self.Revision(), self.Date(), self.Author()]) - def Id(self): - return ' '.join( - [self.RCSFile(), self.Revision(), self.Date(), self.Author()]) - def expand(self, kw): - '''Called from kwexpand, evaluates keyword.''' - return eval('self.%s()' % kw) + def __init__(self, ui, repo, node=None): + self.ui = ui + self.repo = repo + self.node = node + templates = {} + for k, v in self.ui.configitems('keywordmaps'): + templates[k] = v + self.templates = templates or deftemplates + self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % + '|'.join(re.escape(k) for k in self.templates.keys())) + self.t = cmdutil.changeset_templater(self.ui, self.repo, + False, '', False) + + def expand(self, mobj, path): + kw = mobj.group(1) + template = templater.parsestring(self.templates[kw], quoted=False) + self.t.use_template(template) + self.ui.pushbuffer() + self.t.show(changenode=self.node, + changes=self.repo.changelog.read(self.node), + root=self.repo.root, file=path) + return '$%s: %s $' % (kw, self.ui.popbuffer()) def reposetup(ui, repo): - from mercurial import filelog, revlog - + from mercurial import context, filelog, revlog if not repo.local(): return @@ -114,6 +128,7 @@ super(kwfilelog, self).__init__(opener, path, defversion) self._repo = repo self._path = path + self.kwt = kwtemplater(ui, self._repo) def iskwcandidate(self, data): '''Decides whether to act on keywords.''' @@ -124,22 +139,24 @@ '''Substitutes keywords when reading filelog.''' data = super(kwfilelog, self).read(node) if self.iskwcandidate(data): - kwfctx = kwfilectx(self._repo, self._path, - fileid=node, filelog=self) - return re_kw.sub(lambda m: kwexpand(m, kwfctx), data) + c = context.filectx(self._repo, self._path, + fileid=node, filelog=self) + self.kwt.node = c.node() + return self.kwt.re_kw.sub(lambda m: + self.kwt.expand(m, self._path), data) return data def add(self, text, meta, tr, link, p1=None, p2=None): '''Removes keyword substitutions when adding to filelog.''' if self.iskwcandidate(text): - text = re_kw.sub(r'$\1$', text) + text = self.kwt.re_kw.sub(r'$\1$', text) return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) def cmp(self, node, text): '''Removes keyword substitutions for comparison.''' if self.iskwcandidate(text): - text = re_kw.sub(r'$\1$', text) + text = self.kwt.re_kw.sub(r'$\1$', text) return super(kwfilelog, self).cmp(node, text) filelog.filelog = kwfilelog @@ -167,7 +184,7 @@ # above line for backwards compatibility; can be changed to # from mercurial.i18n import _ # some day - from mercurial import cmdutil, commands + from mercurial import commands if hooktype != 'pretxncommit': return True @@ -178,12 +195,15 @@ files, match, anypats = cmdutil.matchpats(repo, sysargs, cmdopts) modified, added = repo.status(files=files, match=match)[:2] + candidates = kwfmatches(ui, repo, modified+added) + if not candidates: + return False - for f in kwfmatches(ui, repo, modified+added): + kwt = kwtemplater(ui, repo, node=repo.changelog.tip()) + for f in candidates: data = repo.wfile(f).read() if not util.binary(data): - kwfctx = kwfilectx(repo, f, changeid=args['node']) - data, kwct = re_kw.subn(lambda m: kwexpand(m, kwfctx), data) + data, kwct = kwt.re_kw.subn(lambda m: kwt.expand(m, f), data) if kwct: ui.debug(_('overwriting %s expanding keywords\n' % f)) repo.wfile(f, 'w').write(data)