[PATCH 2 of 8] templater: add dot operator to easily access a sub item
Yuya Nishihara
yuya at tcha.org
Fri Oct 6 16:05:52 UTC 2017
# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1504953176 -32400
# Sat Sep 09 19:32:56 2017 +0900
# Node ID b191c62651e04bf4ed9aceda32fd08c000570d82
# Parent ebbbf772650c276998b87b307c9c9ddc775e9236
templater: add dot operator to easily access a sub item
This and the next patch will allow us to access a deeply-nested item
by foo.bar.baz syntax.
diff --git a/mercurial/help/templates.txt b/mercurial/help/templates.txt
--- a/mercurial/help/templates.txt
+++ b/mercurial/help/templates.txt
@@ -72,6 +72,11 @@ 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'...'``.
+The dot operator can be used as a shorthand for accessing a sub item:
+
+- ``expr.member`` is roughly equivalent to ``expr % "{member}"`` if ``expr``
+ returns a non-list/dict. The returned value is not stringified.
+
Aliases
=======
diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py
--- a/mercurial/templatekw.py
+++ b/mercurial/templatekw.py
@@ -73,6 +73,7 @@ class _mappable(object):
This class allows us to handle both:
- "{manifest}"
- "{manifest % '{rev}:{node}'}"
+ - "{manifest.rev}"
Unlike a _hybrid, this does not simulate the behavior of the underling
value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -35,6 +35,7 @@ from . import (
elements = {
# token-type: binding-strength, primary, prefix, infix, suffix
"(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
+ ".": (18, None, None, (".", 18), None),
"%": (15, None, None, ("%", 15), None),
"|": (15, None, None, ("|", 15), None),
"*": (5, None, None, ("*", 5), None),
@@ -60,7 +61,7 @@ def tokenize(program, start, end, term=N
c = program[pos]
if c.isspace(): # skip inter-token whitespace
pass
- elif c in "(=,)%|+-*/": # handle simple operators
+ elif c in "(=,).%|+-*/": # handle simple operators
yield (c, None, pos)
elif c in '"\'': # handle quoted templates
s = pos + 1
@@ -450,6 +451,26 @@ def runmap(context, mapping, data):
# If so, return the expanded value.
yield v
+def buildmember(exp, context):
+ darg = compileexp(exp[1], context, methods)
+ memb = getsymbol(exp[2])
+ return (runmember, (darg, memb))
+
+def runmember(context, mapping, data):
+ darg, memb = data
+ d = evalrawexp(context, mapping, darg)
+ if util.safehasattr(d, 'tomap'):
+ lm = mapping.copy()
+ lm.update(d.tomap())
+ return runsymbol(context, lm, memb)
+ # TODO: d.get(memb) if dict-like?
+
+ sym = findsymbolicname(darg)
+ if sym:
+ raise error.ParseError(_("keyword '%s' has no member") % sym)
+ else:
+ raise error.ParseError(_("%r has no member") % d)
+
def buildnegate(exp, context):
arg = compileexp(exp[1], context, exprmethods)
return (runnegate, arg)
@@ -1152,7 +1173,7 @@ exprmethods = {
"symbol": lambda e, c: (runsymbol, e[1]),
"template": buildtemplate,
"group": lambda e, c: compileexp(e[1], c, exprmethods),
-# ".": buildmember,
+ ".": buildmember,
"|": buildfilter,
"%": buildmap,
"func": buildfunc,
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
@@ -3147,6 +3147,51 @@ Test manifest/get() can be join()-ed as
$ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
default
+Test dot operator precedence:
+
+ $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
+ (template
+ (|
+ (.
+ (symbol 'manifest')
+ (symbol 'node'))
+ (symbol 'short'))
+ (string '\n'))
+ 89f4071fec70
+
+ (the following examples are invalid, but seem natural in parsing POV)
+
+ $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
+ (template
+ (|
+ (symbol 'foo')
+ (.
+ (symbol 'bar')
+ (symbol 'baz')))
+ (string '\n'))
+ [255]
+ $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
+ (template
+ (.
+ (symbol 'foo')
+ (func
+ (symbol 'bar')
+ None))
+ (string '\n'))
+ [255]
+
+Test evaluation of dot operator:
+
+ $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
+ ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
+
+ $ hg log -R latesttag -l1 -T '{author.invalid}\n'
+ hg: parse error: keyword 'author' has no member
+ [255]
+ $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
+ hg: parse error: 'a' has no member
+ [255]
+
Test the sub function of templating for expansion:
$ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
More information about the Mercurial-devel
mailing list