[gtksourceview/highlighter-split: 1/2] Split highlighter out of the engine.
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/highlighter-split: 1/2] Split highlighter out of the engine.
- Date: Tue, 3 Jan 2012 21:05:56 +0000 (UTC)
commit 5c6676d59616a6aa2b9a92f7e0e1735e4cec0945
Author: Paolo Borelli <pborelli gnome org>
Date: Tue Dec 27 15:17:17 2011 +0100
Split highlighter out of the engine.
gtksourceview/Makefile.am | 5 +-
gtksourceview/gtksourcecontextengine-private.h | 86 +++
gtksourceview/gtksourcecontextengine.c | 553 ++------------------
gtksourceview/gtksourcehighlighter.c | 667 ++++++++++++++++++++++++
gtksourceview/gtksourcehighlighter.h | 79 +++
5 files changed, 881 insertions(+), 509 deletions(-)
---
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 19bfc6f..c050b4f 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -62,7 +62,9 @@ NOINST_H_FILES = \
gtksourcepixbufhelper.h \
gtksourcegutterrendererlines.h \
gtksourcegutterrenderermarks.h \
- gtksourcegutterrenderer-private.h
+ gtksourcegutterrenderer-private.h \
+ gtksourcehighlighter.h \
+ gtksourcecontextengine-private.h
libgtksourceview_c_files = \
gtksourcebuffer.c \
@@ -99,6 +101,7 @@ libgtksourceview_c_files = \
gtksourceview.c \
gtksourceview-i18n.c \
gtksourceview-utils.c \
+ gtksourcehighlighter.c \
gtktextregion.c
libgtksourceview_3_0_la_SOURCES = \
diff --git a/gtksourceview/gtksourcecontextengine-private.h b/gtksourceview/gtksourcecontextengine-private.h
new file mode 100644
index 0000000..d488667
--- /dev/null
+++ b/gtksourceview/gtksourcecontextengine-private.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcecontextengine-private.h
+ * This file is part of gtksourceview
+ *
+ * Copyright (C) 2010 - Jose Aliste <jose aliste gmail 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GTK_SOURCE_CONTEXT_ENGINE_PRIVATE_H__
+#define __GTK_SOURCE_CONTEXT_ENGINE_PRIVATE_H__
+
+#define SEGMENT_IS_INVALID(s) ((s)->context == NULL)
+
+typedef enum {
+ SUB_PATTERN_WHERE_DEFAULT = 0,
+ SUB_PATTERN_WHERE_START,
+ SUB_PATTERN_WHERE_END
+} SubPatternWhere;
+
+typedef struct _SubPatternDefinition SubPatternDefinition;
+typedef struct _SubPattern SubPattern;
+
+typedef struct _Segment Segment;
+typedef struct _Context Context;
+
+struct _Segment
+{
+ Segment *parent;
+ Segment *next;
+ Segment *prev;
+ Segment *children;
+ Segment *last_child;
+
+ /* This is NULL if and only if it's a dummy segment which denotes
+ * inserted or deleted text. */
+ Context *context;
+
+ /* Subpatterns found in this segment. */
+ SubPattern *sub_patterns;
+
+ /* The context is used in the interval [start_at; end_at). */
+ gint start_at;
+ gint end_at;
+
+ /* In case of container contexts, start_len/end_len is length in chars
+ * of start/end match. */
+ gint start_len;
+ gint end_len;
+
+ /* Whether this segment is a whole good segment, or it's an
+ * an end of bigger one left after erase_segments() call. */
+ guint is_start : 1;
+};
+
+struct _SubPattern
+{
+ SubPatternDefinition *definition;
+ gint start_at;
+ gint end_at;
+ SubPattern *next;
+};
+
+/* Context methods */
+gboolean _gtk_source_context_get_style_inside (Context *context);
+
+/* ContextEngine methods */
+GtkTextTag * _gtk_source_context_engine_get_context_tag (GtkSourceContextEngine *ce,
+ Context *context);
+GtkTextTag * _gtk_source_context_engine_get_subpattern_tag (GtkSourceContextEngine *ce,
+ Context *context,
+ SubPatternDefinition *sp_def);
+
+#endif /* __GTK_SOURCE_CONTEXT_ENGINE_PRIVATE_H__ */
diff --git a/gtksourceview/gtksourcecontextengine.c b/gtksourceview/gtksourcecontextengine.c
index f00f41d..14b0961 100644
--- a/gtksourceview/gtksourcecontextengine.c
+++ b/gtksourceview/gtksourcecontextengine.c
@@ -24,12 +24,14 @@
#include <glib.h>
#include "gtksourceview-i18n.h"
#include "gtksourcecontextengine.h"
+#include "gtksourcecontextengine-private.h"
#include "gtktextregion.h"
#include "gtksourcelanguage-private.h"
#include "gtksourcebuffer.h"
#include "gtksourceregex.h"
#include "gtksourcestyle-private.h"
#include "gtksourceview-utils.h"
+#include "gtksourcehighlighter.h"
#undef ENABLE_DEBUG
#undef ENABLE_PROFILE
@@ -104,16 +106,11 @@
#define CONTEXT_IS_SIMPLE(c) ((c)->definition->type == CONTEXT_TYPE_SIMPLE)
#define CONTEXT_IS_CONTAINER(c) ((c)->definition->type == CONTEXT_TYPE_CONTAINER)
-#define SEGMENT_IS_INVALID(s) ((s)->context == NULL)
#define SEGMENT_IS_SIMPLE(s) CONTEXT_IS_SIMPLE ((s)->context)
#define SEGMENT_IS_CONTAINER(s) CONTEXT_IS_CONTAINER ((s)->context)
#define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
-typedef struct _SubPatternDefinition SubPatternDefinition;
-typedef struct _SubPattern SubPattern;
-typedef struct _Segment Segment;
-typedef struct _Context Context;
typedef struct _ContextPtr ContextPtr;
typedef struct _ContextDefinition ContextDefinition;
typedef struct _DefinitionChild DefinitionChild;
@@ -139,11 +136,6 @@ typedef enum {
CONTEXT_TYPE_CONTAINER
} ContextType;
-typedef enum {
- SUB_PATTERN_WHERE_DEFAULT = 0,
- SUB_PATTERN_WHERE_START,
- SUB_PATTERN_WHERE_END
-} SubPatternWhere;
struct _ContextDefinition
{
@@ -284,42 +276,7 @@ struct _GtkSourceContextReplace
gchar *replace_with;
};
-struct _Segment
-{
- Segment *parent;
- Segment *next;
- Segment *prev;
- Segment *children;
- Segment *last_child;
-
- /* This is NULL if and only if it's a dummy segment which denotes
- * inserted or deleted text. */
- Context *context;
-
- /* Subpatterns found in this segment. */
- SubPattern *sub_patterns;
-
- /* The context is used in the interval [start_at; end_at). */
- gint start_at;
- gint end_at;
-
- /* In case of container contexts, start_len/end_len is length in chars
- * of start/end match. */
- gint start_len;
- gint end_len;
-
- /* Whether this segment is a whole good segment, or it's an
- * an end of bigger one left after erase_segments() call. */
- guint is_start : 1;
-};
-struct _SubPattern
-{
- SubPatternDefinition *definition;
- gint start_at;
- gint end_at;
- SubPattern *next;
-};
/* Line terminator characters (\n, \r, \r\n, or unicode paragraph separator)
* are removed from the line text. The problem is that pcre does not understand
@@ -387,16 +344,11 @@ struct _GtkSourceContextData
struct _GtkSourceContextEnginePrivate
{
+ GtkSourceHighlighter *highlighter;
+
GtkSourceContextData *ctx_data;
GtkTextBuffer *buffer;
- GtkSourceStyleScheme *style_scheme;
-
- /* All tags indexed by style name: values are GSList's of tags, ref()'ed. */
- GHashTable *tags;
- /* Number of all syntax tags created by the engine, needed to set correct
- * tag priorities */
- guint n_tags;
GHashTable *context_classes;
@@ -406,9 +358,6 @@ struct _GtkSourceContextEnginePrivate
/* Whether syntax analysis was disabled because of errors. */
gboolean disabled;
- /* Region covering the unhighlighted text. */
- GtkTextRegion *refresh_region;
-
/* Tree of contexts. */
Context *root_context;
Segment *root_segment;
@@ -559,114 +508,6 @@ context_class_tag_free (ContextClassTag *attrtag)
g_slice_free (ContextClassTag, attrtag);
}
-struct BufAndIters {
- GtkTextBuffer *buffer;
- const GtkTextIter *start, *end;
-};
-
-static void
-unhighlight_region_cb (G_GNUC_UNUSED gpointer style,
- GSList *tags,
- gpointer user_data)
-{
- struct BufAndIters *data = user_data;
-
- while (tags != NULL)
- {
- gtk_text_buffer_remove_tag (data->buffer,
- tags->data,
- data->start,
- data->end);
- tags = tags->next;
- }
-}
-
-static void
-unhighlight_region (GtkSourceContextEngine *ce,
- const GtkTextIter *start,
- const GtkTextIter *end)
-{
- struct BufAndIters data;
-
- data.buffer = ce->priv->buffer;
- data.start = start;
- data.end = end;
-
- if (gtk_text_iter_equal (start, end))
- return;
-
- g_hash_table_foreach (ce->priv->tags, (GHFunc) unhighlight_region_cb, &data);
-}
-
-#define MAX_STYLE_DEPENDENCY_DEPTH 50
-
-static void
-set_tag_style (GtkSourceContextEngine *ce,
- GtkTextTag *tag,
- const gchar *style_id)
-{
- GtkSourceStyle *style;
- const char *map_to;
- int guard = 0;
-
- g_return_if_fail (GTK_IS_TEXT_TAG (tag));
- g_return_if_fail (style_id != NULL);
-
- _gtk_source_style_apply (NULL, tag);
-
- if (ce->priv->style_scheme == NULL)
- return;
-
- map_to = style_id;
- style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, style_id);
-
- while (style == NULL)
- {
- GtkSourceStyleInfo *info;
-
- if (guard > MAX_STYLE_DEPENDENCY_DEPTH)
- {
- g_warning ("Potential circular dependency between styles detected for style '%s'", style_id);
- break;
- }
-
- ++guard;
-
- /* FIXME Style references really must be fixed, both parser for
- * sane use in lang files, and engine for safe use. */
- info = _gtk_source_language_get_style_info (ce->priv->ctx_data->lang, map_to);
-
- map_to = (info != NULL) ? info->map_to : NULL;
- if (!map_to)
- break;
-
- style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, map_to);
- }
-
- /* not having style is fine, since parser checks validity of every style reference,
- * so we don't need to spit a warning here */
- if (style != NULL)
- _gtk_source_style_apply (style, tag);
-}
-
-static GtkTextTag *
-create_tag (GtkSourceContextEngine *ce,
- const gchar *style_id)
-{
- GtkTextTag *new_tag;
-
- g_assert (style_id != NULL);
-
- new_tag = gtk_text_buffer_create_tag (ce->priv->buffer, NULL, NULL);
- /* It must have priority lower than user tags but still
- * higher than highlighting tags created before */
- gtk_text_tag_set_priority (new_tag, ce->priv->n_tags);
- set_tag_style (ce, new_tag, style_id);
- ce->priv->n_tags += 1;
-
- return new_tag;
-}
-
/* Find tag which has to be overridden. */
static GtkTextTag *
get_parent_tag (Context *context,
@@ -690,75 +531,23 @@ get_parent_tag (Context *context,
}
static GtkTextTag *
-get_tag_for_parent (GtkSourceContextEngine *ce,
+get_tag_for_parent (GtkSourceHighlighter *highlighter,
const char *style,
Context *parent)
{
- GSList *tags;
GtkTextTag *parent_tag = NULL;
- GtkTextTag *tag;
g_return_val_if_fail (style != NULL, NULL);
parent_tag = get_parent_tag (parent, style);
- tags = g_hash_table_lookup (ce->priv->tags, style);
-
- if (tags && (!parent_tag ||
- gtk_text_tag_get_priority (tags->data) > gtk_text_tag_get_priority (parent_tag)))
- {
- GSList *link;
-
- tag = tags->data;
-
- /* Now get the tag with lowest priority, so that tag lists do not grow
- * indefinitely. */
- for (link = tags->next; link != NULL; link = link->next)
- {
- if (parent_tag &&
- gtk_text_tag_get_priority (link->data) < gtk_text_tag_get_priority (parent_tag))
- break;
- tag = link->data;
- }
- }
- else
- {
- tag = create_tag (ce, style);
-
- tags = g_slist_prepend (tags, g_object_ref (tag));
- g_hash_table_insert (ce->priv->tags, g_strdup (style), tags);
-
-#ifdef ENABLE_DEBUG
- {
- GString *style_path = g_string_new (style);
- gint n;
-
- while (parent != NULL)
- {
- if (parent->style != NULL)
- {
- g_string_prepend (style_path, "/");
- g_string_prepend (style_path,
- parent->style);
- }
-
- parent = parent->parent;
- }
-
- tags = g_hash_table_lookup (ce->priv->tags, style);
- n = g_slist_length (tags);
- g_print ("created %d tag for style %s: %s\n", n, style, style_path->str);
- g_string_free (style_path, TRUE);
- }
-#endif
- }
- return tag;
+ return _gtk_source_highlighter_get_tag_for_style (highlighter, style, parent_tag);
}
-static GtkTextTag *
-get_subpattern_tag (GtkSourceContextEngine *ce,
- Context *context,
- SubPatternDefinition *sp_def)
+GtkTextTag *
+_gtk_source_context_engine_get_subpattern_tag (GtkSourceContextEngine *ce,
+ Context *context,
+ SubPatternDefinition *sp_def)
{
if (sp_def->style == NULL)
return NULL;
@@ -769,188 +558,29 @@ get_subpattern_tag (GtkSourceContextEngine *ce,
context->subpattern_tags = g_new0 (GtkTextTag*, context->definition->n_sub_patterns);
if (context->subpattern_tags[sp_def->index] == NULL)
- context->subpattern_tags[sp_def->index] = get_tag_for_parent (ce, sp_def->style, context);
+ context->subpattern_tags[sp_def->index] = get_tag_for_parent (ce->priv->highlighter,
+ sp_def->style,
+ context);
g_return_val_if_fail (context->subpattern_tags[sp_def->index] != NULL, NULL);
return context->subpattern_tags[sp_def->index];
}
-static GtkTextTag *
-get_context_tag (GtkSourceContextEngine *ce,
- Context *context)
+GtkTextTag *
+_gtk_source_context_engine_get_context_tag (GtkSourceContextEngine *ce,
+ Context *context)
{
if (context->style != NULL && context->tag == NULL)
- context->tag = get_tag_for_parent (ce,
+ context->tag = get_tag_for_parent (ce->priv->highlighter,
context->style,
context->parent);
return context->tag;
}
-static void
-apply_tags (GtkSourceContextEngine *ce,
- Segment *segment,
- gint start_offset,
- gint end_offset)
-{
- GtkTextTag *tag;
- GtkTextIter start_iter, end_iter;
- GtkTextBuffer *buffer = ce->priv->buffer;
- SubPattern *sp;
- Segment *child;
-
- g_assert (segment != NULL);
-
- if (SEGMENT_IS_INVALID (segment))
- return;
-
- if (segment->start_at >= end_offset || segment->end_at <= start_offset)
- return;
-
- start_offset = MAX (start_offset, segment->start_at);
- end_offset = MIN (end_offset, segment->end_at);
-
- tag = get_context_tag (ce, segment->context);
-
- if (tag != NULL)
- {
- gint style_start_at, style_end_at;
-
- style_start_at = start_offset;
- style_end_at = end_offset;
-
- if (HAS_OPTION (segment->context->definition, STYLE_INSIDE))
- {
- style_start_at = MAX (segment->start_at + segment->start_len, start_offset);
- style_end_at = MIN (segment->end_at - segment->end_len, end_offset);
- }
-
- if (style_start_at > style_end_at)
- {
- g_critical ("%s: oops", G_STRLOC);
- }
- else
- {
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, style_start_at);
- end_iter = start_iter;
- gtk_text_iter_forward_chars (&end_iter, style_end_at - style_start_at);
- gtk_text_buffer_apply_tag (ce->priv->buffer, tag, &start_iter, &end_iter);
- }
- }
-
- for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
- {
- if (sp->start_at >= start_offset && sp->end_at <= end_offset)
- {
- gint start = MAX (start_offset, sp->start_at);
- gint end = MIN (end_offset, sp->end_at);
-
- tag = get_subpattern_tag (ce, segment->context, sp->definition);
-
- if (tag != NULL)
- {
- gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
- end_iter = start_iter;
- gtk_text_iter_forward_chars (&end_iter, end - start);
- gtk_text_buffer_apply_tag (ce->priv->buffer, tag, &start_iter, &end_iter);
- }
- }
- }
-
- for (child = segment->children;
- child != NULL && child->start_at < end_offset;
- child = child->next)
- {
- if (child->end_at > start_offset)
- apply_tags (ce, child, start_offset, end_offset);
- }
-}
-
-/**
- * highlight_region:
- *
- * @ce: a #GtkSourceContextEngine.
- * @start: the beginning of the region to highlight.
- * @end: the end of the region to highlight.
- *
- * Highlights the specified region.
- */
-static void
-highlight_region (GtkSourceContextEngine *ce,
- GtkTextIter *start,
- GtkTextIter *end)
-{
-#ifdef ENABLE_PROFILE
- GTimer *timer;
-#endif
-
- if (gtk_text_iter_starts_line (end))
- gtk_text_iter_backward_char (end);
- if (gtk_text_iter_compare (start, end) >= 0)
- return;
-
-#ifdef ENABLE_PROFILE
- timer = g_timer_new ();
-#endif
-
- /* First we need to delete tags in the regions. */
- unhighlight_region (ce, start, end);
-
- apply_tags (ce, ce->priv->root_segment,
- gtk_text_iter_get_offset (start),
- gtk_text_iter_get_offset (end));
-
-#ifdef ENABLE_PROFILE
- g_print ("highlight (from %d to %d), %g ms elapsed\n",
- gtk_text_iter_get_offset (start),
- gtk_text_iter_get_offset (end),
- g_timer_elapsed (timer, NULL) * 1000);
- g_timer_destroy (timer);
-#endif
-}
-
-/**
- * ensure_highlighted:
- *
- * @ce: a #GtkSourceContextEngine.
- * @start: the beginning of the region to highlight.
- * @end: the end of the region to highlight.
- *
- * Updates text tags in reanalyzed parts of given area.
- * It applies tags according to whatever is in the syntax
- * tree currently, so highlighting may not be correct
- * (gtk_source_context_engine_update_highlight is the method
- * that actually ensures correct highlighting).
- */
-static void
-ensure_highlighted (GtkSourceContextEngine *ce,
- const GtkTextIter *start,
- const GtkTextIter *end)
+gboolean
+_gtk_source_context_get_style_inside (Context *context)
{
- GtkTextRegion *region;
- GtkTextRegionIterator reg_iter;
-
- /* Get the subregions not yet highlighted. */
- region = gtk_text_region_intersect (ce->priv->refresh_region, start, end);
-
- if (region == NULL)
- return;
-
- gtk_text_region_get_iterator (region, ®_iter, 0);
-
- /* Highlight all subregions from the intersection.
- * hopefully this will only be one subregion. */
- while (!gtk_text_region_iterator_is_end (®_iter))
- {
- GtkTextIter s, e;
- gtk_text_region_iterator_get_subregion (®_iter, &s, &e);
- highlight_region (ce, &s, &e);
- gtk_text_region_iterator_next (®_iter);
- }
-
- gtk_text_region_destroy (region, TRUE);
-
- /* Remove the just highlighted region. */
- gtk_text_region_subtract (ce->priv->refresh_region, start, end);
+ return HAS_OPTION (context->definition, STYLE_INSIDE);
}
static GtkTextTag *
@@ -1132,6 +762,11 @@ add_region_context_classes (GtkSourceContextEngine *ce,
}
}
+struct BufAndIters {
+ GtkTextBuffer *buffer;
+ const GtkTextIter *start, *end;
+};
+
static void
remove_region_context_class_cb (G_GNUC_UNUSED gpointer class,
GtkTextTag *tag,
@@ -2267,13 +1902,15 @@ gtk_source_context_engine_update_highlight (GtkSourceEngine *engine,
if (invalid_line < 0 || invalid_line > end_line)
{
- ensure_highlighted (ce, start, end);
+ _gtk_source_highlighter_ensure_highlight (ce->priv->highlighter, start, end);
+
}
else if (synchronous)
{
/* analyze whole region */
update_syntax (ce, end, 0);
- ensure_highlighted (ce, start, end);
+ _gtk_source_highlighter_ensure_highlight (ce->priv->highlighter, start, end);
+
}
else
{
@@ -2282,58 +1919,21 @@ gtk_source_context_engine_update_highlight (GtkSourceEngine *engine,
GtkTextIter valid_end = *start;
gtk_text_iter_set_line (&valid_end, invalid_line);
- ensure_highlighted (ce, start, &valid_end);
+ _gtk_source_highlighter_ensure_highlight (ce->priv->highlighter,
+ start,
+ &valid_end);
}
install_first_update (ce);
}
}
-/**
- * enable_highlight:
- *
- * @ce: a #GtkSourceContextEngine.
- * @enable: whether to enable highlighting.
- *
- * Whether to highlight (i.e. apply tags) analyzed area.
- * Note that this does not turn on/off the analyzis stuff,
- * it affects only text tags.
- */
-static void
-enable_highlight (GtkSourceContextEngine *ce,
- gboolean enable)
-{
- GtkTextIter start, end;
-
- if (!enable == !ce->priv->highlight)
- return;
-
- ce->priv->highlight = enable != 0;
- gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (ce->priv->buffer),
- &start, &end);
-
- if (enable)
- {
- gtk_text_region_add (ce->priv->refresh_region, &start, &end);
-
- refresh_range (ce, &start, &end);
- }
- else
- {
- unhighlight_region (ce, &start, &end);
- }
-}
-
static void
buffer_notify_highlight_syntax_cb (GtkSourceContextEngine *ce)
{
- gboolean highlight;
-
- g_object_get (ce->priv->buffer, "highlight-syntax", &highlight, NULL);
- enable_highlight (ce, highlight);
+ g_object_get (ce->priv->buffer, "highlight-syntax", &ce->priv->highlight, NULL);
}
-
/* IDLE WORKER CODE ------------------------------------------------------- */
/**
@@ -2467,23 +2067,6 @@ gtk_source_context_engine_error_quark (void)
}
static void
-remove_tags_hash_cb (G_GNUC_UNUSED gpointer style,
- GSList *tags,
- GtkTextTagTable *table)
-{
- GSList *l = tags;
-
- while (l != NULL)
- {
- gtk_text_tag_table_remove (table, l->data);
- g_object_unref (l->data);
- l = l->next;
- }
-
- g_slist_free (tags);
-}
-
-static void
remove_context_classes_hash_cb (G_GNUC_UNUSED gpointer class,
GtkTextTag *tag,
GtkTextTagTable *table)
@@ -2491,22 +2074,6 @@ remove_context_classes_hash_cb (G_GNUC_UNUSED gpointer class,
gtk_text_tag_table_remove (table, tag);
}
-/**
- * destroy_tags_hash:
- *
- * @ce: #GtkSourceContextEngine.
- *
- * Destroys syntax tags cache.
- */
-static void
-destroy_tags_hash (GtkSourceContextEngine *ce)
-{
- g_hash_table_foreach (ce->priv->tags, (GHFunc) remove_tags_hash_cb,
- gtk_text_buffer_get_tag_table (ce->priv->buffer));
- g_hash_table_destroy (ce->priv->tags);
- ce->priv->tags = NULL;
-}
-
static void
destroy_context_classes_hash (GtkSourceContextEngine *ce)
{
@@ -2540,8 +2107,8 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
if (ce->priv->buffer != NULL)
{
g_signal_handlers_disconnect_by_func (ce->priv->buffer,
- (gpointer) buffer_notify_highlight_syntax_cb,
- ce);
+ (gpointer) buffer_notify_highlight_syntax_cb,
+ ce);
if (ce->priv->first_update != 0)
g_source_remove (ce->priv->first_update);
@@ -2569,20 +2136,7 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
ce->priv->invalid_region.start = NULL;
ce->priv->invalid_region.end = NULL;
- /* this deletes tags from the tag table, therefore there is no need
- * in removing tags from the text (it may be very slow).
- * FIXME: don't we want to just destroy and forget everything when
- * the buffer is destroyed? Removing tags is still slower than doing
- * nothing. Caveat: if tag table is shared with other buffer, we do
- * need to remove tags. */
- destroy_tags_hash (ce);
- ce->priv->n_tags = 0;
-
destroy_context_classes_hash (ce);
-
- if (ce->priv->refresh_region != NULL)
- gtk_text_region_destroy (ce->priv->refresh_region, FALSE);
- ce->priv->refresh_region = NULL;
}
ce->priv->buffer = buffer;
@@ -2601,7 +2155,6 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
ce->priv->root_context = context_new (NULL, main_definition, NULL, NULL, FALSE);
ce->priv->root_segment = create_segment (ce, NULL, ce->priv->root_context, 0, 0, TRUE, NULL);
- ce->priv->tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
ce->priv->context_classes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
gtk_text_buffer_get_bounds (buffer, &start, &end);
@@ -2622,7 +2175,6 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
}
g_object_get (buffer, "highlight-syntax", &ce->priv->highlight, NULL);
- ce->priv->refresh_region = gtk_text_region_new (buffer);
g_signal_connect_swapped (buffer,
"notify::highlight-syntax",
@@ -2631,6 +2183,10 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
install_first_update (ce);
}
+
+ _gtk_source_highlighter_attach_buffer (ce->priv->highlighter,
+ ce->priv->buffer,
+ ce->priv->root_segment);
}
/**
@@ -2653,18 +2209,6 @@ disable_syntax_analysis (GtkSourceContextEngine *ce)
}
}
-static void
-set_tag_style_hash_cb (const char *style,
- GSList *tags,
- GtkSourceContextEngine *ce)
-{
- while (tags != NULL)
- {
- set_tag_style (ce, tags->data, style);
- tags = tags->next;
- }
-}
-
/**
* gtk_source_context_engine_set_style_scheme:
*
@@ -2685,14 +2229,7 @@ gtk_source_context_engine_set_style_scheme (GtkSourceEngine *engine,
ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
- if (scheme == ce->priv->style_scheme)
- return;
-
- if (ce->priv->style_scheme != NULL)
- g_object_unref (ce->priv->style_scheme);
-
- ce->priv->style_scheme = scheme ? g_object_ref (scheme) : NULL;
- g_hash_table_foreach (ce->priv->tags, (GHFunc) set_tag_style_hash_cb, ce);
+ _gtk_source_highlighter_set_style_scheme (ce->priv->highlighter, scheme);
}
static void
@@ -2708,7 +2245,8 @@ gtk_source_context_engine_finalize (GObject *object)
gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
}
- g_assert (!ce->priv->tags);
+ g_object_unref (ce->priv->highlighter);
+
g_assert (!ce->priv->root_context);
g_assert (!ce->priv->root_segment);
g_assert (!ce->priv->first_update);
@@ -2716,9 +2254,6 @@ gtk_source_context_engine_finalize (GObject *object)
_gtk_source_context_data_unref (ce->priv->ctx_data);
- if (ce->priv->style_scheme != NULL)
- g_object_unref (ce->priv->style_scheme);
-
G_OBJECT_CLASS (_gtk_source_context_engine_parent_class)->finalize (object);
}
@@ -2773,6 +2308,7 @@ _gtk_source_context_engine_new (GtkSourceContextData *ctx_data)
ce = g_object_new (GTK_SOURCE_TYPE_CONTEXT_ENGINE, NULL);
ce->priv->ctx_data = _gtk_source_context_data_ref (ctx_data);
+ ce->priv->highlighter = _gtk_source_highlighter_new (ce, ctx_data->lang);
return ce;
}
@@ -5535,7 +5071,8 @@ update_syntax (GtkSourceContextEngine *ce,
line_info_destroy (&line);
- gtk_text_region_add (ce->priv->refresh_region, &line_start, &line_end);
+ _gtk_source_highlighter_invalidate_region (ce->priv->highlighter,
+ &line_start, &line_end);
analyzed_end = line_end_offset;
invalid = get_invalid_segment (ce);
diff --git a/gtksourceview/gtksourcehighlighter.c b/gtksourceview/gtksourcehighlighter.c
new file mode 100644
index 0000000..86ed772
--- /dev/null
+++ b/gtksourceview/gtksourcehighlighter.c
@@ -0,0 +1,667 @@
+/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcehighlighter.c
+ *
+ * Copyright (C) 2003 - Gustavo GirÃldez <gustavo giraldez gmx net>
+ * Copyright (C) 2005, 2006 - Marco Barisione, Emanuele Aina
+ * Copyright (C) 2010 Jose Aliste <jose aliste gmail com>
+ * Copyright (C) 2011 Paolo Borelli <pborelli gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include "gtksourceview-i18n.h"
+#include "gtksourcehighlighter.h"
+#include "gtktextregion.h"
+#include "gtksourcelanguage-private.h"
+#include "gtksourcebuffer.h"
+#include "gtksourcestyle-private.h"
+#include "gtksourcecontextengine-private.h"
+
+#undef ENABLE_DEBUG
+#undef ENABLE_PROFILE
+
+G_DEFINE_TYPE (GtkSourceHighlighter, _gtk_source_highlighter, G_TYPE_OBJECT)
+
+struct _GtkSourceHighlighterPrivate
+{
+ GtkSourceContextEngine *engine;
+ GtkSourceLanguage *language;
+ GtkTextBuffer *buffer;
+ GtkSourceStyleScheme *style_scheme;
+
+ /* All tags indexed by style name: values are GSList's of tags, ref()'ed. */
+ GHashTable *tags;
+
+ /* Number of all syntax tags created by the highliter, needed to set correct
+ * tag priorities */
+ guint n_tags;
+
+ /* Whether or not to actually highlight the buffer. */
+ gboolean highlight;
+
+ /* Region covering the unhighlighted text. */
+ GtkTextRegion *refresh_region;
+
+ /* Pointer to the segment tree created by the syntax analyzer */
+ Segment *segment_tree;
+};
+
+struct BufAndIters {
+ GtkTextBuffer *buffer;
+ const GtkTextIter *start, *end;
+};
+
+static void
+unhighlight_region_cb (G_GNUC_UNUSED gpointer style,
+ GSList *tags,
+ gpointer user_data)
+{
+ struct BufAndIters *data = user_data;
+
+ while (tags != NULL)
+ {
+ gtk_text_buffer_remove_tag (data->buffer,
+ tags->data,
+ data->start,
+ data->end);
+ tags = tags->next;
+ }
+}
+
+static void
+unhighlight_region (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ struct BufAndIters data;
+
+ data.buffer = highlighter->priv->buffer;
+ data.start = start;
+ data.end = end;
+
+ if (gtk_text_iter_equal (start, end))
+ return;
+
+ g_hash_table_foreach (highlighter->priv->tags, (GHFunc) unhighlight_region_cb, &data);
+}
+
+#define MAX_STYLE_DEPENDENCY_DEPTH 50
+
+static void
+set_tag_style (GtkSourceHighlighter *highlighter,
+ GtkTextTag *tag,
+ const gchar *style_id)
+{
+ GtkSourceStyle *style;
+ const char *map_to;
+ int guard = 0;
+
+ g_return_if_fail (GTK_IS_TEXT_TAG (tag));
+ g_return_if_fail (style_id != NULL);
+
+ _gtk_source_style_apply (NULL, tag);
+
+ if (highlighter->priv->style_scheme == NULL)
+ return;
+
+ map_to = style_id;
+ style = gtk_source_style_scheme_get_style (highlighter->priv->style_scheme, style_id);
+
+ while (style == NULL)
+ {
+ GtkSourceStyleInfo *info;
+
+ if (guard > MAX_STYLE_DEPENDENCY_DEPTH)
+ {
+ g_warning ("Potential circular dependency between styles detected for style '%s'", style_id);
+ break;
+ }
+
+ ++guard;
+
+ /* FIXME Style references really must be fixed, both parser for
+ * sane use in lang files, and highlighter for safe use. */
+ info = _gtk_source_language_get_style_info (highlighter->priv->language, map_to);
+
+ map_to = (info != NULL) ? info->map_to : NULL;
+
+ if (!map_to)
+ break;
+
+ style = gtk_source_style_scheme_get_style (highlighter->priv->style_scheme, map_to);
+ }
+
+ /* not having style is fine, since parser checks validity of every style reference,
+ * so we don't need to spit a warning here */
+ if (style != NULL)
+ _gtk_source_style_apply (style, tag);
+}
+
+static void
+apply_tags (GtkSourceHighlighter *highlighter,
+ Segment *segment,
+ gint start_offset,
+ gint end_offset)
+{
+ GtkTextTag *tag;
+ GtkTextIter start_iter, end_iter;
+ GtkTextBuffer *buffer = highlighter->priv->buffer;
+ SubPattern *sp;
+ Segment *child;
+
+ g_assert (segment != NULL);
+
+ /* Non-annotated segments are invalid.*/
+ if (SEGMENT_IS_INVALID (segment))
+ return;
+
+ if (segment->start_at >= end_offset || segment->end_at <= start_offset)
+ return;
+
+ start_offset = MAX (start_offset, segment->start_at);
+ end_offset = MIN (end_offset, segment->end_at);
+
+ tag = _gtk_source_context_engine_get_context_tag (highlighter->priv->engine,
+ segment->context);
+
+ if (tag != NULL)
+ {
+ gint style_start_at, style_end_at;
+
+ style_start_at = start_offset;
+ style_end_at = end_offset;
+
+ if (_gtk_source_context_get_style_inside (segment->context))
+ {
+ style_start_at = MAX (segment->start_at + segment->start_len, start_offset);
+ style_end_at = MIN (segment->end_at - segment->end_len, end_offset);
+ }
+
+ if (style_start_at > style_end_at)
+ {
+ g_critical ("%s: oops", G_STRLOC);
+ }
+ else
+ { /* FIXME: We should cache the start_at and end_at so we only apply the tag
+ where is needed, instead of erasing all the highlighting */
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, style_start_at);
+ end_iter = start_iter;
+ gtk_text_iter_forward_chars (&end_iter, style_end_at - style_start_at);
+ gtk_text_buffer_apply_tag (highlighter->priv->buffer, tag, &start_iter, &end_iter);
+ }
+ }
+
+ for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
+ {
+ if (sp->start_at >= start_offset && sp->end_at <= end_offset)
+ {
+ gint start = MAX (start_offset, sp->start_at);
+ gint end = MIN (end_offset, sp->end_at);
+
+ tag = _gtk_source_context_engine_get_subpattern_tag (highlighter->priv->engine,
+ segment->context,
+ sp->definition);
+
+ if (tag != NULL)
+ {
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
+ end_iter = start_iter;
+ gtk_text_iter_forward_chars (&end_iter, end - start);
+ gtk_text_buffer_apply_tag (highlighter->priv->buffer, tag, &start_iter, &end_iter);
+ }
+ }
+ }
+
+ for (child = segment->children;
+ child != NULL && child->start_at < end_offset;
+ child = child->next)
+ {
+ if (child->end_at > start_offset)
+ apply_tags (highlighter, child, start_offset, end_offset);
+ }
+}
+
+/**
+ * highlight_region:
+ *
+ * @highlighter: a #GtkSourceHighlighter.
+ * @start: the beginning of the region to highlight.
+ * @end: the end of the region to highlight.
+ *
+ * Highlights the specified region.
+ */
+static void
+highlight_region (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+#ifdef ENABLE_PROFILE
+ GTimer *timer;
+#endif
+ GtkTextIter real_end;
+
+ real_end = *end;
+ if (gtk_text_iter_starts_line (&real_end))
+ gtk_text_iter_backward_char (&real_end);
+ if (gtk_text_iter_compare (start, &real_end) >= 0)
+ return;
+
+#ifdef ENABLE_PROFILE
+ timer = g_timer_new ();
+#endif
+
+ /* First we need to delete tags in the regions. */
+ unhighlight_region (highlighter, start, &real_end);
+
+ apply_tags (highlighter, highlighter->priv->segment_tree,
+ gtk_text_iter_get_offset (start),
+ gtk_text_iter_get_offset (&real_end));
+
+ g_signal_emit_by_name (highlighter->priv->buffer,
+ "highlight_updated",
+ start,
+ &real_end);
+
+#ifdef ENABLE_PROFILE
+ g_print ("highlight (from %d to %d), %g ms elapsed\n",
+ gtk_text_iter_get_offset (start),
+ gtk_text_iter_get_offset (&real_end),
+ g_timer_elapsed (timer, NULL) * 1000);
+ g_timer_destroy (timer);
+#endif
+}
+
+static GtkTextTag *
+create_tag (GtkSourceHighlighter *highlighter,
+ const gchar *style_id)
+{
+ GtkTextTag *new_tag;
+
+ g_assert (style_id != NULL);
+
+ new_tag = gtk_text_buffer_create_tag (highlighter->priv->buffer, NULL, NULL);
+
+ /* It must have priority lower than user tags but still
+ * higher than highlighting tags created before */
+ gtk_text_tag_set_priority (new_tag, highlighter->priv->n_tags);
+ highlighter->priv->n_tags += 1;
+ set_tag_style (highlighter, new_tag, style_id);
+
+ return new_tag;
+}
+
+GtkTextTag *
+_gtk_source_highlighter_get_tag_for_style (GtkSourceHighlighter *highlighter,
+ const gchar *style,
+ GtkTextTag *parent_tag)
+{
+ GSList *tags;
+ GtkTextTag *tag;
+
+ tags = g_hash_table_lookup (highlighter->priv->tags, style);
+
+ if (tags && (!parent_tag ||
+ gtk_text_tag_get_priority (tags->data) > gtk_text_tag_get_priority (parent_tag)))
+ {
+ GSList *link;
+
+ tag = tags->data;
+
+ /* Now get the tag with lowest priority, so that tag lists do not grow
+ * indefinitely. */
+ for (link = tags->next; link != NULL; link = link->next)
+ {
+ if (parent_tag &&
+ gtk_text_tag_get_priority (link->data) < gtk_text_tag_get_priority (parent_tag))
+ break;
+ tag = link->data;
+ }
+ }
+ else
+ {
+ tag = create_tag (highlighter, style);
+
+ tags = g_slist_prepend (tags, g_object_ref (tag));
+ g_hash_table_insert (highlighter->priv->tags, g_strdup (style), tags);
+
+#ifdef ENABLE_DEBUG
+ {
+ GString *style_path = g_string_new (style);
+ gint n;
+
+ while (parent != NULL)
+ {
+ if (parent->style != NULL)
+ {
+ g_string_prepend (style_path, "/");
+ g_string_prepend (style_path,
+ parent->style);
+ }
+
+ parent = parent->parent;
+ }
+
+ tags = g_hash_table_lookup (highlighter->priv->tags, style);
+ n = g_slist_length (tags);
+ g_print ("created %d tag for style %s: %s\n", n, style, style_path->str);
+ g_string_free (style_path, TRUE);
+ }
+#endif
+ }
+
+ return tag;
+}
+
+/**
+ * _gtk_source_highlighter_invalidate_region
+ *
+ * @highlighter:
+ * @start: the start of the invalidated_region
+ * @end: the end of the invalidated_region
+ *
+ * This method is called by the context_engine to let the highlighter
+ * know that the syntax tree has been updated between @start and @end,
+ * and hence, the highlighting of the region is invalid.
+ */
+void
+_gtk_source_highlighter_invalidate_region (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ if (!highlighter->priv->highlight)
+ return;
+
+ gtk_text_region_add (highlighter->priv->refresh_region, start, end);
+}
+
+/**
+ * ensure_highlighted:
+ *
+ * @highlighter: a #GtkSourceHighlighter.
+ * @start: the beginning of the region to highlight.
+ * @end: the end of the region to highlight.
+ *
+ * Updates text tags in reanalyzed parts of given area.
+ * It applies tags according to whatever is in the syntax
+ * tree currently, so highlighting may not be correct
+ * (gtk_source_highlighter_update_highlight is the method
+ * that actually ensures correct highlighting).
+ */
+void
+_gtk_source_highlighter_ensure_highlight (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ GtkTextRegion *region;
+ GtkTextRegionIterator reg_iter;
+
+ if (!highlighter->priv->highlight)
+ return;
+
+ /* Get the subregions not yet highlighted. */
+ region = gtk_text_region_intersect (highlighter->priv->refresh_region, start, end);
+ if (region == NULL)
+ return;
+
+ gtk_text_region_get_iterator (region, ®_iter, 0);
+
+ /* Highlight all subregions from the intersection.
+ * hopefully this will only be one subregion. */
+ while (!gtk_text_region_iterator_is_end (®_iter))
+ {
+ GtkTextIter s, e;
+ gtk_text_region_iterator_get_subregion (®_iter, &s, &e);
+ highlight_region (highlighter, &s, &e);
+ gtk_text_region_iterator_next (®_iter);
+ }
+
+ gtk_text_region_destroy (region, TRUE);
+
+ /* Remove the just highlighted region. */
+ gtk_text_region_subtract (highlighter->priv->refresh_region, start, end);
+}
+
+static void
+set_tag_style_hash_cb (const char *style,
+ GSList *tags,
+ GtkSourceHighlighter *highlighter)
+{
+ while (tags != NULL)
+ {
+ set_tag_style (highlighter, tags->data, style);
+ tags = tags->next;
+ }
+}
+
+/*
+ * enable_highlight:
+ *
+ * @highlighter: a #GtkSourceHighlighter.
+ * @enable: whether to enable highlighting.
+ *
+ * Whether to highlight (i.e. apply tags) analyzed area.
+ * Note that this does not turn on/off the analysis stuff,
+ * it affects only text tags.
+ */
+static void
+enable_highlight (GtkSourceHighlighter *highlighter,
+ gboolean enable)
+{
+ GtkTextIter start, end;
+
+ if (!enable == !highlighter->priv->highlight)
+ return;
+
+ highlighter->priv->highlight = enable != 0;
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (highlighter->priv->buffer),
+ &start, &end);
+
+ if (enable)
+ {
+ gtk_text_region_add (highlighter->priv->refresh_region, &start, &end);
+ //g_hash_table_foreach (highlighter->priv->tags, (GHFunc) set_tag_style_hash_cb, highlighter);
+ //GtkTextIter real_end;
+
+ //if (gtk_text_iter_equal (start, end))
+ //return;
+
+ /* Here we need to make sure we do not make it redraw next line */
+ // real_end = *end;
+ // if (gtk_text_iter_starts_line (&real_end))
+ // {
+ /* I don't quite like this here, but at least it won't jump into
+ * * the middle of \r\n */
+ // gtk_text_iter_backward_cursor_position (&real_end);
+ // }
+
+ g_signal_emit_by_name (highlighter->priv->buffer,
+ "highlight_updated",
+ start,
+ end);
+ }
+ else
+ {
+ unhighlight_region (highlighter, &start, &end);
+ }
+}
+
+static void
+buffer_notify_highlight_syntax_cb (GtkSourceHighlighter *highlighter)
+{
+ gboolean highlight;
+
+ g_object_get (highlighter->priv->buffer, "highlight-syntax", &highlight, NULL);
+ enable_highlight (highlighter, highlight);
+}
+
+static void
+remove_tags_hash_cb (G_GNUC_UNUSED gpointer style,
+ GSList *tags,
+ GtkTextTagTable *table)
+{
+ GSList *l = tags;
+
+ while (l != NULL)
+ {
+ gtk_text_tag_table_remove (table, l->data);
+ g_object_unref (l->data);
+ l = l->next;
+ }
+
+ g_slist_free (tags);
+}
+
+static void
+destroy_tags_hash (GtkSourceHighlighter *highlighter)
+{
+ g_hash_table_foreach (highlighter->priv->tags, (GHFunc) remove_tags_hash_cb,
+ gtk_text_buffer_get_tag_table (highlighter->priv->buffer));
+ g_hash_table_destroy (highlighter->priv->tags);
+ highlighter->priv->tags = NULL;
+}
+
+/*
+ * gtk_source_highlighter_attach_buffer:
+ *
+ * @highlighter: #GtkSourceHighlighter.
+ * @buffer: buffer.
+ *
+ * Detaches highlighter from previous buffer, and attaches to @buffer if
+ * it's not %NULL.
+ */
+void
+_gtk_source_highlighter_attach_buffer (GtkSourceHighlighter *highlighter,
+ GtkTextBuffer *buffer,
+ Segment *root_segment)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HIGHLIGHTER (highlighter));
+
+ if (highlighter->priv->buffer == buffer)
+ return;
+
+ /* Detach previous buffer if there is one. */
+ if (highlighter->priv->buffer != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (highlighter->priv->buffer,
+ (gpointer) buffer_notify_highlight_syntax_cb,
+ highlighter);
+
+ /* the root_segment is owned by the engine. */
+ highlighter->priv->segment_tree = NULL;
+
+ if (highlighter->priv->refresh_region != NULL)
+ gtk_text_region_destroy (highlighter->priv->refresh_region, FALSE);
+ highlighter->priv->refresh_region = NULL;
+
+ /* this deletes tags from the tag table, therefore there is no need
+ * in removing tags from the text (it may be very slow).
+ * FIXME: don't we want to just destroy and forget everything when
+ * the buffer is destroyed? Removing tags is still slower than doing
+ * nothing. Caveat: if tag table is shared with other buffer, we do
+ * need to remove tags. */
+ destroy_tags_hash (highlighter);
+ highlighter->priv->n_tags = 0;
+ }
+
+ highlighter->priv->buffer = buffer;
+
+ if (buffer != NULL)
+ {
+ highlighter->priv->segment_tree = root_segment;
+ highlighter->priv->tags = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, NULL);
+
+ g_object_get (highlighter->priv->buffer, "highlight-syntax",
+ &highlighter->priv->highlight, NULL);
+
+ g_signal_connect_swapped (buffer,
+ "notify::highlight-syntax",
+ G_CALLBACK (buffer_notify_highlight_syntax_cb),
+ highlighter);
+ highlighter->priv->refresh_region = gtk_text_region_new (buffer);
+ }
+}
+
+/**
+ * gtk_source_highlighter_set_style_scheme:
+ * @highlighter: #GtkSourceHighlighter.
+ * @scheme: #GtkSourceStyleScheme to set.
+ *
+ * Sets current style scheme, updates tag styles and everything.
+ */
+void
+_gtk_source_highlighter_set_style_scheme (GtkSourceHighlighter *highlighter,
+ GtkSourceStyleScheme *scheme)
+{
+ g_return_if_fail (GTK_SOURCE_IS_HIGHLIGHTER (highlighter));
+ g_return_if_fail (GTK_SOURCE_IS_STYLE_SCHEME (scheme) || scheme == NULL);
+
+ if (scheme != highlighter->priv->style_scheme)
+ {
+ if (highlighter->priv->style_scheme != NULL)
+ g_object_unref (highlighter->priv->style_scheme);
+
+ highlighter->priv->style_scheme = scheme ? g_object_ref (scheme) : NULL;
+ g_hash_table_foreach (highlighter->priv->tags, (GHFunc) set_tag_style_hash_cb, highlighter);
+ }
+}
+
+static void
+gtk_source_highlighter_finalize (GObject *object)
+{
+ GtkSourceHighlighter *highlighter = GTK_SOURCE_HIGHLIGHTER (object);
+
+ g_assert (!highlighter->priv->tags);
+ g_assert (!highlighter->priv->segment_tree);
+
+ if (highlighter->priv->style_scheme != NULL)
+ g_object_unref (highlighter->priv->style_scheme);
+
+ G_OBJECT_CLASS (_gtk_source_highlighter_parent_class)->finalize (object);
+}
+
+static void
+_gtk_source_highlighter_class_init (GtkSourceHighlighterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_source_highlighter_finalize;
+ g_type_class_add_private (object_class, sizeof (GtkSourceHighlighterPrivate));
+}
+
+static void
+_gtk_source_highlighter_init (GtkSourceHighlighter *highlighter)
+{
+ highlighter->priv = G_TYPE_INSTANCE_GET_PRIVATE (highlighter, GTK_TYPE_SOURCE_HIGHLIGHTER,
+ GtkSourceHighlighterPrivate);
+}
+
+GtkSourceHighlighter *
+_gtk_source_highlighter_new (GtkSourceContextEngine *ce,
+ GtkSourceLanguage *language)
+{
+ GtkSourceHighlighter *highlighter;
+
+ highlighter = g_object_new (GTK_TYPE_SOURCE_HIGHLIGHTER, NULL);
+ highlighter->priv->engine = ce;
+ highlighter->priv->language = language;
+
+ return highlighter;
+}
+
diff --git a/gtksourceview/gtksourcehighlighter.h b/gtksourceview/gtksourcehighlighter.h
new file mode 100644
index 0000000..7fae178
--- /dev/null
+++ b/gtksourceview/gtksourcehighlighter.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ * gtksourcehighlighter.h
+ *
+ * Copyright (C) 2003 - Gustavo GirÃldez
+ * Copyright (C) 2005 - Marco Barisione, Emanuele Aina
+ * Copyright (C) 2010 Jose Aliste <jose aliste gmail com>
+ * Copyright (C) 2011 Paolo Borelli <pborelli gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_SOURCE_HIGHLIGHTER_H__
+#define __GTK_SOURCE_HIGHLIGHTER_H__
+
+#include <gtksourceview/gtksourcecontextengine.h>
+#include <gtksourceview/gtksourcecontextengine-private.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_HIGHLIGHTER (_gtk_source_highlighter_get_type ())
+#define GTK_SOURCE_HIGHLIGHTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_HIGHLIGHTER, GtkSourceHighlighter))
+#define GTK_SOURCE_HIGHLIGHTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_HIGHLIGHTER, GtkSourceHighlighterClass))
+#define GTK_SOURCE_IS_HIGHLIGHTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_HIGHLIGHTER))
+#define GTK_SOURCE_IS_HIGHLIGHTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_HIGHLIGHTER))
+#define GTK_SOURCE_HIGHLIGHTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_HIGHLIGHTER, GtkSourceHighlighterClass))
+
+typedef struct _GtkSourceHighlighter GtkSourceHighlighter;
+typedef struct _GtkSourceHighlighterClass GtkSourceHighlighterClass;
+typedef struct _GtkSourceHighlighterPrivate GtkSourceHighlighterPrivate;
+
+struct _GtkSourceHighlighter
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GtkSourceHighlighterPrivate *priv;
+};
+
+struct _GtkSourceHighlighterClass
+{
+ GObjectClass parent_class;
+};
+
+GType _gtk_source_highlighter_get_type (void) G_GNUC_CONST;
+
+GtkSourceHighlighter * _gtk_source_highlighter_new (GtkSourceContextEngine *engine,
+ GtkSourceLanguage *language);
+void _gtk_source_highlighter_set_style_scheme (GtkSourceHighlighter *highlighter,
+ GtkSourceStyleScheme *scheme);
+void _gtk_source_highlighter_set_styles_map (GtkSourceHighlighter *highlighter,
+ GHashTable *styles);
+void _gtk_source_highlighter_attach_buffer (GtkSourceHighlighter *highlighter,
+ GtkTextBuffer *buffer,
+ Segment *root_segment);
+void _gtk_source_highlighter_ensure_highlight (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+void _gtk_source_highlighter_invalidate_region (GtkSourceHighlighter *highlighter,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+GtkTextTag * _gtk_source_highlighter_get_tag_for_style (GtkSourceHighlighter *highlighter,
+ const gchar *style,
+ GtkTextTag *parent_tag);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_HIGHLIGHTER_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]