Show More
@@ -727,6 +727,10 b' class sessionvars(templateutil.wrapped):' | |||
|
727 | 727 | def getmax(self, context, mapping): |
|
728 | 728 | raise error.ParseError(_('not comparable')) |
|
729 | 729 | |
|
730 | def filter(self, context, mapping, select): | |
|
731 | # implement if necessary | |
|
732 | raise error.ParseError(_('not filterable')) | |
|
733 | ||
|
730 | 734 | def itermaps(self, context): |
|
731 | 735 | separator = self._start |
|
732 | 736 | for key, value in sorted(self._vars.iteritems()): |
@@ -166,6 +166,17 b' def fill(context, mapping, args):' | |||
|
166 | 166 | |
|
167 | 167 | return templatefilters.fill(text, width, initindent, hangindent) |
|
168 | 168 | |
|
169 | @templatefunc('filter(iterable)') | |
|
170 | def filter_(context, mapping, args): | |
|
171 | """Remove empty elements from a list or a dict.""" | |
|
172 | if len(args) != 1: | |
|
173 | # i18n: "filter" is a keyword | |
|
174 | raise error.ParseError(_("filter expects one argument")) | |
|
175 | iterable = evalwrapped(context, mapping, args[0]) | |
|
176 | def select(w): | |
|
177 | return w.tobool(context, mapping) | |
|
178 | return iterable.filter(context, mapping, select) | |
|
179 | ||
|
169 | 180 | @templatefunc('formatnode(node)', requires={'ui'}) |
|
170 | 181 | def formatnode(context, mapping, args): |
|
171 | 182 | """Obtain the preferred form of a changeset hash. (DEPRECATED)""" |
@@ -63,6 +63,14 b' class wrapped(object):' | |||
|
63 | 63 | value depending on the self type""" |
|
64 | 64 | |
|
65 | 65 | @abc.abstractmethod |
|
66 | def filter(self, context, mapping, select): | |
|
67 | """Return new container of the same type which includes only the | |
|
68 | selected elements | |
|
69 | ||
|
70 | select() takes each item as a wrapped object and returns True/False. | |
|
71 | """ | |
|
72 | ||
|
73 | @abc.abstractmethod | |
|
66 | 74 | def itermaps(self, context): |
|
67 | 75 | """Yield each template mapping""" |
|
68 | 76 | |
@@ -130,6 +138,10 b' class wrappedbytes(wrapped):' | |||
|
130 | 138 | raise error.ParseError(_('empty string')) |
|
131 | 139 | return func(pycompat.iterbytestr(self._value)) |
|
132 | 140 | |
|
141 | def filter(self, context, mapping, select): | |
|
142 | raise error.ParseError(_('%r is not filterable') | |
|
143 | % pycompat.bytestr(self._value)) | |
|
144 | ||
|
133 | 145 | def itermaps(self, context): |
|
134 | 146 | raise error.ParseError(_('%r is not iterable of mappings') |
|
135 | 147 | % pycompat.bytestr(self._value)) |
@@ -164,6 +176,9 b' class wrappedvalue(wrapped):' | |||
|
164 | 176 | def getmax(self, context, mapping): |
|
165 | 177 | raise error.ParseError(_("%r is not iterable") % self._value) |
|
166 | 178 | |
|
179 | def filter(self, context, mapping, select): | |
|
180 | raise error.ParseError(_("%r is not iterable") % self._value) | |
|
181 | ||
|
167 | 182 | def itermaps(self, context): |
|
168 | 183 | raise error.ParseError(_('%r is not iterable of mappings') |
|
169 | 184 | % self._value) |
@@ -208,6 +223,9 b' class date(mappable, wrapped):' | |||
|
208 | 223 | def getmax(self, context, mapping): |
|
209 | 224 | raise error.ParseError(_('date is not iterable')) |
|
210 | 225 | |
|
226 | def filter(self, context, mapping, select): | |
|
227 | raise error.ParseError(_('date is not iterable')) | |
|
228 | ||
|
211 | 229 | def join(self, context, mapping, sep): |
|
212 | 230 | raise error.ParseError(_("date is not iterable")) |
|
213 | 231 | |
@@ -273,6 +291,14 b' class hybrid(wrapped):' | |||
|
273 | 291 | return val |
|
274 | 292 | return hybriditem(None, key, val, self._makemap) |
|
275 | 293 | |
|
294 | def filter(self, context, mapping, select): | |
|
295 | if util.safehasattr(self._values, 'get'): | |
|
296 | values = {k: v for k, v in self._values.iteritems() | |
|
297 | if select(self._wrapvalue(k, v))} | |
|
298 | else: | |
|
299 | values = [v for v in self._values if select(self._wrapvalue(v, v))] | |
|
300 | return hybrid(None, values, self._makemap, self._joinfmt, self._keytype) | |
|
301 | ||
|
276 | 302 | def itermaps(self, context): |
|
277 | 303 | makemap = self._makemap |
|
278 | 304 | for x in self._values: |
@@ -336,6 +362,10 b' class hybriditem(mappable, wrapped):' | |||
|
336 | 362 | w = makewrapped(context, mapping, self._value) |
|
337 | 363 | return w.getmax(context, mapping) |
|
338 | 364 | |
|
365 | def filter(self, context, mapping, select): | |
|
366 | w = makewrapped(context, mapping, self._value) | |
|
367 | return w.filter(context, mapping, select) | |
|
368 | ||
|
339 | 369 | def join(self, context, mapping, sep): |
|
340 | 370 | w = makewrapped(context, mapping, self._value) |
|
341 | 371 | return w.join(context, mapping, sep) |
@@ -384,6 +414,9 b' class _mappingsequence(wrapped):' | |||
|
384 | 414 | def getmax(self, context, mapping): |
|
385 | 415 | raise error.ParseError(_('not comparable')) |
|
386 | 416 | |
|
417 | def filter(self, context, mapping, select): | |
|
418 | raise error.ParseError(_('not filterable without template')) | |
|
419 | ||
|
387 | 420 | def join(self, context, mapping, sep): |
|
388 | 421 | mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context)) |
|
389 | 422 | if self._name: |
@@ -472,6 +505,17 b' class mappedgenerator(wrapped):' | |||
|
472 | 505 | raise error.ParseError(_('empty sequence')) |
|
473 | 506 | return func(xs) |
|
474 | 507 | |
|
508 | @staticmethod | |
|
509 | def _filteredgen(context, mapping, make, args, select): | |
|
510 | for x in make(context, *args): | |
|
511 | s = stringify(context, mapping, x) | |
|
512 | if select(wrappedbytes(s)): | |
|
513 | yield s | |
|
514 | ||
|
515 | def filter(self, context, mapping, select): | |
|
516 | args = (mapping, self._make, self._args, select) | |
|
517 | return mappedgenerator(self._filteredgen, args) | |
|
518 | ||
|
475 | 519 | def itermaps(self, context): |
|
476 | 520 | raise error.ParseError(_('list of strings is not mappable')) |
|
477 | 521 |
@@ -435,6 +435,48 b' latesttag() function:' | |||
|
435 | 435 | |
|
436 | 436 | $ cd .. |
|
437 | 437 | |
|
438 | Test filter() empty values: | |
|
439 | ||
|
440 | $ hg log -R a -r 1 -T '{filter(desc|splitlines) % "{line}\n"}' | |
|
441 | other 1 | |
|
442 | other 2 | |
|
443 | other 3 | |
|
444 | $ hg log -R a -r 0 -T '{filter(dict(a=0, b=1) % "{ifeq(key, "a", "{value}\n")}")}' | |
|
445 | 0 | |
|
446 | ||
|
447 | 0 should not be falsy | |
|
448 | ||
|
449 | $ hg log -R a -r 0 -T '{filter(revset("0:2"))}\n' | |
|
450 | 0 1 2 | |
|
451 | ||
|
452 | Test filter() shouldn't crash: | |
|
453 | ||
|
454 | $ hg log -R a -r 0 -T '{filter(extras)}\n' | |
|
455 | branch=default | |
|
456 | $ hg log -R a -r 0 -T '{filter(files)}\n' | |
|
457 | a | |
|
458 | ||
|
459 | Test filter() unsupported arguments: | |
|
460 | ||
|
461 | $ hg log -R a -r 0 -T '{filter()}\n' | |
|
462 | hg: parse error: filter expects one argument | |
|
463 | [255] | |
|
464 | $ hg log -R a -r 0 -T '{filter(date)}\n' | |
|
465 | hg: parse error: date is not iterable | |
|
466 | [255] | |
|
467 | $ hg log -R a -r 0 -T '{filter(rev)}\n' | |
|
468 | hg: parse error: 0 is not iterable | |
|
469 | [255] | |
|
470 | $ hg log -R a -r 0 -T '{filter(desc|firstline)}\n' | |
|
471 | hg: parse error: 'line 1' is not filterable | |
|
472 | [255] | |
|
473 | $ hg log -R a -r 0 -T '{filter(manifest)}\n' | |
|
474 | hg: parse error: '0:a0c8bcbbb45c' is not filterable | |
|
475 | [255] | |
|
476 | $ hg log -R a -r 0 -T '{filter(succsandmarkers)}\n' | |
|
477 | hg: parse error: not filterable without template | |
|
478 | [255] | |
|
479 | ||
|
438 | 480 |
|
|
439 | 481 | |
|
440 | 482 | $ hg log -R latesttag -r tip -T '{join(manifest, ".")}\n' |
General Comments 0
You need to be logged in to leave comments.
Login now