##// END OF EJS Templates
getchangegroup: take an 'outgoing' object as argument (API)...
getchangegroup: take an 'outgoing' object as argument (API) There is various version of this function that differ mostly by the way they define the bundled set. The flexibility is now available in the outgoing object itself so we move the complexity into the caller themself. This will allow use to remove a good share of the similar function to obtains a changegroup in the 'changegroup.py' module. An important side effect is that we stop calling 'computeoutgoing' in 'getchangegroup'. This is fine as code that needs such argument processing is actually going through the 'exchange' module which already all this function itself.

File last commit:

r29636:84ef4517 stable
r29807:d4e02634 default
Show More
templater.py
1163 lines | 38.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
Gregory Szorc
templater: use absolute_import
r25985 from __future__ import absolute_import
import os
import re
Weiwen
template engine: convert generator-based iterator to list-based iterator...
r17982 import types
Gregory Szorc
templater: use absolute_import
r25985
from .i18n import _
from . import (
config,
error,
minirst,
parser,
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 registrar,
Gregory Szorc
templater: use absolute_import
r25985 revset as revsetmod,
templatefilters,
templatekw,
util,
)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
# template parsing
elements = {
Yuya Nishihara
parser: separate actions for primary expression and prefix operator...
r25815 # token-type: binding-strength, primary, prefix, infix, suffix
"(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
",": (2, None, None, ("list", 2), None),
"|": (5, None, None, ("|", 5), None),
"%": (6, None, None, ("%", 6), None),
")": (0, None, None, None, None),
"integer": (0, "integer", None, None, None),
"symbol": (0, "symbol", None, None, None),
"string": (0, "string", None, None, None),
"template": (0, "template", None, None, None),
"end": (0, None, None, None, None),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 }
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 def tokenize(program, start, end, term=None):
"""Parse a template expression into a stream of tokens, which must end
with term if specified"""
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 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)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 elif c in '"\'': # handle quoted templates
s = pos + 1
data, pos = _parsetemplate(program, s, end, c)
yield ('template', data, s)
pos -= 1
Yuya Nishihara
templater: remove processing of "string" literals from tokenizer...
r25784 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
# handle quoted strings
c = program[pos + 1]
s = pos = pos + 2
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 while pos < end: # find closing quote
d = program[pos]
Yuya Nishihara
templater: fix handling of \-escapes in raw string literals...
r25638 if 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:
Yuya Nishihara
templater: unify "string" and "rawstring"...
r25785 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
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
# handle escaped quoted strings for compatibility with 2.9.2-3.4,
# where some of nested templates were preprocessed as strings and
# then compiled. therefore, \"...\" was allowed. (issue4733)
#
# processing flow of _evalifliteral() at 5ab28a2e9962:
# outer template string -> stringify() -> compiletemplate()
# ------------------------ ------------ ------------------
# {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
# ~~~~~~~~
# escaped quoted string
if c == 'r':
pos += 1
Yuya Nishihara
templater: unify "string" and "rawstring"...
r25785 token = 'string'
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 else:
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 token = 'template'
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 quote = program[pos:pos + 2]
s = pos = pos + 2
while pos < end: # find closing escaped quote
if program.startswith('\\\\\\', pos, end):
pos += 4 # skip over double escaped characters
continue
if program.startswith(quote, pos, end):
Matt Mackall
templater: create string unescape helper (issue4798)...
r26215 # interpret as if it were a part of an outer string
Yuya Nishihara
parser: move unescape helper from templater...
r26231 data = parser.unescapestr(program[s:pos])
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 if token == 'template':
data = _parsetemplate(data, 0, len(data))[0]
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 yield (token, data, s)
pos += 1
break
pos += 1
else:
raise error.ParseError(_("unterminated string"), s)
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
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 elif c == term:
Yuya Nishihara
templater: check existence of closing brace of template string
r25782 yield ('end', None, pos + 1)
return
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 else:
raise error.ParseError(_("syntax error"), pos)
pos += 1
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 if term:
raise error.ParseError(_("unterminated template expansion"), start)
yield ('end', None, pos)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 def _parsetemplate(tmpl, start, stop, quote=''):
r"""
>>> _parsetemplate('foo{bar}"baz', 0, 12)
([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
>>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"')
([('string', 'foo'), ('symbol', 'bar')], 9)
>>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
([('string', 'foo')], 4)
>>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
([('string', 'foo"'), ('string', 'bar')], 9)
>>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
Yuya Nishihara
templater: unify "string" and "rawstring"...
r25785 ([('string', 'foo\\')], 6)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 """
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 parsed = []
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 sepchars = '{' + quote
Yuya Nishihara
templater: extract function that parses template string...
r25781 pos = start
Yuya Nishihara
parser: accept iterator of tokens instead of tokenizer function and program...
r25654 p = parser.parser(elements)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 while pos < stop:
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 n = min((tmpl.find(c, pos, stop) for c in sepchars),
key=lambda n: (n < 0, n))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if n < 0:
Yuya Nishihara
parser: move unescape helper from templater...
r26231 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
Yuya Nishihara
templater: respect stop position while parsing template string...
r25780 pos = stop
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 break
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 c = tmpl[n]
Yuya Nishihara
templater: strictly parse leading backslashes of '{' (issue4569) (BC)...
r24949 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
Yuya Nishihara
templater: drop strtoken argument from compiletemplate()...
r25598 if bs % 2 == 1:
# escaped (e.g. '\{', '\\\{', but not '\\{')
Yuya Nishihara
parser: move unescape helper from templater...
r26231 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos = n + 1
continue
if n > pos:
Yuya Nishihara
parser: move unescape helper from templater...
r26231 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 if c == quote:
return parsed, n + 1
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
Bernhard Leiner
revset: report a parse error if a revset is not parsed completely (issue2654)
r13665 parsed.append(parseres)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783
if quote:
raise error.ParseError(_("unterminated string"), start)
Yuya Nishihara
templater: extract function that parses template string...
r25781 return parsed, pos
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 def _unnesttemplatelist(tree):
"""Expand list of templates to node tuple
>>> def f(tree):
... print prettyformat(_unnesttemplatelist(tree))
>>> f(('template', []))
('string', '')
>>> f(('template', [('string', 'foo')]))
('string', 'foo')
>>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
(template
('string', 'foo')
('symbol', 'rev'))
>>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
(template
('symbol', 'rev'))
>>> f(('template', [('template', [('string', 'foo')])]))
('string', 'foo')
"""
if not isinstance(tree, tuple):
return tree
op = tree[0]
if op != 'template':
return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
assert len(tree) == 2
xs = tuple(_unnesttemplatelist(x) for x in tree[1])
if not xs:
return ('string', '') # empty template ""
elif len(xs) == 1 and xs[0][0] == 'string':
return xs[0] # fast path for string with no template fragment "x"
else:
return (op,) + xs
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 def parse(tmpl):
"""Parse template string into tree"""
Yuya Nishihara
templater: extract function that parses template string...
r25781 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 assert pos == len(tmpl), 'unquoted template should be consumed'
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 return _unnesttemplatelist(('template', parsed))
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 def _parseexpr(expr):
"""Parse a template expression into tree
>>> _parseexpr('"foo"')
('string', 'foo')
>>> _parseexpr('foo(bar)')
('func', ('symbol', 'foo'), ('symbol', 'bar'))
>>> _parseexpr('foo(')
Traceback (most recent call last):
...
ParseError: ('not a prefix: end', 4)
>>> _parseexpr('"foo" "bar"')
Traceback (most recent call last):
...
ParseError: ('invalid token', 7)
"""
p = parser.parser(elements)
tree, pos = p.parse(tokenize(expr, 0, len(expr)))
if pos != len(expr):
raise error.ParseError(_('invalid token'), pos)
return _unnesttemplatelist(tree)
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 def prettyformat(tree):
return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 def compileexp(exp, context, curmethods):
Yuya Nishihara
templater: inline compiletemplate() function into engine...
r28956 """Compile parsed template tree to (func, data) pair"""
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 gettemplate(exp, context):
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 """Compile given template tree or load named template from map file;
returns (func, data) pair"""
Yuya Nishihara
templater: relax type of mapped template...
r28546 if exp[0] in ('template', 'string'):
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 return compileexp(exp, context, methods)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if exp[0] == 'symbol':
Yuya Nishihara
templater: comment that gettemplate() has different name resolution order...
r25599 # unlike runsymbol(), here 'symbol' is always taken as template name
# even if it exists in mapping. this allows us to override mapping
# by web templates, e.g. 'changelogtag' is redefined in map file.
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return context._load(exp[1])
raise error.ParseError(_("expected template specifier"))
Yuya Nishihara
templater: extract helper that evaluates filter or function argument...
r26124 def evalfuncarg(context, mapping, arg):
func, data = arg
# 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)
return thing
Yuya Nishihara
templater: factor out function that evaluates argument as integer...
r28343 def evalinteger(context, mapping, arg, err):
Yuya Nishihara
templater: drop redundant type conversion when evaluating integer argument...
r28344 v = evalfuncarg(context, mapping, arg)
Yuya Nishihara
templater: factor out function that evaluates argument as integer...
r28343 try:
Yuya Nishihara
templater: drop redundant type conversion when evaluating integer argument...
r28344 return int(v)
except (TypeError, ValueError):
Yuya Nishihara
templater: factor out function that evaluates argument as integer...
r28343 raise error.ParseError(err)
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 def evalstring(context, mapping, arg):
func, data = arg
return stringify(func(context, mapping, data))
Yuya Nishihara
templater: make label() take unknown symbol as color literal...
r28373 def evalstringliteral(context, mapping, arg):
"""Evaluate given argument as string template, but returns symbol name
if it is unknown"""
func, data = arg
if func is runsymbol:
thing = func(context, mapping, data, default=data)
else:
thing = func(context, mapping, data)
return stringify(thing)
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):
return data
Yuya Nishihara
templater: abort if infinite recursion detected while evaluation (issue4758)...
r27939 def _recursivesymbolblocker(key):
def showrecursion(**args):
raise error.Abort(_("recursive reference '%s' in template") % key)
return showrecursion
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 def _runrecursivesymbol(context, mapping, key):
raise error.Abort(_("recursive reference '%s' in template") % key)
Yuya Nishihara
templater: make label() take unknown symbol as color literal...
r28373 def runsymbol(context, mapping, key, default=''):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 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:
Yuya Nishihara
templater: abort if infinite recursion detected while evaluation (issue4758)...
r27939 # put poison to cut recursion. we can't move this to parsing phase
# because "x = {x}" is allowed if "x" is a keyword. (issue4758)
safemapping = mapping.copy()
safemapping[key] = _recursivesymbolblocker(key)
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 try:
Yuya Nishihara
templater: abort if infinite recursion detected while evaluation (issue4758)...
r27939 v = context.process(key, safemapping)
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 except TemplateNotFound:
Yuya Nishihara
templater: make label() take unknown symbol as color literal...
r28373 v = default
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)
return v
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 def buildtemplate(exp, context):
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 return (runtemplate, ctmpl)
Yuya Nishihara
templater: move runtemplate function out of buildmap/runmap pair...
r25595 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 buildfilter(exp, context):
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 arg = compileexp(exp[1], context, methods)
Yuya Nishihara
templater: inline getfilter() to buildfilter()...
r26104 n = getsymbol(exp[2])
if n in context._filters:
filt = context._filters[n]
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 return (runfilter, (arg, filt))
Yuya Nishihara
templater: introduce unified filter syntax for unary functions...
r26105 if n in funcs:
f = funcs[n]
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 return (f, [arg])
Yuya Nishihara
templater: inline getfilter() to buildfilter()...
r26104 raise error.ParseError(_("unknown function '%s'") % n)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
def runfilter(context, mapping, data):
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 arg, filt = data
thing = evalfuncarg(context, mapping, arg)
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):
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 if isinstance(arg[1], tuple):
dt = arg[1][1]
Neil Kodner
templater: abort when a template filter raises an exception (issue2987)
r17383 else:
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 dt = arg[1]
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("template filter '%s' is not compatible with "
Neil Kodner
templater: abort when a template filter raises an exception (issue2987)
r17383 "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)
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 tfunc, tdata = gettemplate(exp[2], context)
return (runmap, (func, data, tfunc, tdata))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
def runmap(context, mapping, data):
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 func, data, tfunc, tdata = data
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 d = func(context, mapping, data)
Yuya Nishihara
templater: make _hybrid not callable to avoid conflicting semantics...
r27891 if util.safehasattr(d, 'itermaps'):
Yuya Nishihara
templater: handle exception when applying map operator to non-iterable object...
r28349 diter = d.itermaps()
else:
try:
diter = iter(d)
except TypeError:
if func is runsymbol:
raise error.ParseError(_("keyword '%s' is not iterable") % data)
else:
raise error.ParseError(_("%r is not iterable") % d)
Matt Mackall
templating: make new-style templating features work with command line lists
r17631
Yuya Nishihara
templater: handle exception when applying map operator to non-iterable object...
r28349 for i in diter:
Kostia Balytskyi
templater: fix list templating bug...
r28225 lm = mapping.copy()
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if isinstance(i, dict):
lm.update(i)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 lm['originalnode'] = mapping.get('node')
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 yield tfunc(context, lm, tdata)
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]
Yuya Nishihara
templater: drop unneeded destructuring of argument tuple at buildfilter...
r26125 return (runfilter, (args[0], 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
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 # dict of template built-in functions
funcs = {}
templatefunc = registrar.templatefunc(funcs)
@templatefunc('date(date[, fmt])')
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def date(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Format a date. See :hg:`help dates` for formatting
Yuya Nishihara
templatefilters: remove redundant 'date' and 'strip' filters...
r26106 strings. The default is a Unix date format, including the timezone:
"Mon Sep 04 15:13:13 2006 0700"."""
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"))
Yuya Nishihara
templater: make date() use helper function to evaluate argument...
r28334 date = evalfuncarg(context, mapping, args[0])
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:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 fmt = evalstring(context, mapping, args[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: use templatefunc to mark a function as template function...
r28696 @templatefunc('diff([includepattern [, excludepattern]])')
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 def diff(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Show a diff, optionally
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 specifying files to include or exclude."""
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 if len(args) > 2:
# i18n: "diff" is a keyword
timeless
grammar: favor zero, one, two over ... or no
r27293 raise error.ParseError(_("diff expects zero, one, or two arguments"))
FUJIWARA Katsunori
templater: add "diff" template function...
r22434
def getpatterns(i):
if i < len(args):
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 s = evalstring(context, mapping, args[i]).strip()
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 if s:
return [s]
return []
ctx = mapping['ctx']
chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
return ''.join(chunks)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def fill(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Fill many
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[0])
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 width = 76
initindent = ''
hangindent = ''
if 2 <= len(args) <= 4:
Yuya Nishihara
templater: factor out function that evaluates argument as integer...
r28343 width = evalinteger(context, mapping, args[1],
# i18n: "fill" is a keyword
_("fill expects an integer width"))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 try:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 initindent = evalstring(context, mapping, args[2])
hangindent = evalstring(context, mapping, args[3])
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 except IndexError:
pass
return templatefilters.fill(text, width, initindent, hangindent)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('pad(text, width[, fillchar=\' \'[, right=False]])')
Durham Goode
template: add pad function for padding output...
r20370 def pad(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Pad text with a
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: fix pad() to evaluate int argument and handle error
r28345 width = evalinteger(context, mapping, args[1],
# i18n: "pad" is a keyword
_("pad() expects an integer width"))
Durham Goode
template: add pad function for padding output...
r20370
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[0])
Durham Goode
template: add pad function for padding output...
r20370
right = False
fillchar = ' '
if len(args) > 2:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 fillchar = evalstring(context, mapping, args[2])
Durham Goode
template: add pad function for padding output...
r20370 if len(args) > 3:
right = util.parsebool(args[3][1])
if right:
return text.rjust(width, fillchar)
else:
return text.ljust(width, fillchar)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('indent(text, indentchars[, firstline])')
Ryan McElroy
templater: introduce indent function
r25489 def indent(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Indents all non-empty lines
Ryan McElroy
templater: introduce indent function
r25489 with the characters given in the indentchars string. An optional
third parameter will override the indent for the first line only
if present."""
if not (2 <= len(args) <= 3):
# i18n: "indent" is a keyword
raise error.ParseError(_("indent() expects two or three arguments"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[0])
indent = evalstring(context, mapping, args[1])
Ryan McElroy
templater: introduce indent function
r25489
if len(args) == 3:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 firstline = evalstring(context, mapping, args[2])
Ryan McElroy
templater: introduce indent function
r25489 else:
firstline = indent
# the indent function doesn't indent the first line, so we do it here
return templatefilters.indent(firstline + text, indent)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('get(dict, key)')
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 def get(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Get an attribute/key from an object. Some keywords
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 are complex types. This function allows you to obtain the value of an
timeless@mozdev.org
templater: fix get English
r26197 attribute on these types."""
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"))
Yuya Nishihara
templater: fix get() to evaluate arguments eagerly...
r28331 dictarg = evalfuncarg(context, mapping, args[0])
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 if not util.safehasattr(dictarg, 'get'):
# i18n: "get" is a keyword
raise error.ParseError(_("get() expects a dict as first argument"))
Yuya Nishihara
templater: fix get() to evaluate arguments eagerly...
r28331 key = evalfuncarg(context, mapping, args[1])
Yuya Nishihara
templater: make get(dict, key) return a single value...
r27892 return dictarg.get(key)
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('if(expr, then[, else])')
Matt Mackall
templater: add if/ifeq conditionals
r17636 def if_(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Conditionally execute based on the result of
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 test = evalstring(context, mapping, args[0])
Matt Mackall
templater: add if/ifeq conditionals
r17636 if test:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[1][0](context, mapping, args[1][1])
Matt Mackall
templater: add if/ifeq conditionals
r17636 elif len(args) == 3:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[2][0](context, mapping, args[2][1])
Matt Mackall
templater: add if/ifeq conditionals
r17636
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('ifcontains(search, thing, then[, else])')
Durham Goode
template: add ifcontains template function...
r20518 def ifcontains(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Conditionally execute based
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 item = evalstring(context, mapping, args[0])
Yuya Nishihara
templater: fix ifcontains() to evaluate items argument eagerly...
r28332 items = evalfuncarg(context, mapping, args[1])
Durham Goode
template: add ifcontains template function...
r20518
Yuya Nishihara
templater: implement _hybrid.__contains__ so that ifcontains can accept dict...
r24240 if item in items:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[2][0](context, mapping, args[2][1])
Durham Goode
template: add ifcontains template function...
r20518 elif len(args) == 4:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[3][0](context, mapping, args[3][1])
Durham Goode
template: add ifcontains template function...
r20518
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('ifeq(expr1, expr2, then[, else])')
Matt Mackall
templater: add if/ifeq conditionals
r17636 def ifeq(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Conditionally execute based on
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 test = evalstring(context, mapping, args[0])
match = evalstring(context, mapping, args[1])
Matt Mackall
templater: add if/ifeq conditionals
r17636 if test == match:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[2][0](context, mapping, args[2][1])
Matt Mackall
templater: add if/ifeq conditionals
r17636 elif len(args) == 4:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[3][0](context, mapping, args[3][1])
Matt Mackall
templater: add if/ifeq conditionals
r17636
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('join(list, sep)')
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def join(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """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])
Yuya Nishihara
templater: make _hybrid not callable to avoid conflicting semantics...
r27891 if util.safehasattr(joinset, 'itermaps'):
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 jf = joinset.joinfmt
Yuya Nishihara
templater: make _hybrid not callable to avoid conflicting semantics...
r27891 joinset = [jf(x) for x in joinset.itermaps()]
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
joiner = " "
if len(args) > 1:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 joiner = evalstring(context, mapping, args[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
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('label(label, expr)')
Sean Farley
templater: add no-op template function 'label'
r18289 def label(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Apply a label to generated content. Content with
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: make label() just fail if ui object isn't available...
r28462 ui = mapping['ui']
Yuya Nishihara
templater: move label() function from color extension...
r28374 thing = evalstring(context, mapping, args[1])
# preserve unknown symbol as literal so effects like 'red', 'bold',
# etc. don't need to be quoted
label = evalstringliteral(context, mapping, args[0])
Kostia Balytskyi
formatter: make labels work with templated output...
r28384 return ui.label(thing, label)
Sean Farley
templater: add no-op template function 'label'
r18289
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('latesttag([pattern])')
Matt Harbison
templater: introduce {latesttag()} function to match a pattern (issue4184)...
r26485 def latesttag(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """The global tags matching the given pattern on the
Matt Harbison
templater: introduce {latesttag()} function to match a pattern (issue4184)...
r26485 most recent globally tagged ancestor of this changeset."""
if len(args) > 1:
# i18n: "latesttag" is a keyword
raise error.ParseError(_("latesttag expects at most one argument"))
pattern = None
if len(args) == 1:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 pattern = evalstring(context, mapping, args[0])
Matt Harbison
templater: introduce {latesttag()} function to match a pattern (issue4184)...
r26485
return templatekw.showlatesttags(pattern, **mapping)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('localdate(date[, tz])')
Yuya Nishihara
templater: port localdate filter to a function...
r26127 def localdate(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Converts a date to the specified timezone.
Yuya Nishihara
templater: add optional timezone argument to localdate()...
r26128 The default is local date."""
if not (1 <= len(args) <= 2):
Yuya Nishihara
templater: port localdate filter to a function...
r26127 # i18n: "localdate" is a keyword
Yuya Nishihara
templater: add optional timezone argument to localdate()...
r26128 raise error.ParseError(_("localdate expects one or two arguments"))
Yuya Nishihara
templater: port localdate filter to a function...
r26127
date = evalfuncarg(context, mapping, args[0])
try:
date = util.parsedate(date)
except AttributeError: # not str nor date tuple
# i18n: "localdate" is a keyword
raise error.ParseError(_("localdate expects a date information"))
Yuya Nishihara
templater: add optional timezone argument to localdate()...
r26128 if len(args) >= 2:
tzoffset = None
tz = evalfuncarg(context, mapping, args[1])
if isinstance(tz, str):
Matt Mackall
date: refactor timezone parsing...
r29636 tzoffset, remainder = util.parsetimezone(tz)
if remainder:
tzoffset = None
Yuya Nishihara
templater: add optional timezone argument to localdate()...
r26128 if tzoffset is None:
try:
tzoffset = int(tz)
except (TypeError, ValueError):
# i18n: "localdate" is a keyword
raise error.ParseError(_("localdate expects a timezone"))
else:
tzoffset = util.makedate()[1]
Yuya Nishihara
templater: port localdate filter to a function...
r26127 return (date[0], tzoffset)
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('revset(query[, formatargs...])')
Durham Goode
template: add revset() template function...
r20519 def revset(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Execute a revision set query. See
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 :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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 raw = evalstring(context, mapping, args[0])
Durham Goode
template: add revset() template function...
r20519 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:
Yuya Nishihara
templater: fix revset() to evaluate format arguments eagerly...
r28333 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
FUJIWARA Katsunori
templater: enable alias predicates to be used in "revset()" function...
r22304 revs = query(revsetmod.formatspec(raw, *formatargs))
Yuya Nishihara
templater: factor out type conversion of revset() result...
r28178 revs = list(revs)
Durham Goode
template: add revset() template function...
r20519 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)
Yuya Nishihara
templater: factor out type conversion of revset() result...
r28178 revs = list(revs)
Durham Goode
template: add revset() template function...
r20519 revsetcache[raw] = revs
Yuya Nishihara
templater: switch ctx of list expression to rev of revset() (BC)...
r26234 return templatekw.showrevslist("revision", revs, **mapping)
Durham Goode
template: add revset() template function...
r20519
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('rstdoc(text, style)')
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 def rstdoc(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[0])
style = evalstring(context, mapping, args[1])
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747
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
Martin von Zweigbergk
templater: add separate() template function...
r29085 @templatefunc('separate(sep, args)')
def separate(context, mapping, args):
"""Add a separator between non-empty arguments."""
if not args:
# i18n: "separate" is a keyword
raise error.ParseError(_("separate expects at least one argument"))
sep = evalstring(context, mapping, args[0])
first = True
for arg in args[1:]:
argstr = evalstring(context, mapping, arg)
if not argstr:
continue
if first:
first = False
else:
yield sep
yield argstr
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('shortest(node, minlength=4)')
Durham Goode
template: add shortest(node) template function...
r20369 def shortest(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Obtain the shortest representation of
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 node = evalstring(context, mapping, args[0])
Durham Goode
template: add shortest(node) template function...
r20369
minlength = 4
if len(args) > 1:
Yuya Nishihara
templater: fix shortest() to evaluate int argument and handle error
r28346 minlength = evalinteger(context, mapping, args[1],
# i18n: "shortest" is a keyword
_("shortest() expects an integer minlength"))
Durham Goode
template: add shortest(node) template function...
r20369
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
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('strip(text[, chars])')
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 def strip(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Strip characters from a string. By default,
Yuya Nishihara
templatefilters: remove redundant 'date' and 'strip' filters...
r26106 strips all leading and trailing whitespace."""
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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[0])
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 if len(args) == 2:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 chars = evalstring(context, mapping, args[1])
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 return text.strip(chars)
return text.strip()
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('sub(pattern, replacement, expression)')
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def sub(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Perform text substitution
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 pat = evalstring(context, mapping, args[0])
rpl = evalstring(context, mapping, args[1])
src = evalstring(context, mapping, args[2])
Yuya Nishihara
templater: catch regexp error at sub() function...
r26188 try:
patre = re.compile(pat)
except re.error:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
try:
yield patre.sub(rpl, src)
except re.error:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('startswith(pattern, text)')
Ryan McElroy
templater: introduce startswith function...
r21821 def startswith(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """Returns the value from the "text" argument
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 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"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 patn = evalstring(context, mapping, args[0])
text = evalstring(context, mapping, args[1])
Ryan McElroy
templater: introduce startswith function...
r21821 if text.startswith(patn):
return text
return ''
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 @templatefunc('word(number, text[, separator])')
Ryan McElroy
templater: introduce word function...
r21846 def word(context, mapping, args):
FUJIWARA Katsunori
templater: use templatefunc to mark a function as template function...
r28696 """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))
Yuya Nishihara
templater: factor out function that evaluates argument as integer...
r28343 num = evalinteger(context, mapping, args[0],
# i18n: "word" is a keyword
_("word expects an integer index"))
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 text = evalstring(context, mapping, args[1])
Ryan McElroy
templater: introduce word function...
r21846 if len(args) == 3:
Yuya Nishihara
templater: factor out thin helper that evaluates argument as string...
r28348 splitter = evalstring(context, mapping, args[2])
Ryan McElroy
templater: introduce word function...
r21846 else:
splitter = None
tokens = text.split(splitter)
Matt Harbison
templater: protect word() from crashing on out of range negative value...
r26502 if num >= len(tokens) or num < -len(tokens):
Ryan McElroy
templater: introduce word function...
r21846 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]),
"symbol": lambda e, c: (runsymbol, e[1]),
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 "template": buildtemplate,
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
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 class _aliasrules(parser.basealiasrules):
"""Parsing and expansion rule set of template aliases"""
_section = _('template alias')
_parse = staticmethod(_parseexpr)
@staticmethod
def _trygetfunc(tree):
"""Return (name, args) if tree is func(...) or ...|filter; otherwise
None"""
if tree[0] == 'func' and tree[1][0] == 'symbol':
return tree[1][1], getlist(tree[2])
if tree[0] == '|' and tree[2][0] == 'symbol':
return tree[2][1], [tree[1]]
def expandaliases(tree, aliases):
"""Return new tree of aliases are expanded"""
aliasmap = _aliasrules.buildmap(aliases)
return _aliasrules.expand(aliasmap, tree)
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: relax unquotestring() to fall back to bare string...
r28630 '''unwrap quotes if any; otherwise returns unmodified string'''
Yuya Nishihara
templater: do not strip non-quote characters from template config...
r28687 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
Yuya Nishihara
templater: relax unquotestring() to fall back to bare string...
r28630 return s
Yuya Nishihara
templater: remove workaround for escaped quoted string in quoted template...
r25696 return s[1:-1]
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|...}.'''
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 def __init__(self, loader, filters=None, defaults=None, aliases=()):
Matt Mackall
templater: privatize class variables
r10848 self._loader = loader
Pierre-Yves David
templater: remove a mutable default argument...
r26330 if filters is None:
filters = {}
Matt Mackall
templater: privatize class variables
r10848 self._filters = filters
Pierre-Yves David
templater: remove a mutable default argument...
r26331 if defaults is None:
defaults = {}
Matt Mackall
templater: privatize class variables
r10848 self._defaults = defaults
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 self._aliasmap = _aliasrules.buildmap(aliases)
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 self._cache = {} # key: (func, data)
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:
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 # put poison to cut recursion while compiling 't'
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 self._cache[t] = (_runrecursivesymbol, t)
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 try:
Yuya Nishihara
templater: inline compiletemplate() function into engine...
r28956 x = parse(self._loader(t))
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 if self._aliasmap:
x = _aliasrules.expand(self._aliasmap, x)
Yuya Nishihara
templater: inline compiletemplate() function into engine...
r28956 self._cache[t] = compileexp(x, self, methods)
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 except: # re-raises
del self._cache[t]
raise
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 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.'''
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 func, data = self._load(t)
return _flatten(func(self, mapping, data))
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')
timeless
cleanup: remove superfluous space after space after equals (python)
r27637 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(".")
timeless
templater: ignore orig/rej files...
r28403 if split[-1] in ('orig', 'rej'):
continue
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 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
Yuya Nishihara
templater: extract function that loads template map file...
r28953 def _readmapfile(mapfile):
"""Load template elements from the given map file"""
if not os.path.exists(mapfile):
raise error.Abort(_("style '%s' not found") % mapfile,
hint=_("available styles: %s") % stylelist())
base = os.path.dirname(mapfile)
conf = config.config(includepaths=templatepaths())
conf.read(mapfile)
cache = {}
tmap = {}
for key, val in conf[''].items():
if not val:
raise error.ParseError(_('missing value'), conf.source('', key))
if val[0] in "'\"":
if val[0] != val[-1]:
raise error.ParseError(_('unmatched quotes'),
conf.source('', key))
cache[key] = unquotestring(val)
else:
val = 'default', val
if ':' in val[1]:
val = val[1].split(':', 1)
tmap[key] = val[0], os.path.join(base, val[1])
return cache, tmap
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 class TemplateNotFound(error.Abort):
Alexander Plavin
templater: support using templates with non-standard names from map file...
r19770 pass
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 class templater(object):
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 minchunk=1024, maxchunk=65536):
Vadim Gelfer
add doc comments to template code.
r1909 '''set up template engine.
filters is dict of functions. each transforms a value into another.
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 defaults is dict of default map definitions.
aliases is list of alias (name, replacement) pairs.
'''
Pierre-Yves David
templater: remove a mutable default argument...
r26332 if filters is None:
filters = {}
Pierre-Yves David
templater: remove a mutable default argument...
r26333 if defaults is None:
defaults = {}
Pierre-Yves David
templater: remove a mutable default argument...
r26334 if cache is None:
cache = {}
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 = {}
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
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 self._aliases = aliases
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
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 @classmethod
def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
minchunk=1024, maxchunk=65536):
"""Create templater from the specified map file"""
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
Yuya Nishihara
templater: extract function that loads template map file...
r28953 cache, tmap = _readmapfile(mapfile)
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 t.cache.update(cache)
t.map = tmap
return t
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])
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except KeyError as 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])
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Vadim Gelfer
improve template errors when something is wrong.
r1905 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:
Yuya Nishihara
templater: give better error message for invalid engine type...
r28831 try:
ecls = engines[ttype]
except KeyError:
raise error.Abort(_('invalid template engine: %s') % ttype)
Yuya Nishihara
templater: load and expand aliases by template engine (API) (issue4842)...
r28957 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
self._aliases)
Matt Mackall
templater: clarify engine caching
r13187 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
FUJIWARA Katsunori
registrar: add templatefunc to mark a function as template function (API)...
r28695 def loadfunction(ui, extname, registrarobj):
"""Load template function from specified registrarobj
"""
for name, func in registrarobj._table.iteritems():
funcs[name] = func
Yuya Nishihara
templater: tell hggettext to collect help of template functions
r24601 # tell hggettext to extract docstrings from these functions:
i18nfunctions = funcs.values()