diff options
author | Christian Hergert <chergert@redhat.com> | 2021-03-10 19:30:15 -0800 |
---|---|---|
committer | Christian Hergert <chergert@redhat.com> | 2021-03-11 10:29:41 -0800 |
commit | e2402286eba4ed06bc2cbc9e687b3fbb18c72a7d (patch) | |
tree | b53c3933ca07278cd25f3b48483bb92a1c48c0e6 | |
parent | 0c183b46c0d8dad3265ae18ba333239e0929ce3f (diff) | |
download | gtksourceview-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.txt | 16 | ||||
-rw-r--r-- | docs/reference/gtksourceview-docs.xml.in | 5 | ||||
-rw-r--r-- | gtksourceview/gtksource.h | 1 | ||||
-rw-r--r-- | gtksourceview/gtksourceindenter-private.h | 30 | ||||
-rw-r--r-- | gtksourceview/gtksourceindenter.c | 269 | ||||
-rw-r--r-- | gtksourceview/gtksourceindenter.h | 64 | ||||
-rw-r--r-- | gtksourceview/gtksourcetypes.h | 1 | ||||
-rw-r--r-- | gtksourceview/meson.build | 2 |
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', |