[meld] Fix overzealous inline highlighting invalidation (closes bgo#693690)



commit c91c5b459f245214071d3f68aff8094b3de608e6
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Sun Feb 17 10:16:08 2013 +1000

    Fix overzealous inline highlighting invalidation (closes bgo#693690)
    
    In the existing code, any buffer change invalidated all pending inline
    highlighting jobs. This causes serious issues if any highlighting jobs
    are pending when a change is made; these jobs will never be restarted
    unless their chunks are directly changed.
    
    The solution in this patch is to only consider inline highlighting
    results to be invalid if the actual text being highlighted has changed.
    We do this by using gtk.TextMarks instead of gtk.TextIters (avoiding
    buffer invalidation) and checking to see whether the text we're about
    to highlight actually matches what we think we're highlighting. If it
    doesn't, then we have a changed block, which *will* be rehighlighted
    in the relevant changed callback.
    
    The downside to this approach is that creation of gtk.TextMarks and
    validating the text before highlighting is computationally much more
    expensive than our previous approach.

 bin/meld         |    3 ++-
 meld/filediff.py |   37 ++++++++++++++++++++-----------------
 2 files changed, 22 insertions(+), 18 deletions(-)
---
diff --git a/bin/meld b/bin/meld
index ce67b7f..4094176 100755
--- a/bin/meld
+++ b/bin/meld
@@ -138,10 +138,11 @@ try:
 except (ImportError, AssertionError) as e:
     missing_reqs("pygobject", pygobjectver, e)
 
-
+gobject.threads_init()
 gtk.icon_theme_get_default().append_search_path(meld.paths.icon_dir())
 gtk.rc_parse(meld.paths.share_dir("gtkrc"))
 
+
 def main():
     import meld.meldapp
     app = meld.meldapp.app
diff --git a/meld/filediff.py b/meld/filediff.py
index 7a7df39..2010e31 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -83,7 +83,7 @@ class CachedSequenceMatcher(object):
         except KeyError:
             def inline_cb(opcodes):
                 self.cache[(text1, textn)] = [opcodes, time.time()]
-                cb(opcodes)
+                gobject.idle_add(lambda: cb(opcodes))
             process_pool.apply_async(matcher_worker, (text1, textn),
                                      callback=inline_cb)
 
@@ -215,15 +215,6 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         for buf in self.textbuffer:
             buf.create_tag("inline")
 
-        # We need to keep track of gtk.TextIter validity, but this isn't
-        # exposed anywhere. Instead, we keep our own counter for changes
-        # across all of our buffers.
-        self._buffer_changed_stamp = 0
-        def buffer_change(buf):
-            self._buffer_changed_stamp += 1
-        for buf in self.textbuffer:
-            buf.connect("changed", buffer_change)
-
         actions = (
             ("MakePatch", None, _("Format as patch..."), None, _("Create a patch using differences between 
files"), self.make_patch),
             ("PrevConflict", None, _("Previous conflict"), "<Ctrl>I", _("Go to the previous conflict"), 
lambda x: self.on_next_conflict(gtk.gdk.SCROLL_UP)),
@@ -1188,14 +1179,23 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 textn = text_type(textn, 'utf8')
 
                 # For very long sequences, bail rather than trying a very slow comparison
-                inline_limit = 8000 # arbitrary constant
+                inline_limit = 8000
                 if len(text1) + len(textn) > inline_limit:
                     for i in range(2):
                         bufs[i].apply_tag(tags[i], starts[i], ends[i])
                     continue
 
-                def apply_highlight(bufs, tags, starts, change_stamp, matches):
-                    if change_stamp != self._buffer_changed_stamp:
+                def apply_highlight(bufs, tags, starts, ends, texts, matches):
+                    starts = [bufs[0].get_iter_at_mark(starts[0]),
+                              bufs[1].get_iter_at_mark(starts[1])]
+                    ends = [bufs[0].get_iter_at_mark(ends[0]),
+                            bufs[1].get_iter_at_mark(ends[1])]
+                    text1 = bufs[0].get_text(starts[0], ends[0], False)
+                    text1 = text_type(text1, 'utf8')
+                    textn = bufs[1].get_text(starts[1], ends[1], False)
+                    textn = text_type(textn, 'utf8')
+
+                    if texts != (text1, textn):
                         return
 
                     # Remove equal matches of size greater than 3; highlight
@@ -1215,10 +1215,13 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     bufs[0].remove_tag(tags[0], starts[0], ends[0])
                     bufs[1].remove_tag(tags[1], starts[1], ends[1])
 
-                match_cb = functools.partial(apply_highlight,
-                                             bufs, tags, starts,
-                                             self._buffer_changed_stamp)
-                matches = self._cached_match.match(text1, textn, match_cb)
+                starts = [bufs[0].create_mark(None, starts[0], True),
+                          bufs[1].create_mark(None, starts[1], True)]
+                ends = [bufs[0].create_mark(None, ends[0], True),
+                        bufs[1].create_mark(None, ends[1], True)]
+                match_cb = functools.partial(apply_highlight, bufs, tags,
+                                             starts, ends, (text1, textn))
+                self._cached_match.match(text1, textn, match_cb)
 
         self._cached_match.clean(self.linediffer.diff_count())
 


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