mirror of
https://github.com/facebook/sapling.git
synced 2025-01-06 04:43:19 +03:00
fastannotate: add a JSON formatter
Summary: The vanilla annotate command supports `-Tjson`. Let's implement it as well. Test Plan: Added a new test Reviewers: #mercurial, simonfar Reviewed By: simonfar Subscribers: mjpieters Differential Revision: https://phabricator.intern.facebook.com/D4059577 Signature: t1:4059577:1477074802:09b0b3ea0769d480eb3a2e42308636ff2b8d40d2
This commit is contained in:
parent
6318d6eb2a
commit
a91f5d53ac
@ -76,7 +76,7 @@ fastannotatecommandargs = {
|
||||
('', 'long-hash', None, _('show long changeset hash (EXPERIMENTAL)')),
|
||||
('', 'rebuild', None, _('rebuild cache even if it exists '
|
||||
'(EXPERIMENTAL)')),
|
||||
] + commands.diffwsopts + commands.walkopts,
|
||||
] + commands.diffwsopts + commands.walkopts + commands.formatteropts,
|
||||
'synopsis': _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
|
||||
'inferrepo': True,
|
||||
}
|
||||
@ -124,7 +124,11 @@ def fastannotate(ui, repo, *pats, **opts):
|
||||
for name in ui.configlist('fastannotate', 'defaultformat', ['number']):
|
||||
opts[name] = True
|
||||
|
||||
formatter = faformatter.defaultformatter(ui, repo, opts)
|
||||
template = opts.get('template')
|
||||
if template == 'json':
|
||||
formatter = faformatter.jsonformatter(ui, repo, opts)
|
||||
else:
|
||||
formatter = faformatter.defaultformatter(ui, repo, opts)
|
||||
showdeleted = opts.get('deleted', False)
|
||||
showlines = not bool(opts.get('no_content'))
|
||||
showpath = opts.get('file', False)
|
||||
@ -156,6 +160,7 @@ def fastannotate(ui, repo, *pats, **opts):
|
||||
result, lines = result
|
||||
|
||||
formatter.write(result, lines, existinglines=existinglines)
|
||||
formatter.end()
|
||||
|
||||
_newopts = set([])
|
||||
_knownopts = set([opt[1].replace('-', '_') for opt in
|
||||
@ -165,6 +170,9 @@ def _annotatewrapper(orig, ui, repo, *pats, **opts):
|
||||
"""use this in extensions.wrapcommand"""
|
||||
nonemptyopts = set(k for k, v in opts.iteritems() if v)
|
||||
unknownopts = nonemptyopts.difference(_knownopts)
|
||||
if opts.get('template', '') not in ['json', '']:
|
||||
# if -T is used, fastannotate only supports -Tjson
|
||||
unknownopts.add('template')
|
||||
if unknownopts:
|
||||
ui.debug('fastannotate: option %r is not supported, falling back '
|
||||
'to the original annotate\n' % list(unknownopts))
|
||||
|
@ -7,11 +7,13 @@
|
||||
|
||||
from mercurial import (
|
||||
encoding,
|
||||
formatter,
|
||||
node,
|
||||
util,
|
||||
)
|
||||
|
||||
# extracted from mercurial.commands.annotate
|
||||
# imitating mercurial.commands.annotate, not using the vanilla formatter since
|
||||
# the data structures are a bit different, and we have some fast paths.
|
||||
class defaultformatter(object):
|
||||
"""the default formatter that does leftpad and support some common flags"""
|
||||
|
||||
@ -20,25 +22,26 @@ class defaultformatter(object):
|
||||
datefunc = util.shortdate
|
||||
else:
|
||||
datefunc = util.datestr
|
||||
if ui.debugflag or opts.get('long_hash'):
|
||||
hexfunc = node.hex
|
||||
else:
|
||||
hexfunc = node.short
|
||||
datefunc = util.cachefunc(datefunc)
|
||||
getctx = util.cachefunc(lambda x: repo[x[0]])
|
||||
|
||||
opmap = [('user', ' ', lambda x: ui.shortuser(getctx(x).user())),
|
||||
('number', ' ', lambda x: str(getctx(x).rev())),
|
||||
('changeset', ' ', lambda x: hexfunc(x[0])),
|
||||
('date', ' ', lambda x: datefunc(getctx(x).date())),
|
||||
('file', ' ', lambda x: x[2]),
|
||||
('line_number', ':', lambda x: str(x[1] + 1))]
|
||||
|
||||
funcmap = [(get, sep) for op, sep, get in opmap
|
||||
# opt name, separator, raw value (for json/plain), encoder (for plain)
|
||||
opmap = [('user', ' ', lambda x: getctx(x).user(), ui.shortuser),
|
||||
('number', ' ', lambda x: getctx(x).rev(), str),
|
||||
('changeset', ' ', lambda x: self._hexfunc(x[0]), str),
|
||||
('date', ' ', lambda x: getctx(x).date(), datefunc),
|
||||
('file', ' ', lambda x: x[2], str),
|
||||
('line_number', ':', lambda x: x[1] + 1, str)]
|
||||
fieldnamemap = {'number': 'rev', 'changeset': 'node'}
|
||||
funcmap = [(get, sep, fieldnamemap.get(op, op), enc)
|
||||
for op, sep, get, enc in opmap
|
||||
if opts.get(op)]
|
||||
funcmap[0] = (funcmap[0][0], '') # no separator for first column
|
||||
# no separator for first column
|
||||
funcmap[0] = list(funcmap[0])
|
||||
funcmap[0][1] = ''
|
||||
|
||||
self.ui = ui
|
||||
self.opts = opts
|
||||
self.funcmap = funcmap
|
||||
|
||||
def write(self, annotatedresult, lines=None, existinglines=None):
|
||||
@ -48,32 +51,87 @@ class defaultformatter(object):
|
||||
pieces = [] # [[str]]
|
||||
maxwidths = [] # [int]
|
||||
|
||||
# calculate
|
||||
for f, sep in self.funcmap:
|
||||
l = map(f, annotatedresult)
|
||||
# calculate padding
|
||||
for f, sep, name, enc in self.funcmap:
|
||||
l = [enc(f(x)) for x in annotatedresult]
|
||||
pieces.append(l)
|
||||
widths = map(encoding.colwidth, l)
|
||||
widths = map(encoding.colwidth, set(l))
|
||||
maxwidth = (max(widths) if widths else 0)
|
||||
maxwidths.append(maxwidth)
|
||||
|
||||
# output
|
||||
# buffered output
|
||||
result = ''
|
||||
for i in xrange(len(annotatedresult)):
|
||||
msg = ''
|
||||
for j, p in enumerate(pieces):
|
||||
sep = self.funcmap[j][1]
|
||||
padding = ' ' * (maxwidths[j] - len(p[i]))
|
||||
msg += sep + padding + p[i]
|
||||
result += sep + padding + p[i]
|
||||
if lines:
|
||||
if existinglines is None:
|
||||
msg += ': ' + lines[i]
|
||||
result += ': ' + lines[i]
|
||||
else: # extra formatting showing whether a line exists
|
||||
key = (annotatedresult[i][0], annotatedresult[i][1])
|
||||
if key in existinglines:
|
||||
msg += ': ' + lines[i]
|
||||
result += ': ' + lines[i]
|
||||
else:
|
||||
msg += ': ' + self.ui.label('-' + lines[i],
|
||||
'diff.deleted')
|
||||
result += ': ' + self.ui.label('-' + lines[i],
|
||||
'diff.deleted')
|
||||
|
||||
if msg[-1] != '\n':
|
||||
msg += '\n'
|
||||
self.ui.write(msg)
|
||||
if result[-1] != '\n':
|
||||
result += '\n'
|
||||
|
||||
self.ui.write(result)
|
||||
|
||||
@util.propertycache
|
||||
def _hexfunc(self):
|
||||
if self.ui.debugflag or self.opts.get('long_hash'):
|
||||
return node.hex
|
||||
else:
|
||||
return node.short
|
||||
|
||||
def end(self):
|
||||
pass
|
||||
|
||||
class jsonformatter(defaultformatter):
|
||||
def __init__(self, ui, repo, opts):
|
||||
super(jsonformatter, self).__init__(ui, repo, opts)
|
||||
self.ui.write('[')
|
||||
self.needcomma = False
|
||||
|
||||
def write(self, annotatedresult, lines=None, existinglines=None):
|
||||
if annotatedresult:
|
||||
self._writecomma()
|
||||
|
||||
pieces = [(name, map(f, annotatedresult))
|
||||
for f, sep, name, enc in self.funcmap]
|
||||
if lines is not None:
|
||||
pieces.append(('line', lines))
|
||||
pieces.sort()
|
||||
|
||||
seps = [','] * len(pieces[:-1]) + ['']
|
||||
|
||||
result = ''
|
||||
lasti = len(annotatedresult) - 1
|
||||
for i in xrange(len(annotatedresult)):
|
||||
result += '\n {\n'
|
||||
for j, p in enumerate(pieces):
|
||||
k, vs = p
|
||||
result += (' "%s": %s%s\n'
|
||||
% (k, formatter._jsonifyobj(vs[i]), seps[j]))
|
||||
result += ' }%s' % ('' if i == lasti else ',')
|
||||
if lasti >= 0:
|
||||
self.needcomma = True
|
||||
|
||||
self.ui.write(result)
|
||||
|
||||
def _writecomma(self):
|
||||
if self.needcomma:
|
||||
self.ui.write(',')
|
||||
self.needcomma = False
|
||||
|
||||
@util.propertycache
|
||||
def _hexfunc(self):
|
||||
return node.hex
|
||||
|
||||
def end(self):
|
||||
self.ui.write('\n]\n')
|
||||
|
@ -196,3 +196,35 @@ empty file
|
||||
$ touch empty
|
||||
$ hg commit -A empty -m empty
|
||||
$ hg fastannotate empty
|
||||
|
||||
json format
|
||||
|
||||
$ hg fastannotate -Tjson -cludn b a empty
|
||||
[
|
||||
{
|
||||
"date": [0.0, 0],
|
||||
"line": "a\n",
|
||||
"line_number": 1,
|
||||
"node": "1fd620b16252aecb54c6aa530dff5ed6e6ec3d21",
|
||||
"rev": 0,
|
||||
"user": "test"
|
||||
},
|
||||
{
|
||||
"date": [0.0, 0],
|
||||
"line": "b\n",
|
||||
"line_number": 1,
|
||||
"node": "1fd620b16252aecb54c6aa530dff5ed6e6ec3d21",
|
||||
"rev": 0,
|
||||
"user": "test"
|
||||
}
|
||||
]
|
||||
|
||||
$ hg fastannotate -Tjson -cludn empty
|
||||
[
|
||||
]
|
||||
$ hg fastannotate -Tjson --no-content -n a
|
||||
[
|
||||
{
|
||||
"rev": 0
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user