[gtksourceview/wip/highlighter-split: 1/2] Split highlighter out of the engine.



commit b84da2d9d744f0f746b570af7f833c303401d008
Author: Paolo Borelli <pborelli gnome org>
Date:   Tue Dec 27 15:17:17 2011 +0100

    Split highlighter out of the engine.

 gtksourceview/Makefile.am                      |    3 +
 gtksourceview/gtksourcecontextengine-private.h |   86 +++
 gtksourceview/gtksourcecontextengine.c         |  537 ++------------------
 gtksourceview/gtksourcehighlighter.c           |  664 ++++++++++++++++++++++++
 gtksourceview/gtksourcehighlighter.h           |   79 +++
 5 files changed, 877 insertions(+), 492 deletions(-)
---
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index ef5280a..33113db 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -50,7 +50,9 @@ libgtksourceview_private_headers = \
        gtksourcecompletion-private.h           \
        gtksourcecompletionutils.h              \
        gtksourcecontextengine.h                \
+       gtksourcecontextengine-private.h        \
        gtksourceengine.h                       \
+       gtksourcehighlighter.h                  \
        gtksourcegutter-private.h               \
        gtksourcegutterrendererlines.h          \
        gtksourcegutterrenderermarks.h          \
@@ -70,6 +72,7 @@ libgtksourceview_private_c_files = \
        gtksourcecompletionutils.c      \
        gtksourcecontextengine.c        \
        gtksourceengine.c               \
+       gtksourcehighlighter.c          \
        gtksourcepixbufhelper.c         \
        gtksourcelanguage-parser-1.c    \
        gtksourcelanguage-parser-2.c    \
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 907ab3c..3b458fe 100644
--- a/gtksourceview/gtksourcecontextengine.c
+++ b/gtksourceview/gtksourcecontextengine.c
@@ -24,6 +24,7 @@
 #include <glib.h>
 #include "gtksourceview-i18n.h"
 #include "gtksourcecontextengine.h"
+#include "gtksourcecontextengine-private.h"
 #include "gtktextregion.h"
 #include "gtksourcelanguage.h"
 #include "gtksourcelanguage-private.h"
@@ -32,6 +33,7 @@
 #include "gtksourcestyle-private.h"
 #include "gtksourceview-utils.h"
 #include "gtksourcestylescheme.h"
+#include "gtksourcehighlighter.h"
 
 #undef ENABLE_DEBUG
 #undef ENABLE_PROFILE
@@ -106,16 +108,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;
@@ -141,11 +138,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
 {
@@ -286,42 +278,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
@@ -389,16 +346,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;
 
@@ -408,9 +360,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;
@@ -561,110 +510,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)
-       {
-               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. */
-               map_to = gtk_source_language_get_style_fallback (ce->priv->ctx_data->lang, map_to);
-               if (map_to == NULL)
-                       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,
@@ -688,75 +533,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;
@@ -767,178 +560,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);
-       }
-}
-
-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, &reg_iter, 0);
-
-       /* Highlight all subregions from the intersection.
-        * hopefully this will only be one subregion. */
-       while (!gtk_text_region_iterator_is_end (&reg_iter))
-       {
-               GtkTextIter s, e;
-               gtk_text_region_iterator_get_subregion (&reg_iter, &s, &e);
-               highlight_region (ce, &s, &e);
-               gtk_text_region_iterator_next (&reg_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 *
@@ -1120,6 +764,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,
@@ -2230,13 +1879,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
        {
@@ -2245,57 +1896,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 ------------------------------------------------------- */
 
 /**
@@ -2416,23 +2031,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)
@@ -2440,21 +2038,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)
 {
@@ -2487,8 +2070,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);
@@ -2516,20 +2099,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;
@@ -2548,7 +2118,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);
@@ -2569,7 +2138,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",
@@ -2578,6 +2146,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);
 }
 
 /**
@@ -2599,18 +2171,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:
  * @engine: #GtkSourceContextEngine.
@@ -2630,14 +2190,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
@@ -2653,7 +2206,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);
@@ -2661,9 +2215,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);
 }
 
@@ -2718,6 +2269,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;
 }
@@ -5450,7 +5002,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..60d0c1f
--- /dev/null
+++ b/gtksourceview/gtksourcehighlighter.c
@@ -0,0 +1,664 @@
+/* -*- 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"
+#include "gtksourcestylescheme.h"
+#include "gtksourcelanguage.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)
+       {
+               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. */
+               map_to = gtk_source_language_get_style_fallback (highlighter->priv->language, map_to);
+               if (map_to == NULL)
+                       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, &reg_iter, 0);
+
+       /* Highlight all subregions from the intersection.
+        * hopefully this will only be one subregion. */
+       while (!gtk_text_region_iterator_is_end (&reg_iter))
+       {
+               GtkTextIter s, e;
+               gtk_text_region_iterator_get_subregion (&reg_iter, &s, &e);
+               highlight_region (highlighter, &s, &e);
+               gtk_text_region_iterator_next (&reg_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]