Patchwork [2,of,3] templater: add len template filter

login
register
mail settings
Submitter Anton Shestakov
Date Sept. 8, 2014, 12:04 p.m.
Message ID <3302531410177886@web19g.yandex.ru>
Download mbox | patch
Permalink /patch/5725/
State Changes Requested
Headers show

Comments

Anton Shestakov - Sept. 8, 2014, 12:04 p.m.
# HG changeset patch
# User Anton Shestakov <engored@ya.ru>
# Date 1410176607 -32400
#      Mon Sep 08 20:43:27 2014 +0900
# Node ID 82b3cffda4868d23ce436adac8687645f5f7441b
# Parent  9bb0ab35e90ac9ebf6adbd6e503f3412d9f2adb7
templater: add len template filter

Currently there is no way of telling how much children or bookmarks or tags a
certain changeset has in a template. It is possible to tell if a changeset have
either 0 or not 0 bookmarks, but no way to tell if it has 1 or 2 of them, for
example.

This filter, simply named len, makes it possible to count the number of items
in a list or the length of a string (or, anything that python's len can count).
E.g.: {children|len}, {bookmarks|len}, {file_adds|len}.

The reason why it needs a wrapper (i.e. not "len": len,) is that the python
builtin function doesn't have a func_name, which matters if the filter is not
compatible with a keyword (func_name is used to print a message). I've used a
trailing underscore to make it different from builtin function as pep8
recommends, so in case the filter is not compatible with a keywords the message
is, for example:
abort: template filter 'len_' is not compatible with keyword 'rev'
Mads Kiilerich - Sept. 9, 2014, 2:03 a.m.
On 09/08/2014 02:04 PM, Anton Shestakov wrote:
> # HG changeset patch
> # User Anton Shestakov <engored@ya.ru>
> # Date 1410176607 -32400
> #      Mon Sep 08 20:43:27 2014 +0900
> # Node ID 82b3cffda4868d23ce436adac8687645f5f7441b
> # Parent  9bb0ab35e90ac9ebf6adbd6e503f3412d9f2adb7
> templater: add len template filter

Most excellent, thanks!

I had thought about implementing something like this and call it "count" 
instead. I am not sure I like that this approach exposes whatever python 
len does ... but I guess it is ok and can be handy.

The combination of log and revset and len is very powerful (and 
potentially slow). I think that combination also should have some test 
coverage. Perhaps by adding root distance: {revset("::%d",rev)|len} .

Personally, I would prefer to have the test in the same changeset as the 
code so it helps documenting why the change is made. That makes history 
digging so much easier.


I like how this feature almost can replace the {latesttag} and 
{latesttagdistance} concepts with something like:
hg log -r 'tagged()' -T '{revset("%d::.",rev)|len}  {tags}\n'

The following idiom for getting the size of a revset can also be handy 
but is a bit ugly:
hg log -r null -T '{revset("::3.1-::3.0")|len}\n'

I guess templatification of "id" will make that a better way to get the 
size of a revset result ... but a dedicated 'count' command could 
perhaps also be handy.

/Mads
Anton Shestakov - Sept. 9, 2014, 5:13 a.m.
09.09.2014, 11:03, "Mads Kiilerich" <mads@kiilerich.com>:
> On 09/08/2014 02:04 PM, Anton Shestakov wrote:
>>  # HG changeset patch
>>  # User Anton Shestakov <engored@ya.ru>
>>  # Date 1410176607 -32400
>>  #      Mon Sep 08 20:43:27 2014 +0900
>>  # Node ID 82b3cffda4868d23ce436adac8687645f5f7441b
>>  # Parent  9bb0ab35e90ac9ebf6adbd6e503f3412d9f2adb7
>>  templater: add len template filter
>
> Most excellent, thanks!
>
> I had thought about implementing something like this and call it "count"
> instead. I am not sure I like that this approach exposes whatever python
> len does ... but I guess it is ok and can be handy.

Thanks for the feedback! I've named it len because you can call filters just
like functions too, i.e. {len(revset(...))}, which arguably looks more pythonic
than count(), but since templates are not python anyway, I have no problem
renaming it. (Which reminds me of django templates, which are so frustrating at
times exactly because they look much like python, but are not. Let's not bring
that here.)

> The combination of log and revset and len is very powerful (and
> potentially slow). I think that combination also should have some test
> coverage. Perhaps by adding root distance: {revset("::%d",rev)|len} .
>
> Personally, I would prefer to have the test in the same changeset as the
> code so it helps documenting why the change is made. That makes history
> digging so much easier.

Sure, I will do that in the next version of the patch.

> I guess templatification of "id" will make that a better way to get the
> size of a revset result ... but a dedicated 'count' command could
> perhaps also be handy.

Yes, I've decided to make this filter after trying to imitate summary command
with one big template. That's turned out to be a mess when I got to label()ing
every line. So I think that templatificating identify instead is a great idea.
Pierre-Yves David - Sept. 9, 2014, 9:10 a.m.
On 09/09/2014 03:03 AM, Mads Kiilerich wrote:
> On 09/08/2014 02:04 PM, Anton Shestakov wrote:
>> # HG changeset patch
>> # User Anton Shestakov <engored@ya.ru>
>> # Date 1410176607 -32400
>> #      Mon Sep 08 20:43:27 2014 +0900
>> # Node ID 82b3cffda4868d23ce436adac8687645f5f7441b
>> # Parent  9bb0ab35e90ac9ebf6adbd6e503f3412d9f2adb7
>> templater: add len template filter
>
> Most excellent, thanks!
>
> I had thought about implementing something like this and call it "count"
> instead. I am not sure I like that this approach exposes whatever python
> len does ... but I guess it is ok and can be handy.
>
> The combination of log and revset and len is very powerful (and
> potentially slow). I think that combination also should have some test
> coverage. Perhaps by adding root distance: {revset("::%d",rev)|len} .
>
> Personally, I would prefer to have the test in the same changeset as the
> code so it helps documenting why the change is made. That makes history
> digging so much easier.
>
>
> I like how this feature almost can replace the {latesttag} and
> {latesttagdistance} concepts with something like:
> hg log -r 'tagged()' -T '{revset("%d::.",rev)|len}  {tags}\n'
>
> The following idiom for getting the size of a revset can also be handy
> but is a bit ugly:
> hg log -r null -T '{revset("::3.1-::3.0")|len}\n'

Note that you can now use:  only("3.1", "3.0") instead of ::"3.1" - :: 
"3.0".

(I also agree with the mads review here, no strong opinion about len vs 
length vs count. Small preference for count.)

Patch

diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -224,6 +224,10 @@  def jsonescape(s):
         s = s.replace(k, v)
     return ''.join(_uescape(c) for c in s)
 
+def len_(i):
+    """:len: List or text. Returns the length as an integer."""
+    return len(i)
+
 def localdate(text):
     """:localdate: Date. Converts a date to local date."""
     return (util.parsedate(text)[0], util.makedate()[1])
@@ -379,6 +383,7 @@  filters = {
     "isodatesec": isodatesec,
     "json": json,
     "jsonescape": jsonescape,
+    "len": len_,
     "localdate": localdate,
     "nonempty": nonempty,
     "obfuscate": obfuscate,