Mercurial > hg-stable
changeset 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 | 829088e87032 |
files | mercurial/formatter.py tests/test-template-map.t |
diffstat | 2 files changed, 103 insertions(+), 2 deletions(-) [+] |
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'):
--- a/tests/test-template-map.t Sat Oct 05 23:20:35 2019 -0400 +++ b/tests/test-template-map.t Sat Oct 05 23:30:09 2019 -0400 @@ -736,6 +736,18 @@ } ] + $ hg log -r . -T'cbor(rev, node|short)' | "$PYTHON" "$TESTTMP/decodecborarray.py" + [ + { + 'node': '95c24699272e', + 'rev': 8 + } + ] + + $ hg log -r . -T'cbor()' | "$PYTHON" "$TESTTMP/decodecborarray.py" + [ + {} + ] Test JSON style: @@ -1101,6 +1113,17 @@ } ] + $ hg log -l2 -T'json(rev, parents)' + [ + {"parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"], "rev": 8}, + {"parents": ["0000000000000000000000000000000000000000"], "rev": 7} + ] + + $ hg log -r. -T'json()' + [ + {} + ] + Other unsupported formatter styles: $ hg log -qr . -Tpickle @@ -1110,6 +1133,24 @@ abort: "debug" not in template map [255] +Unparsable function-style references: + + $ hg log -qr . -T'json(-)' + hg: parse error at 6: not a prefix: ) + (json(-) + ^ here) + [255] + +For backward compatibility, the following examples are not parsed as +function-style references: + + $ hg log -qr . -T'cbor(rev' + cbor(rev (no-eol) + $ hg log -qr . -T'json (rev)' + json (rev) (no-eol) + $ hg log -qr . -T'json(x="{rev}")' + json(x="8") (no-eol) + Error if style not readable: #if unix-permissions no-root