formatter: add support for changeset templating
authorYuya Nishihara <yuya@tcha.org>
Sat, 25 Feb 2017 17:00:07 +0900
changeset 31172 16272d8c24f6
parent 31171 1ec89cf0ea49
child 31173 052e4f1ffce9
formatter: add support for changeset templating Some formatter-based commands provide fields that are identical to the ones defined in templatekw, but we had to specify them manually to support all changeset-based template keywords. This patch adds fm.context() that populates all templatekw. These keywords are available only in template output, so we still need to set important keywords via fm.data() if they should be available in e.g. JSON output. Currently fm.context() takes only 'ctx' argument. It will eventually be extended to take 'fctx' to support file-based keywords (e.g. {path}) seen in hgweb.
mercurial/formatter.py
--- a/mercurial/formatter.py	Sat Feb 25 16:38:26 2017 +0900
+++ b/mercurial/formatter.py	Sat Feb 25 17:00:07 2017 +0900
@@ -12,6 +12,7 @@
 
 - fm.write() for unconditional output
 - fm.condwrite() to show some extra data conditionally in plain output
+- fm.context() to provide changectx to template output
 - fm.data() to provide extra data to JSON or template output
 - fm.plain() to show raw text that isn't provided to JSON or template output
 
@@ -171,6 +172,9 @@
         # name is mandatory argument for now, but it could be optional if
         # we have default template keyword, e.g. {item}
         return self._converter.formatlist(data, name, fmt, sep)
+    def context(self, **ctxs):
+        '''insert context objects to be used to render template keywords'''
+        pass
     def data(self, **data):
         '''insert data into item that's not shown in default output'''
         self._item.update(data)
@@ -345,9 +349,28 @@
     def __init__(self, ui, topic, opts):
         baseformatter.__init__(self, ui, topic, opts, _templateconverter)
         self._topic = topic
-        self._t = gettemplater(ui, topic, opts.get('template', ''))
+        self._t = gettemplater(ui, topic, opts.get('template', ''),
+                               cache=templatekw.defaulttempl)
+        self._cache = {}  # for templatekw/funcs to store reusable data
+    def context(self, **ctxs):
+        '''insert context objects to be used to render template keywords'''
+        assert all(k == 'ctx' for k in ctxs)
+        self._item.update(ctxs)
     def _showitem(self):
-        g = self._t(self._topic, ui=self._ui, **self._item)
+        # TODO: add support for filectx. probably each template keyword or
+        # function will have to declare dependent resources. e.g.
+        # @templatekeyword(..., requires=('ctx',))
+        if 'ctx' in self._item:
+            props = templatekw.keywords.copy()
+            # explicitly-defined fields precede templatekw
+            props.update(self._item)
+            # but template resources must be always available
+            props['templ'] = self._t
+            props['repo'] = props['ctx'].repo()
+            props['revcache'] = {}
+        else:
+            props = self._item
+        g = self._t(self._topic, ui=self._ui, cache=self._cache, **props)
         self._ui.write(templater.stringify(g))
 
 def lookuptemplate(ui, topic, tmpl):
@@ -382,12 +405,12 @@
     # constant string?
     return tmpl, None
 
-def gettemplater(ui, topic, spec):
+def gettemplater(ui, topic, spec, cache=None):
     tmpl, mapfile = lookuptemplate(ui, topic, spec)
     assert not (tmpl and mapfile)
     if mapfile:
-        return templater.templater.frommapfile(mapfile)
-    return maketemplater(ui, topic, tmpl)
+        return templater.templater.frommapfile(mapfile, cache=cache)
+    return maketemplater(ui, topic, tmpl, cache=cache)
 
 def maketemplater(ui, topic, tmpl, cache=None):
     """Create a templater from a string template 'tmpl'"""