changeset 22480:dff638170c48

annotate: port to generic templater enabled by hidden -T option If the selected formatter is other than plainformatter, raw data are passed to the formatter. In this case, it isn't necessary (and not possible) to calculate column widths. Field names are substituted to be the same as "log" command. There are a few limitations: - "binary file" message is not included in formatted output. - no data structure for multiple files. all lines are packed to single list.
author Yuya Nishihara <yuya@tcha.org>
date Wed, 17 Sep 2014 23:21:20 +0900
parents 5d9e46d93c1d
children 8488955127b0
files mercurial/commands.py tests/test-annotate.t tests/test-completion.t
diffstat 3 files changed, 52 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/commands.py	Tue Sep 16 23:40:24 2014 +0900
+++ b/mercurial/commands.py	Wed Sep 17 23:21:20 2014 +0900
@@ -247,7 +247,7 @@
     ('n', 'number', None, _('list the revision number (default)')),
     ('c', 'changeset', None, _('list the changeset')),
     ('l', 'line-number', None, _('show line number at the first appearance'))
-    ] + diffwsopts + walkopts,
+    ] + diffwsopts + walkopts + formatteropts,
     _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
     inferrepo=True)
 def annotate(ui, repo, *pats, **opts):
@@ -274,8 +274,12 @@
         # to mimic the behavior of Mercurial before version 1.5
         opts['file'] = True
 
+    fm = ui.formatter('annotate', opts)
     datefunc = ui.quiet and util.shortdate or util.datestr
-    hexfn = ui.debugflag and hex or short
+    if fm or ui.debugflag:
+        hexfn = hex
+    else:
+        hexfn = short
 
     opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
              ('number', ' ', lambda x: x[0].rev(), str),
@@ -284,6 +288,7 @@
              ('file', ' ', lambda x: x[0].path(), str),
              ('line_number', ':', lambda x: x[1], str),
             ]
+    fieldnamemap = {'number': 'rev', 'changeset': 'node'}
 
     if (not opts.get('user') and not opts.get('changeset')
         and not opts.get('date') and not opts.get('file')):
@@ -293,11 +298,17 @@
     if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
         raise util.Abort(_('at least one of -n/-c is required for -l'))
 
-    def makefunc(get, fmt):
-        return lambda x: fmt(get(x))
+    if fm:
+        def makefunc(get, fmt):
+            return get
+    else:
+        def makefunc(get, fmt):
+            return lambda x: fmt(get(x))
     funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
                if opts.get(op)]
     funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
+    fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
+                      if opts.get(op))
 
     def bad(x, y):
         raise util.Abort("%s: %s" % (x, y))
@@ -310,7 +321,7 @@
     for abs in ctx.walk(m):
         fctx = ctx[abs]
         if not opts.get('text') and util.binary(fctx.data()):
-            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
+            fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
             continue
 
         lines = fctx.annotate(follow=follow, linenumber=linenumber,
@@ -321,17 +332,23 @@
         for f, sep in funcmap:
             l = [f(n) for n, dummy in lines]
             if l:
-                sizes = [encoding.colwidth(x) for x in l]
-                ml = max(sizes)
-                formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
+                if fm:
+                    formats.append(['%s' for x in l])
+                else:
+                    sizes = [encoding.colwidth(x) for x in l]
+                    ml = max(sizes)
+                    formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
                 pieces.append(l)
 
         for f, p, l in zip(zip(*formats), zip(*pieces), lines):
-            ui.write("".join(f) % p)
-            ui.write(": %s" % l[1])
+            fm.startitem()
+            fm.write(fields, "".join(f), *p)
+            fm.write('line', ": %s", l[1])
 
         if lines and not lines[-1][1].endswith('\n'):
-            ui.write('\n')
+            fm.plain('\n')
+
+    fm.end()
 
 @command('archive',
     [('', 'no-decode', None, _('do not pass files through decoders')),
--- a/tests/test-annotate.t	Tue Sep 16 23:40:24 2014 +0900
+++ b/tests/test-annotate.t	Wed Sep 17 23:21:20 2014 +0900
@@ -51,6 +51,29 @@
   $ hg annotate -cdnul a
   nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000:1: a
 
+annotate (JSON)
+
+  $ hg annotate -Tjson a
+  [
+   {
+    "line": "a\n",
+    "rev": 0
+   }
+  ]
+
+  $ hg annotate -Tjson -cdfnul a
+  [
+   {
+    "date": [1.0, 0],
+    "file": "a",
+    "line": "a\n",
+    "line_number": 1,
+    "node": "8435f90966e442695d2ded29fdade2bac5ad8065",
+    "rev": 0,
+    "user": "nobody"
+   }
+  ]
+
   $ cat <<EOF >>a
   > a
   > a
--- a/tests/test-completion.t	Tue Sep 16 23:40:24 2014 +0900
+++ b/tests/test-completion.t	Wed Sep 17 23:21:20 2014 +0900
@@ -198,7 +198,7 @@
 Show all commands + options
   $ hg debugcommands
   add: include, exclude, subrepos, dry-run
-  annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude
+  annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude, template
   clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
   commit: addremove, close-branch, amend, secret, edit, include, exclude, message, logfile, date, user, subrepos
   diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos