Patchwork [3,of,3] templater: provide loop counter as "index" keyword

login
register
mail settings
Submitter Yuya Nishihara
Date April 2, 2017, 2:32 p.m.
Message ID <12e1172d81bc8140705e.1491143533@mimosa>
Download mbox | patch
Permalink /patch/19910/
State Accepted
Headers show

Comments

Yuya Nishihara - April 2, 2017, 2:32 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1461329193 -32400
#      Fri Apr 22 21:46:33 2016 +0900
# Node ID 12e1172d81bc8140705eabc1dc133ff266dae551
# Parent  18aabf8f98c69cbbcff07a1f290f8bd4cf347293
templater: provide loop counter as "index" keyword

This was originally written for JSON templating where we would have to be
careful to not add extra comma, but seems generally useful.

Inner loop started by % operator has its own counter.
Augie Fackler - April 3, 2017, 8:16 p.m.
On Sun, Apr 02, 2017 at 11:32:13PM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1461329193 -32400
> #      Fri Apr 22 21:46:33 2016 +0900
> # Node ID 12e1172d81bc8140705eabc1dc133ff266dae551
> # Parent  18aabf8f98c69cbbcff07a1f290f8bd4cf347293
> templater: provide loop counter as "index" keyword

queued, thanks

>
> This was originally written for JSON templating where we would have to be
> careful to not add extra comma, but seems generally useful.
>
> Inner loop started by % operator has its own counter.
>
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -8,6 +8,7 @@
>  from __future__ import absolute_import
>
>  import errno
> +import itertools
>  import os
>  import re
>  import tempfile
> @@ -1452,6 +1453,7 @@ class changeset_templater(changeset_prin
>              self.t = formatter.maketemplater(ui, 'changeset', tmpl,
>                                               cache=defaulttempl)
>
> +        self._counter = itertools.count()
>          self.cache = {}
>
>          # find correct templates for current mode
> @@ -1490,6 +1492,7 @@ class changeset_templater(changeset_prin
>          props['ctx'] = ctx
>          props['repo'] = self.repo
>          props['ui'] = self.repo.ui
> +        props['index'] = next(self._counter)
>          props['revcache'] = {'copies': copies}
>          props['cache'] = self.cache
>
> diff --git a/mercurial/formatter.py b/mercurial/formatter.py
> --- a/mercurial/formatter.py
> +++ b/mercurial/formatter.py
> @@ -103,6 +103,7 @@ baz: foo, bar
>
>  from __future__ import absolute_import
>
> +import itertools
>  import os
>
>  from .i18n import _
> @@ -355,6 +356,7 @@ class templateformatter(baseformatter):
>          self._topic = topic
>          self._t = gettemplater(ui, topic, opts.get('template', ''),
>                                 cache=templatekw.defaulttempl)
> +        self._counter = itertools.count()
>          self._cache = {}  # for templatekw/funcs to store reusable data
>      def context(self, **ctxs):
>          '''insert context objects to be used to render template keywords'''
> @@ -367,6 +369,7 @@ class templateformatter(baseformatter):
>          props = {}
>          if 'ctx' in self._item:
>              props.update(templatekw.keywords)
> +        props['index'] = next(self._counter)
>          # explicitly-defined fields precede templatekw
>          props.update(self._item)
>          if 'ctx' in self._item:
> diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py
> --- a/mercurial/templatekw.py
> +++ b/mercurial/templatekw.py
> @@ -7,6 +7,7 @@
>
>  from __future__ import absolute_import
>
> +from .i18n import _
>  from .node import hex, nullid
>  from . import (
>      encoding,
> @@ -422,6 +423,12 @@ def showgraphnode(repo, ctx, **args):
>      else:
>          return 'o'
>
> +@templatekeyword('index')
> +def showindex(**args):
> +    """Integer. The current iteration of the loop. (0 indexed)"""
> +    # just hosts documentation; should be overridden by template mapping
> +    raise error.Abort(_("can't use index in this context"))
> +
>  @templatekeyword('latesttag')
>  def showlatesttag(**args):
>      """List of strings. The global tags on the most recent globally
> diff --git a/mercurial/templater.py b/mercurial/templater.py
> --- a/mercurial/templater.py
> +++ b/mercurial/templater.py
> @@ -411,8 +411,9 @@ def runmap(context, mapping, data):
>              else:
>                  raise error.ParseError(_("%r is not iterable") % d)
>
> -    for v in diter:
> +    for i, v in enumerate(diter):
>          lm = mapping.copy()
> +        lm['index'] = i
>          if isinstance(v, dict):
>              lm.update(v)
>              lm['originalnode'] = mapping.get('node')
> diff --git a/tests/test-command-template.t b/tests/test-command-template.t
> --- a/tests/test-command-template.t
> +++ b/tests/test-command-template.t
> @@ -2683,6 +2683,16 @@ Pass generator object created by templat
>    $ hg log -l 1 --template '{if(author, author)|user}\n'
>    test
>
> +Test index keyword:
> +
> +  $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
> +  10 0:a 1:b 2:fifth 3:fourth 4:third
> +  11 0:a
> +
> +  $ hg branches -T '{index} {branch}\n'
> +  0 default
> +  1 foo
> +
>  Test diff function:
>
>    $ hg diff -c 8
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -8,6 +8,7 @@ 
 from __future__ import absolute_import
 
 import errno
+import itertools
 import os
 import re
 import tempfile
@@ -1452,6 +1453,7 @@  class changeset_templater(changeset_prin
             self.t = formatter.maketemplater(ui, 'changeset', tmpl,
                                              cache=defaulttempl)
 
+        self._counter = itertools.count()
         self.cache = {}
 
         # find correct templates for current mode
@@ -1490,6 +1492,7 @@  class changeset_templater(changeset_prin
         props['ctx'] = ctx
         props['repo'] = self.repo
         props['ui'] = self.repo.ui
+        props['index'] = next(self._counter)
         props['revcache'] = {'copies': copies}
         props['cache'] = self.cache
 
diff --git a/mercurial/formatter.py b/mercurial/formatter.py
--- a/mercurial/formatter.py
+++ b/mercurial/formatter.py
@@ -103,6 +103,7 @@  baz: foo, bar
 
 from __future__ import absolute_import
 
+import itertools
 import os
 
 from .i18n import _
@@ -355,6 +356,7 @@  class templateformatter(baseformatter):
         self._topic = topic
         self._t = gettemplater(ui, topic, opts.get('template', ''),
                                cache=templatekw.defaulttempl)
+        self._counter = itertools.count()
         self._cache = {}  # for templatekw/funcs to store reusable data
     def context(self, **ctxs):
         '''insert context objects to be used to render template keywords'''
@@ -367,6 +369,7 @@  class templateformatter(baseformatter):
         props = {}
         if 'ctx' in self._item:
             props.update(templatekw.keywords)
+        props['index'] = next(self._counter)
         # explicitly-defined fields precede templatekw
         props.update(self._item)
         if 'ctx' in self._item:
diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py
--- a/mercurial/templatekw.py
+++ b/mercurial/templatekw.py
@@ -7,6 +7,7 @@ 
 
 from __future__ import absolute_import
 
+from .i18n import _
 from .node import hex, nullid
 from . import (
     encoding,
@@ -422,6 +423,12 @@  def showgraphnode(repo, ctx, **args):
     else:
         return 'o'
 
+@templatekeyword('index')
+def showindex(**args):
+    """Integer. The current iteration of the loop. (0 indexed)"""
+    # just hosts documentation; should be overridden by template mapping
+    raise error.Abort(_("can't use index in this context"))
+
 @templatekeyword('latesttag')
 def showlatesttag(**args):
     """List of strings. The global tags on the most recent globally
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -411,8 +411,9 @@  def runmap(context, mapping, data):
             else:
                 raise error.ParseError(_("%r is not iterable") % d)
 
-    for v in diter:
+    for i, v in enumerate(diter):
         lm = mapping.copy()
+        lm['index'] = i
         if isinstance(v, dict):
             lm.update(v)
             lm['originalnode'] = mapping.get('node')
diff --git a/tests/test-command-template.t b/tests/test-command-template.t
--- a/tests/test-command-template.t
+++ b/tests/test-command-template.t
@@ -2683,6 +2683,16 @@  Pass generator object created by templat
   $ hg log -l 1 --template '{if(author, author)|user}\n'
   test
 
+Test index keyword:
+
+  $ hg log -l 2 -T '{index + 10}{files % " {index}:{file}"}\n'
+  10 0:a 1:b 2:fifth 3:fourth 4:third
+  11 0:a
+
+  $ hg branches -T '{index} {branch}\n'
+  0 default
+  1 foo
+
 Test diff function:
 
   $ hg diff -c 8