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



commit 0b1bfd522159d5ef64c25a04eec5686f199d0bf4
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         |  573 +++------------------
 gtksourceview/gtksourcecontextengine.h         |    3 +-
 gtksourceview/gtksourcehighlighter.c           |  667 ++++++++++++++++++++++++
 gtksourceview/gtksourcehighlighter.h           |   79 +++
 gtksourceview/gtksourceview-marshal.list       |    1 +
 7 files changed, 900 insertions(+), 514 deletions(-)
---
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index e2cb296..917afa4 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -61,7 +61,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 		\
@@ -97,6 +99,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 81a93fc..cd0d895 100644
--- a/gtksourceview/gtksourcecontextengine.c
+++ b/gtksourceview/gtksourcecontextengine.c
@@ -22,10 +22,12 @@
 
 #include "gtksourceview-i18n.h"
 #include "gtksourcecontextengine.h"
+#include "gtksourcecontextengine-private.h"
 #include "gtktextregion.h"
 #include "gtksourcelanguage-private.h"
 #include "gtksourcebuffer.h"
 #include "gtksourcestyle-private.h"
+#include "gtksourcehighlighter.h"
 
 #include <glib.h>
 
@@ -108,7 +110,7 @@
 
 #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)
 
@@ -117,10 +119,6 @@
 typedef struct _RegexInfo RegexInfo;
 typedef struct _RegexAndMatch RegexAndMatch;
 typedef struct _Regex Regex;
-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;
@@ -146,11 +144,6 @@ typedef enum {
 	CONTEXT_TYPE_CONTAINER
 } ContextType;
 
-typedef enum {
-	SUB_PATTERN_WHERE_DEFAULT = 0,
-	SUB_PATTERN_WHERE_START,
-	SUB_PATTERN_WHERE_END
-} SubPatternWhere;
 
 struct _RegexInfo
 {
@@ -315,42 +308,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
@@ -418,28 +376,20 @@ 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;
 
 	/* Whether or not to actually highlight the buffer. */
 	gboolean		 highlight;
 
-	/* Whether highlighting was disabled because of errors. */
+	/* 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;
@@ -590,114 +540,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,
@@ -721,75 +563,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;
@@ -800,193 +590,34 @@ 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, &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 *
 get_context_class_tag (GtkSourceContextEngine *ce,
-                       gchar const            *name)
+		       gchar const            *name)
 {
 	GtkTextTag *ret;
 
@@ -1121,8 +752,7 @@ add_region_context_classes (GtkSourceContextEngine *ce,
 	start_offset = MAX (start_offset, segment->start_at);
 	end_offset = MIN (end_offset, segment->end_at);
 
-	context_classes = get_context_classes (ce,
-	                                       segment->context);
+	context_classes = get_context_classes (ce, segment->context);
 
 	if (context_classes != NULL)
 	{
@@ -1140,7 +770,7 @@ add_region_context_classes (GtkSourceContextEngine *ce,
 			gint end = MIN (end_offset, sp->end_at);
 
 			context_classes = get_subpattern_context_classes (ce,
-			                                                  segment->context,
+			                                                 segment->context,
 			                                                  sp->definition);
 
 			if (context_classes != NULL)
@@ -1164,6 +794,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,
@@ -2299,68 +1934,39 @@ 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
 	{
 		if (gtk_text_iter_get_line (start) < invalid_line)
 		{
 			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);
+	ce->priv->highlight = highlight;
 }
 
 
@@ -2496,22 +2102,7 @@ gtk_source_context_engine_error_quark (void)
 	return err_q;
 }
 
-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,
@@ -2521,21 +2112,7 @@ 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)
@@ -2569,10 +2146,9 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
 	/* Detach previous buffer if there is one. */
 	if (ce->priv->buffer != NULL)
 	{
-		g_signal_handlers_disconnect_by_func (ce->priv->buffer,
-						      (gpointer) buffer_notify_highlight_syntax_cb,
-						      ce);
-
+		 g_signal_handlers_disconnect_by_func (ce->priv->buffer,			
+						       (gpointer) buffer_notify_highlight_syntax_cb,			
+						       ce);
 		if (ce->priv->first_update != 0)
 			g_source_remove (ce->priv->first_update);
 		if (ce->priv->incremental_update != 0)
@@ -2599,20 +2175,11 @@ 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;
+		_gtk_source_highlighter_attach_buffer (ce->priv->highlighter,
+						       NULL, 
+						       NULL);
 
 		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;
@@ -2631,7 +2198,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);
@@ -2651,20 +2217,21 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
 			ce->priv->invalid_region.delta = 0;
 		}
 
-		g_object_get (ce->priv->buffer, "highlight-syntax", &ce->priv->highlight, NULL);
-		ce->priv->refresh_region = gtk_text_region_new (buffer);
+		g_object_get (buffer, "highlight-syntax", &(ce->priv->highlight), NULL);
 
 		g_signal_connect_swapped (buffer,
 					  "notify::highlight-syntax",
 					  G_CALLBACK (buffer_notify_highlight_syntax_cb),
 					  ce);
-
+		_gtk_source_highlighter_attach_buffer (ce->priv->highlighter,
+						       ce->priv->buffer, 
+						       ce->priv->root_segment);
 		install_first_update (ce);
 	}
 }
 
 /**
- * disable_highlighting:
+ * disable_syntax_analysis:
  *
  * @ce: #GtkSourceContextEngine.
  *
@@ -2673,7 +2240,7 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
  * text editor).
  */
 static void
-disable_highlighting (GtkSourceContextEngine *ce)
+disable_syntax_analysis (GtkSourceContextEngine *ce)
 {
 	if (!ce->priv->disabled)
 	{
@@ -2683,18 +2250,6 @@ disable_highlighting (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:
  *
@@ -2715,14 +2270,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
@@ -2738,7 +2286,6 @@ gtk_source_context_engine_finalize (GObject *object)
 		gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
 	}
 
-	g_assert (!ce->priv->tags);
 	g_assert (!ce->priv->root_context);
 	g_assert (!ce->priv->root_segment);
 	g_assert (!ce->priv->first_update);
@@ -2746,8 +2293,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);
 }
@@ -2803,6 +2348,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;
 }
@@ -4914,7 +4460,7 @@ analyze_line (GtkSourceContextEngine *ce,
 			g_critical ("%s",
 			            _("Highlighting a single line took too much time, "
 				      "syntax highlighting will be disabled"));
-			disable_highlighting (ce);
+			disable_syntax_analysis (ce);
 			break;
 		}
 
@@ -5863,7 +5409,7 @@ update_syntax (GtkSourceContextEngine *ce,
 	analyzed_end = line_end_offset;
 
 	timer = g_timer_new ();
-
+	printf ("Start analyzing\n");
 	while (TRUE)
 	{
 		LineInfo line;
@@ -5929,7 +5475,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);
 
@@ -6015,6 +5562,8 @@ update_syntax (GtkSourceContextEngine *ce,
 
 	refresh_range (ce, &start_iter, &end_iter);
 
+ 	printf ("refresh range %d to %d\n", gtk_text_iter_get_offset (&start_iter), gtk_text_iter_get_offset (&end_iter));
+
 	PROFILE (g_print ("analyzed %d chars from %d to %d in %fms\n",
 			  analyzed_end - start_offset, start_offset, analyzed_end,
 			  g_timer_elapsed (timer, NULL) * 1000));
diff --git a/gtksourceview/gtksourcecontextengine.h b/gtksourceview/gtksourcecontextengine.h
index 4755f59..64e600a 100644
--- a/gtksourceview/gtksourcecontextengine.h
+++ b/gtksourceview/gtksourcecontextengine.h
@@ -82,7 +82,8 @@ GtkSourceContextClass *
 
 void		gtk_source_context_class_free		(GtkSourceContextClass *cclass);
 
-GtkSourceContextEngine *_gtk_source_context_engine_new  (GtkSourceContextData	*data);
+GtkSourceContextEngine *
+		_gtk_source_context_engine_new  (GtkSourceContextData	*data);
 
 gboolean	 _gtk_source_context_data_define_context
 							(GtkSourceContextData	 *data,
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, &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__ */
diff --git a/gtksourceview/gtksourceview-marshal.list b/gtksourceview/gtksourceview-marshal.list
index 1ef9b81..76c1d51 100644
--- a/gtksourceview/gtksourceview-marshal.list
+++ b/gtksourceview/gtksourceview-marshal.list
@@ -21,3 +21,4 @@ BOOLEAN:BOXED,BOXED,INT,INT,OBJECT
 VOID:BOXED,BOXED,FLAGS
 BOOLEAN:BOXED,BOXED,BOXED
 STRING:OBJECT
+VOID:BOXED,BOXED,POINTER



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]