comparison mercurial/templater.py @ 36509: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 43e108027b0d
children a16fceb686a7
comparison
equal deleted inserted replaced
36508:d7a23d6184a2 36509:638c012a87ef
176 return parsed, pos 176 return parsed, pos
177 else: 177 else:
178 raise error.ProgrammingError('unexpected type: %s' % typ) 178 raise error.ProgrammingError('unexpected type: %s' % typ)
179 raise error.ProgrammingError('unterminated scanning of template') 179 raise error.ProgrammingError('unterminated scanning of template')
180 180
181 def scantemplate(tmpl): 181 def scantemplate(tmpl, raw=False):
182 """Scan (type, start, end) positions of outermost elements in template 182 r"""Scan (type, start, end) positions of outermost elements in template
183
184 If raw=True, a backslash is not taken as an escape character just like
185 r'' string in Python. Note that this is different from r'' literal in
186 template in that no template fragment can appear in r'', e.g. r'{foo}'
187 is a literal '{foo}', but ('{foo}', raw=True) is a template expression
188 'foo'.
183 189
184 >>> list(scantemplate(b'foo{bar}"baz')) 190 >>> list(scantemplate(b'foo{bar}"baz'))
185 [('string', 0, 3), ('template', 3, 8), ('string', 8, 12)] 191 [('string', 0, 3), ('template', 3, 8), ('string', 8, 12)]
186 >>> list(scantemplate(b'outer{"inner"}outer')) 192 >>> list(scantemplate(b'outer{"inner"}outer'))
187 [('string', 0, 5), ('template', 5, 14), ('string', 14, 19)] 193 [('string', 0, 5), ('template', 5, 14), ('string', 14, 19)]
188 >>> list(scantemplate(b'foo\\{escaped}')) 194 >>> list(scantemplate(b'foo\\{escaped}'))
189 [('string', 0, 5), ('string', 5, 13)] 195 [('string', 0, 5), ('string', 5, 13)]
196 >>> list(scantemplate(b'foo\\{escaped}', raw=True))
197 [('string', 0, 4), ('template', 4, 13)]
190 """ 198 """
191 last = None 199 last = None
192 for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl)): 200 for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl), raw=raw):
193 if last: 201 if last:
194 yield last + (pos,) 202 yield last + (pos,)
195 if typ == 'end': 203 if typ == 'end':
196 return 204 return
197 else: 205 else:
198 last = (typ, pos) 206 last = (typ, pos)
199 raise error.ProgrammingError('unterminated scanning of template') 207 raise error.ProgrammingError('unterminated scanning of template')
200 208
201 def _scantemplate(tmpl, start, stop, quote=''): 209 def _scantemplate(tmpl, start, stop, quote='', raw=False):
202 """Parse template string into chunks of strings and template expressions""" 210 """Parse template string into chunks of strings and template expressions"""
203 sepchars = '{' + quote 211 sepchars = '{' + quote
212 unescape = [parser.unescapestr, pycompat.identity][raw]
204 pos = start 213 pos = start
205 p = parser.parser(elements) 214 p = parser.parser(elements)
206 while pos < stop: 215 while pos < stop:
207 n = min((tmpl.find(c, pos, stop) for c in sepchars), 216 n = min((tmpl.find(c, pos, stop) for c in sepchars),
208 key=lambda n: (n < 0, n)) 217 key=lambda n: (n < 0, n))
209 if n < 0: 218 if n < 0:
210 yield ('string', parser.unescapestr(tmpl[pos:stop]), pos) 219 yield ('string', unescape(tmpl[pos:stop]), pos)
211 pos = stop 220 pos = stop
212 break 221 break
213 c = tmpl[n:n + 1] 222 c = tmpl[n:n + 1]
214 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) 223 bs = 0 # count leading backslashes
224 if not raw:
225 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
215 if bs % 2 == 1: 226 if bs % 2 == 1:
216 # escaped (e.g. '\{', '\\\{', but not '\\{') 227 # escaped (e.g. '\{', '\\\{', but not '\\{')
217 yield ('string', parser.unescapestr(tmpl[pos:n - 1]) + c, pos) 228 yield ('string', unescape(tmpl[pos:n - 1]) + c, pos)
218 pos = n + 1 229 pos = n + 1
219 continue 230 continue
220 if n > pos: 231 if n > pos:
221 yield ('string', parser.unescapestr(tmpl[pos:n]), pos) 232 yield ('string', unescape(tmpl[pos:n]), pos)
222 if c == quote: 233 if c == quote:
223 yield ('end', None, n + 1) 234 yield ('end', None, n + 1)
224 return 235 return
225 236
226 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}')) 237 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))