hgkw/keyword.py
branchkwmap-templates
changeset 95 9e4cbe64fb4f
parent 93 9f70f953dd3b
child 97 9353e7ce6d9b
--- 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)