⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filediff.py

📁 LINUX下的文件比较工具
💻 PY
📖 第 1 页 / 共 4 页
字号:
### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>### This program is free software; you can redistribute it and/or modify### it under the terms of the GNU General Public License as published by### the Free Software Foundation; either version 2 of the License, or### (at your option) any later version.### This program is distributed in the hope that it will be useful,### but WITHOUT ANY WARRANTY; without even the implied warranty of### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the### GNU General Public License for more details.### You should have received a copy of the GNU General Public License### along with this program; if not, write to the Free Software### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USAfrom __future__ import generatorsimport codecsimport mathimport osimport reimport difflibimport structimport pangoimport gobjectimport gtkimport gtk.keysymsimport diffutilimport gnomegladeimport miscimport melddocimport pathssourceview_available = 0for sourceview in "gtksourceview sourceview".split():    try:        gsv = __import__(sourceview)        sourceview_available = 1        break    except ImportError:        passif sourceview_available:    def set_highlighting_enabled(buf, fname, enabled):        # gnome.vfs.get_mime_type seems to be broken. fake it.        extmap = { "xml":"text/xml",                   "glade":"text/xml",                   "cpp":"text/x-cpp",                   "cxx":"text/x-cpp",                   "cc":"text/x-cpp",                   "C":"text/x-cpp",                   "c":"text/x-c",                   "hpp":"text/x-cpp",                   "hxx":"text/x-cpp",                   "hh":"text/x-cpp",                   "H":"text/x-cpp",                   "h":"text/x-cpp",                   "inl":"text/x-cpp",                   "desktop": "application/x-desktop",                   "diff": "text/x-diff",                   "patch": "text/x-diff",                   "html": "text/html",                   "po": "text/x-po",                   "py": "text/x-python" }        ext = fname.split(".")[-1]        man = gsv.SourceLanguagesManager()        gsl = man.get_language_from_mime_type( extmap.get(ext, "text/plain") )        if gsl:            buf.set_language(gsl)        buf.set_highlight(enabled)gdk = gtk.gdk################################################################################## FileDiff#################################################################################MASK_SHIFT, MASK_CTRL, MASK_ALT = 1, 2, 3class FileDiff(melddoc.MeldDoc, gnomeglade.Component):    """Two or three way diff of text files.    """    keylookup = {gtk.keysyms.Shift_L : MASK_SHIFT,                 gtk.keysyms.Control_L : MASK_CTRL,                 gtk.keysyms.Alt_L : MASK_ALT,                 gtk.keysyms.Shift_R : MASK_SHIFT,                 gtk.keysyms.Control_R : MASK_CTRL,                 gtk.keysyms.Alt_R : MASK_ALT }    def __init__(self, prefs, num_panes):        """Start up an filediff with num_panes empty contents.        """        melddoc.MeldDoc.__init__(self, prefs)        override = {}        if sourceview_available:            override["GtkTextView"] = gsv.SourceView            override["GtkTextBuffer"] = gsv.SourceBuffer        gnomeglade.Component.__init__(self, paths.share_dir("glade2/filediff.glade"), "filediff", override)        self._map_widgets_into_lists( ["textview", "fileentry", "diffmap", "scrolledwindow", "linkmap", "statusimage"] )        self._update_regexes()        self.warned_bad_comparison = False        if sourceview_available:            for v in self.textview:                v.set_buffer( gsv.SourceBuffer() )                v.set_show_line_numbers(self.prefs.show_line_numbers)        self.keymask = 0        self.load_font()        self.deleted_lines_pending = -1        self.textview_overwrite = 0        self.textview_focussed = None        self.textview_overwrite_handlers = [ t.connect("toggle-overwrite", self.on_textview_toggle_overwrite) for t in self.textview ]        for i in range(3):            w = self.scrolledwindow[i]            w.get_vadjustment().connect("value-changed", self._sync_vscroll )            w.get_hadjustment().connect("value-changed", self._sync_hscroll )        self._connect_buffer_handlers()        self.linediffer = diffutil.Differ()        for l in self.linkmap: # glade bug workaround            l.set_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK )            l.set_double_buffered(0) # we call paint_begin ourselves        self.bufferdata = []        for text in self.textview:            text.set_wrap_mode( self.prefs.edit_wrap_lines )            buf = text.get_buffer()            self.bufferdata.append( MeldBufferData() )            def add_tag(name, props):                tag = buf.create_tag(name)                for p,v in props.items():                    tag.set_property(p,v)            add_tag("edited line",   {"background": self.prefs.color_edited_bg,                                      "foreground": self.prefs.color_edited_fg} )            add_tag("delete line",   {"background": self.prefs.color_delete_bg,                                      "foreground": self.prefs.color_delete_fg}  )            add_tag("replace line",  {"background": self.prefs.color_replace_bg,                                      "foreground": self.prefs.color_replace_fg} )            add_tag("conflict line", {"background": self.prefs.color_conflict_bg,                                      "foreground": self.prefs.color_conflict_fg} )            add_tag("inline line",   {"background": self.prefs.color_inline_bg,                                      "foreground": self.prefs.color_inline_fg} )        class ContextMenu(gnomeglade.Component):            def __init__(self, app):                gladefile = paths.share_dir("glade2/filediff.glade")                gnomeglade.Component.__init__(self, gladefile, "popup")                self.parent = app                self.pane = -1            def popup_in_pane( self, pane ):                self.pane = pane                self.copy_left.set_sensitive( pane > 0 )                self.copy_right.set_sensitive( pane+1 < self.parent.num_panes )                self.widget.popup( None, None, None, 3, gtk.get_current_event_time() )            def on_save_activate(self, menuitem):                self.parent.save()            def on_save_as_activate(self, menuitem):                self.parent.save_file( self.pane, 1)            def on_make_patch_activate(self, menuitem):                self.parent.make_patch( self.pane )            def on_cut_activate(self, menuitem):                self.parent.on_cut_activate()            def on_copy_activate(self, menuitem):                self.parent.on_copy_activate()            def on_paste_activate(self, menuitem):                self.parent.on_paste_activate()            def on_copy_left_activate(self, menuitem):                self.parent.copy_selected(-1)            def on_copy_right_activate(self, menuitem):                self.parent.copy_selected(1)            def on_edit_activate(self, menuitem):                if self.parent.bufferdata[self.pane].filename:                    self.parent._edit_files( [self.parent.bufferdata[self.pane].filename] )        self.popup_menu = ContextMenu(self)        self.find_dialog = None        self.last_search = None        self.set_num_panes(num_panes)        gtk.idle_add( lambda *args: self.load_font()) # hack around Bug 316730    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:            buf = textview.get_buffer()            assert hasattr(buf,"handlers")            textview.set_editable(0)            for h in buf.handlers:                buf.disconnect(h)    def _connect_buffer_handlers(self):        for textview in self.textview:            buf = textview.get_buffer()            textview.set_editable(1)            id0 = buf.connect("insert-text", self.on_text_insert_text)            id1 = buf.connect("delete-range", self.on_text_delete_range)            id2 = buf.connect_after("insert-text", self.after_text_insert_text)            id3 = buf.connect_after("delete-range", self.after_text_delete_range)            buf.textview = textview            buf.handlers = id0, id1, id2, id3    def _update_cursor_status(self, buf):        def update():            it = buf.get_iter_at_mark( buf.get_insert() )            # Abbreviation for insert,overwrite so that it will fit in the status bar            insert_overwrite = _("INS,OVR").split(",")[ self.textview_overwrite ]            # Abbreviation for line, column so that it will fit in the status bar            line_column = _("Ln %i, Col %i") % (it.get_line()+1, it.get_line_offset()+1)            status = "%s : %s" % ( insert_overwrite, line_column )            self.emit("status-changed", status  )            raise StopIteration; yield 0        self.scheduler.add_task( update().next )    def on_textbuffer_mark_set(self, buffer, it, mark):        if mark.get_name() == "insert":            self._update_cursor_status(buffer)    def on_textview_focus_in_event(self, view, event):        self.textview_focussed = view        self._update_cursor_status(view.get_buffer())    def on_switch_event(self):        if self.textview_focussed:            self.scheduler.add_task( self.textview_focussed.grab_focus )    def _after_text_modified(self, buffer, startline, sizechange):        if self.num_panes > 1:            buffers = [t.get_buffer() for t in self.textview[:self.num_panes] ]            pane = buffers.index(buffer)            change_range = self.linediffer.change_sequence( pane, startline, sizechange, self._get_texts())            for it in self._update_highlighting( change_range[0], change_range[1] ):                pass            self.queue_draw()        self._update_cursor_status(buffer)    def _get_texts(self, raw=0):        class FakeText(object):            def __init__(self, buf, textfilter):                self.buf, self.textfilter = buf, textfilter            def __getslice__(self, lo, hi):                b = self.buf                txt = b.get_text(b.get_iter_at_line(lo), b.get_iter_at_line(hi), 0)                txt = self.textfilter(txt)                return txt.split("\n")[:-1]        class FakeTextArray(object):            def __init__(self, bufs, textfilter):                self.texts = [FakeText(b, textfilter) for b in  bufs]            def __getitem__(self, i):                return self.texts[i]        return FakeTextArray( [t.get_buffer() for t in self.textview], [self._filter_text, lambda x:x][raw] )    def _filter_text(self, txt):        def killit(m):            assert m.group().count("\n") == 0            if len(m.groups()):                s = m.group()                for g in m.groups():                    if g:                        s = s.replace(g,"")                return s            else:                return ""        try:            for c,r in self.regexes:                txt = c.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)                self.warned_bad_comparison = True        return txt    def after_text_insert_text(self, buffer, it, newtext, textlen):        lines_added = newtext.count("\n")        starting_at = it.get_line() - lines_added        self._after_text_modified(buffer, starting_at, lines_added)    def after_text_delete_range(self, buffer, it0, it1):        starting_at = it0.get_line()        assert self.deleted_lines_pending != -1        self._after_text_modified(buffer, starting_at, -self.deleted_lines_pending)        self.deleted_lines_pending = -1    def load_font(self):        fontdesc = pango.FontDescription(self.prefs.get_current_font())        context = self.textview0.get_pango_context()        metrics = context.get_metrics( fontdesc, context.get_language() )        self.pixels_per_line = (metrics.get_ascent() + metrics.get_descent()) / 1024        self.pango_char_width = metrics.get_approximate_char_width()        tabs = pango.TabArray(10, 0)        tab_size = self.prefs.tab_size;        for i in range(10):            tabs.set_tab(i, pango.TAB_LEFT, i*tab_size*self.pango_char_width)        for i in range(3):            self.textview[i].modify_font(fontdesc)            self.textview[i].set_tabs(tabs)        for i in range(2):            self.linkmap[i].queue_draw()        load = lambda x: gnomeglade.load_pixbuf( paths.share_dir("glade2/pixmaps/"+x), self.pixels_per_line)        self.pixbuf_apply0 = load("button_apply0.xpm")        self.pixbuf_apply1 = load("button_apply1.xpm")        self.pixbuf_delete = load("button_delete.xpm")        self.pixbuf_copy0  = load("button_copy0.xpm")        self.pixbuf_copy1  = load("button_copy1.xpm")    def on_preference_changed(self, key, value):        if key == "draw_style":            for l in self.linkmap:                l.queue_draw()        elif key == "tab_size":            tabs = pango.TabArray(10, 0)            for i in range(10):                tabs.set_tab(i, pango.TAB_LEFT, i*value*self.pango_char_width)            for i in range(3):                self.textview[i].set_tabs(tabs)        elif key == "use_custom_font" or key == "custom_font":            self.load_font()        elif key == "show_line_numbers":            if sourceview_available:                for t in self.textview:                    t.set_show_line_numbers( value )        elif key == "use_syntax_highlighting":            if sourceview_available:                for i in range(self.num_panes):                    set_highlighting_enabled(                        self.textview[i].get_buffer(),                        self.bufferdata[i].filename,                        self.prefs.use_syntax_highlighting )        elif key == "regexes":            self._update_regexes()        elif key == "edit_wrap_lines":            [t.set_wrap_mode( self.prefs.edit_wrap_lines ) for t in self.textview]    def on_key_press_event(self, object, event):        x = self.keylookup.get(event.keyval, 0)        if self.keymask | x != self.keymask:            self.keymask |= x            for l in self.linkmap[:self.num_panes-1]:                a = l.get_allocation()                w = self.pixbuf_copy0.get_width()                l.queue_draw_area(0,      0, w, a[3])                l.queue_draw_area(a[2]-w, 0, w, a[3])    def on_key_release_event(self, object, event):

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -