##// END OF EJS Templates
run-tests: python3.5 now supports mkdtemp using bytes for paths...
run-tests: python3.5 now supports mkdtemp using bytes for paths Now that http://bugs.python.org/issue24230 is fixed (thanks to Gregory Smith for that quick response!) we can drop one more ugly hack around path handling. Tests still pass in 3.5 with this cleaner version, as well as in 2.6.

File last commit:

r25096:081b08e4 default
r25262:5a809deb default
Show More
templater.py
844 lines | 27.5 KiB | text/x-python | PythonLexer
Vadim Gelfer
add doc comments to template code.
r1909 # templater.py - template expansion for output
#
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Vadim Gelfer
add doc comments to template code.
r1909
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Mads Kiilerich
util: introduce datapath for getting the location of supporting data files...
r22633 import os, re
Durham Goode
template: add revset() template function...
r20519 import util, config, templatefilters, templatekw, parser, error
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 import revset as revsetmod
Weiwen
template engine: convert generator-based iterator to list-based iterator...
r17982 import types
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 import minirst
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
# template parsing
elements = {
"(": (20, ("group", 1, ")"), ("func", 1, ")")),
",": (2, None, ("list", 2)),
"|": (5, None, ("|", 5)),
"%": (6, None, ("%", 6)),
")": (0, None, None),
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 "integer": (0, ("integer",), None),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 "symbol": (0, ("symbol",), None),
"string": (0, ("string",), None),
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 "rawstring": (0, ("rawstring",), None),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 "end": (0, None, None),
}
def tokenizer(data):
program, start, end = data
pos = start
while pos < end:
c = program[pos]
if c.isspace(): # skip inter-token whitespace
pass
elif c in "(,)%|": # handle simple operators
yield (c, None, pos)
elif (c in '"\'' or c == 'r' and
program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
if c == 'r':
pos += 1
c = program[pos]
Matt Mackall
templater: correctly deal with r"" strings
r17634 decode = False
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 else:
Matt Mackall
templater: correctly deal with r"" strings
r17634 decode = True
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos += 1
s = pos
while pos < end: # find closing quote
d = program[pos]
Matt Mackall
templater: correctly deal with r"" strings
r17634 if decode and d == '\\': # skip over escaped characters
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos += 2
continue
if d == c:
Matt Mackall
templater: correctly deal with r"" strings
r17634 if not decode:
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 yield ('rawstring', program[s:pos], s)
Matt Mackall
templater: correctly deal with r"" strings
r17634 break
Matt Mackall
templater: fix escaping in nested string literals (issue4102)...
r20066 yield ('string', program[s:pos], s)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 break
pos += 1
else:
raise error.ParseError(_("unterminated string"), s)
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 elif c.isdigit() or c == '-':
s = pos
if c == '-': # simply take negate operator as part of integer
pos += 1
if pos >= end or not program[pos].isdigit():
raise error.ParseError(_("integer literal without digits"), s)
pos += 1
while pos < end:
d = program[pos]
if not d.isdigit():
break
pos += 1
yield ('integer', program[s:pos], s)
pos -= 1
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 elif c.isalnum() or c in '_':
s = pos
pos += 1
while pos < end: # find end of symbol
d = program[pos]
if not (d.isalnum() or d == "_"):
break
pos += 1
sym = program[s:pos]
Brendan Cully
templater: back out 0615b22da148, it breaks schemes ({1})
r18893 yield ('symbol', sym, s)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos -= 1
elif c == '}':
pos += 1
break
else:
raise error.ParseError(_("syntax error"), pos)
pos += 1
yield ('end', None, pos)
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 def compiletemplate(tmpl, context, strtoken="string"):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 parsed = []
pos, stop = 0, len(tmpl)
p = parser.parser(tokenizer, elements)
while pos < stop:
n = tmpl.find('{', pos)
if n < 0:
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 parsed.append((strtoken, tmpl[pos:]))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 break
Yuya Nishihara
templater: strictly parse leading backslashes of '{' (issue4569) (BC)...
r24949 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
if strtoken == 'string' and bs % 2 == 1:
# escaped (e.g. '\{', '\\\{', but not '\\{' nor r'\{')
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos = n + 1
continue
if n > pos:
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 parsed.append((strtoken, tmpl[pos:n]))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
pd = [tmpl, n + 1, stop]
Bernhard Leiner
revset: report a parse error if a revset is not parsed completely (issue2654)
r13665 parseres, pos = p.parse(pd)
parsed.append(parseres)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 return [compileexp(e, context, methods) for e in parsed]
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 def compileexp(exp, context, curmethods):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 t = exp[0]
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 if t in curmethods:
return curmethods[t](exp, context)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 raise error.ParseError(_("unknown method '%s'") % t)
# template evaluation
def getsymbol(exp):
if exp[0] == 'symbol':
return exp[1]
Ryan McElroy
templater: add symbol to error...
r21822 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
def getlist(x):
if not x:
return []
if x[0] == 'list':
return getlist(x[1]) + [x[2]]
return [x]
def getfilter(exp, context):
f = getsymbol(exp)
if f not in context._filters:
raise error.ParseError(_("unknown function '%s'") % f)
return context._filters[f]
def gettemplate(exp, context):
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 if exp[0] == 'string' or exp[0] == 'rawstring':
return compiletemplate(exp[1], context, strtoken=exp[0])
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if exp[0] == 'symbol':
return context._load(exp[1])
raise error.ParseError(_("expected template specifier"))
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 def runinteger(context, mapping, data):
return int(data)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def runstring(context, mapping, data):
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 return data.decode("string-escape")
def runrawstring(context, mapping, data):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return data
def runsymbol(context, mapping, key):
v = mapping.get(key)
if v is None:
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 v = context._defaults.get(key)
if v is None:
try:
v = context.process(key, mapping)
except TemplateNotFound:
v = ''
Augie Fackler
templater: restore use of callable() since it was readded in Python 3.2
r21798 if callable(v):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return v(**mapping)
Weiwen
template engine: convert generator-based iterator to list-based iterator...
r17982 if isinstance(v, types.GeneratorType):
v = list(v)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return v
def buildfilter(exp, context):
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 func, data = compileexp(exp[1], context, methods)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 filt = getfilter(exp[2], context)
return (runfilter, (func, data, filt))
def runfilter(context, mapping, data):
func, data, filt = data
Yuya Nishihara
templater: allow piping generator-type function output to filters...
r24280 # func() may return string, generator of strings or arbitrary object such
# as date tuple, but filter does not want generator.
thing = func(context, mapping, data)
if isinstance(thing, types.GeneratorType):
thing = stringify(thing)
Neil Kodner
templater: abort when a template filter raises an exception (issue2987)
r17383 try:
Yuya Nishihara
templater: allow piping generator-type function output to filters...
r24280 return filt(thing)
Neil Kodner
templater: abort when a template filter raises an exception (issue2987)
r17383 except (ValueError, AttributeError, TypeError):
if isinstance(data, tuple):
dt = data[1]
else:
dt = data
raise util.Abort(_("template filter '%s' is not compatible with "
"keyword '%s'") % (filt.func_name, dt))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
def buildmap(exp, context):
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 func, data = compileexp(exp[1], context, methods)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 ctmpl = gettemplate(exp[2], context)
return (runmap, (func, data, ctmpl))
Matt Mackall
templater: factor out runtemplate method...
r17632 def runtemplate(context, mapping, template):
for func, data in template:
yield func(context, mapping, data)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def runmap(context, mapping, data):
func, data, ctmpl = data
d = func(context, mapping, data)
Augie Fackler
templater: restore use of callable() since it was readded in Python 3.2
r21798 if callable(d):
Matt Mackall
templating: make new-style templating features work with command line lists
r17631 d = d()
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 lm = mapping.copy()
for i in d:
if isinstance(i, dict):
lm.update(i)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 lm['originalnode'] = mapping.get('node')
Matt Mackall
templater: factor out runtemplate method...
r17632 yield runtemplate(context, lm, ctmpl)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 else:
# v is not an iterable of dicts, this happen when 'key'
# has been fully expanded already and format is useless.
# If so, return the expanded value.
yield i
def buildfunc(exp, context):
n = getsymbol(exp[1])
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
Matt Mackall
templater: use a global funcs table
r14925 if n in funcs:
f = funcs[n]
return (f, args)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if n in context._filters:
if len(args) != 1:
raise error.ParseError(_("filter %s expects one argument") % n)
f = context._filters[n]
return (runfilter, (args[0][0], args[0][1], f))
Sean Farley
templater: raise error for unknown func...
r20857 raise error.ParseError(_("unknown function '%s'") % n)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def date(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
strings."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if not (1 <= len(args) <= 2):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "date" is a keyword
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 raise error.ParseError(_("date expects one or two arguments"))
date = args[0][0](context, mapping, args[0][1])
Yuya Nishihara
templater: fix crash by passing invalid object to date() function...
r24903 fmt = None
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if len(args) == 2:
fmt = stringify(args[1][0](context, mapping, args[1][1]))
Yuya Nishihara
templater: fix crash by passing invalid object to date() function...
r24903 try:
if fmt is None:
return util.datestr(date)
else:
return util.datestr(date, fmt)
except (TypeError, ValueError):
# i18n: "date" is a keyword
raise error.ParseError(_("date expects a date information"))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 def diff(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
specifying files to include or exclude."""
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 if len(args) > 2:
# i18n: "diff" is a keyword
raise error.ParseError(_("diff expects one, two or no arguments"))
def getpatterns(i):
if i < len(args):
s = args[i][1].strip()
if s:
return [s]
return []
ctx = mapping['ctx']
chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
return ''.join(chunks)
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def fill(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
paragraphs with optional indentation. See the "fill" filter."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if not (1 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "fill" is a keyword
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 raise error.ParseError(_("fill expects one to four arguments"))
text = stringify(args[0][0](context, mapping, args[0][1]))
width = 76
initindent = ''
hangindent = ''
if 2 <= len(args) <= 4:
try:
width = int(stringify(args[1][0](context, mapping, args[1][1])))
except ValueError:
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "fill" is a keyword
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 raise error.ParseError(_("fill expects an integer width"))
try:
FUJIWARA Katsunori
templater: avoid recursive evaluation of string literals completely...
r20661 initindent = stringify(_evalifliteral(args[2], context, mapping))
hangindent = stringify(_evalifliteral(args[3], context, mapping))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 except IndexError:
pass
return templatefilters.fill(text, width, initindent, hangindent)
Durham Goode
template: add pad function for padding output...
r20370 def pad(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
fill character."""
Durham Goode
template: add pad function for padding output...
r20370 if not (2 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "pad" is a keyword
Durham Goode
template: add pad function for padding output...
r20370 raise error.ParseError(_("pad() expects two to four arguments"))
width = int(args[1][1])
text = stringify(args[0][0](context, mapping, args[0][1]))
if args[0][0] == runstring:
text = stringify(runtemplate(context, mapping,
compiletemplate(text, context)))
right = False
fillchar = ' '
if len(args) > 2:
fillchar = stringify(args[2][0](context, mapping, args[2][1]))
if len(args) > 3:
right = util.parsebool(args[3][1])
if right:
return text.rjust(width, fillchar)
else:
return text.ljust(width, fillchar)
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 def get(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:get(dict, key): Get an attribute/key from an object. Some keywords
are complex types. This function allows you to obtain the value of an
attribute on these type."""
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 if len(args) != 2:
# i18n: "get" is a keyword
raise error.ParseError(_("get() expects two arguments"))
dictarg = args[0][0](context, mapping, args[0][1])
if not util.safehasattr(dictarg, 'get'):
# i18n: "get" is a keyword
raise error.ParseError(_("get() expects a dict as first argument"))
key = args[1][0](context, mapping, args[1][1])
yield dictarg.get(key)
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 def _evalifliteral(arg, context, mapping):
t = stringify(arg[0](context, mapping, arg[1]))
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 if arg[0] == runstring or arg[0] == runrawstring:
yield runtemplate(context, mapping,
compiletemplate(t, context, strtoken='rawstring'))
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 else:
yield t
Matt Mackall
templater: add if/ifeq conditionals
r17636 def if_(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:if(expr, then[, else]): Conditionally execute based on the result of
an expression."""
Matt Mackall
templater: add if/ifeq conditionals
r17636 if not (2 <= len(args) <= 3):
FUJIWARA Katsunori
i18n: add "i18n" comment to error messages of template functions
r17890 # i18n: "if" is a keyword
Matt Mackall
templater: add if/ifeq conditionals
r17636 raise error.ParseError(_("if expects two or three arguments"))
test = stringify(args[0][0](context, mapping, args[0][1]))
if test:
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 yield _evalifliteral(args[1], context, mapping)
Matt Mackall
templater: add if/ifeq conditionals
r17636 elif len(args) == 3:
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 yield _evalifliteral(args[2], context, mapping)
Matt Mackall
templater: add if/ifeq conditionals
r17636
Durham Goode
template: add ifcontains template function...
r20518 def ifcontains(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:ifcontains(search, thing, then[, else]): Conditionally execute based
on whether the item "search" is in "thing"."""
Durham Goode
template: add ifcontains template function...
r20518 if not (3 <= len(args) <= 4):
# i18n: "ifcontains" is a keyword
raise error.ParseError(_("ifcontains expects three or four arguments"))
item = stringify(args[0][0](context, mapping, args[0][1]))
items = args[1][0](context, mapping, args[1][1])
Yuya Nishihara
templater: implement _hybrid.__contains__ so that ifcontains can accept dict...
r24240 if item in items:
Durham Goode
template: add ifcontains template function...
r20518 yield _evalifliteral(args[2], context, mapping)
elif len(args) == 4:
yield _evalifliteral(args[3], context, mapping)
Matt Mackall
templater: add if/ifeq conditionals
r17636 def ifeq(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
whether 2 items are equivalent."""
Matt Mackall
templater: add if/ifeq conditionals
r17636 if not (3 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add "i18n" comment to error messages of template functions
r17890 # i18n: "ifeq" is a keyword
Matt Mackall
templater: add if/ifeq conditionals
r17636 raise error.ParseError(_("ifeq expects three or four arguments"))
test = stringify(args[0][0](context, mapping, args[0][1]))
match = stringify(args[1][0](context, mapping, args[1][1]))
if test == match:
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 yield _evalifliteral(args[2], context, mapping)
Matt Mackall
templater: add if/ifeq conditionals
r17636 elif len(args) == 4:
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 yield _evalifliteral(args[3], context, mapping)
Matt Mackall
templater: add if/ifeq conditionals
r17636
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def join(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:join(list, sep): Join items in a list with a delimiter."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if not (1 <= len(args) <= 2):
# i18n: "join" is a keyword
raise error.ParseError(_("join expects one or two arguments"))
joinset = args[0][0](context, mapping, args[0][1])
Augie Fackler
templater: restore use of callable() since it was readded in Python 3.2
r21798 if callable(joinset):
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 jf = joinset.joinfmt
joinset = [jf(x) for x in joinset()]
joiner = " "
if len(args) > 1:
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 joiner = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
first = True
for x in joinset:
if first:
first = False
else:
yield joiner
yield x
Sean Farley
templater: add no-op template function 'label'
r18289 def label(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:label(label, expr): Apply a label to generated content. Content with
a label applied can result in additional post-processing, such as
automatic colorization."""
Sean Farley
templater: add no-op template function 'label'
r18289 if len(args) != 2:
# i18n: "label" is a keyword
raise error.ParseError(_("label expects two arguments"))
# ignore args[0] (the label string) since this is supposed to be a a no-op
Matt Mackall
templater: only recursively evaluate string literals as templates (issue4103)
r20067 yield _evalifliteral(args[1], context, mapping)
Sean Farley
templater: add no-op template function 'label'
r18289
Durham Goode
template: add revset() template function...
r20519 def revset(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:revset(query[, formatargs...]): Execute a revision set query. See
:hg:`help revset`."""
Durham Goode
template: add revset() template function...
r20519 if not len(args) > 0:
# i18n: "revset" is a keyword
raise error.ParseError(_("revset expects one or more arguments"))
raw = args[0][1]
ctx = mapping['ctx']
Matt Harbison
templater: replace 'ctx._repo' with 'ctx.repo()'
r24337 repo = ctx.repo()
Durham Goode
template: add revset() template function...
r20519
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 def query(expr):
m = revsetmod.match(repo.ui, expr)
Yuya Nishihara
revset: make match function initiate query from full set by default...
r24114 return m(repo)
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304
Durham Goode
template: add revset() template function...
r20519 if len(args) > 1:
formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 revs = query(revsetmod.formatspec(raw, *formatargs))
Durham Goode
template: add revset() template function...
r20519 revs = list([str(r) for r in revs])
else:
revsetcache = mapping['cache'].setdefault("revsetcache", {})
if raw in revsetcache:
revs = revsetcache[raw]
else:
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 revs = query(raw)
Durham Goode
template: add revset() template function...
r20519 revs = list([str(r) for r in revs])
revsetcache[raw] = revs
return templatekw.showlist("revision", revs, **mapping)
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 def rstdoc(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:rstdoc(text, style): Format ReStructuredText."""
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 if len(args) != 2:
# i18n: "rstdoc" is a keyword
raise error.ParseError(_("rstdoc expects two arguments"))
text = stringify(args[0][0](context, mapping, args[0][1]))
style = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
hgweb: make help verbose again (issue3899)...
r19079 return minirst.format(text, style=style, keep=['verbose'])
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747
Durham Goode
template: add shortest(node) template function...
r20369 def shortest(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:shortest(node, minlength=4): Obtain the shortest representation of
a node."""
Durham Goode
template: add shortest(node) template function...
r20369 if not (1 <= len(args) <= 2):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "shortest" is a keyword
Durham Goode
template: add shortest(node) template function...
r20369 raise error.ParseError(_("shortest() expects one or two arguments"))
node = stringify(args[0][0](context, mapping, args[0][1]))
minlength = 4
if len(args) > 1:
minlength = int(args[1][1])
cl = mapping['ctx']._repo.changelog
def isvalid(test):
try:
Durham Goode
template: fix shortest(node) function in pure mercurial...
r20371 try:
cl.index.partialmatch(test)
except AttributeError:
# Pure mercurial doesn't support partialmatch on the index.
# Fallback to the slow way.
if cl._partialmatch(test) is None:
return False
Durham Goode
template: add shortest(node) template function...
r20369 try:
Sean Farley
templater: shorten pure integers...
r20539 i = int(test)
# if we are a pure int, then starting with zero will not be
# confused as a rev; or, obviously, if the int is larger than
# the value of the tip rev
if test[0] == '0' or i > len(cl):
return True
Durham Goode
template: add shortest(node) template function...
r20369 return False
except ValueError:
return True
except error.RevlogError:
return False
shortest = node
startlength = max(6, minlength)
length = startlength
while True:
test = node[:length]
if isvalid(test):
shortest = test
if length == minlength or length > startlength:
return shortest
length -= 1
else:
length += 1
if len(shortest) <= length:
return shortest
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 def strip(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:strip(text[, chars]): Strip characters from a string."""
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 if not (1 <= len(args) <= 2):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "strip" is a keyword
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 raise error.ParseError(_("strip expects one or two arguments"))
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 text = stringify(args[0][0](context, mapping, args[0][1]))
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 if len(args) == 2:
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 chars = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 return text.strip(chars)
return text.strip()
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def sub(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:sub(pattern, replacement, expression): Perform text substitution
using regular expressions."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if len(args) != 3:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub expects three arguments"))
pat = stringify(args[0][0](context, mapping, args[0][1]))
rpl = stringify(args[1][0](context, mapping, args[1][1]))
FUJIWARA Katsunori
templater: avoid recursive evaluation of string literals completely...
r20661 src = stringify(_evalifliteral(args[2], context, mapping))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 yield re.sub(pat, rpl, src)
Ryan McElroy
templater: introduce startswith function...
r21821 def startswith(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:startswith(pattern, text): Returns the value from the "text" argument
if it begins with the content from the "pattern" argument."""
Ryan McElroy
templater: introduce startswith function...
r21821 if len(args) != 2:
FUJIWARA Katsunori
templater: add i18n comments to error messages of newly added functions...
r21960 # i18n: "startswith" is a keyword
Ryan McElroy
templater: introduce startswith function...
r21821 raise error.ParseError(_("startswith expects two arguments"))
patn = stringify(args[0][0](context, mapping, args[0][1]))
text = stringify(args[1][0](context, mapping, args[1][1]))
if text.startswith(patn):
return text
return ''
Ryan McElroy
templater: introduce word function...
r21846 def word(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:word(number, text[, separator]): Return the nth word from a string."""
Ryan McElroy
templater: introduce word function...
r21846 if not (2 <= len(args) <= 3):
FUJIWARA Katsunori
templater: add i18n comments to error messages of newly added functions...
r21960 # i18n: "word" is a keyword
Ryan McElroy
templater: introduce word function...
r21846 raise error.ParseError(_("word expects two or three arguments, got %d")
% len(args))
Ryan McElroy
templater: fail more gracefully for blank strings to word
r24886 try:
num = int(stringify(args[0][0](context, mapping, args[0][1])))
except ValueError:
# i18n: "word" is a keyword
Yuya Nishihara
templater: update error message of invalid number passed to word() function...
r25003 raise error.ParseError(_("word expects an integer index"))
Ryan McElroy
templater: introduce word function...
r21846 text = stringify(args[1][0](context, mapping, args[1][1]))
if len(args) == 3:
splitter = stringify(args[2][0](context, mapping, args[2][1]))
else:
splitter = None
tokens = text.split(splitter)
if num >= len(tokens):
return ''
else:
return tokens[num]
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
exprmethods = {
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 "integer": lambda e, c: (runinteger, e[1]),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 "string": lambda e, c: (runstring, e[1]),
FUJIWARA Katsunori
templater: make strings in template expressions be "string-escape"-ed correctly...
r20663 "rawstring": lambda e, c: (runrawstring, e[1]),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 "symbol": lambda e, c: (runsymbol, e[1]),
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 "group": lambda e, c: compileexp(e[1], c, exprmethods),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # ".": buildmember,
"|": buildfilter,
"%": buildmap,
"func": buildfunc,
}
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
methods = exprmethods.copy()
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001
Matt Mackall
templater: use a global funcs table
r14925 funcs = {
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 "date": date,
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 "diff": diff,
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 "fill": fill,
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 "get": get,
Matt Mackall
templater: add if/ifeq conditionals
r17636 "if": if_,
Durham Goode
template: add ifcontains template function...
r20518 "ifcontains": ifcontains,
Matt Mackall
templater: add if/ifeq conditionals
r17636 "ifeq": ifeq,
Matt Mackall
template: add join function...
r17633 "join": join,
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 "label": label,
Durham Goode
template: add pad function for padding output...
r20370 "pad": pad,
Durham Goode
template: add revset() template function...
r20519 "revset": revset,
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 "rstdoc": rstdoc,
Durham Goode
template: add shortest(node) template function...
r20369 "shortest": shortest,
Ryan McElroy
templater: introduce startswith function...
r21821 "startswith": startswith,
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 "strip": strip,
Matt Mackall
templater: add sub() function
r17635 "sub": sub,
Ryan McElroy
templater: introduce word function...
r21846 "word": word,
Matt Mackall
templater: use a global funcs table
r14925 }
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # template engine
Vadim Gelfer
use safer string parser for template engine.
r1901
Dirkjan Ochtman
templater: provide the standard template filters by default
r8360 stringify = templatefilters.stringify
Brendan Cully
Allow hgweb to search for templates in more than one path....
r7107
Matt Mackall
templater: use recursive flattening...
r10850 def _flatten(thing):
'''yield a single stream from a possibly nested set of iterators'''
if isinstance(thing, str):
yield thing
Augie Fackler
globally: use safehasattr(x, '__iter__') instead of hasattr(x, '__iter__')
r14944 elif not util.safehasattr(thing, '__iter__'):
Dirkjan Ochtman
cleanups: undefined variables
r11305 if thing is not None:
Matt Mackall
templater: raise nested functions
r10852 yield str(thing)
else:
Matt Mackall
templater: use recursive flattening...
r10850 for i in thing:
if isinstance(i, str):
yield i
Augie Fackler
globally: use safehasattr(x, '__iter__') instead of hasattr(x, '__iter__')
r14944 elif not util.safehasattr(i, '__iter__'):
Matt Mackall
templater: raise nested functions
r10852 if i is not None:
yield str(i)
Matt Mackall
templater: use recursive flattening...
r10850 elif i is not None:
for j in _flatten(i):
yield j
Yuya Nishihara
templater: rename parsestring() to unquotestring() (API)...
r24988 def unquotestring(s):
Yuya Nishihara
templater: remove noop calls of parsestring(s, quoted=False) (API)...
r24987 '''unwrap quotes'''
if len(s) < 2 or s[0] != s[-1]:
raise SyntaxError(_('unmatched quotes'))
# de-backslash-ify only <\">. it is invalid syntax in non-string part of
# template, but we are likely to escape <"> in quoted string and it was
# accepted before, thanks to issue4290. <\\"> is unmodified because it
# is ambiguous and it was processed as such before 2.8.1.
#
# template result
# --------- ------------------------
# {\"\"} parse error
# "{""}" {""} -> <>
# "{\"\"}" {""} -> <>
# {"\""} {"\""} -> <">
# '{"\""}' {"\""} -> <">
# "{"\""}" parse error (don't care)
q = s[0]
return s[1:-1].replace('\\\\' + q, '\\\\\\' + q).replace('\\' + q, q)
Vadim Gelfer
move hgweb template code out to templater
r1896
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 class engine(object):
Vadim Gelfer
add doc comments to template code.
r1909 '''template expansion engine.
template expansion works like this. a map file contains key=value
pairs. if value is quoted, it is treated as string. otherwise, it
is treated as name of template file.
templater is asked to expand a key in map. it looks up key, and
TK Soh
minor typo fix in templater's docstring
r4334 looks for strings like this: {foo}. it expands {foo} by looking up
Vadim Gelfer
add doc comments to template code.
r1909 foo in map, and substituting it. expansion is recursive: it stops
when there is no more {foo} to replace.
expansion also allows formatting and filtering.
format uses key to expand each item in list. syntax is
{key%format}.
filter uses function to transform value. syntax is
{key|filter1|filter2|...}.'''
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 def __init__(self, loader, filters={}, defaults={}):
Matt Mackall
templater: privatize class variables
r10848 self._loader = loader
self._filters = filters
self._defaults = defaults
self._cache = {}
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def _load(self, t):
'''load, parse, and cache a template'''
if t not in self._cache:
self._cache[t] = compiletemplate(self._loader(t), self)
return self._cache[t]
Matt Mackall
templater: drop raw method
r10853 def process(self, t, mapping):
'''Perform expansion. t is name of map element to expand.
mapping contains added elements for use during expansion. Is a
generator.'''
Matt Mackall
templater: factor out runtemplate method...
r17632 return _flatten(runtemplate(self, mapping, self._load(t)))
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361 engines = {'default': engine}
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 def stylelist():
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 paths = templatepaths()
Simon Heimberg
templater: selecting a style with no templates does not crash (issue4140)...
r20312 if not paths:
return _('no templates found, try `hg debuginstall` for more info')
dirlist = os.listdir(paths[0])
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 stylelist = []
for file in dirlist:
split = file.split(".")
if split[0] == "map-cmdline":
stylelist.append(split[1])
Augie Fackler
templater: fix output instability from gsoc patches
r19127 return ", ".join(sorted(stylelist))
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 class TemplateNotFound(util.Abort):
pass
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 class templater(object):
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 def __init__(self, mapfile, filters={}, defaults={}, cache={},
minchunk=1024, maxchunk=65536):
Vadim Gelfer
add doc comments to template code.
r1909 '''set up template engine.
mapfile is name of file to read map definitions from.
filters is dict of functions. each transforms a value into another.
defaults is dict of default map definitions.'''
Vadim Gelfer
improve template errors when something is wrong.
r1905 self.mapfile = mapfile or 'template'
Shun-ichi Goto
Duplicate cache when creating templater.
r1975 self.cache = cache.copy()
Vadim Gelfer
move hgweb template code out to templater
r1896 self.map = {}
Jordi GutiƩrrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if mapfile:
self.base = os.path.dirname(mapfile)
else:
self.base = ''
Dirkjan Ochtman
templater: provide the standard template filters by default
r8360 self.filters = templatefilters.filters.copy()
self.filters.update(filters)
Vadim Gelfer
fix template bug that made hgweb break....
r1964 self.defaults = defaults
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 self.minchunk, self.maxchunk = minchunk, maxchunk
Matt Mackall
templater: clarify engine caching
r13187 self.ecache = {}
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
improve template errors when something is wrong.
r1905 if not mapfile:
return
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337 if not os.path.exists(mapfile):
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 raise util.Abort(_("style '%s' not found") % mapfile,
hint=_("available styles: %s") % stylelist())
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337
Jordi GutiƩrrez Hermoso
templater: look for mapfiles in template paths...
r25096 conf = config.config(includepaths=templatepaths())
Matt Mackall
templater: use new config parser...
r8194 conf.read(mapfile)
for key, val in conf[''].items():
Ross Lagerwall
templater: handle a missing value correctly...
r17334 if not val:
raise SyntaxError(_('%s: missing value') % conf.source('', key))
Matt Mackall
templater: use new config parser...
r8194 if val[0] in "'\"":
try:
Yuya Nishihara
templater: rename parsestring() to unquotestring() (API)...
r24988 self.cache[key] = unquotestring(val)
Matt Mackall
templater: use new config parser...
r8194 except SyntaxError, inst:
raise SyntaxError('%s: %s' %
Matt Mackall
config: getsource -> source
r8198 (conf.source('', key), inst.args[0]))
Vadim Gelfer
move hgweb template code out to templater
r1896 else:
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361 val = 'default', val
if ':' in val[1]:
val = val[1].split(':', 1)
self.map[key] = val[0], os.path.join(self.base, val[1])
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
many small changes to templater....
r1899 def __contains__(self, key):
Matt Mackall
templater: simplify cache and remove filter argument in __call__
r3637 return key in self.cache or key in self.map
Vadim Gelfer
many small changes to templater....
r1899
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 def load(self, t):
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 '''Get the template for the given template name. Use a local cache.'''
Brodie Rao
cleanup: "not x in y" -> "x not in y"
r16686 if t not in self.cache:
Vadim Gelfer
improve template errors when something is wrong.
r1905 try:
Dan Villiom Podlaski Christiansen
prevent transient leaks of file handle by using new helper functions...
r14168 self.cache[t] = util.readfile(self.map[t][1])
Matt Mackall
templater: give slightly nicer error for unknown map entries
r13175 except KeyError, inst:
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 raise TemplateNotFound(_('"%s" not in template map') %
inst.args[0])
Vadim Gelfer
improve template errors when something is wrong.
r1905 except IOError, inst:
raise IOError(inst.args[0], _('template file %s: %s') %
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361 (self.map[t][1], inst.args[1]))
Dirkjan Ochtman
templater: make a template a string-only iterator
r6783 return self.cache[t]
Vadim Gelfer
move hgweb template code out to templater
r1896
Matt Mackall
templater: map -> mapping
r10847 def __call__(self, t, **mapping):
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361 ttype = t in self.map and self.map[t][0] or 'default'
Matt Mackall
templater: clarify engine caching
r13187 if ttype not in self.ecache:
self.ecache[ttype] = engines[ttype](self.load,
self.filters, self.defaults)
proc = self.ecache[ttype]
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361
Matt Mackall
templater: map -> mapping
r10847 stream = proc.process(t, mapping)
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 if self.minchunk:
stream = util.increasingchunks(stream, min=self.minchunk,
max=self.maxchunk)
return stream
Dirkjan Ochtman
kill some trailing spaces
r7434
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 def templatepaths():
'''return locations used for template files.'''
Mads Kiilerich
templater: don't search randomly for templates - trust util.datapath...
r22636 pathsrel = ['templates']
Mads Kiilerich
templater: inline global 'path' list in templatepaths
r22635 paths = [os.path.normpath(os.path.join(util.datapath, f))
for f in pathsrel]
return [p for p in paths if os.path.isdir(p)]
Vadim Gelfer
move changeset_templater into templater module.
r2189
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 def templatepath(name):
'''return location of template file. returns None if not found.'''
for p in templatepaths():
f = os.path.join(p, name)
if os.path.exists(f):
return f
return None
Vadim Gelfer
move changeset_templater into templater module.
r2189
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 def stylemap(styles, paths=None):
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 """Return path to mapfile for a given style.
Searches mapfile in the following locations:
1. templatepath/style/map
2. templatepath/map-style
3. templatepath/map
"""
if paths is None:
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 paths = templatepaths()
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 elif isinstance(paths, str):
Dirkjan Ochtman
templater: fix little problem from stylemap() changes
r8223 paths = [paths]
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 if isinstance(styles, str):
styles = [styles]
for style in styles:
Yuya Nishihara
hgweb: prevent loading style map from directories other than specified paths...
r24296 # only plain name is allowed to honor template paths
if (not style
or style in (os.curdir, os.pardir)
or os.sep in style
or os.altsep and os.altsep in style):
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 continue
locations = [os.path.join(style, 'map'), 'map-' + style]
locations.append('map')
for path in paths:
for location in locations:
mapfile = os.path.join(path, location)
if os.path.isfile(mapfile):
return style, mapfile
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
raise RuntimeError("No hgweb templates found in %r" % paths)
Yuya Nishihara
templater: tell hggettext to collect help of template functions
r24601
# tell hggettext to extract docstrings from these functions:
i18nfunctions = funcs.values()