[gtksourceview/wip/chergert/vim: 334/363] try to keep cursor over a char when not processing




commit 0e05adedd78b51ee0f08b53c5923794b59dc1814
Author: Christian Hergert <chergert redhat com>
Date:   Sat Nov 6 11:41:11 2021 -0700

    try to keep cursor over a char when not processing

 gtksourceview/vim/gtk-source-vim.c | 128 ++++++++++++++++++++++++++++++++++---
 1 file changed, 119 insertions(+), 9 deletions(-)
---
diff --git a/gtksourceview/vim/gtk-source-vim.c b/gtksourceview/vim/gtk-source-vim.c
index 0e794834..2b90a91c 100644
--- a/gtksourceview/vim/gtk-source-vim.c
+++ b/gtksourceview/vim/gtk-source-vim.c
@@ -21,17 +21,23 @@
 
 #include "config.h"
 
+#include "gtksourcebuffer.h"
 #include "gtksourceview.h"
 
 #include "gtk-source-vim.h"
 #include "gtk-source-vim-command.h"
 #include "gtk-source-vim-command-bar.h"
+#include "gtk-source-vim-insert.h"
 #include "gtk-source-vim-normal.h"
+#include "gtk-source-vim-replace.h"
 
 struct _GtkSourceVim
 {
-       GtkSourceVimState parent_instance;
-       GString *command_text;
+       GtkSourceVimState  parent_instance;
+       GString           *command_text;
+       GtkSourceBuffer   *buffer;
+       guint              constrain_insert_source;
+       guint              in_handle_event : 1;
 };
 
 G_DEFINE_TYPE (GtkSourceVim, gtk_source_vim, GTK_SOURCE_TYPE_VIM_STATE)
@@ -93,23 +99,25 @@ gtk_source_vim_handle_event (GtkSourceVimState *state,
 {
        GtkSourceVim *self = (GtkSourceVim *)state;
        GtkSourceVimState *current;
-       gboolean ret;
+       gboolean ret = FALSE;
 
        g_assert (GTK_SOURCE_IS_VIM (self));
        g_assert (event != NULL);
 
+       self->in_handle_event = TRUE;
+
+       g_clear_handle_id (&self->constrain_insert_source, g_source_remove);
+
        /* Allow parent states to capture input before current state */
        if (gtk_source_vim_state_capture (state, event))
        {
-               return TRUE;
+               ret = TRUE;
+               goto finish;
        }
 
        current = gtk_source_vim_state_get_current (state);
-
        if (current == state)
-       {
-               return FALSE;
-       }
+               goto finish;
 
        ret = gtk_source_vim_state_handle_event (current, event);
 
@@ -118,15 +126,115 @@ gtk_source_vim_handle_event (GtkSourceVimState *state,
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COMMAND_TEXT]);
        g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COMMAND_BAR_TEXT]);
 
+finish:
+       self->in_handle_event = FALSE;
+
        return ret;
 }
 
+static gboolean
+constrain_insert_source (gpointer data)
+{
+       GtkSourceVim *self = data;
+       GtkSourceVimState *current;
+       GtkSourceBuffer *buffer;
+       GtkTextIter iter, selection;
+
+       self->constrain_insert_source = 0;
+
+       buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &iter, &selection);
+       current = gtk_source_vim_state_get_current (GTK_SOURCE_VIM_STATE (self));
+
+       if (!GTK_SOURCE_IS_VIM_INSERT (current) &&
+           !GTK_SOURCE_IS_VIM_REPLACE (current) &&
+           !gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer)))
+       {
+               if (gtk_text_iter_ends_line (&iter) &&
+                   !gtk_text_iter_starts_line (&iter))
+               {
+                       gtk_text_iter_backward_char (&iter);
+                       gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+               }
+       }
+
+       return G_SOURCE_REMOVE;
+}
+
+static void
+on_cursor_moved_cb (GtkSourceVim    *self,
+                    GtkSourceBuffer *buffer)
+{
+       g_assert (GTK_SOURCE_IS_VIM (self));
+       g_assert (GTK_SOURCE_IS_BUFFER (buffer));
+
+       if (self->in_handle_event)
+               return;
+
+       /* Make sure we are placed on a character instead of on a \n
+        * which is possible when the user clicks with a button or other
+        * external tools. Don't do it until an idle callback though so
+        * that we don't affect anything currently processing.
+        */
+       if (self->constrain_insert_source == 0)
+       {
+               self->constrain_insert_source = g_idle_add (constrain_insert_source, self);
+       }
+}
+
+static void
+on_notify_buffer_cb (GtkSourceVim  *self,
+                     GParamSpec    *pspec,
+                     GtkSourceView *view)
+{
+       GtkSourceBuffer *buffer;
+
+       g_assert (GTK_SOURCE_IS_VIM (self));
+       g_assert (GTK_SOURCE_IS_VIEW (view));
+
+       buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+
+       if (self->buffer == buffer)
+               return;
+
+       if (self->buffer != NULL)
+       {
+               g_signal_handlers_disconnect_by_func (self->buffer,
+                                                     G_CALLBACK (on_cursor_moved_cb),
+                                                     self);
+               g_clear_object (&self->buffer);
+       }
+
+       g_set_object (&self->buffer, buffer);
+
+       if (buffer != NULL)
+       {
+               g_signal_connect_object (buffer,
+                                        "cursor-moved",
+                                        G_CALLBACK (on_cursor_moved_cb),
+                                        self,
+                                        G_CONNECT_SWAPPED);
+               on_cursor_moved_cb (self, buffer);
+       }
+}
+
 static void
 gtk_source_vim_view_set (GtkSourceVimState *state)
 {
-       g_assert (GTK_SOURCE_IS_VIM (state));
+       GtkSourceVim *self = (GtkSourceVim *)state;
+       GtkSourceView *view;
+
+       g_assert (GTK_SOURCE_IS_VIM (self));
        g_assert (gtk_source_vim_state_get_child (state) == NULL);
 
+       view = gtk_source_vim_state_get_view (state);
+
+       g_signal_connect_object (view,
+                                "notify::buffer",
+                                G_CALLBACK (on_notify_buffer_cb),
+                                self,
+                                G_CONNECT_SWAPPED);
+       on_notify_buffer_cb (self, NULL, view);
+
        gtk_source_vim_state_push (state, gtk_source_vim_normal_new ());
 }
 
@@ -135,6 +243,8 @@ gtk_source_vim_finalize (GObject *object)
 {
        GtkSourceVim *self = (GtkSourceVim *)object;
 
+       g_clear_handle_id (&self->constrain_insert_source, g_source_remove);
+       g_clear_object (&self->buffer);
        g_string_free (self->command_text, TRUE);
        self->command_text = 0;
 


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