[gnome-builder] auto-indent: add a rudimentary xml indenter
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] auto-indent: add a rudimentary xml indenter
- Date: Fri, 17 Oct 2014 05:23:57 +0000 (UTC)
commit 088a8fe8a57f18e67283baa14ceeb7520f3ebd47
Author: Christian Hergert <christian hergert me>
Date: Thu Oct 16 22:20:39 2014 -0700
auto-indent: add a rudimentary xml indenter
Emphasis on the rudamentary.
https://bugzilla.gnome.org/show_bug.cgi?id=738038
src/auto-indent/gb-source-auto-indenter-xml.c | 324 +++++++++++++++++++++++++
src/auto-indent/gb-source-auto-indenter-xml.h | 56 +++++
src/editor/gb-editor-tab.c | 3 +
src/gnome-builder.mk | 2 +
4 files changed, 385 insertions(+), 0 deletions(-)
---
diff --git a/src/auto-indent/gb-source-auto-indenter-xml.c b/src/auto-indent/gb-source-auto-indenter-xml.c
new file mode 100644
index 0000000..0625f53
--- /dev/null
+++ b/src/auto-indent/gb-source-auto-indenter-xml.c
@@ -0,0 +1,324 @@
+/* gb-source-auto-indenter-xml.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "indent-xml"
+
+#include "gb-log.h"
+#include "gb-source-auto-indenter-xml.h"
+
+/*
+ * TODO:
+ *
+ * This is very naive. But let's see if it gets the job done enough to not
+ * be super annoying. Things like indent_width belong as fields in a private
+ * structure.
+ *
+ * Anywho, if you want to own this module, go for it.
+ */
+
+#define INDENT_WIDTH 2
+
+G_DEFINE_TYPE (GbSourceAutoIndenterXml,
+ gb_source_auto_indenter_xml,
+ GB_TYPE_SOURCE_AUTO_INDENTER)
+
+static gunichar
+text_iter_peek_next_char (const GtkTextIter *location)
+{
+ GtkTextIter iter = *location;
+
+ if (gtk_text_iter_forward_char (&iter))
+ return gtk_text_iter_get_char (&iter);
+
+ return 0;
+}
+
+static gunichar
+text_iter_peek_prev_char (const GtkTextIter *location)
+{
+ GtkTextIter iter = *location;
+
+ if (gtk_text_iter_backward_char (&iter))
+ return gtk_text_iter_get_char (&iter);
+
+ return 0;
+}
+
+static void
+build_indent (GbSourceAutoIndenterXml *xml,
+ guint line_offset,
+ GtkTextIter *matching_line,
+ GString *str)
+{
+ GtkTextIter iter;
+ gunichar ch;
+
+ if (!line_offset)
+ return;
+
+ gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (matching_line),
+ &iter,
+ gtk_text_iter_get_line (matching_line));
+
+ do {
+ ch = gtk_text_iter_get_char (&iter);
+
+ switch (ch)
+ {
+ case '\t':
+ case ' ':
+ g_string_append_unichar (str, ch);
+ break;
+
+ default:
+ g_string_append_c (str, ' ');
+ break;
+ }
+ } while (gtk_text_iter_forward_char (&iter) &&
+ (gtk_text_iter_compare (&iter, matching_line) <= 0) &&
+ (str->len < line_offset));
+
+ while (str->len < line_offset)
+ g_string_append_c (str, ' ');
+}
+
+GbSourceAutoIndenter *
+gb_source_auto_indenter_xml_new (void)
+{
+ return g_object_new (GB_TYPE_SOURCE_AUTO_INDENTER_XML, NULL);
+}
+
+static gboolean
+text_iter_in_cdata (const GtkTextIter *location)
+{
+ GtkTextIter iter = *location;
+ gboolean ret = FALSE;
+
+ if (gtk_text_iter_backward_search (&iter, "<![CDATA[",
+ GTK_TEXT_SEARCH_TEXT_ONLY,
+ NULL, &iter, NULL))
+ {
+ if (!gtk_text_iter_forward_search (&iter, "]]>",
+ GTK_TEXT_SEARCH_TEXT_ONLY,
+ NULL, NULL, location))
+ {
+ ret = TRUE;
+ GOTO (cleanup);
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+static gboolean
+text_iter_backward_to_element_start (const GtkTextIter *iter,
+ GtkTextIter *match_begin)
+{
+ GtkTextIter tmp = *iter;
+ gboolean ret = FALSE;
+ gint depth = 0;
+
+ g_return_val_if_fail (iter, FALSE);
+ g_return_val_if_fail (match_begin, FALSE);
+
+ while (gtk_text_iter_backward_char (&tmp))
+ {
+ gunichar ch;
+
+ ch = gtk_text_iter_get_char (&tmp);
+
+ if ((ch == '/') && (text_iter_peek_prev_char (&tmp) == '<'))
+ {
+ gtk_text_iter_backward_char (&tmp);
+ depth++;
+ }
+ else if ((ch == '/') && (text_iter_peek_next_char (&tmp) == '>'))
+ {
+ depth++;
+ }
+ else if ((ch == '<') && (text_iter_peek_next_char (&tmp) != '!'))
+ {
+ depth--;
+ if (depth < 0)
+ {
+ *match_begin = tmp;
+ ret = TRUE;
+ GOTO (cleanup);
+ }
+ }
+ }
+
+cleanup:
+ return ret;
+}
+
+static gchar *
+gb_source_auto_indenter_xml_indent (GbSourceAutoIndenterXml *xml,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ gint *cursor_offset,
+ guint tab_width)
+{
+ GtkTextIter match_begin;
+ GString *str;
+ guint offset;
+
+ g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER_XML (xml), NULL);
+ g_return_val_if_fail (begin, NULL);
+ g_return_val_if_fail (end, NULL);
+
+ str = g_string_new (NULL);
+
+ if (text_iter_backward_to_element_start (begin, &match_begin))
+ {
+ offset = gtk_text_iter_get_line_offset (&match_begin);
+ build_indent (xml, offset + INDENT_WIDTH, &match_begin, str);
+ GOTO (cleanup);
+ }
+
+ /* do nothing */
+
+cleanup:
+ return g_string_free (str, (str->len == 0));
+}
+
+static gchar *
+gb_source_auto_indenter_xml_maybe_unindent (GbSourceAutoIndenterXml *xml,
+ GtkTextIter *begin,
+ GtkTextIter *end)
+{
+ GtkTextIter tmp;
+ gunichar ch;
+
+ g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER_XML (xml), NULL);
+ g_return_val_if_fail (begin, NULL);
+ g_return_val_if_fail (end, NULL);
+
+ tmp = *begin;
+
+ if (!gtk_text_iter_backward_char (&tmp))
+ return NULL;
+
+ if (('/' == gtk_text_iter_get_char (&tmp)) &&
+ gtk_text_iter_backward_char (&tmp) &&
+ ('<' == gtk_text_iter_get_char (&tmp)) &&
+ (ch = text_iter_peek_prev_char (&tmp)) &&
+ ((ch == ' ') || (ch == '\t')))
+ {
+ if (ch == '\t')
+ {
+ gtk_text_iter_backward_char (&tmp);
+ *begin = tmp;
+ return g_strdup ("</");
+ }
+ else
+ {
+ gint count = INDENT_WIDTH;
+
+ while (count)
+ {
+ if (!gtk_text_iter_backward_char (&tmp) ||
+ !(ch = gtk_text_iter_get_char (&tmp)) ||
+ (ch != ' '))
+ return NULL;
+ count--;
+ if (count == 0)
+ GOTO (success);
+ }
+ }
+ }
+
+ return NULL;
+
+success:
+ *begin = tmp;
+ return g_strdup ("</");
+}
+
+static gboolean
+gb_source_auto_indenter_xml_is_trigger (GbSourceAutoIndenter *indenter,
+ GdkEventKey *event)
+{
+ switch (event->keyval)
+ {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_slash:
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+gb_source_auto_indenter_xml_format (GbSourceAutoIndenter *indenter,
+ GtkTextView *view,
+ GtkTextBuffer *buffer,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ gint *cursor_offset,
+ GdkEventKey *trigger)
+{
+ GbSourceAutoIndenterXml *xml = (GbSourceAutoIndenterXml *)indenter;
+ guint tab_width = 2;
+
+ g_return_val_if_fail (GB_IS_SOURCE_AUTO_INDENTER_XML (xml), NULL);
+
+ *cursor_offset = 0;
+
+ if (GTK_SOURCE_IS_VIEW (view))
+ tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view));
+
+ /* do nothing if we are in a cdata section */
+ if (text_iter_in_cdata (begin))
+ return NULL;
+
+ switch (trigger->keyval)
+ {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ return gb_source_auto_indenter_xml_indent (xml, begin, end, cursor_offset,
+ tab_width);
+
+ case GDK_KEY_slash:
+ return gb_source_auto_indenter_xml_maybe_unindent (xml, begin, end);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ return NULL;
+}
+
+static void
+gb_source_auto_indenter_xml_class_init (GbSourceAutoIndenterXmlClass *klass)
+{
+ GbSourceAutoIndenterClass *parent = GB_SOURCE_AUTO_INDENTER_CLASS (klass);
+
+ parent->format = gb_source_auto_indenter_xml_format;
+ parent->is_trigger = gb_source_auto_indenter_xml_is_trigger;
+}
+
+static void
+gb_source_auto_indenter_xml_init (GbSourceAutoIndenterXml *self)
+{
+}
diff --git a/src/auto-indent/gb-source-auto-indenter-xml.h b/src/auto-indent/gb-source-auto-indenter-xml.h
new file mode 100644
index 0000000..96bdeea
--- /dev/null
+++ b/src/auto-indent/gb-source-auto-indenter-xml.h
@@ -0,0 +1,56 @@
+/* gb-source-auto-indenter-xml.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SOURCE_AUTO_INDENTER_XML_H
+#define GB_SOURCE_AUTO_INDENTER_XML_H
+
+#include "gb-source-auto-indenter.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_SOURCE_AUTO_INDENTER_XML (gb_source_auto_indenter_xml_get_type())
+#define GB_SOURCE_AUTO_INDENTER_XML(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_AUTO_INDENTER_XML, GbSourceAutoIndenterXml))
+#define GB_SOURCE_AUTO_INDENTER_XML_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_AUTO_INDENTER_XML, GbSourceAutoIndenterXml const))
+#define GB_SOURCE_AUTO_INDENTER_XML_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GB_TYPE_SOURCE_AUTO_INDENTER_XML, GbSourceAutoIndenterXmlClass))
+#define GB_IS_SOURCE_AUTO_INDENTER_XML(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_SOURCE_AUTO_INDENTER_XML))
+#define GB_IS_SOURCE_AUTO_INDENTER_XML_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GB_TYPE_SOURCE_AUTO_INDENTER_XML))
+#define GB_SOURCE_AUTO_INDENTER_XML_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GB_TYPE_SOURCE_AUTO_INDENTER_XML, GbSourceAutoIndenterXmlClass))
+
+typedef struct _GbSourceAutoIndenterXml GbSourceAutoIndenterXml;
+typedef struct _GbSourceAutoIndenterXmlClass GbSourceAutoIndenterXmlClass;
+typedef struct _GbSourceAutoIndenterXmlPrivate GbSourceAutoIndenterXmlPrivate;
+
+struct _GbSourceAutoIndenterXml
+{
+ GbSourceAutoIndenter parent;
+
+ /*< private >*/
+ GbSourceAutoIndenterXmlPrivate *priv;
+};
+
+struct _GbSourceAutoIndenterXmlClass
+{
+ GbSourceAutoIndenterClass parent;
+};
+
+GType gb_source_auto_indenter_xml_get_type (void);
+GbSourceAutoIndenter *gb_source_auto_indenter_xml_new (void);
+
+G_END_DECLS
+
+#endif /* GB_SOURCE_AUTO_INDENTER_XML_H */
diff --git a/src/editor/gb-editor-tab.c b/src/editor/gb-editor-tab.c
index 8bc9ee2..cf17c4a 100644
--- a/src/editor/gb-editor-tab.c
+++ b/src/editor/gb-editor-tab.c
@@ -31,6 +31,7 @@
#include "gb-rgba.h"
#include "gb-source-auto-indenter.h"
#include "gb-source-auto-indenter-c.h"
+#include "gb-source-auto-indenter-xml.h"
#include "gb-source-change-gutter-renderer.h"
#include "gb-source-highlight-menu.h"
#include "gb-source-snippet.h"
@@ -438,6 +439,8 @@ gb_editor_tab_language_changed (GbEditorTab *tab,
if (g_str_equal (lang_id, "c") || g_str_equal (lang_id, "chdr"))
indenter = gb_source_auto_indenter_c_new ();
+ if (g_str_equal (lang_id, "xml") || g_str_equal (lang_id, "html"))
+ indenter = gb_source_auto_indenter_xml_new ();
}
gb_source_view_set_auto_indenter (tab->priv->source_view, indenter);
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index 76e1d82..36aa68f 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -13,6 +13,8 @@ libgnome_builder_la_SOURCES = \
src/auto-indent/gb-source-auto-indenter.h \
src/auto-indent/gb-source-auto-indenter-c.c \
src/auto-indent/gb-source-auto-indenter-c.h \
+ src/auto-indent/gb-source-auto-indenter-xml.c \
+ src/auto-indent/gb-source-auto-indenter-xml.h \
src/commands/gb-command.c \
src/commands/gb-command.h \
src/commands/gb-command-bar.c \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]