summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Hergert <chergert@redhat.com>2021-03-10 19:30:15 -0800
committerChristian Hergert <chergert@redhat.com>2021-03-11 10:29:41 -0800
commite2402286eba4ed06bc2cbc9e687b3fbb18c72a7d (patch)
treeb53c3933ca07278cd25f3b48483bb92a1c48c0e6
parent0c183b46c0d8dad3265ae18ba333239e0929ce3f (diff)
downloadgtksourceview-e2402286eba4ed06bc2cbc9e687b3fbb18c72a7d.tar.gz
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.
-rw-r--r--docs/reference/gtksourceview-5.0-sections.txt16
-rw-r--r--docs/reference/gtksourceview-docs.xml.in5
-rw-r--r--gtksourceview/gtksource.h1
-rw-r--r--gtksourceview/gtksourceindenter-private.h30
-rw-r--r--gtksourceview/gtksourceindenter.c269
-rw-r--r--gtksourceview/gtksourceindenter.h64
-rw-r--r--gtksourceview/gtksourcetypes.h1
-rw-r--r--gtksourceview/meson.build2
8 files changed, 388 insertions, 0 deletions
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
@@ -493,6 +493,20 @@ 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>
gtk_source_init
@@ -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',