[gedit-plugins] Bug 702336 - Show the file's git status in the file browser
- From: Garrett Regier <gregier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit-plugins] Bug 702336 - Show the file's git status in the file browser
- Date: Mon, 1 Jul 2013 15:41:42 +0000 (UTC)
commit 9a414947265750c2e2245048d5e432381020e3f1
Author: Garrett Regier <garrettregier gmail com>
Date: Mon Jul 1 06:43:42 2013 -0700
Bug 702336 - Show the file's git status in the file browser
plugins/git/git/Makefile.am | 3 +-
plugins/git/git/__init__.py | 3 +-
plugins/git/git/viewactivatable.py | 19 ++-
plugins/git/git/windowactivatable.py | 237 ++++++++++++++++++++++++++++++++++
4 files changed, 255 insertions(+), 7 deletions(-)
---
diff --git a/plugins/git/git/Makefile.am b/plugins/git/git/Makefile.am
index c3ab9af..da7fe1e 100644
--- a/plugins/git/git/Makefile.am
+++ b/plugins/git/git/Makefile.am
@@ -5,6 +5,7 @@ plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/git
plugin_PYTHON = \
diffrenderer.py \
__init__.py \
- viewactivatable.py
+ viewactivatable.py \
+ windowactivatable.py
-include $(top_srcdir)/git.mk
diff --git a/plugins/git/git/__init__.py b/plugins/git/git/__init__.py
index 9ee99c5..bcb2787 100644
--- a/plugins/git/git/__init__.py
+++ b/plugins/git/git/__init__.py
@@ -17,6 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307, USA.
-from .viewactivatable import GitPlugin
+from .viewactivatable import GitViewActivatable
+from .windowactivatable import GitWindowActivatable
# ex:ts=4:et:
diff --git a/plugins/git/git/viewactivatable.py b/plugins/git/git/viewactivatable.py
index 84600f6..2426914 100644
--- a/plugins/git/git/viewactivatable.py
+++ b/plugins/git/git/viewactivatable.py
@@ -19,6 +19,7 @@
from gi.repository import GLib, GObject, Gtk, Gedit, Ggit
from .diffrenderer import DiffType, DiffRenderer
+from .windowactivatable import GitWindowActivatable
import difflib
@@ -31,19 +32,22 @@ class LineContext:
self.line_type = DiffType.NONE
-class GitPlugin(GObject.Object, Gedit.ViewActivatable):
+class GitViewActivatable(GObject.Object, Gedit.ViewActivatable):
view = GObject.property(type=Gedit.View)
- def __init__(self):
- GObject.Object.__init__(self)
+ status = GObject.property(type=Ggit.StatusFlags,
+ default=Ggit.StatusFlags.CURRENT)
- Ggit.init()
+ def __init__(self):
+ super().__init__()
self.diff_timeout = 0
self.file_contents_list = None
self.file_context = None
def do_activate(self):
+ GitWindowActivatable.register_view_activatable(self)
+
self.diff_renderer = DiffRenderer()
self.gutter = self.view.get_gutter(Gtk.TextWindowType.LEFT)
@@ -162,6 +166,8 @@ class GitPlugin(GObject.Object, Gedit.ViewActivatable):
# Must be a new file
if not self.file_contents_list:
+ self.status = Ggit.StatusFlags.WORKING_TREE_NEW
+
n_lines = self.buffer.get_line_count()
if len(self.diff_renderer.file_context) == n_lines:
return False
@@ -192,7 +198,10 @@ class GitPlugin(GObject.Object, Gedit.ViewActivatable):
except StopIteration:
# Nothing has changed
- pass
+ self.status = Ggit.StatusFlags.CURRENT
+
+ else:
+ self.status = Ggit.StatusFlags.WORKING_TREE_MODIFIED
file_context = {}
for line_data in diff:
diff --git a/plugins/git/git/windowactivatable.py b/plugins/git/git/windowactivatable.py
new file mode 100644
index 0000000..6d69f35
--- /dev/null
+++ b/plugins/git/git/windowactivatable.py
@@ -0,0 +1,237 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2013 - Garrett Regier
+#
+# 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, USA.
+
+from gi.repository import GLib, GObject, Gio, Gtk, Gedit, Ggit
+
+import weakref
+
+
+class GitWindowActivatable(GObject.Object, Gedit.WindowActivatable):
+ window = GObject.property(type=Gedit.Window)
+
+ windows = weakref.WeakValueDictionary()
+
+ def __init__(self):
+ super().__init__()
+
+ Ggit.init()
+
+ self.view_activatables = weakref.WeakSet()
+
+ self.files = {}
+ self.monitors = {}
+
+ self.repo = None
+ self.tree = None
+
+ @classmethod
+ def register_view_activatable(cls, view_activatable):
+ window = view_activatable.view.get_toplevel()
+
+ if window not in cls.windows:
+ return None
+
+ window_activatable = cls.windows[window]
+
+ window_activatable.view_activatables.add(view_activatable)
+ view_activatable.connect('notify::status',
+ window_activatable.notify_status)
+
+ return window_activatable
+
+ def do_activate(self):
+ # self.window is not set until now
+ self.windows[self.window] = self
+
+ self.bus = self.window.get_message_bus()
+
+ self.files = {}
+ self.file_names = {}
+ self.monitors = {}
+
+ self.window_signals = [
+ self.window.connect('tab-removed', self.tab_removed),
+ self.window.connect('focus-in-event', self.focus_in_event)
+ ]
+
+ self.bus_signals = [
+ self.bus.connect('/plugins/filebrowser', 'root_changed',
+ self.root_changed, None),
+ self.bus.connect('/plugins/filebrowser', 'inserted',
+ self.inserted, None),
+ self.bus.connect('/plugins/filebrowser', 'deleted',
+ self.deleted, None)
+ ]
+
+ self.refresh();
+
+ def do_deactivate(self):
+ self.clear_monitors()
+
+ for sid in self.window_signals:
+ self.window.disconnect(sid)
+
+ for sid in self.bus_signals:
+ self.bus.disconnect(sid)
+
+ self.files = {}
+ self.file_names = {}
+ self.repo = None
+ self.tree = None
+ self.window_signals = []
+ self.bus_signals = []
+
+ self.refresh();
+
+ def refresh(self):
+ if self.bus.is_registered('/plugins/filebrowser', 'refresh'):
+ self.bus.send('/plugins/filebrowser', 'refresh')
+
+ def notify_status(self, view_activatable, psepc):
+ location = view_activatable.view.get_buffer().get_location()
+
+ if location is not None:
+ self.update_location(location)
+
+ def tab_removed(self, window, tab):
+ view = tab.get_view()
+ location = view.get_buffer().get_location()
+
+ if location is None:
+ return
+
+ # Need to remove the view activatable otherwise update_location()
+ # will might use the view's status and not the file's actual status
+ for view_activatable in self.view_activatables:
+ if view_activatable.view == view:
+ self.view_activatables.remove(view_activatable)
+ break
+
+ self.update_location(location)
+
+ def focus_in_event(self, window, event):
+ for view_activatable in self.view_activatables:
+ view_activatable.update()
+
+ for uri in self.files:
+ self.update_location(Gio.File.new_for_uri(uri))
+
+ def root_changed(self, bus, msg, data=None):
+ self.clear_monitors()
+
+ try:
+ repo_file = Ggit.Repository.discover(msg.location)
+ self.repo = Ggit.Repository.open(repo_file)
+ head = self.repo.get_head()
+ commit = self.repo.lookup(head.get_target(), Ggit.Commit.__gtype__)
+ self.tree = commit.get_tree()
+
+ except Exception as e:
+ self.repo = None
+ self.tree = None
+
+ else:
+ self.monitor_directory(msg.location)
+
+ def inserted(self, bus, msg, data=None):
+ self.files[msg.location.get_uri()] = msg.id
+ self.file_names[msg.location.get_uri()] = msg.name
+
+ self.update_location(msg.location)
+
+ if msg.is_directory:
+ self.monitor_directory(msg.location)
+
+ def deleted(self, bus, msg, data=None):
+ # File browser's deleted signal is broken
+ return
+
+ uri = msg.location.get_uri()
+
+ del self.files[uri]
+ del self.file_names[uri]
+
+ if uri in self.monitors:
+ self.monitors[uri].cancel()
+ del self.monitors[uri]
+
+ def update_location(self, location):
+ if self.repo is None:
+ return
+
+ if location.get_uri() not in self.files:
+ return
+
+ status = None
+
+ # First get the status from the open documents
+ for view_activatable in self.view_activatables:
+ doc_location = view_activatable.view.get_buffer().get_location()
+
+ if doc_location is not None and doc_location.equal(location):
+ status = view_activatable.status
+ break
+
+ try:
+ git_status = self.repo.file_status(location)
+
+ except Exception:
+ pass
+
+ else:
+ # Don't use the view activatable's
+ # status if the file is ignored
+ if status is None or git_status & Ggit.StatusFlags.IGNORED:
+ status = git_status
+
+ markup = GLib.markup_escape_text(self.file_names[location.get_uri()])
+
+ if status is not None:
+ if status & Ggit.StatusFlags.INDEX_NEW or \
+ status & Ggit.StatusFlags.WORKING_TREE_NEW:
+ markup = '<span foreground="green">%s</span> [New]' % (markup)
+
+ elif status & Ggit.StatusFlags.INDEX_MODIFIED or \
+ status & Ggit.StatusFlags.WORKING_TREE_MODIFIED:
+ markup = '<span foreground="blue">%s</span> [Modified]' % (markup)
+
+ self.bus.send('/plugins/filebrowser', 'set_markup',
+ id=self.files[location.get_uri()], markup=markup)
+
+ def clear_monitors(self):
+ for uri in self.monitors:
+ self.monitors[uri].cancel()
+
+ self.monitors = {}
+
+ def monitor_directory(self, location):
+ monitor = location.monitor(Gio.FileMonitorFlags.NONE, None)
+ self.monitors[location.get_uri()] = monitor
+
+ monitor.connect('changed', self.monitor_changed)
+
+ def monitor_changed(self, monitor, file, other_file, event_type):
+ # Only monitor for changes as the file browser will monitor
+ # file created and deleted files and emit inserted and deleted
+ if event_type == Gio.FileMonitorEvent.CHANGED:
+ for f in (file, other_file):
+ if f in self.files:
+ self.update_location(f)
+
+# ex:ts=4:et:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]