[meld] Move filter parsing/compilation from FileDiff and DirDiff to MeldApp



commit b005a9780831207aabe2ad4e42dce3cbc1d89c1c
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Wed Feb 9 07:34:06 2011 +1000

    Move filter parsing/compilation from FileDiff and DirDiff to MeldApp
    
    Meld uses filters extensively, and currently several issues arise
    because of filter validity errors, maintaining per-view filters, and
    a default vs. current-view split of active filters. This commit starts
    the process of shifting filter parsing, compilation and validation to
    the application level.
    
    This commit also adds a new FilterEntry class to MeldApp. This class
    supercedes the removed TypeFilter class from DirDiff, and will
    eventually handle both text and file filters.

 meld/dirdiff.py  |   80 ++++++++++++++---------------------------------------
 meld/filediff.py |   22 ++++-----------
 meld/meldapp.py  |   67 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 75 deletions(-)
---
diff --git a/meld/dirdiff.py b/meld/dirdiff.py
index 7a554bf..e22a756 100644
--- a/meld/dirdiff.py
+++ b/meld/dirdiff.py
@@ -16,6 +16,7 @@
 ### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 import collections
+import copy
 import errno
 import paths
 from ui import gnomeglade
@@ -33,10 +34,11 @@ import re
 import stat
 import time
 
-from util.namedtuple import namedtuple
-
 import ui.emblemcellrenderer
 
+from util.namedtuple import namedtuple
+from meldapp import app
+
 gdk = gtk.gdk
 
 ################################################################################
@@ -169,19 +171,6 @@ class DirDiffTreeStore(tree.DiffTreeStore):
         types = [str] * COL_END * ntree
         tree.DiffTreeStore.__init__(self, ntree, types)
 
-################################################################################
-#
-# TypeFilter
-#
-################################################################################
-
-class TypeFilter(object):
-    __slots__ = ("label", "filter", "active")
-    def __init__(self, label, active, filter):
-        self.label = label
-        self.active = active
-        self.filter = filter
-
 
 class CanonicalListing(object):
     """Multi-pane lists with canonicalised matching and error detection"""
@@ -286,17 +275,6 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             tree.STATE_MODIFIED,
             tree.STATE_NEW,
         ]
-        self.update_regexes()
-
-    def update_regexes(self):
-        self.regexes = []
-        for r in [ misc.ListItem(i) for i in self.prefs.regexes.split("\n") ]:
-            if r.active:
-                try:
-                    self.regexes.append( re.compile(r.value+"(?m)") )
-                except re.error:
-                    misc.run_dialog(
-                        text=_("Error converting pattern '%s' to regular expression") % r.value )
 
     def _custom_popup_deactivated(self, popup):
         self.filter_menu_button.set_active(False)
@@ -332,39 +310,23 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         melddoc.MeldDoc.on_container_switch_out_event(self, ui)
 
     def create_name_filters(self):
-        self.name_filters_available = []
-        for f in [misc.ListItem(s) for s in self.prefs.filters.split("\n") ]:
-            bits = f.value.split()
-            if len(bits) > 1:
-                regex = "(%s)$" % "|".join( [misc.shell_to_regex(b)[:-1] for b in bits] )
-            elif len(bits):
-                regex = misc.shell_to_regex(bits[0])
-            else: # an empty pattern would match anything, skip it
-                continue
-            try:
-                cregex = re.compile(regex)
-            except re.error:
-                misc.run_dialog( _("Error converting pattern '%s' to regular expression") % f.value, self )
-            else:
-                func = lambda x, r=cregex : r.match(x) is None
-                self.name_filters_available.append( TypeFilter(f.name, f.active, func) )
-        self.name_filters = [f for f in self.name_filters_available if f.active]
-
+        self.name_filters = [copy.copy(f) for f in app.file_filters]
         actions = []
+        disabled_actions = []
         self.filter_ui = []
-        for i,f in enumerate(self.name_filters_available):
+        for i, f in enumerate(self.name_filters):
             name = "Hide%d" % i
             callback = lambda b, i=i: self._update_name_filter(b, i)
             actions.append((name, None, f.label, None, _("Hide %s") % f.label, callback, f.active))
             self.filter_ui.append(["/CustomPopup" , name, name, gtk.UI_MANAGER_MENUITEM, False])
             self.filter_ui.append(["/Menubar/ViewMenu/FileFilters" , name, name, gtk.UI_MANAGER_MENUITEM, False])
+            if f.filter is None:
+                disabled_actions.append(name)
 
         self.filter_actiongroup = gtk.ActionGroup("DirdiffFilterActions")
         self.filter_actiongroup.add_toggle_actions(actions)
-
-    def on_preference_changed(self, key, value):
-        if key == "regexes":
-            self.update_regexes()
+        for name in disabled_actions:
+            self.filter_actiongroup.get_action(name).set_sensitive(False)
 
     def _do_to_others(self, master, objects, methodname, args):
         if not hasattr(self, "do_to_others_lock"):
@@ -475,7 +437,9 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                     continue
 
                 for f in self.name_filters:
-                    entries = filter(f.filter, entries)
+                    if not f.active or f.filter is None:
+                        continue
+                    entries = [e for e in entries if f.filter.match(e) is None]
 
                 for e in entries:
                     try:
@@ -816,12 +780,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         self._update_state_filter( tree.STATE_MODIFIED, button.get_active() )
 
     def _update_name_filter(self, button, idx):
-        for i in range(len(self.name_filters)):
-            if self.name_filters[i] == self.name_filters_available[idx]:
-                self.name_filters.pop(i)
-                break
-        if button.get_active():
-            self.name_filters.append( self.name_filters_available[idx] )
+        self.name_filters[idx].active = button.get_active()
         self.refresh()
 
     def on_filter_hide_current_clicked(self, button):
@@ -852,12 +811,13 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         """
         assert len(roots) == self.model.ntree
         ret = []
+        regexes = [f.filter for f in app.text_filters if f.active and f.filter]
         for files in fileslist:
             curfiles = [ os.path.join( r, f ) for r,f in zip(roots,files) ]
             is_present = [ os.path.exists( f ) for f in curfiles ]
             all_present = 0 not in is_present
             if all_present:
-                if _files_same(curfiles, self.regexes) in (Same, SameFiltered):
+                if _files_same(curfiles, regexes) in (Same, SameFiltered):
                     state = tree.STATE_NORMAL
                 else:
                     state = tree.STATE_MODIFIED
@@ -871,6 +831,8 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
         """Update the state of the item at 'it'
         """
         files = self.model.value_paths(it)
+        regexes = [f.filter for f in app.text_filters if f.active and f.filter]
+
         def mtime(f):
             try:
                 return os.stat(f).st_mtime
@@ -883,7 +845,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
             newest_index = -1 # all same
         all_present = 0 not in mod_times
         if all_present:
-            all_same = _files_same( files, self.regexes )
+            all_same = _files_same(files, regexes)
             all_present_same = all_same
         else:
             lof = []
@@ -891,7 +853,7 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
                 if mod_times[j]:
                     lof.append( files[j] )
             all_same = Different
-            all_present_same = _files_same( lof, self.regexes )
+            all_present_same = _files_same(lof, regexes)
         different = 1
         one_isdir = [None for i in range(self.model.ntree)]
         for j in range(self.model.ntree):
diff --git a/meld/filediff.py b/meld/filediff.py
index 2a1fba4..6443374 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -38,6 +38,7 @@ import patchdialog
 import paths
 import merge
 
+from meldapp import app
 from util.sourceviewer import srcviewer
 from util.namedtuple import namedtuple
 
@@ -182,7 +183,6 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         melddoc.MeldDoc.__init__(self, prefs)
         gnomeglade.Component.__init__(self, paths.ui_dir("filediff.ui"), "filediff")
         self.map_widgets_into_lists(["textview", "fileentry", "diffmap", "scrolledwindow", "linkmap", "statusimage", "msgarea_mgr", "vbox"])
-        self._update_regexes()
         self.warned_bad_comparison = False
         # Some sourceviews bind their own undo mechanism, which we replace
         gtk.binding_entry_remove(srcviewer.GtkTextView, gtk.keysyms.z,
@@ -302,15 +302,6 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         if self.textview_focussed:
             self.scheduler.add_task(self.textview_focussed.grab_focus)
 
-    def _update_regexes(self):
-        self.regexes = []
-        for r in [ misc.ListItem(i) for i in self.prefs.regexes.split("\n") ]:
-            if r.active:
-                try:
-                    self.regexes.append( (re.compile(r.value+"(?m)"), r.value) )
-                except re.error:
-                    pass
-
     def _disconnect_buffer_handlers(self):
         for textview in self.textview:
             textview.set_editable(0)
@@ -543,12 +534,13 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             else:
                 return ""
         try:
-            for c,r in self.regexes:
-                txt = c.sub(killit,txt)
+            for filt in app.text_filters:
+                if filt.active:
+                    txt = filt.filter.sub(killit, txt)
         except AssertionError:
             if not self.warned_bad_comparison:
-                misc.run_dialog(_("Regular expression '%s' changed the number of lines in the file. "
-                    "Comparison will be incorrect. See the user manual for more details.") % r)
+                misc.run_dialog(_("Filter '%s' changed the number of lines in the file. "
+                    "Comparison will be incorrect. See the user manual for more details.") % filt.label)
                 self.warned_bad_comparison = True
         return txt
 
@@ -616,8 +608,6 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                     self.textbuffer[i],
                     self.bufferdata[i].filename,
                     self.prefs.use_syntax_highlighting )
-        elif key == "regexes":
-            self._update_regexes()
         elif key == "edit_wrap_lines":
             for t in self.textview:
                 t.set_wrap_mode(self.prefs.edit_wrap_lines)
diff --git a/meld/meldapp.py b/meld/meldapp.py
index 302fd84..2de7b48 100644
--- a/meld/meldapp.py
+++ b/meld/meldapp.py
@@ -18,16 +18,34 @@
 
 import optparse
 import os
+import re
 from gettext import gettext as _
 
 import gobject
 import gtk
 
+import misc
 import preferences
 
 version = "1.5.0"
 
 
+class FilterEntry(object):
+
+    __slots__ = ("label", "active", "filter")
+
+    def __init__(self, label, active, filter):
+        self.label = label
+        self.active = active
+        self.filter = filter
+
+    def __copy__(self):
+        new = type(self)(self.label, self.active, None)
+        if self.filter is not None:
+            new.filter = re.compile(self.filter.pattern, self.filter.flags)
+        return new
+
+
 class MeldApp(object):
 
     def __init__(self):
@@ -35,11 +53,60 @@ class MeldApp(object):
         gtk.window_set_default_icon_name("meld")
         self.version = version
         self.prefs = preferences.MeldPreferences()
+        self.prefs.notify_add(self.on_preference_changed)
+        self.file_filters = self._update_filters(self.prefs.filters)
+        self.text_filters = self._update_regexes(self.prefs.regexes)
 
     def create_window(self):
         self.window = meldwindow.MeldWindow()
         return self.window
 
+    def on_preference_changed(self, key, val):
+        if key == "filters":
+            self.file_filters = self._update_filters(val)
+            # FIXME: should emit a file-filters-changed signal here for
+            # DirDiff to respond to
+        elif key == "regexes":
+            self.text_filters = self._update_regexes(val)
+            # FIXME: should emit a text-filters-changed signal here for
+            # FileDiff and DirDiff to respond to
+
+    def _update_filters(self, filters_string):
+        filters = []
+        for filter_string in filters_string.split("\n"):
+            elements = filter_string.split("\t")
+            name, active = elements[0], bool(int(elements[1]))
+            bits = (" ".join(elements[2:])).split()
+            if len(bits) > 1:
+                regexes = [misc.shell_to_regex(b)[:-1] for b in bits]
+                regex = "(%s)$" % "|".join(regexes)
+            elif len(bits):
+                regex = misc.shell_to_regex(bits[0])
+            else: # an empty pattern would match anything, skip it
+                continue
+            try:
+                compiled = re.compile(regex)
+            except re.error:
+                active = False
+                compiled = None
+            filters.append(FilterEntry(name, active, compiled))
+        return filters
+
+    def _update_regexes(self, regexes_string):
+        regexes = []
+        for regex_string in regexes_string.split("\n"):
+            elements = regex_string.split("\t")
+            name, active = elements[0], bool(int(elements[1]))
+            regex = " ".join(elements[2:])
+            try:
+                compiled = re.compile(regex + "(?m)")
+            except re.error:
+                active = False
+                compiled = None
+            regexes.append(FilterEntry(name, active, compiled))
+        return regexes
+
+
     def diff_files_callback(self, option, opt_str, value, parser):
         """Gather --diff arguments and append to a list"""
         assert value is None



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