[gtksourceview] Implemented context classes support
- From: Paolo Borelli <pborelli src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gtksourceview] Implemented context classes support
- Date: Thu, 31 Dec 2009 15:37:47 +0000 (UTC)
commit 4ed7a7a7e3454682bcc0a838aadf168ff07d8a8a
Author: Jesse van den Kieboom <jesse vandenkieboom epfl ch>
Date: Thu Dec 31 14:39:33 2009 +0100
Implemented context classes support
Fixes bug #472660
gtksourceview/gtksourcebuffer.c | 158 +++++++++++++++
gtksourceview/gtksourcebuffer.h | 20 ++
gtksourceview/gtksourcecontextengine.c | 300 +++++++++++++++++++++++++++-
gtksourceview/gtksourcecontextengine.h | 10 +-
gtksourceview/gtksourceengine.c | 11 +
gtksourceview/gtksourceengine.h | 8 +
gtksourceview/gtksourcelanguage-parser-1.c | 5 +-
gtksourceview/gtksourcelanguage-parser-2.c | 91 ++++++++-
gtksourceview/language-specs/language2.rng | 31 +++-
tests/test-widget.c | 97 +++++++++-
10 files changed, 710 insertions(+), 21 deletions(-)
---
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index dfa6659..ecb69a9 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -80,6 +80,8 @@
#define MAX_CHARS_BEFORE_FINDING_A_MATCH 10000
+#define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
+
/* Signals */
enum {
HIGHLIGHT_UPDATED,
@@ -1961,3 +1963,159 @@ gtk_source_buffer_remove_source_marks (GtkSourceBuffer *buffer,
g_slist_free (list);
}
+
+/**
+ * gtk_source_buffer_iter_has_context_class:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: a #GtkTextIter
+ * @context_class: class to search for
+ *
+ * Check if the class @context_klass is set on @iter.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_source_buffer_iter_has_context_class (GtkSourceBuffer *buffer,
+ const GtkTextIter *iter,
+ const gchar *context_class)
+{
+ GtkTextTag *tag;
+
+ g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (context_class != NULL, FALSE);
+
+ tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
+ context_class);
+
+ if (tag != NULL)
+ {
+ return gtk_text_iter_has_tag (iter, tag);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/**
+ * gtk_source_buffer_get_context_classes_at_iter:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: a #GtkTextIter
+ *
+ * Get all defined context classes at @iter.
+ *
+ * Returns: a new %NULL terminated array of context class names. Use
+ * #g_strfreev to free the array if it is no longer needed.
+ *
+ * Since: 2.10
+ **/
+gchar **
+gtk_source_buffer_get_context_classes_at_iter (GtkSourceBuffer *buffer,
+ const GtkTextIter *iter)
+{
+ GSList *tags;
+ GSList *item;
+ GPtrArray *ret;
+
+ g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+ g_return_val_if_fail (iter != NULL, NULL);
+
+ tags = gtk_text_iter_get_tags (iter);
+ ret = g_ptr_array_new ();
+
+ for (item = tags; item; item = g_slist_next (item))
+ {
+ gchar const *name = g_object_get_data (G_OBJECT (item->data),
+ TAG_CONTEXT_CLASS_NAME);
+
+ if (name != NULL)
+ {
+ g_ptr_array_add (ret, g_strdup (name));
+ }
+ }
+
+ g_ptr_array_add (ret, NULL);
+ return (gchar **) g_ptr_array_free (ret, FALSE);
+}
+
+/**
+ * gtk_source_buffer_iter_forward_to_context_class_toggle:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: a #GtkTextIter
+ * @context_class: the context class
+ *
+ * Moves forward to the next toggle (on or off) of the context class. If no
+ * matching context class toggles are found, returns %FALSE, otherwise %TRUE.
+ * Does not return toggles located at @iter, only toggles after @iter. Sets
+ * @iter to the location of the toggle, or to the end of the buffer if no
+ * toggle is found.
+ *
+ * Returns: whether we found a context class toggle after @iter
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_source_buffer_iter_forward_to_context_class_toggle (GtkSourceBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *context_class)
+{
+ GtkTextTag *tag;
+
+ g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (context_class != NULL, FALSE);
+
+ tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
+ context_class);
+
+ if (tag == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return gtk_text_iter_forward_to_tag_toggle (iter, tag);
+ }
+}
+
+/**
+ * gtk_source_buffer_iter_backward_to_context_class_toggle:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: a #GtkTextIter
+ * @context_class: the context class
+ *
+ * Moves backward to the next toggle (on or off) of the context class. If no
+ * matching context class toggles are found, returns %FALSE, otherwise %TRUE.
+ * Does not return toggles located at @iter, only toggles after @iter. Sets
+ * @iter to the location of the toggle, or to the end of the buffer if no
+ * toggle is found.
+ *
+ * Returns: whether we found a context class toggle before @iter
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_source_buffer_iter_backward_to_context_class_toggle (GtkSourceBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *context_class)
+{
+ GtkTextTag *tag;
+
+ g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (context_class != NULL, FALSE);
+
+ tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
+ context_class);
+
+ if (tag == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return gtk_text_iter_backward_to_tag_toggle (iter, tag);
+ }
+}
+
diff --git a/gtksourceview/gtksourcebuffer.h b/gtksourceview/gtksourcebuffer.h
index f578fba..67ec9f2 100644
--- a/gtksourceview/gtksourcebuffer.h
+++ b/gtksourceview/gtksourcebuffer.h
@@ -134,6 +134,26 @@ void gtk_source_buffer_remove_source_marks (GtkSourceBuffer *buffer,
const GtkTextIter *start,
const GtkTextIter *end,
const gchar *category);
+
+gboolean gtk_source_buffer_iter_has_context_class
+ (GtkSourceBuffer *buffer,
+ const GtkTextIter *iter,
+ const gchar *context_class);
+
+gchar **gtk_source_buffer_get_context_classes_at_iter
+ (GtkSourceBuffer *buffer,
+ const GtkTextIter *iter);
+
+gboolean gtk_source_buffer_iter_forward_to_context_class_toggle
+ (GtkSourceBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *context_class);
+
+gboolean gtk_source_buffer_iter_backward_to_context_class_toggle
+ (GtkSourceBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *context_class);
+
/* private */
void _gtk_source_buffer_update_highlight (GtkSourceBuffer *buffer,
const GtkTextIter *start,
diff --git a/gtksourceview/gtksourcecontextengine.c b/gtksourceview/gtksourcecontextengine.c
index 4f4b79d..4b00887 100644
--- a/gtksourceview/gtksourcecontextengine.c
+++ b/gtksourceview/gtksourcecontextengine.c
@@ -116,6 +116,8 @@
#define ENGINE_ID(ce) ((ce)->priv->ctx_data->lang->priv->id)
#define ENGINE_STYLES_MAP(ce) ((ce)->priv->ctx_data->lang->priv->styles)
+#define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
+
typedef struct _RegexInfo RegexInfo;
typedef struct _RegexAndMatch RegexAndMatch;
typedef struct _Regex Regex;
@@ -129,6 +131,7 @@ typedef struct _DefinitionChild DefinitionChild;
typedef struct _DefinitionsIter DefinitionsIter;
typedef struct _LineInfo LineInfo;
typedef struct _InvalidRegion InvalidRegion;
+typedef struct _ContextClassTag ContextClassTag;
typedef enum {
GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID = 0,
@@ -201,6 +204,9 @@ struct _ContextDefinition
GSList *sub_patterns;
guint n_sub_patterns;
+ /* List of class definitions */
+ GSList *context_classes;
+
/* Union of every regular expression we can find from this
* context. */
Regex *reg_all;
@@ -218,6 +224,9 @@ struct _SubPatternDefinition
gchar *style;
SubPatternWhere where;
+ /* List of class definitions */
+ GSList *context_classes;
+
/* index in the ContextDefinition's list */
guint index;
@@ -276,6 +285,12 @@ struct _Context
GtkTextTag *tag;
GtkTextTag **subpattern_tags;
+ /* Cache for generated list of class tags */
+ GSList *context_classes;
+
+ /* Cache for generated list of subpattern class tags */
+ GSList **subpattern_context_classes;
+
guint ref_count;
/* see context_freeze() */
guint frozen : 1;
@@ -383,6 +398,18 @@ struct _InvalidRegion
gint delta;
};
+struct _GtkSourceContextClass
+{
+ gchar *name;
+ gboolean enabled;
+};
+
+struct _ContextClassTag
+{
+ GtkTextTag *tag;
+ gboolean enabled;
+};
+
struct _GtkSourceContextData
{
guint ref_count;
@@ -406,6 +433,8 @@ struct _GtkSourceContextEnginePrivate
* tag priorities */
guint n_tags;
+ GHashTable *context_classes;
+
/* Whether or not to actually highlight the buffer. */
gboolean highlight;
@@ -514,6 +543,49 @@ static gboolean mem_usage_timeout (GtkSourceContextEngine *ce);
/* TAGS AND STUFF -------------------------------------------------------------- */
+GtkSourceContextClass *
+gtk_source_context_class_new (gchar const *name,
+ gboolean enabled)
+{
+ GtkSourceContextClass *def = g_slice_new (GtkSourceContextClass);
+
+ def->name = g_strdup (name);
+ def->enabled = enabled;
+
+ return def;
+}
+
+static GtkSourceContextClass *
+gtk_source_context_class_copy (GtkSourceContextClass *cclass)
+{
+ return gtk_source_context_class_new (cclass->name, cclass->enabled);
+}
+
+void
+gtk_source_context_class_free (GtkSourceContextClass *cclass)
+{
+ g_free (cclass->name);
+ g_slice_free (GtkSourceContextClass, cclass);
+}
+
+static ContextClassTag *
+context_class_tag_new (GtkTextTag *tag,
+ gboolean enabled)
+{
+ ContextClassTag *attrtag = g_slice_new (ContextClassTag);
+
+ attrtag->tag = tag;
+ attrtag->enabled = enabled;
+
+ return attrtag;
+}
+
+static void
+context_class_tag_free (ContextClassTag *attrtag)
+{
+ g_slice_free (ContextClassTag, attrtag);
+}
+
struct BufAndIters {
GtkTextBuffer *buffer;
const GtkTextIter *start, *end;
@@ -537,6 +609,18 @@ unhighlight_region_cb (G_GNUC_UNUSED gpointer style,
}
static void
+unhighlight_region_class_cb (G_GNUC_UNUSED gpointer class,
+ GtkTextTag *tag,
+ gpointer user_data)
+{
+ struct BufAndIters *data = user_data;
+ gtk_text_buffer_remove_tag (data->buffer,
+ tag,
+ data->start,
+ data->end);
+}
+
+static void
unhighlight_region (GtkSourceContextEngine *ce,
const GtkTextIter *start,
const GtkTextIter *end)
@@ -551,6 +635,7 @@ unhighlight_region (GtkSourceContextEngine *ce,
return;
g_hash_table_foreach (ce->priv->tags, (GHFunc) unhighlight_region_cb, &data);
+ g_hash_table_foreach (ce->priv->context_classes, (GHFunc) unhighlight_region_class_cb, &data);
}
#define MAX_STYLE_DEPENDENCY_DEPTH 50
@@ -746,6 +831,118 @@ get_context_tag (GtkSourceContextEngine *ce,
return context->tag;
}
+static GtkTextTag *
+get_context_class_tag (GtkSourceContextEngine *ce,
+ gchar const *name)
+{
+ GtkTextTag *ret;
+
+ ret = g_hash_table_lookup (ce->priv->context_classes, name);
+
+ if (ret == NULL)
+ {
+ ret = gtk_text_buffer_create_tag (ce->priv->buffer, NULL, NULL);
+ g_object_set_data_full (G_OBJECT (ret),
+ TAG_CONTEXT_CLASS_NAME,
+ g_strdup (name),
+ (GDestroyNotify)g_free);
+
+ g_hash_table_insert (ce->priv->context_classes,
+ g_strdup (name),
+ ret);
+ }
+
+ return ret;
+}
+
+static GSList *
+extend_context_classes (GtkSourceContextEngine *ce,
+ GSList *definitions)
+{
+ GSList *item;
+ GSList *ret = NULL;
+
+ for (item = definitions; item != NULL; item = g_slist_next (item))
+ {
+ GtkSourceContextClass *cclass = item->data;
+ ContextClassTag *attrtag = context_class_tag_new (get_context_class_tag (ce, cclass->name),
+ cclass->enabled);
+
+ ret = g_slist_prepend (ret, attrtag);
+ }
+
+ return g_slist_reverse (ret);
+}
+
+static GSList *
+get_subpattern_context_classes (GtkSourceContextEngine *ce,
+ Context *context,
+ SubPatternDefinition *sp_def)
+{
+ g_assert (sp_def->index < context->definition->n_sub_patterns);
+
+ if (context->subpattern_context_classes == NULL)
+ context->subpattern_context_classes = g_new0 (GSList *, context->definition->n_sub_patterns);
+
+ if (context->subpattern_context_classes[sp_def->index] == NULL)
+ {
+ context->subpattern_context_classes[sp_def->index] =
+ extend_context_classes (ce,
+ sp_def->context_classes);
+ }
+
+ return context->subpattern_context_classes[sp_def->index];
+}
+
+static GSList *
+get_context_classes (GtkSourceContextEngine *ce,
+ Context *context)
+{
+ if (context->context_classes == NULL)
+ {
+ context->context_classes =
+ extend_context_classes (ce,
+ context->definition->context_classes);
+ }
+
+ return context->context_classes;
+}
+
+static void
+apply_context_classes (GtkSourceContextEngine *ce,
+ GSList *context_classes,
+ gint start,
+ gint end)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+ GSList *item;
+
+ gtk_text_buffer_get_iter_at_offset (ce->priv->buffer, &start_iter, start);
+ end_iter = start_iter;
+ gtk_text_iter_forward_chars (&end_iter, end - start);
+
+ for (item = context_classes; item != NULL; item = g_slist_next (item))
+ {
+ ContextClassTag *attrtag = item->data;
+
+ if (attrtag->enabled)
+ {
+ gtk_text_buffer_apply_tag (ce->priv->buffer,
+ attrtag->tag,
+ &start_iter,
+ &end_iter);
+ }
+ else
+ {
+ gtk_text_buffer_remove_tag (ce->priv->buffer,
+ attrtag->tag,
+ &start_iter,
+ &end_iter);
+ }
+ }
+}
+
static void
apply_tags (GtkSourceContextEngine *ce,
Segment *segment,
@@ -757,6 +954,7 @@ apply_tags (GtkSourceContextEngine *ce,
GtkTextBuffer *buffer = ce->priv->buffer;
SubPattern *sp;
Segment *child;
+ GSList *context_classes;
g_assert (segment != NULL);
@@ -769,6 +967,17 @@ apply_tags (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);
+
+ if (context_classes != NULL)
+ {
+ apply_context_classes (ce,
+ context_classes,
+ start_offset,
+ end_offset);
+ }
+
tag = get_context_tag (ce, segment->context);
if (tag != NULL)
@@ -801,12 +1010,18 @@ apply_tags (GtkSourceContextEngine *ce,
{
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);
+
+ context_classes = get_subpattern_context_classes (ce,
+ segment->context,
+ sp->definition);
+
tag = get_subpattern_tag (ce, segment->context, sp->definition);
+ apply_context_classes (ce, context_classes, start, end);
if (tag != NULL)
{
- gint start = MAX (start_offset, sp->start_at);
- gint end = MIN (end_offset, sp->end_at);
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
end_iter = start_iter;
gtk_text_iter_forward_chars (&end_iter, end - start);
@@ -2182,6 +2397,14 @@ remove_tags_hash_cb (G_GNUC_UNUSED gpointer style,
g_slist_free (tags);
}
+static void
+remove_context_classes_hash_cb (G_GNUC_UNUSED gpointer class,
+ GtkTextTag *tag,
+ GtkTextTagTable *table)
+{
+ gtk_text_tag_table_remove (table, tag);
+}
+
/**
* destroy_tags_hash:
*
@@ -2198,6 +2421,15 @@ destroy_tags_hash (GtkSourceContextEngine *ce)
ce->priv->tags = NULL;
}
+static void
+destroy_context_classes_hash (GtkSourceContextEngine *ce)
+{
+ g_hash_table_foreach (ce->priv->context_classes, (GHFunc) remove_context_classes_hash_cb,
+ gtk_text_buffer_get_tag_table (ce->priv->buffer));
+ g_hash_table_destroy (ce->priv->context_classes);
+ ce->priv->context_classes = NULL;
+}
+
/**
* gtk_source_context_engine_attach_buffer:
*
@@ -2260,6 +2492,8 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
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);
if (ce->priv->highlight_requests != NULL)
@@ -2289,6 +2523,7 @@ gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
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);
ce->priv->invalid_region.start = gtk_text_buffer_create_mark (buffer, NULL,
@@ -2414,6 +2649,14 @@ gtk_source_context_engine_finalize (GObject *object)
G_OBJECT_CLASS (_gtk_source_context_engine_parent_class)->finalize (object);
}
+static GtkTextTag *
+gtk_source_context_engine_get_context_class_tag (GtkSourceEngine *engine,
+ const gchar *context_class)
+{
+ return g_hash_table_lookup (GTK_SOURCE_CONTEXT_ENGINE (engine)->priv->context_classes,
+ context_class);
+}
+
static void
_gtk_source_context_engine_class_init (GtkSourceContextEngineClass *klass)
{
@@ -2427,6 +2670,7 @@ _gtk_source_context_engine_class_init (GtkSourceContextEngineClass *klass)
engine_class->text_deleted = gtk_source_context_engine_text_deleted;
engine_class->update_highlight = gtk_source_context_engine_update_highlight;
engine_class->set_style_scheme = gtk_source_context_engine_set_style_scheme;
+ engine_class->get_context_class_tag = gtk_source_context_engine_get_context_class_tag;
g_type_class_add_private (object_class, sizeof (GtkSourceContextEnginePrivate));
}
@@ -3393,6 +3637,7 @@ static void
context_unref (Context *context)
{
ContextPtr *children;
+ gint i;
if (context == NULL || --context->ref_count != 0)
return;
@@ -3433,7 +3678,25 @@ context_unref (Context *context)
regex_unref (context->end);
regex_unref (context->reg_all);
+
+ if (context->subpattern_context_classes != NULL)
+ {
+ for (i = 0; i < context->definition->n_sub_patterns; ++i)
+ {
+ g_slist_foreach (context->subpattern_context_classes[i],
+ (GFunc)context_class_tag_free,
+ NULL);
+
+ g_slist_free (context->subpattern_context_classes[i]);
+ }
+ }
+
+ g_slist_foreach (context->context_classes, (GFunc)context_class_tag_free, NULL);
+ g_slist_free (context->context_classes);
+
+ g_free (context->subpattern_context_classes);
g_free (context->subpattern_tags);
+
g_slice_free (Context, context);
}
@@ -5678,6 +5941,20 @@ definition_child_free (DefinitionChild *ch)
#endif
}
+static GSList *
+copy_context_classes (GSList *context_classes)
+{
+ GSList *ret = NULL;
+
+ while (context_classes)
+ {
+ ret = g_slist_prepend (ret, gtk_source_context_class_copy (context_classes->data));
+ context_classes = g_slist_next (context_classes);
+ }
+
+ return g_slist_reverse (ret);
+}
+
static ContextDefinition *
context_definition_new (const gchar *id,
ContextType type,
@@ -5685,6 +5962,7 @@ context_definition_new (const gchar *id,
const gchar *start,
const gchar *end,
const gchar *style,
+ GSList *context_classes,
GtkSourceContextFlags flags,
GError **error)
{
@@ -5775,6 +6053,8 @@ context_definition_new (const gchar *id,
definition->sub_patterns = NULL;
definition->n_sub_patterns = 0;
+ definition->context_classes = copy_context_classes (context_classes);
+
return definition;
}
@@ -5815,6 +6095,10 @@ context_definition_unref (ContextDefinition *definition)
g_free (sp_def->style);
if (sp_def->is_named)
g_free (sp_def->u.name);
+
+ g_slist_foreach (sp_def->context_classes, (GFunc) gtk_source_context_class_free, NULL);
+ g_slist_free (sp_def->context_classes);
+
g_slice_free (SubPatternDefinition, sp_def);
sub_pattern_list = sub_pattern_list->next;
}
@@ -5824,6 +6108,9 @@ context_definition_unref (ContextDefinition *definition)
g_free (definition->default_style);
regex_unref (definition->reg_all);
+ g_slist_foreach (definition->context_classes, (GFunc) gtk_source_context_class_free, NULL);
+ g_slist_free (definition->context_classes);
+
g_slist_foreach (definition->children, (GFunc) definition_child_free, NULL);
g_slist_free (definition->children);
g_slice_free (ContextDefinition, definition);
@@ -5888,6 +6175,7 @@ _gtk_source_context_data_define_context (GtkSourceContextData *ctx_data,
const gchar *start_regex,
const gchar *end_regex,
const gchar *style,
+ GSList *context_classes,
GtkSourceContextFlags flags,
GError **error)
{
@@ -5952,6 +6240,7 @@ _gtk_source_context_data_define_context (GtkSourceContextData *ctx_data,
definition = context_definition_new (id, type, match_regex,
start_regex, end_regex, style,
+ context_classes,
flags, error);
if (definition == NULL)
return FALSE;
@@ -5974,6 +6263,7 @@ _gtk_source_context_data_add_sub_pattern (GtkSourceContextData *ctx_data,
const gchar *name,
const gchar *where,
const gchar *style,
+ GSList *context_classes,
GError **error)
{
ContextDefinition *parent;
@@ -6049,6 +6339,8 @@ _gtk_source_context_data_add_sub_pattern (GtkSourceContextData *ctx_data,
parent->sub_patterns = g_slist_append (parent->sub_patterns, sp_def);
sp_def->index = parent->n_sub_patterns++;
+ sp_def->context_classes = copy_context_classes (context_classes);
+
return TRUE;
}
@@ -6385,7 +6677,7 @@ _gtk_source_context_data_set_escape_char (GtkSourceContextData *ctx_data,
definitions = g_slist_reverse (definitions);
if (!_gtk_source_context_data_define_context (ctx_data, "gtk-source-context-engine-escape",
- NULL, pattern, NULL, NULL, NULL,
+ NULL, pattern, NULL, NULL, NULL, NULL,
GTK_SOURCE_CONTEXT_EXTEND_PARENT,
&error))
goto out;
@@ -6394,7 +6686,7 @@ _gtk_source_context_data_set_escape_char (GtkSourceContextData *ctx_data,
pattern = g_strdup_printf ("%s$", escaped);
if (!_gtk_source_context_data_define_context (ctx_data, "gtk-source-context-engine-line-escape",
- NULL, NULL, pattern, "^", NULL,
+ NULL, NULL, pattern, "^", NULL, NULL,
GTK_SOURCE_CONTEXT_EXTEND_PARENT,
&error))
goto out;
diff --git a/gtksourceview/gtksourcecontextengine.h b/gtksourceview/gtksourcecontextengine.h
index f83fb9f..5074000 100644
--- a/gtksourceview/gtksourcecontextengine.h
+++ b/gtksourceview/gtksourcecontextengine.h
@@ -36,7 +36,7 @@ G_BEGIN_DECLS
typedef struct _GtkSourceContextData GtkSourceContextData;
typedef struct _GtkSourceContextReplace GtkSourceContextReplace;
-
+typedef struct _GtkSourceContextClass GtkSourceContextClass;
typedef struct _GtkSourceContextEngine GtkSourceContextEngine;
typedef struct _GtkSourceContextEngineClass GtkSourceContextEngineClass;
typedef struct _GtkSourceContextEnginePrivate GtkSourceContextEnginePrivate;
@@ -75,6 +75,12 @@ GtkSourceContextData *_gtk_source_context_data_new (GtkSourceLanguage *lang);
GtkSourceContextData *_gtk_source_context_data_ref (GtkSourceContextData *data);
void _gtk_source_context_data_unref (GtkSourceContextData *data);
+GtkSourceContextClass *
+ gtk_source_context_class_new (gchar const *name,
+ gboolean enabled);
+
+void gtk_source_context_class_free (GtkSourceContextClass *cclass);
+
GtkSourceContextEngine *_gtk_source_context_engine_new (GtkSourceContextData *data);
gboolean _gtk_source_context_data_define_context
@@ -85,6 +91,7 @@ gboolean _gtk_source_context_data_define_context
const gchar *start_regex,
const gchar *end_regex,
const gchar *style,
+ GSList *context_classes,
GtkSourceContextFlags flags,
GError **error);
@@ -95,6 +102,7 @@ gboolean _gtk_source_context_data_add_sub_pattern
const gchar *name,
const gchar *where,
const gchar *style,
+ GSList *context_classes,
GError **error);
gboolean _gtk_source_context_data_add_ref (GtkSourceContextData *data,
diff --git a/gtksourceview/gtksourceengine.c b/gtksourceview/gtksourceengine.c
index 699d29e..e89dfe2 100644
--- a/gtksourceview/gtksourceengine.c
+++ b/gtksourceview/gtksourceengine.c
@@ -104,3 +104,14 @@ _gtk_source_engine_set_style_scheme (GtkSourceEngine *engine,
GTK_SOURCE_ENGINE_GET_CLASS (engine)->set_style_scheme (engine, scheme);
}
+
+GtkTextTag *
+_gtk_source_engine_get_context_class_tag (GtkSourceEngine *engine,
+ const gchar *context_class)
+{
+ g_return_val_if_fail (GTK_IS_SOURCE_ENGINE (engine), NULL);
+ g_return_val_if_fail (context_class != NULL, NULL);
+
+ return GTK_SOURCE_ENGINE_GET_CLASS (engine)->get_context_class_tag (engine,
+ context_class);
+}
diff --git a/gtksourceview/gtksourceengine.h b/gtksourceview/gtksourceengine.h
index bc087e9..2d95488 100644
--- a/gtksourceview/gtksourceengine.h
+++ b/gtksourceview/gtksourceengine.h
@@ -63,6 +63,10 @@ struct _GtkSourceEngineClass
void (* set_style_scheme) (GtkSourceEngine *engine,
GtkSourceStyleScheme *scheme);
+
+ GtkTextTag *(* get_context_class_tag)
+ (GtkSourceEngine *engine,
+ const gchar *context_class);
};
GType _gtk_source_engine_get_type (void) G_GNUC_CONST;
@@ -82,6 +86,10 @@ void _gtk_source_engine_update_highlight (GtkSourceEngine *engine,
void _gtk_source_engine_set_style_scheme (GtkSourceEngine *engine,
GtkSourceStyleScheme *scheme);
+GtkTextTag *_gtk_source_engine_get_context_class_tag
+ (GtkSourceEngine *engine,
+ const gchar *context_class);
+
G_END_DECLS
#endif /* __GTK_SOURCE_ENGINE_H__ */
diff --git a/gtksourceview/gtksourcelanguage-parser-1.c b/gtksourceview/gtksourcelanguage-parser-1.c
index 15dab3f..ab61ca9 100644
--- a/gtksourceview/gtksourcelanguage-parser-1.c
+++ b/gtksourceview/gtksourcelanguage-parser-1.c
@@ -97,7 +97,7 @@ ctx_data_add_simple_pattern (GtkSourceContextData *ctx_data,
result = _gtk_source_context_data_define_context (ctx_data, real_id,
root_id,
fixed, NULL, NULL,
- style,
+ style, NULL,
GTK_SOURCE_CONTEXT_EXTEND_PARENT |
GTK_SOURCE_CONTEXT_END_AT_LINE_END,
&error);
@@ -145,6 +145,7 @@ ctx_data_add_syntax_pattern (GtkSourceContextData *ctx_data,
pattern_start,
pattern_end,
style,
+ NULL,
flags,
&error);
@@ -658,7 +659,7 @@ define_root_context (GtkSourceContextData *ctx_data,
id = g_strdup_printf ("%s:%s", language->priv->id, language->priv->id);
result = _gtk_source_context_data_define_context (ctx_data, id,
NULL, NULL, NULL, NULL,
- NULL,
+ NULL, NULL,
GTK_SOURCE_CONTEXT_EXTEND_PARENT,
&error);
diff --git a/gtksourceview/gtksourcelanguage-parser-2.c b/gtksourceview/gtksourcelanguage-parser-2.c
index 0cdfd05..25b4e18 100644
--- a/gtksourceview/gtksourcelanguage-parser-2.c
+++ b/gtksourceview/gtksourcelanguage-parser-2.c
@@ -153,6 +153,7 @@ static gboolean create_definition (ParserState *parser_state,
gchar *id,
gchar *parent_id,
gchar *style,
+ GSList *context_classes,
GError **error);
static void handle_context_element (ParserState *parser_state);
@@ -303,11 +304,56 @@ get_context_flags (ParserState *parser_state)
return flags;
}
+static GSList *
+add_classes (GSList *list,
+ gchar const *classes,
+ gboolean enabled)
+{
+ gchar **parts;
+ gchar **ptr;
+ GSList *newlist = NULL;
+
+ if (classes == NULL)
+ {
+ return list;
+ }
+
+ parts = ptr = g_strsplit (classes, " ", -1);
+
+ while (*ptr)
+ {
+ GtkSourceContextClass *ctx = gtk_source_context_class_new (*ptr, enabled);
+ newlist = g_slist_prepend (newlist, ctx);
+
+ ++ptr;
+ }
+
+ g_strfreev (parts);
+ return g_slist_concat (list, g_slist_reverse (newlist));
+}
+
+static GSList *
+parse_classes (ParserState *parser_state)
+{
+ GSList *ret = NULL;
+ xmlChar *en = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "class");
+ xmlChar *dis = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "class-disabled");
+
+ ret = add_classes (ret, (gchar const *)en, TRUE);
+ ret = add_classes (ret, (gchar const *)dis, FALSE);
+
+ xmlFree (en);
+ xmlFree (dis);
+
+ return ret;
+}
+
static gboolean
create_definition (ParserState *parser_state,
gchar *id,
gchar *parent_id,
gchar *style,
+ GSList *context_classes,
GError **error)
{
gchar *match = NULL, *start = NULL, *end = NULL;
@@ -468,6 +514,7 @@ create_definition (ParserState *parser_state,
}
if (tmp_error == NULL)
+ {
_gtk_source_context_data_define_context (parser_state->ctx_data,
id,
parent_id,
@@ -475,8 +522,10 @@ create_definition (ParserState *parser_state,
start,
end,
style,
+ context_classes,
flags,
&tmp_error);
+ }
g_free (match);
g_free (start);
@@ -608,11 +657,12 @@ add_ref (ParserState *parser_state,
}
static gboolean
-create_sub_pattern (ParserState *parser_state,
- gchar *id,
- gchar *sub_pattern,
- gchar *style,
- GError **error)
+create_sub_pattern (ParserState *parser_state,
+ gchar *id,
+ gchar *sub_pattern,
+ gchar *style,
+ GSList *context_classes,
+ GError **error)
{
gchar *container_id;
xmlChar *where;
@@ -634,6 +684,7 @@ create_sub_pattern (ParserState *parser_state,
sub_pattern,
(gchar*) where,
style,
+ context_classes,
&tmp_error);
xmlFree (where);
@@ -656,6 +707,7 @@ handle_context_element (ParserState *parser_state)
gboolean success;
gboolean ignore_style = FALSE;
GtkSourceContextRefOptions options = 0;
+ GSList *context_classes;
GError *tmp_error = NULL;
@@ -706,6 +758,8 @@ handle_context_element (ParserState *parser_state)
g_warning ("in file %s: style '%s' not defined", parser_state->filename, style_ref);
}
+ context_classes = parse_classes (parser_state);
+
if (ref != NULL)
{
tmp = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "original");
@@ -716,7 +770,11 @@ handle_context_element (ParserState *parser_state)
if (style_ref != NULL)
options |= GTK_SOURCE_CONTEXT_OVERRIDE_STYLE;
- add_ref (parser_state, (gchar*) ref, options, style_ref, &tmp_error);
+ add_ref (parser_state,
+ (gchar*) ref,
+ options,
+ style_ref,
+ &tmp_error);
}
else
{
@@ -741,9 +799,12 @@ handle_context_element (ParserState *parser_state)
{
if (sub_pattern != NULL)
{
- create_sub_pattern (parser_state, id,
- (gchar *)sub_pattern, style_ref,
- &tmp_error);
+ create_sub_pattern (parser_state,
+ id,
+ (gchar *)sub_pattern,
+ style_ref,
+ context_classes,
+ &tmp_error);
}
else
{
@@ -761,11 +822,16 @@ handle_context_element (ParserState *parser_state)
NULL,
NULL,
NULL,
+ NULL,
0,
&tmp_error);
else
- success = create_definition (parser_state, id, parent_id,
- style_ref, &tmp_error);
+ success = create_definition (parser_state,
+ id,
+ parent_id,
+ style_ref,
+ context_classes,
+ &tmp_error);
if (success && !is_empty)
{
@@ -781,6 +847,9 @@ handle_context_element (ParserState *parser_state)
g_free (id);
}
+ g_slist_foreach (context_classes, (GFunc)gtk_source_context_class_free, NULL);
+ g_slist_free (context_classes);
+
g_free (style_ref);
xmlFree (sub_pattern);
xmlFree (ref);
diff --git a/gtksourceview/language-specs/language2.rng b/gtksourceview/language-specs/language2.rng
index 87ead1c..babdd87 100644
--- a/gtksourceview/language-specs/language2.rng
+++ b/gtksourceview/language-specs/language2.rng
@@ -236,7 +236,12 @@
<ref name="boolean-value"/>
</attribute>
</optional>
-
+ <optional>
+ <attribute name="class"/>
+ </optional>
+ <optional>
+ <attribute name="class-disabled"/>
+ </optional>
<choice>
<element name="match">
<ref name="regex-options"/>
@@ -308,6 +313,12 @@
<ref name="boolean-value"/>
</attribute>
</optional>
+ <optional>
+ <attribute name="class"/>
+ </optional>
+ <optional>
+ <attribute name="class-disabled"/>
+ </optional>
<element name="start">
<ref name="regex-options"/>
@@ -347,6 +358,12 @@
<attribute name="id">
<ref name="id-type"/>
</attribute>
+ <optional>
+ <attribute name="class"/>
+ </optional>
+ <optional>
+ <attribute name="class-disabled"/>
+ </optional>
<element name="include">
<interleave>
@@ -379,6 +396,12 @@
<ref name="id-type"/>
</attribute>
</optional>
+ <optional>
+ <attribute name="class"/>
+ </optional>
+ <optional>
+ <attribute name="class-disabled"/>
+ </optional>
<attribute name="sub-pattern"/>
</element>
@@ -396,6 +419,12 @@
<ref name="id-type"/>
</attribute>
</optional>
+ <optional>
+ <attribute name="class"/>
+ </optional>
+ <optional>
+ <attribute name="class-disabled"/>
+ </optional>
<attribute name="sub-pattern"/>
diff --git a/tests/test-widget.c b/tests/test-widget.c
index 5e7d527..46f5f4e 100644
--- a/tests/test-widget.c
+++ b/tests/test-widget.c
@@ -92,6 +92,13 @@ static void indent_toggled_cb (GtkAction *action,
GtkAction *current,
gpointer user_data);
+static void forward_string_cb (GtkAction *action,
+ gpointer user_data);
+
+static void backward_string_cb (GtkAction *action,
+ gpointer user_data);
+
+
static GtkWidget *create_view_window (GtkSourceBuffer *buffer,
GtkSourceView *from);
@@ -119,6 +126,10 @@ static GtkActionEntry view_action_entries[] = {
"Find", G_CALLBACK (find_cb) },
{ "Replace", GTK_STOCK_FIND_AND_REPLACE, "Search and _Replace", "<control>R",
"Search and Replace", G_CALLBACK (replace_cb) },
+ { "ForwardString", NULL, "_Forward to string toggle", "<control>S",
+ "Forward to the start or end of the next string", G_CALLBACK (forward_string_cb) },
+ { "BackwardString", NULL, "_Backward to string toggle", "<control><shift>S",
+ "Backward to the start or end of the next string", G_CALLBACK (backward_string_cb) }
};
static GtkToggleActionEntry toggle_entries[] = {
@@ -223,6 +234,9 @@ static const gchar *view_ui_description =
" <menuitem action=\"SmartHomeEndAfter\"/>"
" <menuitem action=\"SmartHomeEndAlways\"/>"
" </menu>"
+" <separator/>"
+" <menuitem action=\"ForwardString\"/>"
+" <menuitem action=\"BackwardString\"/>"
" </menu>"
" </menubar>"
"</ui>";
@@ -619,6 +633,61 @@ new_view_cb (GtkAction *action, gpointer user_data)
gtk_widget_show (window);
}
+static void
+forward_string_cb (GtkAction *action,
+ gpointer user_data)
+{
+ GtkSourceBuffer *buffer;
+ GtkSourceView *view;
+ GtkTextIter iter;
+ GtkTextMark *insert;
+
+ g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data));
+
+ view = GTK_SOURCE_VIEW (user_data);
+ buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+ &iter,
+ insert);
+
+ if (gtk_source_buffer_iter_forward_to_context_class_toggle (buffer,
+ &iter,
+ "string"))
+ {
+ gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (buffer), &iter);
+ gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view), insert);
+ }
+}
+
+static void
+backward_string_cb (GtkAction *action,
+ gpointer user_data)
+{
+ GtkSourceBuffer *buffer;
+ GtkSourceView *view;
+ GtkTextIter iter;
+ GtkTextMark *insert;
+
+ g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data));
+
+ view = GTK_SOURCE_VIEW (user_data);
+ buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+ &iter,
+ insert);
+
+ if (gtk_source_buffer_iter_backward_to_context_class_toggle (buffer,
+ &iter,
+ "string"))
+ {
+ gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (buffer), &iter);
+ gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view), insert);
+ }
+}
/* Buffer action callbacks ------------------------------------------------------------ */
@@ -1030,6 +1099,9 @@ update_cursor_position (GtkTextBuffer *buffer, gpointer user_data)
GtkTextIter iter, start;
GtkSourceView *view;
GtkLabel *pos_label;
+ gchar **classes;
+ gchar **classes_ptr;
+ GString *str;
g_return_if_fail (GTK_IS_SOURCE_VIEW (user_data));
@@ -1060,9 +1132,30 @@ update_cursor_position (GtkTextBuffer *buffer, gpointer user_data)
gtk_text_iter_forward_char (&start);
}
- msg = g_strdup_printf ("char: %d, line: %d, column: %d", chars, row, col);
+ classes = gtk_source_buffer_get_context_classes_at_iter (GTK_SOURCE_BUFFER (buffer),
+ &iter);
+
+ str = g_string_new ("");
+ classes_ptr = classes;
+
+ while (classes_ptr && *classes_ptr)
+ {
+ if (classes_ptr != classes)
+ {
+ g_string_append (str, ", ");
+ }
+
+ g_string_append_printf (str, "%s", *classes_ptr);
+ ++classes_ptr;
+ }
+
+ g_strfreev (classes);
+
+ msg = g_strdup_printf ("char: %d, line: %d, column: %d, classes: %s", chars, row, col, str->str);
gtk_label_set_text (pos_label, msg);
- g_free (msg);
+
+ g_free (msg);
+ g_string_free (str, TRUE);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]