diff mercurial/formatter.py @ 43101:1d12ae5096d1

formatter: map -Tjson(...) and -Tcbor(...) to templater Even though custom JSON output could be generated by using --config template.json="{dict(...)|json}" ..., doing that is tedious because of the trailing comma handling. This patch introduces special syntax for JSON/CBOR formats. -Tjson(...) is translated to template as if function-style template definition were supported: [templates] json(...) = "{dict(...)|json}" json(...):docheader = "[\n " json(...):docfooter = "\n]\n" json(...):separator = ",\n "
author Yuya Nishihara <yuya@tcha.org>
date Sat, 05 Oct 2019 23:30:09 -0400
parents 90b9a7e06c2c
children d783f945a701
line wrap: on
line diff
--- a/mercurial/formatter.py	Sat Oct 05 23:20:35 2019 -0400
+++ b/mercurial/formatter.py	Sat Oct 05 23:30:09 2019 -0400
@@ -475,7 +475,7 @@
 
 
 class templateformatter(baseformatter):
-    def __init__(self, ui, out, topic, opts, spec):
+    def __init__(self, ui, out, topic, opts, spec, overridetemplates=None):
         baseformatter.__init__(self, ui, topic, opts, _templateconverter)
         self._out = out
         self._tref = spec.ref
@@ -486,6 +486,8 @@
             resources=templateresources(ui),
             cache=templatekw.defaulttempl,
         )
+        if overridetemplates:
+            self._t.cache.update(overridetemplates)
         self._parts = templatepartsmap(
             spec, self._t, [b'docheader', b'docfooter', b'separator']
         )
@@ -523,6 +525,7 @@
     ref = attr.ib()
     tmpl = attr.ib()
     mapfile = attr.ib()
+    refargs = attr.ib(default=None)
 
 
 def lookuptemplate(ui, topic, tmpl):
@@ -556,6 +559,12 @@
     if tmpl in {b'cbor', b'json', b'pickle', b'debug'}:
         return templatespec(tmpl, None, None)
 
+    # a function-style reference to built-in template
+    func, fsep, ftail = tmpl.partition(b'(')
+    if func in {b'cbor', b'json'} and fsep and ftail.endswith(b')'):
+        templater.parseexpr(tmpl)  # make sure syntax errors are confined
+        return templatespec(func, None, None, refargs=ftail[:-1])
+
     # perhaps a stock style?
     if not os.path.split(tmpl)[0]:
         mapname = templater.templatepath(
@@ -719,17 +728,68 @@
     }
 
 
+def _internaltemplateformatter(
+    ui,
+    out,
+    topic,
+    opts,
+    spec,
+    tmpl,
+    docheader=b'',
+    docfooter=b'',
+    separator=b'',
+):
+    """Build template formatter that handles customizable built-in templates
+    such as -Tjson(...)"""
+    templates = {spec.ref: tmpl}
+    if docheader:
+        templates[b'%s:docheader' % spec.ref] = docheader
+    if docfooter:
+        templates[b'%s:docfooter' % spec.ref] = docfooter
+    if separator:
+        templates[b'%s:separator' % spec.ref] = separator
+    return templateformatter(
+        ui, out, topic, opts, spec, overridetemplates=templates
+    )
+
+
 def formatter(ui, out, topic, opts):
     spec = lookuptemplate(ui, topic, opts.get(b'template', b''))
-    if spec.ref == b"cbor":
+    if spec.ref == b"cbor" and spec.refargs is not None:
+        return _internaltemplateformatter(
+            ui,
+            out,
+            topic,
+            opts,
+            spec,
+            tmpl=b'{dict(%s)|cbor}' % spec.refargs,
+            docheader=cborutil.BEGIN_INDEFINITE_ARRAY,
+            docfooter=cborutil.BREAK,
+        )
+    elif spec.ref == b"cbor":
         return cborformatter(ui, out, topic, opts)
+    elif spec.ref == b"json" and spec.refargs is not None:
+        return _internaltemplateformatter(
+            ui,
+            out,
+            topic,
+            opts,
+            spec,
+            tmpl=b'{dict(%s)|json}' % spec.refargs,
+            docheader=b'[\n ',
+            docfooter=b'\n]\n',
+            separator=b',\n ',
+        )
     elif spec.ref == b"json":
         return jsonformatter(ui, out, topic, opts)
     elif spec.ref == b"pickle":
+        assert spec.refargs is None, r'function-style not supported'
         return pickleformatter(ui, out, topic, opts)
     elif spec.ref == b"debug":
+        assert spec.refargs is None, r'function-style not supported'
         return debugformatter(ui, out, topic, opts)
     elif spec.ref or spec.tmpl or spec.mapfile:
+        assert spec.refargs is None, r'function-style not supported'
         return templateformatter(ui, out, topic, opts, spec)
     # developer config: ui.formatdebug
     elif ui.configbool(b'ui', b'formatdebug'):