##// END OF EJS Templates
crecord: fix typo in the help text...
crecord: fix typo in the help text In the crecord help dialog, the toggle all option was wrongfully documented. Instead of using 'a', one must use 'A' to toggle all the hunks. The crecord header that is always displayed on the screen contains the right shortcut and does not need to be changed.

File last commit:

r27892:83aef8d5 default
r27936:fedd8165 stable
Show More
templater.py
977 lines | 32.9 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,
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
parser: accept iterator of tokens instead of tokenizer function and program...
r25654 def tokenize(program, start, end):
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
elif c == '}':
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: check existence of closing brace of template string
r25782 raise error.ParseError(_("unterminated template expansion"), start)
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
parser: accept iterator of tokens instead of tokenizer function and program...
r25654 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: extract function that parses template string...
r25781 def compiletemplate(tmpl, context):
parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 return [compileexp(e, context, methods) for e in parsed]
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 def compileexp(exp, context, curmethods):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 t = exp[0]
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 if t in curmethods:
return curmethods[t](exp, context)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 raise error.ParseError(_("unknown method '%s'") % t)
# template evaluation
def getsymbol(exp):
if exp[0] == 'symbol':
return exp[1]
Ryan McElroy
templater: add symbol to error...
r21822 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
def getlist(x):
if not x:
return []
if x[0] == 'list':
return getlist(x[1]) + [x[2]]
return [x]
def gettemplate(exp, context):
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 if exp[0] == 'template':
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 return [compileexp(e, context, methods) for e in exp[1]]
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: 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
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)
return v
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 def buildtemplate(exp, context):
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 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 if len(ctmpl) == 1:
return ctmpl[0] # fast path for string with no template fragment
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)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 ctmpl = gettemplate(exp[2], context)
return (runmap, (func, data, ctmpl))
def runmap(context, mapping, data):
func, data, ctmpl = data
d = func(context, mapping, data)
Yuya Nishihara
templater: make _hybrid not callable to avoid conflicting semantics...
r27891 if util.safehasattr(d, 'itermaps'):
d = d.itermaps()
Matt Mackall
templating: make new-style templating features work with command line lists
r17631
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 lm = mapping.copy()
for i in d:
if isinstance(i, dict):
lm.update(i)
Weiwen
hgweb: display diff for a changeset against any parents (issue2810)...
r17991 lm['originalnode'] = mapping.get('node')
Matt Mackall
templater: factor out runtemplate method...
r17632 yield runtemplate(context, lm, ctmpl)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 else:
# v is not an iterable of dicts, this happen when 'key'
# has been fully expanded already and format is useless.
# If so, return the expanded value.
yield i
def buildfunc(exp, context):
n = getsymbol(exp[1])
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])]
Matt Mackall
templater: use a global funcs table
r14925 if n in funcs:
f = funcs[n]
return (f, args)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if n in context._filters:
if len(args) != 1:
raise error.ParseError(_("filter %s expects one argument") % n)
f = context._filters[n]
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
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def date(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting
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"))
date = args[0][0](context, mapping, args[0][1])
Yuya Nishihara
templater: fix crash by passing invalid object to date() function...
r24903 fmt = None
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if len(args) == 2:
fmt = stringify(args[1][0](context, mapping, args[1][1]))
Yuya Nishihara
templater: fix crash by passing invalid object to date() function...
r24903 try:
if fmt is None:
return util.datestr(date)
else:
return util.datestr(date, fmt)
except (TypeError, ValueError):
# i18n: "date" is a keyword
raise error.ParseError(_("date expects a date information"))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 def diff(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:diff([includepattern [, excludepattern]]): Show a diff, optionally
specifying files to include or exclude."""
FUJIWARA Katsunori
templater: add "diff" template function...
r22434 if len(args) > 2:
# i18n: "diff" is a keyword
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: evaluate arguments passed to diff() appropriately...
r25562 s = stringify(args[i][0](context, mapping, args[i][1])).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)
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def fill(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:fill(text[, width[, initialident[, hangindent]]]): Fill many
paragraphs with optional indentation. See the "fill" filter."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if not (1 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "fill" is a keyword
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 raise error.ParseError(_("fill expects one to four arguments"))
text = stringify(args[0][0](context, mapping, args[0][1]))
width = 76
initindent = ''
hangindent = ''
if 2 <= len(args) <= 4:
try:
width = int(stringify(args[1][0](context, mapping, args[1][1])))
except ValueError:
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "fill" is a keyword
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 raise error.ParseError(_("fill expects an integer width"))
try:
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 initindent = stringify(args[2][0](context, mapping, args[2][1]))
hangindent = stringify(args[3][0](context, mapping, args[3][1]))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 except IndexError:
pass
return templatefilters.fill(text, width, initindent, hangindent)
Durham Goode
template: add pad function for padding output...
r20370 def pad(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a
fill character."""
Durham Goode
template: add pad function for padding output...
r20370 if not (2 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "pad" is a keyword
Durham Goode
template: add pad function for padding output...
r20370 raise error.ParseError(_("pad() expects two to four arguments"))
width = int(args[1][1])
text = stringify(args[0][0](context, mapping, args[0][1]))
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)
Ryan McElroy
templater: introduce indent function
r25489 def indent(context, mapping, args):
""":indent(text, indentchars[, firstline]): Indents all non-empty lines
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"))
text = stringify(args[0][0](context, mapping, args[0][1]))
indent = stringify(args[1][0](context, mapping, args[1][1]))
if len(args) == 3:
firstline = stringify(args[2][0](context, mapping, args[2][1]))
else:
firstline = indent
# the indent function doesn't indent the first line, so we do it here
return templatefilters.indent(firstline + text, indent)
Benoit Boissinot
templater: add get() function to access dict element (e.g. extra)
r18582 def get(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:get(dict, key): Get an attribute/key from an object. Some keywords
are complex types. This function allows you to obtain the value of an
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"))
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])
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
Matt Mackall
templater: add if/ifeq conditionals
r17636 def if_(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:if(expr, then[, else]): Conditionally execute based on the result of
an expression."""
Matt Mackall
templater: add if/ifeq conditionals
r17636 if not (2 <= len(args) <= 3):
FUJIWARA Katsunori
i18n: add "i18n" comment to error messages of template functions
r17890 # i18n: "if" is a keyword
Matt Mackall
templater: add if/ifeq conditionals
r17636 raise error.ParseError(_("if expects two or three arguments"))
test = stringify(args[0][0](context, mapping, args[0][1]))
if test:
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
Durham Goode
template: add ifcontains template function...
r20518 def ifcontains(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:ifcontains(search, thing, then[, else]): Conditionally execute based
on whether the item "search" is in "thing"."""
Durham Goode
template: add ifcontains template function...
r20518 if not (3 <= len(args) <= 4):
# i18n: "ifcontains" is a keyword
raise error.ParseError(_("ifcontains expects three or four arguments"))
item = stringify(args[0][0](context, mapping, args[0][1]))
items = args[1][0](context, mapping, args[1][1])
Yuya Nishihara
templater: implement _hybrid.__contains__ so that ifcontains can accept dict...
r24240 if item in items:
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
Matt Mackall
templater: add if/ifeq conditionals
r17636 def ifeq(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on
whether 2 items are equivalent."""
Matt Mackall
templater: add if/ifeq conditionals
r17636 if not (3 <= len(args) <= 4):
FUJIWARA Katsunori
i18n: add "i18n" comment to error messages of template functions
r17890 # i18n: "ifeq" is a keyword
Matt Mackall
templater: add if/ifeq conditionals
r17636 raise error.ParseError(_("ifeq expects three or four arguments"))
test = stringify(args[0][0](context, mapping, args[0][1]))
match = stringify(args[1][0](context, mapping, args[1][1]))
if test == match:
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
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def join(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:join(list, sep): Join items in a list with a delimiter."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if not (1 <= len(args) <= 2):
# i18n: "join" is a keyword
raise error.ParseError(_("join expects one or two arguments"))
joinset = args[0][0](context, mapping, args[0][1])
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:
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 joiner = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390
first = True
for x in joinset:
if first:
first = False
else:
yield joiner
yield x
Sean Farley
templater: add no-op template function 'label'
r18289 def label(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:label(label, expr): Apply a label to generated content. Content with
a label applied can result in additional post-processing, such as
automatic colorization."""
Sean Farley
templater: add no-op template function 'label'
r18289 if len(args) != 2:
# i18n: "label" is a keyword
raise error.ParseError(_("label expects two arguments"))
# ignore args[0] (the label string) since this is supposed to be a a no-op
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 yield args[1][0](context, mapping, args[1][1])
Sean Farley
templater: add no-op template function 'label'
r18289
Matt Harbison
templater: introduce {latesttag()} function to match a pattern (issue4184)...
r26485 def latesttag(context, mapping, args):
""":latesttag([pattern]): The global tags matching the given pattern on the
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:
pattern = stringify(args[0][0](context, mapping, args[0][1]))
return templatekw.showlatesttags(pattern, **mapping)
Yuya Nishihara
templater: port localdate filter to a function...
r26127 def localdate(context, mapping, args):
Yuya Nishihara
templater: add optional timezone argument to localdate()...
r26128 """:localdate(date[, tz]): Converts a date to the specified timezone.
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):
tzoffset = util.parsetimezone(tz)
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)
Durham Goode
template: add revset() template function...
r20519 def revset(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:revset(query[, formatargs...]): Execute a revision set query. See
:hg:`help revset`."""
Durham Goode
template: add revset() template function...
r20519 if not len(args) > 0:
# i18n: "revset" is a keyword
raise error.ParseError(_("revset expects one or more arguments"))
Yuya Nishihara
templater: evaluate "query" argument passed to revset()...
r25637 raw = stringify(args[0][0](context, mapping, args[0][1]))
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:
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
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
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 def rstdoc(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:rstdoc(text, style): Format ReStructuredText."""
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 if len(args) != 2:
# i18n: "rstdoc" is a keyword
raise error.ParseError(_("rstdoc expects two arguments"))
text = stringify(args[0][0](context, mapping, args[0][1]))
style = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
hgweb: make help verbose again (issue3899)...
r19079 return minirst.format(text, style=style, keep=['verbose'])
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747
Durham Goode
template: add shortest(node) template function...
r20369 def shortest(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:shortest(node, minlength=4): Obtain the shortest representation of
a node."""
Durham Goode
template: add shortest(node) template function...
r20369 if not (1 <= len(args) <= 2):
FUJIWARA Katsunori
i18n: add i18n comment to error messages of template functions
r23112 # i18n: "shortest" is a keyword
Durham Goode
template: add shortest(node) template function...
r20369 raise error.ParseError(_("shortest() expects one or two arguments"))
node = stringify(args[0][0](context, mapping, args[0][1]))
minlength = 4
if len(args) > 1:
minlength = int(args[1][1])
cl = mapping['ctx']._repo.changelog
def isvalid(test):
try:
Durham Goode
template: fix shortest(node) function in pure mercurial...
r20371 try:
cl.index.partialmatch(test)
except AttributeError:
# Pure mercurial doesn't support partialmatch on the index.
# Fallback to the slow way.
if cl._partialmatch(test) is None:
return False
Durham Goode
template: add shortest(node) template function...
r20369 try:
Sean Farley
templater: shorten pure integers...
r20539 i = int(test)
# if we are a pure int, then starting with zero will not be
# confused as a rev; or, obviously, if the int is larger than
# the value of the tip rev
if test[0] == '0' or i > len(cl):
return True
Durham Goode
template: add shortest(node) template function...
r20369 return False
except ValueError:
return True
except error.RevlogError:
return False
shortest = node
startlength = max(6, minlength)
length = startlength
while True:
test = node[:length]
if isvalid(test):
shortest = test
if length == minlength or length > startlength:
return shortest
length -= 1
else:
length += 1
if len(shortest) <= length:
return shortest
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 def strip(context, mapping, args):
Yuya Nishihara
templatefilters: remove redundant 'date' and 'strip' filters...
r26106 """:strip(text[, chars]): Strip characters from a string. By default,
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"))
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 text = stringify(args[0][0](context, mapping, args[0][1]))
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 if len(args) == 2:
FUJIWARA Katsunori
templater: apply "stringify()" on sub expression to get string correctly...
r20662 chars = stringify(args[1][0](context, mapping, args[1][1]))
Alexander Plavin
templater: add strip function with chars as an extra argument...
r19330 return text.strip(chars)
return text.strip()
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 def sub(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:sub(pattern, replacement, expression): Perform text substitution
using regular expressions."""
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 if len(args) != 3:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub expects three arguments"))
pat = stringify(args[0][0](context, mapping, args[0][1]))
rpl = stringify(args[1][0](context, mapping, args[1][1]))
Yuya Nishihara
templater: do not reevaluate rawstring as template (BC)...
r25597 src = stringify(args[2][0](context, mapping, args[2][1]))
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
Ryan McElroy
templater: introduce startswith function...
r21821 def startswith(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:startswith(pattern, text): Returns the value from the "text" argument
if it begins with the content from the "pattern" argument."""
Ryan McElroy
templater: introduce startswith function...
r21821 if len(args) != 2:
FUJIWARA Katsunori
templater: add i18n comments to error messages of newly added functions...
r21960 # i18n: "startswith" is a keyword
Ryan McElroy
templater: introduce startswith function...
r21821 raise error.ParseError(_("startswith expects two arguments"))
patn = stringify(args[0][0](context, mapping, args[0][1]))
text = stringify(args[1][0](context, mapping, args[1][1]))
if text.startswith(patn):
return text
return ''
Ryan McElroy
templater: introduce word function...
r21846 def word(context, mapping, args):
Gregory Szorc
templater: add consistent docstrings to functions...
r24586 """:word(number, text[, separator]): Return the nth word from a string."""
Ryan McElroy
templater: introduce word function...
r21846 if not (2 <= len(args) <= 3):
FUJIWARA Katsunori
templater: add i18n comments to error messages of newly added functions...
r21960 # i18n: "word" is a keyword
Ryan McElroy
templater: introduce word function...
r21846 raise error.ParseError(_("word expects two or three arguments, got %d")
% len(args))
Ryan McElroy
templater: fail more gracefully for blank strings to word
r24886 try:
num = int(stringify(args[0][0](context, mapping, args[0][1])))
except ValueError:
# i18n: "word" is a keyword
Yuya Nishihara
templater: update error message of invalid number passed to word() function...
r25003 raise error.ParseError(_("word expects an integer index"))
Ryan McElroy
templater: introduce word function...
r21846 text = stringify(args[1][0](context, mapping, args[1][1]))
if len(args) == 3:
splitter = stringify(args[2][0](context, mapping, args[2][1]))
else:
splitter = None
tokens = text.split(splitter)
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
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,
Ryan McElroy
templater: introduce indent function
r25489 "indent": indent,
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,
Matt Harbison
templater: introduce {latesttag()} function to match a pattern (issue4184)...
r26485 "latesttag": latesttag,
Yuya Nishihara
templater: port localdate filter to a function...
r26127 "localdate": localdate,
Durham Goode
template: add pad function for padding output...
r20370 "pad": pad,
Durham Goode
template: add revset() template function...
r20519 "revset": revset,
Dan Villiom Podlaski Christiansen
hgweb: generate HTML documentation...
r18747 "rstdoc": rstdoc,
Durham Goode
template: add shortest(node) template function...
r20369 "shortest": shortest,
Ryan McElroy
templater: introduce startswith function...
r21821 "startswith": startswith,
Alexander Plavin
templater: sort functions alphabetically, as filters are
r19390 "strip": strip,
Matt Mackall
templater: add sub() function
r17635 "sub": sub,
Ryan McElroy
templater: introduce word function...
r21846 "word": word,
Matt Mackall
templater: use a global funcs table
r14925 }
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # template engine
Vadim Gelfer
use safer string parser for template engine.
r1901
Dirkjan Ochtman
templater: provide the standard template filters by default
r8360 stringify = templatefilters.stringify
Brendan Cully
Allow hgweb to search for templates in more than one path....
r7107
Matt Mackall
templater: use recursive flattening...
r10850 def _flatten(thing):
'''yield a single stream from a possibly nested set of iterators'''
if isinstance(thing, str):
yield thing
Augie Fackler
globally: use safehasattr(x, '__iter__') instead of hasattr(x, '__iter__')
r14944 elif not util.safehasattr(thing, '__iter__'):
Dirkjan Ochtman
cleanups: undefined variables
r11305 if thing is not None:
Matt Mackall
templater: raise nested functions
r10852 yield str(thing)
else:
Matt Mackall
templater: use recursive flattening...
r10850 for i in thing:
if isinstance(i, str):
yield i
Augie Fackler
globally: use safehasattr(x, '__iter__') instead of hasattr(x, '__iter__')
r14944 elif not util.safehasattr(i, '__iter__'):
Matt Mackall
templater: raise nested functions
r10852 if i is not None:
yield str(i)
Matt Mackall
templater: use recursive flattening...
r10850 elif i is not None:
for j in _flatten(i):
yield j
Yuya Nishihara
templater: rename parsestring() to unquotestring() (API)...
r24988 def unquotestring(s):
Yuya Nishihara
templater: remove noop calls of parsestring(s, quoted=False) (API)...
r24987 '''unwrap quotes'''
if len(s) < 2 or s[0] != s[-1]:
raise SyntaxError(_('unmatched quotes'))
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|...}.'''
Pierre-Yves David
templater: remove a mutable default argument...
r26331 def __init__(self, loader, filters=None, defaults=None):
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
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')
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(".")
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
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):
Pierre-Yves David
templater: remove a mutable default argument...
r26334 def __init__(self, mapfile, filters=None, defaults=None, cache=None,
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.
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.'''
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 = {}
Vadim Gelfer
improve template errors when something is wrong.
r1905 self.mapfile = mapfile or 'template'
Shun-ichi Goto
Duplicate cache when creating templater.
r1975 self.cache = cache.copy()
Vadim Gelfer
move hgweb template code out to templater
r1896 self.map = {}
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if mapfile:
self.base = os.path.dirname(mapfile)
else:
self.base = ''
Dirkjan Ochtman
templater: provide the standard template filters by default
r8360 self.filters = templatefilters.filters.copy()
self.filters.update(filters)
Vadim Gelfer
fix template bug that made hgweb break....
r1964 self.defaults = defaults
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 self.minchunk, self.maxchunk = minchunk, maxchunk
Matt Mackall
templater: clarify engine caching
r13187 self.ecache = {}
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
improve template errors when something is wrong.
r1905 if not mapfile:
return
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337 if not os.path.exists(mapfile):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("style '%s' not found") % mapfile,
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 hint=_("available styles: %s") % stylelist())
Dirkjan Ochtman
give better error message on non-existent mapfile (issue813)
r6337
Jordi Gutiérrez Hermoso
templater: look for mapfiles in template paths...
r25096 conf = config.config(includepaths=templatepaths())
Matt Mackall
templater: use new config parser...
r8194 conf.read(mapfile)
for key, val in conf[''].items():
Ross Lagerwall
templater: handle a missing value correctly...
r17334 if not val:
raise SyntaxError(_('%s: missing value') % conf.source('', key))
Matt Mackall
templater: use new config parser...
r8194 if val[0] in "'\"":
try:
Yuya Nishihara
templater: rename parsestring() to unquotestring() (API)...
r24988 self.cache[key] = unquotestring(val)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except SyntaxError as inst:
Matt Mackall
templater: use new config parser...
r8194 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])
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:
self.ecache[ttype] = engines[ttype](self.load,
self.filters, self.defaults)
proc = self.ecache[ttype]
Dirkjan Ochtman
templater: make the templating engine pluggable to some extent
r8361
Matt Mackall
templater: map -> mapping
r10847 stream = proc.process(t, mapping)
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 if self.minchunk:
stream = util.increasingchunks(stream, min=self.minchunk,
max=self.maxchunk)
return stream
Dirkjan Ochtman
kill some trailing spaces
r7434
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 def templatepaths():
'''return locations used for template files.'''
Mads Kiilerich
templater: don't search randomly for templates - trust util.datapath...
r22636 pathsrel = ['templates']
Mads Kiilerich
templater: inline global 'path' list in templatepaths
r22635 paths = [os.path.normpath(os.path.join(util.datapath, f))
for f in pathsrel]
return [p for p in paths if os.path.isdir(p)]
Vadim Gelfer
move changeset_templater into templater module.
r2189
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 def templatepath(name):
'''return location of template file. returns None if not found.'''
for p in templatepaths():
f = os.path.join(p, name)
if os.path.exists(f):
return f
return None
Vadim Gelfer
move changeset_templater into templater module.
r2189
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 def stylemap(styles, paths=None):
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 """Return path to mapfile for a given style.
Searches mapfile in the following locations:
1. templatepath/style/map
2. templatepath/map-style
3. templatepath/map
"""
if paths is None:
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 paths = templatepaths()
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 elif isinstance(paths, str):
Dirkjan Ochtman
templater: fix little problem from stylemap() changes
r8223 paths = [paths]
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 if isinstance(styles, str):
styles = [styles]
for style in styles:
Yuya Nishihara
hgweb: prevent loading style map from directories other than specified paths...
r24296 # only plain name is allowed to honor template paths
if (not style
or style in (os.curdir, os.pardir)
or os.sep in style
or os.altsep and os.altsep in style):
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 continue
locations = [os.path.join(style, 'map'), 'map-' + style]
locations.append('map')
for path in paths:
for location in locations:
mapfile = os.path.join(path, location)
if os.path.isfile(mapfile):
return style, mapfile
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
raise RuntimeError("No hgweb templates found in %r" % paths)
Yuya Nishihara
templater: tell hggettext to collect help of template functions
r24601
# tell hggettext to extract docstrings from these functions:
i18nfunctions = funcs.values()