diff --git a/mercurial/formatter.py b/mercurial/formatter.py --- a/mercurial/formatter.py +++ b/mercurial/formatter.py @@ -197,7 +197,8 @@ def gettemplater(ui, topic, spec): def maketemplater(ui, topic, tmpl, filters=None, cache=None): """Create a templater from a string template 'tmpl'""" - t = templater.templater(filters=filters, cache=cache) + aliases = ui.configitems('templatealias') + t = templater.templater(filters=filters, cache=cache, aliases=aliases) if tmpl: t.cache[topic] = tmpl return t diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt --- a/mercurial/help/config.txt +++ b/mercurial/help/config.txt @@ -1488,6 +1488,11 @@ Relative subrepository paths are first m rewrite rules are then applied on the full (absolute) path. The rules are applied in definition order. +``templatealias`` +----------------- + +Alias definitions for templates. See :hg:`help templates` for details. + ``trusted`` ----------- diff --git a/mercurial/help/templates.txt b/mercurial/help/templates.txt --- a/mercurial/help/templates.txt +++ b/mercurial/help/templates.txt @@ -51,6 +51,26 @@ As seen in the above example, ``{templat To prevent it from being interpreted, you can use an escape character ``\{`` or a raw string prefix, ``r'...'``. +New keywords and functions can be defined in the ``templatealias`` section of +a Mercurial configuration file:: + + = + +Arguments of the form `a1`, `a2`, etc. are substituted from the alias into +the definition. + +For example, + +:: + + [templatealias] + r = rev + rn = "{r}:{node|short}" + leftpad(s, w) = pad(s, w, ' ', True) + +defines two symbol aliases, ``r`` and ``rn``, and a function alias +``leftpad()``. + Some sample command line templates: - Format lists, e.g. files:: diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -936,7 +936,7 @@ class engine(object): filter uses function to transform value. syntax is {key|filter1|filter2|...}.''' - def __init__(self, loader, filters=None, defaults=None): + def __init__(self, loader, filters=None, defaults=None, aliases=()): self._loader = loader if filters is None: filters = {} @@ -944,6 +944,7 @@ class engine(object): if defaults is None: defaults = {} self._defaults = defaults + self._aliasmap = _aliasrules.buildmap(aliases) self._cache = {} # key: (func, data) def _load(self, t): @@ -953,6 +954,8 @@ class engine(object): self._cache[t] = (_runrecursivesymbol, t) try: x = parse(self._loader(t)) + if self._aliasmap: + x = _aliasrules.expand(self._aliasmap, x) self._cache[t] = compileexp(x, self, methods) except: # re-raises del self._cache[t] @@ -1014,11 +1017,13 @@ class TemplateNotFound(error.Abort): class templater(object): - def __init__(self, filters=None, defaults=None, cache=None, + def __init__(self, filters=None, defaults=None, cache=None, aliases=(), minchunk=1024, maxchunk=65536): '''set up template engine. filters is dict of functions. each transforms a value into another. - defaults is dict of default map definitions.''' + defaults is dict of default map definitions. + aliases is list of alias (name, replacement) pairs. + ''' if filters is None: filters = {} if defaults is None: @@ -1030,6 +1035,7 @@ class templater(object): self.filters = templatefilters.filters.copy() self.filters.update(filters) self.defaults = defaults + self._aliases = aliases self.minchunk, self.maxchunk = minchunk, maxchunk self.ecache = {} @@ -1037,7 +1043,7 @@ class templater(object): def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None, minchunk=1024, maxchunk=65536): """Create templater from the specified map file""" - t = cls(filters, defaults, cache, minchunk, maxchunk) + t = cls(filters, defaults, cache, [], minchunk, maxchunk) cache, tmap = _readmapfile(mapfile) t.cache.update(cache) t.map = tmap @@ -1066,7 +1072,8 @@ class templater(object): ecls = engines[ttype] except KeyError: raise error.Abort(_('invalid template engine: %s') % ttype) - self.ecache[ttype] = ecls(self.load, self.filters, self.defaults) + self.ecache[ttype] = ecls(self.load, self.filters, self.defaults, + self._aliases) proc = self.ecache[ttype] stream = proc.process(t, mapping) diff --git a/tests/test-command-template.t b/tests/test-command-template.t --- a/tests/test-command-template.t +++ b/tests/test-command-template.t @@ -3693,8 +3693,7 @@ Templater supports aliases of symbol and ('string', 'UTC'))) ('symbol', 'isodate')) ('string', '\n')) - hg: parse error: unknown function 'utcdate' - [255] + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 $ hg debugtemplate -vr0 '{status("A", file_adds)}' (template @@ -3712,8 +3711,7 @@ Templater supports aliases of symbol and ('string', ' ') ('symbol', 'file') ('string', '\n')))) - hg: parse error: unknown function 'status' - [255] + A a A unary function alias can be called as a filter: @@ -3735,8 +3733,28 @@ A unary function alias can be called as ('string', 'UTC'))) ('symbol', 'isodate')) ('string', '\n')) - hg: parse error: unknown function 'utcdate' - [255] + 1970-01-12 13:46 +0000 + +Aliases should be applied only to command arguments and templates in hgrc. +Otherwise, our stock styles and web templates could be corrupted: + + $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n' + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 + + $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"' + 0:1e4e1b8f71e0 1970-01-12 13:46 +0000 + + $ cat < tmpl + > changeset = 'nothing expanded:{rn}\n' + > EOF + $ hg log -r0 --style ./tmpl + nothing expanded: + +Aliases in formatter: + + $ hg branches -T '{pad(branch, 7)} {rn}\n' + default 6:d41e714fe50d + foo 4:bbe44766e73d Unparsable alias: @@ -3745,6 +3763,9 @@ Unparsable alias: ('symbol', 'bad')) abort: failed to parse the definition of template alias "bad": at 2: not a prefix: end [255] + $ hg log --config templatealias.bad='x(' -T '{bad}' + abort: failed to parse the definition of template alias "bad": at 2: not a prefix: end + [255] $ cd .. diff --git a/tests/test-template-engine.t b/tests/test-template-engine.t --- a/tests/test-template-engine.t +++ b/tests/test-template-engine.t @@ -4,7 +4,7 @@ > from mercurial import templater > > class mytemplater(object): - > def __init__(self, loader, filters, defaults): + > def __init__(self, loader, filters, defaults, aliases): > self.loader = loader > > def process(self, t, map):