[PATCH 5 of 8] templater: add function to look up symbols used in template
Yuya Nishihara
yuya at tcha.org
Thu Jun 14 11:40:30 EDT 2018
# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1525316036 -32400
# Thu May 03 11:53:56 2018 +0900
# Node ID 92efa37a0696007f5445c6cd69a395fb2e1cdd5f
# Parent 04ae3f0792bc165d30347aa6a456ab3421d4c394
templater: add function to look up symbols used in template
Formatter can use this information to enable slow paths such as loading
ctx object only when necessary.
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -2482,9 +2482,17 @@ def debugtemplate(ui, repo, tmpl, **opts
if revs is None:
tres = formatter.templateresources(ui, repo)
t = formatter.maketemplater(ui, tmpl, resources=tres)
+ if ui.verbose:
+ kwds, funcs = t.symbolsuseddefault()
+ ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
+ ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
ui.write(t.renderdefault(props))
else:
displayer = logcmdutil.maketemplater(ui, repo, tmpl)
+ if ui.verbose:
+ kwds, funcs = displayer.t.symbolsuseddefault()
+ ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
+ ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
for r in revs:
displayer.show(repo[r], **pycompat.strkwargs(props))
displayer.close()
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -842,6 +842,51 @@ class templater(object):
x = _aliasrules.expand(self._aliasmap, x)
return x
+ def _findsymbolsused(self, tree, syms):
+ if not tree:
+ return
+ op = tree[0]
+ if op == 'symbol':
+ s = tree[1]
+ if s in syms[0]:
+ return # avoid recursion: s -> cache[s] -> s
+ syms[0].add(s)
+ if s in self.cache or s in self._map:
+ # s may be a reference for named template
+ self._findsymbolsused(self.load(s), syms)
+ return
+ if op in {'integer', 'string'}:
+ return
+ # '{arg|func}' == '{func(arg)}'
+ if op == '|':
+ syms[1].add(getsymbol(tree[2]))
+ self._findsymbolsused(tree[1], syms)
+ return
+ if op == 'func':
+ syms[1].add(getsymbol(tree[1]))
+ self._findsymbolsused(tree[2], syms)
+ return
+ for x in tree[1:]:
+ self._findsymbolsused(x, syms)
+
+ def symbolsuseddefault(self):
+ """Look up (keywords, filters/functions) referenced from the default
+ unnamed template
+
+ This may load additional templates from the map file.
+ """
+ return self.symbolsused('')
+
+ def symbolsused(self, t):
+ """Look up (keywords, filters/functions) referenced from the name
+ template 't'
+
+ This may load additional templates from the map file.
+ """
+ syms = (set(), set())
+ self._findsymbolsused(self.load(t), syms)
+ return syms
+
def renderdefault(self, mapping):
"""Render the default unnamed template and return result as string"""
return self.render('', 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
@@ -50,6 +50,8 @@ Test division:
(integer '5')
(integer '2')))
(string '\n'))
+ * keywords:
+ * functions: mod
2 1
$ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
(template
@@ -65,6 +67,8 @@ Test division:
(negate
(integer '2'))))
(string '\n'))
+ * keywords:
+ * functions: mod
-3 -1
$ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
(template
@@ -80,6 +84,8 @@ Test division:
(integer '5'))
(integer '2')))
(string '\n'))
+ * keywords:
+ * functions: mod
-3 1
$ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
(template
@@ -97,6 +103,8 @@ Test division:
(negate
(integer '2'))))
(string '\n'))
+ * keywords:
+ * functions: mod
2 -1
Filters bind closer than arithmetic:
@@ -111,6 +119,8 @@ Filters bind closer than arithmetic:
(symbol 'count'))
(integer '1'))
(string '\n'))
+ * keywords:
+ * functions: count, revset
0
But negate binds closer still:
@@ -123,6 +133,8 @@ But negate binds closer still:
(integer '3')
(symbol 'stringify')))
(string '\n'))
+ * keywords:
+ * functions: stringify
hg: parse error: arithmetic only defined on integers
[255]
$ hg debugtemplate -r0 -v '{-3|stringify}\n'
@@ -132,6 +144,8 @@ But negate binds closer still:
(integer '3'))
(symbol 'stringify'))
(string '\n'))
+ * keywords:
+ * functions: stringify
-3
Filters bind as close as map operator:
@@ -145,6 +159,8 @@ Filters bind as close as map operator:
(template
(symbol 'line')
(string '\n'))))
+ * keywords: desc, line
+ * functions: splitlines
line 1
line 2
@@ -157,6 +173,8 @@ Keyword arguments:
(|
(symbol 'bar')
(symbol 'baz'))))
+ * keywords: bar, foo
+ * functions: baz
hg: parse error: can't use a key-value pair in this context
[255]
@@ -2800,6 +2818,8 @@ Error on syntax:
(template
(group
None))
+ * keywords:
+ * functions:
hg: parse error: missing argument
[255]
@@ -3369,6 +3389,8 @@ Test dot operator precedence:
(symbol 'node'))
(symbol 'short'))
(string '\n'))
+ * keywords: manifest, node, rev
+ * functions: formatnode, short
89f4071fec70
(the following examples are invalid, but seem natural in parsing POV)
@@ -3390,6 +3412,8 @@ Test dot operator precedence:
(symbol 'bar')
None))
(string '\n'))
+ * keywords: foo
+ * functions: bar
[255]
Test evaluation of dot operator:
@@ -3480,12 +3504,16 @@ Test integer literal:
(group
(integer '0'))
(string '\n'))
+ * keywords:
+ * functions:
0
$ hg debugtemplate -v '{(123)}\n'
(template
(group
(integer '123'))
(string '\n'))
+ * keywords:
+ * functions:
123
$ hg debugtemplate -v '{(-4)}\n'
(template
@@ -3493,6 +3521,8 @@ Test integer literal:
(negate
(integer '4')))
(string '\n'))
+ * keywords:
+ * functions:
-4
$ hg debugtemplate '{(-)}\n'
hg: parse error at 3: not a prefix: )
@@ -3509,6 +3539,8 @@ top-level integer literal is interpreted
(template
(integer '1')
(string '\n'))
+ * keywords:
+ * functions:
one
$ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
(template
@@ -3519,6 +3551,8 @@ top-level integer literal is interpreted
(template
(integer '1'))))
(string '\n'))
+ * keywords:
+ * functions: if
one
$ hg debugtemplate -D 1=one -v '{1|stringify}\n'
(template
@@ -3526,6 +3560,8 @@ top-level integer literal is interpreted
(integer '1')
(symbol 'stringify'))
(string '\n'))
+ * keywords:
+ * functions: stringify
one
unless explicit symbol is expected:
@@ -3543,6 +3579,8 @@ Test string literal:
(template
(string 'string with no template fragment')
(string '\n'))
+ * keywords:
+ * functions:
string with no template fragment
$ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
(template
@@ -3550,11 +3588,15 @@ Test string literal:
(string 'template: ')
(symbol 'rev'))
(string '\n'))
+ * keywords: rev
+ * functions:
template: 0
$ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
(template
(string 'rawstring: {rev}')
(string '\n'))
+ * keywords:
+ * functions:
rawstring: {rev}
$ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
(template
@@ -3562,6 +3604,8 @@ Test string literal:
(symbol 'files')
(string 'rawstring: {file}'))
(string '\n'))
+ * keywords: files
+ * functions:
rawstring: {file}
Test string escaping:
@@ -4681,6 +4725,8 @@ Templater supports aliases of symbol and
(string 'UTC')))
(symbol 'isodate'))
(string '\n'))
+ * keywords: date, node, rev
+ * functions: isodate, localdate, short
0:1e4e1b8f71e0 1970-01-12 13:46 +0000
$ hg debugtemplate -vr0 '{status("A", file_adds)}'
@@ -4699,6 +4745,8 @@ Templater supports aliases of symbol and
(string ' ')
(symbol 'file')
(string '\n'))))
+ * keywords: file, file_adds
+ * functions:
A a
A unary function alias can be called as a filter:
@@ -4721,6 +4769,8 @@ A unary function alias can be called as
(string 'UTC')))
(symbol 'isodate'))
(string '\n'))
+ * keywords: date
+ * functions: isodate, localdate
1970-01-12 13:46 +0000
Aliases should be applied only to command arguments and templates in hgrc.
More information about the Mercurial-devel
mailing list