diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -68,6 +68,7 @@ from . import ( sshserver, streamclone, templatekw, + templater, treediscovery, ui as uimod, util, @@ -2757,7 +2758,6 @@ def debuginstall(ui, **opts): fm.condwrite(err, 'extensionserror', " %s\n", err) # templates - from . import templater p = templater.templatepaths() fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p)) fm.condwrite(not p, '', _(" no template directories found\n")) @@ -3592,6 +3592,54 @@ def debugsuccessorssets(ui, repo, *revs) ui.write(node2str(node)) ui.write('\n') +@command('debugtemplate', + [('r', 'rev', [], _('apply template on changesets'), _('REV')), + ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))], + _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'), + optionalrepo=True) +def debugtemplate(ui, repo, tmpl, **opts): + """parse and apply a template + + If -r/--rev is given, the template is processed as a log template and + applied to the given changesets. Otherwise, it is processed as a generic + template. + + Use --verbose to print the parsed tree. + """ + revs = None + if opts['rev']: + if repo is None: + raise error.RepoError(_('there is no Mercurial repository here ' + '(.hg not found)')) + revs = scmutil.revrange(repo, opts['rev']) + + props = {} + for d in opts['define']: + try: + k, v = (e.strip() for e in d.split('=', 1)) + if not k: + raise ValueError + props[k] = v + except ValueError: + raise error.Abort(_('malformed keyword definition: %s') % d) + + if ui.verbose: + tree = templater.parse(tmpl) + ui.note(templater.prettyformat(tree), '\n') + + mapfile = None + if revs is None: + k = 'debugtemplate' + t = templater.templater(mapfile) + t.cache[k] = tmpl + ui.write(templater.stringify(t(k, **props))) + else: + displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl, + mapfile, buffered=False) + for r in revs: + displayer.show(repo[r], **props) + displayer.close() + @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True) def debugwalk(ui, repo, *pats, **opts): """show how files match on given patterns""" 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 @@ -2861,27 +2861,55 @@ Test invalid date: Test integer literal: - $ hg log -Ra -r0 -T '{(0)}\n' + $ hg debugtemplate -v '{(0)}\n' + (template + (group + ('integer', '0')) + ('string', '\n')) 0 - $ hg log -Ra -r0 -T '{(123)}\n' + $ hg debugtemplate -v '{(123)}\n' + (template + (group + ('integer', '123')) + ('string', '\n')) 123 - $ hg log -Ra -r0 -T '{(-4)}\n' + $ hg debugtemplate -v '{(-4)}\n' + (template + (group + ('integer', '-4')) + ('string', '\n')) -4 - $ hg log -Ra -r0 -T '{(-)}\n' + $ hg debugtemplate '{(-)}\n' hg: parse error at 2: integer literal without digits [255] - $ hg log -Ra -r0 -T '{(-a)}\n' + $ hg debugtemplate '{(-a)}\n' hg: parse error at 2: integer literal without digits [255] top-level integer literal is interpreted as symbol (i.e. variable name): - $ hg log -Ra -r0 -T '{1}\n' - - $ hg log -Ra -r0 -T '{if("t", "{1}")}\n' - - $ hg log -Ra -r0 -T '{1|stringify}\n' - + $ hg debugtemplate -D 1=one -v '{1}\n' + (template + ('integer', '1') + ('string', '\n')) + one + $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n' + (template + (func + ('symbol', 'if') + (list + ('string', 't') + (template + ('integer', '1')))) + ('string', '\n')) + one + $ hg debugtemplate -D 1=one -v '{1|stringify}\n' + (template + (| + ('integer', '1') + ('symbol', 'stringify')) + ('string', '\n')) + one unless explicit symbol is expected: @@ -2894,13 +2922,29 @@ unless explicit symbol is expected: Test string literal: - $ hg log -Ra -r0 -T '{"string with no template fragment"}\n' + $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n' + (template + ('string', 'string with no template fragment') + ('string', '\n')) string with no template fragment - $ hg log -Ra -r0 -T '{"template: {rev}"}\n' + $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n' + (template + (template + ('string', 'template: ') + ('symbol', 'rev')) + ('string', '\n')) template: 0 - $ hg log -Ra -r0 -T '{r"rawstring: {rev}"}\n' + $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n' + (template + ('string', 'rawstring: {rev}') + ('string', '\n')) rawstring: {rev} - $ hg log -Ra -r0 -T '{files % r"rawstring: {file}"}\n' + $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n' + (template + (% + ('symbol', 'files') + ('string', 'rawstring: {file}')) + ('string', '\n')) rawstring: {file} Test string escaping: diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -108,6 +108,7 @@ Show debug commands if there are no othe debugsetparents debugsub debugsuccessorssets + debugtemplate debugwalk debugwireargs @@ -272,6 +273,7 @@ Show all commands + options debugsetparents: debugsub: rev debugsuccessorssets: + debugtemplate: rev, define debugwalk: include, exclude debugwireargs: three, four, five, ssh, remotecmd, insecure files: rev, print0, include, exclude, template, subrepos diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -861,6 +861,8 @@ Test list of internal help commands debugsub (no help text available) debugsuccessorssets show set of successors for revision + debugtemplate + parse and apply a template debugwalk show how files match on given patterns debugwireargs (no help text available)