[meld] Restrict changes returned by single/pair_changes with range arguments



commit 3c0cd173732a1b8f3e0d65099d7ee99227a929b7
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Sun Mar 21 08:00:43 2010 +1000

    Restrict changes returned by single/pair_changes with range arguments
    
    The single/pair_changes methods always iterate over all changes, despite
    most callers immediately filtering down to a certain subset. With this
    commit, these methods accept an optional argument of line ranges to
    restrict the set of changes returned.
    
    The semantics of the line range limitation in _range_from_lines() are:
     * the starting change is the change that contains the start line, or
       the first change that starts after the starting line, or None
     * the ending change is the change that contains the end line, or the
       the last change that ends before the ending line, or None

 meld/diffutil.py |   46 +++++++++++++++++++++++++++++++++++++++-------
 meld/filediff.py |   19 ++++---------------
 2 files changed, 43 insertions(+), 22 deletions(-)
---
diff --git a/meld/diffutil.py b/meld/diffutil.py
index aa25d99..1a4f598 100644
--- a/meld/diffutil.py
+++ b/meld/diffutil.py
@@ -115,7 +115,8 @@ class Differ(gobject.GObject):
 
     def _update_line_cache(self):
         for i, l in enumerate(self.seqlength):
-            self._line_cache[i] = [(None, None, None)] * l
+            # seqlength + 1 for after-last-line requests, which we do
+            self._line_cache[i] = [(None, None, None)] * (l + 1)
 
         last_chunk = len(self._merge_cache)
         def find_next(diff, seq, current):
@@ -276,33 +277,64 @@ class Differ(gobject.GObject):
                                                 for c in self.diffs[which][hiidx:] ]
         self.diffs[which][loidx:hiidx] = newdiffs
 
+    def _range_from_lines(self, textindex, lines):
+        lo_line, hi_line = lines
+        top_chunk = self.locate_chunk(textindex, lo_line)
+        start = top_chunk[0]
+        if start is None:
+            start = top_chunk[2]
+        bottom_chunk = self.locate_chunk(textindex, hi_line)
+        end = bottom_chunk[0]
+        if end is None:
+            end = bottom_chunk[1]
+        return start, end
+
     def all_changes(self):
         return iter(self._merge_cache)
 
-    def pair_changes(self, fromindex, toindex):
+    def pair_changes(self, fromindex, toindex, lines=(None, None, None, None)):
         """Give all changes between file1 and either file0 or file2.
         """
+        if None not in lines:
+            start1, end1 = self._range_from_lines(fromindex, lines[0:2])
+            start2, end2 = self._range_from_lines(toindex, lines[2:4])
+            if (start1 is None or end1 is None) and \
+               (start2 is None or end2 is None):
+                return
+            start = min([x for x in (start1, start2) if x is not None])
+            end = max([x for x in (end1, end2) if x is not None])
+            merge_cache = self._merge_cache[start:end + 1]
+        else:
+            merge_cache = self._merge_cache
+
         if fromindex == 1:
             seq = toindex/2
-            for c in self._merge_cache:
+            for c in merge_cache:
                 if c[seq]:
                     yield c[seq]
         else:
             seq = fromindex/2
-            for c in self._merge_cache:
+            for c in merge_cache:
                 if c[seq]:
                     yield reverse_chunk(c[seq])
 
-    def single_changes(self, textindex):
+    def single_changes(self, textindex, lines=(None, None)):
         """Give changes for single file only. do not return 'equal' hunks.
         """
+        if None not in lines:
+            start, end = self._range_from_lines(textindex, lines)
+            if start is None or end is None:
+                return
+            merge_cache = self._merge_cache[start:end + 1]
+        else:
+            merge_cache = self._merge_cache
         if textindex in (0,2):
             seq = textindex/2
-            for cs in self._merge_cache:
+            for cs in merge_cache:
                 if cs[seq]:
                     yield reverse_chunk(cs[seq])
         else:
-            for cs in self._merge_cache:
+            for cs in merge_cache:
                 yield cs[0] or cs[1]
 
     def sequences_identical(self):
diff --git a/meld/filediff.py b/meld/filediff.py
index f6b68fd..d609685 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -932,8 +932,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             return
         visible = textview.get_visible_rect()
         pane = self.textview.index(textview)
-        start_line = self._pixel_to_line(pane, visible.y)
-        end_line = 1+self._pixel_to_line(pane, visible.y+visible.height)
+        bounds = (self._pixel_to_line(pane, visible.y),
+                  self._pixel_to_line(pane, visible.y + visible.height + 1))
 
         width, height = textview.allocation.width, textview.allocation.height
         context = event.window.cairo_create()
@@ -941,12 +941,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         context.clip()
         context.set_line_width(1.0)
 
-        for change in self.linediffer.single_changes(pane):
-            if change[2] < start_line:
-                continue
-            if change[1] > end_line:
-                break
-
+        for change in self.linediffer.single_changes(pane, bounds):
             ypos0 = self._line_to_pixel(pane, change[1]) - visible.y
             ypos1 = self._line_to_pixel(pane, change[2]) - visible.y
 
@@ -1311,13 +1306,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         # For bezier control points
         x_steps = [-0.5, (1. / 3) * wtotal, (2. / 3) * wtotal, wtotal + 0.5]
 
-        for c in self.linediffer.pair_changes(which, which + 1):
-            assert c[0] != "equal"
-            if c[2] < visible[1] and c[4] < visible[3]: # find first visible chunk
-                continue
-            elif c[1] > visible[2] and c[3] > visible[4]: # we've gone past last visible
-                break
-
+        for c in self.linediffer.pair_changes(which, which + 1, visible[1:5]):
             # f and t are short for "from" and "to"
             f0, f1 = [self._line_to_pixel(which, l) - pix_start[which] + rel_offset[which] for l in c[1:3]]
             t0, t1 = [self._line_to_pixel(which + 1, l) - pix_start[which + 1] + rel_offset[which + 1] for l in c[3:5]]



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]