templater: wrap get/min/max result so map operation can apply to element
See the test for usage example.
wraphybridvalue() takes a key/value pair because a hybrid dict passes a key
to its makemap() function. Since makemap() of showmanifest() doesn't need
a key, it's set to None.
--- a/mercurial/templatekw.py Mon Oct 02 07:18:24 2017 +0100
+++ b/mercurial/templatekw.py Sat Sep 09 19:13:25 2017 +0900
@@ -78,13 +78,18 @@
value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
"""
- def __init__(self, gen, value, makemap):
- self.gen = gen
+ def __init__(self, gen, key, value, makemap):
+ if gen is not None:
+ self.gen = gen # generator or function returning generator
+ self._key = key
self._value = value # may be generator of strings
self._makemap = makemap
+ def gen(self):
+ yield pycompat.bytestr(self._value)
+
def tomap(self):
- return self._makemap()
+ return self._makemap(self._key)
def itermaps(self):
yield self.tomap()
@@ -114,6 +119,20 @@
return thing
return thing._value
+def wraphybridvalue(container, key, value):
+ """Wrap an element of hybrid container to be mappable
+
+ The key is passed to the makemap function of the given container, which
+ should be an item generated by iter(container).
+ """
+ makemap = getattr(container, '_makemap', None)
+ if makemap is None:
+ return value
+ if util.safehasattr(value, '_makemap'):
+ # a nested hybrid list/dict, which has its own way of map operation
+ return value
+ return _mappable(None, key, value, makemap)
+
def showdict(name, data, mapping, plural=None, key='key', value='value',
fmt='%s=%s', separator=' '):
c = [{key: k, value: v} for k, v in data.iteritems()]
@@ -578,7 +597,7 @@
f = templ('manifest', **args)
# TODO: perhaps 'ctx' should be dropped from mapping because manifest
# rev and node are completely different from changeset's.
- return _mappable(f, f, lambda: {'rev': mrev, 'node': mhex})
+ return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
def shownames(namespace, **args):
"""helper method to generate a template keyword for a namespace"""
--- a/mercurial/templater.py Mon Oct 02 07:18:24 2017 +0100
+++ b/mercurial/templater.py Sat Sep 09 19:13:25 2017 +0900
@@ -730,7 +730,10 @@
raise error.ParseError(_("get() expects a dict as first argument"))
key = evalfuncarg(context, mapping, args[1])
- return dictarg.get(key)
+ val = dictarg.get(key)
+ if val is None:
+ return
+ return templatekw.wraphybridvalue(dictarg, key, val)
@templatefunc('if(expr, then[, else])')
def if_(context, mapping, args):
@@ -874,10 +877,11 @@
iterable = evalfuncarg(context, mapping, args[0])
try:
- return max(iterable)
+ x = max(iterable)
except (TypeError, ValueError):
# i18n: "max" is a keyword
raise error.ParseError(_("max first argument should be an iterable"))
+ return templatekw.wraphybridvalue(iterable, x, x)
@templatefunc('min(iterable)')
def min_(context, mapping, args, **kwargs):
@@ -888,10 +892,11 @@
iterable = evalfuncarg(context, mapping, args[0])
try:
- return min(iterable)
+ x = min(iterable)
except (TypeError, ValueError):
# i18n: "min" is a keyword
raise error.ParseError(_("min first argument should be an iterable"))
+ return templatekw.wraphybridvalue(iterable, x, x)
@templatefunc('mod(a, b)')
def mod(context, mapping, args):
--- a/tests/test-command-template.t Mon Oct 02 07:18:24 2017 +0100
+++ b/tests/test-command-template.t Sat Sep 09 19:13:25 2017 +0900
@@ -3128,10 +3128,24 @@
$ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
-Test manifest can be join()-ed as before, though it's silly:
+ $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
+ branch: default
+ $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
+ hg: parse error: None is not iterable
+ [255]
+ $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
+ branch: default
+ $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
+ 0:ce3cec86e6c2
+ $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
+ 9:fbc7cd862e9c
+
+Test manifest/get() can be join()-ed as before, though it's silly:
$ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
11:2bc6e9006ce2
+ $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
+ default
Test the sub function of templating for expansion: