~ubuntu-branches/ubuntu/utopic/gtksourceview2/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/fix-invalidate.patch/gtksourceview/gtksourcecontextengine.c

  • Committer: Package Import Robot
  • Author(s): Vlad Orlov
  • Date: 2015-06-02 15:18:59 UTC
  • Revision ID: package-import@ubuntu.com-20150602151859-ns8ilr5ouztq02r5
Tags: 2.10.5-1ubuntu2.14.10.1
fix-invalidate.patch: new patch. Don't invalidate region when
text is deleted and engine is disabled. Fixes crash. (LP: #1461082)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
 
2
 *  gtksourcecontextengine.c
 
3
 *
 
4
 *  Copyright (C) 2003 - Gustavo Giráldez <gustavo.giraldez@gmx.net>
 
5
 *  Copyright (C) 2005, 2006 - Marco Barisione, Emanuele Aina
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 */
 
21
 
 
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"
 
28
 
 
29
#include <glib.h>
 
30
 
 
31
#include <errno.h>
 
32
#include <string.h>
 
33
 
 
34
#undef ENABLE_DEBUG
 
35
#undef ENABLE_PROFILE
 
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 */
 
39
 
 
40
#ifdef ENABLE_DEBUG
 
41
#define DEBUG(x) (x)
 
42
#else
 
43
#define DEBUG(x)
 
44
#endif
 
45
 
 
46
#ifdef ENABLE_PROFILE
 
47
#define PROFILE(x) (x)
 
48
#else
 
49
#define PROFILE(x)
 
50
#endif
 
51
 
 
52
#if defined (ENABLE_DEBUG) || defined (ENABLE_PROFILE) || \
 
53
    defined (ENABLE_CHECK_TREE)
 
54
#define NEED_DEBUG_ID
 
55
#endif
 
56
 
 
57
/* Regex used to match "\%{...@start}". */
 
58
#define START_REF_REGEX "(?<!\\\\)(\\\\\\\\)*\\\\%\\{(.*?)@start\\}"
 
59
 
 
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.
 
64
 */
 
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
 
75
 
 
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
 
79
 
 
80
#define GTK_SOURCE_CONTEXT_ENGINE_ERROR (gtk_source_context_engine_error_quark ())
 
81
 
 
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)))
 
85
 
 
86
#define HAS_OPTION(def,opt) (((def)->flags & GTK_SOURCE_CONTEXT_##opt) != 0)
 
87
 
 
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))
 
94
 
 
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))
 
99
 
 
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)
 
105
 
 
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)
 
111
 
 
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)
 
117
 
 
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)
 
120
 
 
121
#define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
 
122
 
 
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;
 
137
 
 
138
typedef enum {
 
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;
 
149
 
 
150
typedef enum {
 
151
        CONTEXT_TYPE_SIMPLE = 0,
 
152
        CONTEXT_TYPE_CONTAINER
 
153
} ContextType;
 
154
 
 
155
typedef enum {
 
156
        SUB_PATTERN_WHERE_DEFAULT = 0,
 
157
        SUB_PATTERN_WHERE_START,
 
158
        SUB_PATTERN_WHERE_END
 
159
} SubPatternWhere;
 
160
 
 
161
struct _RegexInfo
 
162
{
 
163
        gchar                   *pattern;
 
164
        GRegexCompileFlags       flags;
 
165
};
 
166
 
 
167
/* glib has now so fscking nice API! */
 
168
struct _RegexAndMatch
 
169
{
 
170
        GRegex                  *regex;
 
171
        GMatchInfo              *match;
 
172
};
 
173
 
 
174
/* We do not use directly GRegex to allow the use of "\%{...@start}". */
 
175
struct _Regex
 
176
{
 
177
        union {
 
178
                RegexAndMatch    regex;
 
179
                RegexInfo        info;
 
180
        } u;
 
181
        guint                    ref_count;
 
182
        guint                    resolved : 1;
 
183
};
 
184
 
 
185
struct _ContextDefinition
 
186
{
 
187
        gchar                   *id;
 
188
 
 
189
        ContextType              type;
 
190
        union
 
191
        {
 
192
                Regex           *match;
 
193
                struct {
 
194
                        Regex   *start;
 
195
                        Regex   *end;
 
196
                }                start_end;
 
197
        } u;
 
198
 
 
199
        /* Name of the style used for contexts of this type. */
 
200
        gchar                   *default_style;
 
201
 
 
202
        /* This is a list of DefinitionChild pointers. */
 
203
        GSList                  *children;
 
204
 
 
205
        /* Sub patterns (list of SubPatternDefinition pointers.) */
 
206
        GSList                  *sub_patterns;
 
207
        guint                    n_sub_patterns;
 
208
 
 
209
        /* List of class definitions */
 
210
        GSList                  *context_classes;
 
211
 
 
212
        /* Union of every regular expression we can find from this
 
213
         * context. */
 
214
        Regex                   *reg_all;
 
215
 
 
216
        guint                    flags : 8;
 
217
        guint                    ref_count : 24;
 
218
};
 
219
 
 
220
struct _SubPatternDefinition
 
221
{
 
222
#ifdef NEED_DEBUG_ID
 
223
        /* We need the id only for debugging. */
 
224
        gchar                   *id;
 
225
#endif
 
226
        gchar                   *style;
 
227
        SubPatternWhere          where;
 
228
 
 
229
        /* List of class definitions */
 
230
        GSList                  *context_classes;
 
231
 
 
232
        /* index in the ContextDefinition's list */
 
233
        guint                    index;
 
234
 
 
235
        union
 
236
        {
 
237
                gint             num;
 
238
                gchar           *name;
 
239
        } u;
 
240
        guint                    is_named : 1;
 
241
};
 
242
 
 
243
struct _DefinitionChild
 
244
{
 
245
        union
 
246
        {
 
247
                /* Equal to definition->id, used when it's not resolved yet */
 
248
                gchar                   *id;
 
249
                ContextDefinition       *definition;
 
250
        } u;
 
251
 
 
252
        gchar                   *style;
 
253
 
 
254
        /* Whether this child is a reference to all child contexts of
 
255
         * <definition>. */
 
256
        guint                    is_ref_all : 1;
 
257
        /* Whether it is resolved, i.e. points to actual context definition. */
 
258
        guint                    resolved : 1;
 
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;
 
263
};
 
264
 
 
265
struct _DefinitionsIter
 
266
{
 
267
        GSList                  *children_stack;
 
268
};
 
269
 
 
270
struct _Context
 
271
{
 
272
        /* Definition for the context. */
 
273
        ContextDefinition       *definition;
 
274
 
 
275
        Context                 *parent;
 
276
        ContextPtr              *children;
 
277
 
 
278
        /* This is the regex returned by regex_resolve() called on
 
279
         * definition->start_end.end. */
 
280
        Regex                   *end;
 
281
        /* The regular expression containing every regular expression that
 
282
         * could be matched in this context. */
 
283
        Regex                   *reg_all;
 
284
 
 
285
        /* Either definition->default_style or child_def->style, not copied. */
 
286
        const gchar             *style;
 
287
        GtkTextTag              *tag;
 
288
        GtkTextTag             **subpattern_tags;
 
289
 
 
290
        /* Cache for generated list of class tags */
 
291
        GSList                  *context_classes;
 
292
 
 
293
        /* Cache for generated list of subpattern class tags */
 
294
        GSList                 **subpattern_context_classes;
 
295
 
 
296
        guint                    ref_count;
 
297
        /* see context_freeze() */
 
298
        guint                    frozen : 1;
 
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;
 
303
};
 
304
 
 
305
struct _ContextPtr
 
306
{
 
307
        ContextDefinition       *definition;
 
308
 
 
309
        ContextPtr              *next;
 
310
 
 
311
        union {
 
312
                Context         *context;
 
313
                GHashTable      *hash; /* char* -> Context* */
 
314
        } u;
 
315
        guint                    fixed : 1;
 
316
};
 
317
 
 
318
struct _GtkSourceContextReplace
 
319
{
 
320
        gchar                   *id;
 
321
        gchar                   *replace_with;
 
322
};
 
323
 
 
324
struct _Segment
 
325
{
 
326
        Segment                 *parent;
 
327
        Segment                 *next;
 
328
        Segment                 *prev;
 
329
        Segment                 *children;
 
330
        Segment                 *last_child;
 
331
 
 
332
        /* This is NULL if and only if it's a dummy segment which denotes
 
333
         * inserted or deleted text. */
 
334
        Context                 *context;
 
335
 
 
336
        /* Subpatterns found in this segment. */
 
337
        SubPattern              *sub_patterns;
 
338
 
 
339
        /* The context is used in the interval [start_at; end_at). */
 
340
        gint                     start_at;
 
341
        gint                     end_at;
 
342
 
 
343
        /* In case of container contexts, start_len/end_len is length in chars
 
344
         * of start/end match. */
 
345
        gint                     start_len;
 
346
        gint                     end_len;
 
347
 
 
348
        /* Whether this segment is a whole good segment, or it's an
 
349
         * an end of bigger one left after erase_segments() call. */
 
350
        guint                    is_start : 1;
 
351
};
 
352
 
 
353
struct _SubPattern
 
354
{
 
355
        SubPatternDefinition    *definition;
 
356
        gint                     start_at;
 
357
        gint                     end_at;
 
358
        SubPattern              *next;
 
359
};
 
360
 
 
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
 
371
 * never match).
 
372
 *
 
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)
 
377
 */
 
378
#define NEXT_LINE_OFFSET(l_) ((l_)->start_at + (l_)->char_length + (l_)->eol_length)
 
379
struct _LineInfo
 
380
{
 
381
        /* Line text. */
 
382
        gchar                   *text;
 
383
        /* Character offset of the line in text buffer. */
 
384
        gint                     start_at;
 
385
        /* Character length of line terminator, or 0 if it's the
 
386
         * last line in buffer. */
 
387
        gint                     eol_length;
 
388
        /* Length of the line text not including line terminator */
 
389
        gint                     char_length;
 
390
        gint                     byte_length;
 
391
};
 
392
 
 
393
struct _InvalidRegion
 
394
{
 
395
        gboolean                 empty;
 
396
        GtkTextMark             *start;
 
397
        GtkTextMark             *end;
 
398
        /* offset_at(end) - delta == original offset,
 
399
         * i.e. offset in the tree */
 
400
        gint                     delta;
 
401
};
 
402
 
 
403
struct _GtkSourceContextClass
 
404
{
 
405
        gchar    *name;
 
406
        gboolean  enabled;
 
407
};
 
408
 
 
409
struct _ContextClassTag
 
410
{
 
411
        GtkTextTag *tag;
 
412
        gboolean enabled;
 
413
};
 
414
 
 
415
struct _GtkSourceContextData
 
416
{
 
417
        guint                    ref_count;
 
418
 
 
419
        GtkSourceLanguage       *lang;
 
420
 
 
421
        /* Contains every ContextDefinition indexed by its id. */
 
422
        GHashTable              *definitions;
 
423
};
 
424
 
 
425
struct _GtkSourceContextEnginePrivate
 
426
{
 
427
        GtkSourceContextData    *ctx_data;
 
428
 
 
429
        GtkTextBuffer           *buffer;
 
430
        GtkSourceStyleScheme    *style_scheme;
 
431
 
 
432
        /* All tags indexed by style name: values are GSList's of tags, ref()'ed. */
 
433
        GHashTable              *tags;
 
434
        /* Number of all syntax tags created by the engine, needed to set correct
 
435
         * tag priorities */
 
436
        guint                    n_tags;
 
437
 
 
438
        GHashTable              *context_classes;
 
439
 
 
440
        /* Whether or not to actually highlight the buffer. */
 
441
        gboolean                 highlight;
 
442
 
 
443
        /* Whether highlighting was disabled because of errors. */
 
444
        gboolean                 disabled;
 
445
 
 
446
        /* Region covering the unhighlighted text. */
 
447
        GtkTextRegion           *refresh_region;
 
448
 
 
449
        /* Tree of contexts. */
 
450
        Context                 *root_context;
 
451
        Segment                 *root_segment;
 
452
        Segment                 *hint;
 
453
        Segment                 *hint2;
 
454
        /* list of Segment* */
 
455
        GSList                  *invalid;
 
456
        InvalidRegion            invalid_region;
 
457
 
 
458
        guint                    first_update;
 
459
        guint                    incremental_update;
 
460
 
 
461
        /* Views highlight requests. */
 
462
        GtkTextRegion           *highlight_requests;
 
463
 
 
464
#ifdef ENABLE_MEMORY_DEBUG
 
465
        guint                    mem_usage_timeout;
 
466
#endif
 
467
};
 
468
 
 
469
 
 
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
 
477
#else
 
478
#define CHECK_TREE(ce)
 
479
#define CHECK_SEGMENT_LIST(s)
 
480
#define CHECK_SEGMENT_CHILDREN(s)
 
481
#endif
 
482
 
 
483
 
 
484
static GQuark           gtk_source_context_engine_error_quark (void) G_GNUC_CONST;
 
485
 
 
486
static Segment         *create_segment          (GtkSourceContextEngine *ce,
 
487
                                                 Segment                *parent,
 
488
                                                 Context                *context,
 
489
                                                 gint                    start_at,
 
490
                                                 gint                    end_at,
 
491
                                                 gboolean                is_start,
 
492
                                                 Segment                *hint);
 
493
static Segment         *segment_new             (GtkSourceContextEngine *ce,
 
494
                                                 Segment                *parent,
 
495
                                                 Context                *context,
 
496
                                                 gint                    start_at,
 
497
                                                 gint                    end_at,
 
498
                                                 gboolean                is_start);
 
499
static Context         *context_new             (Context                *parent,
 
500
                                                 ContextDefinition      *definition,
 
501
                                                 const gchar            *line_text,
 
502
                                                 const gchar            *style,
 
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,
 
508
                                                 gint                    start,
 
509
                                                 gint                    end,
 
510
                                                 Segment                *hint);
 
511
static void             segment_remove          (GtkSourceContextEngine *ce,
 
512
                                                 Segment                *segment);
 
513
 
 
514
static void             find_insertion_place    (Segment                *segment,
 
515
                                                 gint                    offset,
 
516
                                                 Segment               **parent,
 
517
                                                 Segment               **prev,
 
518
                                                 Segment               **next,
 
519
                                                 Segment                *hint);
 
520
static void             segment_destroy         (GtkSourceContextEngine *ce,
 
521
                                                 Segment                *segment);
 
522
static ContextDefinition *context_definition_ref(ContextDefinition      *definition);
 
523
static void             context_definition_unref(ContextDefinition      *definition);
 
524
 
 
525
static void             segment_extend          (Segment                *state,
 
526
                                                 gint                    end_at);
 
527
static Context         *ancestor_context_ends_here (Context             *state,
 
528
                                                 LineInfo               *line,
 
529
                                                 gint                    pos);
 
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);
 
534
 
 
535
static void             update_syntax           (GtkSourceContextEngine *ce,
 
536
                                                 const GtkTextIter      *end,
 
537
                                                 gint                    time);
 
538
static void             install_idle_worker     (GtkSourceContextEngine *ce);
 
539
static void             install_first_update    (GtkSourceContextEngine *ce);
 
540
 
 
541
#ifdef ENABLE_MEMORY_DEBUG
 
542
static gboolean         mem_usage_timeout       (GtkSourceContextEngine *ce);
 
543
#endif
 
544
 
 
545
 
 
546
/* TAGS AND STUFF -------------------------------------------------------------- */
 
547
 
 
548
GtkSourceContextClass *
 
549
gtk_source_context_class_new (gchar const *name,
 
550
                              gboolean     enabled)
 
551
{
 
552
        GtkSourceContextClass *def = g_slice_new (GtkSourceContextClass);
 
553
 
 
554
        def->name = g_strdup (name);
 
555
        def->enabled = enabled;
 
556
 
 
557
        return def;
 
558
}
 
559
 
 
560
static GtkSourceContextClass *
 
561
gtk_source_context_class_copy (GtkSourceContextClass *cclass)
 
562
{
 
563
        return gtk_source_context_class_new (cclass->name, cclass->enabled);
 
564
}
 
565
 
 
566
void
 
567
gtk_source_context_class_free (GtkSourceContextClass *cclass)
 
568
{
 
569
        g_free (cclass->name);
 
570
        g_slice_free (GtkSourceContextClass, cclass);
 
571
}
 
572
 
 
573
static ContextClassTag *
 
574
context_class_tag_new (GtkTextTag *tag,
 
575
                       gboolean    enabled)
 
576
{
 
577
        ContextClassTag *attrtag = g_slice_new (ContextClassTag);
 
578
 
 
579
        attrtag->tag = tag;
 
580
        attrtag->enabled = enabled;
 
581
 
 
582
        return attrtag;
 
583
}
 
584
 
 
585
static void
 
586
context_class_tag_free (ContextClassTag *attrtag)
 
587
{
 
588
        g_slice_free (ContextClassTag, attrtag);
 
589
}
 
590
 
 
591
struct BufAndIters {
 
592
        GtkTextBuffer *buffer;
 
593
        const GtkTextIter *start, *end;
 
594
};
 
595
 
 
596
static void
 
597
unhighlight_region_cb (G_GNUC_UNUSED gpointer style,
 
598
                       GSList   *tags,
 
599
                       gpointer  user_data)
 
600
{
 
601
        struct BufAndIters *data = user_data;
 
602
 
 
603
        while (tags != NULL)
 
604
        {
 
605
                gtk_text_buffer_remove_tag (data->buffer,
 
606
                                            tags->data,
 
607
                                            data->start,
 
608
                                            data->end);
 
609
                tags = tags->next;
 
610
        }
 
611
}
 
612
 
 
613
static void
 
614
unhighlight_region (GtkSourceContextEngine *ce,
 
615
                    const GtkTextIter      *start,
 
616
                    const GtkTextIter      *end)
 
617
{
 
618
        struct BufAndIters data;
 
619
 
 
620
        data.buffer = ce->priv->buffer;
 
621
        data.start = start;
 
622
        data.end = end;
 
623
 
 
624
        if (gtk_text_iter_equal (start, end))
 
625
                return;
 
626
 
 
627
        g_hash_table_foreach (ce->priv->tags, (GHFunc) unhighlight_region_cb, &data);
 
628
}
 
629
 
 
630
#define MAX_STYLE_DEPENDENCY_DEPTH      50
 
631
 
 
632
static void
 
633
set_tag_style (GtkSourceContextEngine *ce,
 
634
               GtkTextTag             *tag,
 
635
               const gchar            *style_id)
 
636
{
 
637
        GtkSourceStyle *style;
 
638
 
 
639
        const char *map_to = style_id;
 
640
 
 
641
        int guard = 0;
 
642
 
 
643
        g_return_if_fail (GTK_IS_TEXT_TAG (tag));
 
644
        g_return_if_fail (style_id != NULL);
 
645
 
 
646
        _gtk_source_style_apply (NULL, tag);
 
647
 
 
648
        if (ce->priv->style_scheme == NULL)
 
649
                return;
 
650
 
 
651
        style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, style_id);
 
652
 
 
653
        while (style == NULL)
 
654
        {
 
655
                GtkSourceStyleInfo *info;
 
656
 
 
657
                if (guard > MAX_STYLE_DEPENDENCY_DEPTH)
 
658
                {
 
659
                        g_warning ("Potential circular dependency between styles detected for style '%s'", style_id);
 
660
                        break;
 
661
                }
 
662
 
 
663
                ++guard;
 
664
 
 
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);
 
668
 
 
669
                map_to = (info != NULL) ? info->map_to : NULL;
 
670
 
 
671
                if (!map_to)
 
672
                        break;
 
673
 
 
674
                style = gtk_source_style_scheme_get_style (ce->priv->style_scheme, map_to);
 
675
        }
 
676
 
 
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 */
 
679
        if (style != NULL)
 
680
                _gtk_source_style_apply (style, tag);
 
681
}
 
682
 
 
683
static GtkTextTag *
 
684
create_tag (GtkSourceContextEngine *ce,
 
685
            const gchar            *style_id)
 
686
{
 
687
        GSList *tags;
 
688
        GtkTextTag *new_tag;
 
689
 
 
690
        g_assert (style_id != NULL);
 
691
 
 
692
        tags = g_hash_table_lookup (ce->priv->tags, style_id);
 
693
 
 
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;
 
700
 
 
701
        tags = g_slist_prepend (tags, g_object_ref (new_tag));
 
702
        g_hash_table_insert (ce->priv->tags, g_strdup (style_id), tags);
 
703
 
 
704
        return new_tag;
 
705
}
 
706
 
 
707
/* Find tag which has to be overridden. */
 
708
static GtkTextTag *
 
709
get_parent_tag (Context    *context,
 
710
                const char *style)
 
711
{
 
712
        while (context != NULL)
 
713
        {
 
714
                /* Lang files may repeat same style for nested contexts,
 
715
                 * ignore them. */
 
716
                if (context->style &&
 
717
                    strcmp (context->style, style) != 0)
 
718
                {
 
719
                        g_assert (context->tag != NULL);
 
720
                        return context->tag;
 
721
                }
 
722
 
 
723
                context = context->parent;
 
724
        }
 
725
 
 
726
        return NULL;
 
727
}
 
728
 
 
729
static GtkTextTag *
 
730
get_tag_for_parent (GtkSourceContextEngine *ce,
 
731
                    const char             *style,
 
732
                    Context                *parent)
 
733
{
 
734
        GSList *tags;
 
735
        GtkTextTag *parent_tag = NULL;
 
736
        GtkTextTag *tag;
 
737
 
 
738
        g_return_val_if_fail (style != NULL, NULL);
 
739
 
 
740
        parent_tag = get_parent_tag (parent, style);
 
741
        tags = g_hash_table_lookup (ce->priv->tags, style);
 
742
 
 
743
        if (tags && (!parent_tag ||
 
744
                gtk_text_tag_get_priority (tags->data) > gtk_text_tag_get_priority (parent_tag)))
 
745
        {
 
746
                GSList *link;
 
747
 
 
748
                tag = tags->data;
 
749
 
 
750
                /* Now get the tag with lowest priority, so that tag lists do not grow
 
751
                 * indefinitely. */
 
752
                for (link = tags->next; link != NULL; link = link->next)
 
753
                {
 
754
                        if (parent_tag &&
 
755
                            gtk_text_tag_get_priority (link->data) < gtk_text_tag_get_priority (parent_tag))
 
756
                                break;
 
757
                        tag = link->data;
 
758
                }
 
759
        }
 
760
        else
 
761
        {
 
762
                tag = create_tag (ce, style);
 
763
 
 
764
#ifdef ENABLE_DEBUG
 
765
                {
 
766
                        GString *style_path = g_string_new (style);
 
767
                        gint n;
 
768
 
 
769
                        while (parent != NULL)
 
770
                        {
 
771
                                if (parent->style != NULL)
 
772
                                {
 
773
                                        g_string_prepend (style_path, "/");
 
774
                                        g_string_prepend (style_path,
 
775
                                                          parent->style);
 
776
                                }
 
777
 
 
778
                                parent = parent->parent;
 
779
                        }
 
780
 
 
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);
 
785
                }
 
786
#endif
 
787
        }
 
788
 
 
789
        return tag;
 
790
}
 
791
 
 
792
static GtkTextTag *
 
793
get_subpattern_tag (GtkSourceContextEngine *ce,
 
794
                    Context                *context,
 
795
                    SubPatternDefinition   *sp_def)
 
796
{
 
797
        if (sp_def->style == NULL)
 
798
                return NULL;
 
799
 
 
800
        g_assert (sp_def->index < context->definition->n_sub_patterns);
 
801
 
 
802
        if (context->subpattern_tags == NULL)
 
803
                context->subpattern_tags = g_new0 (GtkTextTag*, context->definition->n_sub_patterns);
 
804
 
 
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);
 
807
 
 
808
        g_return_val_if_fail (context->subpattern_tags[sp_def->index] != NULL, NULL);
 
809
        return context->subpattern_tags[sp_def->index];
 
810
}
 
811
 
 
812
static GtkTextTag *
 
813
get_context_tag (GtkSourceContextEngine *ce,
 
814
                 Context                *context)
 
815
{
 
816
        if (context->style != NULL && context->tag == NULL)
 
817
                context->tag = get_tag_for_parent (ce,
 
818
                                                   context->style,
 
819
                                                   context->parent);
 
820
        return context->tag;
 
821
}
 
822
 
 
823
static void
 
824
apply_tags (GtkSourceContextEngine *ce,
 
825
            Segment                *segment,
 
826
            gint                    start_offset,
 
827
            gint                    end_offset)
 
828
{
 
829
        GtkTextTag *tag;
 
830
        GtkTextIter start_iter, end_iter;
 
831
        GtkTextBuffer *buffer = ce->priv->buffer;
 
832
        SubPattern *sp;
 
833
        Segment *child;
 
834
 
 
835
        g_assert (segment != NULL);
 
836
 
 
837
        if (SEGMENT_IS_INVALID (segment))
 
838
                return;
 
839
 
 
840
        if (segment->start_at >= end_offset || segment->end_at <= start_offset)
 
841
                return;
 
842
 
 
843
        start_offset = MAX (start_offset, segment->start_at);
 
844
        end_offset = MIN (end_offset, segment->end_at);
 
845
 
 
846
        tag = get_context_tag (ce, segment->context);
 
847
 
 
848
        if (tag != NULL)
 
849
        {
 
850
                gint style_start_at, style_end_at;
 
851
 
 
852
                style_start_at = start_offset;
 
853
                style_end_at = end_offset;
 
854
 
 
855
                if (HAS_OPTION (segment->context->definition, STYLE_INSIDE))
 
856
                {
 
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);
 
859
                }
 
860
 
 
861
                if (style_start_at > style_end_at)
 
862
                {
 
863
                        g_critical ("%s: oops", G_STRLOC);
 
864
                }
 
865
                else
 
866
                {
 
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);
 
871
                }
 
872
        }
 
873
 
 
874
        for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
 
875
        {
 
876
                if (sp->start_at >= start_offset && sp->end_at <= end_offset)
 
877
                {
 
878
                        gint start = MAX (start_offset, sp->start_at);
 
879
                        gint end = MIN (end_offset, sp->end_at);
 
880
 
 
881
                        tag = get_subpattern_tag (ce, segment->context, sp->definition);
 
882
 
 
883
                        if (tag != NULL)
 
884
                        {
 
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);
 
889
                        }
 
890
                }
 
891
        }
 
892
 
 
893
        for (child = segment->children;
 
894
             child != NULL && child->start_at < end_offset;
 
895
             child = child->next)
 
896
        {
 
897
                if (child->end_at > start_offset)
 
898
                        apply_tags (ce, child, start_offset, end_offset);
 
899
        }
 
900
}
 
901
 
 
902
/**
 
903
 * highlight_region:
 
904
 *
 
905
 * @ce: a #GtkSourceContextEngine.
 
906
 * @start: the beginning of the region to highlight.
 
907
 * @end: the end of the region to highlight.
 
908
 *
 
909
 * Highlights the specified region.
 
910
 */
 
911
static void
 
912
highlight_region (GtkSourceContextEngine *ce,
 
913
                  GtkTextIter            *start,
 
914
                  GtkTextIter            *end)
 
915
{
 
916
#ifdef ENABLE_PROFILE
 
917
        GTimer *timer;
 
918
#endif
 
919
 
 
920
        if (gtk_text_iter_starts_line (end))
 
921
                gtk_text_iter_backward_char (end);
 
922
        if (gtk_text_iter_compare (start, end) >= 0)
 
923
                return;
 
924
 
 
925
#ifdef ENABLE_PROFILE
 
926
        timer = g_timer_new ();
 
927
#endif
 
928
 
 
929
        /* First we need to delete tags in the regions. */
 
930
        unhighlight_region (ce, start, end);
 
931
 
 
932
        apply_tags (ce, ce->priv->root_segment,
 
933
                    gtk_text_iter_get_offset (start),
 
934
                    gtk_text_iter_get_offset (end));
 
935
 
 
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);
 
942
#endif
 
943
}
 
944
 
 
945
/**
 
946
 * ensure_highlighted:
 
947
 *
 
948
 * @ce: a #GtkSourceContextEngine.
 
949
 * @start: the beginning of the region to highlight.
 
950
 * @end: the end of the region to highlight.
 
951
 *
 
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).
 
957
 */
 
958
static void
 
959
ensure_highlighted (GtkSourceContextEngine *ce,
 
960
                    const GtkTextIter      *start,
 
961
                    const GtkTextIter      *end)
 
962
{
 
963
        GtkTextRegion *region;
 
964
        GtkTextRegionIterator reg_iter;
 
965
 
 
966
        /* Get the subregions not yet highlighted. */
 
967
        region = gtk_text_region_intersect (ce->priv->refresh_region, start, end);
 
968
 
 
969
        if (region == NULL)
 
970
                return;
 
971
 
 
972
        gtk_text_region_get_iterator (region, &reg_iter, 0);
 
973
 
 
974
        /* Highlight all subregions from the intersection.
 
975
         * hopefully this will only be one subregion. */
 
976
        while (!gtk_text_region_iterator_is_end (&reg_iter))
 
977
        {
 
978
                GtkTextIter s, e;
 
979
                gtk_text_region_iterator_get_subregion (&reg_iter, &s, &e);
 
980
                highlight_region (ce, &s, &e);
 
981
                gtk_text_region_iterator_next (&reg_iter);
 
982
        }
 
983
 
 
984
        gtk_text_region_destroy (region, TRUE);
 
985
 
 
986
        /* Remove the just highlighted region. */
 
987
        gtk_text_region_subtract (ce->priv->refresh_region, start, end);
 
988
}
 
989
 
 
990
static GtkTextTag *
 
991
get_context_class_tag (GtkSourceContextEngine *ce,
 
992
                       gchar const            *name)
 
993
{
 
994
        GtkTextTag *ret;
 
995
 
 
996
        ret = g_hash_table_lookup (ce->priv->context_classes, name);
 
997
 
 
998
        if (ret == NULL)
 
999
        {
 
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,
 
1003
                                        g_strdup (name),
 
1004
                                        (GDestroyNotify)g_free);
 
1005
 
 
1006
                g_hash_table_insert (ce->priv->context_classes,
 
1007
                                     g_strdup (name),
 
1008
                                     ret);
 
1009
        }
 
1010
 
 
1011
        return ret;
 
1012
}
 
1013
 
 
1014
static GSList *
 
1015
extend_context_classes (GtkSourceContextEngine *ce,
 
1016
                        GSList                 *definitions)
 
1017
{
 
1018
        GSList *item;
 
1019
        GSList *ret = NULL;
 
1020
 
 
1021
        for (item = definitions; item != NULL; item = g_slist_next (item))
 
1022
        {
 
1023
                GtkSourceContextClass *cclass = item->data;
 
1024
                ContextClassTag *attrtag = context_class_tag_new (get_context_class_tag (ce, cclass->name),
 
1025
                                                                  cclass->enabled);
 
1026
 
 
1027
                ret = g_slist_prepend (ret, attrtag);
 
1028
        }
 
1029
 
 
1030
        return g_slist_reverse (ret);
 
1031
}
 
1032
 
 
1033
static GSList *
 
1034
get_subpattern_context_classes (GtkSourceContextEngine *ce,
 
1035
                                Context                *context,
 
1036
                                SubPatternDefinition   *sp_def)
 
1037
{
 
1038
        g_assert (sp_def->index < context->definition->n_sub_patterns);
 
1039
 
 
1040
        if (context->subpattern_context_classes == NULL)
 
1041
                context->subpattern_context_classes = g_new0 (GSList *, context->definition->n_sub_patterns);
 
1042
 
 
1043
        if (context->subpattern_context_classes[sp_def->index] == NULL)
 
1044
        {
 
1045
                context->subpattern_context_classes[sp_def->index] =
 
1046
                                extend_context_classes (ce,
 
1047
                                                        sp_def->context_classes);
 
1048
        }
 
1049
 
 
1050
        return context->subpattern_context_classes[sp_def->index];
 
1051
}
 
1052
 
 
1053
static GSList *
 
1054
get_context_classes (GtkSourceContextEngine *ce,
 
1055
                     Context                *context)
 
1056
{
 
1057
        if (context->context_classes == NULL)
 
1058
        {
 
1059
                context->context_classes =
 
1060
                                extend_context_classes (ce,
 
1061
                                                        context->definition->context_classes);
 
1062
        }
 
1063
 
 
1064
        return context->context_classes;
 
1065
}
 
1066
 
 
1067
static void
 
1068
apply_context_classes (GtkSourceContextEngine *ce,
 
1069
                       GSList                 *context_classes,
 
1070
                       gint                    start,
 
1071
                       gint                    end)
 
1072
{
 
1073
        GtkTextIter start_iter;
 
1074
        GtkTextIter end_iter;
 
1075
        GSList *item;
 
1076
 
 
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);
 
1080
 
 
1081
        for (item = context_classes; item != NULL; item = g_slist_next (item))
 
1082
        {
 
1083
                ContextClassTag *attrtag = item->data;
 
1084
 
 
1085
                if (attrtag->enabled)
 
1086
                {
 
1087
                        gtk_text_buffer_apply_tag (ce->priv->buffer,
 
1088
                                                   attrtag->tag,
 
1089
                                                   &start_iter,
 
1090
                                                   &end_iter);
 
1091
                }
 
1092
                else
 
1093
                {
 
1094
                        gtk_text_buffer_remove_tag (ce->priv->buffer,
 
1095
                                                    attrtag->tag,
 
1096
                                                    &start_iter,
 
1097
                                                    &end_iter);
 
1098
                }
 
1099
        }
 
1100
}
 
1101
 
 
1102
static void
 
1103
add_region_context_classes (GtkSourceContextEngine *ce,
 
1104
                            Segment                *segment,
 
1105
                            gint                    start_offset,
 
1106
                            gint                    end_offset)
 
1107
{
 
1108
        SubPattern *sp;
 
1109
        Segment *child;
 
1110
        GSList *context_classes;
 
1111
 
 
1112
        g_assert (segment != NULL);
 
1113
 
 
1114
        if (SEGMENT_IS_INVALID (segment))
 
1115
        {
 
1116
                return;
 
1117
        }
 
1118
 
 
1119
        if (segment->start_at >= end_offset || segment->end_at <= start_offset)
 
1120
        {
 
1121
                return;
 
1122
        }
 
1123
 
 
1124
        start_offset = MAX (start_offset, segment->start_at);
 
1125
        end_offset = MIN (end_offset, segment->end_at);
 
1126
 
 
1127
        context_classes = get_context_classes (ce,
 
1128
                                               segment->context);
 
1129
 
 
1130
        if (context_classes != NULL)
 
1131
        {
 
1132
                apply_context_classes (ce,
 
1133
                                       context_classes,
 
1134
                                       start_offset,
 
1135
                                       end_offset);
 
1136
        }
 
1137
 
 
1138
        for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
 
1139
        {
 
1140
                if (sp->start_at >= start_offset && sp->end_at <= end_offset)
 
1141
                {
 
1142
                        gint start = MAX (start_offset, sp->start_at);
 
1143
                        gint end = MIN (end_offset, sp->end_at);
 
1144
 
 
1145
                        context_classes = get_subpattern_context_classes (ce,
 
1146
                                                                          segment->context,
 
1147
                                                                          sp->definition);
 
1148
 
 
1149
                        if (context_classes != NULL)
 
1150
                        {
 
1151
                                apply_context_classes (ce,
 
1152
                                                       context_classes,
 
1153
                                                       start,
 
1154
                                                       end);
 
1155
                        }
 
1156
                }
 
1157
        }
 
1158
 
 
1159
        for (child = segment->children;
 
1160
             child != NULL && child->start_at < end_offset;
 
1161
             child = child->next)
 
1162
        {
 
1163
                if (child->end_at > start_offset)
 
1164
                {
 
1165
                        add_region_context_classes (ce, child, start_offset, end_offset);
 
1166
                }
 
1167
        }
 
1168
}
 
1169
 
 
1170
static void
 
1171
remove_region_context_class_cb (G_GNUC_UNUSED gpointer class,
 
1172
                                GtkTextTag             *tag,
 
1173
                                gpointer                user_data)
 
1174
{
 
1175
        struct BufAndIters *data = user_data;
 
1176
        gtk_text_buffer_remove_tag (data->buffer,
 
1177
                                    tag,
 
1178
                                    data->start,
 
1179
                                    data->end);
 
1180
}
 
1181
 
 
1182
static void
 
1183
remove_region_context_classes (GtkSourceContextEngine *ce,
 
1184
                               const GtkTextIter      *start,
 
1185
                               const GtkTextIter      *end)
 
1186
{
 
1187
        struct BufAndIters data;
 
1188
 
 
1189
        data.buffer = ce->priv->buffer;
 
1190
        data.start = start;
 
1191
        data.end = end;
 
1192
 
 
1193
        if (gtk_text_iter_equal (start, end))
 
1194
                return;
 
1195
 
 
1196
        g_hash_table_foreach (ce->priv->context_classes,
 
1197
                              (GHFunc) remove_region_context_class_cb,
 
1198
                              &data);
 
1199
}
 
1200
 
 
1201
static void
 
1202
refresh_context_classes (GtkSourceContextEngine *ce,
 
1203
                         const GtkTextIter      *start,
 
1204
                         const GtkTextIter      *end)
 
1205
{
 
1206
#ifdef ENABLE_PROFILE
 
1207
        GTimer *timer;
 
1208
#endif
 
1209
        GtkTextIter realend = *end;
 
1210
 
 
1211
        if (gtk_text_iter_starts_line (&realend))
 
1212
        {
 
1213
                gtk_text_iter_backward_char (&realend);
 
1214
        }
 
1215
 
 
1216
        if (gtk_text_iter_compare (start, &realend) >= 0)
 
1217
        {
 
1218
                return;
 
1219
        }
 
1220
 
 
1221
#ifdef ENABLE_PROFILE
 
1222
        timer = g_timer_new ();
 
1223
#endif
 
1224
 
 
1225
        /* First we need to delete tags in the regions. */
 
1226
        remove_region_context_classes (ce, start, &realend);
 
1227
 
 
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));
 
1232
 
 
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);
 
1239
#endif
 
1240
}
 
1241
 
 
1242
/**
 
1243
 * refresh_range:
 
1244
 *
 
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
 
1249
 * refresh_region.
 
1250
 *
 
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).
 
1256
 */
 
1257
static void
 
1258
refresh_range (GtkSourceContextEngine *ce,
 
1259
               const GtkTextIter      *start,
 
1260
               const GtkTextIter      *end,
 
1261
               gboolean                modify_refresh_region)
 
1262
{
 
1263
        GtkTextIter real_end;
 
1264
 
 
1265
        if (gtk_text_iter_equal (start, end))
 
1266
                return;
 
1267
 
 
1268
        if (modify_refresh_region)
 
1269
                gtk_text_region_add (ce->priv->refresh_region, start, end);
 
1270
 
 
1271
        /* Refresh the contex classes here */
 
1272
        refresh_context_classes (ce, start, end);
 
1273
 
 
1274
        /* Here we need to make sure we do not make it redraw next line */
 
1275
        real_end = *end;
 
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);
 
1280
 
 
1281
        g_signal_emit_by_name (ce->priv->buffer,
 
1282
                               "highlight_updated",
 
1283
                               start,
 
1284
                               &real_end);
 
1285
}
 
1286
 
 
1287
 
 
1288
/* SEGMENT TREE ----------------------------------------------------------- */
 
1289
 
 
1290
/**
 
1291
 * segment_cmp:
 
1292
 *
 
1293
 * @s1: first segment.
 
1294
 * @s2: second segment.
 
1295
 *
 
1296
 * Compares segments by their offset, used to sort list of invalid segments.
 
1297
 *
 
1298
 * Returns: an integer like strcmp() does.
 
1299
 */
 
1300
static gint
 
1301
segment_cmp (Segment *s1,
 
1302
             Segment *s2)
 
1303
{
 
1304
        if (s1->start_at < s2->start_at)
 
1305
                return -1;
 
1306
        else if (s1->start_at > s2->start_at)
 
1307
                return 1;
 
1308
        /* one of them must be zero-length */
 
1309
        g_assert (s1->start_at == s1->end_at || s2->start_at == s2->end_at);
 
1310
#ifdef ENABLE_DEBUG
 
1311
        /* A new zero-length segment should never be created if there is
 
1312
         * already an invalid segment. */
 
1313
        g_assert_not_reached ();
 
1314
#endif
 
1315
        g_return_val_if_reached (s1->end_at < s2->end_at ? -1 :
 
1316
                                 (s1->end_at > s2->end_at ? 1 : 0));
 
1317
}
 
1318
 
 
1319
/**
 
1320
 * add_invalid:
 
1321
 *
 
1322
 * @ce: the engine.
 
1323
 * @segment: segment.
 
1324
 *
 
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.
 
1328
 */
 
1329
static void
 
1330
add_invalid (GtkSourceContextEngine *ce,
 
1331
             Segment                *segment)
 
1332
{
 
1333
#ifdef ENABLE_CHECK_TREE
 
1334
        g_assert (!g_slist_find (ce->priv->invalid, segment));
 
1335
#endif
 
1336
        g_return_if_fail (SEGMENT_IS_INVALID (segment));
 
1337
 
 
1338
        ce->priv->invalid = g_slist_insert_sorted (ce->priv->invalid,
 
1339
                                                   segment,
 
1340
                                                   (GCompareFunc) segment_cmp);
 
1341
 
 
1342
        DEBUG (g_print ("%d invalid\n", g_slist_length (ce->priv->invalid)));
 
1343
}
 
1344
 
 
1345
/**
 
1346
 * remove_invalid:
 
1347
 *
 
1348
 * @ce: the engine.
 
1349
 * @segment: segment.
 
1350
 *
 
1351
 * Removes segment from the list of invalid segments;
 
1352
 * Called when an invalid segment is destroyed (invalid
 
1353
 * segments never become valid).
 
1354
 */
 
1355
static void
 
1356
remove_invalid (GtkSourceContextEngine *ce,
 
1357
                Segment                *segment)
 
1358
{
 
1359
        g_assert (g_slist_find (ce->priv->invalid, segment) != NULL);
 
1360
        ce->priv->invalid = g_slist_remove (ce->priv->invalid, segment);
 
1361
}
 
1362
 
 
1363
/**
 
1364
 * fix_offsets_insert_:
 
1365
 *
 
1366
 * @segment: segment.
 
1367
 * @start: start offset.
 
1368
 * @delta: length of inserted text.
 
1369
 *
 
1370
 * Recursively updates offsets after inserting text. To be called
 
1371
 * only from insert_range().
 
1372
 */
 
1373
static void
 
1374
fix_offsets_insert_ (Segment *segment,
 
1375
                     gint     start,
 
1376
                     gint     delta)
 
1377
{
 
1378
        Segment *child;
 
1379
        SubPattern *sp;
 
1380
 
 
1381
        g_assert (segment->start_at >= start);
 
1382
 
 
1383
        if (delta == 0)
 
1384
                return;
 
1385
 
 
1386
        segment->start_at += delta;
 
1387
        segment->end_at += delta;
 
1388
 
 
1389
        for (child = segment->children; child != NULL; child = child->next)
 
1390
                fix_offsets_insert_ (child, start, delta);
 
1391
 
 
1392
        for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
 
1393
        {
 
1394
                sp->start_at += delta;
 
1395
                sp->end_at += delta;
 
1396
        }
 
1397
}
 
1398
 
 
1399
/**
 
1400
 * find_insertion_place_forward_:
 
1401
 *
 
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.
 
1409
 *
 
1410
 * Auxiliary function used in find_insertion_place().
 
1411
 */
 
1412
static void
 
1413
find_insertion_place_forward_ (Segment  *segment,
 
1414
                               gint      offset,
 
1415
                               Segment  *start,
 
1416
                               Segment **parent,
 
1417
                               Segment **prev,
 
1418
                               Segment **next)
 
1419
{
 
1420
        Segment *child;
 
1421
 
 
1422
        g_assert (start->end_at < offset);
 
1423
 
 
1424
        for (child = start; child != NULL; child = child->next)
 
1425
        {
 
1426
                if (child->start_at <= offset && child->end_at >= offset)
 
1427
                {
 
1428
                        find_insertion_place (child, offset, parent, prev, next, NULL);
 
1429
                        return;
 
1430
                }
 
1431
 
 
1432
                if (child->end_at == offset)
 
1433
                {
 
1434
                        if (SEGMENT_IS_INVALID (child))
 
1435
                        {
 
1436
                                *parent = child;
 
1437
                                *prev = NULL;
 
1438
                                *next = NULL;
 
1439
                        }
 
1440
                        else
 
1441
                        {
 
1442
                                *prev = child;
 
1443
                                *next = child->next;
 
1444
                                *parent = segment;
 
1445
                        }
 
1446
 
 
1447
                        return;
 
1448
                }
 
1449
 
 
1450
                if (child->end_at < offset)
 
1451
                {
 
1452
                        *prev = child;
 
1453
                        continue;
 
1454
                }
 
1455
 
 
1456
                if (child->start_at > offset)
 
1457
                {
 
1458
                        *next = child;
 
1459
                        break;
 
1460
                }
 
1461
 
 
1462
                g_assert_not_reached ();
 
1463
        }
 
1464
 
 
1465
        *parent = segment;
 
1466
}
 
1467
 
 
1468
/**
 
1469
 * find_insertion_place_backward_:
 
1470
 *
 
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.
 
1478
 *
 
1479
 * Auxiliary function used in find_insertion_place().
 
1480
 */
 
1481
static void
 
1482
find_insertion_place_backward_ (Segment  *segment,
 
1483
                                gint      offset,
 
1484
                                Segment  *start,
 
1485
                                Segment **parent,
 
1486
                                Segment **prev,
 
1487
                                Segment **next)
 
1488
{
 
1489
        Segment *child;
 
1490
 
 
1491
        g_assert (start->end_at >= offset);
 
1492
 
 
1493
        for (child = start; child != NULL; child = child->prev)
 
1494
        {
 
1495
                if (child->start_at <= offset && child->end_at >= offset)
 
1496
                {
 
1497
                        find_insertion_place (child, offset, parent, prev, next, NULL);
 
1498
                        return;
 
1499
                }
 
1500
 
 
1501
                if (child->end_at == offset)
 
1502
                {
 
1503
                        if (SEGMENT_IS_INVALID (child))
 
1504
                        {
 
1505
                                *parent = child;
 
1506
                                *prev = NULL;
 
1507
                                *next = NULL;
 
1508
                        }
 
1509
                        else
 
1510
                        {
 
1511
                                *prev = child;
 
1512
                                *next = child->next;
 
1513
                                *parent = segment;
 
1514
                        }
 
1515
 
 
1516
                        return;
 
1517
                }
 
1518
 
 
1519
                if (child->end_at < offset)
 
1520
                {
 
1521
                        *prev = child;
 
1522
                        *next = child->next;
 
1523
                        break;
 
1524
                }
 
1525
 
 
1526
                if (child->start_at > offset)
 
1527
                {
 
1528
                        *next = child;
 
1529
                        continue;
 
1530
                }
 
1531
 
 
1532
                g_assert_not_reached ();
 
1533
        }
 
1534
 
 
1535
        *parent = segment;
 
1536
}
 
1537
 
 
1538
/**
 
1539
 * find_insertion_place:
 
1540
 *
 
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.
 
1548
 *
 
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).
 
1555
 */
 
1556
static void
 
1557
find_insertion_place (Segment  *segment,
 
1558
                      gint      offset,
 
1559
                      Segment **parent,
 
1560
                      Segment **prev,
 
1561
                      Segment **next,
 
1562
                      Segment  *hint)
 
1563
{
 
1564
        g_assert (segment->start_at <= offset && segment->end_at >= offset);
 
1565
 
 
1566
        *prev = NULL;
 
1567
        *next = NULL;
 
1568
 
 
1569
        if (SEGMENT_IS_INVALID (segment) || segment->children == NULL)
 
1570
        {
 
1571
                *parent = segment;
 
1572
                return;
 
1573
        }
 
1574
 
 
1575
        if (segment->start_at == offset)
 
1576
        {
 
1577
#ifdef ENABLE_CHECK_TREE
 
1578
                g_assert (!segment->children ||
 
1579
                          !SEGMENT_IS_INVALID (segment->children) ||
 
1580
                          segment->children->start_at > offset);
 
1581
#endif
 
1582
 
 
1583
                *parent = segment;
 
1584
                *next = segment->children;
 
1585
 
 
1586
                return;
 
1587
        }
 
1588
 
 
1589
        if (hint != NULL)
 
1590
                while (hint != NULL && hint->parent != segment)
 
1591
                        hint = hint->parent;
 
1592
 
 
1593
        if (hint == NULL)
 
1594
                hint = segment->children;
 
1595
 
 
1596
        if (hint->end_at < offset)
 
1597
                find_insertion_place_forward_ (segment, offset, hint, parent, prev, next);
 
1598
        else
 
1599
                find_insertion_place_backward_ (segment, offset, hint, parent, prev, next);
 
1600
}
 
1601
 
 
1602
/**
 
1603
 * get_invalid_at:
 
1604
 *
 
1605
 * @ce: the engine.
 
1606
 * @offset: the offset.
 
1607
 *
 
1608
 * Finds invalid segment adjacent to offset (i.e. such that start <= offset <= end),
 
1609
 * if any.
 
1610
 *
 
1611
 * Returns: invalid segment or %NULL.
 
1612
 */
 
1613
static Segment *
 
1614
get_invalid_at (GtkSourceContextEngine *ce,
 
1615
                gint                    offset)
 
1616
{
 
1617
        GSList *link = ce->priv->invalid;
 
1618
 
 
1619
        while (link != NULL)
 
1620
        {
 
1621
                Segment *segment = link->data;
 
1622
 
 
1623
                link = link->next;
 
1624
 
 
1625
                if (segment->start_at > offset)
 
1626
                        break;
 
1627
 
 
1628
                if (segment->end_at < offset)
 
1629
                        continue;
 
1630
 
 
1631
                return segment;
 
1632
        }
 
1633
 
 
1634
        return NULL;
 
1635
}
 
1636
 
 
1637
/**
 
1638
 * segment_add_subpattern:
 
1639
 *
 
1640
 * @state: the segment.
 
1641
 * @sp: subpattern.
 
1642
 *
 
1643
 * Prepends subpattern to subpatterns list in the segment.
 
1644
 */
 
1645
static void
 
1646
segment_add_subpattern (Segment    *state,
 
1647
                        SubPattern *sp)
 
1648
{
 
1649
        sp->next = state->sub_patterns;
 
1650
        state->sub_patterns = sp;
 
1651
}
 
1652
 
 
1653
/**
 
1654
 * sub_pattern_new:
 
1655
 *
 
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.
 
1660
 *
 
1661
 * Creates new subpattern and adds it to the segment's
 
1662
 * subpatterns list.
 
1663
 *
 
1664
 * Returns: new subpattern.
 
1665
 */
 
1666
static SubPattern *
 
1667
sub_pattern_new (Segment              *segment,
 
1668
                 gint                  start_at,
 
1669
                 gint                  end_at,
 
1670
                 SubPatternDefinition *sp_def)
 
1671
{
 
1672
        SubPattern *sp;
 
1673
 
 
1674
        sp = g_slice_new0 (SubPattern);
 
1675
        sp->start_at = start_at;
 
1676
        sp->end_at = end_at;
 
1677
        sp->definition = sp_def;
 
1678
 
 
1679
        segment_add_subpattern (segment, sp);
 
1680
 
 
1681
        return sp;
 
1682
}
 
1683
 
 
1684
/**
 
1685
 * sub_pattern_free:
 
1686
 *
 
1687
 * @sp: subppatern.
 
1688
 *
 
1689
 * Calls g_free on subpattern, was useful for debugging.
 
1690
 */
 
1691
static inline void
 
1692
sub_pattern_free (SubPattern *sp)
 
1693
{
 
1694
#ifdef ENABLE_DEBUG
 
1695
        memset (sp, 1, sizeof (SubPattern));
 
1696
#else
 
1697
        g_slice_free (SubPattern, sp);
 
1698
#endif
 
1699
}
 
1700
 
 
1701
/**
 
1702
 * segment_make_invalid_:
 
1703
 *
 
1704
 * @ce: the engine.
 
1705
 * @segment: segment to invalidate.
 
1706
 *
 
1707
 * Invalidates segment. Called only from insert_range().
 
1708
 */
 
1709
static void
 
1710
segment_make_invalid_ (GtkSourceContextEngine *ce,
 
1711
                       Segment                *segment)
 
1712
{
 
1713
        Context *ctx;
 
1714
        SubPattern *sp;
 
1715
 
 
1716
        g_assert (!SEGMENT_IS_INVALID (segment));
 
1717
 
 
1718
        sp = segment->sub_patterns;
 
1719
        segment->sub_patterns = NULL;
 
1720
 
 
1721
        while (sp != NULL)
 
1722
        {
 
1723
                SubPattern *next = sp->next;
 
1724
                sub_pattern_free (sp);
 
1725
                sp = next;
 
1726
        }
 
1727
 
 
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);
 
1735
}
 
1736
 
 
1737
/**
 
1738
 * simple_segment_split_:
 
1739
 *
 
1740
 * @ce: the engine.
 
1741
 * @segment: segment to split.
 
1742
 * @offset: offset at which text insertion occurred.
 
1743
 *
 
1744
 * Creates a new invalid segment and inserts it in the middle
 
1745
 * of the given one. Called from insert_range() to mark inserted
 
1746
 * text.
 
1747
 *
 
1748
 * Returns: new invalid segment.
 
1749
 */
 
1750
static Segment *
 
1751
simple_segment_split_ (GtkSourceContextEngine *ce,
 
1752
                       Segment                *segment,
 
1753
                       gint                    offset)
 
1754
{
 
1755
        SubPattern *sp;
 
1756
        Segment *new_segment, *invalid;
 
1757
        gint end_at = segment->end_at;
 
1758
 
 
1759
        g_assert (SEGMENT_IS_SIMPLE (segment));
 
1760
        g_assert (segment->start_at < offset && offset < segment->end_at);
 
1761
 
 
1762
        sp = segment->sub_patterns;
 
1763
        segment->sub_patterns = NULL;
 
1764
        segment->end_at = offset;
 
1765
 
 
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);
 
1768
 
 
1769
        while (sp != NULL)
 
1770
        {
 
1771
                Segment *append_to = NULL;
 
1772
                SubPattern *next = sp->next;
 
1773
 
 
1774
                if (sp->end_at <= offset)
 
1775
                {
 
1776
                        append_to = segment;
 
1777
                }
 
1778
                else if (sp->start_at >= offset)
 
1779
                {
 
1780
                        append_to = new_segment;
 
1781
                }
 
1782
                else
 
1783
                {
 
1784
                        sub_pattern_new (new_segment,
 
1785
                                         offset,
 
1786
                                         sp->end_at,
 
1787
                                         sp->definition);
 
1788
                        sp->end_at = offset;
 
1789
                        append_to = segment;
 
1790
                }
 
1791
 
 
1792
                segment_add_subpattern (append_to, sp);
 
1793
 
 
1794
                sp = next;
 
1795
        }
 
1796
 
 
1797
        return invalid;
 
1798
}
 
1799
 
 
1800
/**
 
1801
 * invalidate_region:
 
1802
 *
 
1803
 * @ce: a #GtkSourceContextEngine.
 
1804
 * @offset: the start of invalidated area.
 
1805
 * @length: the length of the area.
 
1806
 *
 
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.
 
1811
 */
 
1812
static void
 
1813
invalidate_region (GtkSourceContextEngine *ce,
 
1814
                   gint                    offset,
 
1815
                   gint                    length)
 
1816
{
 
1817
        InvalidRegion *region = &ce->priv->invalid_region;
 
1818
        GtkTextBuffer *buffer = ce->priv->buffer;
 
1819
        GtkTextIter iter;
 
1820
        gint end_offset;
 
1821
 
 
1822
        end_offset = length >= 0 ? offset + length : offset;
 
1823
 
 
1824
        if (region->empty)
 
1825
        {
 
1826
                region->empty = FALSE;
 
1827
                region->delta = length;
 
1828
 
 
1829
                gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
 
1830
                gtk_text_buffer_move_mark (buffer, region->start, &iter);
 
1831
 
 
1832
                gtk_text_iter_set_offset (&iter, end_offset);
 
1833
                gtk_text_buffer_move_mark (buffer, region->end, &iter);
 
1834
        }
 
1835
        else
 
1836
        {
 
1837
                gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->start);
 
1838
 
 
1839
                if (gtk_text_iter_get_offset (&iter) > offset)
 
1840
                {
 
1841
                        gtk_text_iter_set_offset (&iter, offset);
 
1842
                        gtk_text_buffer_move_mark (buffer, region->start, &iter);
 
1843
                }
 
1844
 
 
1845
                gtk_text_buffer_get_iter_at_mark (buffer, &iter, region->end);
 
1846
 
 
1847
                if (gtk_text_iter_get_offset (&iter) < end_offset)
 
1848
                {
 
1849
                        gtk_text_iter_set_offset (&iter, end_offset);
 
1850
                        gtk_text_buffer_move_mark (buffer, region->end, &iter);
 
1851
                }
 
1852
 
 
1853
                region->delta += length;
 
1854
        }
 
1855
 
 
1856
        DEBUG (({
 
1857
                gint start, end;
 
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);
 
1863
        }));
 
1864
 
 
1865
        CHECK_TREE (ce);
 
1866
 
 
1867
        install_first_update (ce);
 
1868
}
 
1869
 
 
1870
/**
 
1871
 * insert_range:
 
1872
 *
 
1873
 * @ce: a #GtkSourceContextEngine.
 
1874
 * @offset: the start of new segment.
 
1875
 * @length: the length of the segment.
 
1876
 *
 
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).
 
1884
 */
 
1885
static void
 
1886
insert_range (GtkSourceContextEngine *ce,
 
1887
              gint                    offset,
 
1888
              gint                    length)
 
1889
{
 
1890
        Segment *parent, *prev = NULL, *next = NULL, *new_segment;
 
1891
        Segment *segment;
 
1892
 
 
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. */
 
1896
 
 
1897
        parent = get_invalid_at (ce, offset);
 
1898
 
 
1899
        if (parent == NULL)
 
1900
                find_insertion_place (ce->priv->root_segment, offset,
 
1901
                                      &parent, &prev, &next,
 
1902
                                      ce->priv->hint);
 
1903
 
 
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);
 
1910
 
 
1911
        if (SEGMENT_IS_INVALID (parent))
 
1912
        {
 
1913
                /* If length is zero, and we already have an invalid segment there,
 
1914
                 * do nothing. */
 
1915
                if (length == 0)
 
1916
                        return;
 
1917
 
 
1918
                segment = parent;
 
1919
        }
 
1920
        else if (SEGMENT_IS_SIMPLE (parent))
 
1921
        {
 
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)
 
1927
                {
 
1928
                        segment = simple_segment_split_ (ce, parent, offset);
 
1929
                }
 
1930
                else
 
1931
                {
 
1932
                        segment_make_invalid_ (ce, parent);
 
1933
                        segment = parent;
 
1934
                }
 
1935
        }
 
1936
        else
 
1937
        {
 
1938
                /* Just insert new zero-length invalid segment. */
 
1939
 
 
1940
                new_segment = segment_new (ce, parent, NULL, offset, offset, FALSE);
 
1941
 
 
1942
                new_segment->next = next;
 
1943
                new_segment->prev = prev;
 
1944
 
 
1945
                if (next != NULL)
 
1946
                        next->prev = new_segment;
 
1947
                else
 
1948
                        parent->last_child = new_segment;
 
1949
 
 
1950
                if (prev != NULL)
 
1951
                        prev->next = new_segment;
 
1952
                else
 
1953
                        parent->children = new_segment;
 
1954
 
 
1955
                segment = new_segment;
 
1956
        }
 
1957
 
 
1958
        g_assert (!segment->children);
 
1959
 
 
1960
        if (length != 0)
 
1961
        {
 
1962
                /* now fix offsets in all the segments "to the right"
 
1963
                 * of segment. */
 
1964
                while (segment != NULL)
 
1965
                {
 
1966
                        Segment *tmp;
 
1967
                        SubPattern *sp;
 
1968
 
 
1969
                        for (tmp = segment->next; tmp != NULL; tmp = tmp->next)
 
1970
                                fix_offsets_insert_ (tmp, offset, length);
 
1971
 
 
1972
                        segment->end_at += length;
 
1973
 
 
1974
                        for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
 
1975
                        {
 
1976
                                if (sp->start_at > offset)
 
1977
                                        sp->start_at += length;
 
1978
                                if (sp->end_at > offset)
 
1979
                                        sp->end_at += length;
 
1980
                        }
 
1981
 
 
1982
                        segment = segment->parent;
 
1983
                }
 
1984
        }
 
1985
 
 
1986
        CHECK_TREE (ce);
 
1987
}
 
1988
 
 
1989
/**
 
1990
 * gtk_source_context_engine_text_inserted:
 
1991
 *
 
1992
 * @ce: a #GtkSourceContextEngine.
 
1993
 * @start_offset: the start of inserted text.
 
1994
 * @end_offset: the end of inserted text.
 
1995
 *
 
1996
 * Called from GtkTextBuffer::insert_text.
 
1997
 */
 
1998
static void
 
1999
gtk_source_context_engine_text_inserted (GtkSourceEngine *engine,
 
2000
                                         gint             start_offset,
 
2001
                                         gint             end_offset)
 
2002
{
 
2003
        GtkTextIter iter;
 
2004
        GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
 
2005
 
 
2006
        /* Happens when highlighting is disabled */
 
2007
        if (ce->priv->buffer == NULL)
 
2008
        {
 
2009
                return;
 
2010
        }
 
2011
 
 
2012
        g_return_if_fail (start_offset < end_offset);
 
2013
 
 
2014
        invalidate_region (ce, start_offset, end_offset - start_offset);
 
2015
 
 
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))
 
2023
        {
 
2024
                gtk_text_iter_forward_to_line_end (&iter);
 
2025
                invalidate_region (ce, gtk_text_iter_get_offset (&iter), 0);
 
2026
        }
 
2027
}
 
2028
 
 
2029
/**
 
2030
 * fix_offset_delete_one_:
 
2031
 *
 
2032
 * @offset: segment.
 
2033
 * @start: start of deleted text.
 
2034
 * @length: length of deleted text.
 
2035
 *
 
2036
 * Returns: new offset depending on location of @offset
 
2037
 * relative to deleted text.
 
2038
 * Called only from fix_offsets_delete_().
 
2039
 */
 
2040
static inline gint
 
2041
fix_offset_delete_one_ (gint offset,
 
2042
                        gint start,
 
2043
                        gint length)
 
2044
{
 
2045
        if (offset > start)
 
2046
        {
 
2047
                if (offset >= start + length)
 
2048
                        offset -= length;
 
2049
                else
 
2050
                        offset = start;
 
2051
        }
 
2052
 
 
2053
        return offset;
 
2054
}
 
2055
 
 
2056
/**
 
2057
 * fix_offsets_delete_:
 
2058
 *
 
2059
 * @segment: segment.
 
2060
 * @start: start offset.
 
2061
 * @length: length of deleted text.
 
2062
 * @hint: some segment somewhere near deleted text to optimize search.
 
2063
 *
 
2064
 * Recursively updates offsets after deleting text. To be called
 
2065
 * only from delete_range_().
 
2066
 */
 
2067
static void
 
2068
fix_offsets_delete_ (Segment *segment,
 
2069
                     gint     offset,
 
2070
                     gint     length,
 
2071
                     Segment *hint)
 
2072
{
 
2073
        Segment *child;
 
2074
        SubPattern *sp;
 
2075
 
 
2076
        g_return_if_fail (segment->end_at > offset);
 
2077
 
 
2078
        if (hint != NULL)
 
2079
                while (hint != NULL && hint->parent != segment)
 
2080
                        hint = hint->parent;
 
2081
 
 
2082
        if (hint == NULL)
 
2083
                hint = segment->children;
 
2084
 
 
2085
        for (child = hint; child != NULL; child = child->next)
 
2086
        {
 
2087
                if (child->end_at <= offset)
 
2088
                        continue;
 
2089
                fix_offsets_delete_ (child, offset, length, NULL);
 
2090
        }
 
2091
 
 
2092
        for (child = hint ? hint->prev : NULL; child != NULL; child = child->prev)
 
2093
        {
 
2094
                if (child->end_at <= offset)
 
2095
                        break;
 
2096
                fix_offsets_delete_ (child, offset, length, NULL);
 
2097
        }
 
2098
 
 
2099
        for (sp = segment->sub_patterns; sp != NULL; sp = sp->next)
 
2100
        {
 
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);
 
2103
        }
 
2104
 
 
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);
 
2107
}
 
2108
 
 
2109
/**
 
2110
 * delete_range_:
 
2111
 *
 
2112
 * @ce: a #GtkSourceContextEngine.
 
2113
 * @start: the start of deleted area.
 
2114
 * @end: the end of deleted area.
 
2115
 *
 
2116
 * Updates segment tree after deletion: removes segments at deleted
 
2117
 * interval, updates tree offsets, etc.
 
2118
 * It's called only from update_tree().
 
2119
 */
 
2120
static void
 
2121
delete_range_ (GtkSourceContextEngine *ce,
 
2122
               gint                    start,
 
2123
               gint                    end)
 
2124
{
 
2125
        g_return_if_fail (start < end);
 
2126
 
 
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);
 
2130
 
 
2131
        /* no need to invalidate at start, update_tree will do it */
 
2132
 
 
2133
        CHECK_TREE (ce);
 
2134
}
 
2135
 
 
2136
/**
 
2137
 * gtk_source_context_engine_text_deleted:
 
2138
 *
 
2139
 * @ce: a #GtkSourceContextEngine.
 
2140
 * @offset: the start of deleted text.
 
2141
 * @length: the length (in characters) of deleted text.
 
2142
 *
 
2143
 * Called from GtkTextBuffer::delete_range.
 
2144
 */
 
2145
static void
 
2146
gtk_source_context_engine_text_deleted (GtkSourceEngine *engine,
 
2147
                                        gint             offset,
 
2148
                                        gint             length)
 
2149
{
 
2150
        g_return_if_fail (length > 0);
 
2151
        invalidate_region (GTK_SOURCE_CONTEXT_ENGINE (engine),
 
2152
                           offset,
 
2153
                           - length);
 
2154
}
 
2155
 
 
2156
/**
 
2157
 * get_invalid_segment:
 
2158
 *
 
2159
 * @ce: a #GtkSourceContextEngine.
 
2160
 *
 
2161
 * Returns: first invalid segment, or %NULL.
 
2162
 */
 
2163
static Segment *
 
2164
get_invalid_segment (GtkSourceContextEngine *ce)
 
2165
{
 
2166
        g_return_val_if_fail (ce->priv->invalid_region.empty, NULL);
 
2167
        return ce->priv->invalid ? ce->priv->invalid->data : NULL;
 
2168
}
 
2169
 
 
2170
/**
 
2171
 * get_invalid_line:
 
2172
 *
 
2173
 * @ce: a #GtkSourceContextEngine.
 
2174
 *
 
2175
 * Returns: first invalid line, or -1.
 
2176
 */
 
2177
static gint
 
2178
get_invalid_line (GtkSourceContextEngine *ce)
 
2179
{
 
2180
        GtkTextIter iter;
 
2181
        gint offset = G_MAXINT;
 
2182
 
 
2183
        if (!ce->priv->invalid_region.empty)
 
2184
        {
 
2185
                gint tmp;
 
2186
                gtk_text_buffer_get_iter_at_mark (ce->priv->buffer,
 
2187
                                                  &iter,
 
2188
                                                  ce->priv->invalid_region.start);
 
2189
                tmp = gtk_text_iter_get_offset (&iter);
 
2190
                offset = MIN (offset, tmp);
 
2191
        }
 
2192
 
 
2193
        if (ce->priv->invalid)
 
2194
        {
 
2195
                Segment *segment = ce->priv->invalid->data;
 
2196
                offset = MIN (offset, segment->start_at);
 
2197
        }
 
2198
 
 
2199
        if (offset == G_MAXINT)
 
2200
                return -1;
 
2201
 
 
2202
        gtk_text_buffer_get_iter_at_offset (ce->priv->buffer, &iter, offset);
 
2203
        return gtk_text_iter_get_line (&iter);
 
2204
}
 
2205
 
 
2206
/**
 
2207
 * update_tree:
 
2208
 *
 
2209
 * @ce: a #GtkSourceContextEngine.
 
2210
 *
 
2211
 * Modifies syntax tree according to data in invalid_region.
 
2212
 */
 
2213
static void
 
2214
update_tree (GtkSourceContextEngine *ce)
 
2215
{
 
2216
        InvalidRegion *region = &ce->priv->invalid_region;
 
2217
        gint start, end, delta;
 
2218
        gint erase_start, erase_end;
 
2219
        GtkTextIter iter;
 
2220
 
 
2221
        if (region->empty)
 
2222
                return;
 
2223
 
 
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);
 
2228
 
 
2229
        delta = region->delta;
 
2230
 
 
2231
        g_assert (start <= MIN (end, end - delta));
 
2232
 
 
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. */
 
2238
 
 
2239
        if (delta > 0)
 
2240
                insert_range (ce, start, delta);
 
2241
        else if (delta < 0)
 
2242
                delete_range_ (ce, end, end - delta);
 
2243
 
 
2244
        if (delta <= 0)
 
2245
        {
 
2246
                erase_start = start;
 
2247
                erase_end = end;
 
2248
        }
 
2249
        else
 
2250
        {
 
2251
                erase_start = start + delta;
 
2252
                erase_end = end;
 
2253
        }
 
2254
 
 
2255
        if (erase_start < erase_end)
 
2256
        {
 
2257
                erase_segments (ce, erase_start, erase_end, NULL);
 
2258
                create_segment (ce, ce->priv->root_segment, NULL, erase_start, erase_end, FALSE, NULL);
 
2259
        }
 
2260
        else if (get_invalid_at (ce, start) == NULL)
 
2261
        {
 
2262
                insert_range (ce, start, 0);
 
2263
        }
 
2264
 
 
2265
        region->empty = TRUE;
 
2266
 
 
2267
#ifdef ENABLE_CHECK_TREE
 
2268
        g_assert (get_invalid_at (ce, start) != NULL);
 
2269
        CHECK_TREE (ce);
 
2270
#endif
 
2271
}
 
2272
 
 
2273
/**
 
2274
 * gtk_source_context_engine_update_highlight:
 
2275
 *
 
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.
 
2281
 *
 
2282
 * GtkSourceEngine::update_highlight method.
 
2283
 *
 
2284
 * Makes sure the area is analyzed and highlighted. If @asynchronous
 
2285
 * is %FALSE, then it queues idle worker.
 
2286
 */
 
2287
static void
 
2288
gtk_source_context_engine_update_highlight (GtkSourceEngine   *engine,
 
2289
                                            const GtkTextIter *start,
 
2290
                                            const GtkTextIter *end,
 
2291
                                            gboolean           synchronous)
 
2292
{
 
2293
        gint invalid_line;
 
2294
        gint end_line;
 
2295
        GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
 
2296
 
 
2297
        if (!ce->priv->highlight || ce->priv->disabled)
 
2298
                return;
 
2299
 
 
2300
        invalid_line = get_invalid_line (ce);
 
2301
        end_line = gtk_text_iter_get_line (end);
 
2302
 
 
2303
        if (gtk_text_iter_starts_line (end) && end_line > 0)
 
2304
                end_line -= 1;
 
2305
 
 
2306
        if (invalid_line < 0 || invalid_line > end_line)
 
2307
        {
 
2308
                ensure_highlighted (ce, start, end);
 
2309
        }
 
2310
        else if (synchronous)
 
2311
        {
 
2312
                /* analyze whole region */
 
2313
                update_syntax (ce, end, 0);
 
2314
                ensure_highlighted (ce, start, end);
 
2315
        }
 
2316
        else
 
2317
        {
 
2318
                if (gtk_text_iter_get_line (start) >= invalid_line)
 
2319
                {
 
2320
                        gtk_text_region_add (ce->priv->highlight_requests, start, end);
 
2321
                }
 
2322
                else
 
2323
                {
 
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);
 
2328
                }
 
2329
 
 
2330
                install_first_update (ce);
 
2331
        }
 
2332
}
 
2333
 
 
2334
/**
 
2335
 * enable_highlight:
 
2336
 *
 
2337
 * @ce: a #GtkSourceContextEngine.
 
2338
 * @enable: whether to enable highlighting.
 
2339
 *
 
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.
 
2343
 */
 
2344
static void
 
2345
enable_highlight (GtkSourceContextEngine *ce,
 
2346
                  gboolean                enable)
 
2347
{
 
2348
        GtkTextIter start, end;
 
2349
 
 
2350
        if (!enable == !ce->priv->highlight)
 
2351
                return;
 
2352
 
 
2353
        ce->priv->highlight = enable != 0;
 
2354
        gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (ce->priv->buffer),
 
2355
                                    &start, &end);
 
2356
 
 
2357
        if (enable)
 
2358
                refresh_range (ce, &start, &end, TRUE);
 
2359
        else
 
2360
                unhighlight_region (ce, &start, &end);
 
2361
}
 
2362
 
 
2363
static void
 
2364
buffer_notify_highlight_syntax_cb (GtkSourceContextEngine *ce)
 
2365
{
 
2366
        gboolean highlight;
 
2367
        g_object_get (ce->priv->buffer, "highlight-syntax", &highlight, NULL);
 
2368
        enable_highlight (ce, highlight);
 
2369
}
 
2370
 
 
2371
 
 
2372
/* IDLE WORKER CODE ------------------------------------------------------- */
 
2373
 
 
2374
/**
 
2375
 * all_analyzed:
 
2376
 *
 
2377
 * @ce: a #GtkSourceContextEngine.
 
2378
 *
 
2379
 * Returns: whether everything is analyzed (but it doesn't care about the tags).
 
2380
 */
 
2381
static gboolean
 
2382
all_analyzed (GtkSourceContextEngine *ce)
 
2383
{
 
2384
        return ce->priv->invalid == NULL && ce->priv->invalid_region.empty;
 
2385
}
 
2386
 
 
2387
/**
 
2388
 * idle_worker:
 
2389
 *
 
2390
 * @ce: #GtkSourceContextEngine.
 
2391
 *
 
2392
 * Analyzes a batch in idle. Stops when
 
2393
 * whole buffer is analyzed.
 
2394
 */
 
2395
static gboolean
 
2396
idle_worker (GtkSourceContextEngine *ce)
 
2397
{
 
2398
        gboolean retval = TRUE;
 
2399
 
 
2400
        g_return_val_if_fail (ce->priv->buffer != NULL, FALSE);
 
2401
 
 
2402
        gdk_threads_enter ();
 
2403
 
 
2404
        /* analyze batch of text */
 
2405
        update_syntax (ce, NULL, INCREMENTAL_UPDATE_TIME_SLICE);
 
2406
        CHECK_TREE (ce);
 
2407
 
 
2408
        if (all_analyzed (ce))
 
2409
        {
 
2410
                ce->priv->incremental_update = 0;
 
2411
                retval = FALSE;
 
2412
        }
 
2413
 
 
2414
        gdk_threads_leave ();
 
2415
 
 
2416
        return retval;
 
2417
}
 
2418
 
 
2419
/**
 
2420
 * first_update_callback:
 
2421
 *
 
2422
 * @ce: a #GtkSourceContextEngine.
 
2423
 *
 
2424
 * Same as idle_worker, except: it runs once, and install idle_worker
 
2425
 * if not everything was analyzed at once.
 
2426
 */
 
2427
static gboolean
 
2428
first_update_callback (GtkSourceContextEngine *ce)
 
2429
{
 
2430
        g_return_val_if_fail (ce->priv->buffer != NULL, FALSE);
 
2431
 
 
2432
        gdk_threads_enter ();
 
2433
 
 
2434
        /* analyze batch of text */
 
2435
        update_syntax (ce, NULL, FIRST_UPDATE_TIME_SLICE);
 
2436
        CHECK_TREE (ce);
 
2437
 
 
2438
        ce->priv->first_update = 0;
 
2439
 
 
2440
        if (!all_analyzed (ce))
 
2441
                install_idle_worker (ce);
 
2442
 
 
2443
        gdk_threads_leave ();
 
2444
 
 
2445
        return FALSE;
 
2446
}
 
2447
 
 
2448
/**
 
2449
 * install_idle_worker:
 
2450
 *
 
2451
 * @ce: #GtkSourceContextEngine.
 
2452
 *
 
2453
 * Schedules reanalyzing buffer in idle.
 
2454
 * Always safe to call.
 
2455
 */
 
2456
static void
 
2457
install_idle_worker (GtkSourceContextEngine *ce)
 
2458
{
 
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);
 
2463
}
 
2464
 
 
2465
/**
 
2466
 * install_first_update:
 
2467
 *
 
2468
 * @ce: #GtkSourceContextEngine.
 
2469
 *
 
2470
 * Schedules first_update_callback call.
 
2471
 * Always safe to call.
 
2472
 */
 
2473
static void
 
2474
install_first_update (GtkSourceContextEngine *ce)
 
2475
{
 
2476
        if (ce->priv->first_update == 0)
 
2477
        {
 
2478
                if (ce->priv->incremental_update != 0)
 
2479
                {
 
2480
                        g_source_remove (ce->priv->incremental_update);
 
2481
                        ce->priv->incremental_update = 0;
 
2482
                }
 
2483
 
 
2484
                ce->priv->first_update =
 
2485
                        g_idle_add_full (FIRST_UPDATE_PRIORITY,
 
2486
                                         (GSourceFunc) first_update_callback,
 
2487
                                         ce, NULL);
 
2488
        }
 
2489
}
 
2490
 
 
2491
/* GtkSourceContextEngine class ------------------------------------------- */
 
2492
 
 
2493
G_DEFINE_TYPE (GtkSourceContextEngine, _gtk_source_context_engine, GTK_TYPE_SOURCE_ENGINE)
 
2494
 
 
2495
static GQuark
 
2496
gtk_source_context_engine_error_quark (void)
 
2497
{
 
2498
        static GQuark err_q = 0;
 
2499
        if (err_q == 0)
 
2500
                err_q = g_quark_from_static_string ("gtk-source-context-engine-error-quark");
 
2501
        return err_q;
 
2502
}
 
2503
 
 
2504
static void
 
2505
remove_tags_hash_cb (G_GNUC_UNUSED gpointer style,
 
2506
                     GSList          *tags,
 
2507
                     GtkTextTagTable *table)
 
2508
{
 
2509
        GSList *l = tags;
 
2510
 
 
2511
        while (l != NULL)
 
2512
        {
 
2513
                gtk_text_tag_table_remove (table, l->data);
 
2514
                g_object_unref (l->data);
 
2515
                l = l->next;
 
2516
        }
 
2517
 
 
2518
        g_slist_free (tags);
 
2519
}
 
2520
 
 
2521
static void
 
2522
remove_context_classes_hash_cb (G_GNUC_UNUSED gpointer class,
 
2523
                                GtkTextTag             *tag,
 
2524
                                GtkTextTagTable        *table)
 
2525
{
 
2526
        gtk_text_tag_table_remove (table, tag);
 
2527
}
 
2528
 
 
2529
/**
 
2530
 * destroy_tags_hash:
 
2531
 *
 
2532
 * @ce: #GtkSourceContextEngine.
 
2533
 *
 
2534
 * Destroys syntax tags cache.
 
2535
 */
 
2536
static void
 
2537
destroy_tags_hash (GtkSourceContextEngine *ce)
 
2538
{
 
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;
 
2543
}
 
2544
 
 
2545
static void
 
2546
destroy_context_classes_hash (GtkSourceContextEngine *ce)
 
2547
{
 
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;
 
2552
}
 
2553
 
 
2554
/**
 
2555
 * gtk_source_context_engine_attach_buffer:
 
2556
 *
 
2557
 * @ce: #GtkSourceContextEngine.
 
2558
 * @buffer: buffer.
 
2559
 *
 
2560
 * Detaches engine from previous buffer, and attaches to @buffer if
 
2561
 * it's not %NULL.
 
2562
 */
 
2563
static void
 
2564
gtk_source_context_engine_attach_buffer (GtkSourceEngine *engine,
 
2565
                                         GtkTextBuffer   *buffer)
 
2566
{
 
2567
        GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
 
2568
 
 
2569
        g_return_if_fail (!buffer || GTK_IS_TEXT_BUFFER (buffer));
 
2570
 
 
2571
        if (ce->priv->buffer == buffer)
 
2572
                return;
 
2573
 
 
2574
        /* Detach previous buffer if there is one. */
 
2575
        if (ce->priv->buffer != NULL)
 
2576
        {
 
2577
                g_signal_handlers_disconnect_by_func (ce->priv->buffer,
 
2578
                                                      (gpointer) buffer_notify_highlight_syntax_cb,
 
2579
                                                      ce);
 
2580
 
 
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;
 
2587
 
 
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;
 
2597
 
 
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;
 
2606
 
 
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;
 
2615
 
 
2616
                destroy_context_classes_hash (ce);
 
2617
 
 
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;
 
2624
        }
 
2625
 
 
2626
        ce->priv->buffer = buffer;
 
2627
 
 
2628
        if (buffer != NULL)
 
2629
        {
 
2630
                gchar *root_id;
 
2631
                ContextDefinition *main_definition;
 
2632
                GtkTextIter start, end;
 
2633
 
 
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);
 
2637
                g_free (root_id);
 
2638
 
 
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);
 
2642
 
 
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);
 
2645
 
 
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);
 
2648
 
 
2649
                gtk_text_buffer_get_bounds (buffer, &start, &end);
 
2650
                ce->priv->invalid_region.start = gtk_text_buffer_create_mark (buffer, NULL,
 
2651
                                                                              &start, TRUE);
 
2652
                ce->priv->invalid_region.end = gtk_text_buffer_create_mark (buffer, NULL,
 
2653
                                                                            &end, FALSE);
 
2654
 
 
2655
                if (gtk_text_buffer_get_char_count (buffer) != 0)
 
2656
                {
 
2657
                        ce->priv->invalid_region.empty = FALSE;
 
2658
                        ce->priv->invalid_region.delta = gtk_text_buffer_get_char_count (buffer);
 
2659
                }
 
2660
                else
 
2661
                {
 
2662
                        ce->priv->invalid_region.empty = TRUE;
 
2663
                        ce->priv->invalid_region.delta = 0;
 
2664
                }
 
2665
 
 
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);
 
2669
 
 
2670
                g_signal_connect_swapped (buffer,
 
2671
                                          "notify::highlight-syntax",
 
2672
                                          G_CALLBACK (buffer_notify_highlight_syntax_cb),
 
2673
                                          ce);
 
2674
 
 
2675
                install_first_update (ce);
 
2676
        }
 
2677
}
 
2678
 
 
2679
/**
 
2680
 * disable_highlighting:
 
2681
 *
 
2682
 * @ce: #GtkSourceContextEngine.
 
2683
 *
 
2684
 * Dsiables highlighting in case of errors (currently if highlighting
 
2685
 * a single line took too long, so that highlighting doesn't freeze
 
2686
 * text editor).
 
2687
 */
 
2688
static void
 
2689
disable_highlighting (GtkSourceContextEngine *ce)
 
2690
{
 
2691
        if (!ce->priv->disabled)
 
2692
        {
 
2693
                ce->priv->disabled = TRUE;
 
2694
                gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
 
2695
                /* FIXME maybe emit some signal here? */
 
2696
        }
 
2697
}
 
2698
 
 
2699
static void
 
2700
set_tag_style_hash_cb (const char             *style,
 
2701
                       GSList                 *tags,
 
2702
                       GtkSourceContextEngine *ce)
 
2703
{
 
2704
        while (tags != NULL)
 
2705
        {
 
2706
                set_tag_style (ce, tags->data, style);
 
2707
                tags = tags->next;
 
2708
        }
 
2709
}
 
2710
 
 
2711
/**
 
2712
 * gtk_source_context_engine_set_style_scheme:
 
2713
 *
 
2714
 * @engine: #GtkSourceContextEngine.
 
2715
 * @scheme: #GtkSourceStyleScheme to set.
 
2716
 *
 
2717
 * GtkSourceEngine::set_style_scheme method.
 
2718
 * Sets current style scheme, updates tag styles and everything.
 
2719
 */
 
2720
static void
 
2721
gtk_source_context_engine_set_style_scheme (GtkSourceEngine      *engine,
 
2722
                                            GtkSourceStyleScheme *scheme)
 
2723
{
 
2724
        GtkSourceContextEngine *ce;
 
2725
 
 
2726
        g_return_if_fail (GTK_IS_SOURCE_CONTEXT_ENGINE (engine));
 
2727
        g_return_if_fail (GTK_IS_SOURCE_STYLE_SCHEME (scheme) || scheme == NULL);
 
2728
 
 
2729
        ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
 
2730
 
 
2731
        if (scheme == ce->priv->style_scheme)
 
2732
                return;
 
2733
 
 
2734
        if (ce->priv->style_scheme != NULL)
 
2735
                g_object_unref (ce->priv->style_scheme);
 
2736
 
 
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);
 
2739
}
 
2740
 
 
2741
static void
 
2742
gtk_source_context_engine_finalize (GObject *object)
 
2743
{
 
2744
        GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (object);
 
2745
 
 
2746
        if (ce->priv->buffer != NULL)
 
2747
        {
 
2748
                g_critical ("finalizing engine with attached buffer");
 
2749
                /* Disconnect the buffer (if there is one), which destroys almost
 
2750
                 * everything. */
 
2751
                gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
 
2752
        }
 
2753
 
 
2754
#ifdef ENABLE_MEMORY_DEBUG
 
2755
        if (ce->priv->mem_usage_timeout)
 
2756
                g_source_remove (ce->priv->mem_usage_timeout);
 
2757
#endif
 
2758
 
 
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);
 
2764
 
 
2765
        _gtk_source_context_data_unref (ce->priv->ctx_data);
 
2766
 
 
2767
        if (ce->priv->style_scheme != NULL)
 
2768
                g_object_unref (ce->priv->style_scheme);
 
2769
 
 
2770
        G_OBJECT_CLASS (_gtk_source_context_engine_parent_class)->finalize (object);
 
2771
}
 
2772
 
 
2773
static GtkTextTag *
 
2774
gtk_source_context_engine_get_context_class_tag (GtkSourceEngine *engine,
 
2775
                                                 const gchar     *context_class)
 
2776
{
 
2777
        GHashTable *hash_table = GTK_SOURCE_CONTEXT_ENGINE (engine)->priv->context_classes;
 
2778
 
 
2779
        if (hash_table == NULL)
 
2780
        {
 
2781
                /* This happens when highlighting is disabled */
 
2782
                return NULL;
 
2783
        }
 
2784
 
 
2785
        return g_hash_table_lookup (hash_table,
 
2786
                                    context_class);
 
2787
}
 
2788
 
 
2789
static void
 
2790
_gtk_source_context_engine_class_init (GtkSourceContextEngineClass *klass)
 
2791
{
 
2792
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
2793
        GtkSourceEngineClass *engine_class = GTK_SOURCE_ENGINE_CLASS (klass);
 
2794
 
 
2795
        object_class->finalize = gtk_source_context_engine_finalize;
 
2796
 
 
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;
 
2803
 
 
2804
        g_type_class_add_private (object_class, sizeof (GtkSourceContextEnginePrivate));
 
2805
}
 
2806
 
 
2807
static void
 
2808
_gtk_source_context_engine_init (GtkSourceContextEngine *ce)
 
2809
{
 
2810
        ce->priv = G_TYPE_INSTANCE_GET_PRIVATE (ce, GTK_TYPE_SOURCE_CONTEXT_ENGINE,
 
2811
                                                GtkSourceContextEnginePrivate);
 
2812
}
 
2813
 
 
2814
GtkSourceContextEngine *
 
2815
_gtk_source_context_engine_new (GtkSourceContextData *ctx_data)
 
2816
{
 
2817
        GtkSourceContextEngine *ce;
 
2818
 
 
2819
        g_return_val_if_fail (ctx_data != NULL, NULL);
 
2820
        g_return_val_if_fail (ctx_data->lang != NULL, NULL);
 
2821
 
 
2822
        ce = g_object_new (GTK_TYPE_SOURCE_CONTEXT_ENGINE, NULL);
 
2823
        ce->priv->ctx_data = _gtk_source_context_data_ref (ctx_data);
 
2824
 
 
2825
#ifdef ENABLE_MEMORY_DEBUG
 
2826
        ce->priv->mem_usage_timeout =
 
2827
                g_timeout_add (5000, (GSourceFunc) mem_usage_timeout, ce);
 
2828
#endif
 
2829
 
 
2830
        return ce;
 
2831
}
 
2832
 
 
2833
/**
 
2834
 * _gtk_source_context_data_new:
 
2835
 *
 
2836
 * @lang: #GtkSourceLanguage.
 
2837
 *
 
2838
 * Creates new context definition set. It does not set lang->priv->ctx_data,
 
2839
 * that's lang business.
 
2840
 */
 
2841
GtkSourceContextData *
 
2842
_gtk_source_context_data_new (GtkSourceLanguage *lang)
 
2843
{
 
2844
        GtkSourceContextData *ctx_data;
 
2845
 
 
2846
        g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (lang), NULL);
 
2847
 
 
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);
 
2853
 
 
2854
        return ctx_data;
 
2855
}
 
2856
 
 
2857
GtkSourceContextData *
 
2858
_gtk_source_context_data_ref (GtkSourceContextData *ctx_data)
 
2859
{
 
2860
        g_return_val_if_fail (ctx_data != NULL, NULL);
 
2861
        ctx_data->ref_count++;
 
2862
        return ctx_data;
 
2863
}
 
2864
 
 
2865
/**
 
2866
 * _gtk_source_context_data_unref:
 
2867
 *
 
2868
 * @ctx_data: #GtkSourceContextData.
 
2869
 *
 
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
 
2872
 * is unset.
 
2873
 */
 
2874
void
 
2875
_gtk_source_context_data_unref (GtkSourceContextData *ctx_data)
 
2876
{
 
2877
        g_return_if_fail (ctx_data != NULL);
 
2878
 
 
2879
        if (--ctx_data->ref_count == 0)
 
2880
        {
 
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);
 
2886
        }
 
2887
}
 
2888
 
 
2889
/* REGEX HANDLING --------------------------------------------------------- */
 
2890
 
 
2891
static Regex *
 
2892
regex_ref (Regex *regex)
 
2893
{
 
2894
        if (regex != NULL)
 
2895
                regex->ref_count++;
 
2896
        return regex;
 
2897
}
 
2898
 
 
2899
static void
 
2900
regex_unref (Regex *regex)
 
2901
{
 
2902
        if (regex != NULL && --regex->ref_count == 0)
 
2903
        {
 
2904
                if (regex->resolved)
 
2905
                {
 
2906
                        g_regex_unref (regex->u.regex.regex);
 
2907
                        if (regex->u.regex.match)
 
2908
                                g_match_info_free (regex->u.regex.match);
 
2909
                }
 
2910
                else
 
2911
                        g_free (regex->u.info.pattern);
 
2912
                g_slice_free (Regex, regex);
 
2913
        }
 
2914
}
 
2915
 
 
2916
/**
 
2917
 * find_single_byte_escape:
 
2918
 *
 
2919
 * @string: the pattern.
 
2920
 *
 
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.
 
2924
 */
 
2925
static gboolean
 
2926
find_single_byte_escape (const gchar *string)
 
2927
{
 
2928
        const char *p = string;
 
2929
 
 
2930
        while ((p = strstr (p, "\\C")))
 
2931
        {
 
2932
                const char *slash;
 
2933
                gboolean found;
 
2934
 
 
2935
                if (p == string)
 
2936
                        return TRUE;
 
2937
 
 
2938
                found = TRUE;
 
2939
                slash = p - 1;
 
2940
 
 
2941
                while (slash >= string && *slash == '\\')
 
2942
                {
 
2943
                        found = !found;
 
2944
                        slash--;
 
2945
                }
 
2946
 
 
2947
                if (found)
 
2948
                        return TRUE;
 
2949
 
 
2950
                p += 2;
 
2951
        }
 
2952
 
 
2953
        return FALSE;
 
2954
}
 
2955
 
 
2956
/**
 
2957
 * regex_new:
 
2958
 *
 
2959
 * @pattern: the regular expression.
 
2960
 * @flags: compile options for @pattern.
 
2961
 * @error: location to store the error occuring, or %NULL to ignore errors.
 
2962
 *
 
2963
 * Creates a new regex.
 
2964
 *
 
2965
 * Returns: a newly-allocated #Regex.
 
2966
 */
 
2967
static Regex *
 
2968
regex_new (const gchar           *pattern,
 
2969
           GRegexCompileFlags     flags,
 
2970
           GError               **error)
 
2971
{
 
2972
        Regex *regex;
 
2973
        static GRegex *start_ref_re = NULL;
 
2974
 
 
2975
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
2976
 
 
2977
        if (find_single_byte_escape (pattern))
 
2978
        {
 
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"));
 
2982
                return NULL;
 
2983
        }
 
2984
 
 
2985
        regex = g_slice_new0 (Regex);
 
2986
        regex->ref_count = 1;
 
2987
 
 
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,
 
2993
                                            0,
 
2994
                                            NULL);
 
2995
 
 
2996
        if (g_regex_match (start_ref_re, pattern, 0, NULL))
 
2997
        {
 
2998
                regex->resolved = FALSE;
 
2999
                regex->u.info.pattern = g_strdup (pattern);
 
3000
                regex->u.info.flags = flags;
 
3001
        }
 
3002
        else
 
3003
        {
 
3004
                regex->resolved = TRUE;
 
3005
                regex->u.regex.regex = g_regex_new (pattern,
 
3006
                                                    flags | G_REGEX_OPTIMIZE | G_REGEX_NEWLINE_LF, 0,
 
3007
                                                    error);
 
3008
 
 
3009
                if (regex->u.regex.regex == NULL)
 
3010
                {
 
3011
                        g_slice_free (Regex, regex);
 
3012
                        regex = NULL;
 
3013
                }
 
3014
        }
 
3015
 
 
3016
        return regex;
 
3017
}
 
3018
 
 
3019
/**
 
3020
 * sub_pattern_to_int:
 
3021
 *
 
3022
 * @name: the string from lang file.
 
3023
 *
 
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}).
 
3027
 */
 
3028
static gint
 
3029
sub_pattern_to_int (const gchar *name)
 
3030
{
 
3031
        guint64 number;
 
3032
        gchar *end_name;
 
3033
 
 
3034
        if (*name == 0)
 
3035
                return -1;
 
3036
 
 
3037
        errno = 0;
 
3038
        number = g_ascii_strtoull (name, &end_name, 10);
 
3039
 
 
3040
        if (errno !=0 || number > G_MAXINT || *end_name != 0)
 
3041
                return -1;
 
3042
 
 
3043
        return number;
 
3044
}
 
3045
 
 
3046
struct RegexResolveData {
 
3047
        Regex       *start_regex;
 
3048
        const gchar *matched_text;
 
3049
};
 
3050
 
 
3051
static gboolean
 
3052
replace_start_regex (const GMatchInfo *match_info,
 
3053
                     GString          *expanded_regex,
 
3054
                     gpointer          user_data)
 
3055
{
 
3056
        gchar *num_string, *subst, *subst_escaped, *escapes;
 
3057
        gint num;
 
3058
        struct RegexResolveData *data = user_data;
 
3059
 
 
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);
 
3063
 
 
3064
        if (num < 0)
 
3065
                subst = g_match_info_fetch_named (data->start_regex->u.regex.match,
 
3066
                                                  num_string);
 
3067
        else
 
3068
                subst = g_match_info_fetch (data->start_regex->u.regex.match,
 
3069
                                            num);
 
3070
 
 
3071
        if (subst != NULL)
 
3072
        {
 
3073
                subst_escaped = g_regex_escape_string (subst, -1);
 
3074
        }
 
3075
        else
 
3076
        {
 
3077
                g_warning ("Invalid group: %s", num_string);
 
3078
                subst_escaped = g_strdup ("");
 
3079
        }
 
3080
 
 
3081
        g_string_append (expanded_regex, escapes);
 
3082
        g_string_append (expanded_regex, subst_escaped);
 
3083
 
 
3084
        g_free (escapes);
 
3085
        g_free (num_string);
 
3086
        g_free (subst);
 
3087
        g_free (subst_escaped);
 
3088
 
 
3089
        return FALSE;
 
3090
}
 
3091
 
 
3092
/**
 
3093
 * regex_resolve:
 
3094
 *
 
3095
 * @regex: a #Regex.
 
3096
 * @start_regex: a #Regex.
 
3097
 * @matched_text: the text matched against @start_regex.
 
3098
 *
 
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.
 
3102
 *
 
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.
 
3107
 *
 
3108
 * Returns: a #Regex.
 
3109
 */
 
3110
static Regex *
 
3111
regex_resolve (Regex       *regex,
 
3112
               Regex       *start_regex,
 
3113
               const gchar *matched_text)
 
3114
{
 
3115
        GRegex *start_ref;
 
3116
        gchar *expanded_regex;
 
3117
        Regex *new_regex;
 
3118
        struct RegexResolveData data;
 
3119
 
 
3120
        if (regex == NULL || regex->resolved)
 
3121
                return regex_ref (regex);
 
3122
 
 
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,
 
3128
                                               -1, 0, 0,
 
3129
                                               replace_start_regex,
 
3130
                                               &data, NULL);
 
3131
        new_regex = regex_new (expanded_regex, regex->u.info.flags, NULL);
 
3132
 
 
3133
        if (new_regex == NULL || !new_regex->resolved)
 
3134
        {
 
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);
 
3140
        }
 
3141
 
 
3142
        g_free (expanded_regex);
 
3143
        g_regex_unref (start_ref);
 
3144
        return new_regex;
 
3145
}
 
3146
 
 
3147
static gboolean
 
3148
regex_match (Regex       *regex,
 
3149
             const gchar *line,
 
3150
             gint         byte_length,
 
3151
             gint         byte_pos)
 
3152
{
 
3153
        gboolean result;
 
3154
 
 
3155
        g_assert (regex->resolved);
 
3156
 
 
3157
        if (regex->u.regex.match)
 
3158
        {
 
3159
                g_match_info_free (regex->u.regex.match);
 
3160
                regex->u.regex.match = NULL;
 
3161
        }
 
3162
 
 
3163
        result = g_regex_match_full (regex->u.regex.regex, line,
 
3164
                                     byte_length, byte_pos,
 
3165
                                     0, &regex->u.regex.match,
 
3166
                                     NULL);
 
3167
 
 
3168
        return result;
 
3169
}
 
3170
 
 
3171
static gchar *
 
3172
regex_fetch (Regex       *regex,
 
3173
             gint         num)
 
3174
{
 
3175
        g_assert (regex->resolved);
 
3176
        return g_match_info_fetch (regex->u.regex.match, num);
 
3177
}
 
3178
 
 
3179
static void
 
3180
regex_fetch_pos (Regex       *regex,
 
3181
                 const gchar *text,
 
3182
                 gint         num,
 
3183
                 gint        *start_pos, /* character offsets */
 
3184
                 gint        *end_pos)   /* character offsets */
 
3185
{
 
3186
        gint byte_start_pos, byte_end_pos;
 
3187
 
 
3188
        g_assert (regex->resolved);
 
3189
 
 
3190
        if (!g_match_info_fetch_pos (regex->u.regex.match, num, &byte_start_pos, &byte_end_pos))
 
3191
        {
 
3192
                if (start_pos != NULL)
 
3193
                        *start_pos = -1;
 
3194
                if (end_pos != NULL)
 
3195
                        *end_pos = -1;
 
3196
        }
 
3197
        else
 
3198
        {
 
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);
 
3203
        }
 
3204
}
 
3205
 
 
3206
static void
 
3207
regex_fetch_pos_bytes (Regex *regex,
 
3208
                       gint   num,
 
3209
                       gint  *start_pos_p, /* byte offsets */
 
3210
                       gint  *end_pos_p)   /* byte offsets */
 
3211
{
 
3212
        gint start_pos;
 
3213
        gint end_pos;
 
3214
 
 
3215
        g_assert (regex->resolved);
 
3216
 
 
3217
        if (!g_match_info_fetch_pos (regex->u.regex.match, num, &start_pos, &end_pos))
 
3218
        {
 
3219
                start_pos = -1;
 
3220
                end_pos = -1;
 
3221
        }
 
3222
 
 
3223
        if (start_pos_p != NULL)
 
3224
                *start_pos_p = start_pos;
 
3225
        if (end_pos_p != NULL)
 
3226
                *end_pos_p = end_pos;
 
3227
}
 
3228
 
 
3229
static void
 
3230
regex_fetch_named_pos (Regex       *regex,
 
3231
                       const gchar *text,
 
3232
                       const gchar *name,
 
3233
                       gint        *start_pos, /* character offsets */
 
3234
                       gint        *end_pos)   /* character offsets */
 
3235
{
 
3236
        gint byte_start_pos, byte_end_pos;
 
3237
 
 
3238
        g_assert (regex->resolved);
 
3239
 
 
3240
        if (!g_match_info_fetch_named_pos (regex->u.regex.match, name, &byte_start_pos, &byte_end_pos))
 
3241
        {
 
3242
                if (start_pos != NULL)
 
3243
                        *start_pos = -1;
 
3244
                if (end_pos != NULL)
 
3245
                        *end_pos = -1;
 
3246
        }
 
3247
        else
 
3248
        {
 
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);
 
3253
        }
 
3254
}
 
3255
 
 
3256
static const gchar *
 
3257
regex_get_pattern (Regex *regex)
 
3258
{
 
3259
        g_return_val_if_fail (regex && regex->resolved, "");
 
3260
        return g_regex_get_pattern (regex->u.regex.regex);
 
3261
}
 
3262
 
 
3263
/* SYNTAX TREE ------------------------------------------------------------ */
 
3264
 
 
3265
/**
 
3266
 * apply_sub_patterns:
 
3267
 *
 
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.
 
3275
 *
 
3276
 * Applies sub patterns of kind @where to the matched text.
 
3277
 */
 
3278
static void
 
3279
apply_sub_patterns (Segment         *state,
 
3280
                    LineInfo        *line,
 
3281
                    Regex           *regex,
 
3282
                    SubPatternWhere  where)
 
3283
{
 
3284
        GSList *sub_pattern_list = state->context->definition->sub_patterns;
 
3285
 
 
3286
        if (SEGMENT_IS_CONTAINER (state))
 
3287
        {
 
3288
                gint start_pos;
 
3289
                gint end_pos;
 
3290
 
 
3291
                regex_fetch_pos (regex, line->text, 0, &start_pos, &end_pos);
 
3292
 
 
3293
                if (where == SUB_PATTERN_WHERE_START)
 
3294
                {
 
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);
 
3299
                        else
 
3300
                                state->start_len = line->start_at + end_pos - state->start_at;
 
3301
                }
 
3302
                else
 
3303
                {
 
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);
 
3308
                        else
 
3309
                                state->end_len = state->end_at - line->start_at - start_pos;
 
3310
                }
 
3311
        }
 
3312
 
 
3313
        while (sub_pattern_list != NULL)
 
3314
        {
 
3315
                SubPatternDefinition *sp_def = sub_pattern_list->data;
 
3316
 
 
3317
                if (sp_def->where == where)
 
3318
                {
 
3319
                        gint start_pos;
 
3320
                        gint end_pos;
 
3321
 
 
3322
                        if (sp_def->is_named)
 
3323
                                regex_fetch_named_pos (regex,
 
3324
                                                       line->text,
 
3325
                                                       sp_def->u.name,
 
3326
                                                       &start_pos,
 
3327
                                                       &end_pos);
 
3328
                        else
 
3329
                                regex_fetch_pos (regex,
 
3330
                                                 line->text,
 
3331
                                                 sp_def->u.num,
 
3332
                                                 &start_pos,
 
3333
                                                 &end_pos);
 
3334
 
 
3335
                        if (start_pos >= 0 && start_pos != end_pos)
 
3336
                        {
 
3337
                                sub_pattern_new (state,
 
3338
                                                 line->start_at + start_pos,
 
3339
                                                 line->start_at + end_pos,
 
3340
                                                 sp_def);
 
3341
                        }
 
3342
                }
 
3343
 
 
3344
                sub_pattern_list = sub_pattern_list->next;
 
3345
        }
 
3346
}
 
3347
 
 
3348
/**
 
3349
 * can_apply_match:
 
3350
 *
 
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.
 
3356
 *
 
3357
 * See apply_match(), this function is a helper function
 
3358
 * called from where, it doesn't modify syntax tree.
 
3359
 *
 
3360
 * Returns: %TRUE if the match can be applied.
 
3361
 */
 
3362
static gboolean
 
3363
can_apply_match (Context  *state,
 
3364
                 LineInfo *line,
 
3365
                 gint      match_start,
 
3366
                 gint     *match_end,
 
3367
                 Regex    *regex)
 
3368
{
 
3369
        gint end_match_pos;
 
3370
        gboolean ancestor_ends;
 
3371
        gint pos;
 
3372
 
 
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);
 
3376
 
 
3377
        g_assert (end_match_pos <= line->byte_length);
 
3378
 
 
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)
 
3383
        {
 
3384
                pos = match_start + 1;
 
3385
 
 
3386
                while (pos < end_match_pos)
 
3387
                {
 
3388
                        if (ancestor_context_ends_here (state, line, pos))
 
3389
                        {
 
3390
                                ancestor_ends = TRUE;
 
3391
                                break;
 
3392
                        }
 
3393
 
 
3394
                        pos = g_utf8_next_char (line->text + pos) - line->text;
 
3395
                }
 
3396
        }
 
3397
        else
 
3398
        {
 
3399
                pos = end_match_pos;
 
3400
        }
 
3401
 
 
3402
        if (ancestor_ends)
 
3403
        {
 
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))
 
3410
                {
 
3411
                        /* This match is not valid, so we can try to match
 
3412
                         * the next definition, so the position should not
 
3413
                         * change. */
 
3414
                        return FALSE;
 
3415
                }
 
3416
        }
 
3417
 
 
3418
        *match_end = pos;
 
3419
        return TRUE;
 
3420
}
 
3421
 
 
3422
static gint
 
3423
line_pos_to_offset (LineInfo *line,
 
3424
                    gint      pos)
 
3425
{
 
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;
 
3429
}
 
3430
 
 
3431
/**
 
3432
 * apply_match:
 
3433
 *
 
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.
 
3439
 *
 
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
 
3443
 * text.
 
3444
 *
 
3445
 * If the match can be applied the function applies the appropriate
 
3446
 * sub patterns.
 
3447
 *
 
3448
 * Returns: %TRUE if the match can be applied.
 
3449
 */
 
3450
static gboolean
 
3451
apply_match (Segment         *state,
 
3452
             LineInfo        *line,
 
3453
             gint            *line_pos,
 
3454
             Regex           *regex,
 
3455
             SubPatternWhere  where)
 
3456
{
 
3457
        gint match_end;
 
3458
 
 
3459
        if (!can_apply_match (state->context, line, *line_pos, &match_end, regex))
 
3460
                return FALSE;
 
3461
 
 
3462
        segment_extend (state, line_pos_to_offset (line, match_end));
 
3463
        apply_sub_patterns (state, line, regex, where);
 
3464
        *line_pos = match_end;
 
3465
 
 
3466
        return TRUE;
 
3467
}
 
3468
 
 
3469
/**
 
3470
 * create_reg_all:
 
3471
 *
 
3472
 * @context: context.
 
3473
 * @definition: context definition.
 
3474
 *
 
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.
 
3479
 *
 
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
 
3485
 * arguments.
 
3486
 *
 
3487
 * Returns: resulting regex or %NULL when pcre failed to compile the regex.
 
3488
 */
 
3489
static Regex *
 
3490
create_reg_all (Context           *context,
 
3491
                ContextDefinition *definition)
 
3492
{
 
3493
        DefinitionsIter iter;
 
3494
        DefinitionChild *child_def;
 
3495
        GString *all;
 
3496
        Regex *regex;
 
3497
        GError *error = NULL;
 
3498
 
 
3499
        g_return_val_if_fail ((context == NULL && definition != NULL) ||
 
3500
                              (context != NULL && definition == NULL), NULL);
 
3501
 
 
3502
        if (definition == NULL)
 
3503
                definition = context->definition;
 
3504
 
 
3505
        all = g_string_new ("(");
 
3506
 
 
3507
        /* Closing regex. */
 
3508
        if (definition->type == CONTEXT_TYPE_CONTAINER &&
 
3509
            definition->u.start_end.end != NULL)
 
3510
        {
 
3511
                Regex *end;
 
3512
 
 
3513
                if (definition->u.start_end.end->resolved)
 
3514
                {
 
3515
                        end = definition->u.start_end.end;
 
3516
                }
 
3517
                else
 
3518
                {
 
3519
                        g_return_val_if_fail (context && context->end, NULL);
 
3520
                        end = context->end;
 
3521
                }
 
3522
 
 
3523
                g_string_append (all, regex_get_pattern (end));
 
3524
                g_string_append (all, "|");
 
3525
        }
 
3526
 
 
3527
        /* Ancestors. */
 
3528
        if (context != NULL)
 
3529
        {
 
3530
                Context *tmp = context;
 
3531
 
 
3532
                while (ANCESTOR_CAN_END_CONTEXT (tmp))
 
3533
                {
 
3534
                        if (!CONTEXT_EXTENDS_PARENT (tmp))
 
3535
                        {
 
3536
                                gboolean append = TRUE;
 
3537
 
 
3538
                                /* Code as it is seems to be right, and seems working right.
 
3539
                                 * Remove FIXME's below if everything is fine. */
 
3540
 
 
3541
                                if (tmp->parent->end != NULL)
 
3542
                                        g_string_append (all, regex_get_pattern (tmp->parent->end));
 
3543
                                /* FIXME ?
 
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 */
 
3551
                                else
 
3552
                                {
 
3553
                                        /* g_critical ("%s: oops", G_STRLOC); */
 
3554
                                        append = FALSE;
 
3555
                                }
 
3556
 
 
3557
                                if (append)
 
3558
                                        g_string_append (all, "|");
 
3559
                        }
 
3560
 
 
3561
                        tmp = tmp->parent;
 
3562
                }
 
3563
        }
 
3564
 
 
3565
        /* Children. */
 
3566
        definition_iter_init (&iter, definition);
 
3567
        while ((child_def = definition_iter_next (&iter)) != NULL)
 
3568
        {
 
3569
                Regex *child_regex = NULL;
 
3570
 
 
3571
                g_return_val_if_fail (child_def->resolved, NULL);
 
3572
 
 
3573
                switch (child_def->u.definition->type)
 
3574
                {
 
3575
                        case CONTEXT_TYPE_CONTAINER:
 
3576
                                child_regex = child_def->u.definition->u.start_end.start;
 
3577
                                break;
 
3578
                        case CONTEXT_TYPE_SIMPLE:
 
3579
                                child_regex = child_def->u.definition->u.match;
 
3580
                                break;
 
3581
                        default:
 
3582
                                g_return_val_if_reached (NULL);
 
3583
                }
 
3584
 
 
3585
                if (child_regex != NULL)
 
3586
                {
 
3587
                        g_string_append (all, regex_get_pattern (child_regex));
 
3588
                        g_string_append (all, "|");
 
3589
                }
 
3590
        }
 
3591
        definition_iter_destroy (&iter);
 
3592
 
 
3593
        if (all->len > 1)
 
3594
                g_string_truncate (all, all->len - 1);
 
3595
        g_string_append (all, ")");
 
3596
 
 
3597
        regex = regex_new (all->str, 0, &error);
 
3598
 
 
3599
        if (regex == NULL)
 
3600
        {
 
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);
 
3609
        }
 
3610
 
 
3611
        g_string_free (all, TRUE);
 
3612
        return regex;
 
3613
}
 
3614
 
 
3615
static Context *
 
3616
context_ref (Context *context)
 
3617
{
 
3618
        if (context != NULL)
 
3619
                context->ref_count++;
 
3620
        return context;
 
3621
}
 
3622
 
 
3623
/* does not copy style */
 
3624
static Context *
 
3625
context_new (Context           *parent,
 
3626
             ContextDefinition *definition,
 
3627
             const gchar       *line_text,
 
3628
             const gchar       *style,
 
3629
             gboolean           ignore_children_style)
 
3630
{
 
3631
        Context *context;
 
3632
 
 
3633
        context = g_slice_new0 (Context);
 
3634
        context->ref_count = 1;
 
3635
        context->definition = definition;
 
3636
        context->parent = parent;
 
3637
 
 
3638
        context->style = style;
 
3639
        context->ignore_children_style = ignore_children_style;
 
3640
 
 
3641
        if (parent != NULL && parent->ignore_children_style)
 
3642
        {
 
3643
                context->ignore_children_style = TRUE;
 
3644
                context->style = NULL;
 
3645
        }
 
3646
 
 
3647
        if (!parent || (parent->all_ancestors_extend && CONTEXT_EXTENDS_PARENT (parent)))
 
3648
        {
 
3649
                context->all_ancestors_extend = TRUE;
 
3650
        }
 
3651
 
 
3652
        if (line_text &&
 
3653
            definition->type == CONTEXT_TYPE_CONTAINER &&
 
3654
            definition->u.start_end.end)
 
3655
        {
 
3656
                context->end = regex_resolve (definition->u.start_end.end,
 
3657
                                              definition->u.start_end.start,
 
3658
                                              line_text);
 
3659
        }
 
3660
 
 
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))
 
3667
        {
 
3668
                context->reg_all = create_reg_all (context, NULL);
 
3669
        }
 
3670
        else
 
3671
        {
 
3672
                if (!definition->reg_all)
 
3673
                        definition->reg_all = create_reg_all (NULL, definition);
 
3674
                context->reg_all = regex_ref (definition->reg_all);
 
3675
        }
 
3676
 
 
3677
#ifdef ENABLE_DEBUG
 
3678
        {
 
3679
                GString *str = g_string_new (definition->id);
 
3680
                Context *tmp = context->parent;
 
3681
                while (tmp != NULL)
 
3682
                {
 
3683
                        g_string_prepend (str, "/");
 
3684
                        g_string_prepend (str, tmp->definition->id);
 
3685
                        tmp = tmp->parent;
 
3686
                }
 
3687
                g_print ("created context %s: %s\n", definition->id, str->str);
 
3688
                g_string_free (str, TRUE);
 
3689
        }
 
3690
#endif
 
3691
 
 
3692
        return context;
 
3693
}
 
3694
 
 
3695
static void
 
3696
context_unref_hash_cb (G_GNUC_UNUSED gpointer text,
 
3697
                       Context *context)
 
3698
{
 
3699
        context->parent = NULL;
 
3700
        context_unref (context);
 
3701
}
 
3702
 
 
3703
static gboolean
 
3704
remove_context_cb (G_GNUC_UNUSED gpointer text,
 
3705
                   Context *context,
 
3706
                   Context *target)
 
3707
{
 
3708
        return context == target;
 
3709
}
 
3710
 
 
3711
static void
 
3712
context_remove_child (Context *parent,
 
3713
                      Context *context)
 
3714
{
 
3715
        ContextPtr *ptr, *prev = NULL;
 
3716
        gboolean delete = TRUE;
 
3717
 
 
3718
        g_assert (context->parent == parent);
 
3719
 
 
3720
        for (ptr = context->parent->children; ptr; ptr = ptr->next)
 
3721
        {
 
3722
                if (ptr->definition == context->definition)
 
3723
                        break;
 
3724
                prev = ptr;
 
3725
        }
 
3726
 
 
3727
        g_assert (ptr != NULL);
 
3728
 
 
3729
        if (!ptr->fixed)
 
3730
        {
 
3731
                g_hash_table_foreach_remove (ptr->u.hash,
 
3732
                                             (GHRFunc) remove_context_cb,
 
3733
                                             context);
 
3734
 
 
3735
                if (g_hash_table_size (ptr->u.hash) != 0)
 
3736
                        delete = FALSE;
 
3737
        }
 
3738
 
 
3739
        if (delete)
 
3740
        {
 
3741
                if (prev != NULL)
 
3742
                        prev->next = ptr->next;
 
3743
                else
 
3744
                        context->parent->children = ptr->next;
 
3745
 
 
3746
                if (!ptr->fixed)
 
3747
                        g_hash_table_destroy (ptr->u.hash);
 
3748
 
 
3749
#ifdef ENABLE_DEBUG
 
3750
                memset (ptr, 1, sizeof (ContextPtr));
 
3751
#else
 
3752
                g_slice_free (ContextPtr, ptr);
 
3753
#endif
 
3754
        }
 
3755
}
 
3756
 
 
3757
/**
 
3758
 * context_unref:
 
3759
 *
 
3760
 * @context: the context.
 
3761
 *
 
3762
 * Decreases reference count and removes @context
 
3763
 * from the tree when it drops to zero.
 
3764
 */
 
3765
static void
 
3766
context_unref (Context *context)
 
3767
{
 
3768
        ContextPtr *children;
 
3769
        gint i;
 
3770
 
 
3771
        if (context == NULL || --context->ref_count != 0)
 
3772
                return;
 
3773
 
 
3774
        DEBUG (g_print ("destroying context %s\n", context->definition->id));
 
3775
 
 
3776
        children = context->children;
 
3777
        context->children = NULL;
 
3778
 
 
3779
        while (children != NULL)
 
3780
        {
 
3781
                ContextPtr *ptr = children;
 
3782
 
 
3783
                children = children->next;
 
3784
 
 
3785
                if (ptr->fixed)
 
3786
                {
 
3787
                        ptr->u.context->parent = NULL;
 
3788
                        context_unref (ptr->u.context);
 
3789
                }
 
3790
                else
 
3791
                {
 
3792
                        g_hash_table_foreach (ptr->u.hash,
 
3793
                                              (GHFunc) context_unref_hash_cb,
 
3794
                                              NULL);
 
3795
                        g_hash_table_destroy (ptr->u.hash);
 
3796
                }
 
3797
 
 
3798
#ifdef ENABLE_DEBUG
 
3799
                memset (ptr, 1, sizeof (ContextPtr));
 
3800
#else
 
3801
                g_slice_free (ContextPtr, ptr);
 
3802
#endif
 
3803
        }
 
3804
 
 
3805
        if (context->parent != NULL)
 
3806
                context_remove_child (context->parent, context);
 
3807
 
 
3808
        regex_unref (context->end);
 
3809
        regex_unref (context->reg_all);
 
3810
 
 
3811
        if (context->subpattern_context_classes != NULL)
 
3812
        {
 
3813
                for (i = 0; i < context->definition->n_sub_patterns; ++i)
 
3814
                {
 
3815
                        g_slist_foreach (context->subpattern_context_classes[i], 
 
3816
                                         (GFunc)context_class_tag_free,
 
3817
                                         NULL);
 
3818
 
 
3819
                        g_slist_free (context->subpattern_context_classes[i]);
 
3820
                }
 
3821
        }
 
3822
 
 
3823
        g_slist_foreach (context->context_classes, (GFunc)context_class_tag_free, NULL);
 
3824
        g_slist_free (context->context_classes);
 
3825
 
 
3826
        g_free (context->subpattern_context_classes);
 
3827
        g_free (context->subpattern_tags);
 
3828
 
 
3829
        g_slice_free (Context, context);
 
3830
}
 
3831
 
 
3832
static void
 
3833
context_freeze_hash_cb (G_GNUC_UNUSED gpointer text,
 
3834
                        Context *context)
 
3835
{
 
3836
        context_freeze (context);
 
3837
}
 
3838
 
 
3839
/**
 
3840
 * context_freeze:
 
3841
 *
 
3842
 * @context: the context.
 
3843
 *
 
3844
 * Recursively increments reference count in context and its children,
 
3845
 * and marks them, so context_thaw is able to correctly decrement
 
3846
 * reference count.
 
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.
 
3854
 *
 
3855
 * Note this is not reentrant, context_freeze()/context_thaw() pair is called
 
3856
 * only from update_syntax().
 
3857
 */
 
3858
static void
 
3859
context_freeze (Context *ctx)
 
3860
{
 
3861
        ContextPtr *ptr;
 
3862
 
 
3863
        g_assert (!ctx->frozen);
 
3864
        ctx->frozen = TRUE;
 
3865
        context_ref (ctx);
 
3866
 
 
3867
        for (ptr = ctx->children; ptr != NULL; ptr = ptr->next)
 
3868
        {
 
3869
                if (ptr->fixed)
 
3870
                {
 
3871
                        context_freeze (ptr->u.context);
 
3872
                }
 
3873
                else
 
3874
                {
 
3875
                        g_hash_table_foreach (ptr->u.hash,
 
3876
                                              (GHFunc) context_freeze_hash_cb,
 
3877
                                              NULL);
 
3878
                }
 
3879
        }
 
3880
}
 
3881
 
 
3882
static void
 
3883
get_child_contexts_hash_cb (G_GNUC_UNUSED gpointer text,
 
3884
                            Context *context,
 
3885
                            GSList **list)
 
3886
{
 
3887
        *list = g_slist_prepend (*list, context);
 
3888
}
 
3889
 
 
3890
/**
 
3891
 * context_thaw:
 
3892
 *
 
3893
 * @context: the context.
 
3894
 *
 
3895
 * Recursively decrements reference count in context and its children,
 
3896
 * if it was incremented by context_freeze().
 
3897
 */
 
3898
static void
 
3899
context_thaw (Context *ctx)
 
3900
{
 
3901
        ContextPtr *ptr;
 
3902
 
 
3903
        if (!ctx->frozen)
 
3904
                return;
 
3905
 
 
3906
        for (ptr = ctx->children; ptr != NULL; )
 
3907
        {
 
3908
                ContextPtr *next = ptr->next;
 
3909
 
 
3910
                if (ptr->fixed)
 
3911
                {
 
3912
                        context_thaw (ptr->u.context);
 
3913
                }
 
3914
                else
 
3915
                {
 
3916
                        GSList *children = NULL;
 
3917
                        g_hash_table_foreach (ptr->u.hash,
 
3918
                                              (GHFunc) get_child_contexts_hash_cb,
 
3919
                                              &children);
 
3920
                        g_slist_foreach (children, (GFunc) context_thaw, NULL);
 
3921
                        g_slist_free (children);
 
3922
                }
 
3923
 
 
3924
                ptr = next;
 
3925
        }
 
3926
 
 
3927
        ctx->frozen = FALSE;
 
3928
        context_unref (ctx);
 
3929
}
 
3930
 
 
3931
static Context *
 
3932
create_child_context (Context           *parent,
 
3933
                      DefinitionChild   *child_def,
 
3934
                      const gchar       *line_text)
 
3935
{
 
3936
        Context *context;
 
3937
        ContextPtr *ptr;
 
3938
        gchar *match = NULL;
 
3939
        ContextDefinition *definition = child_def->u.definition;
 
3940
 
 
3941
        g_return_val_if_fail (parent != NULL, NULL);
 
3942
 
 
3943
        for (ptr = parent->children;
 
3944
             ptr != NULL && ptr->definition != definition;
 
3945
             ptr = ptr->next) ;
 
3946
 
 
3947
        if (ptr == NULL)
 
3948
        {
 
3949
                ptr = g_slice_new0 (ContextPtr);
 
3950
                ptr->next = parent->children;
 
3951
                parent->children = ptr;
 
3952
                ptr->definition = definition;
 
3953
 
 
3954
                if (definition->type != CONTEXT_TYPE_CONTAINER ||
 
3955
                    !definition->u.start_end.end ||
 
3956
                    definition->u.start_end.end->resolved)
 
3957
                {
 
3958
                        ptr->fixed = TRUE;
 
3959
                }
 
3960
 
 
3961
                if (!ptr->fixed)
 
3962
                        ptr->u.hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
3963
        }
 
3964
 
 
3965
        if (ptr->fixed)
 
3966
        {
 
3967
                context = ptr->u.context;
 
3968
        }
 
3969
        else
 
3970
        {
 
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);
 
3974
        }
 
3975
 
 
3976
        if (context != NULL)
 
3977
        {
 
3978
                g_free (match);
 
3979
                return context_ref (context);
 
3980
        }
 
3981
 
 
3982
        context = context_new (parent,
 
3983
                               definition,
 
3984
                               line_text,
 
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);
 
3989
 
 
3990
        if (ptr->fixed)
 
3991
                ptr->u.context = context;
 
3992
        else
 
3993
                g_hash_table_insert (ptr->u.hash, match, context);
 
3994
 
 
3995
        return context;
 
3996
}
 
3997
 
 
3998
/**
 
3999
 * segment_new:
 
4000
 *
 
4001
 * @ce: the engine.
 
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.
 
4007
 *
 
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.
 
4011
 *
 
4012
 * Returns: newly created segment.
 
4013
 */
 
4014
static Segment *
 
4015
segment_new (GtkSourceContextEngine *ce,
 
4016
             Segment                *parent,
 
4017
             Context                *context,
 
4018
             gint                    start_at,
 
4019
             gint                    end_at,
 
4020
             gboolean                is_start)
 
4021
{
 
4022
        Segment *segment;
 
4023
 
 
4024
#ifdef ENABLE_CHECK_TREE
 
4025
        g_assert (!is_start || context != NULL);
 
4026
#endif
 
4027
 
 
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;
 
4034
 
 
4035
        if (context == NULL)
 
4036
                add_invalid (ce, segment);
 
4037
 
 
4038
        return segment;
 
4039
}
 
4040
 
 
4041
static void
 
4042
find_segment_position_forward_ (Segment  *segment,
 
4043
                                gint      start_at,
 
4044
                                gint      end_at,
 
4045
                                Segment **prev,
 
4046
                                Segment **next)
 
4047
{
 
4048
        g_assert (segment->start_at <= start_at);
 
4049
 
 
4050
        while (segment != NULL)
 
4051
        {
 
4052
                if (segment->end_at == start_at)
 
4053
                {
 
4054
                        while (segment->next != NULL && segment->next->start_at == start_at)
 
4055
                                segment = segment->next;
 
4056
 
 
4057
                        *prev = segment;
 
4058
                        *next = segment->next;
 
4059
 
 
4060
                        break;
 
4061
                }
 
4062
 
 
4063
                if (segment->start_at == end_at)
 
4064
                {
 
4065
                        *next = segment;
 
4066
                        *prev = segment->prev;
 
4067
                        break;
 
4068
                }
 
4069
 
 
4070
                if (segment->start_at > end_at)
 
4071
                {
 
4072
                        *next = segment;
 
4073
                        break;
 
4074
                }
 
4075
 
 
4076
                if (segment->end_at < start_at)
 
4077
                        *prev = segment;
 
4078
 
 
4079
                segment = segment->next;
 
4080
        }
 
4081
}
 
4082
 
 
4083
static void
 
4084
find_segment_position_backward_ (Segment  *segment,
 
4085
                                 gint      start_at,
 
4086
                                 gint      end_at,
 
4087
                                 Segment **prev,
 
4088
                                 Segment **next)
 
4089
{
 
4090
        g_assert (start_at < segment->end_at);
 
4091
 
 
4092
        while (segment != NULL)
 
4093
        {
 
4094
                if (segment->end_at <= start_at)
 
4095
                {
 
4096
                        *prev = segment;
 
4097
                        break;
 
4098
                }
 
4099
 
 
4100
                g_assert (segment->start_at >= end_at);
 
4101
 
 
4102
                *next = segment;
 
4103
                segment = segment->prev;
 
4104
        }
 
4105
}
 
4106
 
 
4107
/**
 
4108
 * find_segment_position:
 
4109
 *
 
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.
 
4116
 *
 
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.
 
4120
 */
 
4121
static void
 
4122
find_segment_position (Segment  *parent,
 
4123
                       Segment  *hint,
 
4124
                       gint      start_at,
 
4125
                       gint      end_at,
 
4126
                       Segment **prev,
 
4127
                       Segment **next)
 
4128
{
 
4129
        Segment *tmp;
 
4130
 
 
4131
        g_assert (parent->start_at <= start_at && end_at <= parent->end_at);
 
4132
        g_assert (!hint || hint->parent == parent);
 
4133
 
 
4134
        *prev = *next = NULL;
 
4135
 
 
4136
        if (parent->children == NULL)
 
4137
                return;
 
4138
 
 
4139
        if (parent->children->next == NULL)
 
4140
        {
 
4141
                tmp = parent->children;
 
4142
 
 
4143
                if (start_at >= tmp->end_at)
 
4144
                        *prev = tmp;
 
4145
                else
 
4146
                        *next = tmp;
 
4147
 
 
4148
                return;
 
4149
        }
 
4150
 
 
4151
        if (hint == NULL)
 
4152
                hint = parent->children;
 
4153
 
 
4154
        if (hint->end_at <= start_at)
 
4155
                find_segment_position_forward_ (hint, start_at, end_at, prev, next);
 
4156
        else
 
4157
                find_segment_position_backward_ (hint, start_at, end_at, prev, next);
 
4158
}
 
4159
 
 
4160
/**
 
4161
 * create_segment:
 
4162
 *
 
4163
 * @ce: the engine.
 
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.
 
4170
 *
 
4171
 * Creates a new segment and inserts it into the tree.
 
4172
 *
 
4173
 * Returns: newly created segment.
 
4174
 */
 
4175
static Segment *
 
4176
create_segment (GtkSourceContextEngine *ce,
 
4177
                Segment                *parent,
 
4178
                Context                *context,
 
4179
                gint                    start_at,
 
4180
                gint                    end_at,
 
4181
                gboolean                is_start,
 
4182
                Segment                *hint)
 
4183
{
 
4184
        Segment *segment;
 
4185
 
 
4186
        g_assert (!parent || (parent->start_at <= start_at && end_at <= parent->end_at));
 
4187
 
 
4188
        segment = segment_new (ce, parent, context, start_at, end_at, is_start);
 
4189
 
 
4190
        if (parent != NULL)
 
4191
        {
 
4192
                Segment *prev, *next;
 
4193
 
 
4194
                if (hint == NULL)
 
4195
                {
 
4196
                        hint = ce->priv->hint;
 
4197
                        while (hint != NULL && hint->parent != parent)
 
4198
                                hint = hint->parent;
 
4199
                }
 
4200
 
 
4201
                find_segment_position (parent, hint,
 
4202
                                       start_at, end_at,
 
4203
                                       &prev, &next);
 
4204
 
 
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);
 
4209
 
 
4210
                segment->next = next;
 
4211
                segment->prev = prev;
 
4212
 
 
4213
                if (next != NULL)
 
4214
                        next->prev = segment;
 
4215
                else
 
4216
                        parent->last_child = segment;
 
4217
 
 
4218
                if (prev != NULL)
 
4219
                        prev->next = segment;
 
4220
                else
 
4221
                        parent->children = segment;
 
4222
 
 
4223
                CHECK_SEGMENT_LIST (parent);
 
4224
                CHECK_TREE (ce);
 
4225
        }
 
4226
 
 
4227
        return segment;
 
4228
}
 
4229
 
 
4230
/**
 
4231
 * segment_extend:
 
4232
 *
 
4233
 * @state: the semgent.
 
4234
 * @end_at: new end offset, characters.
 
4235
 *
 
4236
 * Updates end offset in the segment and its ancestors.
 
4237
 */
 
4238
static void
 
4239
segment_extend (Segment *state,
 
4240
                gint     end_at)
 
4241
{
 
4242
        while (state != NULL && state->end_at < end_at)
 
4243
        {
 
4244
                state->end_at = end_at;
 
4245
                state = state->parent;
 
4246
        }
 
4247
        CHECK_SEGMENT_LIST (state->parent);
 
4248
}
 
4249
 
 
4250
static void
 
4251
segment_destroy_children (GtkSourceContextEngine *ce,
 
4252
                          Segment                *segment)
 
4253
{
 
4254
        Segment *child;
 
4255
        SubPattern *sp;
 
4256
 
 
4257
        g_return_if_fail (segment != NULL);
 
4258
 
 
4259
        child = segment->children;
 
4260
        segment->children = NULL;
 
4261
        segment->last_child = NULL;
 
4262
 
 
4263
        while (child != NULL)
 
4264
        {
 
4265
                Segment *next = child->next;
 
4266
                segment_destroy (ce, child);
 
4267
                child = next;
 
4268
        }
 
4269
 
 
4270
        sp = segment->sub_patterns;
 
4271
        segment->sub_patterns = NULL;
 
4272
 
 
4273
        while (sp != NULL)
 
4274
        {
 
4275
                SubPattern *next = sp->next;
 
4276
                sub_pattern_free (sp);
 
4277
                sp = next;
 
4278
        }
 
4279
}
 
4280
 
 
4281
/**
 
4282
 * segment_destroy:
 
4283
 *
 
4284
 * @ce: the engine.
 
4285
 * @context: the segment to destroy.
 
4286
 *
 
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.
 
4291
 */
 
4292
static void
 
4293
segment_destroy (GtkSourceContextEngine *ce,
 
4294
                 Segment                *segment)
 
4295
{
 
4296
        g_return_if_fail (segment != NULL);
 
4297
 
 
4298
        segment_destroy_children (ce, segment);
 
4299
 
 
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;
 
4306
 
 
4307
        if (SEGMENT_IS_INVALID (segment))
 
4308
                remove_invalid (ce, segment);
 
4309
 
 
4310
        context_unref (segment->context);
 
4311
 
 
4312
#ifdef ENABLE_DEBUG
 
4313
        g_assert (!g_slist_find (ce->priv->invalid, segment));
 
4314
        memset (segment, 1, sizeof (Segment));
 
4315
#else
 
4316
        g_slice_free (Segment, segment);
 
4317
#endif
 
4318
}
 
4319
 
 
4320
/**
 
4321
 * container_context_starts_here:
 
4322
 *
 
4323
 * See child_starts_here().
 
4324
 */
 
4325
static gboolean
 
4326
container_context_starts_here (GtkSourceContextEngine  *ce,
 
4327
                               Segment                 *state,
 
4328
                               DefinitionChild         *child_def,
 
4329
                               LineInfo                *line,
 
4330
                               gint                    *line_pos, /* bytes */
 
4331
                               Segment                **new_state)
 
4332
{
 
4333
        Context *new_context;
 
4334
        Segment *new_segment;
 
4335
        gint match_end;
 
4336
        ContextDefinition *definition = child_def->u.definition;
 
4337
 
 
4338
        g_assert (*line_pos <= line->byte_length);
 
4339
 
 
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)
 
4343
                return FALSE;
 
4344
 
 
4345
        if (!regex_match (definition->u.start_end.start,
 
4346
                          line->text, line->byte_length, *line_pos))
 
4347
        {
 
4348
                return FALSE;
 
4349
        }
 
4350
 
 
4351
        new_context = create_child_context (state->context, child_def, line->text);
 
4352
        g_return_val_if_fail (new_context != NULL, FALSE);
 
4353
 
 
4354
        if (!can_apply_match (new_context, line, *line_pos, &match_end,
 
4355
                              definition->u.start_end.start))
 
4356
        {
 
4357
                context_unref (new_context);
 
4358
                return FALSE;
 
4359
        }
 
4360
 
 
4361
        g_assert (match_end <= line->byte_length);
 
4362
 
 
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),
 
4367
                                      TRUE,
 
4368
                                      ce->priv->hint2);
 
4369
 
 
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))
 
4381
        {
 
4382
                segment_remove (ce, new_segment);
 
4383
                return FALSE;
 
4384
        }
 
4385
 
 
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);
 
4393
        return TRUE;
 
4394
}
 
4395
 
 
4396
/**
 
4397
 * simple_context_starts_here:
 
4398
 *
 
4399
 * See child_starts_here().
 
4400
 */
 
4401
static gboolean
 
4402
simple_context_starts_here (GtkSourceContextEngine *ce,
 
4403
                            Segment                *state,
 
4404
                            DefinitionChild        *child_def,
 
4405
                            LineInfo               *line,
 
4406
                            gint                   *line_pos, /* bytes */
 
4407
                            Segment               **new_state)
 
4408
{
 
4409
        gint match_end;
 
4410
        Context *new_context;
 
4411
        ContextDefinition *definition = child_def->u.definition;
 
4412
 
 
4413
        g_return_val_if_fail (definition->u.match != NULL, FALSE);
 
4414
 
 
4415
        g_assert (*line_pos <= line->byte_length);
 
4416
 
 
4417
        if (!regex_match (definition->u.match, line->text, line->byte_length, *line_pos))
 
4418
                return FALSE;
 
4419
 
 
4420
        new_context = create_child_context (state->context, child_def, line->text);
 
4421
        g_return_val_if_fail (new_context != NULL, FALSE);
 
4422
 
 
4423
        if (!can_apply_match (new_context, line, *line_pos, &match_end, definition->u.match))
 
4424
        {
 
4425
                context_unref (new_context);
 
4426
                return FALSE;
 
4427
        }
 
4428
 
 
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
 
4434
         * so on). */
 
4435
        if (*line_pos == match_end &&
 
4436
            (!CONTEXT_ENDS_PARENT (new_context) ||
 
4437
                line_pos_to_offset (line, *line_pos) == state->start_at))
 
4438
        {
 
4439
                context_unref (new_context);
 
4440
                return FALSE;
 
4441
        }
 
4442
 
 
4443
        g_assert (match_end <= line->byte_length);
 
4444
        segment_extend (state, line_pos_to_offset (line, match_end));
 
4445
 
 
4446
        if (*line_pos != match_end)
 
4447
        {
 
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),
 
4453
                                              TRUE,
 
4454
                                              ce->priv->hint2);
 
4455
                apply_sub_patterns (new_segment, line, definition->u.match, SUB_PATTERN_WHERE_DEFAULT);
 
4456
                ce->priv->hint2 = new_segment;
 
4457
        }
 
4458
 
 
4459
        /* Terminate parent if needed */
 
4460
        if (CONTEXT_ENDS_PARENT (new_context))
 
4461
        {
 
4462
                do
 
4463
                {
 
4464
                        ce->priv->hint2 = state;
 
4465
                        state = state->parent;
 
4466
                }
 
4467
                while (SEGMENT_ENDS_PARENT (state));
 
4468
        }
 
4469
 
 
4470
        *line_pos = match_end;
 
4471
        *new_state = state;
 
4472
        context_unref (new_context);
 
4473
        return TRUE;
 
4474
}
 
4475
 
 
4476
/**
 
4477
 * child_starts_here:
 
4478
 *
 
4479
 * @ce: the engine.
 
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.
 
4485
 *
 
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.
 
4489
 *
 
4490
 * Returns: %TRUE if the context starts here.
 
4491
 */
 
4492
static gboolean
 
4493
child_starts_here (GtkSourceContextEngine *ce,
 
4494
                   Segment                *state,
 
4495
                   DefinitionChild        *child_def,
 
4496
                   LineInfo               *line,
 
4497
                   gint                   *line_pos,
 
4498
                   Segment               **new_state)
 
4499
{
 
4500
        g_return_val_if_fail (child_def->resolved, FALSE);
 
4501
 
 
4502
        switch (child_def->u.definition->type)
 
4503
        {
 
4504
                case CONTEXT_TYPE_SIMPLE:
 
4505
                        return simple_context_starts_here (ce,
 
4506
                                                           state,
 
4507
                                                           child_def,
 
4508
                                                           line,
 
4509
                                                           line_pos,
 
4510
                                                           new_state);
 
4511
                case CONTEXT_TYPE_CONTAINER:
 
4512
                        return container_context_starts_here (ce,
 
4513
                                                              state,
 
4514
                                                              child_def,
 
4515
                                                              line,
 
4516
                                                              line_pos,
 
4517
                                                              new_state);
 
4518
                default:
 
4519
                        g_return_val_if_reached (FALSE);
 
4520
        }
 
4521
}
 
4522
 
 
4523
/**
 
4524
 * segment_ends_here:
 
4525
 *
 
4526
 * @state: the segment.
 
4527
 * @line: analyzed line.
 
4528
 * @pos: the position inside @line, bytes.
 
4529
 *
 
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.
 
4533
 */
 
4534
static gboolean
 
4535
segment_ends_here (Segment  *state,
 
4536
                   LineInfo *line,
 
4537
                   gint      pos)
 
4538
{
 
4539
        g_assert (SEGMENT_IS_CONTAINER (state));
 
4540
 
 
4541
        return state->context->definition->u.start_end.end &&
 
4542
                regex_match (state->context->end,
 
4543
                             line->text,
 
4544
                             line->byte_length,
 
4545
                             pos);
 
4546
}
 
4547
 
 
4548
/**
 
4549
 * ancestor_context_ends_here:
 
4550
 *
 
4551
 * @state: current context.
 
4552
 * @line: the line to analyze.
 
4553
 * @line_pos: the position inside @line, bytes.
 
4554
 *
 
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().
 
4558
 *
 
4559
 * Returns: the ancestor context that terminates here or %NULL.
 
4560
 */
 
4561
static Context *
 
4562
ancestor_context_ends_here (Context                *state,
 
4563
                            LineInfo               *line,
 
4564
                            gint                    line_pos)
 
4565
{
 
4566
        Context *current_context;
 
4567
        GSList *current_context_list;
 
4568
        GSList *check_ancestors;
 
4569
        Context *terminating_context;
 
4570
 
 
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
 
4574
         * depth. */
 
4575
        check_ancestors = NULL;
 
4576
        current_context = state;
 
4577
        while (ANCESTOR_CAN_END_CONTEXT (current_context))
 
4578
        {
 
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;
 
4583
        }
 
4584
 
 
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)
 
4589
        {
 
4590
                current_context = current_context_list->data;
 
4591
 
 
4592
                if (current_context->end &&
 
4593
                    current_context->end->u.regex.regex &&
 
4594
                    regex_match (current_context->end,
 
4595
                                 line->text,
 
4596
                                 line->byte_length,
 
4597
                                 line_pos))
 
4598
                {
 
4599
                        terminating_context = current_context;
 
4600
                        break;
 
4601
                }
 
4602
 
 
4603
                current_context_list = current_context_list->next;
 
4604
        }
 
4605
        g_slist_free (check_ancestors);
 
4606
 
 
4607
        return terminating_context;
 
4608
}
 
4609
 
 
4610
/**
 
4611
 * ancestor_ends_here:
 
4612
 *
 
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.
 
4617
 *
 
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.
 
4622
 *
 
4623
 * Returns: %TRUE if an ancestor ends at the given position.
 
4624
 */
 
4625
static gboolean
 
4626
ancestor_ends_here (Segment                *state,
 
4627
                    LineInfo               *line,
 
4628
                    gint                    line_pos,
 
4629
                    Segment               **new_state)
 
4630
{
 
4631
        Context *terminating_context;
 
4632
 
 
4633
        terminating_context = ancestor_context_ends_here (state->context, line, line_pos);
 
4634
 
 
4635
        if (new_state != NULL && terminating_context != NULL)
 
4636
        {
 
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;
 
4641
 
 
4642
                while (current_segment->context != terminating_context)
 
4643
                        current_segment = current_segment->parent;
 
4644
 
 
4645
                *new_state = current_segment;
 
4646
                g_assert (*new_state != NULL);
 
4647
        }
 
4648
 
 
4649
        return terminating_context != NULL;
 
4650
}
 
4651
 
 
4652
/**
 
4653
 * next_segment:
 
4654
 *
 
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.
 
4661
 *
 
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.
 
4664
 *
 
4665
 * Returns: %FALSE is there are no more contexts in @line.
 
4666
 */
 
4667
static gboolean
 
4668
next_segment (GtkSourceContextEngine  *ce,
 
4669
              Segment                 *state,
 
4670
              LineInfo                *line,
 
4671
              gint                    *line_pos,
 
4672
              Segment                **new_state)
 
4673
{
 
4674
        gint pos = *line_pos;
 
4675
 
 
4676
        g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
 
4677
        g_assert (pos <= line->byte_length);
 
4678
 
 
4679
        while (pos <= line->byte_length)
 
4680
        {
 
4681
                DefinitionsIter def_iter;
 
4682
                gboolean context_end_found;
 
4683
                DefinitionChild *child_def;
 
4684
 
 
4685
                if (state->context->reg_all)
 
4686
                {
 
4687
                        if (!regex_match (state->context->reg_all,
 
4688
                                          line->text,
 
4689
                                          line->byte_length,
 
4690
                                          pos))
 
4691
                        {
 
4692
                                return FALSE;
 
4693
                        }
 
4694
 
 
4695
                        regex_fetch_pos_bytes (state->context->reg_all,
 
4696
                                               0, &pos, NULL);
 
4697
                }
 
4698
 
 
4699
                /* Does an ancestor end here? */
 
4700
                if (ANCESTOR_CAN_END_CONTEXT (state->context) &&
 
4701
                    ancestor_ends_here (state, line, pos, new_state))
 
4702
                {
 
4703
                        g_assert (pos <= line->byte_length);
 
4704
                        segment_extend (state, line_pos_to_offset (line, pos));
 
4705
                        *line_pos = pos;
 
4706
                        return TRUE;
 
4707
                }
 
4708
 
 
4709
                /* Does the current context end here? */
 
4710
                context_end_found = segment_ends_here (state, line, pos);
 
4711
 
 
4712
                /* Iter over the definitions we can find in the current
 
4713
                 * context. */
 
4714
                definition_iter_init (&def_iter, state->context->definition);
 
4715
                while ((child_def = definition_iter_next (&def_iter)) != NULL)
 
4716
                {
 
4717
                        gboolean try_this = TRUE;
 
4718
 
 
4719
                        g_return_val_if_fail (child_def->resolved, FALSE);
 
4720
 
 
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)
 
4725
                                try_this = FALSE;
 
4726
 
 
4727
                        if (HAS_OPTION (child_def->u.definition, FIRST_LINE_ONLY) && line->start_at != 0)
 
4728
                                try_this = FALSE;
 
4729
 
 
4730
                        if (HAS_OPTION (child_def->u.definition, ONCE_ONLY))
 
4731
                        {
 
4732
                                Segment *prev;
 
4733
 
 
4734
                                for (prev = state->children; prev != NULL; prev = prev->next)
 
4735
                                {
 
4736
                                        if (prev->context != NULL &&
 
4737
                                            prev->context->definition == child_def->u.definition)
 
4738
                                        {
 
4739
                                                try_this = FALSE;
 
4740
                                                break;
 
4741
                                        }
 
4742
                                }
 
4743
                        }
 
4744
 
 
4745
                        if (try_this)
 
4746
                        {
 
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))
 
4751
                                {
 
4752
                                        g_assert (pos <= line->byte_length);
 
4753
                                        *line_pos = pos;
 
4754
                                        definition_iter_destroy (&def_iter);
 
4755
                                        return TRUE;
 
4756
                                }
 
4757
                        }
 
4758
 
 
4759
                        /* This child does not start here, so we analyze
 
4760
                         * another definition. */
 
4761
                }
 
4762
                definition_iter_destroy (&def_iter);
 
4763
 
 
4764
                if (context_end_found)
 
4765
                {
 
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()
 
4770
                         * checks this. */
 
4771
                        if (apply_match (state, line, &pos, state->context->end, SUB_PATTERN_WHERE_END))
 
4772
                        {
 
4773
                                g_assert (pos <= line->byte_length);
 
4774
 
 
4775
                                while (SEGMENT_ENDS_PARENT (state))
 
4776
                                        state = state->parent;
 
4777
 
 
4778
                                *new_state = state->parent;
 
4779
                                ce->priv->hint2 = state;
 
4780
                                *line_pos = pos;
 
4781
                                return TRUE;
 
4782
                        }
 
4783
                }
 
4784
 
 
4785
                /* Nothing new at this position, go to next char. */
 
4786
                pos = g_utf8_next_char (line->text + pos) - line->text;
 
4787
        }
 
4788
 
 
4789
        return FALSE;
 
4790
}
 
4791
 
 
4792
/**
 
4793
 * check_line_end:
 
4794
 *
 
4795
 * @state: current state.
 
4796
 * @hint: child of @state used in analyze_line() and next_segment().
 
4797
 *
 
4798
 * Closes the contexts that cannot contain end of lines if needed.
 
4799
 * Updates hint if new state is different from @state.
 
4800
 *
 
4801
 * Returns: the new state.
 
4802
 */
 
4803
static Segment *
 
4804
check_line_end (GtkSourceContextEngine *ce,
 
4805
                Segment                *state)
 
4806
{
 
4807
        Segment *current_segment;
 
4808
        Segment *terminating_segment;
 
4809
 
 
4810
        g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
 
4811
 
 
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;
 
4817
 
 
4818
        while (current_segment != NULL)
 
4819
        {
 
4820
                if (SEGMENT_END_AT_LINE_END (current_segment))
 
4821
                        terminating_segment = current_segment;
 
4822
                else if (!ANCESTOR_CAN_END_CONTEXT(current_segment->context))
 
4823
                        break;
 
4824
                current_segment = current_segment->parent;
 
4825
        }
 
4826
 
 
4827
        if (terminating_segment != NULL)
 
4828
        {
 
4829
                ce->priv->hint2 = terminating_segment;
 
4830
                return terminating_segment->parent;
 
4831
        }
 
4832
        else
 
4833
        {
 
4834
                return state;
 
4835
        }
 
4836
}
 
4837
 
 
4838
static void
 
4839
delete_zero_length_segments (GtkSourceContextEngine *ce,
 
4840
                             GList                  *list)
 
4841
{
 
4842
        while (list != NULL)
 
4843
        {
 
4844
                Segment *s = list->data;
 
4845
 
 
4846
                if (s->start_at == s->end_at)
 
4847
                {
 
4848
                        GList *l;
 
4849
 
 
4850
                        for (l = list->next; l != NULL; )
 
4851
                        {
 
4852
                                GList *next = l->next;
 
4853
                                Segment *s2 = l->data;
 
4854
                                gboolean child = FALSE;
 
4855
 
 
4856
                                while (s2 != NULL)
 
4857
                                {
 
4858
                                        if (s2 == s)
 
4859
                                        {
 
4860
                                                child = TRUE;
 
4861
                                                break;
 
4862
                                        }
 
4863
 
 
4864
                                        s2 = s2->parent;
 
4865
                                }
 
4866
 
 
4867
                                if (child)
 
4868
                                        list = g_list_delete_link (list, l);
 
4869
 
 
4870
                                l = next;
 
4871
                        }
 
4872
 
 
4873
                        if (ce->priv->hint2 != NULL)
 
4874
                        {
 
4875
                                Segment *s2 = ce->priv->hint2;
 
4876
                                gboolean child = FALSE;
 
4877
 
 
4878
                                while (s2 != NULL)
 
4879
                                {
 
4880
                                        if (s2 == s)
 
4881
                                        {
 
4882
                                                child = TRUE;
 
4883
                                                break;
 
4884
                                        }
 
4885
 
 
4886
                                        s2 = s2->parent;
 
4887
                                }
 
4888
 
 
4889
                                if (child)
 
4890
                                        ce->priv->hint2 = s->parent;
 
4891
                        }
 
4892
 
 
4893
                        segment_remove (ce, s);
 
4894
                }
 
4895
 
 
4896
                list = g_list_delete_link (list, list);
 
4897
        }
 
4898
}
 
4899
 
 
4900
/**
 
4901
 * analyze_line:
 
4902
 *
 
4903
 * @ce: #GtkSourceContextEngine.
 
4904
 * @state: the state at the beginning of line.
 
4905
 * @line: the line.
 
4906
 * @hint: a child of @state around start of line, to make it faster.
 
4907
 *
 
4908
 * Finds contexts at the line and updates the syntax tree on it.
 
4909
 *
 
4910
 * Returns: starting state at the next line.
 
4911
 */
 
4912
static Segment *
 
4913
analyze_line (GtkSourceContextEngine *ce,
 
4914
              Segment                *state,
 
4915
              LineInfo               *line)
 
4916
{
 
4917
        gint line_pos = 0;
 
4918
        GList *end_segments = NULL;
 
4919
        GTimer *timer;
 
4920
 
 
4921
        g_assert (SEGMENT_IS_CONTAINER (state));
 
4922
 
 
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);
 
4926
 
 
4927
        timer = g_timer_new ();
 
4928
 
 
4929
        /* Find the contexts in the line. */
 
4930
        while (line_pos <= line->byte_length)
 
4931
        {
 
4932
                Segment *new_state = NULL;
 
4933
 
 
4934
                if (!next_segment (ce, state, line, &line_pos, &new_state))
 
4935
                        break;
 
4936
 
 
4937
                if (g_timer_elapsed (timer, NULL) * 1000 > MAX_TIME_FOR_ONE_LINE)
 
4938
                {
 
4939
                        g_critical ("%s",
 
4940
                                    _("Highlighting a single line took too much time, "
 
4941
                                      "syntax highlighting will be disabled"));
 
4942
                        disable_highlighting (ce);
 
4943
                        break;
 
4944
                }
 
4945
 
 
4946
                g_assert (new_state != NULL);
 
4947
                g_assert (SEGMENT_IS_CONTAINER (new_state));
 
4948
 
 
4949
                state = new_state;
 
4950
 
 
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);
 
4954
 
 
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);
 
4962
        }
 
4963
 
 
4964
        g_timer_destroy (timer);
 
4965
        if (ce->priv->disabled)
 
4966
                return NULL;
 
4967
 
 
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);
 
4971
 
 
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))
 
4976
        {
 
4977
                state = check_line_end (ce, state);
 
4978
        }
 
4979
 
 
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));
 
4983
 
 
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);
 
4987
        else
 
4988
                delete_zero_length_segments (ce, end_segments);
 
4989
 
 
4990
        CHECK_TREE (ce);
 
4991
 
 
4992
        return state;
 
4993
}
 
4994
 
 
4995
/**
 
4996
 * get_line_info:
 
4997
 *
 
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.
 
5003
 *
 
5004
 * Retrieves line text from the buffer, finds line terminator and fills
 
5005
 * @line structure.
 
5006
 */
 
5007
static void
 
5008
get_line_info (GtkTextBuffer     *buffer,
 
5009
               const GtkTextIter *line_start,
 
5010
               const GtkTextIter *line_end,
 
5011
               LineInfo          *line)
 
5012
{
 
5013
        g_assert (!gtk_text_iter_equal (line_start, line_end));
 
5014
 
 
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);
 
5017
 
 
5018
        if (!gtk_text_iter_starts_line (line_end))
 
5019
        {
 
5020
                line->eol_length = 0;
 
5021
                line->char_length = g_utf8_strlen (line->text, -1);
 
5022
                line->byte_length = strlen (line->text);
 
5023
        }
 
5024
        else
 
5025
        {
 
5026
                gint eol_index, next_line_index;
 
5027
 
 
5028
                pango_find_paragraph_boundary (line->text, -1,
 
5029
                                               &eol_index,
 
5030
                                               &next_line_index);
 
5031
 
 
5032
                g_assert (eol_index < next_line_index);
 
5033
 
 
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;
 
5037
        }
 
5038
 
 
5039
        g_assert (gtk_text_iter_get_offset (line_end) ==
 
5040
                        line->start_at + line->char_length + line->eol_length);
 
5041
}
 
5042
 
 
5043
/**
 
5044
 * line_info_destroy:
 
5045
 *
 
5046
 * @line: #LineInfo.
 
5047
 *
 
5048
 * Destroys data allocated by get_line_info().
 
5049
 */
 
5050
static void
 
5051
line_info_destroy (LineInfo *line)
 
5052
{
 
5053
        g_free (line->text);
 
5054
}
 
5055
 
 
5056
/**
 
5057
 * segment_tree_zero_len:
 
5058
 *
 
5059
 * @ce: #GtkSoucreContextEngine.
 
5060
 *
 
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
 
5063
 * the buffer.
 
5064
 */
 
5065
static void
 
5066
segment_tree_zero_len (GtkSourceContextEngine *ce)
 
5067
{
 
5068
        Segment *root = ce->priv->root_segment;
 
5069
        segment_destroy_children (ce, root);
 
5070
        root->start_at = root->end_at = 0;
 
5071
        CHECK_TREE (ce);
 
5072
}
 
5073
 
 
5074
#ifdef ENABLE_CHECK_TREE
 
5075
static Segment *
 
5076
get_segment_at_offset_slow_ (Segment *segment,
 
5077
                             gint     offset)
 
5078
{
 
5079
        Segment *child;
 
5080
 
 
5081
start:
 
5082
        if (segment->parent == NULL && offset == segment->end_at)
 
5083
                return segment;
 
5084
 
 
5085
        if (segment->start_at > offset)
 
5086
        {
 
5087
                g_assert (segment->parent != NULL);
 
5088
                segment = segment->parent;
 
5089
                goto start;
 
5090
        }
 
5091
 
 
5092
        if (segment->start_at == offset)
 
5093
        {
 
5094
                if (segment->children != NULL && segment->children->start_at == offset)
 
5095
                {
 
5096
                        segment = segment->children;
 
5097
                        goto start;
 
5098
                }
 
5099
 
 
5100
                return segment;
 
5101
        }
 
5102
 
 
5103
        if (segment->end_at <= offset && segment->parent != NULL)
 
5104
        {
 
5105
                if (segment->next != NULL)
 
5106
                {
 
5107
                        if (segment->next->start_at > offset)
 
5108
                                return segment->parent;
 
5109
 
 
5110
                        segment = segment->next;
 
5111
                }
 
5112
                else
 
5113
                {
 
5114
                        segment = segment->parent;
 
5115
                }
 
5116
 
 
5117
                goto start;
 
5118
        }
 
5119
 
 
5120
        for (child = segment->children; child != NULL; child = child->next)
 
5121
        {
 
5122
                if (child->start_at == offset)
 
5123
                {
 
5124
                        segment = child;
 
5125
                        goto start;
 
5126
                }
 
5127
 
 
5128
                if (child->end_at <= offset)
 
5129
                        continue;
 
5130
 
 
5131
                if (child->start_at > offset)
 
5132
                        break;
 
5133
 
 
5134
                segment = child;
 
5135
                goto start;
 
5136
        }
 
5137
 
 
5138
        return segment;
 
5139
}
 
5140
#endif /* ENABLE_CHECK_TREE */
 
5141
 
 
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))))
 
5145
static Segment *
 
5146
get_segment_in_ (Segment *segment,
 
5147
                 gint     offset)
 
5148
{
 
5149
        Segment *child;
 
5150
 
 
5151
        g_assert (segment->start_at <= offset && segment->end_at > offset);
 
5152
 
 
5153
        if (segment->children == NULL)
 
5154
                return segment;
 
5155
 
 
5156
        if (segment->children == segment->last_child)
 
5157
        {
 
5158
                if (SEGMENT_IS_ZERO_LEN_AT (segment->children, offset))
 
5159
                        return segment->children;
 
5160
 
 
5161
                if (SEGMENT_CONTAINS (segment->children, offset))
 
5162
                        return get_segment_in_ (segment->children, offset);
 
5163
 
 
5164
                return segment;
 
5165
        }
 
5166
 
 
5167
        if (segment->children->start_at > offset || segment->last_child->end_at < offset)
 
5168
                return segment;
 
5169
 
 
5170
        if (SEGMENT_DISTANCE (segment->children, offset) >= SEGMENT_DISTANCE (segment->last_child, offset))
 
5171
        {
 
5172
                for (child = segment->children; child; child = child->next)
 
5173
                {
 
5174
                        if (child->start_at > offset)
 
5175
                                return segment;
 
5176
 
 
5177
                        if (SEGMENT_IS_ZERO_LEN_AT (child, offset))
 
5178
                                return child;
 
5179
 
 
5180
                        if (SEGMENT_CONTAINS (child, offset))
 
5181
                                return get_segment_in_ (child, offset);
 
5182
                }
 
5183
        }
 
5184
        else
 
5185
        {
 
5186
                for (child = segment->last_child; child; child = child->prev)
 
5187
                {
 
5188
                        if (SEGMENT_IS_ZERO_LEN_AT (child, offset))
 
5189
                        {
 
5190
                                while (child->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (child->prev, offset))
 
5191
                                        child = child->prev;
 
5192
                                return child;
 
5193
                        }
 
5194
 
 
5195
                        if (child->end_at <= offset)
 
5196
                                return segment;
 
5197
 
 
5198
                        if (SEGMENT_CONTAINS (child, offset))
 
5199
                                return get_segment_in_ (child, offset);
 
5200
                }
 
5201
        }
 
5202
 
 
5203
        return segment;
 
5204
}
 
5205
 
 
5206
/* assumes zero-length segments can't have children */
 
5207
static Segment *
 
5208
get_segment_ (Segment *segment,
 
5209
              gint     offset)
 
5210
{
 
5211
        if (segment->parent != NULL)
 
5212
        {
 
5213
                if (!SEGMENT_CONTAINS (segment->parent, offset))
 
5214
                        return get_segment_ (segment->parent, offset);
 
5215
        }
 
5216
        else
 
5217
        {
 
5218
                g_assert (offset >= segment->start_at);
 
5219
                g_assert (offset <= segment->end_at);
 
5220
        }
 
5221
 
 
5222
        if (SEGMENT_CONTAINS (segment, offset))
 
5223
                return get_segment_in_ (segment, offset);
 
5224
 
 
5225
        if (SEGMENT_IS_ZERO_LEN_AT (segment, offset))
 
5226
        {
 
5227
                while (segment->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
 
5228
                        segment = segment->prev;
 
5229
                return segment;
 
5230
        }
 
5231
 
 
5232
        if (offset < segment->start_at)
 
5233
        {
 
5234
                while (segment->prev != NULL && segment->prev->start_at > offset)
 
5235
                        segment = segment->prev;
 
5236
 
 
5237
                g_assert (!segment->prev || segment->prev->start_at <= offset);
 
5238
 
 
5239
                if (segment->prev == NULL)
 
5240
                        return segment->parent;
 
5241
 
 
5242
                if (segment->prev->end_at > offset)
 
5243
                        return get_segment_in_ (segment->prev, offset);
 
5244
 
 
5245
                if (segment->prev->end_at == offset)
 
5246
                {
 
5247
                        if (SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
 
5248
                        {
 
5249
                                segment = segment->prev;
 
5250
                                while (segment->prev != NULL && SEGMENT_IS_ZERO_LEN_AT (segment->prev, offset))
 
5251
                                        segment = segment->prev;
 
5252
                                return segment;
 
5253
                        }
 
5254
 
 
5255
                        return segment->parent;
 
5256
                }
 
5257
 
 
5258
                /* segment->prev->end_at < offset */
 
5259
                return segment->parent;
 
5260
        }
 
5261
 
 
5262
        /* offset >= segment->end_at, not zero-length */
 
5263
 
 
5264
        while (segment->next != NULL)
 
5265
        {
 
5266
                if (SEGMENT_IS_ZERO_LEN_AT (segment->next, offset))
 
5267
                        return segment->next;
 
5268
 
 
5269
                if (segment->next->end_at > offset)
 
5270
                {
 
5271
                        if (segment->next->start_at <= offset)
 
5272
                                return get_segment_in_ (segment->next, offset);
 
5273
                        else
 
5274
                                return segment->parent;
 
5275
                }
 
5276
 
 
5277
                segment = segment->next;
 
5278
        }
 
5279
 
 
5280
        return segment->parent;
 
5281
}
 
5282
#undef SEGMENT_IS_ZERO_LEN_AT
 
5283
#undef SEGMENT_CONTAINS
 
5284
#undef SEGMENT_DISTANCE
 
5285
 
 
5286
/**
 
5287
 * get_segment_at_offset:
 
5288
 *
 
5289
 * @ce: #GtkSoucreContextEngine.
 
5290
 * @hint: segment to start search from or %NULL.
 
5291
 * @offset: the offset, characters.
 
5292
 *
 
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.
 
5299
 */
 
5300
static Segment *
 
5301
get_segment_at_offset (GtkSourceContextEngine *ce,
 
5302
                       Segment                *hint,
 
5303
                       gint                    offset)
 
5304
{
 
5305
        Segment *result;
 
5306
 
 
5307
        if (offset == ce->priv->root_segment->end_at)
 
5308
                return ce->priv->root_segment;
 
5309
 
 
5310
#ifdef ENABLE_DEBUG
 
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)
 
5315
        {
 
5316
                static int c;
 
5317
                g_print ("searching from root %d\n", ++c);
 
5318
        }
 
5319
#endif
 
5320
 
 
5321
        result = get_segment_ (hint ? hint : ce->priv->root_segment, offset);
 
5322
 
 
5323
#ifdef ENABLE_CHECK_TREE
 
5324
        g_assert (result == get_segment_at_offset_slow_ (hint, offset));
 
5325
#endif
 
5326
 
 
5327
        return result;
 
5328
}
 
5329
 
 
5330
/**
 
5331
 * segment_remove:
 
5332
 *
 
5333
 * @ce: #GtkSoucreContextEngine.
 
5334
 * @segment: segment to remove.
 
5335
 *
 
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.
 
5340
 */
 
5341
static void
 
5342
segment_remove (GtkSourceContextEngine *ce,
 
5343
                Segment                *segment)
 
5344
{
 
5345
        if (segment->next != NULL)
 
5346
                segment->next->prev = segment->prev;
 
5347
        else
 
5348
                segment->parent->last_child = segment->prev;
 
5349
 
 
5350
        if (segment->prev != NULL)
 
5351
                segment->prev->next = segment->next;
 
5352
        else
 
5353
                segment->parent->children = segment->next;
 
5354
 
 
5355
        /* if ce->priv->hint is being deleted, set it to some
 
5356
         * neighbour segment */
 
5357
        if (ce->priv->hint == segment)
 
5358
        {
 
5359
                if (segment->next != NULL)
 
5360
                        ce->priv->hint = segment->next;
 
5361
                else if (segment->prev != NULL)
 
5362
                        ce->priv->hint = segment->prev;
 
5363
                else
 
5364
                        ce->priv->hint = segment->parent;
 
5365
        }
 
5366
 
 
5367
        /* if ce->priv->hint2 is being deleted, set it to some
 
5368
         * neighbour segment */
 
5369
        if (ce->priv->hint2 == segment)
 
5370
        {
 
5371
                if (segment->next != NULL)
 
5372
                        ce->priv->hint2 = segment->next;
 
5373
                else if (segment->prev != NULL)
 
5374
                        ce->priv->hint2 = segment->prev;
 
5375
                else
 
5376
                        ce->priv->hint2 = segment->parent;
 
5377
        }
 
5378
 
 
5379
        segment_destroy (ce, segment);
 
5380
}
 
5381
 
 
5382
static void
 
5383
segment_erase_middle_ (GtkSourceContextEngine *ce,
 
5384
                       Segment                *segment,
 
5385
                       gint                    start,
 
5386
                       gint                    end)
 
5387
{
 
5388
        Segment *new_segment, *child;
 
5389
        SubPattern *sp;
 
5390
 
 
5391
        new_segment = segment_new (ce,
 
5392
                                   segment->parent,
 
5393
                                   segment->context,
 
5394
                                   end,
 
5395
                                   segment->end_at,
 
5396
                                   FALSE);
 
5397
        segment->end_at = start;
 
5398
 
 
5399
        new_segment->next = segment->next;
 
5400
        segment->next = new_segment;
 
5401
        new_segment->prev = segment;
 
5402
 
 
5403
        if (new_segment->next != NULL)
 
5404
                new_segment->next->prev = new_segment;
 
5405
        else
 
5406
                new_segment->parent->last_child = new_segment;
 
5407
 
 
5408
        child = segment->children;
 
5409
        segment->children = NULL;
 
5410
        segment->last_child = NULL;
 
5411
 
 
5412
        while (child != NULL)
 
5413
        {
 
5414
                Segment *append_to;
 
5415
                Segment *next = child->next;
 
5416
 
 
5417
                if (child->start_at < start)
 
5418
                {
 
5419
                        g_assert (child->end_at <= start);
 
5420
                        append_to = segment;
 
5421
                }
 
5422
                else
 
5423
                {
 
5424
                        g_assert (child->start_at >= end);
 
5425
                        append_to = new_segment;
 
5426
                }
 
5427
 
 
5428
                child->parent = append_to;
 
5429
 
 
5430
                if (append_to->last_child != NULL)
 
5431
                {
 
5432
                        append_to->last_child->next = child;
 
5433
                        child->prev = append_to->last_child;
 
5434
                        child->next = NULL;
 
5435
                        append_to->last_child = child;
 
5436
                }
 
5437
                else
 
5438
                {
 
5439
                        child->next = child->prev = NULL;
 
5440
                        append_to->last_child = child;
 
5441
                        append_to->children = child;
 
5442
                }
 
5443
 
 
5444
                child = next;
 
5445
        }
 
5446
 
 
5447
        sp = segment->sub_patterns;
 
5448
        segment->sub_patterns = NULL;
 
5449
 
 
5450
        while (sp != NULL)
 
5451
        {
 
5452
                SubPattern *next = sp->next;
 
5453
                Segment *append_to;
 
5454
 
 
5455
                if (sp->start_at < start)
 
5456
                {
 
5457
                        sp->end_at = MIN (sp->end_at, start);
 
5458
                        append_to = segment;
 
5459
                }
 
5460
                else
 
5461
                {
 
5462
                        g_assert (sp->end_at > end);
 
5463
                        sp->start_at = MAX (sp->start_at, end);
 
5464
                        append_to = new_segment;
 
5465
                }
 
5466
 
 
5467
                sp->next = append_to->sub_patterns;
 
5468
                append_to->sub_patterns = sp;
 
5469
 
 
5470
                sp = next;
 
5471
        }
 
5472
 
 
5473
        CHECK_SEGMENT_CHILDREN (segment);
 
5474
        CHECK_SEGMENT_CHILDREN (new_segment);
 
5475
}
 
5476
 
 
5477
/**
 
5478
 * segment_erase_range_:
 
5479
 *
 
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.
 
5484
 *
 
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.
 
5492
 */
 
5493
static void
 
5494
segment_erase_range_ (GtkSourceContextEngine *ce,
 
5495
                      Segment                *segment,
 
5496
                      gint                    start,
 
5497
                      gint                    end)
 
5498
{
 
5499
        g_assert (start < end);
 
5500
 
 
5501
        if (segment->start_at == segment->end_at)
 
5502
        {
 
5503
                if (segment->start_at >= start && segment->start_at <= end)
 
5504
                        segment_remove (ce, segment);
 
5505
                return;
 
5506
        }
 
5507
 
 
5508
        if (segment->start_at > end || segment->end_at < start)
 
5509
                return;
 
5510
 
 
5511
        if (segment->start_at >= start && segment->end_at <= end && segment->parent)
 
5512
        {
 
5513
                segment_remove (ce, segment);
 
5514
                return;
 
5515
        }
 
5516
 
 
5517
        if (segment->start_at == end)
 
5518
        {
 
5519
                Segment *child = segment->children;
 
5520
 
 
5521
                while (child != NULL && child->start_at == end)
 
5522
                {
 
5523
                        Segment *next = child->next;
 
5524
                        segment_erase_range_ (ce, child, start, end);
 
5525
                        child = next;
 
5526
                }
 
5527
        }
 
5528
        else if (segment->end_at == start)
 
5529
        {
 
5530
                Segment *child = segment->last_child;
 
5531
 
 
5532
                while (child != NULL && child->end_at == start)
 
5533
                {
 
5534
                        Segment *prev = child->prev;
 
5535
                        segment_erase_range_ (ce, child, start, end);
 
5536
                        child = prev;
 
5537
                }
 
5538
        }
 
5539
        else
 
5540
        {
 
5541
                Segment *child = segment->children;
 
5542
 
 
5543
                while (child != NULL)
 
5544
                {
 
5545
                        Segment *next = child->next;
 
5546
                        segment_erase_range_ (ce, child, start, end);
 
5547
                        child = next;
 
5548
                }
 
5549
        }
 
5550
 
 
5551
        if (segment->sub_patterns != NULL)
 
5552
        {
 
5553
                SubPattern *sp;
 
5554
 
 
5555
                sp = segment->sub_patterns;
 
5556
                segment->sub_patterns = NULL;
 
5557
 
 
5558
                while (sp != NULL)
 
5559
                {
 
5560
                        SubPattern *next = sp->next;
 
5561
 
 
5562
                        if (sp->start_at >= start && sp->end_at <= end)
 
5563
                                sub_pattern_free (sp);
 
5564
                        else
 
5565
                                segment_add_subpattern (segment, sp);
 
5566
 
 
5567
                        sp = next;
 
5568
                }
 
5569
        }
 
5570
 
 
5571
        if (segment->parent != NULL)
 
5572
        {
 
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)
 
5577
                {
 
5578
                        segment_erase_middle_ (ce, segment, start, end);
 
5579
                }
 
5580
                else
 
5581
                {
 
5582
                        g_assert ((segment->start_at >= start && segment->end_at > end) ||
 
5583
                                  (segment->start_at < start && segment->end_at <= end));
 
5584
 
 
5585
                        if (segment->end_at > end)
 
5586
                        {
 
5587
                                /* If we erase the beginning, we need to clear
 
5588
                                 * is_start flag. */
 
5589
                                segment->start_at = end;
 
5590
                                segment->is_start = FALSE;
 
5591
                        }
 
5592
                        else
 
5593
                        {
 
5594
                                segment->end_at = start;
 
5595
                        }
 
5596
                }
 
5597
        }
 
5598
}
 
5599
 
 
5600
/**
 
5601
 * segment_merge:
 
5602
 *
 
5603
 * @ce: #GtkSourceContextEngine.
 
5604
 * @first: first segment.
 
5605
 * @second: second segment.
 
5606
 *
 
5607
 * Merges adjacent segments @first and @second given
 
5608
 * their contexts are equal.
 
5609
 */
 
5610
static void
 
5611
segment_merge (GtkSourceContextEngine *ce,
 
5612
               Segment                *first,
 
5613
               Segment                *second)
 
5614
{
 
5615
        Segment *parent;
 
5616
 
 
5617
        if (first == second)
 
5618
                return;
 
5619
 
 
5620
        g_assert (!SEGMENT_IS_INVALID (first));
 
5621
        g_assert (first->context == second->context);
 
5622
        g_assert (first->end_at == second->start_at);
 
5623
 
 
5624
        if (first->parent != second->parent)
 
5625
                segment_merge (ce, first->parent, second->parent);
 
5626
 
 
5627
        parent = first->parent;
 
5628
 
 
5629
        g_assert (first->next == second);
 
5630
        g_assert (first->parent == second->parent);
 
5631
        g_assert (second != parent->children);
 
5632
 
 
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;
 
5638
 
 
5639
        first->end_at = second->end_at;
 
5640
 
 
5641
        if (second->children != NULL)
 
5642
        {
 
5643
                Segment *child;
 
5644
 
 
5645
                for (child = second->children; child != NULL; child = child->next)
 
5646
                        child->parent = first;
 
5647
 
 
5648
                if (first->children == NULL)
 
5649
                {
 
5650
                        g_assert (!first->last_child);
 
5651
                        first->children = second->children;
 
5652
                        first->last_child = second->last_child;
 
5653
                }
 
5654
                else
 
5655
                {
 
5656
                        first->last_child->next = second->children;
 
5657
                        second->children->prev = first->last_child;
 
5658
                        first->last_child = second->last_child;
 
5659
                }
 
5660
        }
 
5661
 
 
5662
        if (second->sub_patterns != NULL)
 
5663
        {
 
5664
                if (first->sub_patterns == NULL)
 
5665
                {
 
5666
                        first->sub_patterns = second->sub_patterns;
 
5667
                }
 
5668
                else
 
5669
                {
 
5670
                        while (second->sub_patterns != NULL)
 
5671
                        {
 
5672
                                SubPattern *sp = second->sub_patterns;
 
5673
                                second->sub_patterns = sp->next;
 
5674
                                sp->next = first->sub_patterns;
 
5675
                                first->sub_patterns = sp;
 
5676
                        }
 
5677
                }
 
5678
        }
 
5679
 
 
5680
        second->children = NULL;
 
5681
        second->last_child = NULL;
 
5682
        second->sub_patterns = NULL;
 
5683
 
 
5684
        segment_destroy (ce, second);
 
5685
}
 
5686
 
 
5687
/**
 
5688
 * erase_segments:
 
5689
 *
 
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.
 
5694
 *
 
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).
 
5699
 */
 
5700
static void
 
5701
erase_segments (GtkSourceContextEngine *ce,
 
5702
                gint                    start,
 
5703
                gint                    end,
 
5704
                Segment                *hint)
 
5705
{
 
5706
        Segment *root = ce->priv->root_segment;
 
5707
        Segment *child, *hint_prev;
 
5708
 
 
5709
        if (root->children == NULL)
 
5710
                return;
 
5711
 
 
5712
        if (hint == NULL)
 
5713
                hint = ce->priv->hint;
 
5714
 
 
5715
        if (hint != NULL)
 
5716
                while (hint != NULL && hint->parent != ce->priv->root_segment)
 
5717
                        hint = hint->parent;
 
5718
 
 
5719
        if (hint == NULL)
 
5720
                hint = root->children;
 
5721
 
 
5722
        hint_prev = hint->prev;
 
5723
 
 
5724
        child = hint;
 
5725
        while (child != NULL)
 
5726
        {
 
5727
                Segment *next = child->next;
 
5728
 
 
5729
                if (child->end_at < start)
 
5730
                {
 
5731
                        child = next;
 
5732
 
 
5733
                        if (next != NULL)
 
5734
                                ce->priv->hint = next;
 
5735
 
 
5736
                        continue;
 
5737
                }
 
5738
 
 
5739
                if (child->start_at > end)
 
5740
                {
 
5741
                        ce->priv->hint = child;
 
5742
                        break;
 
5743
                }
 
5744
 
 
5745
                segment_erase_range_ (ce, child, start, end);
 
5746
                child = next;
 
5747
        }
 
5748
 
 
5749
        child = hint_prev;
 
5750
        while (child != NULL)
 
5751
        {
 
5752
                Segment *prev = child->prev;
 
5753
 
 
5754
                if (ce->priv->hint == NULL)
 
5755
                        ce->priv->hint = child;
 
5756
 
 
5757
                if (child->start_at > end)
 
5758
                {
 
5759
                        child = prev;
 
5760
                        continue;
 
5761
                }
 
5762
 
 
5763
                if (child->end_at < start)
 
5764
                {
 
5765
                        break;
 
5766
                }
 
5767
 
 
5768
                segment_erase_range_ (ce, child, start, end);
 
5769
                child = prev;
 
5770
        }
 
5771
 
 
5772
        CHECK_TREE (ce);
 
5773
}
 
5774
 
 
5775
/**
 
5776
 * update_syntax:
 
5777
 *
 
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'.
 
5782
 *
 
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).
 
5790
 */
 
5791
/* XXX it must be refactored. */
 
5792
static void
 
5793
update_syntax (GtkSourceContextEngine *ce,
 
5794
               const GtkTextIter      *end,
 
5795
               gint                    time)
 
5796
{
 
5797
        Segment *invalid;
 
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;
 
5802
        gint analyzed_end;
 
5803
        GtkTextBuffer *buffer = ce->priv->buffer;
 
5804
        Segment *state = ce->priv->root_segment;
 
5805
        GTimer *timer;
 
5806
 
 
5807
        context_freeze (ce->priv->root_context);
 
5808
        update_tree (ce);
 
5809
 
 
5810
        if (!gtk_text_buffer_get_char_count (buffer))
 
5811
        {
 
5812
                segment_tree_zero_len (ce);
 
5813
                goto out;
 
5814
        }
 
5815
 
 
5816
        invalid = get_invalid_segment (ce);
 
5817
 
 
5818
        if (invalid == NULL)
 
5819
                goto out;
 
5820
 
 
5821
        if (end != NULL && invalid->start_at >= gtk_text_iter_get_offset (end))
 
5822
                goto out;
 
5823
 
 
5824
        if (end != NULL)
 
5825
        {
 
5826
                end_offset = gtk_text_iter_get_offset (end);
 
5827
                start_offset = MIN (end_offset, invalid->start_at);
 
5828
        }
 
5829
        else
 
5830
        {
 
5831
                start_offset = invalid->start_at;
 
5832
                end_offset = gtk_text_buffer_get_char_count (buffer);
 
5833
        }
 
5834
 
 
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);
 
5837
 
 
5838
        if (!gtk_text_iter_starts_line (&start_iter))
 
5839
        {
 
5840
                gtk_text_iter_set_line_offset (&start_iter, 0);
 
5841
                start_offset = gtk_text_iter_get_offset (&start_iter);
 
5842
        }
 
5843
 
 
5844
        if (!gtk_text_iter_starts_line (&end_iter))
 
5845
        {
 
5846
                gtk_text_iter_forward_line (&end_iter);
 
5847
                end_offset = gtk_text_iter_get_offset (&end_iter);
 
5848
        }
 
5849
 
 
5850
        /* This happens after deleting all text on last line. */
 
5851
        if (start_offset == end_offset)
 
5852
        {
 
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);
 
5856
                CHECK_TREE (ce);
 
5857
                goto out;
 
5858
        }
 
5859
 
 
5860
 
 
5861
        /* Main loop */
 
5862
 
 
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;
 
5869
 
 
5870
        timer = g_timer_new ();
 
5871
 
 
5872
        while (TRUE)
 
5873
        {
 
5874
                LineInfo line;
 
5875
                gboolean next_line_invalid = FALSE;
 
5876
                gboolean need_invalidate_next = FALSE;
 
5877
 
 
5878
                /* Last buffer line. */
 
5879
                if (line_start_offset == line_end_offset)
 
5880
                {
 
5881
                        g_assert (line_start_offset == gtk_text_buffer_get_char_count (buffer));
 
5882
                        break;
 
5883
                }
 
5884
 
 
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);
 
5888
 
 
5889
#ifdef ENABLE_CHECK_TREE
 
5890
                {
 
5891
                        Segment *inv = get_invalid_segment (ce);
 
5892
                        g_assert (inv == NULL || inv->start_at >= line_end_offset);
 
5893
                }
 
5894
#endif
 
5895
 
 
5896
                if (line_start_offset == 0)
 
5897
                        state = ce->priv->root_segment;
 
5898
                else
 
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);
 
5903
 
 
5904
                ce->priv->hint2 = ce->priv->hint;
 
5905
 
 
5906
                if (ce->priv->hint2 != NULL && ce->priv->hint2->parent != state)
 
5907
                        ce->priv->hint2 = NULL;
 
5908
 
 
5909
                state = analyze_line (ce, state, &line);
 
5910
 
 
5911
                /* At this point analyze_line() could have disabled highlighting */
 
5912
                if (ce->priv->disabled)
 
5913
                        return;
 
5914
 
 
5915
#ifdef ENABLE_CHECK_TREE
 
5916
                {
 
5917
                        Segment *inv = get_invalid_segment (ce);
 
5918
                        g_assert (inv == NULL || inv->start_at >= line_end_offset);
 
5919
                }
 
5920
#endif
 
5921
 
 
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;
 
5927
                else
 
5928
                        ce->priv->hint = state;
 
5929
 
 
5930
                line_info_destroy (&line);
 
5931
 
 
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);
 
5935
 
 
5936
                if (invalid != NULL)
 
5937
                {
 
5938
                        GtkTextIter iter;
 
5939
 
 
5940
                        gtk_text_buffer_get_iter_at_offset (buffer, &iter, invalid->start_at);
 
5941
                        gtk_text_iter_set_line_offset (&iter, 0);
 
5942
 
 
5943
                        if (gtk_text_iter_get_offset (&iter) == line_end_offset)
 
5944
                                next_line_invalid = TRUE;
 
5945
                }
 
5946
 
 
5947
                if (!next_line_invalid)
 
5948
                {
 
5949
                        Segment *old_state, *hint;
 
5950
 
 
5951
                        hint = ce->priv->hint ? ce->priv->hint : state;
 
5952
                        old_state = get_segment_at_offset (ce, hint, line_end_offset);
 
5953
 
 
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))
 
5959
                        {
 
5960
                                need_invalidate_next = TRUE;
 
5961
                                next_line_invalid = TRUE;
 
5962
                        }
 
5963
                        else
 
5964
                        {
 
5965
                                segment_merge (ce, state, old_state);
 
5966
                                CHECK_TREE (ce);
 
5967
                        }
 
5968
                }
 
5969
 
 
5970
                if ((time != 0 && g_timer_elapsed (timer, NULL) * 1000 > time) ||
 
5971
                    line_end_offset >= end_offset ||
 
5972
                    (invalid == NULL && !next_line_invalid))
 
5973
                {
 
5974
                        if (need_invalidate_next)
 
5975
                                insert_range (ce, line_end_offset, 0);
 
5976
                        break;
 
5977
                }
 
5978
 
 
5979
                if (next_line_invalid)
 
5980
                {
 
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);
 
5985
                }
 
5986
                else
 
5987
                {
 
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);
 
5994
                }
 
5995
        }
 
5996
 
 
5997
        if (analyzed_end == gtk_text_buffer_get_char_count (buffer))
 
5998
        {
 
5999
                g_assert (g_slist_length (ce->priv->invalid) <= 1);
 
6000
 
 
6001
                if (ce->priv->invalid != NULL)
 
6002
                {
 
6003
                        invalid = get_invalid_segment (ce);
 
6004
                        segment_remove (ce, invalid);
 
6005
                        CHECK_TREE (ce);
 
6006
                }
 
6007
        }
 
6008
 
 
6009
        if (!all_analyzed (ce))
 
6010
                install_idle_worker (ce);
 
6011
 
 
6012
        gtk_text_iter_set_offset (&end_iter, analyzed_end);
 
6013
        refresh_range (ce, &start_iter, &end_iter, FALSE);
 
6014
 
 
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));
 
6018
 
 
6019
        g_timer_destroy (timer);
 
6020
 
 
6021
out:
 
6022
        /* must call context_thaw, so this is the only return point */
 
6023
        context_thaw (ce->priv->root_context);
 
6024
}
 
6025
 
 
6026
 
 
6027
/* DEFINITIONS MANAGEMENT ------------------------------------------------- */
 
6028
 
 
6029
static DefinitionChild *
 
6030
definition_child_new (ContextDefinition *definition,
 
6031
                      const gchar       *child_id,
 
6032
                      const gchar       *style,
 
6033
                      gboolean           override_style,
 
6034
                      gboolean           is_ref_all,
 
6035
                      gboolean           original_ref)
 
6036
{
 
6037
        DefinitionChild *ch;
 
6038
 
 
6039
        g_return_val_if_fail (child_id != NULL, NULL);
 
6040
 
 
6041
        ch = g_slice_new0 (DefinitionChild);
 
6042
 
 
6043
        if (original_ref)
 
6044
                ch->u.id = g_strdup_printf ("@%s", child_id);
 
6045
        else
 
6046
                ch->u.id = g_strdup (child_id);
 
6047
 
 
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);
 
6053
 
 
6054
        definition->children = g_slist_append (definition->children, ch);
 
6055
 
 
6056
        return ch;
 
6057
}
 
6058
 
 
6059
static void
 
6060
definition_child_free (DefinitionChild *ch)
 
6061
{
 
6062
        if (!ch->resolved)
 
6063
                g_free (ch->u.id);
 
6064
        g_free (ch->style);
 
6065
 
 
6066
#ifdef ENABLE_DEBUG
 
6067
        memset (ch, 1, sizeof (DefinitionChild));
 
6068
#else
 
6069
        g_slice_free (DefinitionChild, ch);
 
6070
#endif
 
6071
}
 
6072
 
 
6073
static GSList *
 
6074
copy_context_classes (GSList *context_classes)
 
6075
{
 
6076
        GSList *ret = NULL;
 
6077
 
 
6078
        while (context_classes)
 
6079
        {
 
6080
                ret = g_slist_prepend (ret, gtk_source_context_class_copy (context_classes->data));
 
6081
                context_classes = g_slist_next (context_classes);
 
6082
        }
 
6083
 
 
6084
        return g_slist_reverse (ret);
 
6085
}
 
6086
 
 
6087
static ContextDefinition *
 
6088
context_definition_new (const gchar        *id,
 
6089
                        ContextType         type,
 
6090
                        const gchar        *match,
 
6091
                        const gchar        *start,
 
6092
                        const gchar        *end,
 
6093
                        const gchar        *style,
 
6094
                        GSList             *context_classes,
 
6095
                        GtkSourceContextFlags flags,
 
6096
                        GError            **error)
 
6097
{
 
6098
        ContextDefinition *definition;
 
6099
        gboolean regex_error = FALSE;
 
6100
        gboolean unresolved_error = FALSE;
 
6101
 
 
6102
        g_return_val_if_fail (id != NULL, NULL);
 
6103
 
 
6104
        switch (type)
 
6105
        {
 
6106
                case CONTEXT_TYPE_SIMPLE:
 
6107
                        g_return_val_if_fail (match != NULL, NULL);
 
6108
                        g_return_val_if_fail (!end && !start, NULL);
 
6109
                        break;
 
6110
                case CONTEXT_TYPE_CONTAINER:
 
6111
                        g_return_val_if_fail (!match, NULL);
 
6112
                        g_return_val_if_fail (!end || start, NULL);
 
6113
                        break;
 
6114
        }
 
6115
 
 
6116
        definition = g_slice_new0 (ContextDefinition);
 
6117
 
 
6118
        if (match != NULL)
 
6119
        {
 
6120
                definition->u.match = regex_new (match, G_REGEX_ANCHORED, error);
 
6121
 
 
6122
                if (definition->u.match == NULL)
 
6123
                {
 
6124
                        regex_error = TRUE;
 
6125
                }
 
6126
                else if (!definition->u.match->resolved)
 
6127
                {
 
6128
                        regex_error = TRUE;
 
6129
                        unresolved_error = TRUE;
 
6130
                        regex_unref (definition->u.match);
 
6131
                        definition->u.match = NULL;
 
6132
                }
 
6133
        }
 
6134
 
 
6135
        if (start != NULL)
 
6136
        {
 
6137
                definition->u.start_end.start = regex_new (start, G_REGEX_ANCHORED, error);
 
6138
 
 
6139
                if (definition->u.start_end.start == NULL)
 
6140
                {
 
6141
                        regex_error = TRUE;
 
6142
                }
 
6143
                else if (!definition->u.start_end.start->resolved)
 
6144
                {
 
6145
                        regex_error = TRUE;
 
6146
                        unresolved_error = TRUE;
 
6147
                        regex_unref (definition->u.start_end.start);
 
6148
                        definition->u.start_end.start = NULL;
 
6149
                }
 
6150
        }
 
6151
 
 
6152
        if (end != NULL && !regex_error)
 
6153
        {
 
6154
                definition->u.start_end.end = regex_new (end, G_REGEX_ANCHORED, error);
 
6155
 
 
6156
                if (definition->u.start_end.end == NULL)
 
6157
                        regex_error = TRUE;
 
6158
        }
 
6159
 
 
6160
        if (unresolved_error)
 
6161
        {
 
6162
                g_set_error (error,
 
6163
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR,
 
6164
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_START_REF,
 
6165
                             _("context '%s' cannot contain a \\%%{...@start} command"),
 
6166
                             id);
 
6167
                regex_error = TRUE;
 
6168
        }
 
6169
 
 
6170
        if (regex_error)
 
6171
        {
 
6172
                g_slice_free (ContextDefinition, definition);
 
6173
                return NULL;
 
6174
        }
 
6175
 
 
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;
 
6184
 
 
6185
        definition->context_classes = copy_context_classes (context_classes);
 
6186
 
 
6187
        return definition;
 
6188
}
 
6189
 
 
6190
static ContextDefinition *
 
6191
context_definition_ref (ContextDefinition *definition)
 
6192
{
 
6193
        g_return_val_if_fail (definition != NULL, NULL);
 
6194
        definition->ref_count += 1;
 
6195
        return definition;
 
6196
}
 
6197
 
 
6198
static void
 
6199
context_definition_unref (ContextDefinition *definition)
 
6200
{
 
6201
        GSList *sub_pattern_list;
 
6202
 
 
6203
        if (definition == NULL || --definition->ref_count != 0)
 
6204
                return;
 
6205
 
 
6206
        switch (definition->type)
 
6207
        {
 
6208
                case CONTEXT_TYPE_SIMPLE:
 
6209
                        regex_unref (definition->u.match);
 
6210
                        break;
 
6211
                case CONTEXT_TYPE_CONTAINER:
 
6212
                        regex_unref (definition->u.start_end.start);
 
6213
                        regex_unref (definition->u.start_end.end);
 
6214
                        break;
 
6215
        }
 
6216
 
 
6217
        sub_pattern_list = definition->sub_patterns;
 
6218
        while (sub_pattern_list != NULL)
 
6219
        {
 
6220
                SubPatternDefinition *sp_def = sub_pattern_list->data;
 
6221
#ifdef NEED_DEBUG_ID
 
6222
                g_free (sp_def->id);
 
6223
#endif
 
6224
                g_free (sp_def->style);
 
6225
                if (sp_def->is_named)
 
6226
                        g_free (sp_def->u.name);
 
6227
 
 
6228
                g_slist_foreach (sp_def->context_classes, (GFunc) gtk_source_context_class_free, NULL);
 
6229
                g_slist_free (sp_def->context_classes);
 
6230
 
 
6231
                g_slice_free (SubPatternDefinition, sp_def);
 
6232
                sub_pattern_list = sub_pattern_list->next;
 
6233
        }
 
6234
        g_slist_free (definition->sub_patterns);
 
6235
 
 
6236
        g_free (definition->id);
 
6237
        g_free (definition->default_style);
 
6238
        regex_unref (definition->reg_all);
 
6239
 
 
6240
        g_slist_foreach (definition->context_classes, (GFunc) gtk_source_context_class_free, NULL);
 
6241
        g_slist_free (definition->context_classes);
 
6242
 
 
6243
        g_slist_foreach (definition->children, (GFunc) definition_child_free, NULL);
 
6244
        g_slist_free (definition->children);
 
6245
        g_slice_free (ContextDefinition, definition);
 
6246
}
 
6247
 
 
6248
static void
 
6249
definition_iter_init (DefinitionsIter   *iter,
 
6250
                      ContextDefinition *definition)
 
6251
{
 
6252
        iter->children_stack = g_slist_prepend (NULL, definition->children);
 
6253
}
 
6254
 
 
6255
static void
 
6256
definition_iter_destroy (DefinitionsIter *iter)
 
6257
{
 
6258
        g_slist_free (iter->children_stack);
 
6259
}
 
6260
 
 
6261
static DefinitionChild *
 
6262
definition_iter_next (DefinitionsIter *iter)
 
6263
{
 
6264
        GSList *children_list;
 
6265
 
 
6266
        if (iter->children_stack == NULL)
 
6267
                return NULL;
 
6268
 
 
6269
        children_list = iter->children_stack->data;
 
6270
        if (children_list == NULL)
 
6271
        {
 
6272
                iter->children_stack = g_slist_delete_link (iter->children_stack,
 
6273
                                                            iter->children_stack);
 
6274
                return definition_iter_next (iter);
 
6275
        }
 
6276
        else
 
6277
        {
 
6278
                DefinitionChild *curr_child = children_list->data;
 
6279
                ContextDefinition *definition = curr_child->u.definition;
 
6280
 
 
6281
                g_return_val_if_fail (curr_child->resolved, NULL);
 
6282
 
 
6283
                children_list = g_slist_next (children_list);
 
6284
                iter->children_stack->data = children_list;
 
6285
 
 
6286
                if (curr_child->is_ref_all)
 
6287
                {
 
6288
                        iter->children_stack = g_slist_prepend (iter->children_stack,
 
6289
                                                                definition->children);
 
6290
                        return definition_iter_next (iter);
 
6291
                }
 
6292
                else
 
6293
                {
 
6294
                        return curr_child;
 
6295
                }
 
6296
        }
 
6297
}
 
6298
 
 
6299
gboolean
 
6300
_gtk_source_context_data_define_context (GtkSourceContextData *ctx_data,
 
6301
                                         const gchar          *id,
 
6302
                                         const gchar          *parent_id,
 
6303
                                         const gchar          *match_regex,
 
6304
                                         const gchar          *start_regex,
 
6305
                                         const gchar          *end_regex,
 
6306
                                         const gchar          *style,
 
6307
                                         GSList               *context_classes,
 
6308
                                         GtkSourceContextFlags flags,
 
6309
                                         GError              **error)
 
6310
{
 
6311
        ContextDefinition *definition, *parent = NULL;
 
6312
        ContextType type;
 
6313
        gchar *original_id;
 
6314
        gboolean wrong_args = FALSE;
 
6315
 
 
6316
        g_return_val_if_fail (ctx_data != NULL, FALSE);
 
6317
        g_return_val_if_fail (id != NULL, FALSE);
 
6318
 
 
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
 
6321
         * XML lang file) */
 
6322
        if (LOOKUP_DEFINITION (ctx_data, id) != NULL)
 
6323
        {
 
6324
                g_set_error (error,
 
6325
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR,
 
6326
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID,
 
6327
                             _("duplicated context id '%s'"), id);
 
6328
                return FALSE;
 
6329
        }
 
6330
 
 
6331
        if (match_regex != NULL)
 
6332
                type = CONTEXT_TYPE_SIMPLE;
 
6333
        else
 
6334
                type = CONTEXT_TYPE_CONTAINER;
 
6335
 
 
6336
        /* Check if the arguments passed are exactly what we expect, no more, no less. */
 
6337
        switch (type)
 
6338
        {
 
6339
                case CONTEXT_TYPE_SIMPLE:
 
6340
                        if (start_regex != NULL || end_regex != NULL)
 
6341
                                wrong_args = TRUE;
 
6342
                        break;
 
6343
                case CONTEXT_TYPE_CONTAINER:
 
6344
                        if (match_regex != NULL)
 
6345
                                wrong_args = TRUE;
 
6346
                        break;
 
6347
        }
 
6348
 
 
6349
        if (wrong_args)
 
6350
        {
 
6351
                g_set_error (error,
 
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);
 
6357
                return FALSE;
 
6358
        }
 
6359
 
 
6360
        if (parent_id == NULL)
 
6361
        {
 
6362
                parent = NULL;
 
6363
        }
 
6364
        else
 
6365
        {
 
6366
                parent = LOOKUP_DEFINITION (ctx_data, parent_id);
 
6367
                g_return_val_if_fail (parent != NULL, FALSE);
 
6368
        }
 
6369
 
 
6370
        definition = context_definition_new (id, type, match_regex,
 
6371
                                             start_regex, end_regex, style,
 
6372
                                             context_classes,
 
6373
                                             flags, error);
 
6374
        if (definition == NULL)
 
6375
                return FALSE;
 
6376
 
 
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));
 
6381
 
 
6382
        if (parent != NULL)
 
6383
                definition_child_new (parent, id, NULL, FALSE, FALSE, FALSE);
 
6384
 
 
6385
        return TRUE;
 
6386
}
 
6387
 
 
6388
gboolean
 
6389
_gtk_source_context_data_add_sub_pattern (GtkSourceContextData *ctx_data,
 
6390
                                          const gchar          *id,
 
6391
                                          const gchar          *parent_id,
 
6392
                                          const gchar          *name,
 
6393
                                          const gchar          *where,
 
6394
                                          const gchar          *style,
 
6395
                                          GSList               *context_classes,
 
6396
                                          GError              **error)
 
6397
{
 
6398
        ContextDefinition *parent;
 
6399
        SubPatternDefinition *sp_def;
 
6400
        SubPatternWhere where_num;
 
6401
        gint number;
 
6402
 
 
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);
 
6407
 
 
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
 
6410
         * XML lang file) */
 
6411
        if (LOOKUP_DEFINITION (ctx_data, id) != NULL)
 
6412
        {
 
6413
                g_set_error (error,
 
6414
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR,
 
6415
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR_DUPLICATED_ID,
 
6416
                             _("duplicated context id '%s'"), id);
 
6417
                return FALSE;
 
6418
        }
 
6419
 
 
6420
        parent = LOOKUP_DEFINITION (ctx_data, parent_id);
 
6421
        g_return_val_if_fail (parent != NULL, FALSE);
 
6422
 
 
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;
 
6429
        else
 
6430
                where_num = (SubPatternWhere) -1;
 
6431
 
 
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))
 
6434
        {
 
6435
                where_num = (SubPatternWhere) -1;
 
6436
        }
 
6437
 
 
6438
        if (where_num == (SubPatternWhere) -1)
 
6439
        {
 
6440
                g_set_error (error,
 
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'",
 
6445
                             where, id);
 
6446
                return FALSE;
 
6447
        }
 
6448
 
 
6449
        sp_def = g_slice_new0 (SubPatternDefinition);
 
6450
#ifdef NEED_DEBUG_ID
 
6451
        sp_def->id = g_strdup (id);
 
6452
#endif
 
6453
        sp_def->style = g_strdup (style);
 
6454
        sp_def->where = where_num;
 
6455
        number = sub_pattern_to_int (name);
 
6456
 
 
6457
        if (number < 0)
 
6458
        {
 
6459
                sp_def->is_named = TRUE;
 
6460
                sp_def->u.name = g_strdup (name);
 
6461
        }
 
6462
        else
 
6463
        {
 
6464
                sp_def->is_named = FALSE;
 
6465
                sp_def->u.num = number;
 
6466
        }
 
6467
 
 
6468
        parent->sub_patterns = g_slist_append (parent->sub_patterns, sp_def);
 
6469
        sp_def->index = parent->n_sub_patterns++;
 
6470
 
 
6471
        sp_def->context_classes = copy_context_classes (context_classes);
 
6472
 
 
6473
        return TRUE;
 
6474
}
 
6475
 
 
6476
/**
 
6477
 * context_is_pure_container:
 
6478
 *
 
6479
 * @def: context definition.
 
6480
 *
 
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:*).
 
6484
 */
 
6485
static gboolean
 
6486
context_is_pure_container (ContextDefinition *def)
 
6487
{
 
6488
        return def->type == CONTEXT_TYPE_CONTAINER &&
 
6489
                def->u.start_end.start == NULL;
 
6490
}
 
6491
 
 
6492
gboolean
 
6493
_gtk_source_context_data_add_ref (GtkSourceContextData *ctx_data,
 
6494
                                  const gchar          *parent_id,
 
6495
                                  const gchar          *ref_id,
 
6496
                                  GtkSourceContextRefOptions options,
 
6497
                                  const gchar          *style,
 
6498
                                  gboolean              all,
 
6499
                                  GError              **error)
 
6500
{
 
6501
        ContextDefinition *parent;
 
6502
        ContextDefinition *ref;
 
6503
        gboolean override_style = FALSE;
 
6504
 
 
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);
 
6508
 
 
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);
 
6512
 
 
6513
        if (parent->type != CONTEXT_TYPE_CONTAINER)
 
6514
        {
 
6515
                g_set_error (error,
 
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'",
 
6520
                             ref_id);
 
6521
                return FALSE;
 
6522
        }
 
6523
 
 
6524
        if (ref != NULL && context_is_pure_container (ref))
 
6525
                all = TRUE;
 
6526
 
 
6527
        if (all && (options & (GTK_SOURCE_CONTEXT_IGNORE_STYLE | GTK_SOURCE_CONTEXT_OVERRIDE_STYLE)))
 
6528
        {
 
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);
 
6534
                return FALSE;
 
6535
        }
 
6536
 
 
6537
        if (options & (GTK_SOURCE_CONTEXT_IGNORE_STYLE | GTK_SOURCE_CONTEXT_OVERRIDE_STYLE))
 
6538
                override_style = TRUE;
 
6539
 
 
6540
        definition_child_new (parent, ref_id, style, override_style, all,
 
6541
                              (options & GTK_SOURCE_CONTEXT_REF_ORIGINAL) != 0);
 
6542
 
 
6543
        return TRUE;
 
6544
}
 
6545
 
 
6546
/**
 
6547
 * resolve_reference:
 
6548
 *
 
6549
 * Checks whether all children of a context definition refer to valid
 
6550
 * contexts. Called from _gtk_source_context_data_finish_parse.
 
6551
 */
 
6552
struct ResolveRefData {
 
6553
        GtkSourceContextData *ctx_data;
 
6554
        GError *error;
 
6555
};
 
6556
 
 
6557
static void
 
6558
resolve_reference (G_GNUC_UNUSED const gchar *id,
 
6559
                   ContextDefinition *definition,
 
6560
                   gpointer           user_data)
 
6561
{
 
6562
        GSList *l;
 
6563
 
 
6564
        struct ResolveRefData *data = user_data;
 
6565
 
 
6566
        if (data->error != NULL)
 
6567
                return;
 
6568
 
 
6569
        for (l = definition->children; l != NULL && data->error == NULL; l = l->next)
 
6570
        {
 
6571
                ContextDefinition *ref;
 
6572
                DefinitionChild *child_def = l->data;
 
6573
 
 
6574
                if (child_def->resolved)
 
6575
                        continue;
 
6576
 
 
6577
                ref = LOOKUP_DEFINITION (data->ctx_data, child_def->u.id);
 
6578
 
 
6579
                if (ref != NULL)
 
6580
                {
 
6581
                        g_free (child_def->u.id);
 
6582
                        child_def->u.definition = ref;
 
6583
                        child_def->resolved = TRUE;
 
6584
 
 
6585
                        if (context_is_pure_container (ref))
 
6586
                        {
 
6587
                                if (child_def->override_style)
 
6588
                                {
 
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);
 
6594
                                }
 
6595
                                else
 
6596
                                {
 
6597
                                        child_def->is_ref_all = TRUE;
 
6598
                                }
 
6599
                        }
 
6600
                }
 
6601
                else
 
6602
                {
 
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);
 
6606
                }
 
6607
        }
 
6608
}
 
6609
 
 
6610
static gboolean
 
6611
process_replace (GtkSourceContextData *ctx_data,
 
6612
                 const gchar          *id,
 
6613
                 const gchar          *replace_with,
 
6614
                 GError              **error)
 
6615
{
 
6616
        ContextDefinition *to_replace, *new;
 
6617
 
 
6618
        to_replace = LOOKUP_DEFINITION (ctx_data, id);
 
6619
 
 
6620
        if (to_replace == NULL)
 
6621
        {
 
6622
                g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
 
6623
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
 
6624
                             _("unknown context '%s'"), id);
 
6625
                return FALSE;
 
6626
        }
 
6627
 
 
6628
        new = LOOKUP_DEFINITION (ctx_data, replace_with);
 
6629
 
 
6630
        if (new == NULL)
 
6631
        {
 
6632
                g_set_error (error, GTK_SOURCE_CONTEXT_ENGINE_ERROR,
 
6633
                             GTK_SOURCE_CONTEXT_ENGINE_ERROR_INVALID_REF,
 
6634
                             _("unknown context '%s'"), replace_with);
 
6635
                return FALSE;
 
6636
        }
 
6637
 
 
6638
        g_hash_table_insert (ctx_data->definitions, g_strdup (id), context_definition_ref (new));
 
6639
 
 
6640
        return TRUE;
 
6641
}
 
6642
 
 
6643
GtkSourceContextReplace *
 
6644
_gtk_source_context_replace_new (const gchar *to_replace_id,
 
6645
                                 const gchar *replace_with_id)
 
6646
{
 
6647
        GtkSourceContextReplace *repl;
 
6648
 
 
6649
        g_return_val_if_fail (to_replace_id != NULL, NULL);
 
6650
        g_return_val_if_fail (replace_with_id != NULL, NULL);
 
6651
 
 
6652
        repl = g_slice_new (GtkSourceContextReplace);
 
6653
        repl->id = g_strdup (to_replace_id);
 
6654
        repl->replace_with = g_strdup (replace_with_id);
 
6655
 
 
6656
        return repl;
 
6657
}
 
6658
 
 
6659
void
 
6660
_gtk_source_context_replace_free (GtkSourceContextReplace *repl)
 
6661
{
 
6662
        if (repl != NULL)
 
6663
        {
 
6664
                g_free (repl->id);
 
6665
                g_free (repl->replace_with);
 
6666
                g_slice_free (GtkSourceContextReplace, repl);
 
6667
        }
 
6668
}
 
6669
 
 
6670
/**
 
6671
 * _gtk_source_context_data_finish_parse:
 
6672
 *
 
6673
 * @ctx_data: #GtkSourceContextData.
 
6674
 * @overrides: list of #GtkSourceContextOverride objects.
 
6675
 * @error: error structure to be filled in when failed.
 
6676
 *
 
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
 
6684
 * done.
 
6685
 *
 
6686
 * Returns: %TRUE on success, %FALSE if there were unresolved
 
6687
 * references.
 
6688
 */
 
6689
gboolean
 
6690
_gtk_source_context_data_finish_parse (GtkSourceContextData *ctx_data,
 
6691
                                       GList                *overrides,
 
6692
                                       GError              **error)
 
6693
{
 
6694
        struct ResolveRefData data;
 
6695
        gchar *root_id;
 
6696
        ContextDefinition *main_definition;
 
6697
 
 
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);
 
6701
 
 
6702
        while (overrides != NULL)
 
6703
        {
 
6704
                GtkSourceContextReplace *repl = overrides->data;
 
6705
 
 
6706
                g_return_val_if_fail (repl != NULL, FALSE);
 
6707
 
 
6708
                if (!process_replace (ctx_data, repl->id, repl->replace_with, error))
 
6709
                        return FALSE;
 
6710
 
 
6711
                overrides = overrides->next;
 
6712
        }
 
6713
 
 
6714
        data.ctx_data = ctx_data;
 
6715
        data.error = NULL;
 
6716
 
 
6717
        g_hash_table_foreach (ctx_data->definitions, (GHFunc) resolve_reference, &data);
 
6718
 
 
6719
        if (data.error != NULL)
 
6720
        {
 
6721
                g_propagate_error (error, data.error);
 
6722
                return FALSE;
 
6723
        }
 
6724
 
 
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);
 
6728
        g_free (root_id);
 
6729
 
 
6730
        if (main_definition == NULL)
 
6731
        {
 
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);
 
6737
                return FALSE;
 
6738
        }
 
6739
 
 
6740
        return TRUE;
 
6741
}
 
6742
 
 
6743
static void
 
6744
add_escape_ref (ContextDefinition    *definition,
 
6745
                GtkSourceContextData *ctx_data)
 
6746
{
 
6747
        GError *error = NULL;
 
6748
 
 
6749
        if (definition->type != CONTEXT_TYPE_CONTAINER)
 
6750
                return;
 
6751
 
 
6752
        _gtk_source_context_data_add_ref (ctx_data, definition->id,
 
6753
                                          "gtk-source-context-engine-escape",
 
6754
                                          0, NULL, FALSE, &error);
 
6755
 
 
6756
        if (error)
 
6757
                goto out;
 
6758
 
 
6759
        _gtk_source_context_data_add_ref (ctx_data, definition->id,
 
6760
                                          "gtk-source-context-engine-line-escape",
 
6761
                                          0, NULL, FALSE, &error);
 
6762
 
 
6763
out:
 
6764
        if (error)
 
6765
        {
 
6766
                g_warning ("%s", error->message);
 
6767
                g_error_free (error);
 
6768
        }
 
6769
}
 
6770
 
 
6771
static void
 
6772
prepend_definition (G_GNUC_UNUSED gchar *id,
 
6773
                    ContextDefinition *definition,
 
6774
                    GSList **list)
 
6775
{
 
6776
        *list = g_slist_prepend (*list, definition);
 
6777
}
 
6778
 
 
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
 
6785
   next line. */
 
6786
void
 
6787
_gtk_source_context_data_set_escape_char (GtkSourceContextData *ctx_data,
 
6788
                                          gunichar              escape_char)
 
6789
{
 
6790
        GError *error = NULL;
 
6791
        char buf[10];
 
6792
        gint len;
 
6793
        char *escaped, *pattern;
 
6794
        GSList *definitions = NULL;
 
6795
 
 
6796
        g_return_if_fail (ctx_data != NULL);
 
6797
        g_return_if_fail (escape_char != 0);
 
6798
 
 
6799
        len = g_unichar_to_utf8 (escape_char, buf);
 
6800
        g_return_if_fail (len > 0);
 
6801
 
 
6802
        escaped = g_regex_escape_string (buf, 1);
 
6803
        pattern = g_strdup_printf ("%s.", escaped);
 
6804
 
 
6805
        g_hash_table_foreach (ctx_data->definitions, (GHFunc) prepend_definition, &definitions);
 
6806
        definitions = g_slist_reverse (definitions);
 
6807
 
 
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,
 
6811
                                                      &error))
 
6812
                goto out;
 
6813
 
 
6814
        g_free (pattern);
 
6815
        pattern = g_strdup_printf ("%s$", escaped);
 
6816
 
 
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,
 
6820
                                                      &error))
 
6821
                goto out;
 
6822
 
 
6823
        g_slist_foreach (definitions, (GFunc) add_escape_ref, ctx_data);
 
6824
 
 
6825
out:
 
6826
        if (error)
 
6827
        {
 
6828
                g_warning ("%s", error->message);
 
6829
                g_error_free (error);
 
6830
        }
 
6831
 
 
6832
        g_free (pattern);
 
6833
        g_free (escaped);
 
6834
        g_slist_free (definitions);
 
6835
}
 
6836
 
 
6837
 
 
6838
/* DEBUG CODE ------------------------------------------------------------- */
 
6839
 
 
6840
#ifdef ENABLE_CHECK_TREE
 
6841
static void
 
6842
check_segment (GtkSourceContextEngine *ce,
 
6843
               Segment                *segment)
 
6844
{
 
6845
        Segment *child;
 
6846
 
 
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);
 
6850
 
 
6851
        if (SEGMENT_IS_INVALID (segment))
 
6852
                g_assert (g_slist_find (ce->priv->invalid, segment) != NULL);
 
6853
        else
 
6854
                g_assert (g_slist_find (ce->priv->invalid, segment) == NULL);
 
6855
 
 
6856
        if (segment->children != NULL)
 
6857
                g_assert (!SEGMENT_IS_INVALID (segment) && SEGMENT_IS_CONTAINER (segment));
 
6858
 
 
6859
        for (child = segment->children; child != NULL; child = child->next)
 
6860
        {
 
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);
 
6867
        }
 
6868
}
 
6869
 
 
6870
struct CheckContextData {
 
6871
        Context *parent;
 
6872
        ContextDefinition *definition;
 
6873
};
 
6874
 
 
6875
static void
 
6876
check_context_hash_cb (const char *text,
 
6877
                       Context    *context,
 
6878
                       gpointer    user_data)
 
6879
{
 
6880
        struct CheckContextData *data = user_data;
 
6881
 
 
6882
        g_assert (text != NULL);
 
6883
        g_assert (context != NULL);
 
6884
        g_assert (context->definition == data->definition);
 
6885
        g_assert (context->parent == data->parent);
 
6886
}
 
6887
 
 
6888
static void
 
6889
check_context (Context *context)
 
6890
{
 
6891
        ContextPtr *ptr;
 
6892
 
 
6893
        for (ptr = context->children; ptr != NULL; ptr = ptr->next)
 
6894
        {
 
6895
                if (ptr->fixed)
 
6896
                {
 
6897
                        g_assert (ptr->u.context->parent == context);
 
6898
                        g_assert (ptr->u.context->definition == ptr->definition);
 
6899
                        check_context (ptr->u.context);
 
6900
                }
 
6901
                else
 
6902
                {
 
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,
 
6908
                                              &data);
 
6909
                }
 
6910
        }
 
6911
}
 
6912
 
 
6913
static void
 
6914
check_regex (void)
 
6915
{
 
6916
        static gboolean done;
 
6917
 
 
6918
        if (!done)
 
6919
        {
 
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"));
 
6926
 
 
6927
                done = TRUE;
 
6928
        }
 
6929
}
 
6930
 
 
6931
static void
 
6932
check_tree (GtkSourceContextEngine *ce)
 
6933
{
 
6934
        Segment *root = ce->priv->root_segment;
 
6935
 
 
6936
        check_regex ();
 
6937
 
 
6938
        g_assert (root->start_at == 0);
 
6939
 
 
6940
        if (ce->priv->invalid_region.empty)
 
6941
                g_assert (root->end_at == gtk_text_buffer_get_char_count (ce->priv->buffer));
 
6942
 
 
6943
        g_assert (!root->parent);
 
6944
        check_segment (ce, root);
 
6945
 
 
6946
        g_assert (!ce->priv->root_context->parent);
 
6947
        g_assert (root->context == ce->priv->root_context);
 
6948
        check_context (ce->priv->root_context);
 
6949
}
 
6950
 
 
6951
static void
 
6952
check_segment_children (Segment *segment)
 
6953
{
 
6954
        Segment *ch;
 
6955
 
 
6956
        g_assert (segment != NULL);
 
6957
        check_segment_list (segment->parent);
 
6958
 
 
6959
        for (ch = segment->children; ch != NULL; ch = ch->next)
 
6960
        {
 
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);
 
6968
        }
 
6969
}
 
6970
 
 
6971
static void
 
6972
check_segment_list (Segment *segment)
 
6973
{
 
6974
        Segment *ch;
 
6975
 
 
6976
        if (segment == NULL)
 
6977
                return;
 
6978
 
 
6979
        for (ch = segment->children; ch != NULL; ch = ch->next)
 
6980
        {
 
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);
 
6986
        }
 
6987
}
 
6988
#endif /* ENABLE_CHECK_TREE */
 
6989
 
 
6990
 
 
6991
#ifdef ENABLE_MEMORY_DEBUG
 
6992
typedef struct {
 
6993
        GSList *def_regexes;
 
6994
        GSList *ctx_regexes;
 
6995
        gsize def_mem;
 
6996
        gsize ctx_mem;
 
6997
        guint n_ctx;
 
6998
} MemInfo;
 
6999
 
 
7000
typedef struct
 
7001
{
 
7002
  gpointer   key;
 
7003
  gpointer   value;
 
7004
  gpointer   next;
 
7005
} HashNodeStruct;
 
7006
 
 
7007
typedef struct
 
7008
{
 
7009
  gint             size;
 
7010
  gint             nnodes;
 
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;
 
7017
} HashTableStruct;
 
7018
 
 
7019
static gsize
 
7020
get_hash_table_mem (GHashTable *ht)
 
7021
{
 
7022
        return sizeof (HashTableStruct) +
 
7023
                sizeof (HashNodeStruct) * g_hash_table_size (ht);
 
7024
}
 
7025
 
 
7026
static void
 
7027
add_regex_mem (MemInfo *info,
 
7028
               Regex   *regex,
 
7029
               gboolean def)
 
7030
{
 
7031
        if (!regex)
 
7032
                return;
 
7033
 
 
7034
        if (def)
 
7035
        {
 
7036
                if (!g_slist_find (info->def_regexes, regex))
 
7037
                        info->def_regexes = g_slist_prepend (info->def_regexes, regex);
 
7038
        }
 
7039
        else
 
7040
        {
 
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);
 
7044
        }
 
7045
}
 
7046
 
 
7047
static gsize
 
7048
get_str_mem (const gchar *string)
 
7049
{
 
7050
        return string ? strlen (string) + 1 : 0;
 
7051
}
 
7052
 
 
7053
static void
 
7054
get_def_mem (ContextDefinition *def,
 
7055
             MemInfo           *info)
 
7056
{
 
7057
        GSList *l;
 
7058
 
 
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);
 
7062
 
 
7063
        if (def->type == CONTEXT_TYPE_CONTAINER)
 
7064
        {
 
7065
                add_regex_mem (info, def->u.start_end.start, TRUE);
 
7066
                add_regex_mem (info, def->u.start_end.end, TRUE);
 
7067
        }
 
7068
        else
 
7069
        {
 
7070
                add_regex_mem (info, def->u.match, TRUE);
 
7071
        }
 
7072
 
 
7073
        for (l = def->children; l != NULL; l = l->next)
 
7074
        {
 
7075
                DefinitionChild *child_def = l->data;
 
7076
 
 
7077
                info->def_mem += sizeof (DefinitionChild);
 
7078
                info->def_mem += get_str_mem (child_def->style);
 
7079
 
 
7080
                if (child_def->resolved)
 
7081
                        info->def_mem += get_str_mem (child_def->u.id);
 
7082
        }
 
7083
 
 
7084
        for (l = def->sub_patterns; l != NULL; l = l->next)
 
7085
        {
 
7086
                SubPatternDefinition *sp_def = l->data;
 
7087
 
 
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);
 
7092
#endif
 
7093
 
 
7094
                if (sp_def->is_named)
 
7095
                        info->def_mem += get_str_mem (sp_def->u.name);
 
7096
        }
 
7097
 
 
7098
        add_regex_mem (info, def->reg_all, TRUE);
 
7099
}
 
7100
 
 
7101
static void get_context_mem (Context *ctx, MemInfo *info);
 
7102
 
 
7103
static void
 
7104
get_context_mem_cb (const char *id,
 
7105
                    Context    *ctx,
 
7106
                    MemInfo    *info)
 
7107
{
 
7108
        info->ctx_mem += get_str_mem (id);
 
7109
        get_context_mem (ctx, info);
 
7110
}
 
7111
 
 
7112
static void
 
7113
get_context_ptr_mem (ContextPtr *ptr,
 
7114
                     MemInfo    *info)
 
7115
{
 
7116
        if (ptr)
 
7117
        {
 
7118
                info->ctx_mem += sizeof (ContextPtr);
 
7119
 
 
7120
                if (ptr->fixed)
 
7121
                        get_context_mem (ptr->u.context, info);
 
7122
                else
 
7123
                {
 
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);
 
7126
                }
 
7127
 
 
7128
                get_context_ptr_mem (ptr->next, info);
 
7129
        }
 
7130
}
 
7131
 
 
7132
static void
 
7133
get_context_mem (Context *ctx,
 
7134
                 MemInfo *info)
 
7135
{
 
7136
        if (ctx)
 
7137
        {
 
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*);
 
7143
                info->n_ctx += 1;
 
7144
        }
 
7145
}
 
7146
 
 
7147
static void
 
7148
get_def_mem_cb (const char        *id,
 
7149
                ContextDefinition *def,
 
7150
                MemInfo           *info)
 
7151
{
 
7152
        info->def_mem += get_str_mem (id);
 
7153
        get_def_mem (def, info);
 
7154
}
 
7155
 
 
7156
static void
 
7157
get_definitions_mem (GtkSourceContextEngine *ce,
 
7158
                     MemInfo                *info)
 
7159
{
 
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,
 
7164
                              info);
 
7165
}
 
7166
 
 
7167
static gsize
 
7168
get_regex_mem (Regex *regex)
 
7169
{
 
7170
        gsize mem = 0;
 
7171
 
 
7172
        if (!regex)
 
7173
                return 0;
 
7174
 
 
7175
        mem += sizeof (Regex);
 
7176
 
 
7177
        if (regex->resolved)
 
7178
                mem += _egg_regex_get_memory (regex->u.regex.regex);
 
7179
        else
 
7180
                mem += get_str_mem (regex->u.info.pattern);
 
7181
 
 
7182
        return mem;
 
7183
}
 
7184
 
 
7185
static gboolean
 
7186
mem_usage_timeout (GtkSourceContextEngine *ce)
 
7187
{
 
7188
        GSList *l;
 
7189
        MemInfo info = {NULL, NULL, 0, 0, 0};
 
7190
 
 
7191
        get_definitions_mem (ce, &info);
 
7192
        get_context_mem (ce->priv->root_context, &info);
 
7193
 
 
7194
        for (l = info.def_regexes; l != NULL; l = l->next)
 
7195
                info.def_mem += get_regex_mem (l->data);
 
7196
 
 
7197
        for (l = info.ctx_regexes; l != NULL; l = l->next)
 
7198
                info.ctx_mem += get_regex_mem (l->data);
 
7199
 
 
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);
 
7202
 
 
7203
        g_slist_free (info.def_regexes);
 
7204
        g_slist_free (info.ctx_regexes);
 
7205
 
 
7206
        return TRUE;
 
7207
}
 
7208
#endif /* ENABLE_MEMORY_DEBUG */