1
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
2
* gtksourcecontextengine.c
4
* Copyright (C) 2003 - Gustavo Giráldez <gustavo.giraldez@gmx.net>
5
* Copyright (C) 2005, 2006 - Marco Barisione, Emanuele Aina
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU Library General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU Library General Public License for more details.
17
* You should have received a copy of the GNU Library General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
#include "gtksourceview-i18n.h"
23
#include "gtksourcecontextengine.h"
24
#include "gtktextregion.h"
25
#include "gtksourcelanguage-private.h"
26
#include "gtksourcebuffer.h"
27
#include "gtksourcestyle-private.h"
36
#undef ENABLE_CHECK_TREE
37
#undef ENABLE_MEMORY_DEBUG /* define it to make it print memory usage information */
38
/* it won't work with GRegex */
47
#define PROFILE(x) (x)
52
#if defined (ENABLE_DEBUG) || defined (ENABLE_PROFILE) || \
53
defined (ENABLE_CHECK_TREE)
57
/* Regex used to match "\%{...@start}". */
58
#define START_REF_REGEX "(?<!\\\\)(\\\\\\\\)*\\\\%\\{(.*?)@start\\}"
60
/* Priority of one-time idle which is installed after buffer is modified. */
61
#define FIRST_UPDATE_PRIORITY G_PRIORITY_HIGH_IDLE
62
/* Maximal amount of time allowed to spent in this first idle. Should be
63
* small enough, since in worst case we block ui for this time after each keypress.
65
#define FIRST_UPDATE_TIME_SLICE 10
66
/* Priority of long running idle which is used to analyze whole buffer, if
67
* the engine wasn't quick enough to analyze it in one shot. */
68
/* FIXME this priority is low, since we don't want to block other gui stuff.
69
* But, e.g. if we have a big file, and scroll down, we do want the engine
70
* to analyze quickly. Perhaps we want to reinstall first_update in case
71
* of expose events or something. */
72
#define INCREMENTAL_UPDATE_PRIORITY G_PRIORITY_LOW
73
/* Maximal amount of time allowed to spent in one cycle of background idle. */
74
#define INCREMENTAL_UPDATE_TIME_SLICE 30
76
/* Maximal amount of time allowed to spent highlihting a single line. If it
77
* is not enough, then highlighting is disabled. */
78
#define MAX_TIME_FOR_ONE_LINE 2000
80
#define GTK_SOURCE_CONTEXT_ENGINE_ERROR (gtk_source_context_engine_error_quark ())
82
/* Returns the definition corrsponding to the specified id. */
83
#define LOOKUP_DEFINITION(ctx_data, id) \
84
(g_hash_table_lookup ((ctx_data)->definitions, (id)))
86
#define HAS_OPTION(def,opt) (((def)->flags & GTK_SOURCE_CONTEXT_##opt) != 0)
88
/* Can the context be terminated by ancestor? */
89
/* Root context can't be terminated; its child may not be terminated by it;
90
* grandchildren look at the flag */
91
#define ANCESTOR_CAN_END_CONTEXT(ctx) \
92
((ctx)->parent != NULL && (ctx)->parent->parent != NULL && \
93
(!HAS_OPTION ((ctx)->definition, EXTEND_PARENT) || !(ctx)->all_ancestors_extend))
95
/* Root context and its children have this TRUE; grandchildren use the flag */
96
#define CONTEXT_EXTENDS_PARENT(ctx) \
97
((ctx)->parent == NULL || (ctx)->parent->parent == NULL || \
98
HAS_OPTION ((ctx)->definition, EXTEND_PARENT))
100
/* Root and its children have this FALSE; grandchildren use the flag */
101
#define CONTEXT_ENDS_PARENT(ctx) \
102
((ctx)->parent != NULL && (ctx)->parent->parent != NULL && \
103
HAS_OPTION ((ctx)->definition, END_PARENT))
104
#define SEGMENT_ENDS_PARENT(s) CONTEXT_ENDS_PARENT ((s)->context)
106
/* Does the segment terminate at line end? */
107
/* Root segment doesn't, children look at the flag */
108
#define CONTEXT_END_AT_LINE_END(ctx) \
109
((ctx)->parent != NULL && HAS_OPTION ((ctx)->definition, END_AT_LINE_END))
110
#define SEGMENT_END_AT_LINE_END(s) CONTEXT_END_AT_LINE_END((s)->context)
112
#define CONTEXT_IS_SIMPLE(c) ((c)->definition->type == CONTEXT_TYPE_SIMPLE)
113
#define CONTEXT_IS_CONTAINER(c) ((c)->definition->type == CONTEXT_TYPE_CONTAINER)
114
#define SEGMENT_IS_INVALID(s) ((s)->context == NULL)
115
#define SEGMENT_IS_SIMPLE(s) CONTEXT_IS_SIMPLE ((s)->context)
116
#define SEGMENT_IS_CONTAINER(s) CONTEXT_IS_CONTAINER ((s)->context)
118
#define ENGINE_ID(ce) ((ce)->priv->ctx_data->lang->priv->id)
119
#define ENGINE_STYLES_MAP(ce) ((ce)->priv->ctx_data->lang->priv->styles)
121
#define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
123
typedef struct _RegexInfo RegexInfo;
124
typedef struct _RegexAndMatch RegexAndMatch;
125
typedef struct _Regex Regex;
126
typedef struct _SubPatternDefinition SubPatternDefinition;
127
typedef struct _SubPattern SubPattern;
128
typedef struct _Segment Segment;
129
typedef struct _Context Context;
130
typedef struct _ContextPtr ContextPtr;
131
typedef struct _ContextDefinition ContextDefinition;
132
typedef struct _DefinitionChild DefinitionChild;
133
typedef struct _DefinitionsIter DefinitionsIter;
134
typedef struct _LineInfo LineInfo;
135
typedef struct _InvalidRegion InvalidRegion;
136
typedef struct _ContextClassTag ContextClassTag;
139
GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID = 0,
140
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_ARGS,
141
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_PARENT,
142
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
143
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_WHERE,
144
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_START_REF,
145
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REGEX,
146
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_STYLE,
147
GTK_SOURCE_CONTEXT_ENGINE_ERROR_BAD_FILE
148
} GtkSourceContextEngineError;
151
CONTEXT_TYPE_SIMPLE = 0,
152
CONTEXT_TYPE_CONTAINER
156
SUB_PATTERN_WHERE_DEFAULT = 0,
157
SUB_PATTERN_WHERE_START,
158
SUB_PATTERN_WHERE_END
164
GRegexCompileFlags flags;
167
/* glib has now so fscking nice API! */
168
struct _RegexAndMatch
174
/* We do not use directly GRegex to allow the use of "\%{...@start}". */
185
struct _ContextDefinition
199
/* Name of the style used for contexts of this type. */
200
gchar *default_style;
202
/* This is a list of DefinitionChild pointers. */
205
/* Sub patterns (list of SubPatternDefinition pointers.) */
206
GSList *sub_patterns;
207
guint n_sub_patterns;
209
/* List of class definitions */
210
GSList *context_classes;
212
/* Union of every regular expression we can find from this
217
guint ref_count : 24;
220
struct _SubPatternDefinition
223
/* We need the id only for debugging. */
227
SubPatternWhere where;
229
/* List of class definitions */
230
GSList *context_classes;
232
/* index in the ContextDefinition's list */
243
struct _DefinitionChild
247
/* Equal to definition->id, used when it's not resolved yet */
249
ContextDefinition *definition;
254
/* Whether this child is a reference to all child contexts of
256
guint is_ref_all : 1;
257
/* Whether it is resolved, i.e. points to actual context definition. */
259
/* Whether style is overridden, i.e. use child->style instead of what definition says. */
260
guint override_style : 1;
261
/* Whether style should be ignored for this and all child contexts. */
262
guint override_style_deep : 1;
265
struct _DefinitionsIter
267
GSList *children_stack;
272
/* Definition for the context. */
273
ContextDefinition *definition;
276
ContextPtr *children;
278
/* This is the regex returned by regex_resolve() called on
279
* definition->start_end.end. */
281
/* The regular expression containing every regular expression that
282
* could be matched in this context. */
285
/* Either definition->default_style or child_def->style, not copied. */
288
GtkTextTag **subpattern_tags;
290
/* Cache for generated list of class tags */
291
GSList *context_classes;
293
/* Cache for generated list of subpattern class tags */
294
GSList **subpattern_context_classes;
297
/* see context_freeze() */
299
/* Do all the ancestors extend their parent? */
300
guint all_ancestors_extend : 1;
301
/* Do not apply styles to children contexts */
302
guint ignore_children_style : 1;
307
ContextDefinition *definition;
313
GHashTable *hash; /* char* -> Context* */
318
struct _GtkSourceContextReplace
332
/* This is NULL if and only if it's a dummy segment which denotes
333
* inserted or deleted text. */
336
/* Subpatterns found in this segment. */
337
SubPattern *sub_patterns;
339
/* The context is used in the interval [start_at; end_at). */
343
/* In case of container contexts, start_len/end_len is length in chars
344
* of start/end match. */
348
/* Whether this segment is a whole good segment, or it's an
349
* an end of bigger one left after erase_segments() call. */
355
SubPatternDefinition *definition;
361
/* Line terminator characters (\n, \r, \r\n, or unicode paragraph separator)
362
* are removed from the line text. The problem is that pcre does not understand
363
* arbitrary line terminators, so $ in pcre means (?=\n) (not quite, it's also
364
* end of matched string), while we really need "((?=\r\n)|(?=[\r\n])|(?=\xE2\x80\xA9)|$)".
365
* It could be worked around by replacing line terminator in matched text with
366
* \n, but it's a good source of errors, since offsets (not all, unfortunately) returned
367
* from pcre need to be compared to line length, and adjusted when necessary.
368
* Not using line terminator only means that \n can't be in patterns, it's not a
369
* big deal: line end can't be highlighted anyway; if a rule needs to match it, it can
370
* can use "$" as start and "^" as end (not in a single pattern of course, "$^" will
373
* UPDATE: the above isn't true anymore, pcre can do arbitrary line terminators.
374
* BUT: how do we know whether we should get one/two/N lines to match? Single-line
375
* case to highlight end of line is covered by above ($). I do not feel brave enough
376
* to modify this now for no real benefit. (muntyan)
378
#define NEXT_LINE_OFFSET(l_) ((l_)->start_at + (l_)->char_length + (l_)->eol_length)
383
/* Character offset of the line in text buffer. */
385
/* Character length of line terminator, or 0 if it's the
386
* last line in buffer. */
388
/* Length of the line text not including line terminator */
393
struct _InvalidRegion
398
/* offset_at(end) - delta == original offset,
399
* i.e. offset in the tree */
403
struct _GtkSourceContextClass
409
struct _ContextClassTag
415
struct _GtkSourceContextData
419
GtkSourceLanguage *lang;
421
/* Contains every ContextDefinition indexed by its id. */
422
GHashTable *definitions;
425
struct _GtkSourceContextEnginePrivate
427
GtkSourceContextData *ctx_data;
429
GtkTextBuffer *buffer;
430
GtkSourceStyleScheme *style_scheme;
432
/* All tags indexed by style name: values are GSList's of tags, ref()'ed. */
434
/* Number of all syntax tags created by the engine, needed to set correct
438
GHashTable *context_classes;
440
/* Whether or not to actually highlight the buffer. */
443
/* Whether highlighting was disabled because of errors. */
446
/* Region covering the unhighlighted text. */
447
GtkTextRegion *refresh_region;
449
/* Tree of contexts. */
450
Context *root_context;
451
Segment *root_segment;
454
/* list of Segment* */
456
InvalidRegion invalid_region;
459
guint incremental_update;
461
/* Views highlight requests. */
462
GtkTextRegion *highlight_requests;
464
#ifdef ENABLE_MEMORY_DEBUG
465
guint mem_usage_timeout;
470
#ifdef ENABLE_CHECK_TREE
471
static void check_tree (GtkSourceContextEngine *ce);
472
static void check_segment_list (Segment *segment);
473
static void check_segment_children (Segment *segment);
474
#define CHECK_TREE check_tree
475
#define CHECK_SEGMENT_LIST check_segment_list
476
#define CHECK_SEGMENT_CHILDREN check_segment_children
478
#define CHECK_TREE(ce)
479
#define CHECK_SEGMENT_LIST(s)
480
#define CHECK_SEGMENT_CHILDREN(s)
484
static GQuark gtk_source_context_engine_error_quark (void) G_GNUC_CONST;
486
static Segment *create_segment (GtkSourceContextEngine *ce,
493
static Segment *segment_new (GtkSourceContextEngine *ce,
499
static Context *context_new (Context *parent,
500
ContextDefinition *definition,
501
const gchar *line_text,
503
gboolean ignore_children_style);
504
static void context_unref (Context *context);
505
static void context_freeze (Context *context);
506
static void context_thaw (Context *context);
507
static void erase_segments (GtkSourceContextEngine *ce,
511
static void segment_remove (GtkSourceContextEngine *ce,
514
static void find_insertion_place (Segment *segment,
520
static void segment_destroy (GtkSourceContextEngine *ce,
522
static ContextDefinition *context_definition_ref(ContextDefinition *definition);
523
static void context_definition_unref(ContextDefinition *definition);
525
static void segment_extend (Segment *state,
527
static Context *ancestor_context_ends_here (Context *state,
530
static void definition_iter_init (DefinitionsIter *iter,
531
ContextDefinition *definition);
532
static DefinitionChild *definition_iter_next (DefinitionsIter *iter);
533
static void definition_iter_destroy (DefinitionsIter *iter);
535
static void update_syntax (GtkSourceContextEngine *ce,
536
const GtkTextIter *end,
538
static void install_idle_worker (GtkSourceContextEngine *ce);
539
static void install_first_update (GtkSourceContextEngine *ce);
541
#ifdef ENABLE_MEMORY_DEBUG
542
static gboolean mem_usage_timeout (GtkSourceContextEngine *ce);
546
/* TAGS AND STUFF -------------------------------------------------------------- */
548
GtkSourceContextClass *
549
gtk_source_context_class_new (gchar const *name,
552
GtkSourceContextClass *def = g_slice_new (GtkSourceContextClass);
554
def->name = g_strdup (name);
555
def->enabled = enabled;
560
static GtkSourceContextClass *
561
gtk_source_context_class_copy (GtkSourceContextClass *cclass)
563
return gtk_source_context_class_new (cclass->name, cclass->enabled);
567
gtk_source_context_class_free (GtkSourceContextClass *cclass)
569
g_free (cclass->name);
570
g_slice_free (GtkSourceContextClass, cclass);
573
static ContextClassTag *
574
context_class_tag_new (GtkTextTag *tag,
577
ContextClassTag *attrtag = g_slice_new (ContextClassTag);
580
attrtag->enabled = enabled;
586
context_class_tag_free (ContextClassTag *attrtag)
588
g_slice_free (ContextClassTag, attrtag);
592
GtkTextBuffer *buffer;
593
const GtkTextIter *start, *end;
597
unhighlight_region_cb (G_GNUC_UNUSED gpointer style,
601
struct BufAndIters *data = user_data;
605
gtk_text_buffer_remove_tag (data->buffer,
614
unhighlight_region (GtkSourceContextEngine *ce,
615
const GtkTextIter *start,
616
const GtkTextIter *end)
618
struct BufAndIters data;
620
data.buffer = ce->priv->buffer;
624
if (gtk_text_iter_equal (start, end))
627
g_hash_table_foreach (ce->priv->tags, (GHFunc) unhighlight_region_cb, &data);
630
#define MAX_STYLE_DEPENDENCY_DEPTH 50
633
set_tag_style (GtkSourceContextEngine *ce,
635
const gchar *style_id)
637
GtkSourceStyle *style;
639
const char *map_to = style_id;
643
g_return_if_fail (GTK_IS_TEXT_TAG (tag));
644
g_return_if_fail (style_id != NULL);
646
_gtk_source_style_apply (NULL, tag);
648
if (ce->priv->style_scheme == NULL)
651
style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, style_id);
653
while (style == NULL)
655
GtkSourceStyleInfo *info;
657
if (guard > MAX_STYLE_DEPENDENCY_DEPTH)
659
g_warning ("Potential circular dependency between styles detected for style '%s'", style_id);
665
/* FIXME Style references really must be fixed, both parser for
666
* sane use in lang files, and engine for safe use. */
667
info = g_hash_table_lookup (ENGINE_STYLES_MAP(ce), map_to);
669
map_to = (info != NULL) ? info->map_to : NULL;
674
style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, map_to);
677
/* not having style is fine, since parser checks validity of every style reference,
678
* so we don't need to spit a warning here */
680
_gtk_source_style_apply (style, tag);
684
create_tag (GtkSourceContextEngine *ce,
685
const gchar *style_id)
690
g_assert (style_id != NULL);
692
tags = g_hash_table_lookup (ce->priv->tags, style_id);
694
new_tag = gtk_text_buffer_create_tag (ce->priv->buffer, NULL, NULL);
695
/* It must have priority lower than user tags but still
696
* higher than highlighting tags created before */
697
gtk_text_tag_set_priority (new_tag, ce->priv->n_tags);
698
set_tag_style (ce, new_tag, style_id);
699
ce->priv->n_tags += 1;
701
tags = g_slist_prepend (tags, g_object_ref (new_tag));
702
g_hash_table_insert (ce->priv->tags, g_strdup (style_id), tags);
707
/* Find tag which has to be overridden. */
709
get_parent_tag (Context *context,
712
while (context != NULL)
714
/* Lang files may repeat same style for nested contexts,
716
if (context->style &&
717
strcmp (context->style, style) != 0)
719
g_assert (context->tag != NULL);
723
context = context->parent;
730
get_tag_for_parent (GtkSourceContextEngine *ce,
735
GtkTextTag *parent_tag = NULL;
738
g_return_val_if_fail (style != NULL, NULL);
740
parent_tag = get_parent_tag (parent, style);
741
tags = g_hash_table_lookup (ce->priv->tags, style);
743
if (tags && (!parent_tag ||
744
gtk_text_tag_get_priority (tags->data) > gtk_text_tag_get_priority (parent_tag)))
750
/* Now get the tag with lowest priority, so that tag lists do not grow
752
for (link = tags->next; link != NULL; link = link->next)
755
gtk_text_tag_get_priority (link->data) < gtk_text_tag_get_priority (parent_tag))
762
tag = create_tag (ce, style);
766
GString *style_path = g_string_new (style);
769
while (parent != NULL)
771
if (parent->style != NULL)
773
g_string_prepend (style_path, "/");
774
g_string_prepend (style_path,
778
parent = parent->parent;
781
tags = g_hash_table_lookup (ce->priv->tags, style);
782
n = g_slist_length (tags);
783
g_print ("created %d tag for style %s: %s\n", n, style, style_path->str);
784
g_string_free (style_path, TRUE);
793
get_subpattern_tag (GtkSourceContextEngine *ce,
795
SubPatternDefinition *sp_def)
797
if (sp_def->style == NULL)
800
g_assert (sp_def->index < context->definition->n_sub_patterns);
802
if (context->subpattern_tags == NULL)
803
context->subpattern_tags = g_new0 (GtkTextTag*, context->definition->n_sub_patterns);
805
if (context->subpattern_tags[sp_def->index] == NULL)
806
context->subpattern_tags[sp_def->index] = get_tag_for_parent (ce, sp_def->style, context);
808
g_return_val_if_fail (context->subpattern_tags[sp_def->index] != NULL, NULL);
809
return context->subpattern_tags[sp_def->index];
813
get_context_tag (GtkSourceContextEngine *ce,
816
if (context->style != NULL && context->tag == NULL)
817
context->tag = get_tag_for_parent (ce,
824
apply_tags (GtkSourceContextEngine *ce,
830
GtkTextIter start_iter, end_iter;
831
GtkTextBuffer *buffer = ce->priv->buffer;
835
g_assert (segment != NULL);
837
if (SEGMENT_IS_INVALID (segment))
840
if (segment->start_at >= end_offset || segment->end_at <= start_offset)
843
start_offset = MAX (start_offset, segment->start_at);
844
end_offset = MIN (end_offset, segment->end_at);
846
tag = get_context_tag (ce, segment->context);
850
gint style_start_at, style_end_at;
852
style_start_at = start_offset;
853
style_end_at = end_offset;
855
if (HAS_OPTION (segment->context->definition, STYLE_INSIDE))
857
style_start_at = MAX (segment->start_at + segment->start_len, start_offset);
858
style_end_at = MIN (segment->end_at - segment->end_len, end_offset);
861
if (style_start_at > style_end_at)
863
g_critical ("%s: oops", G_STRLOC);
867
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, style_start_at);
868
end_iter = start_iter;
869
gtk_text_iter_forward_chars (&end_iter, style_end_at - style_start_at);
870
gtk_text_buffer_apply_tag (ce->priv->buffer, tag, &start_iter, &end_iter);
874
for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
876
if (sp->start_at >= start_offset && sp->end_at <= end_offset)
878
gint start = MAX (start_offset, sp->start_at);
879
gint end = MIN (end_offset, sp->end_at);
881
tag = get_subpattern_tag (ce, segment->context, sp->definition);
885
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
886
end_iter = start_iter;
887
gtk_text_iter_forward_chars (&end_iter, end - start);
888
gtk_text_buffer_apply_tag (ce->priv->buffer, tag, &start_iter, &end_iter);
893
for (child = segment->children;
894
child != NULL && child->start_at < end_offset;
897
if (child->end_at > start_offset)
898
apply_tags (ce, child, start_offset, end_offset);
905
* @ce: a #GtkSourceContextEngine.
906
* @start: the beginning of the region to highlight.
907
* @end: the end of the region to highlight.
909
* Highlights the specified region.
912
highlight_region (GtkSourceContextEngine *ce,
916
#ifdef ENABLE_PROFILE
920
if (gtk_text_iter_starts_line (end))
921
gtk_text_iter_backward_char (end);
922
if (gtk_text_iter_compare (start, end) >= 0)
925
#ifdef ENABLE_PROFILE
926
timer = g_timer_new ();
929
/* First we need to delete tags in the regions. */
930
unhighlight_region (ce, start, end);
932
apply_tags (ce, ce->priv->root_segment,
933
gtk_text_iter_get_offset (start),
934
gtk_text_iter_get_offset (end));
936
#ifdef ENABLE_PROFILE
937
g_print ("highlight (from %d to %d), %g ms elapsed\n",
938
gtk_text_iter_get_offset (start),
939
gtk_text_iter_get_offset (end),
940
g_timer_elapsed (timer, NULL) * 1000);
941
g_timer_destroy (timer);
946
* ensure_highlighted:
948
* @ce: a #GtkSourceContextEngine.
949
* @start: the beginning of the region to highlight.
950
* @end: the end of the region to highlight.
952
* Updates text tags in reanalyzed parts of given area.
953
* It applies tags according to whatever is in the syntax
954
* tree currently, so highlighting may not be correct
955
* (gtk_source_context_engine_update_highlight is the method
956
* that actually ensures correct highlighting).
959
ensure_highlighted (GtkSourceContextEngine *ce,
960
const GtkTextIter *start,
961
const GtkTextIter *end)
963
GtkTextRegion *region;
964
GtkTextRegionIterator reg_iter;
966
/* Get the subregions not yet highlighted. */
967
region = gtk_text_region_intersect (ce->priv->refresh_region, start, end);
972
gtk_text_region_get_iterator (region, ®_iter, 0);
974
/* Highlight all subregions from the intersection.
975
* hopefully this will only be one subregion. */
976
while (!gtk_text_region_iterator_is_end (®_iter))
979
gtk_text_region_iterator_get_subregion (®_iter, &s, &e);
980
highlight_region (ce, &s, &e);
981
gtk_text_region_iterator_next (®_iter);
984
gtk_text_region_destroy (region, TRUE);
986
/* Remove the just highlighted region. */
987
gtk_text_region_subtract (ce->priv->refresh_region, start, end);
991
get_context_class_tag (GtkSourceContextEngine *ce,
996
ret = g_hash_table_lookup (ce->priv->context_classes, name);
1000
ret = gtk_text_buffer_create_tag (ce->priv->buffer, NULL, NULL);
1001
g_object_set_data_full (G_OBJECT (ret),
1002
TAG_CONTEXT_CLASS_NAME,
1004
(GDestroyNotify)g_free);
1006
g_hash_table_insert (ce->priv->context_classes,
1015
extend_context_classes (GtkSourceContextEngine *ce,
1016
GSList *definitions)
1021
for (item = definitions; item != NULL; item = g_slist_next (item))
1023
GtkSourceContextClass *cclass = item->data;
1024
ContextClassTag *attrtag = context_class_tag_new (get_context_class_tag (ce, cclass->name),
1027
ret = g_slist_prepend (ret, attrtag);
1030
return g_slist_reverse (ret);
1034
get_subpattern_context_classes (GtkSourceContextEngine *ce,
1036
SubPatternDefinition *sp_def)
1038
g_assert (sp_def->index < context->definition->n_sub_patterns);
1040
if (context->subpattern_context_classes == NULL)
1041
context->subpattern_context_classes = g_new0 (GSList *, context->definition->n_sub_patterns);
1043
if (context->subpattern_context_classes[sp_def->index] == NULL)
1045
context->subpattern_context_classes[sp_def->index] =
1046
extend_context_classes (ce,
1047
sp_def->context_classes);
1050
return context->subpattern_context_classes[sp_def->index];
1054
get_context_classes (GtkSourceContextEngine *ce,
1057
if (context->context_classes == NULL)
1059
context->context_classes =
1060
extend_context_classes (ce,
1061
context->definition->context_classes);
1064
return context->context_classes;
1068
apply_context_classes (GtkSourceContextEngine *ce,
1069
GSList *context_classes,
1073
GtkTextIter start_iter;
1074
GtkTextIter end_iter;
1077
gtk_text_buffer_get_iter_at_offset (ce->priv->buffer, &start_iter, start);
1078
end_iter = start_iter;
1079
gtk_text_iter_forward_chars (&end_iter, end - start);
1081
for (item = context_classes; item != NULL; item = g_slist_next (item))
1083
ContextClassTag *attrtag = item->data;
1085
if (attrtag->enabled)
1087
gtk_text_buffer_apply_tag (ce->priv->buffer,
1094
gtk_text_buffer_remove_tag (ce->priv->buffer,
1103
add_region_context_classes (GtkSourceContextEngine *ce,
1110
GSList *context_classes;
1112
g_assert (segment != NULL);
1114
if (SEGMENT_IS_INVALID (segment))
1119
if (segment->start_at >= end_offset || segment->end_at <= start_offset)
1124
start_offset = MAX (start_offset, segment->start_at);
1125
end_offset = MIN (end_offset, segment->end_at);
1127
context_classes = get_context_classes (ce,
1130
if (context_classes != NULL)
1132
apply_context_classes (ce,
1138
for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
1140
if (sp->start_at >= start_offset && sp->end_at <= end_offset)
1142
gint start = MAX (start_offset, sp->start_at);
1143
gint end = MIN (end_offset, sp->end_at);
1145
context_classes = get_subpattern_context_classes (ce,
1149
if (context_classes != NULL)
1151
apply_context_classes (ce,
1159
for (child = segment->children;
1160
child != NULL && child->start_at < end_offset;
1161
child = child->next)
1163
if (child->end_at > start_offset)
1165
add_region_context_classes (ce, child, start_offset, end_offset);
1171
remove_region_context_class_cb (G_GNUC_UNUSED gpointer class,
1175
struct BufAndIters *data = user_data;
1176
gtk_text_buffer_remove_tag (data->buffer,
1183
remove_region_context_classes (GtkSourceContextEngine *ce,
1184
const GtkTextIter *start,
1185
const GtkTextIter *end)
1187
struct BufAndIters data;
1189
data.buffer = ce->priv->buffer;
1193
if (gtk_text_iter_equal (start, end))
1196
g_hash_table_foreach (ce->priv->context_classes,
1197
(GHFunc) remove_region_context_class_cb,
1202
refresh_context_classes (GtkSourceContextEngine *ce,
1203
const GtkTextIter *start,
1204
const GtkTextIter *end)
1206
#ifdef ENABLE_PROFILE
1209
GtkTextIter realend = *end;
1211
if (gtk_text_iter_starts_line (&realend))
1213
gtk_text_iter_backward_char (&realend);
1216
if (gtk_text_iter_compare (start, &realend) >= 0)
1221
#ifdef ENABLE_PROFILE
1222
timer = g_timer_new ();
1225
/* First we need to delete tags in the regions. */
1226
remove_region_context_classes (ce, start, &realend);
1228
add_region_context_classes (ce,
1229
ce->priv->root_segment,
1230
gtk_text_iter_get_offset (start),
1231
gtk_text_iter_get_offset (&realend));
1233
#ifdef ENABLE_PROFILE
1234
g_print ("applied context classes (from %d to %d), %g ms elapsed\n",
1235
gtk_text_iter_get_offset (start),
1236
gtk_text_iter_get_offset (&realend),
1237
g_timer_elapsed (timer, NULL) * 1000);
1238
g_timer_destroy (timer);
1245
* @ce: a #GtkSourceContextEngine.
1246
* @start: the beginning of updated area.
1247
* @end: the end of updated area.
1248
* @modify_refresh_region: whether updated area should be added to
1251
* Marks the area as updated - notifies view about it, and adds it to
1252
* refresh_region if @modify_refresh_region is %TRUE (update_syntax may
1253
* process huge area though actually updated is couple of lines, so in
1254
* that case update_syntax() takes care of refresh_region, and this
1255
* function only notifies the view).
1258
refresh_range (GtkSourceContextEngine *ce,
1259
const GtkTextIter *start,
1260
const GtkTextIter *end,
1261
gboolean modify_refresh_region)
1263
GtkTextIter real_end;
1265
if (gtk_text_iter_equal (start, end))
1268
if (modify_refresh_region)
1269
gtk_text_region_add (ce->priv->refresh_region, start, end);
1271
/* Refresh the contex classes here */
1272
refresh_context_classes (ce, start, end);
1274
/* Here we need to make sure we do not make it redraw next line */
1276
if (gtk_text_iter_starts_line (&real_end))
1277
/* I don't quite like this here, but at least it won't jump into
1278
* the middle of \r\n */
1279
gtk_text_iter_backward_cursor_position (&real_end);
1281
g_signal_emit_by_name (ce->priv->buffer,
1282
"highlight_updated",
1288
/* SEGMENT TREE ----------------------------------------------------------- */
1293
* @s1: first segment.
1294
* @s2: second segment.
1296
* Compares segments by their offset, used to sort list of invalid segments.
1298
* Returns: an integer like strcmp() does.
1301
segment_cmp (Segment *s1,
1304
if (s1->start_at < s2->start_at)
1306
else if (s1->start_at > s2->start_at)
1308
/* one of them must be zero-length */
1309
g_assert (s1->start_at == s1->end_at || s2->start_at == s2->end_at);
1311
/* A new zero-length segment should never be created if there is
1312
* already an invalid segment. */
1313
g_assert_not_reached ();
1315
g_return_val_if_reached (s1->end_at < s2->end_at ? -1 :
1316
(s1->end_at > s2->end_at ? 1 : 0));
1323
* @segment: segment.
1325
* Inserts segment into the list of invalid segments.
1326
* Called whenever new invalid segment is created or when
1327
* a segment is marked invalid.
1330
add_invalid (GtkSourceContextEngine *ce,
1333
#ifdef ENABLE_CHECK_TREE
1334
g_assert (!g_slist_find (ce->priv->invalid, segment));
1336
g_return_if_fail (SEGMENT_IS_INVALID (segment));
1338
ce->priv->invalid = g_slist_insert_sorted (ce->priv->invalid,
1340
(GCompareFunc) segment_cmp);
1342
DEBUG (g_print ("%d invalid\n", g_slist_length (ce->priv->invalid)));
1349
* @segment: segment.
1351
* Removes segment from the list of invalid segments;
1352
* Called when an invalid segment is destroyed (invalid
1353
* segments never become valid).
1356
remove_invalid (GtkSourceContextEngine *ce,
1359
g_assert (g_slist_find (ce->priv->invalid, segment) != NULL);
1360
ce->priv->invalid = g_slist_remove (ce->priv->invalid, segment);
1364
* fix_offsets_insert_:
1366
* @segment: segment.
1367
* @start: start offset.
1368
* @delta: length of inserted text.
1370
* Recursively updates offsets after inserting text. To be called
1371
* only from insert_range().
1374
fix_offsets_insert_ (Segment *segment,
1381
g_assert (segment->start_at >= start);
1386
segment->start_at += delta;
1387
segment->end_at += delta;
1389
for (child = segment->children; child != NULL; child = child->next)
1390
fix_offsets_insert_ (child, start, delta);
1392
for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
1394
sp->start_at += delta;
1395
sp->end_at += delta;
1400
* find_insertion_place_forward_:
1402
* @segment: (grand)parent segment the new one should be inserted into.
1403
* @offset: offset at which text is inserted.
1404
* @start: segment from which to start search (to avoid
1405
* walking whole tree).
1406
* @parent: initialized with the parent of new segment.
1407
* @prev: initialized with the previous sibling of new segment.
1408
* @next: initialized with the next sibling of new segment.
1410
* Auxiliary function used in find_insertion_place().
1413
find_insertion_place_forward_ (Segment *segment,
1422
g_assert (start->end_at < offset);
1424
for (child = start; child != NULL; child = child->next)
1426
if (child->start_at <= offset && child->end_at >= offset)
1428
find_insertion_place (child, offset, parent, prev, next, NULL);
1432
if (child->end_at == offset)
1434
if (SEGMENT_IS_INVALID (child))
1443
*next = child->next;
1450
if (child->end_at < offset)
1456
if (child->start_at > offset)
1462
g_assert_not_reached ();
1469
* find_insertion_place_backward_:
1471
* @segment: (grand)parent segment the new one should be inserted into.
1472
* @offset: offset at which text is inserted.
1473
* @start: segment from which to start search (to avoid
1474
* walking whole tree).
1475
* @parent: initialized with the parent of new segment.
1476
* @prev: initialized with the previous sibling of new segment.
1477
* @next: initialized with the next sibling of new segment.
1479
* Auxiliary function used in find_insertion_place().
1482
find_insertion_place_backward_ (Segment *segment,
1491
g_assert (start->end_at >= offset);
1493
for (child = start; child != NULL; child = child->prev)
1495
if (child->start_at <= offset && child->end_at >= offset)
1497
find_insertion_place (child, offset, parent, prev, next, NULL);
1501
if (child->end_at == offset)
1503
if (SEGMENT_IS_INVALID (child))
1512
*next = child->next;
1519
if (child->end_at < offset)
1522
*next = child->next;
1526
if (child->start_at > offset)
1532
g_assert_not_reached ();
1539
* find_insertion_place:
1541
* @segment: (grand)parent segment the new one should be inserted into.
1542
* @offset: offset at which text is inserted.
1543
* @start: segment from which to start search (to avoid
1544
* walking whole tree).
1545
* @parent: initialized with the parent of new segment.
1546
* @prev: initialized with the previous sibling of new segment.
1547
* @hint: a segment somewhere near insertion place to optimize search.
1549
* After text is inserted, a new invalid segment is created and inserted
1550
* into the tree. This function finds an appropriate position for the new
1551
* segment. To make it faster, it uses hint and calls
1552
* find_insertion_place_forward_ or find_insertion_place_backward_ depending
1553
* on position of offset relative to hint.
1554
* There is no return value, it always succeeds (or crashes).
1557
find_insertion_place (Segment *segment,
1564
g_assert (segment->start_at <= offset && segment->end_at >= offset);
1569
if (SEGMENT_IS_INVALID (segment) || segment->children == NULL)
1575
if (segment->start_at == offset)
1577
#ifdef ENABLE_CHECK_TREE
1578
g_assert (!segment->children ||
1579
!SEGMENT_IS_INVALID (segment->children) ||
1580
segment->children->start_at > offset);
1584
*next = segment->children;
1590
while (hint != NULL && hint->parent != segment)
1591
hint = hint->parent;
1594
hint = segment->children;
1596
if (hint->end_at < offset)
1597
find_insertion_place_forward_ (segment, offset, hint, parent, prev, next);
1599
find_insertion_place_backward_ (segment, offset, hint, parent, prev, next);
1606
* @offset: the offset.
1608
* Finds invalid segment adjacent to offset (i.e. such that start <= offset <= end),
1611
* Returns: invalid segment or %NULL.
1614
get_invalid_at (GtkSourceContextEngine *ce,
1617
GSList *link = ce->priv->invalid;
1619
while (link != NULL)
1621
Segment *segment = link->data;
1625
if (segment->start_at > offset)
1628
if (segment->end_at < offset)
1638
* segment_add_subpattern:
1640
* @state: the segment.
1643
* Prepends subpattern to subpatterns list in the segment.
1646
segment_add_subpattern (Segment *state,
1649
sp->next = state->sub_patterns;
1650
state->sub_patterns = sp;
1656
* @segment: the segment.
1657
* @start_at: start offset of the subpattern.
1658
* @end_at: end offset of the subpattern.
1659
* @sp_def: the subppatern definition.
1661
* Creates new subpattern and adds it to the segment's
1664
* Returns: new subpattern.
1667
sub_pattern_new (Segment *segment,
1670
SubPatternDefinition *sp_def)
1674
sp = g_slice_new0 (SubPattern);
1675
sp->start_at = start_at;
1676
sp->end_at = end_at;
1677
sp->definition = sp_def;
1679
segment_add_subpattern (segment, sp);
1689
* Calls g_free on subpattern, was useful for debugging.
1692
sub_pattern_free (SubPattern *sp)
1695
memset (sp, 1, sizeof (SubPattern));
1697
g_slice_free (SubPattern, sp);
1702
* segment_make_invalid_:
1705
* @segment: segment to invalidate.
1707
* Invalidates segment. Called only from insert_range().
1710
segment_make_invalid_ (GtkSourceContextEngine *ce,
1716
g_assert (!SEGMENT_IS_INVALID (segment));
1718
sp = segment->sub_patterns;
1719
segment->sub_patterns = NULL;
1723
SubPattern *next = sp->next;
1724
sub_pattern_free (sp);
1728
ctx = segment->context;
1729
segment->context = NULL;
1730
segment->is_start = FALSE;
1731
segment->start_len = 0;
1732
segment->end_len = 0;
1733
add_invalid (ce, segment);
1734
context_unref (ctx);
1738
* simple_segment_split_:
1741
* @segment: segment to split.
1742
* @offset: offset at which text insertion occurred.
1744
* Creates a new invalid segment and inserts it in the middle
1745
* of the given one. Called from insert_range() to mark inserted
1748
* Returns: new invalid segment.
1751
simple_segment_split_ (GtkSourceContextEngine *ce,
1756
Segment *new_segment, *invalid;
1757
gint end_at = segment->end_at;
1759
g_assert (SEGMENT_IS_SIMPLE (segment));
1760
g_assert (segment->start_at < offset && offset < segment->end_at);
1762
sp = segment->sub_patterns;
1763
segment->sub_patterns = NULL;
1764
segment->end_at = offset;
1766
invalid = create_segment (ce, segment->parent, NULL, offset, offset, FALSE, segment);
1767
new_segment = create_segment (ce, segment->parent, segment->context, offset, end_at, FALSE, invalid);
1771
Segment *append_to = NULL;
1772
SubPattern *next = sp->next;
1774
if (sp->end_at <= offset)
1776
append_to = segment;
1778
else if (sp->start_at >= offset)
1780
append_to = new_segment;
1784
sub_pattern_new (new_segment,
1788
sp->end_at = offset;
1789
append_to = segment;
1792
segment_add_subpattern (append_to, sp);
1801
* invalidate_region:
1803
* @ce: a #GtkSourceContextEngine.
1804
* @offset: the start of invalidated area.
1805
* @length: the length of the area.
1807
* Adds the area to the invalid region and queues highlighting.
1808
* @length may be negative which means deletion; positive
1809
* means insertion; 0 means "something happened here", it's
1810
* treated as zero-length insertion.
1813
invalidate_region (GtkSourceContextEngine *ce,
1817
InvalidRegion *region = &ce->priv->invalid_region;
1818
GtkTextBuffer *buffer = ce->priv->buffer;
1822
end_offset = length >= 0 ? offset + length : offset;
1826
region->empty = FALSE;
1827
region->delta = length;
1829
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
1830
gtk_text_buffer_move_mark (buffer, region->start, &iter);
1832
gtk_text_iter_set_offset (&iter, end_offset);
1833
gtk_text_buffer_move_mark (buffer, region->end, &iter);
1837
gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->start);
1839
if (gtk_text_iter_get_offset (&iter) > offset)
1841
gtk_text_iter_set_offset (&iter, offset);
1842
gtk_text_buffer_move_mark (buffer, region->start, &iter);
1845
gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->end);
1847
if (gtk_text_iter_get_offset (&iter) < end_offset)
1849
gtk_text_iter_set_offset (&iter, end_offset);
1850
gtk_text_buffer_move_mark (buffer, region->end, &iter);
1853
region->delta += length;
1858
gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->start);
1859
start = gtk_text_iter_get_offset (&iter);
1860
gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->end);
1861
end = gtk_text_iter_get_offset (&iter);
1862
g_assert (start <= end - region->delta);
1867
install_first_update (ce);
1873
* @ce: a #GtkSourceContextEngine.
1874
* @offset: the start of new segment.
1875
* @length: the length of the segment.
1877
* Updates segment tree after insertion: it updates tree
1878
* offsets as appropriate, and inserts a new invalid segment
1879
* or extends existing invalid segment as @offset, so
1880
* after the call segment [@offset, @offset + @length) is marked
1881
* invalid in the tree.
1882
* It may be safely called with length == 0 at any moment
1883
* to invalidate some offset (and it's used here and there).
1886
insert_range (GtkSourceContextEngine *ce,
1890
Segment *parent, *prev = NULL, *next = NULL, *new_segment;
1893
/* If there is an invalid segment adjacent to offset, use it.
1894
* Otherwise, find the deepest segment to split and insert
1895
* dummy segment in there. */
1897
parent = get_invalid_at (ce, offset);
1900
find_insertion_place (ce->priv->root_segment, offset,
1901
&parent, &prev, &next,
1904
g_assert (parent->start_at <= offset);
1905
g_assert (parent->end_at >= offset);
1906
g_assert (!prev || prev->parent == parent);
1907
g_assert (!next || next->parent == parent);
1908
g_assert (!prev || prev->next == next);
1909
g_assert (!next || next->prev == prev);
1911
if (SEGMENT_IS_INVALID (parent))
1913
/* If length is zero, and we already have an invalid segment there,
1920
else if (SEGMENT_IS_SIMPLE (parent))
1922
/* If it's a simple context, then:
1923
* if one of its ends is offset, then we just invalidate it;
1924
* otherwise, we split it into two, and insert zero-lentgh
1925
* invalid segment in the middle. */
1926
if (parent->start_at < offset && parent->end_at > offset)
1928
segment = simple_segment_split_ (ce, parent, offset);
1932
segment_make_invalid_ (ce, parent);
1938
/* Just insert new zero-length invalid segment. */
1940
new_segment = segment_new (ce, parent, NULL, offset, offset, FALSE);
1942
new_segment->next = next;
1943
new_segment->prev = prev;
1946
next->prev = new_segment;
1948
parent->last_child = new_segment;
1951
prev->next = new_segment;
1953
parent->children = new_segment;
1955
segment = new_segment;
1958
g_assert (!segment->children);
1962
/* now fix offsets in all the segments "to the right"
1964
while (segment != NULL)
1969
for (tmp = segment->next; tmp != NULL; tmp = tmp->next)
1970
fix_offsets_insert_ (tmp, offset, length);
1972
segment->end_at += length;
1974
for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
1976
if (sp->start_at > offset)
1977
sp->start_at += length;
1978
if (sp->end_at > offset)
1979
sp->end_at += length;
1982
segment = segment->parent;
1990
* gtk_source_context_engine_text_inserted:
1992
* @ce: a #GtkSourceContextEngine.
1993
* @start_offset: the start of inserted text.
1994
* @end_offset: the end of inserted text.
1996
* Called from GtkTextBuffer::insert_text.
1999
gtk_source_context_engine_text_inserted (GtkSourceEngine *engine,
2004
GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
2006
/* Happens when highlighting is disabled */
2007
if (ce->priv->buffer == NULL)
2012
g_return_if_fail (start_offset < end_offset);
2014
invalidate_region (ce, start_offset, end_offset - start_offset);
2016
/* If end_offset is at the start of a line (enter key pressed) then
2017
* we need to invalidate the whole new line, otherwise it may not be
2018
* highlighted because the engine analyzes the previous line, end
2019
* context there is none, start context at this line is none too,
2020
* and the engine stops. */
2021
gtk_text_buffer_get_iter_at_offset (ce->priv->buffer, &iter, end_offset);
2022
if (gtk_text_iter_starts_line (&iter) && !gtk_text_iter_ends_line (&iter))
2024
gtk_text_iter_forward_to_line_end (&iter);
2025
invalidate_region (ce, gtk_text_iter_get_offset (&iter), 0);
2030
* fix_offset_delete_one_:
2033
* @start: start of deleted text.
2034
* @length: length of deleted text.
2036
* Returns: new offset depending on location of @offset
2037
* relative to deleted text.
2038
* Called only from fix_offsets_delete_().
2041
fix_offset_delete_one_ (gint offset,
2047
if (offset >= start + length)
2057
* fix_offsets_delete_:
2059
* @segment: segment.
2060
* @start: start offset.
2061
* @length: length of deleted text.
2062
* @hint: some segment somewhere near deleted text to optimize search.
2064
* Recursively updates offsets after deleting text. To be called
2065
* only from delete_range_().
2068
fix_offsets_delete_ (Segment *segment,
2076
g_return_if_fail (segment->end_at > offset);
2079
while (hint != NULL && hint->parent != segment)
2080
hint = hint->parent;
2083
hint = segment->children;
2085
for (child = hint; child != NULL; child = child->next)
2087
if (child->end_at <= offset)
2089
fix_offsets_delete_ (child, offset, length, NULL);
2092
for (child = hint ? hint->prev : NULL; child != NULL; child = child->prev)
2094
if (child->end_at <= offset)
2096
fix_offsets_delete_ (child, offset, length, NULL);
2099
for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
2101
sp->start_at = fix_offset_delete_one_ (sp->start_at, offset, length);
2102
sp->end_at = fix_offset_delete_one_ (sp->end_at, offset, length);
2105
segment->start_at = fix_offset_delete_one_ (segment->start_at, offset, length);
2106
segment->end_at = fix_offset_delete_one_ (segment->end_at, offset, length);
2112
* @ce: a #GtkSourceContextEngine.
2113
* @start: the start of deleted area.
2114
* @end: the end of deleted area.
2116
* Updates segment tree after deletion: removes segments at deleted
2117
* interval, updates tree offsets, etc.
2118
* It's called only from update_tree().
2121
delete_range_ (GtkSourceContextEngine *ce,
2125
g_return_if_fail (start < end);
2127
/* FIXME adjacent invalid segments? */
2128
erase_segments (ce, start, end, NULL);
2129
fix_offsets_delete_ (ce->priv->root_segment, start, end - start, ce->priv->hint);
2131
/* no need to invalidate at start, update_tree will do it */
2137
* gtk_source_context_engine_text_deleted:
2139
* @ce: a #GtkSourceContextEngine.
2140
* @offset: the start of deleted text.
2141
* @length: the length (in characters) of deleted text.
2143
* Called from GtkTextBuffer::delete_range.
2146
gtk_source_context_engine_text_deleted (GtkSourceEngine *engine,
2150
g_return_if_fail (length > 0);
2151
invalidate_region (GTK_SOURCE_CONTEXT_ENGINE (engine),
2157
* get_invalid_segment:
2159
* @ce: a #GtkSourceContextEngine.
2161
* Returns: first invalid segment, or %NULL.
2164
get_invalid_segment (GtkSourceContextEngine *ce)
2166
g_return_val_if_fail (ce->priv->invalid_region.empty, NULL);
2167
return ce->priv->invalid ? ce->priv->invalid->data : NULL;
2173
* @ce: a #GtkSourceContextEngine.
2175
* Returns: first invalid line, or -1.
2178
get_invalid_line (GtkSourceContextEngine *ce)
2181
gint offset = G_MAXINT;
2183
if (!ce->priv->invalid_region.empty)
2186
gtk_text_buffer_get_iter_at_mark (ce->priv->buffer,
2188
ce->priv->invalid_region.start);
2189
tmp = gtk_text_iter_get_offset (&iter);
2190
offset = MIN (offset, tmp);
2193
if (ce->priv->invalid)
2195
Segment *segment = ce->priv->invalid->data;
2196
offset = MIN (offset, segment->start_at);
2199
if (offset == G_MAXINT)
2202
gtk_text_buffer_get_iter_at_offset (ce->priv->buffer, &iter, offset);
2203
return gtk_text_iter_get_line (&iter);
2209
* @ce: a #GtkSourceContextEngine.
2211
* Modifies syntax tree according to data in invalid_region.
2214
update_tree (GtkSourceContextEngine *ce)
2216
InvalidRegion *region = &ce->priv->invalid_region;
2217
gint start, end, delta;
2218
gint erase_start, erase_end;
2224
gtk_text_buffer_get_iter_at_mark (ce->priv->buffer, &iter, region->start);
2225
start = gtk_text_iter_get_offset (&iter);
2226
gtk_text_buffer_get_iter_at_mark (ce->priv->buffer, &iter, region->end);
2227
end = gtk_text_iter_get_offset (&iter);
2229
delta = region->delta;
2231
g_assert (start <= MIN (end, end - delta));
2233
/* Here start and end are actual offsets in the buffer (they do not match offsets
2234
* in the tree if delta is not zero); delta is how much was inserted/removed.
2235
* First, we insert/delete range from the tree, to make offsets in tree
2236
* match offsets in the buffer. Then, create an invalid segment for the rest
2237
* of the area if needed. */
2240
insert_range (ce, start, delta);
2242
delete_range_ (ce, end, end - delta);
2246
erase_start = start;
2251
erase_start = start + delta;
2255
if (erase_start < erase_end)
2257
erase_segments (ce, erase_start, erase_end, NULL);
2258
create_segment (ce, ce->priv->root_segment, NULL, erase_start, erase_end, FALSE, NULL);
2260
else if (get_invalid_at (ce, start) == NULL)
2262
insert_range (ce, start, 0);
2265
region->empty = TRUE;
2267
#ifdef ENABLE_CHECK_TREE
2268
g_assert (get_invalid_at (ce, start) != NULL);
2274
* gtk_source_context_engine_update_highlight:
2276
* @ce: a #GtkSourceContextEngine.
2277
* @start: start of area to update.
2278
* @end: start of area to update.
2279
* @synchronous: whether it should block until everything
2280
* is analyzed/highlighted.
2282
* GtkSourceEngine::update_highlight method.
2284
* Makes sure the area is analyzed and highlighted. If @asynchronous
2285
* is %FALSE, then it queues idle worker.
2288
gtk_source_context_engine_update_highlight (GtkSourceEngine *engine,
2289
const GtkTextIter *start,
2290
const GtkTextIter *end,
2291
gboolean synchronous)
2295
GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
2297
if (!ce->priv->highlight || ce->priv->disabled)
2300
invalid_line = get_invalid_line (ce);
2301
end_line = gtk_text_iter_get_line (end);
2303
if (gtk_text_iter_starts_line (end) && end_line > 0)
2306
if (invalid_line < 0 || invalid_line > end_line)
2308
ensure_highlighted (ce, start, end);
2310
else if (synchronous)
2312
/* analyze whole region */
2313
update_syntax (ce, end, 0);
2314
ensure_highlighted (ce, start, end);
2318
if (gtk_text_iter_get_line (start) >= invalid_line)
2320
gtk_text_region_add (ce->priv->highlight_requests, start, end);
2324
GtkTextIter valid_end = *start;
2325
gtk_text_iter_set_line (&valid_end, invalid_line);
2326
ensure_highlighted (ce, start, &valid_end);
2327
gtk_text_region_add (ce->priv->highlight_requests, &valid_end, end);
2330
install_first_update (ce);
2337
* @ce: a #GtkSourceContextEngine.
2338
* @enable: whether to enable highlighting.
2340
* Whether to highlight (i.e. apply tags) analyzed area.
2341
* Note that this does not turn on/off the analyzis stuff,
2342
* it affects only text tags.
2345
enable_highlight (GtkSourceContextEngine *ce,
2348
GtkTextIter start, end;
2350
if (!enable == !ce->priv->highlight)
2353
ce->priv->highlight = enable != 0;
2354
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (ce->priv->buffer),
2358
refresh_range (ce, &start, &end, TRUE);
2360
unhighlight_region (ce, &start, &end);
2364
buffer_notify_highlight_syntax_cb (GtkSourceContextEngine *ce)
2367
g_object_get (ce->priv->buffer, "highlight-syntax", &highlight, NULL);
2368
enable_highlight (ce, highlight);
2372
/* IDLE WORKER CODE ------------------------------------------------------- */
2377
* @ce: a #GtkSourceContextEngine.
2379
* Returns: whether everything is analyzed (but it doesn't care about the tags).
2382
all_analyzed (GtkSourceContextEngine *ce)
2384
return ce->priv->invalid == NULL && ce->priv->invalid_region.empty;
2390
* @ce: #GtkSourceContextEngine.
2392
* Analyzes a batch in idle. Stops when
2393
* whole buffer is analyzed.
2396
idle_worker (GtkSourceContextEngine *ce)
2398
gboolean retval = TRUE;
2400
g_return_val_if_fail (ce->priv->buffer != NULL, FALSE);
2402
gdk_threads_enter ();
2404
/* analyze batch of text */
2405
update_syntax (ce, NULL, INCREMENTAL_UPDATE_TIME_SLICE);
2408
if (all_analyzed (ce))
2410
ce->priv->incremental_update = 0;
2414
gdk_threads_leave ();
2420
* first_update_callback:
2422
* @ce: a #GtkSourceContextEngine.
2424
* Same as idle_worker, except: it runs once, and install idle_worker
2425
* if not everything was analyzed at once.
2428
first_update_callback (GtkSourceContextEngine *ce)
2430
g_return_val_if_fail (ce->priv->buffer != NULL, FALSE);
2432
gdk_threads_enter ();
2434
/* analyze batch of text */
2435
update_syntax (ce, NULL, FIRST_UPDATE_TIME_SLICE);
2438
ce->priv->first_update = 0;
2440
if (!all_analyzed (ce))
2441
install_idle_worker (ce);
2443
gdk_threads_leave ();
2449
* install_idle_worker:
2451
* @ce: #GtkSourceContextEngine.
2453
* Schedules reanalyzing buffer in idle.
2454
* Always safe to call.
2457
install_idle_worker (GtkSourceContextEngine *ce)
2459
if (ce->priv->first_update == 0 && ce->priv->incremental_update == 0)
2460
ce->priv->incremental_update =
2461
g_idle_add_full (INCREMENTAL_UPDATE_PRIORITY,
2462
(GSourceFunc) idle_worker, ce, NULL);
2466
* install_first_update:
2468
* @ce: #GtkSourceContextEngine.
2470
* Schedules first_update_callback call.
2471
* Always safe to call.
2474
install_first_update (GtkSourceContextEngine *ce)
2476
if (ce->priv->first_update == 0)
2478
if (ce->priv->incremental_update != 0)
2480
g_source_remove (ce->priv->incremental_update);
2481
ce->priv->incremental_update = 0;
2484
ce->priv->first_update =
2485
g_idle_add_full (FIRST_UPDATE_PRIORITY,
2486
(GSourceFunc) first_update_callback,
2491
/* GtkSourceContextEngine class ------------------------------------------- */
2493
G_DEFINE_TYPE (GtkSourceContextEngine, _gtk_source_context_engine, GTK_TYPE_SOURCE_ENGINE)
2496
gtk_source_context_engine_error_quark (void)
2498
static GQuark err_q = 0;
2500
err_q = g_quark_from_static_string ("gtk-source-context-engine-error-quark");
2505
remove_tags_hash_cb (G_GNUC_UNUSED gpointer style,
2507
GtkTextTagTable *table)
2513
gtk_text_tag_table_remove (table, l->data);
2514
g_object_unref (l->data);
2518
g_slist_free (tags);
2522
remove_context_classes_hash_cb (G_GNUC_UNUSED gpointer class,
2524
GtkTextTagTable *table)
2526
gtk_text_tag_table_remove (table, tag);
2530
* destroy_tags_hash:
2532
* @ce: #GtkSourceContextEngine.
2534
* Destroys syntax tags cache.
2537
destroy_tags_hash (GtkSourceContextEngine *ce)
2539
g_hash_table_foreach (ce->priv->tags, (GHFunc) remove_tags_hash_cb,
2540
gtk_text_buffer_get_tag_table (ce->priv->buffer));
2541
g_hash_table_destroy (ce->priv->tags);
2542
ce->priv->tags = NULL;
2546
destroy_context_classes_hash (GtkSourceContextEngine *ce)
2548
g_hash_table_foreach (ce->priv->context_classes, (GHFunc) remove_context_classes_hash_cb,
2549
gtk_text_buffer_get_tag_table (ce->priv->buffer));
2550
g_hash_table_destroy (ce->priv->context_classes);
2551
ce->priv->context_classes = NULL;
2555
* gtk_source_context_engine_attach_buffer:
2557
* @ce: #GtkSourceContextEngine.
2560
* Detaches engine from previous buffer, and attaches to @buffer if
2564
gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
2565
GtkTextBuffer *buffer)
2567
GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
2569
g_return_if_fail (!buffer || GTK_IS_TEXT_BUFFER (buffer));
2571
if (ce->priv->buffer == buffer)
2574
/* Detach previous buffer if there is one. */
2575
if (ce->priv->buffer != NULL)
2577
g_signal_handlers_disconnect_by_func (ce->priv->buffer,
2578
(gpointer) buffer_notify_highlight_syntax_cb,
2581
if (ce->priv->first_update != 0)
2582
g_source_remove (ce->priv->first_update);
2583
if (ce->priv->incremental_update != 0)
2584
g_source_remove (ce->priv->incremental_update);
2585
ce->priv->first_update = 0;
2586
ce->priv->incremental_update = 0;
2588
if (ce->priv->root_segment != NULL)
2589
segment_destroy (ce, ce->priv->root_segment);
2590
if (ce->priv->root_context != NULL)
2591
context_unref (ce->priv->root_context);
2592
g_assert (!ce->priv->invalid);
2593
g_slist_free (ce->priv->invalid);
2594
ce->priv->root_segment = NULL;
2595
ce->priv->root_context = NULL;
2596
ce->priv->invalid = NULL;
2598
if (ce->priv->invalid_region.start != NULL)
2599
gtk_text_buffer_delete_mark (ce->priv->buffer,
2600
ce->priv->invalid_region.start);
2601
if (ce->priv->invalid_region.end != NULL)
2602
gtk_text_buffer_delete_mark (ce->priv->buffer,
2603
ce->priv->invalid_region.end);
2604
ce->priv->invalid_region.start = NULL;
2605
ce->priv->invalid_region.end = NULL;
2607
/* this deletes tags from the tag table, therefore there is no need
2608
* in removing tags from the text (it may be very slow).
2609
* FIXME: don't we want to just destroy and forget everything when
2610
* the buffer is destroyed? Removing tags is still slower than doing
2611
* nothing. Caveat: if tag table is shared with other buffer, we do
2612
* need to remove tags. */
2613
destroy_tags_hash (ce);
2614
ce->priv->n_tags = 0;
2616
destroy_context_classes_hash (ce);
2618
if (ce->priv->refresh_region != NULL)
2619
gtk_text_region_destroy (ce->priv->refresh_region, FALSE);
2620
if (ce->priv->highlight_requests != NULL)
2621
gtk_text_region_destroy (ce->priv->highlight_requests, FALSE);
2622
ce->priv->refresh_region = NULL;
2623
ce->priv->highlight_requests = NULL;
2626
ce->priv->buffer = buffer;
2631
ContextDefinition *main_definition;
2632
GtkTextIter start, end;
2634
/* Create the root context. */
2635
root_id = g_strdup_printf ("%s:%s", ENGINE_ID (ce), ENGINE_ID (ce));
2636
main_definition = LOOKUP_DEFINITION (ce->priv->ctx_data, root_id);
2639
/* If we don't abort here, we will crash later (#485661). But it should
2640
* never happen, _gtk_source_context_data_finish_parse checks main context. */
2641
g_assert (main_definition != NULL);
2643
ce->priv->root_context = context_new (NULL, main_definition, NULL, NULL, FALSE);
2644
ce->priv->root_segment = create_segment (ce, NULL, ce->priv->root_context, 0, 0, TRUE, NULL);
2646
ce->priv->tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2647
ce->priv->context_classes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2649
gtk_text_buffer_get_bounds (buffer, &start, &end);
2650
ce->priv->invalid_region.start = gtk_text_buffer_create_mark (buffer, NULL,
2652
ce->priv->invalid_region.end = gtk_text_buffer_create_mark (buffer, NULL,
2655
if (gtk_text_buffer_get_char_count (buffer) != 0)
2657
ce->priv->invalid_region.empty = FALSE;
2658
ce->priv->invalid_region.delta = gtk_text_buffer_get_char_count (buffer);
2662
ce->priv->invalid_region.empty = TRUE;
2663
ce->priv->invalid_region.delta = 0;
2666
g_object_get (ce->priv->buffer, "highlight-syntax", &ce->priv->highlight, NULL);
2667
ce->priv->refresh_region = gtk_text_region_new (buffer);
2668
ce->priv->highlight_requests = gtk_text_region_new (buffer);
2670
g_signal_connect_swapped (buffer,
2671
"notify::highlight-syntax",
2672
G_CALLBACK (buffer_notify_highlight_syntax_cb),
2675
install_first_update (ce);
2680
* disable_highlighting:
2682
* @ce: #GtkSourceContextEngine.
2684
* Dsiables highlighting in case of errors (currently if highlighting
2685
* a single line took too long, so that highlighting doesn't freeze
2689
disable_highlighting (GtkSourceContextEngine *ce)
2691
if (!ce->priv->disabled)
2693
ce->priv->disabled = TRUE;
2694
gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
2695
/* FIXME maybe emit some signal here? */
2700
set_tag_style_hash_cb (const char *style,
2702
GtkSourceContextEngine *ce)
2704
while (tags != NULL)
2706
set_tag_style (ce, tags->data, style);
2712
* gtk_source_context_engine_set_style_scheme:
2714
* @engine: #GtkSourceContextEngine.
2715
* @scheme: #GtkSourceStyleScheme to set.
2717
* GtkSourceEngine::set_style_scheme method.
2718
* Sets current style scheme, updates tag styles and everything.
2721
gtk_source_context_engine_set_style_scheme (GtkSourceEngine *engine,
2722
GtkSourceStyleScheme *scheme)
2724
GtkSourceContextEngine *ce;
2726
g_return_if_fail (GTK_IS_SOURCE_CONTEXT_ENGINE (engine));
2727
g_return_if_fail (GTK_IS_SOURCE_STYLE_SCHEME (scheme) || scheme == NULL);
2729
ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
2731
if (scheme == ce->priv->style_scheme)
2734
if (ce->priv->style_scheme != NULL)
2735
g_object_unref (ce->priv->style_scheme);
2737
ce->priv->style_scheme = scheme ? g_object_ref (scheme) : NULL;
2738
g_hash_table_foreach (ce->priv->tags, (GHFunc) set_tag_style_hash_cb, ce);
2742
gtk_source_context_engine_finalize (GObject *object)
2744
GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (object);
2746
if (ce->priv->buffer != NULL)
2748
g_critical ("finalizing engine with attached buffer");
2749
/* Disconnect the buffer (if there is one), which destroys almost
2751
gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
2754
#ifdef ENABLE_MEMORY_DEBUG
2755
if (ce->priv->mem_usage_timeout)
2756
g_source_remove (ce->priv->mem_usage_timeout);
2759
g_assert (!ce->priv->tags);
2760
g_assert (!ce->priv->root_context);
2761
g_assert (!ce->priv->root_segment);
2762
g_assert (!ce->priv->first_update);
2763
g_assert (!ce->priv->incremental_update);
2765
_gtk_source_context_data_unref (ce->priv->ctx_data);
2767
if (ce->priv->style_scheme != NULL)
2768
g_object_unref (ce->priv->style_scheme);
2770
G_OBJECT_CLASS (_gtk_source_context_engine_parent_class)->finalize (object);
2774
gtk_source_context_engine_get_context_class_tag (GtkSourceEngine *engine,
2775
const gchar *context_class)
2777
GHashTable *hash_table = GTK_SOURCE_CONTEXT_ENGINE (engine)->priv->context_classes;
2779
if (hash_table == NULL)
2781
/* This happens when highlighting is disabled */
2785
return g_hash_table_lookup (hash_table,
2790
_gtk_source_context_engine_class_init (GtkSourceContextEngineClass *klass)
2792
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2793
GtkSourceEngineClass *engine_class = GTK_SOURCE_ENGINE_CLASS (klass);
2795
object_class->finalize = gtk_source_context_engine_finalize;
2797
engine_class->attach_buffer = gtk_source_context_engine_attach_buffer;
2798
engine_class->text_inserted = gtk_source_context_engine_text_inserted;
2799
engine_class->text_deleted = gtk_source_context_engine_text_deleted;
2800
engine_class->update_highlight = gtk_source_context_engine_update_highlight;
2801
engine_class->set_style_scheme = gtk_source_context_engine_set_style_scheme;
2802
engine_class->get_context_class_tag = gtk_source_context_engine_get_context_class_tag;
2804
g_type_class_add_private (object_class, sizeof (GtkSourceContextEnginePrivate));
2808
_gtk_source_context_engine_init (GtkSourceContextEngine *ce)
2810
ce->priv = G_TYPE_INSTANCE_GET_PRIVATE (ce, GTK_TYPE_SOURCE_CONTEXT_ENGINE,
2811
GtkSourceContextEnginePrivate);
2814
GtkSourceContextEngine *
2815
_gtk_source_context_engine_new (GtkSourceContextData *ctx_data)
2817
GtkSourceContextEngine *ce;
2819
g_return_val_if_fail (ctx_data != NULL, NULL);
2820
g_return_val_if_fail (ctx_data->lang != NULL, NULL);
2822
ce = g_object_new (GTK_TYPE_SOURCE_CONTEXT_ENGINE, NULL);
2823
ce->priv->ctx_data = _gtk_source_context_data_ref (ctx_data);
2825
#ifdef ENABLE_MEMORY_DEBUG
2826
ce->priv->mem_usage_timeout =
2827
g_timeout_add (5000, (GSourceFunc) mem_usage_timeout, ce);
2834
* _gtk_source_context_data_new:
2836
* @lang: #GtkSourceLanguage.
2838
* Creates new context definition set. It does not set lang->priv->ctx_data,
2839
* that's lang business.
2841
GtkSourceContextData *
2842
_gtk_source_context_data_new (GtkSourceLanguage *lang)
2844
GtkSourceContextData *ctx_data;
2846
g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (lang), NULL);
2848
ctx_data = g_slice_new0 (GtkSourceContextData);
2849
ctx_data->ref_count = 1;
2850
ctx_data->lang = lang;
2851
ctx_data->definitions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2852
(GDestroyNotify) context_definition_unref);
2857
GtkSourceContextData *
2858
_gtk_source_context_data_ref (GtkSourceContextData *ctx_data)
2860
g_return_val_if_fail (ctx_data != NULL, NULL);
2861
ctx_data->ref_count++;
2866
* _gtk_source_context_data_unref:
2868
* @ctx_data: #GtkSourceContextData.
2870
* Decreases reference count in ctx_data. When reference count
2871
* drops to zero, ctx_data is freed, and ctx_data->lang->priv->ctx_data
2875
_gtk_source_context_data_unref (GtkSourceContextData *ctx_data)
2877
g_return_if_fail (ctx_data != NULL);
2879
if (--ctx_data->ref_count == 0)
2881
if (ctx_data->lang != NULL && ctx_data->lang->priv != NULL &&
2882
ctx_data->lang->priv->ctx_data == ctx_data)
2883
ctx_data->lang->priv->ctx_data = NULL;
2884
g_hash_table_destroy (ctx_data->definitions);
2885
g_slice_free (GtkSourceContextData, ctx_data);
2889
/* REGEX HANDLING --------------------------------------------------------- */
2892
regex_ref (Regex *regex)
2900
regex_unref (Regex *regex)
2902
if (regex != NULL && --regex->ref_count == 0)
2904
if (regex->resolved)
2906
g_regex_unref (regex->u.regex.regex);
2907
if (regex->u.regex.match)
2908
g_match_info_free (regex->u.regex.match);
2911
g_free (regex->u.info.pattern);
2912
g_slice_free (Regex, regex);
2917
* find_single_byte_escape:
2919
* @string: the pattern.
2921
* Checks whether pattern contains \C escape sequence,
2922
* which means "single byte" in pcre and naturally leads
2923
* to crash if used for highlighting.
2926
find_single_byte_escape (const gchar *string)
2928
const char *p = string;
2930
while ((p = strstr (p, "\\C")))
2941
while (slash >= string && *slash == '\\')
2959
* @pattern: the regular expression.
2960
* @flags: compile options for @pattern.
2961
* @error: location to store the error occuring, or %NULL to ignore errors.
2963
* Creates a new regex.
2965
* Returns: a newly-allocated #Regex.
2968
regex_new (const gchar *pattern,
2969
GRegexCompileFlags flags,
2973
static GRegex *start_ref_re = NULL;
2975
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2977
if (find_single_byte_escape (pattern))
2979
g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
2980
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REGEX,
2981
"%s", _("using \\C is not supported in language definitions"));
2985
regex = g_slice_new0 (Regex);
2986
regex->ref_count = 1;
2988
if (start_ref_re == NULL)
2989
start_ref_re = g_regex_new (START_REF_REGEX,
2990
/* http://bugzilla.gnome.org/show_bug.cgi?id=455640
2991
* we don't care about line ends anyway */
2992
G_REGEX_OPTIMIZE | G_REGEX_NEWLINE_LF,
2996
if (g_regex_match (start_ref_re, pattern, 0, NULL))
2998
regex->resolved = FALSE;
2999
regex->u.info.pattern = g_strdup (pattern);
3000
regex->u.info.flags = flags;
3004
regex->resolved = TRUE;
3005
regex->u.regex.regex = g_regex_new (pattern,
3006
flags | G_REGEX_OPTIMIZE | G_REGEX_NEWLINE_LF, 0,
3009
if (regex->u.regex.regex == NULL)
3011
g_slice_free (Regex, regex);
3020
* sub_pattern_to_int:
3022
* @name: the string from lang file.
3024
* Tries to convert @name to a number and assumes
3025
* it's a name if that fails. Used for references in
3026
* subpattern contexts (e.g. \%{1@start} or \%{blah@start}).
3029
sub_pattern_to_int (const gchar *name)
3038
number = g_ascii_strtoull (name, &end_name, 10);
3040
if (errno !=0 || number > G_MAXINT || *end_name != 0)
3046
struct RegexResolveData {
3048
const gchar *matched_text;
3052
replace_start_regex (const GMatchInfo *match_info,
3053
GString *expanded_regex,
3056
gchar *num_string, *subst, *subst_escaped, *escapes;
3058
struct RegexResolveData *data = user_data;
3060
escapes = g_match_info_fetch (match_info, 1);
3061
num_string = g_match_info_fetch (match_info, 2);
3062
num = sub_pattern_to_int (num_string);
3065
subst = g_match_info_fetch_named (data->start_regex->u.regex.match,
3068
subst = g_match_info_fetch (data->start_regex->u.regex.match,
3073
subst_escaped = g_regex_escape_string (subst, -1);
3077
g_warning ("Invalid group: %s", num_string);
3078
subst_escaped = g_strdup ("");
3081
g_string_append (expanded_regex, escapes);
3082
g_string_append (expanded_regex, subst_escaped);
3085
g_free (num_string);
3087
g_free (subst_escaped);
3096
* @start_regex: a #Regex.
3097
* @matched_text: the text matched against @start_regex.
3099
* If the regular expression does not contain references to the start
3100
* regular expression, the functions increases the reference count
3101
* of @regex and returns it.
3103
* If the regular expression contains references to the start regular
3104
* expression in the form "\%{start_sub_pattern@start}", it replaces
3105
* them (they are extracted from @start_regex and @matched_text) and
3106
* returns the new regular expression.
3108
* Returns: a #Regex.
3111
regex_resolve (Regex *regex,
3113
const gchar *matched_text)
3116
gchar *expanded_regex;
3118
struct RegexResolveData data;
3120
if (regex == NULL || regex->resolved)
3121
return regex_ref (regex);
3123
start_ref = g_regex_new (START_REF_REGEX, G_REGEX_NEWLINE_LF, 0, NULL);
3124
data.start_regex = start_regex;
3125
data.matched_text = matched_text;
3126
expanded_regex = g_regex_replace_eval (start_ref,
3127
regex->u.info.pattern,
3129
replace_start_regex,
3131
new_regex = regex_new (expanded_regex, regex->u.info.flags, NULL);
3133
if (new_regex == NULL || !new_regex->resolved)
3135
regex_unref (new_regex);
3136
g_warning ("Regular expression %s cannot be expanded.",
3137
regex->u.info.pattern);
3138
/* Returns a regex that nevers matches. */
3139
new_regex = regex_new ("$never-match^", 0, NULL);
3142
g_free (expanded_regex);
3143
g_regex_unref (start_ref);
3148
regex_match (Regex *regex,
3155
g_assert (regex->resolved);
3157
if (regex->u.regex.match)
3159
g_match_info_free (regex->u.regex.match);
3160
regex->u.regex.match = NULL;
3163
result = g_regex_match_full (regex->u.regex.regex, line,
3164
byte_length, byte_pos,
3165
0, ®ex->u.regex.match,
3172
regex_fetch (Regex *regex,
3175
g_assert (regex->resolved);
3176
return g_match_info_fetch (regex->u.regex.match, num);
3180
regex_fetch_pos (Regex *regex,
3183
gint *start_pos, /* character offsets */
3184
gint *end_pos) /* character offsets */
3186
gint byte_start_pos, byte_end_pos;
3188
g_assert (regex->resolved);
3190
if (!g_match_info_fetch_pos (regex->u.regex.match, num, &byte_start_pos, &byte_end_pos))
3192
if (start_pos != NULL)
3194
if (end_pos != NULL)
3199
if (start_pos != NULL)
3200
*start_pos = g_utf8_pointer_to_offset (text, text + byte_start_pos);
3201
if (end_pos != NULL)
3202
*end_pos = g_utf8_pointer_to_offset (text, text + byte_end_pos);
3207
regex_fetch_pos_bytes (Regex *regex,
3209
gint *start_pos_p, /* byte offsets */
3210
gint *end_pos_p) /* byte offsets */
3215
g_assert (regex->resolved);
3217
if (!g_match_info_fetch_pos (regex->u.regex.match, num, &start_pos, &end_pos))
3223
if (start_pos_p != NULL)
3224
*start_pos_p = start_pos;
3225
if (end_pos_p != NULL)
3226
*end_pos_p = end_pos;
3230
regex_fetch_named_pos (Regex *regex,
3233
gint *start_pos, /* character offsets */
3234
gint *end_pos) /* character offsets */
3236
gint byte_start_pos, byte_end_pos;
3238
g_assert (regex->resolved);
3240
if (!g_match_info_fetch_named_pos (regex->u.regex.match, name, &byte_start_pos, &byte_end_pos))
3242
if (start_pos != NULL)
3244
if (end_pos != NULL)
3249
if (start_pos != NULL)
3250
*start_pos = g_utf8_pointer_to_offset (text, text + byte_start_pos);
3251
if (end_pos != NULL)
3252
*end_pos = g_utf8_pointer_to_offset (text, text + byte_end_pos);
3256
static const gchar *
3257
regex_get_pattern (Regex *regex)
3259
g_return_val_if_fail (regex && regex->resolved, "");
3260
return g_regex_get_pattern (regex->u.regex.regex);
3263
/* SYNTAX TREE ------------------------------------------------------------ */
3266
* apply_sub_patterns:
3268
* @contextstate: a #Context.
3269
* @line_starts_at: beginning offset of the line.
3270
* @line: the line to analyze.
3271
* @line_pos: the position inside @line.
3272
* @line_length: the length of @line.
3273
* @regex: regex that matched.
3274
* @where: kind of sub patterns to apply.
3276
* Applies sub patterns of kind @where to the matched text.
3279
apply_sub_patterns (Segment *state,
3282
SubPatternWhere where)
3284
GSList *sub_pattern_list = state->context->definition->sub_patterns;
3286
if (SEGMENT_IS_CONTAINER (state))
3291
regex_fetch_pos (regex, line->text, 0, &start_pos, &end_pos);
3293
if (where == SUB_PATTERN_WHERE_START)
3295
if (line->start_at + start_pos != state->start_at)
3296
g_critical ("%s: oops", G_STRLOC);
3297
else if (line->start_at + end_pos > state->end_at)
3298
g_critical ("%s: oops", G_STRLOC);
3300
state->start_len = line->start_at + end_pos - state->start_at;
3304
if (line->start_at + start_pos < state->start_at)
3305
g_critical ("%s: oops", G_STRLOC);
3306
else if (line->start_at + end_pos != state->end_at)
3307
g_critical ("%s: oops", G_STRLOC);
3309
state->end_len = state->end_at - line->start_at - start_pos;
3313
while (sub_pattern_list != NULL)
3315
SubPatternDefinition *sp_def = sub_pattern_list->data;
3317
if (sp_def->where == where)
3322
if (sp_def->is_named)
3323
regex_fetch_named_pos (regex,
3329
regex_fetch_pos (regex,
3335
if (start_pos >= 0 && start_pos != end_pos)
3337
sub_pattern_new (state,
3338
line->start_at + start_pos,
3339
line->start_at + end_pos,
3344
sub_pattern_list = sub_pattern_list->next;
3351
* @state: the current state of the parser.
3352
* @line: the line to analyze.
3353
* @match_start: start position of match, bytes.
3354
* @match_end: where to put end of match, bytes.
3355
* @where: kind of sub patterns to apply.
3357
* See apply_match(), this function is a helper function
3358
* called from where, it doesn't modify syntax tree.
3360
* Returns: %TRUE if the match can be applied.
3363
can_apply_match (Context *state,
3370
gboolean ancestor_ends;
3373
ancestor_ends = FALSE;
3374
/* end_match_pos is the position of the end of the matched regex. */
3375
regex_fetch_pos_bytes (regex, 0, NULL, &end_match_pos);
3377
g_assert (end_match_pos <= line->byte_length);
3379
/* Verify if an ancestor ends in the matched text. */
3380
if (ANCESTOR_CAN_END_CONTEXT (state) &&
3381
/* there is no middle of zero-length match */
3382
match_start < end_match_pos)
3384
pos = match_start + 1;
3386
while (pos < end_match_pos)
3388
if (ancestor_context_ends_here (state, line, pos))
3390
ancestor_ends = TRUE;
3394
pos = g_utf8_next_char (line->text + pos) - line->text;
3399
pos = end_match_pos;
3404
/* An ancestor ends in the middle of the match, we verify
3405
* if the regex matches against the available string before
3406
* the end of the ancestor.
3407
* For instance in C a net-address context matches even if
3408
* it contains the end of a multi-line comment. */
3409
if (!regex_match (regex, line->text, pos, match_start))
3411
/* This match is not valid, so we can try to match
3412
* the next definition, so the position should not
3423
line_pos_to_offset (LineInfo *line,
3426
if (line->char_length != line->byte_length)
3427
pos = g_utf8_pointer_to_offset (line->text, line->text + pos);
3428
return line->start_at + pos;
3434
* @state: the current state of the parser.
3435
* @line: the line to analyze.
3436
* @line_pos: position in the line, bytes.
3437
* @regex: regex that matched.
3438
* @where: kind of sub patterns to apply.
3440
* Moves @line_pos after the matched text. @line_pos is not
3441
* updated and the function returns %FALSE if the match cannot be
3442
* applied because an ancestor ends in the middle of the matched
3445
* If the match can be applied the function applies the appropriate
3448
* Returns: %TRUE if the match can be applied.
3451
apply_match (Segment *state,
3455
SubPatternWhere where)
3459
if (!can_apply_match (state->context, line, *line_pos, &match_end, regex))
3462
segment_extend (state, line_pos_to_offset (line, match_end));
3463
apply_sub_patterns (state, line, regex, where);
3464
*line_pos = match_end;
3472
* @context: context.
3473
* @definition: context definition.
3475
* Creates regular expression for all possible transitions: it
3476
* combines terminating regex, terminating regexes of parent
3477
* contexts if those can terminate this one, and start regexes
3478
* of child contexts.
3480
* It takes as an argument actual context or a context definition. In
3481
* case when context end depends on start (\%{foo@start} references),
3482
* it must use the context, definition is not enough. If there are no
3483
* those references, then the reg_all is created right in the definition
3484
* when no contexts exist yet. This is why this function has its funny
3487
* Returns: resulting regex or %NULL when pcre failed to compile the regex.
3490
create_reg_all (Context *context,
3491
ContextDefinition *definition)
3493
DefinitionsIter iter;
3494
DefinitionChild *child_def;
3497
GError *error = NULL;
3499
g_return_val_if_fail ((context == NULL && definition != NULL) ||
3500
(context != NULL && definition == NULL), NULL);
3502
if (definition == NULL)
3503
definition = context->definition;
3505
all = g_string_new ("(");
3507
/* Closing regex. */
3508
if (definition->type == CONTEXT_TYPE_CONTAINER &&
3509
definition->u.start_end.end != NULL)
3513
if (definition->u.start_end.end->resolved)
3515
end = definition->u.start_end.end;
3519
g_return_val_if_fail (context && context->end, NULL);
3523
g_string_append (all, regex_get_pattern (end));
3524
g_string_append (all, "|");
3528
if (context != NULL)
3530
Context *tmp = context;
3532
while (ANCESTOR_CAN_END_CONTEXT (tmp))
3534
if (!CONTEXT_EXTENDS_PARENT (tmp))
3536
gboolean append = TRUE;
3538
/* Code as it is seems to be right, and seems working right.
3539
* Remove FIXME's below if everything is fine. */
3541
if (tmp->parent->end != NULL)
3542
g_string_append (all, regex_get_pattern (tmp->parent->end));
3544
* The old code insisted on having tmp->parent->end != NULL here,
3545
* though e.g. in case line-comment -> email-address it's not the case.
3546
* Apparently using $ fixes the problem. */
3547
else if (CONTEXT_END_AT_LINE_END (tmp->parent))
3548
g_string_append (all, "$");
3549
/* FIXME it's not clear whether it can happen, maybe we need assert here
3550
* or parser need to check it */
3553
/* g_critical ("%s: oops", G_STRLOC); */
3558
g_string_append (all, "|");
3566
definition_iter_init (&iter, definition);
3567
while ((child_def = definition_iter_next (&iter)) != NULL)
3569
Regex *child_regex = NULL;
3571
g_return_val_if_fail (child_def->resolved, NULL);
3573
switch (child_def->u.definition->type)
3575
case CONTEXT_TYPE_CONTAINER:
3576
child_regex = child_def->u.definition->u.start_end.start;
3578
case CONTEXT_TYPE_SIMPLE:
3579
child_regex = child_def->u.definition->u.match;
3582
g_return_val_if_reached (NULL);
3585
if (child_regex != NULL)
3587
g_string_append (all, regex_get_pattern (child_regex));
3588
g_string_append (all, "|");
3591
definition_iter_destroy (&iter);
3594
g_string_truncate (all, all->len - 1);
3595
g_string_append (all, ")");
3597
regex = regex_new (all->str, 0, &error);
3601
/* regex_new could fail, for instance if there are different
3602
* named sub-patterns with the same name or if resulting regex is
3603
* too long. In this case fixing lang file helps (e.g. renaming
3604
* subpatterns, making huge keywords use bigger prefixes, etc.) */
3605
g_warning (_("Cannot create a regex for all the transitions, "
3606
"the syntax highlighting process will be slower "
3607
"than usual.\nThe error was: %s"), error->message);
3608
g_error_free (error);
3611
g_string_free (all, TRUE);
3616
context_ref (Context *context)
3618
if (context != NULL)
3619
context->ref_count++;
3623
/* does not copy style */
3625
context_new (Context *parent,
3626
ContextDefinition *definition,
3627
const gchar *line_text,
3629
gboolean ignore_children_style)
3633
context = g_slice_new0 (Context);
3634
context->ref_count = 1;
3635
context->definition = definition;
3636
context->parent = parent;
3638
context->style = style;
3639
context->ignore_children_style = ignore_children_style;
3641
if (parent != NULL && parent->ignore_children_style)
3643
context->ignore_children_style = TRUE;
3644
context->style = NULL;
3647
if (!parent || (parent->all_ancestors_extend && CONTEXT_EXTENDS_PARENT (parent)))
3649
context->all_ancestors_extend = TRUE;
3653
definition->type == CONTEXT_TYPE_CONTAINER &&
3654
definition->u.start_end.end)
3656
context->end = regex_resolve (definition->u.start_end.end,
3657
definition->u.start_end.start,
3661
/* Create reg_all. If it is possibile we share the same reg_all
3662
* for more contexts storing it in the definition. */
3663
if (ANCESTOR_CAN_END_CONTEXT (context) ||
3664
(definition->type == CONTEXT_TYPE_CONTAINER &&
3665
definition->u.start_end.end != NULL &&
3666
!definition->u.start_end.end->resolved))
3668
context->reg_all = create_reg_all (context, NULL);
3672
if (!definition->reg_all)
3673
definition->reg_all = create_reg_all (NULL, definition);
3674
context->reg_all = regex_ref (definition->reg_all);
3679
GString *str = g_string_new (definition->id);
3680
Context *tmp = context->parent;
3683
g_string_prepend (str, "/");
3684
g_string_prepend (str, tmp->definition->id);
3687
g_print ("created context %s: %s\n", definition->id, str->str);
3688
g_string_free (str, TRUE);
3696
context_unref_hash_cb (G_GNUC_UNUSED gpointer text,
3699
context->parent = NULL;
3700
context_unref (context);
3704
remove_context_cb (G_GNUC_UNUSED gpointer text,
3708
return context == target;
3712
context_remove_child (Context *parent,
3715
ContextPtr *ptr, *prev = NULL;
3716
gboolean delete = TRUE;
3718
g_assert (context->parent == parent);
3720
for (ptr = context->parent->children; ptr; ptr = ptr->next)
3722
if (ptr->definition == context->definition)
3727
g_assert (ptr != NULL);
3731
g_hash_table_foreach_remove (ptr->u.hash,
3732
(GHRFunc) remove_context_cb,
3735
if (g_hash_table_size (ptr->u.hash) != 0)
3742
prev->next = ptr->next;
3744
context->parent->children = ptr->next;
3747
g_hash_table_destroy (ptr->u.hash);
3750
memset (ptr, 1, sizeof (ContextPtr));
3752
g_slice_free (ContextPtr, ptr);
3760
* @context: the context.
3762
* Decreases reference count and removes @context
3763
* from the tree when it drops to zero.
3766
context_unref (Context *context)
3768
ContextPtr *children;
3771
if (context == NULL || --context->ref_count != 0)
3774
DEBUG (g_print ("destroying context %s\n", context->definition->id));
3776
children = context->children;
3777
context->children = NULL;
3779
while (children != NULL)
3781
ContextPtr *ptr = children;
3783
children = children->next;
3787
ptr->u.context->parent = NULL;
3788
context_unref (ptr->u.context);
3792
g_hash_table_foreach (ptr->u.hash,
3793
(GHFunc) context_unref_hash_cb,
3795
g_hash_table_destroy (ptr->u.hash);
3799
memset (ptr, 1, sizeof (ContextPtr));
3801
g_slice_free (ContextPtr, ptr);
3805
if (context->parent != NULL)
3806
context_remove_child (context->parent, context);
3808
regex_unref (context->end);
3809
regex_unref (context->reg_all);
3811
if (context->subpattern_context_classes != NULL)
3813
for (i = 0; i < context->definition->n_sub_patterns; ++i)
3815
g_slist_foreach (context->subpattern_context_classes[i],
3816
(GFunc)context_class_tag_free,
3819
g_slist_free (context->subpattern_context_classes[i]);
3823
g_slist_foreach (context->context_classes, (GFunc)context_class_tag_free, NULL);
3824
g_slist_free (context->context_classes);
3826
g_free (context->subpattern_context_classes);
3827
g_free (context->subpattern_tags);
3829
g_slice_free (Context, context);
3833
context_freeze_hash_cb (G_GNUC_UNUSED gpointer text,
3836
context_freeze (context);
3842
* @context: the context.
3844
* Recursively increments reference count in context and its children,
3845
* and marks them, so context_thaw is able to correctly decrement
3847
* This function is for update_syntax: we want to preserve existing
3848
* contexts when possible, and update_syntax erases contexts from
3849
* reanalyzed lines; so to avoid destructing and recreating contexts
3850
* every time, we need to increment reference count on existing contexts,
3851
* and decrement it when we are done with analysis, so no more needed
3852
* contexts go away. Keeping a list of referenced contexts is painful
3853
* or slow, so we just reference all contexts present at the moment.
3855
* Note this is not reentrant, context_freeze()/context_thaw() pair is called
3856
* only from update_syntax().
3859
context_freeze (Context *ctx)
3863
g_assert (!ctx->frozen);
3867
for (ptr = ctx->children; ptr != NULL; ptr = ptr->next)
3871
context_freeze (ptr->u.context);
3875
g_hash_table_foreach (ptr->u.hash,
3876
(GHFunc) context_freeze_hash_cb,
3883
get_child_contexts_hash_cb (G_GNUC_UNUSED gpointer text,
3887
*list = g_slist_prepend (*list, context);
3893
* @context: the context.
3895
* Recursively decrements reference count in context and its children,
3896
* if it was incremented by context_freeze().
3899
context_thaw (Context *ctx)
3906
for (ptr = ctx->children; ptr != NULL; )
3908
ContextPtr *next = ptr->next;
3912
context_thaw (ptr->u.context);
3916
GSList *children = NULL;
3917
g_hash_table_foreach (ptr->u.hash,
3918
(GHFunc) get_child_contexts_hash_cb,
3920
g_slist_foreach (children, (GFunc) context_thaw, NULL);
3921
g_slist_free (children);
3927
ctx->frozen = FALSE;
3928
context_unref (ctx);
3932
create_child_context (Context *parent,
3933
DefinitionChild *child_def,
3934
const gchar *line_text)
3938
gchar *match = NULL;
3939
ContextDefinition *definition = child_def->u.definition;
3941
g_return_val_if_fail (parent != NULL, NULL);
3943
for (ptr = parent->children;
3944
ptr != NULL && ptr->definition != definition;
3949
ptr = g_slice_new0 (ContextPtr);
3950
ptr->next = parent->children;
3951
parent->children = ptr;
3952
ptr->definition = definition;
3954
if (definition->type != CONTEXT_TYPE_CONTAINER ||
3955
!definition->u.start_end.end ||
3956
definition->u.start_end.end->resolved)
3962
ptr->u.hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3967
context = ptr->u.context;
3971
match = regex_fetch (definition->u.start_end.start, 0);
3972
g_return_val_if_fail (match != NULL, NULL);
3973
context = g_hash_table_lookup (ptr->u.hash, match);
3976
if (context != NULL)
3979
return context_ref (context);
3982
context = context_new (parent,
3985
child_def->override_style ? child_def->style :
3986
child_def->u.definition->default_style,
3987
child_def->override_style ? child_def->override_style_deep : FALSE);
3988
g_return_val_if_fail (context != NULL, NULL);
3991
ptr->u.context = context;
3993
g_hash_table_insert (ptr->u.hash, match, context);
4002
* @parent: parent segment (%NULL for the root segment).
4003
* @context: context for this segment (%NULL for invalid segments).
4004
* @start_at: start offset in the buffer, characters.
4005
* @end_at: end offset in the buffer, characters.
4006
* @is_start: is_start flag.
4008
* Creates a new segment structure. It doesn't take care about
4009
* parent or siblings, create_segment() is the function to
4010
* create new segments in the tree.
4012
* Returns: newly created segment.
4015
segment_new (GtkSourceContextEngine *ce,
4024
#ifdef ENABLE_CHECK_TREE
4025
g_assert (!is_start || context != NULL);
4028
segment = g_slice_new0 (Segment);
4029
segment->parent = parent;
4030
segment->context = context_ref (context);
4031
segment->start_at = start_at;
4032
segment->end_at = end_at;
4033
segment->is_start = is_start;
4035
if (context == NULL)
4036
add_invalid (ce, segment);
4042
find_segment_position_forward_ (Segment *segment,
4048
g_assert (segment->start_at <= start_at);
4050
while (segment != NULL)
4052
if (segment->end_at == start_at)
4054
while (segment->next != NULL && segment->next->start_at == start_at)
4055
segment = segment->next;
4058
*next = segment->next;
4063
if (segment->start_at == end_at)
4066
*prev = segment->prev;
4070
if (segment->start_at > end_at)
4076
if (segment->end_at < start_at)
4079
segment = segment->next;
4084
find_segment_position_backward_ (Segment *segment,
4090
g_assert (start_at < segment->end_at);
4092
while (segment != NULL)
4094
if (segment->end_at <= start_at)
4100
g_assert (segment->start_at >= end_at);
4103
segment = segment->prev;
4108
* find_segment_position:
4110
* @parent: parent segment (not %NULL).
4111
* @hint: segment somewhere near new segment position.
4112
* @start_at: start offset.
4113
* @end_at: end offset.
4114
* @prev: location to return previous sibling.
4115
* @next: location to return next sibling.
4117
* Finds siblings of a new segment to be created at interval
4118
* (start_at, end_at). It uses hint to avoid walking whole
4119
* parent->children list.
4122
find_segment_position (Segment *parent,
4131
g_assert (parent->start_at <= start_at && end_at <= parent->end_at);
4132
g_assert (!hint || hint->parent == parent);
4134
*prev = *next = NULL;
4136
if (parent->children == NULL)
4139
if (parent->children->next == NULL)
4141
tmp = parent->children;
4143
if (start_at >= tmp->end_at)
4152
hint = parent->children;
4154
if (hint->end_at <= start_at)
4155
find_segment_position_forward_ (hint, start_at, end_at, prev, next);
4157
find_segment_position_backward_ (hint, start_at, end_at, prev, next);
4164
* @parent: parent segment (%NULL for the root segment).
4165
* @context: context for this segment (%NULL for invalid segments).
4166
* @start_at: start offset, characters.
4167
* @end_at: end offset, characters.
4168
* @is_start: is_start flag.
4169
* @hint: a segment somewhere near new one, to omtimize search.
4171
* Creates a new segment and inserts it into the tree.
4173
* Returns: newly created segment.
4176
create_segment (GtkSourceContextEngine *ce,
4186
g_assert (!parent || (parent->start_at <= start_at && end_at <= parent->end_at));
4188
segment = segment_new (ce, parent, context, start_at, end_at, is_start);
4192
Segment *prev, *next;
4196
hint = ce->priv->hint;
4197
while (hint != NULL && hint->parent != parent)
4198
hint = hint->parent;
4201
find_segment_position (parent, hint,
4205
g_assert ((!parent->children && !prev && !next) ||
4206
(parent->children && (prev || next)));
4207
g_assert (!prev || prev->next == next);
4208
g_assert (!next || next->prev == prev);
4210
segment->next = next;
4211
segment->prev = prev;
4214
next->prev = segment;
4216
parent->last_child = segment;
4219
prev->next = segment;
4221
parent->children = segment;
4223
CHECK_SEGMENT_LIST (parent);
4233
* @state: the semgent.
4234
* @end_at: new end offset, characters.
4236
* Updates end offset in the segment and its ancestors.
4239
segment_extend (Segment *state,
4242
while (state != NULL && state->end_at < end_at)
4244
state->end_at = end_at;
4245
state = state->parent;
4247
CHECK_SEGMENT_LIST (state->parent);
4251
segment_destroy_children (GtkSourceContextEngine *ce,
4257
g_return_if_fail (segment != NULL);
4259
child = segment->children;
4260
segment->children = NULL;
4261
segment->last_child = NULL;
4263
while (child != NULL)
4265
Segment *next = child->next;
4266
segment_destroy (ce, child);
4270
sp = segment->sub_patterns;
4271
segment->sub_patterns = NULL;
4275
SubPattern *next = sp->next;
4276
sub_pattern_free (sp);
4285
* @context: the segment to destroy.
4287
* Recursively frees given segment. It removes the segment
4288
* from ce structure, but it doesn't update parent and
4289
* siblings. segment_remove() is the function that takes
4290
* care of everything.
4293
segment_destroy (GtkSourceContextEngine *ce,
4296
g_return_if_fail (segment != NULL);
4298
segment_destroy_children (ce, segment);
4300
/* segment neighbours and parent may be invalid here,
4301
* so we only can unset the hint */
4302
if (ce->priv->hint == segment)
4303
ce->priv->hint = NULL;
4304
if (ce->priv->hint2 == segment)
4305
ce->priv->hint2 = NULL;
4307
if (SEGMENT_IS_INVALID (segment))
4308
remove_invalid (ce, segment);
4310
context_unref (segment->context);
4313
g_assert (!g_slist_find (ce->priv->invalid, segment));
4314
memset (segment, 1, sizeof (Segment));
4316
g_slice_free (Segment, segment);
4321
* container_context_starts_here:
4323
* See child_starts_here().
4326
container_context_starts_here (GtkSourceContextEngine *ce,
4328
DefinitionChild *child_def,
4330
gint *line_pos, /* bytes */
4331
Segment **new_state)
4333
Context *new_context;
4334
Segment *new_segment;
4336
ContextDefinition *definition = child_def->u.definition;
4338
g_assert (*line_pos <= line->byte_length);
4340
/* We can have a container context definition (i.e. the main
4341
* language definition) without start_end.start. */
4342
if (definition->u.start_end.start == NULL)
4345
if (!regex_match (definition->u.start_end.start,
4346
line->text, line->byte_length, *line_pos))
4351
new_context = create_child_context (state->context, child_def, line->text);
4352
g_return_val_if_fail (new_context != NULL, FALSE);
4354
if (!can_apply_match (new_context, line, *line_pos, &match_end,
4355
definition->u.start_end.start))
4357
context_unref (new_context);
4361
g_assert (match_end <= line->byte_length);
4363
segment_extend (state, line_pos_to_offset (line, match_end));
4364
new_segment = create_segment (ce, state, new_context,
4365
line_pos_to_offset (line, *line_pos),
4366
line_pos_to_offset (line, match_end),
4370
/* This new context could end at the same position (i.e. have zero length),
4371
* and then we get an infinite loop. We can't possibly know about it at this point
4372
* (since we need to know that the context indeed *ends* here, and that's
4373
* discovered only later) so we look at the previous sibling: if it's the same,
4374
* and has zero length then we remove the segment. We do it this way instead of
4375
* checking before creating the segment because it's more convenient. */
4376
if (*line_pos == match_end &&
4377
new_segment->prev != NULL &&
4378
new_segment->prev->context == new_segment->context &&
4379
new_segment->prev->start_at == new_segment->prev->end_at &&
4380
new_segment->prev->start_at == line_pos_to_offset (line, *line_pos))
4382
segment_remove (ce, new_segment);
4386
apply_sub_patterns (new_segment, line,
4387
definition->u.start_end.start,
4388
SUB_PATTERN_WHERE_START);
4389
*line_pos = match_end;
4390
*new_state = new_segment;
4391
ce->priv->hint2 = NULL;
4392
context_unref (new_context);
4397
* simple_context_starts_here:
4399
* See child_starts_here().
4402
simple_context_starts_here (GtkSourceContextEngine *ce,
4404
DefinitionChild *child_def,
4406
gint *line_pos, /* bytes */
4407
Segment **new_state)
4410
Context *new_context;
4411
ContextDefinition *definition = child_def->u.definition;
4413
g_return_val_if_fail (definition->u.match != NULL, FALSE);
4415
g_assert (*line_pos <= line->byte_length);
4417
if (!regex_match (definition->u.match, line->text, line->byte_length, *line_pos))
4420
new_context = create_child_context (state->context, child_def, line->text);
4421
g_return_val_if_fail (new_context != NULL, FALSE);
4423
if (!can_apply_match (new_context, line, *line_pos, &match_end, definition->u.match))
4425
context_unref (new_context);
4429
/* If length of the match is zero, then we get zero-length segment and return to
4430
* the same state, so it's an infinite loop. But, if this child ends parent, we
4431
* do want to terminate parent. Still, if match is at the beginning of the parent
4432
* then we get an infinite loop again, so we check that (NOTE it really should destroy
4433
* parent context then, but then we again can get parent context be recreated here and
4435
if (*line_pos == match_end &&
4436
(!CONTEXT_ENDS_PARENT (new_context) ||
4437
line_pos_to_offset (line, *line_pos) == state->start_at))
4439
context_unref (new_context);
4443
g_assert (match_end <= line->byte_length);
4444
segment_extend (state, line_pos_to_offset (line, match_end));
4446
if (*line_pos != match_end)
4448
/* Normal non-zero-length match, create a child segment */
4449
Segment *new_segment;
4450
new_segment = create_segment (ce, state, new_context,
4451
line_pos_to_offset (line, *line_pos),
4452
line_pos_to_offset (line, match_end),
4455
apply_sub_patterns (new_segment, line, definition->u.match, SUB_PATTERN_WHERE_DEFAULT);
4456
ce->priv->hint2 = new_segment;
4459
/* Terminate parent if needed */
4460
if (CONTEXT_ENDS_PARENT (new_context))
4464
ce->priv->hint2 = state;
4465
state = state->parent;
4467
while (SEGMENT_ENDS_PARENT (state));
4470
*line_pos = match_end;
4472
context_unref (new_context);
4477
* child_starts_here:
4480
* @state: current state.
4481
* @child_def: the child.
4482
* @line: line to analyze.
4483
* @line_pos: the position inside @line, bytes.
4484
* @new_state: where to store the new state.
4486
* Verifies if a context of the type in @curr_definition starts at
4487
* @line_pos in @line. If the contexts start here @new_state and
4488
* @line_pos are updated.
4490
* Returns: %TRUE if the context starts here.
4493
child_starts_here (GtkSourceContextEngine *ce,
4495
DefinitionChild *child_def,
4498
Segment **new_state)
4500
g_return_val_if_fail (child_def->resolved, FALSE);
4502
switch (child_def->u.definition->type)
4504
case CONTEXT_TYPE_SIMPLE:
4505
return simple_context_starts_here (ce,
4511
case CONTEXT_TYPE_CONTAINER:
4512
return container_context_starts_here (ce,
4519
g_return_val_if_reached (FALSE);
4524
* segment_ends_here:
4526
* @state: the segment.
4527
* @line: analyzed line.
4528
* @pos: the position inside @line, bytes.
4530
* Checks whether given segment ends at pos. Unlike
4531
* child_starts_here() it doesn't modify tree, it merely
4532
* calls regex_match() for the end regex.
4535
segment_ends_here (Segment *state,
4539
g_assert (SEGMENT_IS_CONTAINER (state));
4541
return state->context->definition->u.start_end.end &&
4542
regex_match (state->context->end,
4549
* ancestor_context_ends_here:
4551
* @state: current context.
4552
* @line: the line to analyze.
4553
* @line_pos: the position inside @line, bytes.
4555
* Verifies if some ancestor context ends at the current position.
4556
* This function only checks conetxts and does not modify the tree,
4557
* it's used by ancestor_ends_here().
4559
* Returns: the ancestor context that terminates here or %NULL.
4562
ancestor_context_ends_here (Context *state,
4566
Context *current_context;
4567
GSList *current_context_list;
4568
GSList *check_ancestors;
4569
Context *terminating_context;
4571
/* A context can be terminated by the parent if extend_parent is
4572
* FALSE, so we need to verify the end of all the parents of
4573
* not-extending contexts. The list is ordered by ascending
4575
check_ancestors = NULL;
4576
current_context = state;
4577
while (ANCESTOR_CAN_END_CONTEXT (current_context))
4579
if (!CONTEXT_EXTENDS_PARENT (current_context))
4580
check_ancestors = g_slist_prepend (check_ancestors,
4581
current_context->parent);
4582
current_context = current_context->parent;
4585
/* The first context that ends here terminates its descendants. */
4586
terminating_context = NULL;
4587
current_context_list = check_ancestors;
4588
while (current_context_list != NULL)
4590
current_context = current_context_list->data;
4592
if (current_context->end &&
4593
current_context->end->u.regex.regex &&
4594
regex_match (current_context->end,
4599
terminating_context = current_context;
4603
current_context_list = current_context_list->next;
4605
g_slist_free (check_ancestors);
4607
return terminating_context;
4611
* ancestor_ends_here:
4613
* @state: current state.
4614
* @line: the line to analyze.
4615
* @line_pos: the position inside @line, bytes.
4616
* @new_state: where to store the new state.
4618
* Verifies if some ancestor context ends at given position. If
4619
* state changed and @new_state is not %NULL, then the new state is stored
4620
* in @new_state, and descendants of @new_state are closed, so the
4621
* terminating segment becomes current state.
4623
* Returns: %TRUE if an ancestor ends at the given position.
4626
ancestor_ends_here (Segment *state,
4629
Segment **new_state)
4631
Context *terminating_context;
4633
terminating_context = ancestor_context_ends_here (state->context, line, line_pos);
4635
if (new_state != NULL && terminating_context != NULL)
4637
/* We have found a context that ends here, so we close
4638
* all the descendants. terminating_segment will be
4639
* closed by next next_segment() call from analyze_line. */
4640
Segment *current_segment = state;
4642
while (current_segment->context != terminating_context)
4643
current_segment = current_segment->parent;
4645
*new_state = current_segment;
4646
g_assert (*new_state != NULL);
4649
return terminating_context != NULL;
4655
* @ce: #GtkSourceContextEngine.
4656
* @state: current state.
4657
* @line: analyzed line.
4658
* @line_pos: position inside @line, bytes.
4659
* @new_state: where to store the new state.
4660
* @hint: child of @state used to optimize tree operations.
4662
* Verifies if a context starts or ends in @line at @line_pos of after it.
4663
* If the contexts starts or ends here @new_state and @line_pos are updated.
4665
* Returns: %FALSE is there are no more contexts in @line.
4668
next_segment (GtkSourceContextEngine *ce,
4672
Segment **new_state)
4674
gint pos = *line_pos;
4676
g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
4677
g_assert (pos <= line->byte_length);
4679
while (pos <= line->byte_length)
4681
DefinitionsIter def_iter;
4682
gboolean context_end_found;
4683
DefinitionChild *child_def;
4685
if (state->context->reg_all)
4687
if (!regex_match (state->context->reg_all,
4695
regex_fetch_pos_bytes (state->context->reg_all,
4699
/* Does an ancestor end here? */
4700
if (ANCESTOR_CAN_END_CONTEXT (state->context) &&
4701
ancestor_ends_here (state, line, pos, new_state))
4703
g_assert (pos <= line->byte_length);
4704
segment_extend (state, line_pos_to_offset (line, pos));
4709
/* Does the current context end here? */
4710
context_end_found = segment_ends_here (state, line, pos);
4712
/* Iter over the definitions we can find in the current
4714
definition_iter_init (&def_iter, state->context->definition);
4715
while ((child_def = definition_iter_next (&def_iter)) != NULL)
4717
gboolean try_this = TRUE;
4719
g_return_val_if_fail (child_def->resolved, FALSE);
4721
/* If the child definition does not extend the parent
4722
* and the current context could end here we do not
4723
* need to examine this child. */
4724
if (!HAS_OPTION (child_def->u.definition, EXTEND_PARENT) && context_end_found)
4727
if (HAS_OPTION (child_def->u.definition, FIRST_LINE_ONLY) && line->start_at != 0)
4730
if (HAS_OPTION (child_def->u.definition, ONCE_ONLY))
4734
for (prev = state->children; prev != NULL; prev = prev->next)
4736
if (prev->context != NULL &&
4737
prev->context->definition == child_def->u.definition)
4747
/* Does this child definition start a new
4748
* context at the current position? */
4749
if (child_starts_here (ce, state, child_def,
4750
line, &pos, new_state))
4752
g_assert (pos <= line->byte_length);
4754
definition_iter_destroy (&def_iter);
4759
/* This child does not start here, so we analyze
4760
* another definition. */
4762
definition_iter_destroy (&def_iter);
4764
if (context_end_found)
4766
/* We have found that the current context could end
4767
* here and that it cannot be extended by a child.
4768
* Still, it may happen that parent context ends in
4769
* the middle of the end regex match, apply_match()
4771
if (apply_match (state, line, &pos, state->context->end, SUB_PATTERN_WHERE_END))
4773
g_assert (pos <= line->byte_length);
4775
while (SEGMENT_ENDS_PARENT (state))
4776
state = state->parent;
4778
*new_state = state->parent;
4779
ce->priv->hint2 = state;
4785
/* Nothing new at this position, go to next char. */
4786
pos = g_utf8_next_char (line->text + pos) - line->text;
4795
* @state: current state.
4796
* @hint: child of @state used in analyze_line() and next_segment().
4798
* Closes the contexts that cannot contain end of lines if needed.
4799
* Updates hint if new state is different from @state.
4801
* Returns: the new state.
4804
check_line_end (GtkSourceContextEngine *ce,
4807
Segment *current_segment;
4808
Segment *terminating_segment;
4810
g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
4812
/* A context can be terminated by the parent if extend_parent is
4813
* FALSE, so we need to verify the end of all the parents of
4814
* not-extending contexts. */
4815
terminating_segment = NULL;
4816
current_segment = state;
4818
while (current_segment != NULL)
4820
if (SEGMENT_END_AT_LINE_END (current_segment))
4821
terminating_segment = current_segment;
4822
else if (!ANCESTOR_CAN_END_CONTEXT(current_segment->context))
4824
current_segment = current_segment->parent;
4827
if (terminating_segment != NULL)
4829
ce->priv->hint2 = terminating_segment;
4830
return terminating_segment->parent;
4839
delete_zero_length_segments (GtkSourceContextEngine *ce,
4842
while (list != NULL)
4844
Segment *s = list->data;
4846
if (s->start_at == s->end_at)
4850
for (l = list->next; l != NULL; )
4852
GList *next = l->next;
4853
Segment *s2 = l->data;
4854
gboolean child = FALSE;
4868
list = g_list_delete_link (list, l);
4873
if (ce->priv->hint2 != NULL)
4875
Segment *s2 = ce->priv->hint2;
4876
gboolean child = FALSE;
4890
ce->priv->hint2 = s->parent;
4893
segment_remove (ce, s);
4896
list = g_list_delete_link (list, list);
4903
* @ce: #GtkSourceContextEngine.
4904
* @state: the state at the beginning of line.
4906
* @hint: a child of @state around start of line, to make it faster.
4908
* Finds contexts at the line and updates the syntax tree on it.
4910
* Returns: starting state at the next line.
4913
analyze_line (GtkSourceContextEngine *ce,
4918
GList *end_segments = NULL;
4921
g_assert (SEGMENT_IS_CONTAINER (state));
4923
if (ce->priv->hint2 == NULL || ce->priv->hint2->parent != state)
4924
ce->priv->hint2 = state->last_child;
4925
g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
4927
timer = g_timer_new ();
4929
/* Find the contexts in the line. */
4930
while (line_pos <= line->byte_length)
4932
Segment *new_state = NULL;
4934
if (!next_segment (ce, state, line, &line_pos, &new_state))
4937
if (g_timer_elapsed (timer, NULL) * 1000 > MAX_TIME_FOR_ONE_LINE)
4940
_("Highlighting a single line took too much time, "
4941
"syntax highlighting will be disabled"));
4942
disable_highlighting (ce);
4946
g_assert (new_state != NULL);
4947
g_assert (SEGMENT_IS_CONTAINER (new_state));
4951
if (ce->priv->hint2 == NULL || ce->priv->hint2->parent != state)
4952
ce->priv->hint2 = state->last_child;
4953
g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
4955
/* XXX this a temporary workaround for zero-length segments in the end
4956
* of line. there are no zero-length segments in the middle because it goes
4957
* into infinite loop in that case. */
4958
/* state may be extended later, so not all elements of new_segments
4959
* really have zero length */
4960
if (state->start_at == line->char_length)
4961
end_segments = g_list_prepend (end_segments, state);
4964
g_timer_destroy (timer);
4965
if (ce->priv->disabled)
4968
/* Extend current state to the end of line. */
4969
segment_extend (state, line->start_at + line->char_length);
4970
g_assert (line_pos <= line->byte_length);
4972
/* Verify if we need to close the context because we are at
4973
* the end of the line. */
4974
if (ANCESTOR_CAN_END_CONTEXT (state->context) ||
4975
SEGMENT_END_AT_LINE_END (state))
4977
state = check_line_end (ce, state);
4980
/* Extend the segment to the beginning of next line. */
4981
g_assert (SEGMENT_IS_CONTAINER (state));
4982
segment_extend (state, NEXT_LINE_OFFSET (line));
4984
/* if it's the last line, don't bother with zero length segments */
4985
if (!line->eol_length)
4986
g_list_free (end_segments);
4988
delete_zero_length_segments (ce, end_segments);
4998
* @buffer: #GtkTextBuffer.
4999
* @line_start: iterator pointing to the beginning of line.
5000
* @line_end: iterator pointing to the beginning of next line or to the end
5001
* of this line if it's the last line in @buffer.
5002
* @line: #LineInfo structure to be filled.
5004
* Retrieves line text from the buffer, finds line terminator and fills
5008
get_line_info (GtkTextBuffer *buffer,
5009
const GtkTextIter *line_start,
5010
const GtkTextIter *line_end,
5013
g_assert (!gtk_text_iter_equal (line_start, line_end));
5015
line->text = gtk_text_buffer_get_slice (buffer, line_start, line_end, TRUE);
5016
line->start_at = gtk_text_iter_get_offset (line_start);
5018
if (!gtk_text_iter_starts_line (line_end))
5020
line->eol_length = 0;
5021
line->char_length = g_utf8_strlen (line->text, -1);
5022
line->byte_length = strlen (line->text);
5026
gint eol_index, next_line_index;
5028
pango_find_paragraph_boundary (line->text, -1,
5032
g_assert (eol_index < next_line_index);
5034
line->char_length = g_utf8_strlen (line->text, eol_index);
5035
line->eol_length = g_utf8_strlen (line->text + eol_index, -1);
5036
line->byte_length = eol_index;
5039
g_assert (gtk_text_iter_get_offset (line_end) ==
5040
line->start_at + line->char_length + line->eol_length);
5044
* line_info_destroy:
5048
* Destroys data allocated by get_line_info().
5051
line_info_destroy (LineInfo *line)
5053
g_free (line->text);
5057
* segment_tree_zero_len:
5059
* @ce: #GtkSoucreContextEngine.
5061
* Erases syntax tree and sets root segment length to zero.
5062
* It's a shortcut for case when all the text is deleted from
5066
segment_tree_zero_len (GtkSourceContextEngine *ce)
5068
Segment *root = ce->priv->root_segment;
5069
segment_destroy_children (ce, root);
5070
root->start_at = root->end_at = 0;
5074
#ifdef ENABLE_CHECK_TREE
5076
get_segment_at_offset_slow_ (Segment *segment,
5082
if (segment->parent == NULL && offset == segment->end_at)
5085
if (segment->start_at > offset)
5087
g_assert (segment->parent != NULL);
5088
segment = segment->parent;
5092
if (segment->start_at == offset)
5094
if (segment->children != NULL && segment->children->start_at == offset)
5096
segment = segment->children;
5103
if (segment->end_at <= offset && segment->parent != NULL)
5105
if (segment->next != NULL)
5107
if (segment->next->start_at > offset)
5108
return segment->parent;
5110
segment = segment->next;
5114
segment = segment->parent;
5120
for (child = segment->children; child != NULL; child = child->next)
5122
if (child->start_at == offset)
5128
if (child->end_at <= offset)
5131
if (child->start_at > offset)
5140
#endif /* ENABLE_CHECK_TREE */
5142
#define SEGMENT_IS_ZERO_LEN_AT(s,o) ((s)->start_at == (o) && (s)->end_at == (o))
5143
#define SEGMENT_CONTAINS(s,o) ((s)->start_at <= (o) && (s)->end_at > (o))
5144
#define SEGMENT_DISTANCE(s,o) (MIN (ABS ((s)->start_at - (o)), ABS ((s)->end_at - (o))))
5146
get_segment_in_ (Segment *segment,
5151
g_assert (segment->start_at <= offset && segment->end_at > offset);
5153
if (segment->children == NULL)
5156
if (segment->children == segment->last_child)
5158
if (SEGMENT_IS_ZERO_LEN_AT (segment->children, offset))
5159
return segment->children;
5161
if (SEGMENT_CONTAINS (segment->children, offset))
5162
return get_segment_in_ (segment->children, offset);
5167
if (segment->children->start_at > offset || segment->last_child->end_at < offset)
5170
if (SEGMENT_DISTANCE (segment->children, offset) >= SEGMENT_DISTANCE (segment->last_child, offset))
5172
for (child = segment->children; child; child = child->next)
5174
if (child->start_at > offset)
5177
if (SEGMENT_IS_ZERO_LEN_AT (child, offset))
5180
if (SEGMENT_CONTAINS (child, offset))
5181
return get_segment_in_ (child, offset);
5186
for (child = segment->last_child; child; child = child->prev)
5188
if (SEGMENT_IS_ZERO_LEN_AT (child, offset))
5190
while (child->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (child->prev, offset))
5191
child = child->prev;
5195
if (child->end_at <= offset)
5198
if (SEGMENT_CONTAINS (child, offset))
5199
return get_segment_in_ (child, offset);
5206
/* assumes zero-length segments can't have children */
5208
get_segment_ (Segment *segment,
5211
if (segment->parent != NULL)
5213
if (!SEGMENT_CONTAINS (segment->parent, offset))
5214
return get_segment_ (segment->parent, offset);
5218
g_assert (offset >= segment->start_at);
5219
g_assert (offset <= segment->end_at);
5222
if (SEGMENT_CONTAINS (segment, offset))
5223
return get_segment_in_ (segment, offset);
5225
if (SEGMENT_IS_ZERO_LEN_AT (segment, offset))
5227
while (segment->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
5228
segment = segment->prev;
5232
if (offset < segment->start_at)
5234
while (segment->prev != NULL && segment->prev->start_at > offset)
5235
segment = segment->prev;
5237
g_assert (!segment->prev || segment->prev->start_at <= offset);
5239
if (segment->prev == NULL)
5240
return segment->parent;
5242
if (segment->prev->end_at > offset)
5243
return get_segment_in_ (segment->prev, offset);
5245
if (segment->prev->end_at == offset)
5247
if (SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
5249
segment = segment->prev;
5250
while (segment->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
5251
segment = segment->prev;
5255
return segment->parent;
5258
/* segment->prev->end_at < offset */
5259
return segment->parent;
5262
/* offset >= segment->end_at, not zero-length */
5264
while (segment->next != NULL)
5266
if (SEGMENT_IS_ZERO_LEN_AT (segment->next, offset))
5267
return segment->next;
5269
if (segment->next->end_at > offset)
5271
if (segment->next->start_at <= offset)
5272
return get_segment_in_ (segment->next, offset);
5274
return segment->parent;
5277
segment = segment->next;
5280
return segment->parent;
5282
#undef SEGMENT_IS_ZERO_LEN_AT
5283
#undef SEGMENT_CONTAINS
5284
#undef SEGMENT_DISTANCE
5287
* get_segment_at_offset:
5289
* @ce: #GtkSoucreContextEngine.
5290
* @hint: segment to start search from or %NULL.
5291
* @offset: the offset, characters.
5293
* Finds the deepest segment "at @offset".
5294
* More precisely, it returns toplevel segment if
5295
* @offset is equal to length of buffer; or non-zero-length
5296
* segment which contains character at @offset; or zero-length
5297
* segment at @offset. In case when there are several zero-length
5298
* segments, it returns the first one.
5301
get_segment_at_offset (GtkSourceContextEngine *ce,
5307
if (offset == ce->priv->root_segment->end_at)
5308
return ce->priv->root_segment;
5311
/* if you see this message (often), then something is
5312
* wrong with the hints business, i.e. optimizations
5313
* do not work quite like they should */
5314
if (hint == NULL || hint == ce->priv->root_segment)
5317
g_print ("searching from root %d\n", ++c);
5321
result = get_segment_ (hint ? hint : ce->priv->root_segment, offset);
5323
#ifdef ENABLE_CHECK_TREE
5324
g_assert (result == get_segment_at_offset_slow_ (hint, offset));
5333
* @ce: #GtkSoucreContextEngine.
5334
* @segment: segment to remove.
5336
* Removes the segment from syntax tree and frees it.
5337
* It correctly updates parent's children list, not
5338
* like segment_destroy() where caller has to take care
5339
* of tree integrity.
5342
segment_remove (GtkSourceContextEngine *ce,
5345
if (segment->next != NULL)
5346
segment->next->prev = segment->prev;
5348
segment->parent->last_child = segment->prev;
5350
if (segment->prev != NULL)
5351
segment->prev->next = segment->next;
5353
segment->parent->children = segment->next;
5355
/* if ce->priv->hint is being deleted, set it to some
5356
* neighbour segment */
5357
if (ce->priv->hint == segment)
5359
if (segment->next != NULL)
5360
ce->priv->hint = segment->next;
5361
else if (segment->prev != NULL)
5362
ce->priv->hint = segment->prev;
5364
ce->priv->hint = segment->parent;
5367
/* if ce->priv->hint2 is being deleted, set it to some
5368
* neighbour segment */
5369
if (ce->priv->hint2 == segment)
5371
if (segment->next != NULL)
5372
ce->priv->hint2 = segment->next;
5373
else if (segment->prev != NULL)
5374
ce->priv->hint2 = segment->prev;
5376
ce->priv->hint2 = segment->parent;
5379
segment_destroy (ce, segment);
5383
segment_erase_middle_ (GtkSourceContextEngine *ce,
5388
Segment *new_segment, *child;
5391
new_segment = segment_new (ce,
5397
segment->end_at = start;
5399
new_segment->next = segment->next;
5400
segment->next = new_segment;
5401
new_segment->prev = segment;
5403
if (new_segment->next != NULL)
5404
new_segment->next->prev = new_segment;
5406
new_segment->parent->last_child = new_segment;
5408
child = segment->children;
5409
segment->children = NULL;
5410
segment->last_child = NULL;
5412
while (child != NULL)
5415
Segment *next = child->next;
5417
if (child->start_at < start)
5419
g_assert (child->end_at <= start);
5420
append_to = segment;
5424
g_assert (child->start_at >= end);
5425
append_to = new_segment;
5428
child->parent = append_to;
5430
if (append_to->last_child != NULL)
5432
append_to->last_child->next = child;
5433
child->prev = append_to->last_child;
5435
append_to->last_child = child;
5439
child->next = child->prev = NULL;
5440
append_to->last_child = child;
5441
append_to->children = child;
5447
sp = segment->sub_patterns;
5448
segment->sub_patterns = NULL;
5452
SubPattern *next = sp->next;
5455
if (sp->start_at < start)
5457
sp->end_at = MIN (sp->end_at, start);
5458
append_to = segment;
5462
g_assert (sp->end_at > end);
5463
sp->start_at = MAX (sp->start_at, end);
5464
append_to = new_segment;
5467
sp->next = append_to->sub_patterns;
5468
append_to->sub_patterns = sp;
5473
CHECK_SEGMENT_CHILDREN (segment);
5474
CHECK_SEGMENT_CHILDREN (new_segment);
5478
* segment_erase_range_:
5480
* @ce: #GtkSourceContextEngine.
5481
* @segment: the segment.
5482
* @start: start offset of range to erase, characters.
5483
* @end: end offset of range to erase, characters.
5485
* Recurisvely removes segments from [@start, @end] interval
5486
* starting from @segment. If @segment belongs to the range,
5487
* or it's a zero-length segment at @end offset, and it's not
5488
* the toplevel segment, then it's removed from the tree.
5489
* If @segment intersects with the range (unless it's the toplevel
5490
* segment), then its ends are adjusted appropriately, and it's
5491
* split into two if it completely contains the range.
5494
segment_erase_range_ (GtkSourceContextEngine *ce,
5499
g_assert (start < end);
5501
if (segment->start_at == segment->end_at)
5503
if (segment->start_at >= start && segment->start_at <= end)
5504
segment_remove (ce, segment);
5508
if (segment->start_at > end || segment->end_at < start)
5511
if (segment->start_at >= start && segment->end_at <= end && segment->parent)
5513
segment_remove (ce, segment);
5517
if (segment->start_at == end)
5519
Segment *child = segment->children;
5521
while (child != NULL && child->start_at == end)
5523
Segment *next = child->next;
5524
segment_erase_range_ (ce, child, start, end);
5528
else if (segment->end_at == start)
5530
Segment *child = segment->last_child;
5532
while (child != NULL && child->end_at == start)
5534
Segment *prev = child->prev;
5535
segment_erase_range_ (ce, child, start, end);
5541
Segment *child = segment->children;
5543
while (child != NULL)
5545
Segment *next = child->next;
5546
segment_erase_range_ (ce, child, start, end);
5551
if (segment->sub_patterns != NULL)
5555
sp = segment->sub_patterns;
5556
segment->sub_patterns = NULL;
5560
SubPattern *next = sp->next;
5562
if (sp->start_at >= start && sp->end_at <= end)
5563
sub_pattern_free (sp);
5565
segment_add_subpattern (segment, sp);
5571
if (segment->parent != NULL)
5573
/* Now all children and subpatterns are cleaned up,
5574
* so we only need to split segment properly if its middle
5575
* was erased. Otherwise, only ends need to be adjusted. */
5576
if (segment->start_at < start && segment->end_at > end)
5578
segment_erase_middle_ (ce, segment, start, end);
5582
g_assert ((segment->start_at >= start && segment->end_at > end) ||
5583
(segment->start_at < start && segment->end_at <= end));
5585
if (segment->end_at > end)
5587
/* If we erase the beginning, we need to clear
5589
segment->start_at = end;
5590
segment->is_start = FALSE;
5594
segment->end_at = start;
5603
* @ce: #GtkSourceContextEngine.
5604
* @first: first segment.
5605
* @second: second segment.
5607
* Merges adjacent segments @first and @second given
5608
* their contexts are equal.
5611
segment_merge (GtkSourceContextEngine *ce,
5617
if (first == second)
5620
g_assert (!SEGMENT_IS_INVALID (first));
5621
g_assert (first->context == second->context);
5622
g_assert (first->end_at == second->start_at);
5624
if (first->parent != second->parent)
5625
segment_merge (ce, first->parent, second->parent);
5627
parent = first->parent;
5629
g_assert (first->next == second);
5630
g_assert (first->parent == second->parent);
5631
g_assert (second != parent->children);
5633
if (second == parent->last_child)
5634
parent->last_child = first;
5635
first->next = second->next;
5636
if (second->next != NULL)
5637
second->next->prev = first;
5639
first->end_at = second->end_at;
5641
if (second->children != NULL)
5645
for (child = second->children; child != NULL; child = child->next)
5646
child->parent = first;
5648
if (first->children == NULL)
5650
g_assert (!first->last_child);
5651
first->children = second->children;
5652
first->last_child = second->last_child;
5656
first->last_child->next = second->children;
5657
second->children->prev = first->last_child;
5658
first->last_child = second->last_child;
5662
if (second->sub_patterns != NULL)
5664
if (first->sub_patterns == NULL)
5666
first->sub_patterns = second->sub_patterns;
5670
while (second->sub_patterns != NULL)
5672
SubPattern *sp = second->sub_patterns;
5673
second->sub_patterns = sp->next;
5674
sp->next = first->sub_patterns;
5675
first->sub_patterns = sp;
5680
second->children = NULL;
5681
second->last_child = NULL;
5682
second->sub_patterns = NULL;
5684
segment_destroy (ce, second);
5690
* @ce: #GtkSourceContextEngine.
5691
* @start: start offset of region to erase, characters.
5692
* @end: end offset of region to erase, characters.
5693
* @hint: segment around @start to make it faster.
5695
* Erases all non-toplevel segments in the interval
5696
* [@start, @end]. Its action on the tree is roughly
5697
* equivalent to segment_erase_range_(ce->priv->root_segment, start, end)
5698
* (but that does not accept toplevel segment).
5701
erase_segments (GtkSourceContextEngine *ce,
5706
Segment *root = ce->priv->root_segment;
5707
Segment *child, *hint_prev;
5709
if (root->children == NULL)
5713
hint = ce->priv->hint;
5716
while (hint != NULL && hint->parent != ce->priv->root_segment)
5717
hint = hint->parent;
5720
hint = root->children;
5722
hint_prev = hint->prev;
5725
while (child != NULL)
5727
Segment *next = child->next;
5729
if (child->end_at < start)
5734
ce->priv->hint = next;
5739
if (child->start_at > end)
5741
ce->priv->hint = child;
5745
segment_erase_range_ (ce, child, start, end);
5750
while (child != NULL)
5752
Segment *prev = child->prev;
5754
if (ce->priv->hint == NULL)
5755
ce->priv->hint = child;
5757
if (child->start_at > end)
5763
if (child->end_at < start)
5768
segment_erase_range_ (ce, child, start, end);
5778
* @ce: #GtkSourceContextEngine.
5779
* @end: desired end of region to analyze or %NULL.
5780
* @time: maximal amount of time in milliseconds allowed to spend here
5781
* or 0 for 'unlimited'.
5783
* Updates syntax tree. If @end is not %NULL, then it analyzes
5784
* (reanalyzes invalid areas in) region from start of buffer
5785
* to @end. Otherwise, it analyzes batch of text starting at
5786
* first invalid line.
5787
* In order to avoid blocking ui it uses a timer and stops
5788
* when time elapsed is greater than @time, so analyzed region is
5789
* not necessarily what's requested (unless @time is 0).
5791
/* XXX it must be refactored. */
5793
update_syntax (GtkSourceContextEngine *ce,
5794
const GtkTextIter *end,
5798
GtkTextIter start_iter, end_iter;
5799
GtkTextIter line_start, line_end;
5800
gint start_offset, end_offset;
5801
gint line_start_offset, line_end_offset;
5803
GtkTextBuffer *buffer = ce->priv->buffer;
5804
Segment *state = ce->priv->root_segment;
5807
context_freeze (ce->priv->root_context);
5810
if (!gtk_text_buffer_get_char_count (buffer))
5812
segment_tree_zero_len (ce);
5816
invalid = get_invalid_segment (ce);
5818
if (invalid == NULL)
5821
if (end != NULL && invalid->start_at >= gtk_text_iter_get_offset (end))
5826
end_offset = gtk_text_iter_get_offset (end);
5827
start_offset = MIN (end_offset, invalid->start_at);
5831
start_offset = invalid->start_at;
5832
end_offset = gtk_text_buffer_get_char_count (buffer);
5835
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
5836
gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end_offset);
5838
if (!gtk_text_iter_starts_line (&start_iter))
5840
gtk_text_iter_set_line_offset (&start_iter, 0);
5841
start_offset = gtk_text_iter_get_offset (&start_iter);
5844
if (!gtk_text_iter_starts_line (&end_iter))
5846
gtk_text_iter_forward_line (&end_iter);
5847
end_offset = gtk_text_iter_get_offset (&end_iter);
5850
/* This happens after deleting all text on last line. */
5851
if (start_offset == end_offset)
5853
g_assert (end_offset == gtk_text_buffer_get_char_count (buffer));
5854
g_assert (g_slist_length (ce->priv->invalid) == 1);
5855
segment_remove (ce, invalid);
5863
line_start = start_iter;
5864
line_start_offset = start_offset;
5865
line_end = line_start;
5866
gtk_text_iter_forward_line (&line_end);
5867
line_end_offset = gtk_text_iter_get_offset (&line_end);
5868
analyzed_end = line_end_offset;
5870
timer = g_timer_new ();
5875
gboolean next_line_invalid = FALSE;
5876
gboolean need_invalidate_next = FALSE;
5878
/* Last buffer line. */
5879
if (line_start_offset == line_end_offset)
5881
g_assert (line_start_offset == gtk_text_buffer_get_char_count (buffer));
5885
/* Analyze the line */
5886
erase_segments (ce, line_start_offset, line_end_offset, ce->priv->hint);
5887
get_line_info (buffer, &line_start, &line_end, &line);
5889
#ifdef ENABLE_CHECK_TREE
5891
Segment *inv = get_invalid_segment (ce);
5892
g_assert (inv == NULL || inv->start_at >= line_end_offset);
5896
if (line_start_offset == 0)
5897
state = ce->priv->root_segment;
5899
state = get_segment_at_offset (ce,
5900
ce->priv->hint ? ce->priv->hint : state,
5901
line_start_offset - 1);
5902
g_assert (state->context != NULL);
5904
ce->priv->hint2 = ce->priv->hint;
5906
if (ce->priv->hint2 != NULL && ce->priv->hint2->parent != state)
5907
ce->priv->hint2 = NULL;
5909
state = analyze_line (ce, state, &line);
5911
/* At this point analyze_line() could have disabled highlighting */
5912
if (ce->priv->disabled)
5915
#ifdef ENABLE_CHECK_TREE
5917
Segment *inv = get_invalid_segment (ce);
5918
g_assert (inv == NULL || inv->start_at >= line_end_offset);
5922
/* XXX this is wrong */
5923
/* I don't know anymore why it's wrong, I guess it means
5924
* "may be inefficient" */
5925
if (ce->priv->hint2 != NULL)
5926
ce->priv->hint = ce->priv->hint2;
5928
ce->priv->hint = state;
5930
line_info_destroy (&line);
5932
gtk_text_region_add (ce->priv->refresh_region, &line_start, &line_end);
5933
analyzed_end = line_end_offset;
5934
invalid = get_invalid_segment (ce);
5936
if (invalid != NULL)
5940
gtk_text_buffer_get_iter_at_offset (buffer, &iter, invalid->start_at);
5941
gtk_text_iter_set_line_offset (&iter, 0);
5943
if (gtk_text_iter_get_offset (&iter) == line_end_offset)
5944
next_line_invalid = TRUE;
5947
if (!next_line_invalid)
5949
Segment *old_state, *hint;
5951
hint = ce->priv->hint ? ce->priv->hint : state;
5952
old_state = get_segment_at_offset (ce, hint, line_end_offset);
5954
/* We can merge old and new stuff if: contexts are the same,
5955
* and the segment on the next line is continuation of the
5956
* segment from previous line. */
5957
if (old_state != state &&
5958
(old_state->context != state->context || state->is_start))
5960
need_invalidate_next = TRUE;
5961
next_line_invalid = TRUE;
5965
segment_merge (ce, state, old_state);
5970
if ((time != 0 && g_timer_elapsed (timer, NULL) * 1000 > time) ||
5971
line_end_offset >= end_offset ||
5972
(invalid == NULL && !next_line_invalid))
5974
if (need_invalidate_next)
5975
insert_range (ce, line_end_offset, 0);
5979
if (next_line_invalid)
5981
line_start_offset = line_end_offset;
5982
line_start = line_end;
5983
gtk_text_iter_forward_line (&line_end);
5984
line_end_offset = gtk_text_iter_get_offset (&line_end);
5988
gtk_text_buffer_get_iter_at_offset (buffer, &line_start, invalid->start_at);
5989
gtk_text_iter_set_line_offset (&line_start, 0);
5990
line_start_offset = gtk_text_iter_get_offset (&line_start);
5991
line_end = line_start;
5992
gtk_text_iter_forward_line (&line_end);
5993
line_end_offset = gtk_text_iter_get_offset (&line_end);
5997
if (analyzed_end == gtk_text_buffer_get_char_count (buffer))
5999
g_assert (g_slist_length (ce->priv->invalid) <= 1);
6001
if (ce->priv->invalid != NULL)
6003
invalid = get_invalid_segment (ce);
6004
segment_remove (ce, invalid);
6009
if (!all_analyzed (ce))
6010
install_idle_worker (ce);
6012
gtk_text_iter_set_offset (&end_iter, analyzed_end);
6013
refresh_range (ce, &start_iter, &end_iter, FALSE);
6015
PROFILE (g_print ("analyzed %d chars from %d to %d in %fms\n",
6016
analyzed_end - start_offset, start_offset, analyzed_end,
6017
g_timer_elapsed (timer, NULL) * 1000));
6019
g_timer_destroy (timer);
6022
/* must call context_thaw, so this is the only return point */
6023
context_thaw (ce->priv->root_context);
6027
/* DEFINITIONS MANAGEMENT ------------------------------------------------- */
6029
static DefinitionChild *
6030
definition_child_new (ContextDefinition *definition,
6031
const gchar *child_id,
6033
gboolean override_style,
6034
gboolean is_ref_all,
6035
gboolean original_ref)
6037
DefinitionChild *ch;
6039
g_return_val_if_fail (child_id != NULL, NULL);
6041
ch = g_slice_new0 (DefinitionChild);
6044
ch->u.id = g_strdup_printf ("@%s", child_id);
6046
ch->u.id = g_strdup (child_id);
6048
ch->style = g_strdup (style);
6049
ch->is_ref_all = is_ref_all;
6050
ch->resolved = FALSE;
6051
ch->override_style = override_style;
6052
ch->override_style_deep = (override_style && style == NULL);
6054
definition->children = g_slist_append (definition->children, ch);
6060
definition_child_free (DefinitionChild *ch)
6067
memset (ch, 1, sizeof (DefinitionChild));
6069
g_slice_free (DefinitionChild, ch);
6074
copy_context_classes (GSList *context_classes)
6078
while (context_classes)
6080
ret = g_slist_prepend (ret, gtk_source_context_class_copy (context_classes->data));
6081
context_classes = g_slist_next (context_classes);
6084
return g_slist_reverse (ret);
6087
static ContextDefinition *
6088
context_definition_new (const gchar *id,
6094
GSList *context_classes,
6095
GtkSourceContextFlags flags,
6098
ContextDefinition *definition;
6099
gboolean regex_error = FALSE;
6100
gboolean unresolved_error = FALSE;
6102
g_return_val_if_fail (id != NULL, NULL);
6106
case CONTEXT_TYPE_SIMPLE:
6107
g_return_val_if_fail (match != NULL, NULL);
6108
g_return_val_if_fail (!end && !start, NULL);
6110
case CONTEXT_TYPE_CONTAINER:
6111
g_return_val_if_fail (!match, NULL);
6112
g_return_val_if_fail (!end || start, NULL);
6116
definition = g_slice_new0 (ContextDefinition);
6120
definition->u.match = regex_new (match, G_REGEX_ANCHORED, error);
6122
if (definition->u.match == NULL)
6126
else if (!definition->u.match->resolved)
6129
unresolved_error = TRUE;
6130
regex_unref (definition->u.match);
6131
definition->u.match = NULL;
6137
definition->u.start_end.start = regex_new (start, G_REGEX_ANCHORED, error);
6139
if (definition->u.start_end.start == NULL)
6143
else if (!definition->u.start_end.start->resolved)
6146
unresolved_error = TRUE;
6147
regex_unref (definition->u.start_end.start);
6148
definition->u.start_end.start = NULL;
6152
if (end != NULL && !regex_error)
6154
definition->u.start_end.end = regex_new (end, G_REGEX_ANCHORED, error);
6156
if (definition->u.start_end.end == NULL)
6160
if (unresolved_error)
6163
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6164
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_START_REF,
6165
_("context '%s' cannot contain a \\%%{...@start} command"),
6172
g_slice_free (ContextDefinition, definition);
6176
definition->ref_count = 1;
6177
definition->id = g_strdup (id);
6178
definition->default_style = g_strdup (style);
6179
definition->type = type;
6180
definition->flags = flags;
6181
definition->children = NULL;
6182
definition->sub_patterns = NULL;
6183
definition->n_sub_patterns = 0;
6185
definition->context_classes = copy_context_classes (context_classes);
6190
static ContextDefinition *
6191
context_definition_ref (ContextDefinition *definition)
6193
g_return_val_if_fail (definition != NULL, NULL);
6194
definition->ref_count += 1;
6199
context_definition_unref (ContextDefinition *definition)
6201
GSList *sub_pattern_list;
6203
if (definition == NULL || --definition->ref_count != 0)
6206
switch (definition->type)
6208
case CONTEXT_TYPE_SIMPLE:
6209
regex_unref (definition->u.match);
6211
case CONTEXT_TYPE_CONTAINER:
6212
regex_unref (definition->u.start_end.start);
6213
regex_unref (definition->u.start_end.end);
6217
sub_pattern_list = definition->sub_patterns;
6218
while (sub_pattern_list != NULL)
6220
SubPatternDefinition *sp_def = sub_pattern_list->data;
6221
#ifdef NEED_DEBUG_ID
6222
g_free (sp_def->id);
6224
g_free (sp_def->style);
6225
if (sp_def->is_named)
6226
g_free (sp_def->u.name);
6228
g_slist_foreach (sp_def->context_classes, (GFunc) gtk_source_context_class_free, NULL);
6229
g_slist_free (sp_def->context_classes);
6231
g_slice_free (SubPatternDefinition, sp_def);
6232
sub_pattern_list = sub_pattern_list->next;
6234
g_slist_free (definition->sub_patterns);
6236
g_free (definition->id);
6237
g_free (definition->default_style);
6238
regex_unref (definition->reg_all);
6240
g_slist_foreach (definition->context_classes, (GFunc) gtk_source_context_class_free, NULL);
6241
g_slist_free (definition->context_classes);
6243
g_slist_foreach (definition->children, (GFunc) definition_child_free, NULL);
6244
g_slist_free (definition->children);
6245
g_slice_free (ContextDefinition, definition);
6249
definition_iter_init (DefinitionsIter *iter,
6250
ContextDefinition *definition)
6252
iter->children_stack = g_slist_prepend (NULL, definition->children);
6256
definition_iter_destroy (DefinitionsIter *iter)
6258
g_slist_free (iter->children_stack);
6261
static DefinitionChild *
6262
definition_iter_next (DefinitionsIter *iter)
6264
GSList *children_list;
6266
if (iter->children_stack == NULL)
6269
children_list = iter->children_stack->data;
6270
if (children_list == NULL)
6272
iter->children_stack = g_slist_delete_link (iter->children_stack,
6273
iter->children_stack);
6274
return definition_iter_next (iter);
6278
DefinitionChild *curr_child = children_list->data;
6279
ContextDefinition *definition = curr_child->u.definition;
6281
g_return_val_if_fail (curr_child->resolved, NULL);
6283
children_list = g_slist_next (children_list);
6284
iter->children_stack->data = children_list;
6286
if (curr_child->is_ref_all)
6288
iter->children_stack = g_slist_prepend (iter->children_stack,
6289
definition->children);
6290
return definition_iter_next (iter);
6300
_gtk_source_context_data_define_context (GtkSourceContextData *ctx_data,
6302
const gchar *parent_id,
6303
const gchar *match_regex,
6304
const gchar *start_regex,
6305
const gchar *end_regex,
6307
GSList *context_classes,
6308
GtkSourceContextFlags flags,
6311
ContextDefinition *definition, *parent = NULL;
6314
gboolean wrong_args = FALSE;
6316
g_return_val_if_fail (ctx_data != NULL, FALSE);
6317
g_return_val_if_fail (id != NULL, FALSE);
6319
/* If the id is already present in the hashtable it is a duplicate,
6320
* so we report the error (probably there is a duplicate id in the
6322
if (LOOKUP_DEFINITION (ctx_data, id) != NULL)
6325
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6326
GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID,
6327
_("duplicated context id '%s'"), id);
6331
if (match_regex != NULL)
6332
type = CONTEXT_TYPE_SIMPLE;
6334
type = CONTEXT_TYPE_CONTAINER;
6336
/* Check if the arguments passed are exactly what we expect, no more, no less. */
6339
case CONTEXT_TYPE_SIMPLE:
6340
if (start_regex != NULL || end_regex != NULL)
6343
case CONTEXT_TYPE_CONTAINER:
6344
if (match_regex != NULL)
6352
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6353
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_ARGS,
6354
/* do not translate, parser should take care of this */
6355
"insufficient or redundant arguments creating "
6356
"the context '%s'", id);
6360
if (parent_id == NULL)
6366
parent = LOOKUP_DEFINITION (ctx_data, parent_id);
6367
g_return_val_if_fail (parent != NULL, FALSE);
6370
definition = context_definition_new (id, type, match_regex,
6371
start_regex, end_regex, style,
6374
if (definition == NULL)
6377
g_hash_table_insert (ctx_data->definitions, g_strdup (id), definition);
6378
original_id = g_strdup_printf ("@%s", id);
6379
g_hash_table_insert (ctx_data->definitions, original_id,
6380
context_definition_ref (definition));
6383
definition_child_new (parent, id, NULL, FALSE, FALSE, FALSE);
6389
_gtk_source_context_data_add_sub_pattern (GtkSourceContextData *ctx_data,
6391
const gchar *parent_id,
6395
GSList *context_classes,
6398
ContextDefinition *parent;
6399
SubPatternDefinition *sp_def;
6400
SubPatternWhere where_num;
6403
g_return_val_if_fail (ctx_data != NULL, FALSE);
6404
g_return_val_if_fail (id != NULL, FALSE);
6405
g_return_val_if_fail (parent_id != NULL, FALSE);
6406
g_return_val_if_fail (name != NULL, FALSE);
6408
/* If the id is already present in the hashtable it is a duplicate,
6409
* so we report the error (probably there is a duplicate id in the
6411
if (LOOKUP_DEFINITION (ctx_data, id) != NULL)
6414
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6415
GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID,
6416
_("duplicated context id '%s'"), id);
6420
parent = LOOKUP_DEFINITION (ctx_data, parent_id);
6421
g_return_val_if_fail (parent != NULL, FALSE);
6423
if (!where || !where[0] || !strcmp (where, "default"))
6424
where_num = SUB_PATTERN_WHERE_DEFAULT;
6425
else if (!strcmp (where, "start"))
6426
where_num = SUB_PATTERN_WHERE_START;
6427
else if (!strcmp (where, "end"))
6428
where_num = SUB_PATTERN_WHERE_END;
6430
where_num = (SubPatternWhere) -1;
6432
if ((parent->type == CONTEXT_TYPE_SIMPLE && where_num != SUB_PATTERN_WHERE_DEFAULT) ||
6433
(parent->type == CONTEXT_TYPE_CONTAINER && where_num == SUB_PATTERN_WHERE_DEFAULT))
6435
where_num = (SubPatternWhere) -1;
6438
if (where_num == (SubPatternWhere) -1)
6441
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6442
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_WHERE,
6443
/* do not translate, parent takes care of this */
6444
"invalid location ('%s') for sub pattern '%s'",
6449
sp_def = g_slice_new0 (SubPatternDefinition);
6450
#ifdef NEED_DEBUG_ID
6451
sp_def->id = g_strdup (id);
6453
sp_def->style = g_strdup (style);
6454
sp_def->where = where_num;
6455
number = sub_pattern_to_int (name);
6459
sp_def->is_named = TRUE;
6460
sp_def->u.name = g_strdup (name);
6464
sp_def->is_named = FALSE;
6465
sp_def->u.num = number;
6468
parent->sub_patterns = g_slist_append (parent->sub_patterns, sp_def);
6469
sp_def->index = parent->n_sub_patterns++;
6471
sp_def->context_classes = copy_context_classes (context_classes);
6477
* context_is_pure_container:
6479
* @def: context definition.
6481
* Checks whether context is a container with no start regex.
6482
* References to such contexts are implicitly translated to
6483
* wildcard references (context_id:*).
6486
context_is_pure_container (ContextDefinition *def)
6488
return def->type == CONTEXT_TYPE_CONTAINER &&
6489
def->u.start_end.start == NULL;
6493
_gtk_source_context_data_add_ref (GtkSourceContextData *ctx_data,
6494
const gchar *parent_id,
6495
const gchar *ref_id,
6496
GtkSourceContextRefOptions options,
6501
ContextDefinition *parent;
6502
ContextDefinition *ref;
6503
gboolean override_style = FALSE;
6505
g_return_val_if_fail (parent_id != NULL, FALSE);
6506
g_return_val_if_fail (ref_id != NULL, FALSE);
6507
g_return_val_if_fail (ctx_data != NULL, FALSE);
6509
ref = LOOKUP_DEFINITION (ctx_data, ref_id);
6510
parent = LOOKUP_DEFINITION (ctx_data, parent_id);
6511
g_return_val_if_fail (parent != NULL, FALSE);
6513
if (parent->type != CONTEXT_TYPE_CONTAINER)
6516
GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6517
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_PARENT,
6518
/* do not translate, parent takes care of this */
6519
"invalid parent type for the context '%s'",
6524
if (ref != NULL && context_is_pure_container (ref))
6527
if (all && (options & (GTK_SOURCE_CONTEXT_IGNORE_STYLE | GTK_SOURCE_CONTEXT_OVERRIDE_STYLE)))
6529
g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6530
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_STYLE,
6531
_("style override used with wildcard context reference"
6532
" in language '%s' in ref '%s'"),
6533
ctx_data->lang->priv->id, ref_id);
6537
if (options & (GTK_SOURCE_CONTEXT_IGNORE_STYLE | GTK_SOURCE_CONTEXT_OVERRIDE_STYLE))
6538
override_style = TRUE;
6540
definition_child_new (parent, ref_id, style, override_style, all,
6541
(options & GTK_SOURCE_CONTEXT_REF_ORIGINAL) != 0);
6547
* resolve_reference:
6549
* Checks whether all children of a context definition refer to valid
6550
* contexts. Called from _gtk_source_context_data_finish_parse.
6552
struct ResolveRefData {
6553
GtkSourceContextData *ctx_data;
6558
resolve_reference (G_GNUC_UNUSED const gchar *id,
6559
ContextDefinition *definition,
6564
struct ResolveRefData *data = user_data;
6566
if (data->error != NULL)
6569
for (l = definition->children; l != NULL && data->error == NULL; l = l->next)
6571
ContextDefinition *ref;
6572
DefinitionChild *child_def = l->data;
6574
if (child_def->resolved)
6577
ref = LOOKUP_DEFINITION (data->ctx_data, child_def->u.id);
6581
g_free (child_def->u.id);
6582
child_def->u.definition = ref;
6583
child_def->resolved = TRUE;
6585
if (context_is_pure_container (ref))
6587
if (child_def->override_style)
6589
g_set_error (&data->error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6590
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_STYLE,
6591
_("style override used with wildcard context reference"
6592
" in language '%s' in ref '%s'"),
6593
data->ctx_data->lang->priv->id, ref->id);
6597
child_def->is_ref_all = TRUE;
6603
g_set_error (&data->error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6604
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
6605
_("invalid context reference '%s'"), child_def->u.id);
6611
process_replace (GtkSourceContextData *ctx_data,
6613
const gchar *replace_with,
6616
ContextDefinition *to_replace, *new;
6618
to_replace = LOOKUP_DEFINITION (ctx_data, id);
6620
if (to_replace == NULL)
6622
g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6623
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
6624
_("unknown context '%s'"), id);
6628
new = LOOKUP_DEFINITION (ctx_data, replace_with);
6632
g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6633
GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
6634
_("unknown context '%s'"), replace_with);
6638
g_hash_table_insert (ctx_data->definitions, g_strdup (id), context_definition_ref (new));
6643
GtkSourceContextReplace *
6644
_gtk_source_context_replace_new (const gchar *to_replace_id,
6645
const gchar *replace_with_id)
6647
GtkSourceContextReplace *repl;
6649
g_return_val_if_fail (to_replace_id != NULL, NULL);
6650
g_return_val_if_fail (replace_with_id != NULL, NULL);
6652
repl = g_slice_new (GtkSourceContextReplace);
6653
repl->id = g_strdup (to_replace_id);
6654
repl->replace_with = g_strdup (replace_with_id);
6660
_gtk_source_context_replace_free (GtkSourceContextReplace *repl)
6665
g_free (repl->replace_with);
6666
g_slice_free (GtkSourceContextReplace, repl);
6671
* _gtk_source_context_data_finish_parse:
6673
* @ctx_data: #GtkSourceContextData.
6674
* @overrides: list of #GtkSourceContextOverride objects.
6675
* @error: error structure to be filled in when failed.
6677
* Checks all context references and applies overrides. Lang file may
6678
* use cross-references between contexts, e.g. context A may include
6679
* context B, and context B in turn include context A. Hence during
6680
* parsing it just records referenced context id, and then it needs to
6681
* check the references and replace them with actual context definitions
6682
* (which in turn may be overridden using <override> or <replace> tags).
6683
* May be called any number of times, must be called after parsing is
6686
* Returns: %TRUE on success, %FALSE if there were unresolved
6690
_gtk_source_context_data_finish_parse (GtkSourceContextData *ctx_data,
6694
struct ResolveRefData data;
6696
ContextDefinition *main_definition;
6698
g_return_val_if_fail (ctx_data != NULL, FALSE);
6699
g_return_val_if_fail (ctx_data->lang != NULL, FALSE);
6700
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
6702
while (overrides != NULL)
6704
GtkSourceContextReplace *repl = overrides->data;
6706
g_return_val_if_fail (repl != NULL, FALSE);
6708
if (!process_replace (ctx_data, repl->id, repl->replace_with, error))
6711
overrides = overrides->next;
6714
data.ctx_data = ctx_data;
6717
g_hash_table_foreach (ctx_data->definitions, (GHFunc) resolve_reference, &data);
6719
if (data.error != NULL)
6721
g_propagate_error (error, data.error);
6725
/* Sanity check: user may have screwed up the files by now (#485661) */
6726
root_id = g_strdup_printf ("%s:%s", ctx_data->lang->priv->id, ctx_data->lang->priv->id);
6727
main_definition = LOOKUP_DEFINITION (ctx_data, root_id);
6730
if (main_definition == NULL)
6732
g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
6733
GTK_SOURCE_CONTEXT_ENGINE_ERROR_BAD_FILE,
6734
_("Missing main language "
6735
"definition (id = \"%s\".)"),
6736
ctx_data->lang->priv->id);
6744
add_escape_ref (ContextDefinition *definition,
6745
GtkSourceContextData *ctx_data)
6747
GError *error = NULL;
6749
if (definition->type != CONTEXT_TYPE_CONTAINER)
6752
_gtk_source_context_data_add_ref (ctx_data, definition->id,
6753
"gtk-source-context-engine-escape",
6754
0, NULL, FALSE, &error);
6759
_gtk_source_context_data_add_ref (ctx_data, definition->id,
6760
"gtk-source-context-engine-line-escape",
6761
0, NULL, FALSE, &error);
6766
g_warning ("%s", error->message);
6767
g_error_free (error);
6772
prepend_definition (G_GNUC_UNUSED gchar *id,
6773
ContextDefinition *definition,
6776
*list = g_slist_prepend (*list, definition);
6779
/* Only for lang files version 1, do not use it */
6780
/* It's called after lang file is parsed. It creates two special contexts
6781
contexts and puts them into every container context defined. These contexts
6782
are 'x.' and 'x$', where 'x' is the escape char. In this way, patterns from
6783
lang files are matched only if match doesn't start with escaped char, and
6784
escaped char in the end of line means that the current contexts extends to the
6787
_gtk_source_context_data_set_escape_char (GtkSourceContextData *ctx_data,
6788
gunichar escape_char)
6790
GError *error = NULL;
6793
char *escaped, *pattern;
6794
GSList *definitions = NULL;
6796
g_return_if_fail (ctx_data != NULL);
6797
g_return_if_fail (escape_char != 0);
6799
len = g_unichar_to_utf8 (escape_char, buf);
6800
g_return_if_fail (len > 0);
6802
escaped = g_regex_escape_string (buf, 1);
6803
pattern = g_strdup_printf ("%s.", escaped);
6805
g_hash_table_foreach (ctx_data->definitions, (GHFunc) prepend_definition, &definitions);
6806
definitions = g_slist_reverse (definitions);
6808
if (!_gtk_source_context_data_define_context (ctx_data, "gtk-source-context-engine-escape",
6809
NULL, pattern, NULL, NULL, NULL, NULL,
6810
GTK_SOURCE_CONTEXT_EXTEND_PARENT,
6815
pattern = g_strdup_printf ("%s$", escaped);
6817
if (!_gtk_source_context_data_define_context (ctx_data, "gtk-source-context-engine-line-escape",
6818
NULL, NULL, pattern, "^", NULL, NULL,
6819
GTK_SOURCE_CONTEXT_EXTEND_PARENT,
6823
g_slist_foreach (definitions, (GFunc) add_escape_ref, ctx_data);
6828
g_warning ("%s", error->message);
6829
g_error_free (error);
6834
g_slist_free (definitions);
6838
/* DEBUG CODE ------------------------------------------------------------- */
6840
#ifdef ENABLE_CHECK_TREE
6842
check_segment (GtkSourceContextEngine *ce,
6847
g_assert (segment != NULL);
6848
g_assert (segment->start_at <= segment->end_at);
6849
g_assert (!segment->next || segment->next->start_at >= segment->end_at);
6851
if (SEGMENT_IS_INVALID (segment))
6852
g_assert (g_slist_find (ce->priv->invalid, segment) != NULL);
6854
g_assert (g_slist_find (ce->priv->invalid, segment) == NULL);
6856
if (segment->children != NULL)
6857
g_assert (!SEGMENT_IS_INVALID (segment) && SEGMENT_IS_CONTAINER (segment));
6859
for (child = segment->children; child != NULL; child = child->next)
6861
g_assert (child->parent == segment);
6862
g_assert (child->start_at >= segment->start_at);
6863
g_assert (child->end_at <= segment->end_at);
6864
g_assert (child->prev || child == segment->children);
6865
g_assert (child->next || child == segment->last_child);
6866
check_segment (ce, child);
6870
struct CheckContextData {
6872
ContextDefinition *definition;
6876
check_context_hash_cb (const char *text,
6880
struct CheckContextData *data = user_data;
6882
g_assert (text != NULL);
6883
g_assert (context != NULL);
6884
g_assert (context->definition == data->definition);
6885
g_assert (context->parent == data->parent);
6889
check_context (Context *context)
6893
for (ptr = context->children; ptr != NULL; ptr = ptr->next)
6897
g_assert (ptr->u.context->parent == context);
6898
g_assert (ptr->u.context->definition == ptr->definition);
6899
check_context (ptr->u.context);
6903
struct CheckContextData data;
6904
data.parent = context;
6905
data.definition = ptr->definition;
6906
g_hash_table_foreach (ptr->u.hash,
6907
(GHFunc) check_context_hash_cb,
6916
static gboolean done;
6920
g_assert (!find_single_byte_escape ("gfregerg"));
6921
g_assert (!find_single_byte_escape ("\\\\C"));
6922
g_assert (find_single_byte_escape ("\\C"));
6923
g_assert (find_single_byte_escape ("ewfwefwefwef\\Cwefwefwefwe"));
6924
g_assert (find_single_byte_escape ("ewfwefwefwef\\\\Cwefw\\Cefwefwe"));
6925
g_assert (!find_single_byte_escape ("ewfwefwefwef\\\\Cwefw\\\\Cefwefwe"));
6932
check_tree (GtkSourceContextEngine *ce)
6934
Segment *root = ce->priv->root_segment;
6938
g_assert (root->start_at == 0);
6940
if (ce->priv->invalid_region.empty)
6941
g_assert (root->end_at == gtk_text_buffer_get_char_count (ce->priv->buffer));
6943
g_assert (!root->parent);
6944
check_segment (ce, root);
6946
g_assert (!ce->priv->root_context->parent);
6947
g_assert (root->context == ce->priv->root_context);
6948
check_context (ce->priv->root_context);
6952
check_segment_children (Segment *segment)
6956
g_assert (segment != NULL);
6957
check_segment_list (segment->parent);
6959
for (ch = segment->children; ch != NULL; ch = ch->next)
6961
g_assert (ch->parent == segment);
6962
g_assert (ch->start_at <= ch->end_at);
6963
g_assert (!ch->next || ch->next->start_at >= ch->end_at);
6964
g_assert (ch->start_at >= segment->start_at);
6965
g_assert (ch->end_at <= segment->end_at);
6966
g_assert (ch->prev || ch == segment->children);
6967
g_assert (ch->next || ch == segment->last_child);
6972
check_segment_list (Segment *segment)
6976
if (segment == NULL)
6979
for (ch = segment->children; ch != NULL; ch = ch->next)
6981
g_assert (ch->parent == segment);
6982
g_assert (ch->start_at <= ch->end_at);
6983
g_assert (!ch->next || ch->next->start_at >= ch->end_at);
6984
g_assert (ch->prev || ch == segment->children);
6985
g_assert (ch->next || ch == segment->last_child);
6988
#endif /* ENABLE_CHECK_TREE */
6991
#ifdef ENABLE_MEMORY_DEBUG
6993
GSList *def_regexes;
6994
GSList *ctx_regexes;
7011
HashNodeStruct **nodes;
7012
GHashFunc hash_func;
7013
GEqualFunc key_equal_func;
7014
volatile guint ref_count;
7015
GDestroyNotify key_destroy_func;
7016
GDestroyNotify value_destroy_func;
7020
get_hash_table_mem (GHashTable *ht)
7022
return sizeof (HashTableStruct) +
7023
sizeof (HashNodeStruct) * g_hash_table_size (ht);
7027
add_regex_mem (MemInfo *info,
7036
if (!g_slist_find (info->def_regexes, regex))
7037
info->def_regexes = g_slist_prepend (info->def_regexes, regex);
7041
if (!g_slist_find (info->def_regexes, regex) &&
7042
!g_slist_find (info->ctx_regexes, regex))
7043
info->ctx_regexes = g_slist_prepend (info->ctx_regexes, regex);
7048
get_str_mem (const gchar *string)
7050
return string ? strlen (string) + 1 : 0;
7054
get_def_mem (ContextDefinition *def,
7059
info->def_mem += sizeof (ContextDefinition);
7060
info->def_mem += get_str_mem (def->id);
7061
info->def_mem += get_str_mem (def->default_style);
7063
if (def->type == CONTEXT_TYPE_CONTAINER)
7065
add_regex_mem (info, def->u.start_end.start, TRUE);
7066
add_regex_mem (info, def->u.start_end.end, TRUE);
7070
add_regex_mem (info, def->u.match, TRUE);
7073
for (l = def->children; l != NULL; l = l->next)
7075
DefinitionChild *child_def = l->data;
7077
info->def_mem += sizeof (DefinitionChild);
7078
info->def_mem += get_str_mem (child_def->style);
7080
if (child_def->resolved)
7081
info->def_mem += get_str_mem (child_def->u.id);
7084
for (l = def->sub_patterns; l != NULL; l = l->next)
7086
SubPatternDefinition *sp_def = l->data;
7088
info->def_mem += sizeof (SubPatternDefinition);
7089
info->def_mem += get_str_mem (sp_def->style);
7090
#ifdef NEED_DEBUG_ID
7091
info->def_mem += get_str_mem (sp_def->id);
7094
if (sp_def->is_named)
7095
info->def_mem += get_str_mem (sp_def->u.name);
7098
add_regex_mem (info, def->reg_all, TRUE);
7101
static void get_context_mem (Context *ctx, MemInfo *info);
7104
get_context_mem_cb (const char *id,
7108
info->ctx_mem += get_str_mem (id);
7109
get_context_mem (ctx, info);
7113
get_context_ptr_mem (ContextPtr *ptr,
7118
info->ctx_mem += sizeof (ContextPtr);
7121
get_context_mem (ptr->u.context, info);
7124
info->ctx_mem += get_hash_table_mem (ptr->u.hash);
7125
g_hash_table_foreach (ptr->u.hash, (GHFunc) get_context_mem_cb, info);
7128
get_context_ptr_mem (ptr->next, info);
7133
get_context_mem (Context *ctx,
7138
info->ctx_mem += sizeof (Context);
7139
add_regex_mem (info, ctx->end, FALSE);
7140
add_regex_mem (info, ctx->reg_all, FALSE);
7141
get_context_ptr_mem (ctx->children, info);
7142
info->ctx_mem += ctx->definition->n_sub_patterns * sizeof (GtkTextTag*);
7148
get_def_mem_cb (const char *id,
7149
ContextDefinition *def,
7152
info->def_mem += get_str_mem (id);
7153
get_def_mem (def, info);
7157
get_definitions_mem (GtkSourceContextEngine *ce,
7160
info->def_mem += sizeof (GtkSourceContextData);
7161
info->def_mem += get_hash_table_mem (ce->priv->ctx_data->definitions);
7162
g_hash_table_foreach (ce->priv->ctx_data->definitions,
7163
(GHFunc) get_def_mem_cb,
7168
get_regex_mem (Regex *regex)
7175
mem += sizeof (Regex);
7177
if (regex->resolved)
7178
mem += _egg_regex_get_memory (regex->u.regex.regex);
7180
mem += get_str_mem (regex->u.info.pattern);
7186
mem_usage_timeout (GtkSourceContextEngine *ce)
7189
MemInfo info = {NULL, NULL, 0, 0, 0};
7191
get_definitions_mem (ce, &info);
7192
get_context_mem (ce->priv->root_context, &info);
7194
for (l = info.def_regexes; l != NULL; l = l->next)
7195
info.def_mem += get_regex_mem (l->data);
7197
for (l = info.ctx_regexes; l != NULL; l = l->next)
7198
info.ctx_mem += get_regex_mem (l->data);
7200
g_print ("%s: definitions: %d bytes, contexts: %d bytes in %d contexts\n",
7201
ENGINE_ID (ce), info.def_mem, info.ctx_mem, info.n_ctx);
7203
g_slist_free (info.def_regexes);
7204
g_slist_free (info.ctx_regexes);
7208
#endif /* ENABLE_MEMORY_DEBUG */