[gtksourceview/wip/chergert/vim: 196/293] implement history without commands




commit e7a916871fc0739124c20184d306de032d1f46c3
Author: Christian Hergert <chergert redhat com>
Date:   Mon Nov 1 17:06:18 2021 -0700

    implement history without commands
    
    we actually want truncatd history as if you move around while in insert
    mode we should be truncating what gets replayed.

 gtksourceview/vim/gtk-source-vim-insert.c       |  46 ++----
 gtksourceview/vim/gtk-source-vim-text-history.c | 205 +++++++++++++++++++++---
 gtksourceview/vim/gtk-source-vim-text-history.h |   4 +-
 3 files changed, 197 insertions(+), 58 deletions(-)
---
diff --git a/gtksourceview/vim/gtk-source-vim-insert.c b/gtksourceview/vim/gtk-source-vim-insert.c
index 38506a64..1a52dc6a 100644
--- a/gtksourceview/vim/gtk-source-vim-insert.c
+++ b/gtksourceview/vim/gtk-source-vim-insert.c
@@ -95,33 +95,9 @@ static gboolean
 gtk_source_vim_insert_capture (GtkSourceVimState *state,
                                GdkEvent          *event)
 {
-       GtkSourceVimInsert *self = (GtkSourceVimInsert *)state;
-       GtkSourceVimState *child;
-       GdkModifierType mods;
-       guint keyval;
-
-       g_assert (GTK_SOURCE_IS_VIM_INSERT (self));
+       g_assert (GTK_SOURCE_IS_VIM_INSERT (state));
        g_assert (event != NULL);
 
-       if (self->finished)
-       {
-               return FALSE;
-       }
-
-       child = gtk_source_vim_state_get_child (state);
-       keyval = gdk_key_event_get_keyval (event);
-       mods = gdk_event_get_modifier_state (event)
-            & gtk_accelerator_get_default_mod_mask ();
-
-       if (child != NULL)
-       {
-               gtk_source_vim_text_history_record (self->history, event);
-       }
-       else if (!gtk_source_vim_state_is_escape (keyval, mods))
-       {
-               gtk_source_vim_text_history_record (self->history, event);
-       }
-
        return FALSE;
 }
 
@@ -315,32 +291,34 @@ gtk_source_vim_insert_enter (GtkSourceVimState *state)
        gtk_source_vim_state_set_overwrite (state, FALSE);
 
        g_clear_object (&self->history);
-       self->history = g_object_new (GTK_SOURCE_TYPE_VIM_TEXT_HISTORY, NULL);
+       self->history = g_object_new (GTK_SOURCE_TYPE_VIM_TEXT_HISTORY,
+                                     "parent", self,
+                                     NULL);
 
        gtk_source_vim_insert_prepare (self);
+
+       gtk_source_vim_text_history_begin (self->history);
 }
 
 static void
 gtk_source_vim_insert_leave (GtkSourceVimState *state)
 {
        GtkSourceVimInsert *self = (GtkSourceVimInsert *)state;
-       GtkSourceVimState *history;
        int count;
 
        g_assert (GTK_SOURCE_IS_VIM_INSERT (self));
 
        self->finished = TRUE;
 
+       gtk_source_vim_text_history_end (self->history);
+
        count = gtk_source_vim_state_get_count (state);
-       history = GTK_SOURCE_VIM_STATE (self->history);
 
-       gtk_source_vim_state_push (state, g_object_ref (history));
        while (--count > 0)
        {
                gtk_source_vim_insert_prepare (self);
                gtk_source_vim_text_history_replay (self->history);
        }
-       gtk_source_vim_state_pop (history);
 
        gtk_source_vim_state_end_user_action (state);
 }
@@ -349,21 +327,21 @@ static void
 gtk_source_vim_insert_repeat (GtkSourceVimState *state)
 {
        GtkSourceVimInsert *self = (GtkSourceVimInsert *)state;
-       GtkSourceVimState *history;
        int count;
 
        g_assert (GTK_SOURCE_IS_VIM_INSERT (self));
 
        count = gtk_source_vim_state_get_count (state);
-       history = GTK_SOURCE_VIM_STATE (self->history);
 
-       gtk_source_vim_state_push (state, g_object_ref (history));
+       gtk_source_vim_state_begin_user_action (state);
+
        for (int i = 0; i < count; i++)
        {
                gtk_source_vim_insert_prepare (self);
                gtk_source_vim_text_history_replay (self->history);
        }
-       gtk_source_vim_state_pop (history);
+
+       gtk_source_vim_state_end_user_action (state);
 }
 
 static void
diff --git a/gtksourceview/vim/gtk-source-vim-text-history.c b/gtksourceview/vim/gtk-source-vim-text-history.c
index fa0f1738..ece86630 100644
--- a/gtksourceview/vim/gtk-source-vim-text-history.c
+++ b/gtksourceview/vim/gtk-source-vim-text-history.c
@@ -21,12 +21,30 @@
 
 #include "config.h"
 
+#include "gtksourcebuffer.h"
+
 #include "gtk-source-vim-text-history.h"
 
+typedef enum
+{
+       OP_INSERT,
+       OP_DELETE,
+       OP_BACKSPACE,
+} OpKind;
+
+typedef struct
+{
+       OpKind kind : 2;
+       guint length : 30;
+       guint offset;
+} Op;
+
 struct _GtkSourceVimTextHistory
 {
        GObject      parent_instance;
-       GPtrArray   *events;
+       GArray      *ops;
+       GString     *bytes;
+       int          cursor_position;
 };
 
 G_DEFINE_TYPE (GtkSourceVimTextHistory, gtk_source_vim_text_history, GTK_SOURCE_TYPE_VIM_STATE)
@@ -37,12 +55,110 @@ gtk_source_vim_text_history_new (void)
        return g_object_new (GTK_SOURCE_TYPE_VIM_TEXT_HISTORY, NULL);
 }
 
+static void
+gtk_source_vim_text_history_truncate (GtkSourceVimTextHistory *self)
+{
+       g_assert (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
+
+       g_string_truncate (self->bytes, 0);
+
+       if (self->ops->len > 0)
+       {
+               g_array_remove_range (self->ops, 0, self->ops->len);
+       }
+}
+
+static void
+gtk_source_vim_text_history_insert_text_cb (GtkSourceVimTextHistory *self,
+                                            const GtkTextIter       *iter,
+                                            const char              *text,
+                                            int                      len,
+                                            GtkSourceBuffer         *buffer)
+{
+       guint position;
+       Op op;
+
+       g_assert (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
+       g_assert (GTK_SOURCE_IS_BUFFER (buffer));
+       g_assert (iter != NULL);
+       g_assert (gtk_text_iter_get_buffer (iter) == GTK_TEXT_BUFFER (buffer));
+       g_assert (text != NULL);
+
+       if (len == 0)
+               return;
+
+       position = gtk_text_iter_get_offset (iter);
+
+       if ((int)position != self->cursor_position)
+       {
+               gtk_source_vim_text_history_truncate (self);
+       }
+
+       op.kind = OP_INSERT;
+       op.length = g_utf8_strlen (text, len);
+       op.offset = self->bytes->len;
+
+       g_string_append_len (self->bytes, text, len);
+       g_array_append_val (self->ops, op);
+
+       self->cursor_position = position + op.length;
+}
+
+static void
+gtk_source_vim_text_history_delete_range_cb (GtkSourceVimTextHistory *self,
+                                             const GtkTextIter       *begin,
+                                             const GtkTextIter       *end,
+                                             GtkSourceBuffer         *buffer)
+{
+       GtkTextIter a, b;
+       Op op;
+
+       g_assert (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
+       g_assert (GTK_SOURCE_IS_BUFFER (buffer));
+       g_assert (begin != NULL);
+       g_assert (end != NULL);
+       g_assert (gtk_text_iter_get_buffer (begin) == gtk_text_iter_get_buffer (end));
+
+       if (gtk_text_iter_get_offset (begin) == gtk_text_iter_get_offset (end))
+               return;
+
+       a = *begin;
+       b = *end;
+       gtk_text_iter_order (&a, &b);
+
+       op.length = (int)gtk_text_iter_get_offset (&b) - (int)gtk_text_iter_get_offset (&a);
+       op.offset = 0;
+
+       if (gtk_text_iter_get_offset (&a) == self->cursor_position)
+       {
+               op.kind = OP_DELETE;
+       }
+       else if (gtk_text_iter_get_offset (&b) == self->cursor_position)
+       {
+               op.kind = OP_BACKSPACE;
+       }
+       else
+       {
+               gtk_source_vim_text_history_truncate (self);
+       }
+
+       self->cursor_position = gtk_text_iter_get_offset (&a);
+
+       g_array_append_val (self->ops, op);
+}
+
 static void
 gtk_source_vim_text_history_dispose (GObject *object)
 {
        GtkSourceVimTextHistory *self = (GtkSourceVimTextHistory *)object;
 
-       g_clear_pointer (&self->events, g_ptr_array_unref);
+       g_clear_pointer (&self->ops, g_array_unref);
+
+       if (self->bytes)
+       {
+               g_string_free (self->bytes, TRUE);
+               self->bytes = NULL;
+       }
 
        G_OBJECT_CLASS (gtk_source_vim_text_history_parent_class)->dispose (object);
 }
@@ -58,43 +174,88 @@ gtk_source_vim_text_history_class_init (GtkSourceVimTextHistoryClass *klass)
 static void
 gtk_source_vim_text_history_init (GtkSourceVimTextHistory *self)
 {
-       self->events = g_ptr_array_new_with_free_func ((GDestroyNotify)gdk_event_unref);
+       self->bytes = g_string_new (NULL);
+       self->ops = g_array_new (FALSE, FALSE, sizeof (Op));
 }
 
 void
-gtk_source_vim_text_history_record (GtkSourceVimTextHistory *self,
-                                    GdkEvent                *event)
+gtk_source_vim_text_history_begin (GtkSourceVimTextHistory *self)
 {
+       GtkSourceBuffer *buffer;
+
        g_return_if_fail (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
-       g_return_if_fail (event != NULL);
 
-       g_ptr_array_add (self->events, gdk_event_ref (event));
+       buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), NULL, NULL);
+
+       g_signal_connect_object (buffer,
+                                "insert-text",
+                                G_CALLBACK (gtk_source_vim_text_history_insert_text_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+
+       g_signal_connect_object (buffer,
+                                "delete-range",
+                                G_CALLBACK (gtk_source_vim_text_history_delete_range_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+}
+
+void
+gtk_source_vim_text_history_end (GtkSourceVimTextHistory *self)
+{
+       GtkSourceBuffer *buffer;
+
+       g_return_if_fail (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
+
+       buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), NULL, NULL);
+
+       g_signal_handlers_disconnect_by_func (buffer,
+                                             G_CALLBACK (gtk_source_vim_text_history_insert_text_cb),
+                                             self);
+       g_signal_handlers_disconnect_by_func (buffer,
+                                             G_CALLBACK (gtk_source_vim_text_history_delete_range_cb),
+                                             self);
 }
 
 void
 gtk_source_vim_text_history_replay (GtkSourceVimTextHistory *self)
 {
-       static guint signal_id;
-       GtkSourceView *view;
-       GdkSurface *surface;
-       GtkNative *native;
+       GtkSourceBuffer *buffer;
+       GtkTextIter iter;
+       GtkTextIter end;
+       const char *str;
+       int len;
 
        g_return_if_fail (GTK_SOURCE_IS_VIM_TEXT_HISTORY (self));
 
-       if (signal_id == 0)
+       buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &iter, NULL);
+
+       for (guint i = 0; i < self->ops->len; i++)
        {
-               signal_id = g_signal_lookup ("event", GDK_TYPE_SURFACE);
-       }
+               const Op *op = &g_array_index (self->ops, Op, i);
 
-       view = gtk_source_vim_state_get_view (GTK_SOURCE_VIM_STATE (self));
-       native = gtk_widget_get_native (GTK_WIDGET (view));
-       surface = gtk_native_get_surface (native);
+               switch (op->kind)
+               {
+                       case OP_INSERT:
+                               str = self->bytes->str + op->offset;
+                               len = g_utf8_offset_to_pointer (str, op->length) - str;
+                               gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer), &iter, str, len);
+                               break;
 
-       for (guint i = 0; i < self->events->len; i++)
-       {
-               GdkEvent *event = g_ptr_array_index (self->events, i);
-               gboolean ret;
+                       case OP_DELETE:
+                               end = iter;
+                               gtk_text_iter_forward_chars (&end, op->length);
+                               gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer), &iter, &end);
+                               break;
+
+                       case OP_BACKSPACE:
+                               end = iter;
+                               gtk_text_iter_backward_chars (&end, op->length);
+                               gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer), &iter, &end);
+                               break;
 
-               g_signal_emit (surface, signal_id, 0, event, &ret);
+                       default:
+                               g_assert_not_reached ();
+               }
        }
 }
diff --git a/gtksourceview/vim/gtk-source-vim-text-history.h b/gtksourceview/vim/gtk-source-vim-text-history.h
index de56daec..e64db007 100644
--- a/gtksourceview/vim/gtk-source-vim-text-history.h
+++ b/gtksourceview/vim/gtk-source-vim-text-history.h
@@ -31,7 +31,7 @@ G_DECLARE_FINAL_TYPE (GtkSourceVimTextHistory, gtk_source_vim_text_history, GTK_
 
 GtkSourceVimState *gtk_source_vim_text_history_new    (void);
 void               gtk_source_vim_text_history_replay (GtkSourceVimTextHistory *self);
-void               gtk_source_vim_text_history_record (GtkSourceVimTextHistory *self,
-                                                       GdkEvent                *event);
+void               gtk_source_vim_text_history_begin  (GtkSourceVimTextHistory *self);
+void               gtk_source_vim_text_history_end    (GtkSourceVimTextHistory *self);
 
 G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]