Patchwork Add splitlines, wordN, and startswith to templates

login
register
mail settings
Submitter Ryan McElroy
Date April 12, 2014, 6:45 a.m.
Message ID <CACL-Bzd_bZbzLzHxeqqv9zebC+6XnV7Wncx+XuMLpkEbaEpgSw@mail.gmail.com>
Download mbox | patch
Permalink /patch/4296/
State Changes Requested
Headers show

Comments

Ryan McElroy - April 12, 2014, 6:45 a.m.
# HG changeset patch
# User Ryan McElroy <ryanmce@gmail.com>
# Date 1397283707 25200
#      Fri Apr 11 23:21:47 2014 -0700
# Node ID 5701aea429330d7499f2f3de6fd633559b5dfbf3
# Parent  40800668e01921e41db1eb97d19e473971e93f5e
Add splitlines, wordN, and startswith to templates

This allows for much more useful description parsing techniques using
templates. For example, in phabrictor-based workflows, the Revision ID can
be
extracted from the description using a template like this:

{splitlines(desc) % '{if(startswith(\"Differential Revision\", line),
line|word3|basename)}
Siddharth Agarwal - April 12, 2014, 6:54 a.m.
On 04/11/2014 11:45 PM, Ryan McElroy wrote:
> # HG changeset patch
> # User Ryan McElroy <ryanmce@gmail.com <mailto:ryanmce@gmail.com>>
> # Date 1397283707 25200
> #      Fri Apr 11 23:21:47 2014 -0700
> # Node ID 5701aea429330d7499f2f3de6fd633559b5dfbf3
> # Parent  40800668e01921e41db1eb97d19e473971e93f5e
> Add splitlines, wordN, and startswith to templates

Please see http://mercurial.selenic.com/wiki/ContributingChanges. In 
particular, this should be split up into multiple patches, each 
introducing a keyword/function, and each with corresponding tests.


>
> This allows for much more useful description parsing techniques using
> templates. For example, in phabrictor-based workflows, the Revision ID 
> can be
> extracted from the description using a template like this:
>
> {splitlines(desc) % '{if(startswith(\"Differential Revision\", line), 
> line|word3|basename)}
>
> diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
> --- a/mercurial/templatefilters.py
> +++ b/mercurial/templatefilters.py
> @@ -302,6 +302,15 @@
>      """:shortdate: Date. Returns a date like "2006-09-18"."""
>      return util.shortdate(text)
>
> +def splitlines(text):
> +    """split text into a list of lines"""
> +    from templatekw import showlist
> +    return showlist('line', text.splitlines(), 'lines')
> +
> +def takeword(n):
> +    """return nth word from a string"""
> +    return lambda s: s.split()[n]
> +
>  def stringescape(text):
>      return text.encode('string_escape')
>
> @@ -384,6 +393,7 @@
>      "short": short,
>      "shortbisect": shortbisect,
>      "shortdate": shortdate,
> +    "splitlines": splitlines,
>      "stringescape": stringescape,
>      "stringify": stringify,
>      "strip": strip,
> @@ -392,6 +402,9 @@
>      "urlescape": urlescape,
>      "user": userfilter,
>      "emailuser": emailuser,
> +    "word1": takeword(0),
> +    "word2": takeword(1),
> +    "word3": takeword(2),

This isn't great. What about words beyond the third one?

FWIW, if there's a need for something specifically like this, we can 
implement and distribute it within FB as an extension.


>      "xmlescape": xmlescape,
>  }
>
> diff --git a/mercurial/templater.py b/mercurial/templater.py
> --- a/mercurial/templater.py
> +++ b/mercurial/templater.py
> @@ -111,7 +111,7 @@
>  def getsymbol(exp):
>      if exp[0] == 'symbol':
>          return exp[1]
> -    raise error.ParseError(_("expected a symbol"))
> +    raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])

This should be a separate patch as well.

>
>  def getlist(x):
>      if not x:
> @@ -464,6 +464,14 @@
>      src = stringify(_evalifliteral(args[2], context, mapping))
>      yield re.sub(pat, rpl, src)
>
> +def startswith(context, mapping, args):
> +    if len(args) != 2:
> +        raise error.ParseError(_("starts_with expects two arguments"))

startswith, not starts_with

> +
> +    patn = stringify(args[0][0](context, mapping, args[0][1]))
> +    text = stringify(args[1][0](context, mapping, args[1][1]))
> +    return text if text.startswith(patn) else ''
> +
>  methods = {
>      "string": lambda e, c: (runstring, e[1]),
>      "rawstring": lambda e, c: (runrawstring, e[1]),
> @@ -488,6 +496,7 @@
>      "revset": revset,
>      "rstdoc": rstdoc,
>      "shortest": shortest,
> +    "startswith": startswith,
>      "strip": strip,
>      "sub": sub,
>  }
>
>
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Durham Goode - April 14, 2014, 9:49 p.m.
On 4/11/14, 11:54 PM, Siddharth Agarwal wrote:
> On 04/11/2014 11:45 PM, Ryan McElroy wrote:
>> # HG changeset patch
>> # User Ryan McElroy <ryanmce@gmail.com <mailto:ryanmce@gmail.com>>
>> # Date 1397283707 25200
>> #      Fri Apr 11 23:21:47 2014 -0700
>> # Node ID 5701aea429330d7499f2f3de6fd633559b5dfbf3
>> # Parent  40800668e01921e41db1eb97d19e473971e93f5e
>> Add splitlines, wordN, and startswith to templates
>
> Please see http://mercurial.selenic.com/wiki/ContributingChanges. In 
> particular, this should be split up into multiple patches, each 
> introducing a keyword/function, and each with corresponding tests.
>
>> @@ -384,6 +393,7 @@
>>      "short": short,
>>      "shortbisect": shortbisect,
>>      "shortdate": shortdate,
>> +    "splitlines": splitlines,
>>      "stringescape": stringescape,
>>      "stringify": stringify,
>>      "strip": strip,
>> @@ -392,6 +402,9 @@
>>      "urlescape": urlescape,
>>      "user": userfilter,
>>      "emailuser": emailuser,
>> +    "word1": takeword(0),
>> +    "word2": takeword(1),
>> +    "word3": takeword(2),
>
> This isn't great. What about words beyond the third one?
>
> FWIW, if there's a need for something specifically like this, we can 
> implement and distribute it within FB as an extension.

split() and atindex() functions might be a more generic way to do this 
kind of stuff. 'split({desc}, '\n')' would give you lines, and 
'split({line}, ' ')' would give you words.  atindex(3, split({line}, ' 
')) would give you the third word (and you could do the same to get the 
3rd line).  splitting over words would also allow you to iterate over 
the words, looking for the D#### formatted word (instead of hard coding 
it as the 3rd word).

Patch

diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -302,6 +302,15 @@ 
     """:shortdate: Date. Returns a date like "2006-09-18"."""
     return util.shortdate(text)

+def splitlines(text):
+    """split text into a list of lines"""
+    from templatekw import showlist
+    return showlist('line', text.splitlines(), 'lines')
+
+def takeword(n):
+    """return nth word from a string"""
+    return lambda s: s.split()[n]
+
 def stringescape(text):
     return text.encode('string_escape')

@@ -384,6 +393,7 @@ 
     "short": short,
     "shortbisect": shortbisect,
     "shortdate": shortdate,
+    "splitlines": splitlines,
     "stringescape": stringescape,
     "stringify": stringify,
     "strip": strip,
@@ -392,6 +402,9 @@ 
     "urlescape": urlescape,
     "user": userfilter,
     "emailuser": emailuser,
+    "word1": takeword(0),
+    "word2": takeword(1),
+    "word3": takeword(2),
     "xmlescape": xmlescape,
 }

diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -111,7 +111,7 @@ 
 def getsymbol(exp):
     if exp[0] == 'symbol':
         return exp[1]
-    raise error.ParseError(_("expected a symbol"))
+    raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])

 def getlist(x):
     if not x:
@@ -464,6 +464,14 @@ 
     src = stringify(_evalifliteral(args[2], context, mapping))
     yield re.sub(pat, rpl, src)

+def startswith(context, mapping, args):
+    if len(args) != 2:
+        raise error.ParseError(_("starts_with expects two arguments"))
+
+    patn = stringify(args[0][0](context, mapping, args[0][1]))
+    text = stringify(args[1][0](context, mapping, args[1][1]))
+    return text if text.startswith(patn) else ''
+
 methods = {
     "string": lambda e, c: (runstring, e[1]),
     "rawstring": lambda e, c: (runrawstring, e[1]),
@@ -488,6 +496,7 @@ 
     "revset": revset,
     "rstdoc": rstdoc,
     "shortest": shortest,
+    "startswith": startswith,
     "strip": strip,
     "sub": sub,
 }