[gtksourceview/wip/chergert/indenter: 3/6] indenter: add GtkSourceIndenter interface




commit e2402286eba4ed06bc2cbc9e687b3fbb18c72a7d
Author: Christian Hergert <chergert redhat com>
Date:   Wed Mar 10 19:30:15 2021 -0800

    indenter: add GtkSourceIndenter interface
    
    This is a minimal interface that can be used to perform indentations.
    Applications may implement this interface and connect it to a
    GtkSourceView using gtk_source_view_set_indenter() to take over
    indentation for the view.

 docs/reference/gtksourceview-5.0-sections.txt |  16 ++
 docs/reference/gtksourceview-docs.xml.in      |   5 +
 gtksourceview/gtksource.h                     |   1 +
 gtksourceview/gtksourceindenter-private.h     |  30 +++
 gtksourceview/gtksourceindenter.c             | 269 ++++++++++++++++++++++++++
 gtksourceview/gtksourceindenter.h             |  64 ++++++
 gtksourceview/gtksourcetypes.h                |   1 +
 gtksourceview/meson.build                     |   2 +
 8 files changed, 388 insertions(+)
---
diff --git a/docs/reference/gtksourceview-5.0-sections.txt b/docs/reference/gtksourceview-5.0-sections.txt
index 6f3f9e2a..e84316f8 100644
--- a/docs/reference/gtksourceview-5.0-sections.txt
+++ b/docs/reference/gtksourceview-5.0-sections.txt
@@ -492,6 +492,20 @@ GTK_SOURCE_TYPE_HOVER_PROVIDER
 gtk_source_hover_provider_get_type
 </SECTION>
 
+<SECTION>
+<FILE>indenter</FILE>
+GtkSourceIndenter
+GtkSourceIndenterInterface
+gtk_source_indenter_is_trigger
+gtk_source_indenter_indent
+<SUBSECTION Standard>
+GTK_SOURCE_IS_INDENTER
+GTK_SOURCE_INDENTER
+GTK_SOURCE_INDENTER_GET_IFACE
+GTK_SOURCE_TYPE_INDENTER
+gtk_source_indenter_get_type
+</SECTION>
+
 <SECTION>
 <FILE>init</FILE>
 <TITLE>GtkSourceView Initialization and Finalization</TITLE>
@@ -1023,6 +1037,8 @@ gtk_source_view_new_with_buffer
 gtk_source_view_get_completion
 gtk_source_view_get_gutter
 gtk_source_view_get_hover
+gtk_source_view_get_indenter
+gtk_source_view_set_indenter
 gtk_source_view_get_space_drawer
 gtk_source_view_set_show_line_numbers
 gtk_source_view_get_show_line_numbers
diff --git a/docs/reference/gtksourceview-docs.xml.in b/docs/reference/gtksourceview-docs.xml.in
index 067e936d..54d9608c 100644
--- a/docs/reference/gtksourceview-docs.xml.in
+++ b/docs/reference/gtksourceview-docs.xml.in
@@ -79,6 +79,11 @@
       <xi:include href="xml/hoverprovider.xml"/>
     </chapter>
 
+    <chapter id="indenter">
+      <title>Auto-Indentation</title>
+      <xi:include href="xml/indenter.xml"/>
+    </chapter>
+
     <chapter id="printing">
       <title>Printing</title>
       <xi:include href="xml/printcompositor.xml"/>
diff --git a/gtksourceview/gtksource.h b/gtksourceview/gtksource.h
index 45820372..e80198a9 100644
--- a/gtksourceview/gtksource.h
+++ b/gtksourceview/gtksource.h
@@ -38,6 +38,7 @@
 #include "gtksourcehovercontext.h"
 #include "gtksourcehoverdisplay.h"
 #include "gtksourcehoverprovider.h"
+#include "gtksourceindenter.h"
 #include "gtksourceinit.h"
 #include "gtksourcelanguage.h"
 #include "gtksourcelanguagemanager.h"
diff --git a/gtksourceview/gtksourceindenter-private.h b/gtksourceview/gtksourceindenter-private.h
new file mode 100644
index 00000000..0c8b0c54
--- /dev/null
+++ b/gtksourceview/gtksourceindenter-private.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "gtksourceindenter.h"
+
+G_BEGIN_DECLS
+
+GtkSourceIndenter *_gtk_source_indenter_internal_new (void);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourceindenter.c b/gtksourceview/gtksourceindenter.c
new file mode 100644
index 00000000..70243267
--- /dev/null
+++ b/gtksourceview/gtksourceindenter.c
@@ -0,0 +1,269 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2015-2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtksourceindenter-private.h"
+#include "gtksourceview.h"
+
+/**
+ * SECTION:indenter
+ * @Title: GtkSourceIndenter
+ * @Short_description: Auto-indentation interface
+ * @See_also: gtk_source_view_set_indenter()
+ *
+ * By default, #GtkSourceView can auto-indent as you type when
+ * #GtkSourceView:auto-indent is enabled. The indentation simply copies the
+ * previous lines indentation.
+ *
+ * This can be changed by implementing #GtkSourceIndenter and setting the
+ * #GtkSourceView:indenter property.
+ *
+ * Implementors of this interface should implement both
+ * #GtkSourceIndenterInterface.is_trigger and
+ * #GtkSourceIndenterInterface.indent.
+ *
+ * #GtkSourceIndenterInterface.is_trigger is called upon key-press to
+ * determine of the key press should trigger an indentation.  The default
+ * implementation of the interface checks to see if the key was
+ * %GDK_KEY_Return or %GDK_KEY_KP_Enter without %GDK_SHIFT_MASK set.
+ *
+ * #GtkSourceIndenterInterface.indent is called after text has been
+ * inserted into #GtkSourceBuffer when
+ * #GtkSourceIndenterInterface.is_trigger returned %TRUE. The #GtkTextIter
+ * is placed directly after the inserted character or characters.
+ *
+ * It may be beneficial to move the insertion mark using
+ * gtk_text_buffer_select_range() depending on how the indenter changes
+ * the indentation.
+ *
+ * All changes are encapsulated within a single user action so that the
+ * user may undo them using standard undo/redo accelerators.
+ *
+ * Since: 5.0
+ */
+
+/**
+ * GtkSourceIndenterInterface:
+ * @is_trigger: the virtual function pointer for gtk_source_indenter_is_trigger()
+ * @indent: the virtual function pointer for gtk_source_indenter_indent()
+ *
+ * The virtual function table for #GtkSourceIndenter.
+ *
+ * Since: 5.0
+ */
+
+static inline gboolean
+char_is_space (gunichar ch)
+{
+       return ch != '\n' &&
+              ch != '\r' &&
+              g_unichar_isspace (ch);
+}
+
+static gchar *
+copy_prefix_for_line (GtkTextBuffer *buffer,
+                      guint          line)
+{
+       GtkTextIter begin;
+       GtkTextIter end;
+
+       g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+       gtk_text_buffer_get_iter_at_line_offset (buffer, &begin, line, 0);
+
+       end = begin;
+       while (!gtk_text_iter_ends_line (&end) &&
+              char_is_space (gtk_text_iter_get_char (&end)) &&
+              gtk_text_iter_forward_char (&end))
+       {
+               /* Do Nothing */
+       }
+
+       return gtk_text_iter_get_slice (&begin, &end);
+}
+
+static void
+indent_by_copying_previous_line (GtkSourceIndenter *self,
+                                 GtkSourceView     *view,
+                                 GtkTextIter       *location)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter begin;
+       GtkTextIter end;
+       guint line;
+
+       g_assert (GTK_SOURCE_IS_INDENTER (self));
+       g_assert (GTK_SOURCE_IS_VIEW (view));
+       g_assert (location != NULL);
+
+       buffer = gtk_text_iter_get_buffer (location);
+       line = gtk_text_iter_get_line (location);
+
+       begin = *location;
+       if (!gtk_text_iter_starts_line (&begin))
+       {
+               gtk_text_iter_set_line_offset (&begin, 0);
+       }
+
+       end = *location;
+       while (!gtk_text_iter_ends_line (&end) &&
+              char_is_space (gtk_text_iter_get_char (&end)) &&
+              gtk_text_iter_forward_char (&end))
+       {
+               /* Do Nothing */
+       }
+
+       if (!gtk_text_iter_equal (&begin, &end))
+       {
+               gtk_text_buffer_delete (buffer, &begin, &end);
+       }
+
+       if (line > 0)
+       {
+               gchar *text = copy_prefix_for_line (buffer, line - 1);
+               g_print ("prefix: '%s'\n", text);
+               gtk_text_buffer_insert (buffer, &begin, text, -1);
+               g_free (text);
+       }
+
+       *location = begin;
+}
+
+static gboolean
+trigger_on_newline (GtkSourceIndenter *self,
+                    GtkSourceView     *view,
+                    const GtkTextIter *location,
+                    GdkModifierType    state,
+                    guint              keyval)
+{
+       return !(state & GDK_SHIFT_MASK) &&
+              (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter);
+}
+
+G_DEFINE_INTERFACE (GtkSourceIndenter, gtk_source_indenter, G_TYPE_OBJECT)
+
+static void
+gtk_source_indenter_default_init (GtkSourceIndenterInterface *iface)
+{
+       iface->is_trigger = trigger_on_newline;
+       iface->indent = indent_by_copying_previous_line;
+}
+
+/**
+ * gtk_source_indenter_is_trigger:
+ * @self: a #GtkSourceIndenter
+ * @view: a #GtkSourceView
+ * @location: the location where @ch is to be inserted
+ * @state: modifier state for the insertion
+ * @keyval: the keyval pressed such as %GDK_KEY_Return
+ *
+ * This function is used to determine if a key pressed should cause the
+ * indenter to automatically indent.
+ *
+ * The default implementation of this virtual method will check to see
+ * if @keyval is %GDK_KEY_Return or %GDK_KEY_KP_Enter and @state does
+ * not have %GDK_SHIFT_MASK set. This is to allow the user to avoid
+ * indentation when Shift+Return is pressed. Other indenters may want
+ * to copy this behavior to provide a consistent experience to users.
+ *
+ * Returns: %TRUE if indentation should be automatically triggered;
+ *   otherwise %FALSE and no indentation will be performed.
+ *
+ * Since: 5.0
+ */
+gboolean
+gtk_source_indenter_is_trigger (GtkSourceIndenter *self,
+                                GtkSourceView     *view,
+                                const GtkTextIter *location,
+                                GdkModifierType    state,
+                                guint              keyval)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_INDENTER (self), FALSE);
+       g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
+       g_return_val_if_fail (location != NULL, FALSE);
+
+       return GTK_SOURCE_INDENTER_GET_IFACE (self)->is_trigger (self, view, location, state, keyval);
+}
+
+/**
+ * gtk_source_indenter_indent:
+ * @self: a #GtkSourceIndenter
+ * @view: a #GtkSourceView
+ * @iter: (inout): the location of the indentation request
+ *
+ * This function should be implemented to alter the indentation of text
+ * within the view. @view is provided so that the indenter may retrieve
+ * settings such as indentation and tab widths.
+ *
+ * @iter is the location where the indentation was requested. This typically
+ * is after having just inserted a newline (\n) character but can be other
+ * situations such as a manually requested indentation or reformatting.
+ *
+ * See gtk_source_indenter_is_trigger() for how to trigger indentation on
+ * various characters inserted into the buffer.
+ *
+ * The implementor of this function is expected to keep @iter valid across
+ * calls to the function and should contain the location of the insert mark
+ * after calling this function.
+ *
+ * The default implementation for this virtual function will copy the
+ * indentation of the previous line.
+ *
+ * Since: 5.0
+ */
+void
+gtk_source_indenter_indent (GtkSourceIndenter *self,
+                            GtkSourceView     *view,
+                            GtkTextIter       *iter)
+{
+       g_return_if_fail (GTK_SOURCE_IS_INDENTER (self));
+       g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
+       g_return_if_fail (iter != NULL);
+
+       GTK_SOURCE_INDENTER_GET_IFACE (self)->indent (self, view, iter);
+}
+
+struct _GtkSourceIndenterInternal
+{
+       GObject parent_instance;
+};
+
+#define GTK_SOURCE_TYPE_INDENTER_INTERNAL (gtk_source_indenter_internal_get_type())
+G_DECLARE_FINAL_TYPE (GtkSourceIndenterInternal, gtk_source_indenter_internal, GTK_SOURCE, 
INDENTER_INTERNAL, GObject)
+G_DEFINE_TYPE_WITH_CODE (GtkSourceIndenterInternal, gtk_source_indenter_internal, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_INDENTER, NULL))
+
+static void
+gtk_source_indenter_internal_class_init (GtkSourceIndenterInternalClass *klass)
+{
+}
+
+static void
+gtk_source_indenter_internal_init (GtkSourceIndenterInternal *self)
+{
+}
+
+GtkSourceIndenter *
+_gtk_source_indenter_internal_new (void)
+{
+       return g_object_new (GTK_SOURCE_TYPE_INDENTER_INTERNAL, NULL);
+}
diff --git a/gtksourceview/gtksourceindenter.h b/gtksourceview/gtksourceindenter.h
new file mode 100644
index 00000000..d19d996b
--- /dev/null
+++ b/gtksourceview/gtksourceindenter.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2015-2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#if !defined (GTK_SOURCE_H_INSIDE) && !defined (GTK_SOURCE_COMPILATION)
+# error "Only <gtksourceview/gtksource.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+#include "gtksourcetypes.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_INDENTER (gtk_source_indenter_get_type())
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+G_DECLARE_INTERFACE (GtkSourceIndenter, gtk_source_indenter, GTK_SOURCE, INDENTER, GObject)
+
+struct _GtkSourceIndenterInterface
+{
+       GTypeInterface parent_iface;
+
+       gboolean (*is_trigger) (GtkSourceIndenter *self,
+                               GtkSourceView     *view,
+                               const GtkTextIter *location,
+                               GdkModifierType    state,
+                               guint              keyval);
+       void     (*indent)     (GtkSourceIndenter  *self,
+                               GtkSourceView     *view,
+                               GtkTextIter       *iter);
+};
+
+GTK_SOURCE_AVAILABLE_IN_5_0
+gboolean gtk_source_indenter_is_trigger (GtkSourceIndenter *self,
+                                         GtkSourceView     *view,
+                                         const GtkTextIter *location,
+                                         GdkModifierType    state,
+                                         guint              keyval);
+GTK_SOURCE_AVAILABLE_IN_5_0
+void     gtk_source_indenter_indent     (GtkSourceIndenter *self,
+                                         GtkSourceView     *view,
+                                         GtkTextIter       *iter);
+
+G_END_DECLS
diff --git a/gtksourceview/gtksourcetypes.h b/gtksourceview/gtksourcetypes.h
index 6f1530cc..2a1849fd 100644
--- a/gtksourceview/gtksourcetypes.h
+++ b/gtksourceview/gtksourcetypes.h
@@ -54,6 +54,7 @@ typedef struct _GtkSourceHover                     GtkSourceHover;
 typedef struct _GtkSourceHoverContext              GtkSourceHoverContext;
 typedef struct _GtkSourceHoverDisplay              GtkSourceHoverDisplay;
 typedef struct _GtkSourceHoverProvider             GtkSourceHoverProvider;
+typedef struct _GtkSourceIndenter                  GtkSourceIndenter;
 typedef struct _GtkSourceLanguage                  GtkSourceLanguage;
 typedef struct _GtkSourceLanguageManager           GtkSourceLanguageManager;
 typedef struct _GtkSourceMap                       GtkSourceMap;
diff --git a/gtksourceview/meson.build b/gtksourceview/meson.build
index 4abec0c0..72d140d9 100644
--- a/gtksourceview/meson.build
+++ b/gtksourceview/meson.build
@@ -25,6 +25,7 @@ core_public_h = files([
   'gtksourcehovercontext.h',
   'gtksourcehoverprovider.h',
   'gtksourcehoverdisplay.h',
+  'gtksourceindenter.h',
   'gtksourceinit.h',
   'gtksourcelanguage.h',
   'gtksourcelanguagemanager.h',
@@ -72,6 +73,7 @@ core_public_c = files([
   'gtksourcehovercontext.c',
   'gtksourcehoverdisplay.c',
   'gtksourcehoverprovider.c',
+  'gtksourceindenter.c',
   'gtksourceinit.c',
   'gtksourcelanguage.c',
   'gtksourcelanguagemanager.c',


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