[gnome-builder/wip/gtk4-port] libide/code: add IdeBuffer commit hooks
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port] libide/code: add IdeBuffer commit hooks
- Date: Mon, 11 Apr 2022 22:31:56 +0000 (UTC)
commit 6945b627b14ccaf047b5699b9d47ff91cebd170d
Author: Christian Hergert <chergert redhat com>
Date: Mon Apr 11 15:31:00 2022 -0700
libide/code: add IdeBuffer commit hooks
This is intended to help us manage operations with text "commit" operations
where the underlying buffer is changed. It doesn't give much information,
but then again, it's intended to be rather fast and correct in the sense
of requiring consumers to not do further modifications from their
function callbacks.
This can be used by spellcheck to update the region b+tree in concert with
the text buffer.
src/libide/code/ide-buffer.c | 149 ++++++++++++++++++++++++++++++++++++++++++-
src/libide/code/ide-buffer.h | 16 +++++
2 files changed, 162 insertions(+), 3 deletions(-)
---
diff --git a/src/libide/code/ide-buffer.c b/src/libide/code/ide-buffer.c
index efceb67a7..6f5d51bef 100644
--- a/src/libide/code/ide-buffer.c
+++ b/src/libide/code/ide-buffer.c
@@ -95,6 +95,9 @@ struct _IdeBuffer
int hold;
guint release_in_idle;
+ GArray *commit_funcs;
+ guint next_commit_handler;
+
/* Bit-fields */
IdeBufferState state : 3;
guint can_restore_cursor : 1;
@@ -105,6 +108,17 @@ struct _IdeBuffer
guint highlight_diagnostics : 1;
};
+typedef struct
+{
+ IdeBufferCommitFunc before_insert_text;
+ IdeBufferCommitFunc after_insert_text;
+ IdeBufferCommitFunc before_delete_range;
+ IdeBufferCommitFunc after_delete_range;
+ gpointer user_data;
+ GDestroyNotify user_data_destroy;
+ guint handler_id;
+} CommitHooks;
+
typedef struct
{
IdeNotification *notif;
@@ -272,6 +286,15 @@ lookup_symbol_data_free (LookUpSymbolData *data)
g_slice_free (LookUpSymbolData, data);
}
+static void
+clear_commit_func (gpointer data)
+{
+ CommitHooks *hooks = data;
+
+ if (hooks->user_data_destroy)
+ hooks->user_data_destroy (hooks->user_data);
+}
+
IdeBuffer *
_ide_buffer_new (IdeBufferManager *buffer_manager,
GFile *file,
@@ -505,6 +528,7 @@ ide_buffer_dispose (GObject *object)
g_clear_pointer (&self->content, g_bytes_unref);
g_clear_object (&self->diagnostics);
ide_clear_and_destroy_object (&self->file_settings);
+ g_clear_pointer (&self->commit_funcs, g_array_unref);
G_OBJECT_CLASS (ide_buffer_parent_class)->dispose (object);
}
@@ -1030,13 +1054,16 @@ ide_buffer_class_init (IdeBufferClass *klass)
static void
ide_buffer_init (IdeBuffer *self)
{
+ g_assert (IDE_IS_MAIN_THREAD ());
+
self->in_flight_symbol_at_location_pos = -1;
self->source_file = gtk_source_file_new ();
self->can_restore_cursor = TRUE;
self->highlight_diagnostics = TRUE;
self->enable_addins = TRUE;
- g_assert (IDE_IS_MAIN_THREAD ());
+ self->commit_funcs = g_array_new (FALSE, FALSE, sizeof (CommitHooks));
+ g_array_set_clear_func (self->commit_funcs, clear_commit_func);
g_signal_connect (self,
"notify::language",
@@ -1991,10 +2018,14 @@ ide_buffer_delete_range (GtkTextBuffer *buffer,
GtkTextIter *begin,
GtkTextIter *end)
{
+ IdeBuffer *self = (IdeBuffer *)buffer;
+ guint position;
+ guint length;
+
IDE_ENTRY;
g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (IDE_IS_BUFFER (self));
g_assert (begin != NULL);
g_assert (end != NULL);
@@ -2014,8 +2045,27 @@ ide_buffer_delete_range (GtkTextBuffer *buffer,
}
#endif
+ position = gtk_text_iter_get_offset (begin);
+ length = gtk_text_iter_get_offset (end) - position;
+
+ for (guint i = 0; i < self->commit_funcs->len; i++)
+ {
+ const CommitHooks *hooks = &g_array_index (self->commit_funcs, CommitHooks, i);
+
+ if (hooks->before_delete_range != NULL)
+ hooks->before_delete_range (self, position, length, hooks->user_data);
+ }
+
GTK_TEXT_BUFFER_CLASS (ide_buffer_parent_class)->delete_range (buffer, begin, end);
+ for (guint i = 0; i < self->commit_funcs->len; i++)
+ {
+ const CommitHooks *hooks = &g_array_index (self->commit_funcs, CommitHooks, i);
+
+ if (hooks->after_delete_range != NULL)
+ hooks->after_delete_range (self, position, length, hooks->user_data);
+ }
+
IDE_EXIT;
}
@@ -2025,12 +2075,15 @@ ide_buffer_insert_text (GtkTextBuffer *buffer,
const gchar *text,
gint len)
{
+ IdeBuffer *self = (IdeBuffer *)buffer;
gboolean recheck_language = FALSE;
+ guint position;
+ guint length;
IDE_ENTRY;
g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (IDE_IS_BUFFER (self));
g_assert (location != NULL);
g_assert (text != NULL);
@@ -2044,8 +2097,27 @@ ide_buffer_insert_text (GtkTextBuffer *buffer,
((text [0] == '\n') || ((len > 1) && (strchr (text, '\n') != NULL))))
recheck_language = TRUE;
+ position = gtk_text_iter_get_offset (location);
+ length = g_utf8_strlen (text, len);
+
+ for (guint i = 0; i < self->commit_funcs->len; i++)
+ {
+ const CommitHooks *hooks = &g_array_index (self->commit_funcs, CommitHooks, i);
+
+ if (hooks->before_insert_text != NULL)
+ hooks->before_insert_text (self, position, length, hooks->user_data);
+ }
+
GTK_TEXT_BUFFER_CLASS (ide_buffer_parent_class)->insert_text (buffer, location, text, len);
+ for (guint i = 0; i < self->commit_funcs->len; i++)
+ {
+ const CommitHooks *hooks = &g_array_index (self->commit_funcs, CommitHooks, i);
+
+ if (hooks->after_insert_text != NULL)
+ hooks->after_insert_text (self, position, length, hooks->user_data);
+ }
+
if G_UNLIKELY (recheck_language)
ide_buffer_guess_language (IDE_BUFFER (buffer));
@@ -4141,3 +4213,74 @@ _ide_buffer_is_file (IdeBuffer *self,
return g_file_equal (nolink_file, ide_buffer_get_file (self)) ||
g_file_equal (nolink_file, self->readlink_file);
}
+
+/**
+ * ide_buffer_add_commit_funcs:
+ * @self: a #IdeBuffer
+ * @before_insert_text: (nullable) (scope async): function for before inserting text
+ * @after_insert_text: (nullable) (scope async): function for after inserting text
+ * @before_delete_range: (nullable) (scope async): function for before deleting a range
+ * @after_delete_range: (nullable) (scope async): function for after deleting a range
+ * @user_data: closure data
+ * @user_data_destroy: destroy notify for @user_data
+ *
+ * Adds function callbacks to handle important changes to text
+ * internally within the GtkTextBuffer. You can use these instead
+ * of signals like #GtkTextBuffer::insert-text or
+ * #GtkTextBuffer::delete-range when you want to be sure you're
+ * getting unprocessed changes right before they are commited to
+ * underlying GTK data structures.
+ *
+ * However, this has the requirement that you do not change this
+ * content in any way, only access the information that these events
+ * occurred.
+ *
+ * Returns: a handler-id which can be used with
+ * ide_buffer_remove_commit_funcs().
+ */
+guint
+ide_buffer_add_commit_funcs (IdeBuffer *self,
+ IdeBufferCommitFunc before_insert_text,
+ IdeBufferCommitFunc after_insert_text,
+ IdeBufferCommitFunc before_delete_range,
+ IdeBufferCommitFunc after_delete_range,
+ gpointer user_data,
+ GDestroyNotify user_data_destroy)
+{
+ CommitHooks hooks;
+
+ g_return_val_if_fail (IDE_IS_BUFFER (self), 0);
+
+ hooks.before_insert_text = before_insert_text;
+ hooks.after_insert_text = after_insert_text;
+ hooks.after_delete_range = after_delete_range;
+ hooks.before_delete_range = before_delete_range;
+ hooks.user_data = user_data;
+ hooks.user_data_destroy = user_data_destroy;
+ hooks.handler_id = ++self->next_commit_handler;
+
+ g_array_append_val (self->commit_funcs, hooks);
+
+ return hooks.handler_id;
+}
+
+void
+ide_buffer_remove_commit_funcs (IdeBuffer *self,
+ guint commit_funcs_handler)
+{
+ g_return_if_fail (IDE_IS_BUFFER (self));
+ g_return_if_fail (commit_funcs_handler > 0);
+
+ for (guint i = 0; i < self->commit_funcs->len; i++)
+ {
+ const CommitHooks *hooks = &g_array_index (self->commit_funcs, CommitHooks, i);
+
+ if (hooks->handler_id == commit_funcs_handler)
+ {
+ g_array_remove_index (self->commit_funcs, i);
+ return;
+ }
+ }
+
+ g_warning ("Failed to locate commit handler %u", commit_funcs_handler);
+}
diff --git a/src/libide/code/ide-buffer.h b/src/libide/code/ide-buffer.h
index 566009d7a..0510f067a 100644
--- a/src/libide/code/ide-buffer.h
+++ b/src/libide/code/ide-buffer.h
@@ -48,6 +48,11 @@ typedef enum
IDE_BUFFER_STATE_FAILED,
} IdeBufferState;
+typedef void (*IdeBufferCommitFunc) (IdeBuffer *buffer,
+ guint position,
+ guint length,
+ gpointer user_data);
+
IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeBuffer, ide_buffer, IDE, BUFFER, GtkSourceBuffer)
@@ -182,5 +187,16 @@ void ide_buffer_set_highlight_diagnostics (IdeBuffer
IDE_AVAILABLE_IN_ALL
void ide_buffer_set_style_scheme_name (IdeBuffer *self,
const gchar
*style_scheme_name);
+IDE_AVAILABLE_IN_ALL
+guint ide_buffer_add_commit_funcs (IdeBuffer *self,
+ IdeBufferCommitFunc
before_insert_text,
+ IdeBufferCommitFunc after_insert_text,
+ IdeBufferCommitFunc
before_delete_range,
+ IdeBufferCommitFunc
after_delete_range,
+ gpointer user_data,
+ GDestroyNotify
user_data_destroy);
+IDE_AVAILABLE_IN_ALL
+void ide_buffer_remove_commit_funcs (IdeBuffer *self,
+ guint
commit_funcs_handler);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]