##// END OF EJS Templates
vfs: add a 'reljoin' function for joining relative paths...
vfs: add a 'reljoin' function for joining relative paths The vfs.join method only works for absolute paths. We need something that works for relative paths too when transforming filenames. Since os.path.join may misbehave in tricky encoding situations, encapsulate the new join method in our vfs abstraction. The default implementation remains os.path.join, but this opens the door to other VFSes doing something more intelligent based on their needs. In the same go, we replace the usage of 'os.path.join' in transaction code.

File last commit:

r23167:a3c2d921 stable
r23581:aed981c7 default
Show More
templater.py
761 lines | 23.7 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),
"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)
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
if n > 0 and tmpl[n - 1] == '\\':
# escaped
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
return [compileexp(e, context) for e in parsed]
def compileexp(exp, context):
t = exp[0]
if t in methods:
return methods[t](exp, context)
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"))
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):
func, data = compileexp(exp[1], context)
filt = getfilter(exp[2], context)
return (runfilter, (func, data, filt))
def runfilter(context, mapping, data):
func, data, filt = data
Neil Kodner
templater: abort when a template filter raises an exception (issue2987)
r17383 try:
return filt(func(context, mapping, data))
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):
func, data = compileexp(exp[1], context)
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])
args = [compileexp(x, context) 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):
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])
if len(args) == 2:
fmt = stringify(args[1][0](context, mapping, args[1][1]))
return util.datestr(date, fmt)
return util.datestr(date)
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 def diff(context, mapping, args):
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):
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):
"""usage: pad(text, width, fillchar=' ', right=False)
"""
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):
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):
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):
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])
Durham Goode
templates: fix ifcontains against sets with length > 1 (issue4259)...
r21540 # Iterating over items gives a formatted string, so we iterate
# directly over the raw values.
Matt Mackall
templater: fix ifcontains when list is a string (issue4399)
r22843 if ((callable(items) and item in [i.values()[0] for i in items()]) or
(isinstance(items, str) and 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):
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):
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):
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):
"""usage: revset(query[, formatargs...])
"""
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']
repo = ctx._repo
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 def query(expr):
m = revsetmod.match(repo.ui, expr)
return m(repo, revsetmod.spanset(repo))
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):
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):
"""usage: shortest(node, minlength=4)
"""
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):
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):
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):
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):
"""return nth word from a string"""
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))
num = int(stringify(args[0][0](context, mapping, args[0][1])))
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]
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 methods = {
"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]),
"group": lambda e, c: compileexp(e[1], c),
# ".": buildmember,
"|": buildfilter,
"%": buildmap,
"func": buildfunc,
}
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
Vadim Gelfer
make parsestring work with strings that do not have quotes.
r1902 def parsestring(s, quoted=True):
Vadim Gelfer
add doc comments to template code.
r1909 '''parse a string using simple c-like syntax.
string must be in quotes if quoted is True.'''
Vadim Gelfer
make parsestring work with strings that do not have quotes.
r1902 if quoted:
Matt Mackall
templater: simplify parsestring
r3639 if len(s) < 2 or s[0] != s[-1]:
raise SyntaxError(_('unmatched quotes'))
return s[1:-1].decode('string_escape')
Matt Mackall
templater: use str.decode in parse_string
r3632
return s.decode('string_escape')
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 = {}
Vadim Gelfer
improve template errors when something is wrong.
r1905 self.base = (mapfile and os.path.dirname(mapfile)) or ''
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
Matt Mackall
templater: use new config parser...
r8194 conf = config.config()
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:
self.cache[key] = parsestring(val)
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:
if not style:
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)