File ticket13861.patch of Package trac

diff -ur Trac-1.6/doc/api/trac_versioncontrol_diff.rst Trac-1.6_fix/doc/api/trac_versioncontrol_diff.rst
--- doc/api/trac_versioncontrol_diff.rst	2023-09-23 01:00:51.000000000 +0200
+++ doc/api/trac_versioncontrol_diff.rst	2025-02-20 09:21:59.390945253 +0100
@@ -10,7 +10,7 @@
 `difflib.SequenceMatcher`, and they generate groups of opcodes
 corresponding to diff "hunks".
 
-`get_change_extent` is a low-level utility used when marking
+`get_change_extents` is a low-level utility used when marking
 intra-lines differences.
 
 `diff_blocks` is used at a higher-level to fill the template data
@@ -27,7 +27,7 @@
 Function Reference
 ------------------
 
-.. autofunction :: get_change_extent
+.. autofunction :: get_change_extents
 .. autofunction :: get_filtered_hunks
 .. autofunction :: get_hunks
 .. autofunction :: diff_blocks
diff -ur Trac-1.6/trac/mimeview/patch.py Trac-1.6_fix/trac/mimeview/patch.py
--- trac/mimeview/patch.py	2023-09-23 01:00:49.000000000 +0200
+++ trac/mimeview/patch.py	2025-02-20 09:20:07.829126484 +0100
@@ -77,15 +77,20 @@
         def _markup_intraline_change(fromlines, tolines):
             from trac.versioncontrol.diff import get_change_extent
             for i in range(len(fromlines)):
-                fr, to = fromlines[i], tolines[i]
-                (start, end) = get_change_extent(fr, to)
-                if start != 0 or end != 0:
-                    last = end+len(fr)
-                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
-                                   '\1' + fr[last:]
-                    last = end+len(to)
-                    tolines[i] = to[:start] + '\0' + to[start:last] + \
-                                 '\1' + to[last:]
+                fromline, toline = fromlines[i], tolines[i]
+                fromlen = len(fromline)
+                tolen = len(toline)
+                for fromstart, fromend, tostart, toend in get_change_extents(fromline, toline):
+                    if fromstart != fromend and (fromstart != 0 or fromend != fromlen):
+                        fromline = (
+                            fromline[:fromstart] + '\0' + fromline[fromstart:fromend] +
+                            '\1' + fromline[fromend:])
+                    if tostart != toend and (tostart != 0 or toend != tolen):
+                        toline = (
+                            toline[:tostart] + '\0' + toline[tostart:toend] +
+                                '\1' + toline[toend:])
+                fromlines[i] = fromline
+                tolines[i] = toline
 
         space_re = re.compile(' ( +)|^ ')
         def htmlify(match):
diff -ur Trac-1.6/trac/versioncontrol/diff.py Trac-1.6_fix/trac/versioncontrol/diff.py
--- trac/versioncontrol/diff.py	2023-09-23 01:00:28.000000000 +0200
+++ trac/versioncontrol/diff.py	2025-02-20 09:16:31.485599568 +0100
@@ -30,24 +30,42 @@
     return ' '.join(_whitespace_split(text))
 
 
-def get_change_extent(str1, str2):
-    """Determines the extent of differences between two strings.
+def get_change_extents(str1, str2):
+    """Determines the extents of differences between two strings.
 
-    Returns a pair containing the offset at which the changes start,
-    and the negative offset at which the changes end.
-
-    If the two strings have neither a common prefix nor a common
-    suffix, ``(0, 0)`` is returned.
+    Returns a list of typles containing the offsets at which the changes start,
+    and the offsets at which the changes end:
+    [(startstr1,endstr1,startstr2,endstr2),(...)].
+    The list is sorted from the last to the first.
     """
+    done = []
     start = 0
-    limit = min(len(str1), len(str2))
+    minlen = 7
+    l1 = len(str1)
+    l2 = len(str2)
+    limit = min(l1, l2)
     while start < limit and str1[start] == str2[start]:
         start += 1
     end = -1
     limit = limit - start
     while -end <= limit and str1[end] == str2[end]:
         end -= 1
-    return start, end + 1
+    new = [(start, l1 + end + 1, start, l2 + end + 1)]
+    matcher = difflib.SequenceMatcher(None, str1, str2)
+    while new:
+        n = []
+        for e in new:
+            if min(e[1]-e[0],e[3]-e[2]) < minlen+2:
+                done.append(e)
+            else:
+                (s1,s2,s) = matcher.find_longest_match(e[0], e[1], e[2], e[3])
+                if s < minlen:
+                    done.append(e)
+                else:
+                    n.append((e[0],s1,e[2],s2))
+                    n.append((s1+s,e[1],s2+s,e[3]))
+        new = n
+    return sorted(done, reverse=True)
 
 
 def get_filtered_hunks(fromlines, tolines, context=None,
@@ -207,16 +225,19 @@
             if tag == 'replace' and i2 - i1 == j2 - j1:
                 for i in range(i2 - i1):
                     fromline, toline = fromlines[i1 + i], tolines[j1 + i]
-                    (start, end) = get_change_extent(fromline, toline)
-                    if start != 0 or end != 0:
-                        last = end + len(fromline)
-                        fromlines[i1 + i] = (
-                            fromline[:start] + '\0' + fromline[start:last] +
-                            '\1' + fromline[last:])
-                        last = end+len(toline)
-                        tolines[j1 + i] = (
-                            toline[:start] + '\0' + toline[start:last] +
-                            '\1' + toline[last:])
+                    fromlen = len(fromline)
+                    tolen = len(toline)
+                    for fromstart, fromend, tostart, toend in get_change_extents(fromline, toline):
+                        if fromstart != fromend and (fromstart != 0 or fromend != fromlen):
+                            fromline = (
+                                fromline[:fromstart] + '\0' + fromline[fromstart:fromend] +
+                                '\1' + fromline[fromend:])
+                        if tostart != toend and (tostart != 0 or toend != tolen):
+                            toline = (
+                                toline[:tostart] + '\0' + toline[tostart:toend] +
+                                    '\1' + toline[toend:])
+                    fromlines[i1 + i] = fromline
+                    tolines[j1 + i] = toline
             yield tag, i1, i2, j1, j2
 
     changes = []
diff -ur Trac-1.6/trac/versioncontrol/tests/diff.py Trac-1.6_fix/trac/versioncontrol/tests/diff.py
--- trac/versioncontrol/tests/diff.py	2023-09-23 01:00:29.000000000 +0200
+++ trac/versioncontrol/tests/diff.py	2025-02-20 09:31:47.556991308 +0100
@@ -25,15 +25,15 @@
 class DiffTestCase(unittest.TestCase):
 
     def testget_change_extent(self):
-        self.assertEqual((3, 0), diff.get_change_extent('xxx', 'xxx'))
-        self.assertEqual((0, 0), diff.get_change_extent('', 'xxx'))
-        self.assertEqual((0, 0), diff.get_change_extent('xxx', ''))
-        self.assertEqual((0, 0), diff.get_change_extent('xxx', 'yyy'))
-        self.assertEqual((1, -1), diff.get_change_extent('xxx', 'xyx'))
-        self.assertEqual((1, -1), diff.get_change_extent('xxx', 'xyyyx'))
-        self.assertEqual((1, 0), diff.get_change_extent('xy', 'xzz'))
-        self.assertEqual((1, -1), diff.get_change_extent('xyx', 'xzzx'))
-        self.assertEqual((1, -1), diff.get_change_extent('xzzx', 'xyx'))
+        self.assertEqual([(3, 3, 3, 3)], diff.get_change_extents('xxx', 'xxx'))
+        self.assertEqual([(0, 0, 0, 3)], diff.get_change_extents('', 'xxx'))
+        self.assertEqual([(0, 3, 0, 0)], diff.get_change_extents('xxx', ''))
+        self.assertEqual([(0, 3, 0, 3)], diff.get_change_extents('xxx', 'yyy'))
+        self.assertEqual([(1, 2, 1, 2)], diff.get_change_extents('xxx', 'xyx'))
+        self.assertEqual([(1, 2, 1, 4)], diff.get_change_extents('xxx', 'xyyyx'))
+        self.assertEqual([(1, 2, 1, 3)], diff.get_change_extents('xy', 'xzz'))
+        self.assertEqual([(1, 2, 1, 3)], diff.get_change_extents('xyx', 'xzzx'))
+        self.assertEqual([(1, 3, 1, 2)], diff.get_change_extents('xzzx', 'xyx'))
 
     def test_insert_blank_line(self):
         opcodes = get_opcodes(['A', 'B'], ['A', 'B', ''], ignore_blank_lines=0)
openSUSE Build Service is sponsored by