# HG changeset patch # User Yuya Nishihara # Date 1525316036 -32400 # Node ID d4fae9a0ab1f5c28e7146fcbbb030b7027868744 # Parent e637dc0b3b1f9fb53562772969cfe7e3981441a8 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 -r e637dc0b3b1f -r d4fae9a0ab1f mercurial/debugcommands.py --- a/mercurial/debugcommands.py Thu May 03 11:17:52 2018 +0900 +++ b/mercurial/debugcommands.py Thu May 03 11:53:56 2018 +0900 @@ -2488,9 +2488,17 @@ 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 -r e637dc0b3b1f -r d4fae9a0ab1f mercurial/templater.py --- a/mercurial/templater.py Thu May 03 11:17:52 2018 +0900 +++ b/mercurial/templater.py Thu May 03 11:53:56 2018 +0900 @@ -842,6 +842,51 @@ 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 -r e637dc0b3b1f -r d4fae9a0ab1f tests/test-command-template.t --- a/tests/test-command-template.t Thu May 03 11:17:52 2018 +0900 +++ b/tests/test-command-template.t Thu May 03 11:53:56 2018 +0900 @@ -50,6 +50,8 @@ (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 @@ (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 @@ (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 @@ (negate (integer '2')))) (string '\n')) + * keywords: + * functions: mod 2 -1 Filters bind closer than arithmetic: @@ -111,6 +119,8 @@ (symbol 'count')) (integer '1')) (string '\n')) + * keywords: + * functions: count, revset 0 But negate binds closer still: @@ -123,6 +133,8 @@ (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 @@ (integer '3')) (symbol 'stringify')) (string '\n')) + * keywords: + * functions: stringify -3 Filters bind as close as map operator: @@ -145,6 +159,8 @@ (template (symbol 'line') (string '\n')))) + * keywords: desc, line + * functions: splitlines line 1 line 2 @@ -157,6 +173,8 @@ (| (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 @@ (template (group None)) + * keywords: + * functions: hg: parse error: missing argument [255] @@ -3369,6 +3389,8 @@ (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 @@ (symbol 'bar') None)) (string '\n')) + * keywords: foo + * functions: bar [255] Test evaluation of dot operator: @@ -3480,12 +3504,16 @@ (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 @@ (negate (integer '4'))) (string '\n')) + * keywords: + * functions: -4 $ hg debugtemplate '{(-)}\n' hg: parse error at 3: not a prefix: ) @@ -3509,6 +3539,8 @@ (template (integer '1') (string '\n')) + * keywords: + * functions: one $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n' (template @@ -3519,6 +3551,8 @@ (template (integer '1')))) (string '\n')) + * keywords: + * functions: if one $ hg debugtemplate -D 1=one -v '{1|stringify}\n' (template @@ -3526,6 +3560,8 @@ (integer '1') (symbol 'stringify')) (string '\n')) + * keywords: + * functions: stringify one unless explicit symbol is expected: @@ -3543,6 +3579,8 @@ (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 @@ (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 @@ (symbol 'files') (string 'rawstring: {file}')) (string '\n')) + * keywords: files + * functions: rawstring: {file} Test string escaping: @@ -4681,6 +4725,8 @@ (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 @@ (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 @@ (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.