r""" When replacing one block of lines with another, search the blocks for *similar* lines; the best-matching pair (if any) is used as a synch point, and intraline difference marking is done on the similar pair. Lots of work, but often worth it. Example
(self, a, alo, ahi, b, blo, bhi)
| 891 | yield from g |
| 892 | |
| 893 | def _fancy_replace(self, a, alo, ahi, b, blo, bhi): |
| 894 | r""" |
| 895 | When replacing one block of lines with another, search the blocks |
| 896 | for *similar* lines; the best-matching pair (if any) is used as a |
| 897 | synch point, and intraline difference marking is done on the |
| 898 | similar pair. Lots of work, but often worth it. |
| 899 | |
| 900 | Example: |
| 901 | |
| 902 | >>> d = Differ() |
| 903 | >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1, |
| 904 | ... ['abcdefGhijkl\n'], 0, 1) |
| 905 | >>> print(''.join(results), end="") |
| 906 | - abcDefghiJkl |
| 907 | ? ^ ^ ^ |
| 908 | + abcdefGhijkl |
| 909 | ? ^ ^ ^ |
| 910 | """ |
| 911 | |
| 912 | # don't synch up unless the lines have a similarity score of at |
| 913 | # least cutoff; best_ratio tracks the best score seen so far |
| 914 | best_ratio, cutoff = 0.74, 0.75 |
| 915 | cruncher = SequenceMatcher(self.charjunk) |
| 916 | eqi, eqj = None, None # 1st indices of equal lines (if any) |
| 917 | |
| 918 | # search for the pair that matches best without being identical |
| 919 | # (identical lines must be junk lines, & we don't want to synch up |
| 920 | # on junk -- unless we have to) |
| 921 | for j in range(blo, bhi): |
| 922 | bj = b[j] |
| 923 | cruncher.set_seq2(bj) |
| 924 | for i in range(alo, ahi): |
| 925 | ai = a[i] |
| 926 | if ai == bj: |
| 927 | if eqi is None: |
| 928 | eqi, eqj = i, j |
| 929 | continue |
| 930 | cruncher.set_seq1(ai) |
| 931 | # computing similarity is expensive, so use the quick |
| 932 | # upper bounds first -- have seen this speed up messy |
| 933 | # compares by a factor of 3. |
| 934 | # note that ratio() is only expensive to compute the first |
| 935 | # time it's called on a sequence pair; the expensive part |
| 936 | # of the computation is cached by cruncher |
| 937 | if cruncher.real_quick_ratio() > best_ratio and \ |
| 938 | cruncher.quick_ratio() > best_ratio and \ |
| 939 | cruncher.ratio() > best_ratio: |
| 940 | best_ratio, best_i, best_j = cruncher.ratio(), i, j |
| 941 | if best_ratio < cutoff: |
| 942 | # no non-identical "pretty close" pair |
| 943 | if eqi is None: |
| 944 | # no identical pair either -- treat it as a straight replace |
| 945 | yield from self._plain_replace(a, alo, ahi, b, blo, bhi) |
| 946 | return |
| 947 | # no close pair, but an identical pair -- synch up on that |
| 948 | best_i, best_j, best_ratio = eqi, eqj, 1.0 |
| 949 | else: |
| 950 | # there's a close pair, so forget the identical pair (if any) |
no test coverage detected