templater: extend filter() to accept template expression for emptiness test
This utilizes the pass-by-name nature of template arguments.
--- a/mercurial/templatefuncs.py Thu Jun 14 22:33:26 2018 +0900
+++ b/mercurial/templatefuncs.py Thu Jun 14 23:10:14 2018 +0900
@@ -166,15 +166,23 @@
return templatefilters.fill(text, width, initindent, hangindent)
-@templatefunc('filter(iterable)')
+@templatefunc('filter(iterable[, expr])')
def filter_(context, mapping, args):
- """Remove empty elements from a list or a dict."""
- if len(args) != 1:
+ """Remove empty elements from a list or a dict. If expr specified, it's
+ applied to each element to test emptiness."""
+ if not (1 <= len(args) <= 2):
# i18n: "filter" is a keyword
- raise error.ParseError(_("filter expects one argument"))
+ raise error.ParseError(_("filter expects one or two arguments"))
iterable = evalwrapped(context, mapping, args[0])
- def select(w):
- return w.tobool(context, mapping)
+ if len(args) == 1:
+ def select(w):
+ return w.tobool(context, mapping)
+ else:
+ def select(w):
+ if not isinstance(w, templateutil.mappable):
+ raise error.ParseError(_("not filterable by expression"))
+ lm = context.overlaymap(mapping, w.tomap(context))
+ return evalboolean(context, lm, args[1])
return iterable.filter(context, mapping, select)
@templatefunc('formatnode(node)', requires={'ui'})
--- a/mercurial/templateutil.py Thu Jun 14 22:33:26 2018 +0900
+++ b/mercurial/templateutil.py Thu Jun 14 23:10:14 2018 +0900
@@ -415,6 +415,7 @@
raise error.ParseError(_('not comparable'))
def filter(self, context, mapping, select):
+ # implement if necessary; we'll need a wrapped type for a mapping dict
raise error.ParseError(_('not filterable without template'))
def join(self, context, mapping, sep):
--- a/tests/test-template-functions.t Thu Jun 14 22:33:26 2018 +0900
+++ b/tests/test-template-functions.t Thu Jun 14 23:10:14 2018 +0900
@@ -449,6 +449,13 @@
$ hg log -R a -r 0 -T '{filter(revset("0:2"))}\n'
0 1 2
+Test filter() by expression:
+
+ $ hg log -R a -r 1 -T '{filter(desc|splitlines, ifcontains("1", line, "t"))}\n'
+ other 1
+ $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1), ifeq(key, "b", "t"))}\n'
+ b=1
+
Test filter() shouldn't crash:
$ hg log -R a -r 0 -T '{filter(extras)}\n'
@@ -459,7 +466,7 @@
Test filter() unsupported arguments:
$ hg log -R a -r 0 -T '{filter()}\n'
- hg: parse error: filter expects one argument
+ hg: parse error: filter expects one or two arguments
[255]
$ hg log -R a -r 0 -T '{filter(date)}\n'
hg: parse error: date is not iterable
@@ -476,6 +483,9 @@
$ hg log -R a -r 0 -T '{filter(succsandmarkers)}\n'
hg: parse error: not filterable without template
[255]
+ $ hg log -R a -r 0 -T '{filter(desc|splitlines % "{line}", "")}\n'
+ hg: parse error: not filterable by expression
+ [255]
Test manifest/get() can be join()-ed as string, though it's silly: