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