changeset 36539:638c012a87ef

templater: add option to parse template string just like raw string literal This seems a bit odd because the template syntax has no raw string literal containing template fragments, but is necessary to port filename format string to templater. See the next patch.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 18 Feb 2018 11:53:26 +0900
parents d7a23d6184a2
children aa3294027936
files mercurial/templater.py
diffstat 1 files changed, 19 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/templater.py	Sun Feb 18 10:58:15 2018 +0900
+++ b/mercurial/templater.py	Sun Feb 18 11:53:26 2018 +0900
@@ -178,8 +178,14 @@
             raise error.ProgrammingError('unexpected type: %s' % typ)
     raise error.ProgrammingError('unterminated scanning of template')
 
-def scantemplate(tmpl):
-    """Scan (type, start, end) positions of outermost elements in template
+def scantemplate(tmpl, raw=False):
+    r"""Scan (type, start, end) positions of outermost elements in template
+
+    If raw=True, a backslash is not taken as an escape character just like
+    r'' string in Python. Note that this is different from r'' literal in
+    template in that no template fragment can appear in r'', e.g. r'{foo}'
+    is a literal '{foo}', but ('{foo}', raw=True) is a template expression
+    'foo'.
 
     >>> list(scantemplate(b'foo{bar}"baz'))
     [('string', 0, 3), ('template', 3, 8), ('string', 8, 12)]
@@ -187,9 +193,11 @@
     [('string', 0, 5), ('template', 5, 14), ('string', 14, 19)]
     >>> list(scantemplate(b'foo\\{escaped}'))
     [('string', 0, 5), ('string', 5, 13)]
+    >>> list(scantemplate(b'foo\\{escaped}', raw=True))
+    [('string', 0, 4), ('template', 4, 13)]
     """
     last = None
-    for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl)):
+    for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl), raw=raw):
         if last:
             yield last + (pos,)
         if typ == 'end':
@@ -198,27 +206,30 @@
             last = (typ, pos)
     raise error.ProgrammingError('unterminated scanning of template')
 
-def _scantemplate(tmpl, start, stop, quote=''):
+def _scantemplate(tmpl, start, stop, quote='', raw=False):
     """Parse template string into chunks of strings and template expressions"""
     sepchars = '{' + quote
+    unescape = [parser.unescapestr, pycompat.identity][raw]
     pos = start
     p = parser.parser(elements)
     while pos < stop:
         n = min((tmpl.find(c, pos, stop) for c in sepchars),
                 key=lambda n: (n < 0, n))
         if n < 0:
-            yield ('string', parser.unescapestr(tmpl[pos:stop]), pos)
+            yield ('string', unescape(tmpl[pos:stop]), pos)
             pos = stop
             break
         c = tmpl[n:n + 1]
-        bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
+        bs = 0  # count leading backslashes
+        if not raw:
+            bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
         if bs % 2 == 1:
             # escaped (e.g. '\{', '\\\{', but not '\\{')
-            yield ('string', parser.unescapestr(tmpl[pos:n - 1]) + c, pos)
+            yield ('string', unescape(tmpl[pos:n - 1]) + c, pos)
             pos = n + 1
             continue
         if n > pos:
-            yield ('string', parser.unescapestr(tmpl[pos:n]), pos)
+            yield ('string', unescape(tmpl[pos:n]), pos)
         if c == quote:
             yield ('end', None, n + 1)
             return