Mercurial > hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
43100:90b9a7e06c2c | 43101:1d12ae5096d1 |
---|---|
473 | 473 |
474 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f) | 474 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f) |
475 | 475 |
476 | 476 |
477 class templateformatter(baseformatter): | 477 class templateformatter(baseformatter): |
478 def __init__(self, ui, out, topic, opts, spec): | 478 def __init__(self, ui, out, topic, opts, spec, overridetemplates=None): |
479 baseformatter.__init__(self, ui, topic, opts, _templateconverter) | 479 baseformatter.__init__(self, ui, topic, opts, _templateconverter) |
480 self._out = out | 480 self._out = out |
481 self._tref = spec.ref | 481 self._tref = spec.ref |
482 self._t = loadtemplater( | 482 self._t = loadtemplater( |
483 ui, | 483 ui, |
484 spec, | 484 spec, |
485 defaults=templatekw.keywords, | 485 defaults=templatekw.keywords, |
486 resources=templateresources(ui), | 486 resources=templateresources(ui), |
487 cache=templatekw.defaulttempl, | 487 cache=templatekw.defaulttempl, |
488 ) | 488 ) |
489 if overridetemplates: | |
490 self._t.cache.update(overridetemplates) | |
489 self._parts = templatepartsmap( | 491 self._parts = templatepartsmap( |
490 spec, self._t, [b'docheader', b'docfooter', b'separator'] | 492 spec, self._t, [b'docheader', b'docfooter', b'separator'] |
491 ) | 493 ) |
492 self._counter = itertools.count() | 494 self._counter = itertools.count() |
493 self._renderitem(b'docheader', {}) | 495 self._renderitem(b'docheader', {}) |
521 @attr.s(frozen=True) | 523 @attr.s(frozen=True) |
522 class templatespec(object): | 524 class templatespec(object): |
523 ref = attr.ib() | 525 ref = attr.ib() |
524 tmpl = attr.ib() | 526 tmpl = attr.ib() |
525 mapfile = attr.ib() | 527 mapfile = attr.ib() |
528 refargs = attr.ib(default=None) | |
526 | 529 |
527 | 530 |
528 def lookuptemplate(ui, topic, tmpl): | 531 def lookuptemplate(ui, topic, tmpl): |
529 """Find the template matching the given -T/--template spec 'tmpl' | 532 """Find the template matching the given -T/--template spec 'tmpl' |
530 | 533 |
553 return templatespec(b'', tmpl, None) | 556 return templatespec(b'', tmpl, None) |
554 | 557 |
555 # a reference to built-in (formatter) template | 558 # a reference to built-in (formatter) template |
556 if tmpl in {b'cbor', b'json', b'pickle', b'debug'}: | 559 if tmpl in {b'cbor', b'json', b'pickle', b'debug'}: |
557 return templatespec(tmpl, None, None) | 560 return templatespec(tmpl, None, None) |
561 | |
562 # a function-style reference to built-in template | |
563 func, fsep, ftail = tmpl.partition(b'(') | |
564 if func in {b'cbor', b'json'} and fsep and ftail.endswith(b')'): | |
565 templater.parseexpr(tmpl) # make sure syntax errors are confined | |
566 return templatespec(func, None, None, refargs=ftail[:-1]) | |
558 | 567 |
559 # perhaps a stock style? | 568 # perhaps a stock style? |
560 if not os.path.split(tmpl)[0]: | 569 if not os.path.split(tmpl)[0]: |
561 mapname = templater.templatepath( | 570 mapname = templater.templatepath( |
562 b'map-cmdline.' + tmpl | 571 b'map-cmdline.' + tmpl |
717 b'ctx': _loadctx, | 726 b'ctx': _loadctx, |
718 b'fctx': _loadfctx, | 727 b'fctx': _loadfctx, |
719 } | 728 } |
720 | 729 |
721 | 730 |
731 def _internaltemplateformatter( | |
732 ui, | |
733 out, | |
734 topic, | |
735 opts, | |
736 spec, | |
737 tmpl, | |
738 docheader=b'', | |
739 docfooter=b'', | |
740 separator=b'', | |
741 ): | |
742 """Build template formatter that handles customizable built-in templates | |
743 such as -Tjson(...)""" | |
744 templates = {spec.ref: tmpl} | |
745 if docheader: | |
746 templates[b'%s:docheader' % spec.ref] = docheader | |
747 if docfooter: | |
748 templates[b'%s:docfooter' % spec.ref] = docfooter | |
749 if separator: | |
750 templates[b'%s:separator' % spec.ref] = separator | |
751 return templateformatter( | |
752 ui, out, topic, opts, spec, overridetemplates=templates | |
753 ) | |
754 | |
755 | |
722 def formatter(ui, out, topic, opts): | 756 def formatter(ui, out, topic, opts): |
723 spec = lookuptemplate(ui, topic, opts.get(b'template', b'')) | 757 spec = lookuptemplate(ui, topic, opts.get(b'template', b'')) |
724 if spec.ref == b"cbor": | 758 if spec.ref == b"cbor" and spec.refargs is not None: |
759 return _internaltemplateformatter( | |
760 ui, | |
761 out, | |
762 topic, | |
763 opts, | |
764 spec, | |
765 tmpl=b'{dict(%s)|cbor}' % spec.refargs, | |
766 docheader=cborutil.BEGIN_INDEFINITE_ARRAY, | |
767 docfooter=cborutil.BREAK, | |
768 ) | |
769 elif spec.ref == b"cbor": | |
725 return cborformatter(ui, out, topic, opts) | 770 return cborformatter(ui, out, topic, opts) |
771 elif spec.ref == b"json" and spec.refargs is not None: | |
772 return _internaltemplateformatter( | |
773 ui, | |
774 out, | |
775 topic, | |
776 opts, | |
777 spec, | |
778 tmpl=b'{dict(%s)|json}' % spec.refargs, | |
779 docheader=b'[\n ', | |
780 docfooter=b'\n]\n', | |
781 separator=b',\n ', | |
782 ) | |
726 elif spec.ref == b"json": | 783 elif spec.ref == b"json": |
727 return jsonformatter(ui, out, topic, opts) | 784 return jsonformatter(ui, out, topic, opts) |
728 elif spec.ref == b"pickle": | 785 elif spec.ref == b"pickle": |
786 assert spec.refargs is None, r'function-style not supported' | |
729 return pickleformatter(ui, out, topic, opts) | 787 return pickleformatter(ui, out, topic, opts) |
730 elif spec.ref == b"debug": | 788 elif spec.ref == b"debug": |
789 assert spec.refargs is None, r'function-style not supported' | |
731 return debugformatter(ui, out, topic, opts) | 790 return debugformatter(ui, out, topic, opts) |
732 elif spec.ref or spec.tmpl or spec.mapfile: | 791 elif spec.ref or spec.tmpl or spec.mapfile: |
792 assert spec.refargs is None, r'function-style not supported' | |
733 return templateformatter(ui, out, topic, opts, spec) | 793 return templateformatter(ui, out, topic, opts, spec) |
734 # developer config: ui.formatdebug | 794 # developer config: ui.formatdebug |
735 elif ui.configbool(b'ui', b'formatdebug'): | 795 elif ui.configbool(b'ui', b'formatdebug'): |
736 return debugformatter(ui, out, topic, opts) | 796 return debugformatter(ui, out, topic, opts) |
737 # deprecated config: ui.formatjson | 797 # deprecated config: ui.formatjson |