Mercurial > public > mercurial-scm > hg
comparison mercurial/commands.py @ 45696:de6f2afc0247
grep: move match and diff logic to new module
commands.grep() has lots of functions and classes. Let's split it into
reusable components so we can leverage them to implement a revset predicate
for 'hg grep --diff'. I want to do 'hg log -r "diff(pattern)"'.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Wed, 09 Sep 2020 15:23:49 +0900 |
parents | 760bb4d74aad |
children | 494642ed3c50 |
comparison
equal
deleted
inserted
replaced
45695:760bb4d74aad | 45696:de6f2afc0247 |
---|---|
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 from __future__ import absolute_import | 8 from __future__ import absolute_import |
9 | 9 |
10 import difflib | |
11 import errno | 10 import errno |
12 import os | 11 import os |
13 import re | 12 import re |
14 import sys | 13 import sys |
15 | 14 |
39 exchange, | 38 exchange, |
40 extensions, | 39 extensions, |
41 filemerge, | 40 filemerge, |
42 formatter, | 41 formatter, |
43 graphmod, | 42 graphmod, |
43 grep as grepmod, | |
44 hbisect, | 44 hbisect, |
45 help, | 45 help, |
46 hg, | 46 hg, |
47 logcmdutil, | 47 logcmdutil, |
48 match as matchmod, | 48 match as matchmod, |
3397 sep, eol = b':', b'\n' | 3397 sep, eol = b':', b'\n' |
3398 if opts.get(b'print0'): | 3398 if opts.get(b'print0'): |
3399 sep = eol = b'\0' | 3399 sep = eol = b'\0' |
3400 | 3400 |
3401 getfile = util.lrucachefunc(repo.file) | 3401 getfile = util.lrucachefunc(repo.file) |
3402 | |
3403 def matchlines(body, regexp): | |
3404 begin = 0 | |
3405 linenum = 0 | |
3406 while begin < len(body): | |
3407 match = regexp.search(body, begin) | |
3408 if not match: | |
3409 break | |
3410 mstart, mend = match.span() | |
3411 linenum += body.count(b'\n', begin, mstart) + 1 | |
3412 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin | |
3413 begin = body.find(b'\n', mend) + 1 or len(body) + 1 | |
3414 lend = begin - 1 | |
3415 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend] | |
3416 | |
3417 class linestate(object): | |
3418 def __init__(self, line, linenum, colstart, colend): | |
3419 self.line = line | |
3420 self.linenum = linenum | |
3421 self.colstart = colstart | |
3422 self.colend = colend | |
3423 | |
3424 def __hash__(self): | |
3425 return hash(self.line) | |
3426 | |
3427 def __eq__(self, other): | |
3428 return self.line == other.line | |
3429 | |
3430 def findpos(self, regexp): | |
3431 """Iterate all (start, end) indices of matches""" | |
3432 yield self.colstart, self.colend | |
3433 p = self.colend | |
3434 while p < len(self.line): | |
3435 m = regexp.search(self.line, p) | |
3436 if not m: | |
3437 break | |
3438 if m.end() == p: | |
3439 p += 1 | |
3440 else: | |
3441 yield m.span() | |
3442 p = m.end() | |
3443 | |
3444 matches = {} | 3402 matches = {} |
3445 copies = {} | 3403 copies = {} |
3446 | 3404 |
3447 def grepbody(fn, rev, body): | 3405 def grepbody(fn, rev, body): |
3448 matches[rev].setdefault(fn, []) | 3406 matches[rev].setdefault(fn, []) |
3449 m = matches[rev][fn] | 3407 m = matches[rev][fn] |
3450 if body is None: | 3408 if body is None: |
3451 return | 3409 return |
3452 | 3410 |
3453 for lnum, cstart, cend, line in matchlines(body, regexp): | 3411 for lnum, cstart, cend, line in grepmod.matchlines(body, regexp): |
3454 s = linestate(line, lnum, cstart, cend) | 3412 s = grepmod.linestate(line, lnum, cstart, cend) |
3455 m.append(s) | 3413 m.append(s) |
3456 | |
3457 def difflinestates(a, b): | |
3458 sm = difflib.SequenceMatcher(None, a, b) | |
3459 for tag, alo, ahi, blo, bhi in sm.get_opcodes(): | |
3460 if tag == 'insert': | |
3461 for i in pycompat.xrange(blo, bhi): | |
3462 yield (b'+', b[i]) | |
3463 elif tag == 'delete': | |
3464 for i in pycompat.xrange(alo, ahi): | |
3465 yield (b'-', a[i]) | |
3466 elif tag == 'replace': | |
3467 for i in pycompat.xrange(alo, ahi): | |
3468 yield (b'-', a[i]) | |
3469 for i in pycompat.xrange(blo, bhi): | |
3470 yield (b'+', b[i]) | |
3471 | 3414 |
3472 uipathfn = scmutil.getuipathfn(repo) | 3415 uipathfn = scmutil.getuipathfn(repo) |
3473 | 3416 |
3474 def display(fm, fn, ctx, pstates, states): | 3417 def display(fm, fn, ctx, pstates, states): |
3475 rev = scmutil.intrev(ctx) | 3418 rev = scmutil.intrev(ctx) |
3491 except error.WdirUnsupported: | 3434 except error.WdirUnsupported: |
3492 return ctx[fn].isbinary() | 3435 return ctx[fn].isbinary() |
3493 | 3436 |
3494 fieldnamemap = {b'linenumber': b'lineno'} | 3437 fieldnamemap = {b'linenumber': b'lineno'} |
3495 if diff: | 3438 if diff: |
3496 iter = difflinestates(pstates, states) | 3439 iter = grepmod.difflinestates(pstates, states) |
3497 else: | 3440 else: |
3498 iter = [(b'', l) for l in states] | 3441 iter = [(b'', l) for l in states] |
3499 for change, l in iter: | 3442 for change, l in iter: |
3500 fm.startitem() | 3443 fm.startitem() |
3501 fm.context(ctx=ctx) | 3444 fm.context(ctx=ctx) |