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 | # Don't synch up unless the lines have a similarity score above |
| 912 | # cutoff. Previously only the smallest pair was handled here, |
| 913 | # and if there are many pairs with the best ratio, recursion |
| 914 | # could grow very deep, and runtime cubic. See: |
| 915 | # https://github.com/python/cpython/issues/119105 |
| 916 | # |
| 917 | # Later, more pathological cases prompted removing recursion |
| 918 | # entirely. |
| 919 | cutoff = 0.74999 |
| 920 | cruncher = SequenceMatcher(self.charjunk) |
| 921 | crqr = cruncher.real_quick_ratio |
| 922 | cqr = cruncher.quick_ratio |
| 923 | cr = cruncher.ratio |
| 924 | |
| 925 | WINDOW = 10 |
| 926 | best_i = best_j = None |
| 927 | dump_i, dump_j = alo, blo # smallest indices not yet resolved |
| 928 | for j in range(blo, bhi): |
| 929 | cruncher.set_seq2(b[j]) |
| 930 | # Search the corresponding i's within WINDOW for rhe highest |
| 931 | # ratio greater than `cutoff`. |
| 932 | aequiv = alo + (j - blo) |
| 933 | arange = range(max(aequiv - WINDOW, dump_i), |
| 934 | min(aequiv + WINDOW + 1, ahi)) |
| 935 | if not arange: # likely exit if `a` is shorter than `b` |
| 936 | break |
| 937 | best_ratio = cutoff |
| 938 | for i in arange: |
| 939 | cruncher.set_seq1(a[i]) |
| 940 | # Ordering by cheapest to most expensive ratio is very |
| 941 | # valuable, most often getting out early. |
| 942 | if (crqr() > best_ratio |
| 943 | and cqr() > best_ratio |
| 944 | and cr() > best_ratio): |
| 945 | best_i, best_j, best_ratio = i, j, cr() |
| 946 | |
| 947 | if best_i is None: |
| 948 | # found nothing to synch on yet - move to next j |
| 949 | continue |
| 950 |
no test coverage detected