[gtksourceview] vim: add filter (=) when in visual mode



commit 04642e59ee8da09041eb249040f5248e2cff3b5e
Author: Christian Hergert <chergert redhat com>
Date:   Fri Nov 26 10:06:19 2021 -0600

    vim: add filter (=) when in visual mode
    
    This has a fallback implementation which just re-indents the contents using
    the current indenter.

 gtksourceview/vim/gtksourcevim.c        | 181 +++++++++++++++++++++++++++++---
 gtksourceview/vim/gtksourcevim.h        |   5 +-
 gtksourceview/vim/gtksourcevimcommand.c |  29 +++++
 gtksourceview/vim/gtksourcevimvisual.c  |   3 +
 4 files changed, 205 insertions(+), 13 deletions(-)
---
diff --git a/gtksourceview/vim/gtksourcevim.c b/gtksourceview/vim/gtksourcevim.c
index 4bfd78fe..cf455135 100644
--- a/gtksourceview/vim/gtksourcevim.c
+++ b/gtksourceview/vim/gtksourcevim.c
@@ -21,6 +21,7 @@
 
 #include "config.h"
 
+#include "gtksourceindenter.h"
 #include "gtksourceview.h"
 
 #include "gtksourcevim.h"
@@ -51,6 +52,7 @@ enum {
 
 enum {
        EXECUTE_COMMAND,
+       FILTER,
        FORMAT,
        READY,
        SPLIT,
@@ -70,6 +72,95 @@ gtk_source_vim_new (GtkSourceView *view)
                             NULL);
 }
 
+static inline gboolean
+compare_position (const GtkTextIter *iter,
+                 GtkTextMark       *mark)
+{
+       GtkTextIter mark_iter;
+       gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (mark),
+                                         &mark_iter, mark);
+       return gtk_text_iter_compare (iter, &mark_iter);
+}
+
+static gboolean
+gtk_source_vim_real_format (GtkSourceVim *self,
+                            GtkTextIter  *begin,
+                            GtkTextIter  *end)
+{
+       return FALSE;
+}
+
+static gboolean
+gtk_source_vim_real_filter (GtkSourceVim *self,
+                            GtkTextIter  *begin,
+                            GtkTextIter  *end)
+{
+       GtkSourceIndenter *indenter;
+       GtkSourceView *view;
+       GtkTextBuffer *buffer;
+       GtkTextMark *begin_mark;
+       GtkTextMark *end_mark;
+       GtkTextIter iter;
+
+       g_assert (GTK_SOURCE_IS_VIM (self));
+       g_assert (begin != NULL);
+       g_assert (end != NULL);
+
+       buffer = gtk_text_iter_get_buffer (begin);
+       view = gtk_source_vim_state_get_view (GTK_SOURCE_VIM_STATE (self));
+
+       /* If there is no indenter, we can't do anything here */
+       if (!(indenter = gtk_source_view_get_indenter (view)))
+       {
+               return FALSE;
+       }
+
+       /* Create temporary marks for bounds checking */
+       begin_mark = gtk_text_buffer_create_mark (buffer, NULL, begin, TRUE);
+       end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, FALSE);
+
+       /* Start at beginning of first line */
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter, begin_mark);
+       gtk_text_iter_set_line_offset (&iter, 0);
+
+       /* Remove prefix space from each line */
+       while (compare_position (&iter, end_mark) < 0)
+       {
+               GtkTextIter end_of_space = iter;
+               guint line;
+
+               while (!gtk_text_iter_ends_line (&end_of_space) &&
+                      g_unichar_isspace (gtk_text_iter_get_char (&end_of_space)))
+               {
+                       gtk_text_iter_forward_char (&end_of_space);
+               }
+
+               if (!gtk_text_iter_equal (&iter, &end_of_space))
+               {
+                       gtk_text_buffer_delete (buffer, &iter, &end_of_space);
+               }
+
+               /* The thought here to get_iter_at_line() instead of forward_line()
+                * is that it is a bit more resilient against how much the indenter
+                * changes outside of the direct indentation point. It ensures that
+                * we take the next line after we were on and then processes it too.
+                */
+               line = gtk_text_iter_get_line (&iter);
+               gtk_source_indenter_indent (indenter, view, &iter);
+               gtk_text_buffer_get_iter_at_line (buffer, &iter, line + 1);
+       }
+
+       /* Revalidate iter positions */
+       gtk_text_buffer_get_iter_at_mark (buffer, begin, begin_mark);
+       gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark);
+
+       /* Remove our temporary marks */
+       gtk_text_buffer_delete_mark (buffer, begin_mark);
+       gtk_text_buffer_delete_mark (buffer, end_mark);
+
+       return TRUE;
+}
+
 static gboolean
 gtk_source_vim_handle_event (GtkSourceVimState *state,
                              GdkEvent          *event)
@@ -341,16 +432,58 @@ gtk_source_vim_class_init (GtkSourceVimClass *klass)
         * @begin: the beginning of the text range
         * @end: the end of the text range
         *
-        * Requests that the text range @begin to @end be reformatted.
+        * Requests that the text range @begin to @end be formatted.
+        *
+        * This is equivalent to the `gq` command in Vim.
+        *
         * Applications should conntect to this signal to implement
-        * reformatting as they would like.
+        * formatting as they would like.
+        *
+        * Returns: %TRUE if the format request was handled; otherwise %FALSE
         */
        signals[FORMAT] =
-               g_signal_new ("format",
-                             G_TYPE_FROM_CLASS (klass),
-                             G_SIGNAL_RUN_LAST,
-                             0, NULL, NULL, NULL,
-                             G_TYPE_NONE, 2, GTK_TYPE_TEXT_ITER, GTK_TYPE_TEXT_ITER);
+               g_signal_new_class_handler ("format",
+                                           G_TYPE_FROM_CLASS (klass),
+                                           G_SIGNAL_RUN_LAST,
+                                           G_CALLBACK (gtk_source_vim_real_format),
+                                           g_signal_accumulator_true_handled, NULL,
+                                           NULL,
+                                           G_TYPE_BOOLEAN,
+                                           2,
+                                           GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
+                                           GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       /**
+        * GtkSourceVim::filter:
+        * @self: a #GtkSourceVim
+        * @begin: the beginning of the text range
+        * @end: the end of the text range
+        *
+        * Requests that the text range @begin to @end be filtered (transformed
+        * in some way and replaced).
+        *
+        * Applications should conntect to this signal to implement
+        * filtering as they would like.
+        *
+        * The default handler will attempt to filter by using the
+        * #GtkSourceView's indenter to reindent each line.
+        *
+        * In the future, some effort may be made to restrict line width for
+        * languages and contexts which are known to be safe.
+        *
+        * Returns: %TRUE if the filter request was handled; otherwise %FALSE
+        */
+       signals[FILTER] =
+               g_signal_new_class_handler ("filter",
+                                           G_TYPE_FROM_CLASS (klass),
+                                           G_SIGNAL_RUN_LAST,
+                                           G_CALLBACK (gtk_source_vim_real_filter),
+                                           g_signal_accumulator_true_handled, NULL,
+                                           NULL,
+                                           G_TYPE_BOOLEAN,
+                                           2,
+                                           GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
+                                           GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
 
        signals[READY] =
                g_signal_new ("ready",
@@ -494,14 +627,38 @@ gtk_source_vim_emit_ready (GtkSourceVim *self)
        g_signal_emit (self, signals[READY], 0);
 }
 
-void
+gboolean
+gtk_source_vim_emit_filter (GtkSourceVim *self,
+                            GtkTextIter  *begin,
+                            GtkTextIter  *end)
+{
+       gboolean ret;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM (self), FALSE);
+       g_return_val_if_fail (begin != NULL, FALSE);
+       g_return_val_if_fail (end != NULL, FALSE);
+
+       gtk_text_iter_order (begin, end);
+
+       g_signal_emit (self, signals[FILTER], 0, begin, end, &ret);
+
+       return ret;
+}
+
+gboolean
 gtk_source_vim_emit_format (GtkSourceVim *self,
                             GtkTextIter  *begin,
                             GtkTextIter  *end)
 {
-       g_return_if_fail (GTK_SOURCE_IS_VIM (self));
-       g_return_if_fail (begin != NULL);
-       g_return_if_fail (end != NULL);
+       gboolean ret;
 
-       g_signal_emit (self, signals[FORMAT], 0, begin, end);
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM (self), FALSE);
+       g_return_val_if_fail (begin != NULL, FALSE);
+       g_return_val_if_fail (end != NULL, FALSE);
+
+       gtk_text_iter_order (begin, end);
+
+       g_signal_emit (self, signals[FORMAT], 0, begin, end, &ret);
+
+       return ret;
 }
diff --git a/gtksourceview/vim/gtksourcevim.h b/gtksourceview/vim/gtksourcevim.h
index 2f48c1fa..295b757c 100644
--- a/gtksourceview/vim/gtksourcevim.h
+++ b/gtksourceview/vim/gtksourcevim.h
@@ -35,7 +35,10 @@ const char   *gtk_source_vim_get_command_text     (GtkSourceVim   *self);
 const char   *gtk_source_vim_get_command_bar_text (GtkSourceVim   *self);
 gboolean      gtk_source_vim_emit_execute_command (GtkSourceVim   *self,
                                                    const char     *command);
-void          gtk_source_vim_emit_format          (GtkSourceVim   *self,
+gboolean      gtk_source_vim_emit_filter          (GtkSourceVim   *self,
+                                                   GtkTextIter    *begin,
+                                                   GtkTextIter    *end);
+gboolean      gtk_source_vim_emit_format          (GtkSourceVim   *self,
                                                    GtkTextIter    *begin,
                                                    GtkTextIter    *end);
 void          gtk_source_vim_emit_ready           (GtkSourceVim   *self);
diff --git a/gtksourceview/vim/gtksourcevimcommand.c b/gtksourceview/vim/gtksourcevimcommand.c
index d4c6aa32..b923555d 100644
--- a/gtksourceview/vim/gtksourcevimcommand.c
+++ b/gtksourceview/vim/gtksourcevimcommand.c
@@ -98,6 +98,34 @@ parse_number (const char *str,
        return TRUE;
 }
 
+static void
+gtk_source_vim_command_filter (GtkSourceVimCommand *self)
+{
+       GtkSourceVimState *root;
+       GtkSourceBuffer *buffer;
+       GtkTextIter iter;
+       GtkTextIter selection;
+
+       if (!gtk_source_vim_state_get_editable (GTK_SOURCE_VIM_STATE (self)))
+               return;
+
+       buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &iter, &selection);
+       root = gtk_source_vim_state_get_root (GTK_SOURCE_VIM_STATE (self));
+
+       if (GTK_SOURCE_IS_VIM (root))
+       {
+               gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
+               gtk_source_vim_emit_filter (GTK_SOURCE_VIM (root), &iter, &selection);
+               gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
+
+               gtk_text_iter_order (&iter, &selection);
+
+               gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+       }
+
+       self->ignore_mark = TRUE;
+}
+
 static void
 gtk_source_vim_command_format (GtkSourceVimCommand *self)
 {
@@ -1515,6 +1543,7 @@ gtk_source_vim_command_class_init (GtkSourceVimCommandClass *klass)
        ADD_COMMAND ("indent",         gtk_source_vim_command_indent);
        ADD_COMMAND ("unindent",       gtk_source_vim_command_unindent);
        ADD_COMMAND ("line-number",    gtk_source_vim_command_line_number);
+       ADD_COMMAND ("filter",         gtk_source_vim_command_filter);
        ADD_COMMAND ("format",         gtk_source_vim_command_format);
        ADD_COMMAND ("search",         gtk_source_vim_command_search);
        ADD_COMMAND ("search-replace", gtk_source_vim_command_search_replace);
diff --git a/gtksourceview/vim/gtksourcevimvisual.c b/gtksourceview/vim/gtksourcevimvisual.c
index c969b036..f0595562 100644
--- a/gtksourceview/vim/gtksourcevimvisual.c
+++ b/gtksourceview/vim/gtksourcevimvisual.c
@@ -597,6 +597,9 @@ key_handler_initial (GtkSourceVimVisual *self,
                case GDK_KEY_less:
                        return gtk_source_vim_visual_begin_command (self, "unindent", FALSE);
 
+               case GDK_KEY_equal:
+                       return gtk_source_vim_visual_begin_command (self, "filter", FALSE);
+
                case GDK_KEY_slash:
                case GDK_KEY_question:
                {


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