Return list of 5-tuples describing how to turn a into b. Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the tuple preceding it, and likewise for j1 == the previous j2. The tags are str
(self)
| 491 | return self.matching_blocks |
| 492 | |
| 493 | def get_opcodes(self): |
| 494 | """Return list of 5-tuples describing how to turn a into b. |
| 495 | |
| 496 | Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple |
| 497 | has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the |
| 498 | tuple preceding it, and likewise for j1 == the previous j2. |
| 499 | |
| 500 | The tags are strings, with these meanings: |
| 501 | |
| 502 | 'replace': a[i1:i2] should be replaced by b[j1:j2] |
| 503 | 'delete': a[i1:i2] should be deleted. |
| 504 | Note that j1==j2 in this case. |
| 505 | 'insert': b[j1:j2] should be inserted at a[i1:i1]. |
| 506 | Note that i1==i2 in this case. |
| 507 | 'equal': a[i1:i2] == b[j1:j2] |
| 508 | |
| 509 | >>> a = "qabxcd" |
| 510 | >>> b = "abycdf" |
| 511 | >>> s = SequenceMatcher(None, a, b) |
| 512 | >>> for tag, i1, i2, j1, j2 in s.get_opcodes(): |
| 513 | ... print(("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % |
| 514 | ... (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))) |
| 515 | delete a[0:1] (q) b[0:0] () |
| 516 | equal a[1:3] (ab) b[0:2] (ab) |
| 517 | replace a[3:4] (x) b[2:3] (y) |
| 518 | equal a[4:6] (cd) b[3:5] (cd) |
| 519 | insert a[6:6] () b[5:6] (f) |
| 520 | """ |
| 521 | |
| 522 | if self.opcodes is not None: |
| 523 | return self.opcodes |
| 524 | i = j = 0 |
| 525 | self.opcodes = answer = [] |
| 526 | for ai, bj, size in self.get_matching_blocks(): |
| 527 | # invariant: we've pumped out correct diffs to change |
| 528 | # a[:i] into b[:j], and the next matching block is |
| 529 | # a[ai:ai+size] == b[bj:bj+size]. So we need to pump |
| 530 | # out a diff to change a[i:ai] into b[j:bj], pump out |
| 531 | # the matching block, and move (i,j) beyond the match |
| 532 | tag = '' |
| 533 | if i < ai and j < bj: |
| 534 | tag = 'replace' |
| 535 | elif i < ai: |
| 536 | tag = 'delete' |
| 537 | elif j < bj: |
| 538 | tag = 'insert' |
| 539 | if tag: |
| 540 | answer.append( (tag, i, ai, j, bj) ) |
| 541 | i, j = ai+size, bj+size |
| 542 | # the list of matching blocks is terminated by a |
| 543 | # sentinel with size 0 |
| 544 | if size: |
| 545 | answer.append( ('equal', ai, i, bj, j) ) |
| 546 | return answer |
| 547 | |
| 548 | def get_grouped_opcodes(self, n=3): |
| 549 | """ Isolate change clusters by eliminating ranges with no changes. |