merge with stable
authorMatt Mackall <mpm@selenic.com>
Tue, 11 Mar 2014 16:19:08 -0500
changeset 20670 0084fcd5d7e2
parent 20669 870d60294b04 (current diff)
parent 20663 5ab28a2e9962 (diff)
child 20671 5442cab57b09
merge with stable
hgext/color.py
mercurial/templater.py
tests/test-command-template.t
--- a/hgext/color.py	Mon Mar 10 15:00:41 2014 -0500
+++ b/hgext/color.py	Tue Mar 11 16:19:08 2014 -0500
@@ -393,9 +393,7 @@
     if isinstance(repo, str):
         return thing
 
-    label = templater.stringify(args[0][0](context, mapping, args[0][1]))
-    label = templater.runtemplate(context, mapping,
-                                  templater.compiletemplate(label, context))
+    label = templater._evalifliteral(args[0], context, mapping)
 
     thing = templater.stringify(thing)
     label = templater.stringify(label)
--- a/mercurial/templater.py	Mon Mar 10 15:00:41 2014 -0500
+++ b/mercurial/templater.py	Tue Mar 11 16:19:08 2014 -0500
@@ -21,6 +21,7 @@
     ")": (0, None, None),
     "symbol": (0, ("symbol",), None),
     "string": (0, ("string",), None),
+    "rawstring": (0, ("rawstring",), None),
     "end": (0, None, None),
 }
 
@@ -50,7 +51,7 @@
                     continue
                 if d == c:
                     if not decode:
-                        yield ('string', program[s:pos].replace('\\', r'\\'), s)
+                        yield ('rawstring', program[s:pos], s)
                         break
                     yield ('string', program[s:pos], s)
                     break
@@ -76,23 +77,22 @@
         pos += 1
     yield ('end', None, pos)
 
-def compiletemplate(tmpl, context):
+def compiletemplate(tmpl, context, strtoken="string"):
     parsed = []
     pos, stop = 0, len(tmpl)
     p = parser.parser(tokenizer, elements)
     while pos < stop:
         n = tmpl.find('{', pos)
         if n < 0:
-            parsed.append(("string", tmpl[pos:].decode("string-escape")))
+            parsed.append((strtoken, tmpl[pos:]))
             break
         if n > 0 and tmpl[n - 1] == '\\':
             # escaped
-            parsed.append(("string",
-                           (tmpl[pos:n - 1] + "{").decode("string-escape")))
+            parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
             pos = n + 1
             continue
         if n > pos:
-            parsed.append(("string", tmpl[pos:n].decode("string-escape")))
+            parsed.append((strtoken, tmpl[pos:n]))
 
         pd = [tmpl, n + 1, stop]
         parseres, pos = p.parse(pd)
@@ -127,13 +127,16 @@
     return context._filters[f]
 
 def gettemplate(exp, context):
-    if exp[0] == 'string':
-        return compiletemplate(exp[1], context)
+    if exp[0] == 'string' or exp[0] == 'rawstring':
+        return compiletemplate(exp[1], context, strtoken=exp[0])
     if exp[0] == 'symbol':
         return context._load(exp[1])
     raise error.ParseError(_("expected template specifier"))
 
 def runstring(context, mapping, data):
+    return data.decode("string-escape")
+
+def runrawstring(context, mapping, data):
     return data
 
 def runsymbol(context, mapping, key):
@@ -234,12 +237,8 @@
         except ValueError:
             raise error.ParseError(_("fill expects an integer width"))
         try:
-            initindent = stringify(args[2][0](context, mapping, args[2][1]))
-            initindent = stringify(runtemplate(context, mapping,
-                                     compiletemplate(initindent, context)))
-            hangindent = stringify(args[3][0](context, mapping, args[3][1]))
-            hangindent = stringify(runtemplate(context, mapping,
-                                     compiletemplate(hangindent, context)))
+            initindent = stringify(_evalifliteral(args[2], context, mapping))
+            hangindent = stringify(_evalifliteral(args[3], context, mapping))
         except IndexError:
             pass
 
@@ -285,8 +284,9 @@
 
 def _evalifliteral(arg, context, mapping):
     t = stringify(arg[0](context, mapping, arg[1]))
-    if arg[0] == runstring:
-        yield runtemplate(context, mapping, compiletemplate(t, context))
+    if arg[0] == runstring or arg[0] == runrawstring:
+        yield runtemplate(context, mapping,
+                          compiletemplate(t, context, strtoken='rawstring'))
     else:
         yield t
 
@@ -338,7 +338,7 @@
 
     joiner = " "
     if len(args) > 1:
-        joiner = args[1][0](context, mapping, args[1][1])
+        joiner = stringify(args[1][0](context, mapping, args[1][1]))
 
     first = True
     for x in joinset:
@@ -447,9 +447,9 @@
     if not (1 <= len(args) <= 2):
         raise error.ParseError(_("strip expects one or two arguments"))
 
-    text = args[0][0](context, mapping, args[0][1])
+    text = stringify(args[0][0](context, mapping, args[0][1]))
     if len(args) == 2:
-        chars = args[1][0](context, mapping, args[1][1])
+        chars = stringify(args[1][0](context, mapping, args[1][1]))
         return text.strip(chars)
     return text.strip()
 
@@ -460,13 +460,12 @@
 
     pat = stringify(args[0][0](context, mapping, args[0][1]))
     rpl = stringify(args[1][0](context, mapping, args[1][1]))
-    src = stringify(args[2][0](context, mapping, args[2][1]))
-    src = stringify(runtemplate(context, mapping,
-                                compiletemplate(src, context)))
+    src = stringify(_evalifliteral(args[2], context, mapping))
     yield re.sub(pat, rpl, src)
 
 methods = {
     "string": lambda e, c: (runstring, e[1]),
+    "rawstring": lambda e, c: (runrawstring, e[1]),
     "symbol": lambda e, c: (runsymbol, e[1]),
     "group": lambda e, c: compileexp(e[1], c),
 #    ".": buildmember,
--- a/tests/test-command-template.t	Mon Mar 10 15:00:41 2014 -0500
+++ b/tests/test-command-template.t	Tue Mar 11 16:19:08 2014 -0500
@@ -1633,6 +1633,92 @@
   <>\n<]>
   <>\n<
 
+"string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
+
+  $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
+  \x6e
+  $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
+  \x5c\x786e
+  $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
+  \x6e
+  $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
+  \x5c\x786e
+
+  $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
+  \x6e
+  $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
+  \x5c\x786e
+  $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
+  \x6e
+  $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
+  \x5c\x786e
+
+  $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
+  fourth
+  second
+  third
+  $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
+  fourth\nsecond\nthird
+
+  $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
+  <p>
+  1st
+  </p>
+  <p>
+  2nd
+  </p>
+  $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
+  <p>
+  1st\n\n2nd
+  </p>
+  $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
+  1st
+  
+  2nd
+
+  $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
+  o perso
+  $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
+  no person
+  $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
+  o perso
+  $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
+  no perso
+
+  $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
+  -o perso-
+  $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
+  no person
+  $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
+  \x2do perso\x2d
+  $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
+  -o perso-
+  $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
+  \x2do perso\x6e
+
+  $ hg log -R a -r 8 --template '{files % "{file}\n"}'
+  fourth
+  second
+  third
+  $ hg log -R a -r 8 --template '{files % r"{file}\n"}\n'
+  fourth\nsecond\nthird\n
+
+Test string escapeing in nested expression:
+
+  $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
+  fourth\x6esecond\x6ethird
+  $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
+  fourth\x6esecond\x6ethird
+
+  $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
+  fourth\x6esecond\x6ethird
+  $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
+  fourth\x5c\x786esecond\x5c\x786ethird
+
+  $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
+  3:\x6eo user, \x6eo domai\x6e
+  4:\x5c\x786eew bra\x5c\x786ech
+
 Test recursive evaluation:
 
   $ hg init r
@@ -1645,6 +1731,39 @@
   $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
   test 0
 
+  $ hg branch -q 'text.{rev}'
+  $ echo aa >> aa
+  $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
+
+  $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
+  {node|short}desc to
+  text.{rev}be wrapped
+  text.{rev}desc to be
+  text.{rev}wrapped (no-eol)
+  $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
+  bcc7ff960b8e:desc to
+  text.1:be wrapped
+  text.1:desc to be
+  text.1:wrapped (no-eol)
+
+  $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
+  {node|short} (no-eol)
+  $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
+  bcc-ff---b-e (no-eol)
+
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > color=
+  > [color]
+  > mode=ansi
+  > text.{rev} = red
+  > text.1 = green
+  > EOF
+  $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
+  \x1b[0;31mtext\x1b[0m (esc)
+  $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
+  \x1b[0;32mtext\x1b[0m (esc)
+
 Test branches inside if statement:
 
   $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
@@ -1655,43 +1774,56 @@
   $ echo b > b
   $ hg ci -qAm b
   $ hg log --template '{shortest(node)}\n'
-  d97c
+  e777
+  bcc7
   f776
   $ hg log --template '{shortest(node, 10)}\n'
-  d97c383ae3
+  e777603221
+  bcc7ff960b
   f7769ec2ab
 
 Test pad function
 
   $ hg log --template '{pad(rev, 20)} {author|user}\n'
-  1                    test
+  2                    test
+  1                    {node|short}
   0                    test
 
   $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
-                     1 test
+                     2 test
+                     1 {node|short}
                      0 test
 
   $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
-  1------------------- test
+  2------------------- test
+  1------------------- {node|short}
   0------------------- test
 
 Test ifcontains function
 
   $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
+  2 did not add a
   1 did not add a
   0 added a
 
 Test revset function
 
   $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
-  1 current rev
+  2 current rev
+  1 not current rev
   0 not current rev
 
   $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
+  2 Parents: 1
   1 Parents: 0
   0 Parents: 
 
   $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
+  Rev: 2
+  Ancestor: 0
+  Ancestor: 1
+  Ancestor: 2
+  
   Rev: 1
   Ancestor: 0
   Ancestor: 1
@@ -1704,5 +1836,15 @@
   $ hg book foo
   $ hg book bar
   $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
-  1 bar* foo 
+  2 bar* foo 
+  1 
   0 
+
+Test stringify on sub expressions
+
+  $ cd ..
+  $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
+  fourth, second, third
+  $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
+  abc
+