templater: split template functions to new module
It has grown enough to be a dedicated module.
--- a/Makefile Thu Mar 08 23:15:09 2018 +0900
+++ b/Makefile Thu Mar 08 22:23:02 2018 +0900
@@ -132,8 +132,9 @@
$(PYTHON) i18n/hggettext mercurial/commands.py \
hgext/*.py hgext/*/__init__.py \
mercurial/fileset.py mercurial/revset.py \
- mercurial/templatefilters.py mercurial/templatekw.py \
- mercurial/templater.py \
+ mercurial/templatefilters.py \
+ mercurial/templatefuncs.py \
+ mercurial/templatekw.py \
mercurial/filemerge.py \
mercurial/hgweb/webcommands.py \
mercurial/util.py \
--- a/mercurial/extensions.py Thu Mar 08 23:15:09 2018 +0900
+++ b/mercurial/extensions.py Thu Mar 08 22:23:02 2018 +0900
@@ -290,8 +290,8 @@
fileset,
revset,
templatefilters,
+ templatefuncs,
templatekw,
- templater,
)
# list of (objname, loadermod, loadername) tuple:
@@ -307,7 +307,7 @@
('internalmerge', filemerge, 'loadinternalmerge'),
('revsetpredicate', revset, 'loadpredicate'),
('templatefilter', templatefilters, 'loadfilter'),
- ('templatefunc', templater, 'loadfunction'),
+ ('templatefunc', templatefuncs, 'loadfunction'),
('templatekeyword', templatekw, 'loadkeyword'),
]
_loadextra(ui, newindex, extraloaders)
--- a/mercurial/help.py Thu Mar 08 23:15:09 2018 +0900
+++ b/mercurial/help.py Thu Mar 08 22:23:02 2018 +0900
@@ -26,8 +26,8 @@
pycompat,
revset,
templatefilters,
+ templatefuncs,
templatekw,
- templater,
util,
)
from .hgweb import (
@@ -309,7 +309,7 @@
addtopicsymbols('revisions', '.. predicatesmarker', revset.symbols)
addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
-addtopicsymbols('templates', '.. functionsmarker', templater.funcs)
+addtopicsymbols('templates', '.. functionsmarker', templatefuncs.funcs)
addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
dedent=True)
--- a/mercurial/registrar.py Thu Mar 08 23:15:09 2018 +0900
+++ b/mercurial/registrar.py Thu Mar 08 22:23:02 2018 +0900
@@ -368,7 +368,7 @@
extension, if an instance named as 'templatefunc' is used for
decorating in extension.
- Otherwise, explicit 'templater.loadfunction()' is needed.
+ Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
"""
_getname = _funcregistrarbase._parsefuncdecl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templatefuncs.py Thu Mar 08 22:23:02 2018 +0900
@@ -0,0 +1,664 @@
+# templatefuncs.py - common template functions
+#
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import re
+
+from .i18n import _
+from . import (
+ color,
+ encoding,
+ error,
+ minirst,
+ obsutil,
+ pycompat,
+ registrar,
+ revset as revsetmod,
+ revsetlang,
+ scmutil,
+ templatefilters,
+ templatekw,
+ templateutil,
+ util,
+)
+from .utils import dateutil
+
+evalrawexp = templateutil.evalrawexp
+evalfuncarg = templateutil.evalfuncarg
+evalboolean = templateutil.evalboolean
+evalinteger = templateutil.evalinteger
+evalstring = templateutil.evalstring
+evalstringliteral = templateutil.evalstringliteral
+evalastype = templateutil.evalastype
+
+# dict of template built-in functions
+funcs = {}
+templatefunc = registrar.templatefunc(funcs)
+
+@templatefunc('date(date[, fmt])')
+def date(context, mapping, args):
+ """Format a date. See :hg:`help dates` for formatting
+ strings. The default is a Unix date format, including the timezone:
+ "Mon Sep 04 15:13:13 2006 0700"."""
+ if not (1 <= len(args) <= 2):
+ # i18n: "date" is a keyword
+ raise error.ParseError(_("date expects one or two arguments"))
+
+ date = evalfuncarg(context, mapping, args[0])
+ fmt = None
+ if len(args) == 2:
+ fmt = evalstring(context, mapping, args[1])
+ try:
+ if fmt is None:
+ return dateutil.datestr(date)
+ else:
+ return dateutil.datestr(date, fmt)
+ except (TypeError, ValueError):
+ # i18n: "date" is a keyword
+ raise error.ParseError(_("date expects a date information"))
+
+@templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
+def dict_(context, mapping, args):
+ """Construct a dict from key-value pairs. A key may be omitted if
+ a value expression can provide an unambiguous name."""
+ data = util.sortdict()
+
+ for v in args['args']:
+ k = templateutil.findsymbolicname(v)
+ if not k:
+ raise error.ParseError(_('dict key cannot be inferred'))
+ if k in data or k in args['kwargs']:
+ raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
+ data[k] = evalfuncarg(context, mapping, v)
+
+ data.update((k, evalfuncarg(context, mapping, v))
+ for k, v in args['kwargs'].iteritems())
+ return templateutil.hybriddict(data)
+
+@templatefunc('diff([includepattern [, excludepattern]])')
+def diff(context, mapping, args):
+ """Show a diff, optionally
+ specifying files to include or exclude."""
+ if len(args) > 2:
+ # i18n: "diff" is a keyword
+ raise error.ParseError(_("diff expects zero, one, or two arguments"))
+
+ def getpatterns(i):
+ if i < len(args):
+ s = evalstring(context, mapping, args[i]).strip()
+ if s:
+ return [s]
+ return []
+
+ ctx = context.resource(mapping, 'ctx')
+ chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
+
+ return ''.join(chunks)
+
+@templatefunc('extdata(source)', argspec='source')
+def extdata(context, mapping, args):
+ """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
+ if 'source' not in args:
+ # i18n: "extdata" is a keyword
+ raise error.ParseError(_('extdata expects one argument'))
+
+ source = evalstring(context, mapping, args['source'])
+ cache = context.resource(mapping, 'cache').setdefault('extdata', {})
+ ctx = context.resource(mapping, 'ctx')
+ if source in cache:
+ data = cache[source]
+ else:
+ data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
+ return data.get(ctx.rev(), '')
+
+@templatefunc('files(pattern)')
+def files(context, mapping, args):
+ """All files of the current changeset matching the pattern. See
+ :hg:`help patterns`."""
+ if not len(args) == 1:
+ # i18n: "files" is a keyword
+ raise error.ParseError(_("files expects one argument"))
+
+ raw = evalstring(context, mapping, args[0])
+ ctx = context.resource(mapping, 'ctx')
+ m = ctx.match([raw])
+ files = list(ctx.matches(m))
+ return templateutil.compatlist(context, mapping, "file", files)
+
+@templatefunc('fill(text[, width[, initialident[, hangindent]]])')
+def fill(context, mapping, args):
+ """Fill many
+ paragraphs with optional indentation. See the "fill" filter."""
+ if not (1 <= len(args) <= 4):
+ # i18n: "fill" is a keyword
+ raise error.ParseError(_("fill expects one to four arguments"))
+
+ text = evalstring(context, mapping, args[0])
+ width = 76
+ initindent = ''
+ hangindent = ''
+ if 2 <= len(args) <= 4:
+ width = evalinteger(context, mapping, args[1],
+ # i18n: "fill" is a keyword
+ _("fill expects an integer width"))
+ try:
+ initindent = evalstring(context, mapping, args[2])
+ hangindent = evalstring(context, mapping, args[3])
+ except IndexError:
+ pass
+
+ return templatefilters.fill(text, width, initindent, hangindent)
+
+@templatefunc('formatnode(node)')
+def formatnode(context, mapping, args):
+ """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
+ if len(args) != 1:
+ # i18n: "formatnode" is a keyword
+ raise error.ParseError(_("formatnode expects one argument"))
+
+ ui = context.resource(mapping, 'ui')
+ node = evalstring(context, mapping, args[0])
+ if ui.debugflag:
+ return node
+ return templatefilters.short(node)
+
+@templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
+ argspec='text width fillchar left')
+def pad(context, mapping, args):
+ """Pad text with a
+ fill character."""
+ if 'text' not in args or 'width' not in args:
+ # i18n: "pad" is a keyword
+ raise error.ParseError(_("pad() expects two to four arguments"))
+
+ width = evalinteger(context, mapping, args['width'],
+ # i18n: "pad" is a keyword
+ _("pad() expects an integer width"))
+
+ text = evalstring(context, mapping, args['text'])
+
+ left = False
+ fillchar = ' '
+ if 'fillchar' in args:
+ fillchar = evalstring(context, mapping, args['fillchar'])
+ if len(color.stripeffects(fillchar)) != 1:
+ # i18n: "pad" is a keyword
+ raise error.ParseError(_("pad() expects a single fill character"))
+ if 'left' in args:
+ left = evalboolean(context, mapping, args['left'])
+
+ fillwidth = width - encoding.colwidth(color.stripeffects(text))
+ if fillwidth <= 0:
+ return text
+ if left:
+ return fillchar * fillwidth + text
+ else:
+ return text + fillchar * fillwidth
+
+@templatefunc('indent(text, indentchars[, firstline])')
+def indent(context, mapping, args):
+ """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 = evalstring(context, mapping, args[0])
+ indent = evalstring(context, mapping, args[1])
+
+ if len(args) == 3:
+ firstline = evalstring(context, mapping, args[2])
+ else:
+ firstline = indent
+
+ # the indent function doesn't indent the first line, so we do it here
+ return templatefilters.indent(firstline + text, indent)
+
+@templatefunc('get(dict, key)')
+def get(context, mapping, args):
+ """Get an attribute/key from an object. Some keywords
+ are complex types. This function allows you to obtain the value of an
+ attribute on these types."""
+ if len(args) != 2:
+ # i18n: "get" is a keyword
+ raise error.ParseError(_("get() expects two arguments"))
+
+ dictarg = evalfuncarg(context, mapping, args[0])
+ if not util.safehasattr(dictarg, 'get'):
+ # i18n: "get" is a keyword
+ raise error.ParseError(_("get() expects a dict as first argument"))
+
+ key = evalfuncarg(context, mapping, args[1])
+ return templateutil.getdictitem(dictarg, key)
+
+@templatefunc('if(expr, then[, else])')
+def if_(context, mapping, args):
+ """Conditionally execute based on the result of
+ an expression."""
+ if not (2 <= len(args) <= 3):
+ # i18n: "if" is a keyword
+ raise error.ParseError(_("if expects two or three arguments"))
+
+ test = evalboolean(context, mapping, args[0])
+ if test:
+ yield evalrawexp(context, mapping, args[1])
+ elif len(args) == 3:
+ yield evalrawexp(context, mapping, args[2])
+
+@templatefunc('ifcontains(needle, haystack, then[, else])')
+def ifcontains(context, mapping, args):
+ """Conditionally execute based
+ on whether the item "needle" is in "haystack"."""
+ if not (3 <= len(args) <= 4):
+ # i18n: "ifcontains" is a keyword
+ raise error.ParseError(_("ifcontains expects three or four arguments"))
+
+ haystack = evalfuncarg(context, mapping, args[1])
+ try:
+ needle = evalastype(context, mapping, args[0],
+ getattr(haystack, 'keytype', None) or bytes)
+ found = (needle in haystack)
+ except error.ParseError:
+ found = False
+
+ if found:
+ yield evalrawexp(context, mapping, args[2])
+ elif len(args) == 4:
+ yield evalrawexp(context, mapping, args[3])
+
+@templatefunc('ifeq(expr1, expr2, then[, else])')
+def ifeq(context, mapping, args):
+ """Conditionally execute based on
+ whether 2 items are equivalent."""
+ if not (3 <= len(args) <= 4):
+ # i18n: "ifeq" is a keyword
+ raise error.ParseError(_("ifeq expects three or four arguments"))
+
+ test = evalstring(context, mapping, args[0])
+ match = evalstring(context, mapping, args[1])
+ if test == match:
+ yield evalrawexp(context, mapping, args[2])
+ elif len(args) == 4:
+ yield evalrawexp(context, mapping, args[3])
+
+@templatefunc('join(list, sep)')
+def join(context, mapping, args):
+ """Join items in a list with a delimiter."""
+ if not (1 <= len(args) <= 2):
+ # i18n: "join" is a keyword
+ raise error.ParseError(_("join expects one or two arguments"))
+
+ # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
+ # abuses generator as a keyword that returns a list of dicts.
+ joinset = evalrawexp(context, mapping, args[0])
+ joinset = templateutil.unwrapvalue(joinset)
+ joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
+ joiner = " "
+ if len(args) > 1:
+ joiner = evalstring(context, mapping, args[1])
+
+ first = True
+ for x in pycompat.maybebytestr(joinset):
+ if first:
+ first = False
+ else:
+ yield joiner
+ yield joinfmt(x)
+
+@templatefunc('label(label, expr)')
+def label(context, mapping, args):
+ """Apply a label to generated content. Content with
+ a label applied can result in additional post-processing, such as
+ automatic colorization."""
+ if len(args) != 2:
+ # i18n: "label" is a keyword
+ raise error.ParseError(_("label expects two arguments"))
+
+ ui = context.resource(mapping, 'ui')
+ 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])
+
+ return ui.label(thing, label)
+
+@templatefunc('latesttag([pattern])')
+def latesttag(context, mapping, args):
+ """The global tags matching the given pattern on the
+ most recent globally tagged ancestor of this changeset.
+ If no such tags exist, the "{tag}" template resolves to
+ the string "null"."""
+ 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 = evalstring(context, mapping, args[0])
+ return templatekw.showlatesttags(context, mapping, pattern)
+
+@templatefunc('localdate(date[, tz])')
+def localdate(context, mapping, args):
+ """Converts a date to the specified timezone.
+ The default is local date."""
+ if not (1 <= len(args) <= 2):
+ # i18n: "localdate" is a keyword
+ raise error.ParseError(_("localdate expects one or two arguments"))
+
+ date = evalfuncarg(context, mapping, args[0])
+ try:
+ date = dateutil.parsedate(date)
+ except AttributeError: # not str nor date tuple
+ # i18n: "localdate" is a keyword
+ raise error.ParseError(_("localdate expects a date information"))
+ if len(args) >= 2:
+ tzoffset = None
+ tz = evalfuncarg(context, mapping, args[1])
+ if isinstance(tz, bytes):
+ tzoffset, remainder = dateutil.parsetimezone(tz)
+ if remainder:
+ tzoffset = None
+ 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 = dateutil.makedate()[1]
+ return (date[0], tzoffset)
+
+@templatefunc('max(iterable)')
+def max_(context, mapping, args, **kwargs):
+ """Return the max of an iterable"""
+ if len(args) != 1:
+ # i18n: "max" is a keyword
+ raise error.ParseError(_("max expects one argument"))
+
+ iterable = evalfuncarg(context, mapping, args[0])
+ try:
+ x = max(pycompat.maybebytestr(iterable))
+ except (TypeError, ValueError):
+ # i18n: "max" is a keyword
+ raise error.ParseError(_("max first argument should be an iterable"))
+ return templateutil.wraphybridvalue(iterable, x, x)
+
+@templatefunc('min(iterable)')
+def min_(context, mapping, args, **kwargs):
+ """Return the min of an iterable"""
+ if len(args) != 1:
+ # i18n: "min" is a keyword
+ raise error.ParseError(_("min expects one argument"))
+
+ iterable = evalfuncarg(context, mapping, args[0])
+ try:
+ x = min(pycompat.maybebytestr(iterable))
+ except (TypeError, ValueError):
+ # i18n: "min" is a keyword
+ raise error.ParseError(_("min first argument should be an iterable"))
+ return templateutil.wraphybridvalue(iterable, x, x)
+
+@templatefunc('mod(a, b)')
+def mod(context, mapping, args):
+ """Calculate a mod b such that a / b + a mod b == a"""
+ if not len(args) == 2:
+ # i18n: "mod" is a keyword
+ raise error.ParseError(_("mod expects two arguments"))
+
+ func = lambda a, b: a % b
+ return templateutil.runarithmetic(context, mapping,
+ (func, args[0], args[1]))
+
+@templatefunc('obsfateoperations(markers)')
+def obsfateoperations(context, mapping, args):
+ """Compute obsfate related information based on markers (EXPERIMENTAL)"""
+ if len(args) != 1:
+ # i18n: "obsfateoperations" is a keyword
+ raise error.ParseError(_("obsfateoperations expects one argument"))
+
+ markers = evalfuncarg(context, mapping, args[0])
+
+ try:
+ data = obsutil.markersoperations(markers)
+ return templateutil.hybridlist(data, name='operation')
+ except (TypeError, KeyError):
+ # i18n: "obsfateoperations" is a keyword
+ errmsg = _("obsfateoperations first argument should be an iterable")
+ raise error.ParseError(errmsg)
+
+@templatefunc('obsfatedate(markers)')
+def obsfatedate(context, mapping, args):
+ """Compute obsfate related information based on markers (EXPERIMENTAL)"""
+ if len(args) != 1:
+ # i18n: "obsfatedate" is a keyword
+ raise error.ParseError(_("obsfatedate expects one argument"))
+
+ markers = evalfuncarg(context, mapping, args[0])
+
+ try:
+ data = obsutil.markersdates(markers)
+ return templateutil.hybridlist(data, name='date', fmt='%d %d')
+ except (TypeError, KeyError):
+ # i18n: "obsfatedate" is a keyword
+ errmsg = _("obsfatedate first argument should be an iterable")
+ raise error.ParseError(errmsg)
+
+@templatefunc('obsfateusers(markers)')
+def obsfateusers(context, mapping, args):
+ """Compute obsfate related information based on markers (EXPERIMENTAL)"""
+ if len(args) != 1:
+ # i18n: "obsfateusers" is a keyword
+ raise error.ParseError(_("obsfateusers expects one argument"))
+
+ markers = evalfuncarg(context, mapping, args[0])
+
+ try:
+ data = obsutil.markersusers(markers)
+ return templateutil.hybridlist(data, name='user')
+ except (TypeError, KeyError, ValueError):
+ # i18n: "obsfateusers" is a keyword
+ msg = _("obsfateusers first argument should be an iterable of "
+ "obsmakers")
+ raise error.ParseError(msg)
+
+@templatefunc('obsfateverb(successors, markers)')
+def obsfateverb(context, mapping, args):
+ """Compute obsfate related information based on successors (EXPERIMENTAL)"""
+ if len(args) != 2:
+ # i18n: "obsfateverb" is a keyword
+ raise error.ParseError(_("obsfateverb expects two arguments"))
+
+ successors = evalfuncarg(context, mapping, args[0])
+ markers = evalfuncarg(context, mapping, args[1])
+
+ try:
+ return obsutil.obsfateverb(successors, markers)
+ except TypeError:
+ # i18n: "obsfateverb" is a keyword
+ errmsg = _("obsfateverb first argument should be countable")
+ raise error.ParseError(errmsg)
+
+@templatefunc('relpath(path)')
+def relpath(context, mapping, args):
+ """Convert a repository-absolute path into a filesystem path relative to
+ the current working directory."""
+ if len(args) != 1:
+ # i18n: "relpath" is a keyword
+ raise error.ParseError(_("relpath expects one argument"))
+
+ repo = context.resource(mapping, 'ctx').repo()
+ path = evalstring(context, mapping, args[0])
+ return repo.pathto(path)
+
+@templatefunc('revset(query[, formatargs...])')
+def revset(context, mapping, args):
+ """Execute a revision set query. See
+ :hg:`help revset`."""
+ if not len(args) > 0:
+ # i18n: "revset" is a keyword
+ raise error.ParseError(_("revset expects one or more arguments"))
+
+ raw = evalstring(context, mapping, args[0])
+ ctx = context.resource(mapping, 'ctx')
+ repo = ctx.repo()
+
+ def query(expr):
+ m = revsetmod.match(repo.ui, expr, repo=repo)
+ return m(repo)
+
+ if len(args) > 1:
+ formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
+ revs = query(revsetlang.formatspec(raw, *formatargs))
+ revs = list(revs)
+ else:
+ cache = context.resource(mapping, 'cache')
+ revsetcache = cache.setdefault("revsetcache", {})
+ if raw in revsetcache:
+ revs = revsetcache[raw]
+ else:
+ revs = query(raw)
+ revs = list(revs)
+ revsetcache[raw] = revs
+ return templatekw.showrevslist(context, mapping, "revision", revs)
+
+@templatefunc('rstdoc(text, style)')
+def rstdoc(context, mapping, args):
+ """Format reStructuredText."""
+ if len(args) != 2:
+ # i18n: "rstdoc" is a keyword
+ raise error.ParseError(_("rstdoc expects two arguments"))
+
+ text = evalstring(context, mapping, args[0])
+ style = evalstring(context, mapping, args[1])
+
+ return minirst.format(text, style=style, keep=['verbose'])
+
+@templatefunc('separate(sep, args)', argspec='sep *args')
+def separate(context, mapping, args):
+ """Add a separator between non-empty arguments."""
+ if 'sep' not in args:
+ # i18n: "separate" is a keyword
+ raise error.ParseError(_("separate expects at least one argument"))
+
+ sep = evalstring(context, mapping, args['sep'])
+ first = True
+ for arg in args['args']:
+ argstr = evalstring(context, mapping, arg)
+ if not argstr:
+ continue
+ if first:
+ first = False
+ else:
+ yield sep
+ yield argstr
+
+@templatefunc('shortest(node, minlength=4)')
+def shortest(context, mapping, args):
+ """Obtain the shortest representation of
+ a node."""
+ if not (1 <= len(args) <= 2):
+ # i18n: "shortest" is a keyword
+ raise error.ParseError(_("shortest() expects one or two arguments"))
+
+ node = evalstring(context, mapping, args[0])
+
+ minlength = 4
+ if len(args) > 1:
+ minlength = evalinteger(context, mapping, args[1],
+ # i18n: "shortest" is a keyword
+ _("shortest() expects an integer minlength"))
+
+ # _partialmatch() of filtered changelog could take O(len(repo)) time,
+ # which would be unacceptably slow. so we look for hash collision in
+ # unfiltered space, which means some hashes may be slightly longer.
+ cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog
+ return cl.shortest(node, minlength)
+
+@templatefunc('strip(text[, chars])')
+def strip(context, mapping, args):
+ """Strip characters from a string. By default,
+ strips all leading and trailing whitespace."""
+ if not (1 <= len(args) <= 2):
+ # i18n: "strip" is a keyword
+ raise error.ParseError(_("strip expects one or two arguments"))
+
+ text = evalstring(context, mapping, args[0])
+ if len(args) == 2:
+ chars = evalstring(context, mapping, args[1])
+ return text.strip(chars)
+ return text.strip()
+
+@templatefunc('sub(pattern, replacement, expression)')
+def sub(context, mapping, args):
+ """Perform text substitution
+ using regular expressions."""
+ if len(args) != 3:
+ # i18n: "sub" is a keyword
+ raise error.ParseError(_("sub expects three arguments"))
+
+ pat = evalstring(context, mapping, args[0])
+ rpl = evalstring(context, mapping, args[1])
+ src = evalstring(context, mapping, args[2])
+ 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)
+
+@templatefunc('startswith(pattern, text)')
+def startswith(context, mapping, args):
+ """Returns the value from the "text" argument
+ if it begins with the content from the "pattern" argument."""
+ if len(args) != 2:
+ # i18n: "startswith" is a keyword
+ raise error.ParseError(_("startswith expects two arguments"))
+
+ patn = evalstring(context, mapping, args[0])
+ text = evalstring(context, mapping, args[1])
+ if text.startswith(patn):
+ return text
+ return ''
+
+@templatefunc('word(number, text[, separator])')
+def word(context, mapping, args):
+ """Return the nth word from a string."""
+ if not (2 <= len(args) <= 3):
+ # i18n: "word" is a keyword
+ raise error.ParseError(_("word expects two or three arguments, got %d")
+ % len(args))
+
+ num = evalinteger(context, mapping, args[0],
+ # i18n: "word" is a keyword
+ _("word expects an integer index"))
+ text = evalstring(context, mapping, args[1])
+ if len(args) == 3:
+ splitter = evalstring(context, mapping, args[2])
+ else:
+ splitter = None
+
+ tokens = text.split(splitter)
+ if num >= len(tokens) or num < -len(tokens):
+ return ''
+ else:
+ return tokens[num]
+
+def loadfunction(ui, extname, registrarobj):
+ """Load template function from specified registrarobj
+ """
+ for name, func in registrarobj._table.iteritems():
+ funcs[name] = func
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = funcs.values()
--- a/mercurial/templater.py Thu Mar 08 23:15:09 2018 +0900
+++ b/mercurial/templater.py Thu Mar 08 22:23:02 2018 +0900
@@ -8,36 +8,19 @@
from __future__ import absolute_import, print_function
import os
-import re
from .i18n import _
from . import (
- color,
config,
encoding,
error,
- minirst,
- obsutil,
parser,
pycompat,
- registrar,
- revset as revsetmod,
- revsetlang,
- scmutil,
templatefilters,
- templatekw,
+ templatefuncs,
templateutil,
util,
)
-from .utils import dateutil
-
-evalrawexp = templateutil.evalrawexp
-evalfuncarg = templateutil.evalfuncarg
-evalboolean = templateutil.evalboolean
-evalinteger = templateutil.evalinteger
-evalstring = templateutil.evalstring
-evalstringliteral = templateutil.evalstringliteral
-evalastype = templateutil.evalastype
# template parsing
@@ -455,625 +438,6 @@
def buildkeyvaluepair(exp, content):
raise error.ParseError(_("can't use a key-value pair in this context"))
-# dict of template built-in functions
-funcs = {}
-
-templatefunc = registrar.templatefunc(funcs)
-
-@templatefunc('date(date[, fmt])')
-def date(context, mapping, args):
- """Format a date. See :hg:`help dates` for formatting
- strings. The default is a Unix date format, including the timezone:
- "Mon Sep 04 15:13:13 2006 0700"."""
- if not (1 <= len(args) <= 2):
- # i18n: "date" is a keyword
- raise error.ParseError(_("date expects one or two arguments"))
-
- date = evalfuncarg(context, mapping, args[0])
- fmt = None
- if len(args) == 2:
- fmt = evalstring(context, mapping, args[1])
- try:
- if fmt is None:
- return dateutil.datestr(date)
- else:
- return dateutil.datestr(date, fmt)
- except (TypeError, ValueError):
- # i18n: "date" is a keyword
- raise error.ParseError(_("date expects a date information"))
-
-@templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
-def dict_(context, mapping, args):
- """Construct a dict from key-value pairs. A key may be omitted if
- a value expression can provide an unambiguous name."""
- data = util.sortdict()
-
- for v in args['args']:
- k = templateutil.findsymbolicname(v)
- if not k:
- raise error.ParseError(_('dict key cannot be inferred'))
- if k in data or k in args['kwargs']:
- raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
- data[k] = evalfuncarg(context, mapping, v)
-
- data.update((k, evalfuncarg(context, mapping, v))
- for k, v in args['kwargs'].iteritems())
- return templateutil.hybriddict(data)
-
-@templatefunc('diff([includepattern [, excludepattern]])')
-def diff(context, mapping, args):
- """Show a diff, optionally
- specifying files to include or exclude."""
- if len(args) > 2:
- # i18n: "diff" is a keyword
- raise error.ParseError(_("diff expects zero, one, or two arguments"))
-
- def getpatterns(i):
- if i < len(args):
- s = evalstring(context, mapping, args[i]).strip()
- if s:
- return [s]
- return []
-
- ctx = context.resource(mapping, 'ctx')
- chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
-
- return ''.join(chunks)
-
-@templatefunc('extdata(source)', argspec='source')
-def extdata(context, mapping, args):
- """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
- if 'source' not in args:
- # i18n: "extdata" is a keyword
- raise error.ParseError(_('extdata expects one argument'))
-
- source = evalstring(context, mapping, args['source'])
- cache = context.resource(mapping, 'cache').setdefault('extdata', {})
- ctx = context.resource(mapping, 'ctx')
- if source in cache:
- data = cache[source]
- else:
- data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
- return data.get(ctx.rev(), '')
-
-@templatefunc('files(pattern)')
-def files(context, mapping, args):
- """All files of the current changeset matching the pattern. See
- :hg:`help patterns`."""
- if not len(args) == 1:
- # i18n: "files" is a keyword
- raise error.ParseError(_("files expects one argument"))
-
- raw = evalstring(context, mapping, args[0])
- ctx = context.resource(mapping, 'ctx')
- m = ctx.match([raw])
- files = list(ctx.matches(m))
- return templateutil.compatlist(context, mapping, "file", files)
-
-@templatefunc('fill(text[, width[, initialident[, hangindent]]])')
-def fill(context, mapping, args):
- """Fill many
- paragraphs with optional indentation. See the "fill" filter."""
- if not (1 <= len(args) <= 4):
- # i18n: "fill" is a keyword
- raise error.ParseError(_("fill expects one to four arguments"))
-
- text = evalstring(context, mapping, args[0])
- width = 76
- initindent = ''
- hangindent = ''
- if 2 <= len(args) <= 4:
- width = evalinteger(context, mapping, args[1],
- # i18n: "fill" is a keyword
- _("fill expects an integer width"))
- try:
- initindent = evalstring(context, mapping, args[2])
- hangindent = evalstring(context, mapping, args[3])
- except IndexError:
- pass
-
- return templatefilters.fill(text, width, initindent, hangindent)
-
-@templatefunc('formatnode(node)')
-def formatnode(context, mapping, args):
- """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
- if len(args) != 1:
- # i18n: "formatnode" is a keyword
- raise error.ParseError(_("formatnode expects one argument"))
-
- ui = context.resource(mapping, 'ui')
- node = evalstring(context, mapping, args[0])
- if ui.debugflag:
- return node
- return templatefilters.short(node)
-
-@templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
- argspec='text width fillchar left')
-def pad(context, mapping, args):
- """Pad text with a
- fill character."""
- if 'text' not in args or 'width' not in args:
- # i18n: "pad" is a keyword
- raise error.ParseError(_("pad() expects two to four arguments"))
-
- width = evalinteger(context, mapping, args['width'],
- # i18n: "pad" is a keyword
- _("pad() expects an integer width"))
-
- text = evalstring(context, mapping, args['text'])
-
- left = False
- fillchar = ' '
- if 'fillchar' in args:
- fillchar = evalstring(context, mapping, args['fillchar'])
- if len(color.stripeffects(fillchar)) != 1:
- # i18n: "pad" is a keyword
- raise error.ParseError(_("pad() expects a single fill character"))
- if 'left' in args:
- left = evalboolean(context, mapping, args['left'])
-
- fillwidth = width - encoding.colwidth(color.stripeffects(text))
- if fillwidth <= 0:
- return text
- if left:
- return fillchar * fillwidth + text
- else:
- return text + fillchar * fillwidth
-
-@templatefunc('indent(text, indentchars[, firstline])')
-def indent(context, mapping, args):
- """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 = evalstring(context, mapping, args[0])
- indent = evalstring(context, mapping, args[1])
-
- if len(args) == 3:
- firstline = evalstring(context, mapping, args[2])
- else:
- firstline = indent
-
- # the indent function doesn't indent the first line, so we do it here
- return templatefilters.indent(firstline + text, indent)
-
-@templatefunc('get(dict, key)')
-def get(context, mapping, args):
- """Get an attribute/key from an object. Some keywords
- are complex types. This function allows you to obtain the value of an
- attribute on these types."""
- if len(args) != 2:
- # i18n: "get" is a keyword
- raise error.ParseError(_("get() expects two arguments"))
-
- dictarg = evalfuncarg(context, mapping, args[0])
- if not util.safehasattr(dictarg, 'get'):
- # i18n: "get" is a keyword
- raise error.ParseError(_("get() expects a dict as first argument"))
-
- key = evalfuncarg(context, mapping, args[1])
- return templateutil.getdictitem(dictarg, key)
-
-@templatefunc('if(expr, then[, else])')
-def if_(context, mapping, args):
- """Conditionally execute based on the result of
- an expression."""
- if not (2 <= len(args) <= 3):
- # i18n: "if" is a keyword
- raise error.ParseError(_("if expects two or three arguments"))
-
- test = evalboolean(context, mapping, args[0])
- if test:
- yield evalrawexp(context, mapping, args[1])
- elif len(args) == 3:
- yield evalrawexp(context, mapping, args[2])
-
-@templatefunc('ifcontains(needle, haystack, then[, else])')
-def ifcontains(context, mapping, args):
- """Conditionally execute based
- on whether the item "needle" is in "haystack"."""
- if not (3 <= len(args) <= 4):
- # i18n: "ifcontains" is a keyword
- raise error.ParseError(_("ifcontains expects three or four arguments"))
-
- haystack = evalfuncarg(context, mapping, args[1])
- try:
- needle = evalastype(context, mapping, args[0],
- getattr(haystack, 'keytype', None) or bytes)
- found = (needle in haystack)
- except error.ParseError:
- found = False
-
- if found:
- yield evalrawexp(context, mapping, args[2])
- elif len(args) == 4:
- yield evalrawexp(context, mapping, args[3])
-
-@templatefunc('ifeq(expr1, expr2, then[, else])')
-def ifeq(context, mapping, args):
- """Conditionally execute based on
- whether 2 items are equivalent."""
- if not (3 <= len(args) <= 4):
- # i18n: "ifeq" is a keyword
- raise error.ParseError(_("ifeq expects three or four arguments"))
-
- test = evalstring(context, mapping, args[0])
- match = evalstring(context, mapping, args[1])
- if test == match:
- yield evalrawexp(context, mapping, args[2])
- elif len(args) == 4:
- yield evalrawexp(context, mapping, args[3])
-
-@templatefunc('join(list, sep)')
-def join(context, mapping, args):
- """Join items in a list with a delimiter."""
- if not (1 <= len(args) <= 2):
- # i18n: "join" is a keyword
- raise error.ParseError(_("join expects one or two arguments"))
-
- # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
- # abuses generator as a keyword that returns a list of dicts.
- joinset = evalrawexp(context, mapping, args[0])
- joinset = templateutil.unwrapvalue(joinset)
- joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
- joiner = " "
- if len(args) > 1:
- joiner = evalstring(context, mapping, args[1])
-
- first = True
- for x in pycompat.maybebytestr(joinset):
- if first:
- first = False
- else:
- yield joiner
- yield joinfmt(x)
-
-@templatefunc('label(label, expr)')
-def label(context, mapping, args):
- """Apply a label to generated content. Content with
- a label applied can result in additional post-processing, such as
- automatic colorization."""
- if len(args) != 2:
- # i18n: "label" is a keyword
- raise error.ParseError(_("label expects two arguments"))
-
- ui = context.resource(mapping, 'ui')
- 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])
-
- return ui.label(thing, label)
-
-@templatefunc('latesttag([pattern])')
-def latesttag(context, mapping, args):
- """The global tags matching the given pattern on the
- most recent globally tagged ancestor of this changeset.
- If no such tags exist, the "{tag}" template resolves to
- the string "null"."""
- 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 = evalstring(context, mapping, args[0])
- return templatekw.showlatesttags(context, mapping, pattern)
-
-@templatefunc('localdate(date[, tz])')
-def localdate(context, mapping, args):
- """Converts a date to the specified timezone.
- The default is local date."""
- if not (1 <= len(args) <= 2):
- # i18n: "localdate" is a keyword
- raise error.ParseError(_("localdate expects one or two arguments"))
-
- date = evalfuncarg(context, mapping, args[0])
- try:
- date = dateutil.parsedate(date)
- except AttributeError: # not str nor date tuple
- # i18n: "localdate" is a keyword
- raise error.ParseError(_("localdate expects a date information"))
- if len(args) >= 2:
- tzoffset = None
- tz = evalfuncarg(context, mapping, args[1])
- if isinstance(tz, bytes):
- tzoffset, remainder = dateutil.parsetimezone(tz)
- if remainder:
- tzoffset = None
- 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 = dateutil.makedate()[1]
- return (date[0], tzoffset)
-
-@templatefunc('max(iterable)')
-def max_(context, mapping, args, **kwargs):
- """Return the max of an iterable"""
- if len(args) != 1:
- # i18n: "max" is a keyword
- raise error.ParseError(_("max expects one argument"))
-
- iterable = evalfuncarg(context, mapping, args[0])
- try:
- x = max(pycompat.maybebytestr(iterable))
- except (TypeError, ValueError):
- # i18n: "max" is a keyword
- raise error.ParseError(_("max first argument should be an iterable"))
- return templateutil.wraphybridvalue(iterable, x, x)
-
-@templatefunc('min(iterable)')
-def min_(context, mapping, args, **kwargs):
- """Return the min of an iterable"""
- if len(args) != 1:
- # i18n: "min" is a keyword
- raise error.ParseError(_("min expects one argument"))
-
- iterable = evalfuncarg(context, mapping, args[0])
- try:
- x = min(pycompat.maybebytestr(iterable))
- except (TypeError, ValueError):
- # i18n: "min" is a keyword
- raise error.ParseError(_("min first argument should be an iterable"))
- return templateutil.wraphybridvalue(iterable, x, x)
-
-@templatefunc('mod(a, b)')
-def mod(context, mapping, args):
- """Calculate a mod b such that a / b + a mod b == a"""
- if not len(args) == 2:
- # i18n: "mod" is a keyword
- raise error.ParseError(_("mod expects two arguments"))
-
- func = lambda a, b: a % b
- return templateutil.runarithmetic(context, mapping,
- (func, args[0], args[1]))
-
-@templatefunc('obsfateoperations(markers)')
-def obsfateoperations(context, mapping, args):
- """Compute obsfate related information based on markers (EXPERIMENTAL)"""
- if len(args) != 1:
- # i18n: "obsfateoperations" is a keyword
- raise error.ParseError(_("obsfateoperations expects one argument"))
-
- markers = evalfuncarg(context, mapping, args[0])
-
- try:
- data = obsutil.markersoperations(markers)
- return templateutil.hybridlist(data, name='operation')
- except (TypeError, KeyError):
- # i18n: "obsfateoperations" is a keyword
- errmsg = _("obsfateoperations first argument should be an iterable")
- raise error.ParseError(errmsg)
-
-@templatefunc('obsfatedate(markers)')
-def obsfatedate(context, mapping, args):
- """Compute obsfate related information based on markers (EXPERIMENTAL)"""
- if len(args) != 1:
- # i18n: "obsfatedate" is a keyword
- raise error.ParseError(_("obsfatedate expects one argument"))
-
- markers = evalfuncarg(context, mapping, args[0])
-
- try:
- data = obsutil.markersdates(markers)
- return templateutil.hybridlist(data, name='date', fmt='%d %d')
- except (TypeError, KeyError):
- # i18n: "obsfatedate" is a keyword
- errmsg = _("obsfatedate first argument should be an iterable")
- raise error.ParseError(errmsg)
-
-@templatefunc('obsfateusers(markers)')
-def obsfateusers(context, mapping, args):
- """Compute obsfate related information based on markers (EXPERIMENTAL)"""
- if len(args) != 1:
- # i18n: "obsfateusers" is a keyword
- raise error.ParseError(_("obsfateusers expects one argument"))
-
- markers = evalfuncarg(context, mapping, args[0])
-
- try:
- data = obsutil.markersusers(markers)
- return templateutil.hybridlist(data, name='user')
- except (TypeError, KeyError, ValueError):
- # i18n: "obsfateusers" is a keyword
- msg = _("obsfateusers first argument should be an iterable of "
- "obsmakers")
- raise error.ParseError(msg)
-
-@templatefunc('obsfateverb(successors, markers)')
-def obsfateverb(context, mapping, args):
- """Compute obsfate related information based on successors (EXPERIMENTAL)"""
- if len(args) != 2:
- # i18n: "obsfateverb" is a keyword
- raise error.ParseError(_("obsfateverb expects two arguments"))
-
- successors = evalfuncarg(context, mapping, args[0])
- markers = evalfuncarg(context, mapping, args[1])
-
- try:
- return obsutil.obsfateverb(successors, markers)
- except TypeError:
- # i18n: "obsfateverb" is a keyword
- errmsg = _("obsfateverb first argument should be countable")
- raise error.ParseError(errmsg)
-
-@templatefunc('relpath(path)')
-def relpath(context, mapping, args):
- """Convert a repository-absolute path into a filesystem path relative to
- the current working directory."""
- if len(args) != 1:
- # i18n: "relpath" is a keyword
- raise error.ParseError(_("relpath expects one argument"))
-
- repo = context.resource(mapping, 'ctx').repo()
- path = evalstring(context, mapping, args[0])
- return repo.pathto(path)
-
-@templatefunc('revset(query[, formatargs...])')
-def revset(context, mapping, args):
- """Execute a revision set query. See
- :hg:`help revset`."""
- if not len(args) > 0:
- # i18n: "revset" is a keyword
- raise error.ParseError(_("revset expects one or more arguments"))
-
- raw = evalstring(context, mapping, args[0])
- ctx = context.resource(mapping, 'ctx')
- repo = ctx.repo()
-
- def query(expr):
- m = revsetmod.match(repo.ui, expr, repo=repo)
- return m(repo)
-
- if len(args) > 1:
- formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
- revs = query(revsetlang.formatspec(raw, *formatargs))
- revs = list(revs)
- else:
- cache = context.resource(mapping, 'cache')
- revsetcache = cache.setdefault("revsetcache", {})
- if raw in revsetcache:
- revs = revsetcache[raw]
- else:
- revs = query(raw)
- revs = list(revs)
- revsetcache[raw] = revs
- return templatekw.showrevslist(context, mapping, "revision", revs)
-
-@templatefunc('rstdoc(text, style)')
-def rstdoc(context, mapping, args):
- """Format reStructuredText."""
- if len(args) != 2:
- # i18n: "rstdoc" is a keyword
- raise error.ParseError(_("rstdoc expects two arguments"))
-
- text = evalstring(context, mapping, args[0])
- style = evalstring(context, mapping, args[1])
-
- return minirst.format(text, style=style, keep=['verbose'])
-
-@templatefunc('separate(sep, args)', argspec='sep *args')
-def separate(context, mapping, args):
- """Add a separator between non-empty arguments."""
- if 'sep' not in args:
- # i18n: "separate" is a keyword
- raise error.ParseError(_("separate expects at least one argument"))
-
- sep = evalstring(context, mapping, args['sep'])
- first = True
- for arg in args['args']:
- argstr = evalstring(context, mapping, arg)
- if not argstr:
- continue
- if first:
- first = False
- else:
- yield sep
- yield argstr
-
-@templatefunc('shortest(node, minlength=4)')
-def shortest(context, mapping, args):
- """Obtain the shortest representation of
- a node."""
- if not (1 <= len(args) <= 2):
- # i18n: "shortest" is a keyword
- raise error.ParseError(_("shortest() expects one or two arguments"))
-
- node = evalstring(context, mapping, args[0])
-
- minlength = 4
- if len(args) > 1:
- minlength = evalinteger(context, mapping, args[1],
- # i18n: "shortest" is a keyword
- _("shortest() expects an integer minlength"))
-
- # _partialmatch() of filtered changelog could take O(len(repo)) time,
- # which would be unacceptably slow. so we look for hash collision in
- # unfiltered space, which means some hashes may be slightly longer.
- cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog
- return cl.shortest(node, minlength)
-
-@templatefunc('strip(text[, chars])')
-def strip(context, mapping, args):
- """Strip characters from a string. By default,
- strips all leading and trailing whitespace."""
- if not (1 <= len(args) <= 2):
- # i18n: "strip" is a keyword
- raise error.ParseError(_("strip expects one or two arguments"))
-
- text = evalstring(context, mapping, args[0])
- if len(args) == 2:
- chars = evalstring(context, mapping, args[1])
- return text.strip(chars)
- return text.strip()
-
-@templatefunc('sub(pattern, replacement, expression)')
-def sub(context, mapping, args):
- """Perform text substitution
- using regular expressions."""
- if len(args) != 3:
- # i18n: "sub" is a keyword
- raise error.ParseError(_("sub expects three arguments"))
-
- pat = evalstring(context, mapping, args[0])
- rpl = evalstring(context, mapping, args[1])
- src = evalstring(context, mapping, args[2])
- 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)
-
-@templatefunc('startswith(pattern, text)')
-def startswith(context, mapping, args):
- """Returns the value from the "text" argument
- if it begins with the content from the "pattern" argument."""
- if len(args) != 2:
- # i18n: "startswith" is a keyword
- raise error.ParseError(_("startswith expects two arguments"))
-
- patn = evalstring(context, mapping, args[0])
- text = evalstring(context, mapping, args[1])
- if text.startswith(patn):
- return text
- return ''
-
-@templatefunc('word(number, text[, separator])')
-def word(context, mapping, args):
- """Return the nth word from a string."""
- if not (2 <= len(args) <= 3):
- # i18n: "word" is a keyword
- raise error.ParseError(_("word expects two or three arguments, got %d")
- % len(args))
-
- num = evalinteger(context, mapping, args[0],
- # i18n: "word" is a keyword
- _("word expects an integer index"))
- text = evalstring(context, mapping, args[1])
- if len(args) == 3:
- splitter = evalstring(context, mapping, args[2])
- else:
- splitter = None
-
- tokens = text.split(splitter)
- if num >= len(tokens) or num < -len(tokens):
- return ''
- else:
- return tokens[num]
-
# methods to interpret function arguments or inner expressions (e.g. {_(x)})
exprmethods = {
"integer": lambda e, c: (templateutil.runinteger, e[1]),
@@ -1177,7 +541,7 @@
if filters is None:
filters = {}
self._filters = filters
- self._funcs = funcs # make this a parameter if needed
+ self._funcs = templatefuncs.funcs # make this a parameter if needed
if defaults is None:
defaults = {}
if resources is None:
@@ -1433,12 +797,3 @@
return style, mapfile
raise RuntimeError("No hgweb templates found in %r" % paths)
-
-def loadfunction(ui, extname, registrarobj):
- """Load template function from specified registrarobj
- """
- for name, func in registrarobj._table.iteritems():
- funcs[name] = func
-
-# tell hggettext to extract docstrings from these functions:
-i18nfunctions = funcs.values()