mercurial/templater.py
changeset 43098 9691fc764bdc
parent 43089 c59eb1560c44
child 43106 d783f945a701
equal deleted inserted replaced
43097:27c4f93d07a9 43098:9691fc764bdc
   298             pos += 1
   298             pos += 1
   299 
   299 
   300         if quote:
   300         if quote:
   301             raise error.ParseError(_(b"unterminated string"), start)
   301             raise error.ParseError(_(b"unterminated string"), start)
   302     except error.ParseError as inst:
   302     except error.ParseError as inst:
   303         if len(inst.args) > 1:  # has location
   303         _addparseerrorhint(inst, tmpl)
   304             loc = inst.args[1]
       
   305             # Offset the caret location by the number of newlines before the
       
   306             # location of the error, since we will replace one-char newlines
       
   307             # with the two-char literal r'\n'.
       
   308             offset = tmpl[:loc].count(b'\n')
       
   309             tmpl = tmpl.replace(b'\n', br'\n')
       
   310             # We want the caret to point to the place in the template that
       
   311             # failed to parse, but in a hint we get a open paren at the
       
   312             # start. Therefore, we print "loc + 1" spaces (instead of "loc")
       
   313             # to line up the caret with the location of the error.
       
   314             inst.hint = (
       
   315                 tmpl + b'\n' + b' ' * (loc + 1 + offset) + b'^ ' + _(b'here')
       
   316             )
       
   317         raise
   304         raise
   318     yield (b'end', None, pos)
   305     yield (b'end', None, pos)
       
   306 
       
   307 
       
   308 def _addparseerrorhint(inst, tmpl):
       
   309     if len(inst.args) <= 1:
       
   310         return  # no location
       
   311     loc = inst.args[1]
       
   312     # Offset the caret location by the number of newlines before the
       
   313     # location of the error, since we will replace one-char newlines
       
   314     # with the two-char literal r'\n'.
       
   315     offset = tmpl[:loc].count(b'\n')
       
   316     tmpl = tmpl.replace(b'\n', br'\n')
       
   317     # We want the caret to point to the place in the template that
       
   318     # failed to parse, but in a hint we get a open paren at the
       
   319     # start. Therefore, we print "loc + 1" spaces (instead of "loc")
       
   320     # to line up the caret with the location of the error.
       
   321     inst.hint = tmpl + b'\n' + b' ' * (loc + 1 + offset) + b'^ ' + _(b'here')
   319 
   322 
   320 
   323 
   321 def _unnesttemplatelist(tree):
   324 def _unnesttemplatelist(tree):
   322     """Expand list of templates to node tuple
   325     """Expand list of templates to node tuple
   323 
   326 
   358     parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
   361     parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
   359     assert pos == len(tmpl), b'unquoted template should be consumed'
   362     assert pos == len(tmpl), b'unquoted template should be consumed'
   360     return _unnesttemplatelist((b'template', parsed))
   363     return _unnesttemplatelist((b'template', parsed))
   361 
   364 
   362 
   365 
   363 def _parseexpr(expr):
   366 def parseexpr(expr):
   364     """Parse a template expression into tree
   367     """Parse a template expression into tree
   365 
   368 
   366     >>> _parseexpr(b'"foo"')
   369     >>> parseexpr(b'"foo"')
   367     ('string', 'foo')
   370     ('string', 'foo')
   368     >>> _parseexpr(b'foo(bar)')
   371     >>> parseexpr(b'foo(bar)')
   369     ('func', ('symbol', 'foo'), ('symbol', 'bar'))
   372     ('func', ('symbol', 'foo'), ('symbol', 'bar'))
   370     >>> _parseexpr(b'foo(')
   373     >>> parseexpr(b'foo(')
   371     Traceback (most recent call last):
   374     Traceback (most recent call last):
   372       ...
   375       ...
   373     ParseError: ('not a prefix: end', 4)
   376     ParseError: ('not a prefix: end', 4)
   374     >>> _parseexpr(b'"foo" "bar"')
   377     >>> parseexpr(b'"foo" "bar"')
   375     Traceback (most recent call last):
   378     Traceback (most recent call last):
   376       ...
   379       ...
   377     ParseError: ('invalid token', 7)
   380     ParseError: ('invalid token', 7)
   378     """
   381     """
       
   382     try:
       
   383         return _parseexpr(expr)
       
   384     except error.ParseError as inst:
       
   385         _addparseerrorhint(inst, expr)
       
   386         raise
       
   387 
       
   388 
       
   389 def _parseexpr(expr):
   379     p = parser.parser(elements)
   390     p = parser.parser(elements)
   380     tree, pos = p.parse(tokenize(expr, 0, len(expr)))
   391     tree, pos = p.parse(tokenize(expr, 0, len(expr)))
   381     if pos != len(expr):
   392     if pos != len(expr):
   382         raise error.ParseError(_(b'invalid token'), pos)
   393         raise error.ParseError(_(b'invalid token'), pos)
   383     return _unnesttemplatelist(tree)
   394     return _unnesttemplatelist(tree)