Mercurial > hg
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, '}')) |