grep: add formatter support
Several fields are renamed to be consistent with the annotate command, which
doesn't mean the last call for the name unification [1]. Actually, I'd rather
rename line_number to linenumber, linenum, lineno or line, but I want to
port the grep command to formatter first.
[1]: https://www.mercurial-scm.org/wiki/GenericTemplatingPlan#Dictionary
I don't have any better name for the list of matched/unmatched texts, so
they are just called as "texts".
--- a/mercurial/commands.py Thu Aug 18 14:52:06 2016 +0900
+++ b/mercurial/commands.py Thu Aug 18 15:13:56 2016 +0900
@@ -4283,7 +4283,7 @@
_('only search files changed within revision range'), _('REV')),
('u', 'user', None, _('list the author (long with -v)')),
('d', 'date', None, _('list the date (short with -q)')),
- ] + walkopts,
+ ] + formatteropts + walkopts,
_('[OPTION]... PATTERN [FILE]...'),
inferrepo=True)
def grep(ui, repo, pattern, *pats, **opts):
@@ -4380,59 +4380,75 @@
for i in xrange(blo, bhi):
yield ('+', b[i])
- def display(fn, ctx, pstates, states):
+ def display(fm, fn, ctx, pstates, states):
rev = ctx.rev()
+ if fm:
+ formatuser = str
+ else:
+ formatuser = ui.shortuser
if ui.quiet:
- datefunc = util.shortdate
+ datefmt = '%Y-%m-%d'
else:
- datefunc = util.datestr
+ datefmt = '%a %b %d %H:%M:%S %Y %1%2'
found = False
@util.cachefunc
def binary():
flog = getfile(fn)
return util.binary(flog.read(ctx.filenode(fn)))
+ fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
if opts.get('all'):
iter = difflinestates(pstates, states)
else:
iter = [('', l) for l in states]
for change, l in iter:
+ fm.startitem()
+ fm.data(node=fm.hexfunc(ctx.node()))
cols = [
('filename', fn, True),
- ('rev', str(rev), True),
- ('linenumber', str(l.linenum), opts.get('line_number')),
+ ('rev', rev, True),
+ ('linenumber', l.linenum, opts.get('line_number')),
]
if opts.get('all'):
cols.append(('change', change, True))
cols.extend([
- ('user', ui.shortuser(ctx.user()), opts.get('user')),
- ('date', datefunc(ctx.date()), opts.get('date')),
+ ('user', formatuser(ctx.user()), opts.get('user')),
+ ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
])
lastcol = next(name for name, data, cond in reversed(cols) if cond)
for name, data, cond in cols:
- if cond:
- ui.write(data, label='grep.%s' % name)
+ field = fieldnamemap.get(name, name)
+ fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
if cond and name != lastcol:
- ui.write(sep, label='grep.sep')
+ fm.plain(sep, label='grep.sep')
if not opts.get('files_with_matches'):
- ui.write(sep, label='grep.sep')
+ fm.plain(sep, label='grep.sep')
if not opts.get('text') and binary():
- ui.write(_(" Binary file matches"))
+ fm.plain(_(" Binary file matches"))
else:
- displaymatches(l)
- ui.write(eol)
+ displaymatches(fm.nested('texts'), l)
+ fm.plain(eol)
found = True
if opts.get('files_with_matches'):
break
return found
- def displaymatches(l):
+ def displaymatches(fm, l):
p = 0
for s, e in l.findpos():
- ui.write(l.line[p:s])
- ui.write(l.line[s:e], label='grep.match')
+ if p < s:
+ fm.startitem()
+ fm.write('text', '%s', l.line[p:s])
+ fm.data(matched=False)
+ fm.startitem()
+ fm.write('text', '%s', l.line[s:e], label='grep.match')
+ fm.data(matched=True)
p = e
- ui.write(l.line[p:])
+ if p < len(l.line):
+ fm.startitem()
+ fm.write('text', '%s', l.line[p:])
+ fm.data(matched=False)
+ fm.end()
skip = {}
revfiles = {}
@@ -4475,6 +4491,7 @@
except error.LookupError:
pass
+ fm = ui.formatter('grep', opts)
for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
rev = ctx.rev()
parent = ctx.p1().rev()
@@ -4487,7 +4504,7 @@
continue
pstates = matches.get(parent, {}).get(copy or fn, [])
if pstates or states:
- r = display(fn, ctx, pstates, states)
+ r = display(fm, fn, ctx, pstates, states)
found = found or r
if r and not opts.get('all'):
skip[fn] = True
@@ -4495,6 +4512,7 @@
skip[copy] = True
del matches[rev]
del revfiles[rev]
+ fm.end()
return not found
--- a/tests/test-completion.t Thu Aug 18 14:52:06 2016 +0900
+++ b/tests/test-completion.t Thu Aug 18 15:13:56 2016 +0900
@@ -278,7 +278,7 @@
debugwireargs: three, four, five, ssh, remotecmd, insecure
files: rev, print0, include, exclude, template, subrepos
graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
- grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
+ grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude
heads: rev, topo, active, closed, style, template
help: extension, command, keyword, system
identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
--- a/tests/test-grep.t Thu Aug 18 14:52:06 2016 +0900
+++ b/tests/test-grep.t Thu Aug 18 15:13:56 2016 +0900
@@ -40,6 +40,61 @@
\x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc)
\x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/ex\x1b[0;31;1mport\x1b[0m (esc)
+simple templated
+
+ $ hg grep port \
+ > -T '{file}:{rev}:{node|short}:{texts % "{if(matched, text|upper, text)}"}\n'
+ port:4:914fa752cdea:exPORT
+ port:4:914fa752cdea:vaPORTight
+ port:4:914fa752cdea:imPORT/exPORT
+
+simple JSON (no "change" field)
+
+ $ hg grep -Tjson port
+ [
+ {
+ "date": [4.0, 0],
+ "file": "port",
+ "line_number": 1,
+ "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
+ "rev": 4,
+ "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "date": [4.0, 0],
+ "file": "port",
+ "line_number": 2,
+ "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
+ "rev": 4,
+ "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
+ "user": "spam"
+ },
+ {
+ "date": [4.0, 0],
+ "file": "port",
+ "line_number": 3,
+ "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
+ "rev": 4,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ }
+ ]
+
+simple JSON without matching lines
+
+ $ hg grep -Tjson -l port
+ [
+ {
+ "date": [4.0, 0],
+ "file": "port",
+ "line_number": 1,
+ "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
+ "rev": 4,
+ "user": "spam"
+ }
+ ]
+
all
$ hg grep --traceback --all -nu port port
@@ -53,6 +108,102 @@
port:1:2:+:eggs:export
port:0:1:+:spam:import
+all JSON
+
+ $ hg grep --all -Tjson port port
+ [
+ {
+ "change": "-",
+ "date": [4.0, 0],
+ "file": "port",
+ "line_number": 4,
+ "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
+ "rev": 4,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "change": "+",
+ "date": [3.0, 0],
+ "file": "port",
+ "line_number": 4,
+ "node": "95040cfd017d658c536071c6290230a613c4c2a6",
+ "rev": 3,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
+ "user": "eggs"
+ },
+ {
+ "change": "-",
+ "date": [2.0, 0],
+ "file": "port",
+ "line_number": 1,
+ "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
+ "rev": 2,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "change": "-",
+ "date": [2.0, 0],
+ "file": "port",
+ "line_number": 2,
+ "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
+ "rev": 2,
+ "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "change": "+",
+ "date": [2.0, 0],
+ "file": "port",
+ "line_number": 1,
+ "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
+ "rev": 2,
+ "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "change": "+",
+ "date": [2.0, 0],
+ "file": "port",
+ "line_number": 2,
+ "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
+ "rev": 2,
+ "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
+ "user": "spam"
+ },
+ {
+ "change": "+",
+ "date": [2.0, 0],
+ "file": "port",
+ "line_number": 3,
+ "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
+ "rev": 2,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ },
+ {
+ "change": "+",
+ "date": [1.0, 0],
+ "file": "port",
+ "line_number": 2,
+ "node": "8b20f75c158513ff5ac80bd0e5219bfb6f0eb587",
+ "rev": 1,
+ "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
+ "user": "eggs"
+ },
+ {
+ "change": "+",
+ "date": [0.0, 0],
+ "file": "port",
+ "line_number": 1,
+ "node": "f31323c9217050ba245ee8b537c713ec2e8ab226",
+ "rev": 0,
+ "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
+ "user": "spam"
+ }
+ ]
+
other
$ hg grep -l port port