# HG changeset patch # User Yuya Nishihara # Date 1528985414 -32400 # Node ID bc8d925342f000359327e30c092855e327a7144b # Parent dae829b4de78bd8331bb8ce802ae733919d22c59 templater: extend filter() to accept template expression for emptiness test This utilizes the pass-by-name nature of template arguments. diff -r dae829b4de78 -r bc8d925342f0 mercurial/templatefuncs.py --- 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'}) diff -r dae829b4de78 -r bc8d925342f0 mercurial/templateutil.py --- 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): diff -r dae829b4de78 -r bc8d925342f0 tests/test-template-functions.t --- 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: