211 """Parse template string into chunks of strings and template expressions""" |
211 """Parse template string into chunks of strings and template expressions""" |
212 sepchars = '{' + quote |
212 sepchars = '{' + quote |
213 unescape = [parser.unescapestr, pycompat.identity][raw] |
213 unescape = [parser.unescapestr, pycompat.identity][raw] |
214 pos = start |
214 pos = start |
215 p = parser.parser(elements) |
215 p = parser.parser(elements) |
216 while pos < stop: |
216 try: |
217 n = min((tmpl.find(c, pos, stop) for c in sepchars), |
217 while pos < stop: |
218 key=lambda n: (n < 0, n)) |
218 n = min((tmpl.find(c, pos, stop) for c in sepchars), |
219 if n < 0: |
219 key=lambda n: (n < 0, n)) |
220 yield ('string', unescape(tmpl[pos:stop]), pos) |
220 if n < 0: |
221 pos = stop |
221 yield ('string', unescape(tmpl[pos:stop]), pos) |
222 break |
222 pos = stop |
223 c = tmpl[n:n + 1] |
223 break |
224 bs = 0 # count leading backslashes |
224 c = tmpl[n:n + 1] |
225 if not raw: |
225 bs = 0 # count leading backslashes |
226 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) |
226 if not raw: |
227 if bs % 2 == 1: |
227 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) |
228 # escaped (e.g. '\{', '\\\{', but not '\\{') |
228 if bs % 2 == 1: |
229 yield ('string', unescape(tmpl[pos:n - 1]) + c, pos) |
229 # escaped (e.g. '\{', '\\\{', but not '\\{') |
230 pos = n + 1 |
230 yield ('string', unescape(tmpl[pos:n - 1]) + c, pos) |
231 continue |
231 pos = n + 1 |
232 if n > pos: |
232 continue |
233 yield ('string', unescape(tmpl[pos:n]), pos) |
233 if n > pos: |
234 if c == quote: |
234 yield ('string', unescape(tmpl[pos:n]), pos) |
235 yield ('end', None, n + 1) |
235 if c == quote: |
236 return |
236 yield ('end', None, n + 1) |
237 |
237 return |
238 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}')) |
238 |
239 if not tmpl.endswith('}', n + 1, pos): |
239 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}')) |
240 raise error.ParseError(_("invalid token"), pos) |
240 if not tmpl.endswith('}', n + 1, pos): |
241 yield ('template', parseres, n) |
241 raise error.ParseError(_("invalid token"), pos) |
242 |
242 yield ('template', parseres, n) |
243 if quote: |
243 |
244 raise error.ParseError(_("unterminated string"), start) |
244 if quote: |
|
245 raise error.ParseError(_("unterminated string"), start) |
|
246 except error.ParseError as inst: |
|
247 if len(inst.args) > 1: # has location |
|
248 loc = inst.args[1] |
|
249 # TODO: Opportunity for improvement! If there is a newline in the |
|
250 # template, this hint does not point to the right place, so skip. |
|
251 if '\n' not in tmpl: |
|
252 # We want the caret to point to the place in the template that |
|
253 # failed to parse, but in a hint we get a open paren at the |
|
254 # start. Therefore, we print "loc" spaces (instead of "loc - 1") |
|
255 # to line up the caret with the location of the error. |
|
256 inst.hint = tmpl + '\n' + ' ' * (loc) + '^ ' + _('here') |
|
257 raise |
245 yield ('end', None, pos) |
258 yield ('end', None, pos) |
246 |
259 |
247 def _unnesttemplatelist(tree): |
260 def _unnesttemplatelist(tree): |
248 """Expand list of templates to node tuple |
261 """Expand list of templates to node tuple |
249 |
262 |