[gnome-builder/auto-indent] auto-indenter: lots of incremental work here.



commit 81710b88f82dcab7fd10b2f473e487b3e578f2fb
Author: Christian Hergert <christian hergert me>
Date:   Wed Sep 17 22:20:41 2014 -0700

    auto-indenter: lots of incremental work here.
    
     * move "auto-indenter" to GbSourceView.
     * Start plumbing API so we can do more than just indentation.
       for example, we need to be able to remove text so we can adjust
       indent for } and end comments and such.
     * stub } and / triggers.

 src/editor/gb-editor-tab-private.h     |    5 -
 src/editor/gb-editor-tab.c             |   44 +++--------
 src/editor/gb-source-auto-indenter-c.c |   80 ++++++++++++++++++--
 src/editor/gb-source-auto-indenter.c   |   54 ++++++++++++++
 src/editor/gb-source-auto-indenter.h   |   30 ++++++--
 src/editor/gb-source-view.c            |  126 +++++++++++++++++++++++++++++++-
 src/editor/gb-source-view.h            |    5 +
 7 files changed, 291 insertions(+), 53 deletions(-)
---
diff --git a/src/editor/gb-editor-tab-private.h b/src/editor/gb-editor-tab-private.h
index ad8b9ef..0122ad2 100644
--- a/src/editor/gb-editor-tab-private.h
+++ b/src/editor/gb-editor-tab-private.h
@@ -65,11 +65,6 @@ struct _GbEditorTabPrivate
   GtkSourceGutterRenderer *change_renderer;
 
   /*
-   * Auto-indentation support for a given language.
-   */
-  GbSourceAutoIndenter *auto_indenter;
-
-  /*
    * Tab related settings.
    */
   GbEditorSettings *settings;
diff --git a/src/editor/gb-editor-tab.c b/src/editor/gb-editor-tab.c
index 1ee6282..8351e6d 100644
--- a/src/editor/gb-editor-tab.c
+++ b/src/editor/gb-editor-tab.c
@@ -378,13 +378,26 @@ gb_editor_tab_language_changed (GbEditorTab      *tab,
                                 GbEditorDocument *document)
 {
   GtkSourceLanguage *language;
+  GbSourceAutoIndenter *indenter = NULL;
 
   g_return_if_fail (GB_IS_EDITOR_TAB (tab));
   g_return_if_fail (GB_IS_EDITOR_DOCUMENT (document));
 
   language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (document));
 
+  if (language)
+    {
+      const gchar *lang_id = gtk_source_language_get_id (language);
+
+      if (lang_id)
+        indenter = gb_source_auto_indenter_c_new ();
+    }
+
+  gb_source_view_set_auto_indenter (tab->priv->source_view, indenter);
+  g_clear_object (&indenter);
+
   gb_editor_tab_reload_snippets (tab, language);
+
 }
 
 
@@ -861,29 +874,6 @@ on_source_view_push_snippet (GbSourceView           *source_view,
     }
 }
 
-static gchar *
-on_source_view_query_auto_indent (GbSourceView *source_view,
-                                  GtkTextIter  *iter,
-                                  GbEditorTab  *tab)
-{
-  GtkTextBuffer *buffer;
-  GtkTextView *text_view = (GtkTextView *)source_view;
-  gchar *ret = NULL;
-
-  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
-  g_return_val_if_fail (GB_IS_EDITOR_TAB (tab), NULL);
-  g_return_val_if_fail (iter, NULL);
-
-  if (tab->priv->auto_indenter)
-    {
-      buffer = gtk_text_view_get_buffer (text_view);
-      ret = gb_source_auto_indenter_query (tab->priv->auto_indenter, text_view,
-                                           buffer, iter);
-    }
-
-  return ret;
-}
-
 static gboolean
 transform_file_to_language (GBinding     *binding,
                             const GValue *src_value,
@@ -991,11 +981,6 @@ gb_editor_tab_constructed (GObject *object)
                     "push-snippet",
                     G_CALLBACK (on_source_view_push_snippet),
                     tab);
-  g_signal_connect (priv->source_view,
-                    "query-auto-indent",
-                    G_CALLBACK (on_source_view_query_auto_indent),
-                    tab);
-  g_print ("Connected\n");
 
   g_signal_connect_swapped (priv->go_down_button,
                             "clicked",
@@ -1071,8 +1056,6 @@ gb_editor_tab_constructed (GObject *object)
     gtk_source_gutter_insert (gutter, priv->change_renderer, 0);
   }
 
-  priv->auto_indenter = gb_source_auto_indenter_c_new ();
-
   gb_editor_tab_cursor_moved (tab, priv->document);
 
   EXIT;
@@ -1133,7 +1116,6 @@ gb_editor_tab_dispose (GObject *object)
   g_clear_object (&tab->priv->search_highlighter);
   g_clear_object (&tab->priv->search_settings);
   g_clear_object (&tab->priv->search_context);
-  g_clear_object (&tab->priv->auto_indenter);
   g_clear_object (&tab->priv->settings);
 
   EXIT;
diff --git a/src/editor/gb-source-auto-indenter-c.c b/src/editor/gb-source-auto-indenter-c.c
index 2125a55..bc69096 100644
--- a/src/editor/gb-source-auto-indenter-c.c
+++ b/src/editor/gb-source-auto-indenter-c.c
@@ -269,17 +269,16 @@ in_c89_comment (GtkTextIter *iter)
 }
 
 static gchar *
-gb_source_auto_indenter_c_query (GbSourceAutoIndenter *indenter,
-                                 GtkTextView          *view,
-                                 GtkTextBuffer        *buffer,
-                                 GtkTextIter          *iter)
+gb_source_auto_indenter_c_indent (GbSourceAutoIndenterC *c,
+                                  GtkTextView           *view,
+                                  GtkTextBuffer         *buffer,
+                                  GtkTextIter           *iter)
 {
   GbSourceAutoIndenterCPrivate *priv;
-  GbSourceAutoIndenterC *c = (GbSourceAutoIndenterC *)indenter;
   GtkTextIter cur;
   gunichar ch;
   GString *str;
-  gchar *ret;
+  gchar *ret = NULL;
 
   ENTRY;
 
@@ -302,8 +301,9 @@ gb_source_auto_indenter_c_query (GbSourceAutoIndenter *indenter,
    * start by moving back one character to get to the pre-newline insertion
    * point.
    */
-  if (!gtk_text_iter_backward_find_char (iter, non_space_predicate, NULL, NULL))
-    GOTO (cleanup);
+  if (!g_unichar_isspace (gtk_text_iter_get_char (iter)))
+    if (!gtk_text_iter_backward_find_char (iter, non_space_predicate, NULL, NULL))
+      GOTO (cleanup);
 
   /*
    * Get our last non \n character entered.
@@ -420,6 +420,67 @@ cleanup:
   RETURN (ret);
 }
 
+static gboolean
+gb_source_auto_indenter_c_is_trigger (GbSourceAutoIndenter *indenter,
+                                      GdkEventKey          *event)
+{
+  switch (event->keyval) {
+  case GDK_KEY_Return:
+  case GDK_KEY_KP_Enter:
+  case GDK_KEY_slash:
+  case GDK_KEY_braceright:
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+static gchar *
+gb_source_auto_indenter_c_format (GbSourceAutoIndenter *indenter,
+                                  GtkTextView          *view,
+                                  GtkTextBuffer        *buffer,
+                                  GtkTextIter          *begin,
+                                  GtkTextIter          *end,
+                                  GdkEventKey          *event)
+{
+  GbSourceAutoIndenterC *c = (GbSourceAutoIndenterC *)indenter;
+  GtkTextIter begin_copy;
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER_C (c), NULL);
+
+  switch (event->keyval) {
+  case GDK_KEY_Return:
+  case GDK_KEY_KP_Enter:
+    gtk_text_iter_assign (&begin_copy, begin);
+    ret = gb_source_auto_indenter_c_indent (c, view, buffer, begin);
+    gtk_text_iter_assign (begin, &begin_copy);
+    break;
+
+  case GDK_KEY_braceright:
+    /*
+     * Probably need to unindent this line.
+     */
+    g_debug ("TODO: unindent the curly brace if needed.");
+    break;
+
+  case GDK_KEY_slash:
+    /*
+     * TODO:
+     *
+     * If we are at the " * " beginning of a multi-line comment, let's just
+     * close the comment.
+     */
+    g_debug ("TODO: close current multi-line comment.");
+    break;
+
+  default:
+    break;
+  }
+
+  return ret;
+}
+
 static void
 gb_source_auto_indenter_c_get_property (GObject    *object,
                                         guint       prop_id,
@@ -465,7 +526,8 @@ gb_source_auto_indenter_c_class_init (GbSourceAutoIndenterCClass *klass)
   object_class->get_property = gb_source_auto_indenter_c_get_property;
   object_class->set_property = gb_source_auto_indenter_c_set_property;
 
-  indenter_class->query = gb_source_auto_indenter_c_query;
+  indenter_class->is_trigger = gb_source_auto_indenter_c_is_trigger;
+  indenter_class->format = gb_source_auto_indenter_c_format;
 
   gParamSpecs [PROP_SCOPE_INDENT] =
     g_param_spec_int ("scope-indent",
diff --git a/src/editor/gb-source-auto-indenter.c b/src/editor/gb-source-auto-indenter.c
index 45bfeed..58ace44 100644
--- a/src/editor/gb-source-auto-indenter.c
+++ b/src/editor/gb-source-auto-indenter.c
@@ -47,10 +47,64 @@ gb_source_auto_indenter_query (GbSourceAutoIndenter *indenter,
                                                               buffer, iter);
 }
 
+static gboolean
+gb_source_auto_indenter_real_is_trigger (GbSourceAutoIndenter *indenter,
+                                         GdkEventKey          *event)
+{
+  return FALSE;
+}
+
+gboolean
+gb_source_auto_indenter_is_trigger (GbSourceAutoIndenter *indenter,
+                                    GdkEventKey          *event)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER (indenter), FALSE);
+  g_return_val_if_fail (event, FALSE);
+
+  return GB_SOURCE_AUTO_INDENTER_GET_CLASS (indenter)->is_trigger (indenter,
+                                                                   event);
+}
+
+gchar *
+gb_source_auto_indenter_format (GbSourceAutoIndenter *indenter,
+                                GtkTextView          *view,
+                                GtkTextBuffer        *buffer,
+                                GtkTextIter          *begin,
+                                GtkTextIter          *end,
+                                GdkEventKey          *event)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER (indenter), NULL);
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (view), NULL);
+  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+  g_return_val_if_fail (event, NULL);
+
+  return GB_SOURCE_AUTO_INDENTER_GET_CLASS (indenter)->format (indenter,
+                                                               view,
+                                                               buffer,
+                                                               begin,
+                                                               end,
+                                                               event);
+}
+
+gchar *
+gb_source_auto_indenter_real_format (GbSourceAutoIndenter *indenter,
+                                     GtkTextView          *view,
+                                     GtkTextBuffer        *buffer,
+                                     GtkTextIter          *begin,
+                                     GtkTextIter          *end,
+                                     GdkEventKey          *event)
+{
+  return NULL;
+}
+
 static void
 gb_source_auto_indenter_class_init (GbSourceAutoIndenterClass *klass)
 {
   klass->query = gb_source_auto_indenter_real_query;
+  klass->is_trigger = gb_source_auto_indenter_real_is_trigger;
+  klass->format = gb_source_auto_indenter_real_format;
 }
 
 static void
diff --git a/src/editor/gb-source-auto-indenter.h b/src/editor/gb-source-auto-indenter.h
index 9270ac2..7d4bebd 100644
--- a/src/editor/gb-source-auto-indenter.h
+++ b/src/editor/gb-source-auto-indenter.h
@@ -53,14 +53,32 @@ struct _GbSourceAutoIndenterClass
                    GtkTextBuffer        *buffer,
                    GtkTextIter          *iter);
 
-  gpointer padding[7];
+  gchar *(*format) (GbSourceAutoIndenter *indenter,
+                    GtkTextView          *view,
+                    GtkTextBuffer        *buffer,
+                    GtkTextIter          *begin,
+                    GtkTextIter          *end,
+                    GdkEventKey          *trigger);
+
+  gboolean (*is_trigger) (GbSourceAutoIndenter *indenter,
+                          GdkEventKey          *event);
+
+  gpointer padding[6];
 };
 
-GType  gb_source_auto_indenter_get_type (void) G_GNUC_CONST;
-gchar *gb_source_auto_indenter_query    (GbSourceAutoIndenter *indenter,
-                                         GtkTextView          *view,
-                                         GtkTextBuffer        *buffer,
-                                         GtkTextIter          *iter);
+GType     gb_source_auto_indenter_get_type   (void) G_GNUC_CONST;
+gchar    *gb_source_auto_indenter_query      (GbSourceAutoIndenter *indenter,
+                                              GtkTextView          *view,
+                                              GtkTextBuffer        *buffer,
+                                              GtkTextIter          *iter);
+gboolean  gb_source_auto_indenter_is_trigger (GbSourceAutoIndenter *indenter,
+                                              GdkEventKey          *event);
+gchar    *gb_source_auto_indenter_format     (GbSourceAutoIndenter *indenter,
+                                              GtkTextView          *view,
+                                              GtkTextBuffer        *buffer,
+                                              GtkTextIter          *begin,
+                                              GtkTextIter          *end,
+                                              GdkEventKey          *event);
 
 G_END_DECLS
 
diff --git a/src/editor/gb-source-view.c b/src/editor/gb-source-view.c
index f5061e2..d1f2fdc 100644
--- a/src/editor/gb-source-view.c
+++ b/src/editor/gb-source-view.c
@@ -26,6 +26,7 @@
 #include "gb-cairo.h"
 #include "gb-editor-document.h"
 #include "gb-log.h"
+#include "gb-source-auto-indenter.h"
 #include "gb-source-search-highlighter.h"
 #include "gb-source-snippet-context.h"
 #include "gb-source-snippet-private.h"
@@ -37,11 +38,14 @@ struct _GbSourceViewPrivate
   GQueue                    *snippets;
   GbSourceSearchHighlighter *search_highlighter;
   GtkTextBuffer             *buffer;
+  GbSourceAutoIndenter      *auto_indenter;
+
   guint                      buffer_insert_text_handler;
   guint                      buffer_insert_text_after_handler;
   guint                      buffer_delete_range_handler;
   guint                      buffer_delete_range_after_handler;
   guint                      buffer_mark_set_handler;
+
   guint                      show_shadow : 1;
 };
 
@@ -54,6 +58,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (GbSourceView, gb_source_view, GTK_SOURCE_TYPE_VIEW)
 
 enum {
   PROP_0,
+  PROP_AUTO_INDENTER,
   PROP_SEARCH_HIGHLIGHTER,
   PROP_SHOW_SHADOW,
   LAST_PROP
@@ -77,8 +82,6 @@ on_search_highlighter_changed (GbSourceSearchHighlighter *highlighter,
   g_return_if_fail (GB_IS_SOURCE_VIEW (view));
   g_return_if_fail (GB_IS_SOURCE_SEARCH_HIGHLIGHTER (highlighter));
 
-  g_print ("%s()\n", G_STRFUNC);
-
   EXIT;
 }
 
@@ -714,6 +717,9 @@ gb_source_view_key_press_event (GtkWidget   *widget,
 
   priv = view->priv;
 
+  /*
+   * Handle movement through the tab stops of the current snippet if needed.
+   */
   if ((snippet = g_queue_peek_head (priv->snippets)))
     {
       switch ((gint) event->keyval)
@@ -745,6 +751,59 @@ gb_source_view_key_press_event (GtkWidget   *widget,
         }
     }
 
+  if (priv->auto_indenter &&
+      gb_source_auto_indenter_is_trigger (priv->auto_indenter, event))
+    {
+      GtkTextMark *insert;
+      GtkTextIter begin;
+      GtkTextIter end;
+      gunichar ch;
+      GString *str;
+      gchar *indent;
+
+      if ((event->keyval == GDK_KEY_Return) || (event->keyval == GDK_KEY_KP_Enter))
+        if (gtk_text_view_im_context_filter_keypress (GTK_TEXT_VIEW (view), event))
+          return TRUE;
+
+      gtk_text_buffer_begin_user_action (priv->buffer);
+
+      /*
+       * Insert the current keypress into the buffer.
+       */
+      str = g_string_new (NULL);
+      ch = gdk_keyval_to_unicode (event->keyval);
+      g_string_append_unichar (str, ch);
+      gtk_text_buffer_insert_at_cursor (priv->buffer, str->str, str->len);
+      g_string_free (str, TRUE);
+
+      /*
+       * Set begin and end to the position of the new insertion point.
+       */
+      insert = gtk_text_buffer_get_insert (priv->buffer);
+      gtk_text_buffer_get_iter_at_mark (priv->buffer, &begin, insert);
+      gtk_text_buffer_get_iter_at_mark (priv->buffer, &end, insert);
+
+      /*
+       * Let the formatter potentially set the replacement text.
+       */
+      indent = gb_source_auto_indenter_format (priv->auto_indenter,
+                                               GTK_TEXT_VIEW (view),
+                                               priv->buffer, &begin, &end,
+                                               event);
+
+      if (indent)
+        {
+          if (!gtk_text_iter_equal (&begin, &end))
+            gtk_text_buffer_delete (priv->buffer, &begin, &end);
+          gtk_text_buffer_insert (priv->buffer, &begin, indent, -1);
+          g_free (indent);
+        }
+
+      gtk_text_buffer_end_user_action (priv->buffer);
+
+      return TRUE;
+    }
+
   return GTK_WIDGET_CLASS (gb_source_view_parent_class)->key_press_event (widget, event);
 }
 
@@ -994,12 +1053,51 @@ gb_source_view_finalize (GObject *object)
 
   priv = GB_SOURCE_VIEW (object)->priv;
 
+  if (priv->buffer)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (priv->buffer),
+                                    (gpointer *)&priv->buffer);
+      priv->buffer = NULL;
+    }
+
   g_clear_pointer (&priv->snippets, g_queue_free);
   g_clear_object (&priv->search_highlighter);
+  g_clear_object (&priv->auto_indenter);
 
   G_OBJECT_CLASS (gb_source_view_parent_class)->finalize (object);
 }
 
+GbSourceAutoIndenter *
+gb_source_view_get_auto_indenter (GbSourceView *view)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_VIEW (view), NULL);
+
+  return view->priv->auto_indenter;
+}
+
+void
+gb_source_view_set_auto_indenter (GbSourceView         *view,
+                                  GbSourceAutoIndenter *auto_indenter)
+{
+  GbSourceViewPrivate *priv;
+
+  g_return_if_fail (GB_IS_SOURCE_VIEW (view));
+  g_return_if_fail (!auto_indenter ||
+                    GB_IS_SOURCE_AUTO_INDENTER (auto_indenter));
+
+  priv = view->priv;
+
+  if (priv->auto_indenter != auto_indenter)
+    {
+      g_clear_object (&priv->auto_indenter);
+      priv->auto_indenter = auto_indenter
+                          ? g_object_ref (auto_indenter)
+                          : NULL;
+      g_object_notify_by_pspec (G_OBJECT (view),
+                                gParamSpecs [PROP_AUTO_INDENTER]);
+    }
+}
+
 static void
 gb_source_view_get_property (GObject    *object,
                              guint       prop_id,
@@ -1010,6 +1108,10 @@ gb_source_view_get_property (GObject    *object,
 
   switch (prop_id)
     {
+    case PROP_AUTO_INDENTER:
+      g_value_set_object (value, gb_source_view_get_auto_indenter (view));
+      break;
+
     case PROP_SEARCH_HIGHLIGHTER:
       g_value_set_object (value, gb_source_view_get_search_highlighter (view));
       break;
@@ -1033,6 +1135,10 @@ gb_source_view_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    case PROP_AUTO_INDENTER:
+      gb_source_view_set_auto_indenter (view, g_value_get_object (value));
+      break;
+
     case PROP_SEARCH_HIGHLIGHTER:
       gb_source_view_set_search_highlighter (view, g_value_get_object (value));
       break;
@@ -1062,6 +1168,22 @@ gb_source_view_class_init (GbSourceViewClass *klass)
 
   text_view_class->draw_layer = gb_source_view_draw_layer;
 
+  /**
+   * GbSourceView:auto-indenter:
+   *
+   * Sets the #GbSourceAutoIndenter to use while typing in the source view.
+   *
+   * %NULL to unset the auto-indenter.
+   */
+  gParamSpecs [PROP_AUTO_INDENTER] =
+    g_param_spec_object ("auto-indenter",
+                         _("Auto Indenter"),
+                         _("The indenter to use when auto_indent is set."),
+                         GB_TYPE_SOURCE_AUTO_INDENTER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_AUTO_INDENTER,
+                                   gParamSpecs [PROP_AUTO_INDENTER]);
+
   gParamSpecs[PROP_SHOW_SHADOW] =
     g_param_spec_boolean ("show-shadow",
                           _ ("Show Shadow"),
diff --git a/src/editor/gb-source-view.h b/src/editor/gb-source-view.h
index 301bb9f..c9cc9db 100644
--- a/src/editor/gb-source-view.h
+++ b/src/editor/gb-source-view.h
@@ -21,6 +21,7 @@
 
 #include <gtksourceview/gtksourceview.h>
 
+#include "gb-source-auto-indenter.h"
 #include "gb-source-snippet.h"
 
 G_BEGIN_DECLS
@@ -66,6 +67,10 @@ gboolean gb_source_view_get_show_shadow (GbSourceView *view); /* XXX: Remove thi
 void     gb_source_view_set_show_shadow (GbSourceView *view, /* XXX: Remove this */
                                          gboolean      show_shadow);
 
+GbSourceAutoIndenter *gb_source_view_get_auto_indenter (GbSourceView         *view);
+void                  gb_source_view_set_auto_indenter (GbSourceView         *view,
+                                                        GbSourceAutoIndenter *auto_indenter);
+
 G_END_DECLS
 
 #endif /* GB_SOURCE_VIEW_H */


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