[gnome-builder] libide: be smarter about avoiding diff calculation
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide: be smarter about avoiding diff calculation
- Date: Mon, 23 Mar 2015 23:45:15 +0000 (UTC)
commit 85e76244dcdf4f7da31a47ca2fbcd8b3eb1a8e49
Author: Christian Hergert <christian hergert me>
Date: Thu Feb 26 17:44:37 2015 -0800
libide: be smarter about avoiding diff calculation
Try to get the things we know need to be calculated immediately served
immediately. Try to catch the hard stuff (like manually changing a line
back to its original contents) in a followup handler a second after the
most recent edit.
The longer we keep typing, the longer we try to delay the followup pass.
This is because it is pretty likely the user will perform either a newline
insertion or deletion (requiring an immediate diff) within the not too
distant future.
This significantly reduces the number of diffs calculated for me. Should
result in some power savings.
libide/git/ide-git-buffer-change-monitor.c | 173 +++++++++++++++++++++++++---
1 files changed, 158 insertions(+), 15 deletions(-)
---
diff --git a/libide/git/ide-git-buffer-change-monitor.c b/libide/git/ide-git-buffer-change-monitor.c
index c2381b5..d2baee8 100644
--- a/libide/git/ide-git-buffer-change-monitor.c
+++ b/libide/git/ide-git-buffer-change-monitor.c
@@ -49,8 +49,11 @@ struct _IdeGitBufferChangeMonitor
GgitBlob *cached_blob;
+ guint changed_timeout;
+
guint state_dirty : 1;
guint in_calculation : 1;
+ guint delete_range_requires_recalculation : 1;
};
typedef struct
@@ -225,27 +228,15 @@ ide_git_buffer_change_monitor__calculate_cb (GObject *object,
}
static void
-ide_git_buffer_change_monitor__buffer_changed_cb (IdeGitBufferChangeMonitor *self,
- IdeBuffer *buffer)
+ide_git_buffer_change_monitor_recalculate (IdeGitBufferChangeMonitor *self)
{
g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
- g_assert (IDE_IS_BUFFER (buffer));
self->state_dirty = TRUE;
- /* We recalculate state upon completion of current request */
if (self->in_calculation)
return;
- /*
- * TODO:
- *
- * we shouldn't do this here:
- *
- * instead we should hook to insert/delete-range signals and determine if there was a multiline
- * change. also, if the line has not changed, we need to recalculate. but we can avoid the
- * check if the line has already changed and no \n were added/removed.
- */
ide_git_buffer_change_monitor_calculate_async (self,
NULL,
ide_git_buffer_change_monitor__calculate_cb,
@@ -253,6 +244,134 @@ ide_git_buffer_change_monitor__buffer_changed_cb (IdeGitBufferChangeMonitor *sel
}
static void
+ide_git_buffer_change_monitor__buffer_delete_range_after_cb (IdeGitBufferChangeMonitor *self,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ IdeBuffer *buffer)
+{
+ g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+ g_assert (begin);
+ g_assert (end);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (self->delete_range_requires_recalculation)
+ {
+ self->delete_range_requires_recalculation = FALSE;
+ ide_git_buffer_change_monitor_recalculate (self);
+ }
+}
+
+static void
+ide_git_buffer_change_monitor__buffer_delete_range_cb (IdeGitBufferChangeMonitor *self,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ IdeBuffer *buffer)
+{
+ IdeBufferLineChange change;
+
+ g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+ g_assert (begin);
+ g_assert (end);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ /*
+ * We need to recalculate the diff when text is deleted if:
+ *
+ * 1) The range includes a newline.
+ * 2) The current line change is set to NONE.
+ *
+ * Technically we need to do it on every change to be more correct, but that wastes a lot of
+ * power. So instead, we'll be a bit lazy about it here and pick up the other changes on a much
+ * more conservative timeout, generated by ide_git_buffer_change_monitor__buffer_changed_cb().
+ */
+
+ if (gtk_text_iter_get_line (begin) != gtk_text_iter_get_line (end))
+ goto recalculate;
+
+ change = ide_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self), begin);
+ if (change == IDE_BUFFER_LINE_CHANGE_NONE)
+ goto recalculate;
+
+ return;
+
+recalculate:
+ /*
+ * We need to wait for the delete to occur, so mark it as necessary and let
+ * ide_git_buffer_change_monitor__buffer_delete_range_after_cb perform the operation.
+ */
+ self->delete_range_requires_recalculation = TRUE;
+}
+
+static void
+ide_git_buffer_change_monitor__buffer_insert_text_after_cb (IdeGitBufferChangeMonitor *self,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ IdeBuffer *buffer)
+{
+ IdeBufferLineChange change;
+
+ g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self));
+ g_assert (location);
+ g_assert (text);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ /*
+ * We need to recalculate the diff when text is inserted if:
+ *
+ * 1) A newline is included in the text.
+ * 2) The line currently has flags of NONE.
+ *
+ * Technically we need to do it on every change to be more correct, but that wastes a lot of
+ * power. So instead, we'll be a bit lazy about it here and pick up the other changes on a much
+ * more conservative timeout, generated by ide_git_buffer_change_monitor__buffer_changed_cb().
+ */
+
+ if (strchr (text, '\n') != NULL)
+ goto recalculate;
+
+ change = ide_git_buffer_change_monitor_get_change (IDE_BUFFER_CHANGE_MONITOR (self), location);
+ if (change == IDE_BUFFER_LINE_CHANGE_NONE)
+ goto recalculate;
+
+ return;
+
+recalculate:
+ ide_git_buffer_change_monitor_recalculate (self);
+}
+
+static gboolean
+ide_git_buffer_change_monitor__changed_timeout_cb (gpointer user_data)
+{
+ IdeGitBufferChangeMonitor *self = user_data;
+
+ ide_git_buffer_change_monitor_recalculate (self);
+ self->changed_timeout = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ide_git_buffer_change_monitor__buffer_changed_after_cb (IdeGitBufferChangeMonitor *self,
+ IdeBuffer *buffer)
+{
+ g_assert (IDE_IS_BUFFER_CHANGE_MONITOR (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ self->state_dirty = TRUE;
+
+ if (self->in_calculation)
+ return;
+
+ if (self->changed_timeout)
+ g_source_remove (self->changed_timeout);
+
+ self->changed_timeout = g_timeout_add_seconds (1,
+ ide_git_buffer_change_monitor__changed_timeout_cb,
+ self);
+}
+
+static void
ide_git_buffer_change_monitor_set_buffer (IdeBufferChangeMonitor *monitor,
IdeBuffer *buffer)
{
@@ -265,10 +384,28 @@ ide_git_buffer_change_monitor_set_buffer (IdeBufferChangeMonitor *monitor,
self->buffer = g_object_ref (buffer);
g_signal_connect_object (self->buffer,
- "changed",
- G_CALLBACK (ide_git_buffer_change_monitor__buffer_changed_cb),
+ "insert-text",
+ G_CALLBACK (ide_git_buffer_change_monitor__buffer_insert_text_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ g_signal_connect_object (self->buffer,
+ "delete-range",
+ G_CALLBACK (ide_git_buffer_change_monitor__buffer_delete_range_cb),
self,
G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->buffer,
+ "delete-range",
+ G_CALLBACK (ide_git_buffer_change_monitor__buffer_delete_range_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ g_signal_connect_object (self->buffer,
+ "changed",
+ G_CALLBACK (ide_git_buffer_change_monitor__buffer_changed_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
}
static gint
@@ -484,6 +621,12 @@ ide_git_buffer_change_monitor_dispose (GObject *object)
{
IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)object;
+ if (self->changed_timeout)
+ {
+ g_source_remove (self->changed_timeout);
+ self->changed_timeout = 0;
+ }
+
g_clear_object (&self->cached_blob);
g_clear_object (&self->buffer);
g_clear_object (&self->repository);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]