IdeBufferChangeMonitor * gbp_git_buffer_change_monitor_new (IdeBuffer *buffer, IpcGitRepository *repository, GFile *file, GCancellable *cancellable, GError **error) { GbpGitBufferChangeMonitor *ret; g_autoptr(IpcGitChangeMonitor) proxy = NULL; g_autoptr(IdeContext) context = NULL; g_autoptr(GFile) workdir = NULL; g_autofree gchar *relative_path = NULL; g_autofree gchar *obj_path = NULL; GDBusConnection *connection; g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL); g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL); g_return_val_if_fail (G_IS_FILE (file), NULL); g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL); context = ide_buffer_ref_context (buffer); workdir = ide_context_ref_workdir (context); if (!g_file_has_prefix (file, workdir)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cannot monitor files outside the working directory"); return NULL; } relative_path = g_file_get_relative_path (workdir, file); if (!ipc_git_repository_call_create_change_monitor_sync (repository, relative_path, &obj_path, cancellable, error)) return NULL; connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (repository)); if (!(proxy = ipc_git_change_monitor_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, obj_path, cancellable, error))) return NULL; ret = g_object_new (GBP_TYPE_GIT_BUFFER_CHANGE_MONITOR, "buffer", buffer, NULL); ret->proxy = g_steal_pointer (&proxy); return IDE_BUFFER_CHANGE_MONITOR (g_steal_pointer (&ret)); }
static void ide_buffer_change_monitor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { IdeBufferChangeMonitor *self = IDE_BUFFER_CHANGE_MONITOR (object); switch (prop_id) { case PROP_BUFFER: ide_buffer_change_monitor_set_buffer (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } }
void gbp_git_buffer_change_monitor_wait_async (GbpGitBufferChangeMonitor *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(IdeTask) task = NULL; IdeBuffer *buffer; guint change_count; g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = ide_task_new (self, cancellable, callback, user_data); ide_task_set_source_tag (task, gbp_git_buffer_change_monitor_wait_async); if (ide_task_return_error_if_cancelled (task)) return; buffer = ide_buffer_change_monitor_get_buffer (IDE_BUFFER_CHANGE_MONITOR (self)); change_count = ide_buffer_get_change_count (buffer); /* Update the peer of buffer contents immediately in-case it does * not yet have teh newest version. */ if (change_count != self->last_change_count) { g_autoptr(GBytes) bytes = ide_buffer_dup_content (buffer); self->last_change_count = change_count; ipc_git_change_monitor_call_update_content (self->proxy, (const gchar *)g_bytes_get_data (bytes, NULL), NULL, NULL, NULL); } ipc_git_change_monitor_call_list_changes (self->proxy, cancellable, gbp_git_buffer_change_monitor_wait_cb, g_steal_pointer (&task)); }
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 gbp_git_buffer_change_monitor_wait_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IpcGitChangeMonitor *proxy = (IpcGitChangeMonitor *)object; g_autoptr(GVariant) changes = NULL; g_autoptr(IdeTask) task = user_data; g_autoptr(GError) error = NULL; GbpGitBufferChangeMonitor *self; g_assert (IDE_IS_MAIN_THREAD ()); g_assert (IPC_IS_GIT_CHANGE_MONITOR (proxy)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (IDE_IS_TASK (task)); self = ide_task_get_source_object (task); if (!ipc_git_change_monitor_call_list_changes_finish (proxy, &changes, result, &error)) { g_clear_pointer (&self->cache, line_cache_free); self->not_found = TRUE; if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND)) ide_task_return_boolean (task, TRUE); else ide_task_return_error (task, g_steal_pointer (&error)); } else { g_clear_pointer (&self->cache, line_cache_free); self->not_found = FALSE; self->cache = line_cache_new_from_variant (changes); ide_buffer_change_monitor_emit_changed (IDE_BUFFER_CHANGE_MONITOR (self)); ide_task_return_boolean (task, 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 void ide_git_buffer_change_monitor__calculate_cb (GObject *object, GAsyncResult *result, gpointer user_data_unused) { IdeGitBufferChangeMonitor *self = (IdeGitBufferChangeMonitor *)object; g_autoptr(GHashTable) ret = NULL; g_autoptr(GError) error = NULL; g_assert (IDE_IS_GIT_BUFFER_CHANGE_MONITOR (self)); self->in_calculation = FALSE; ret = ide_git_buffer_change_monitor_calculate_finish (self, result, &error); if (!ret) { if (!g_error_matches (error, GGIT_ERROR, GGIT_ERROR_NOTFOUND)) g_message ("%s", error->message); } else { g_clear_pointer (&self->state, g_hash_table_unref); self->state = g_hash_table_ref (ret); } ide_buffer_change_monitor_emit_changed (IDE_BUFFER_CHANGE_MONITOR (self)); /* * Recalculate the state if the buffer has changed since we submitted our request. */ if (self->state_dirty) ide_git_buffer_change_monitor_calculate_async (self, NULL, ide_git_buffer_change_monitor__calculate_cb, NULL); }