~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric-updates

« back to all changes in this revision

Viewing changes to src/addins/Mono.Texteditor/Mono.TextEditor/Document.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-18 08:40:51 UTC
  • mfrom: (1.2.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20090218084051-gh8m6ukvokbwj7cf
Tags: 1.9.2+dfsg-1ubuntu1
* Merge from Debian Experimental (LP: #330519), remaining Ubuntu changes:
  + debian/control:
    - Update for Gnome# 2.24
    - Add libmono-cairo1.0-cil to build-deps to fool pkg-config check

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Document.cs
 
2
//
 
3
// Author:
 
4
//   Mike Krüger <mkrueger@novell.com>
 
5
//
 
6
// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
 
7
//
 
8
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
9
// of this software and associated documentation files (the "Software"), to deal
 
10
// in the Software without restriction, including without limitation the rights
 
11
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
12
// copies of the Software, and to permit persons to whom the Software is
 
13
// furnished to do so, subject to the following conditions:
 
14
//
 
15
// The above copyright notice and this permission notice shall be included in
 
16
// all copies or substantial portions of the Software.
 
17
//
 
18
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
19
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
20
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
21
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
22
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
23
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
24
// THE SOFTWARE.
 
25
//
 
26
//
 
27
 
 
28
using System;
 
29
using System.Collections.Generic;
 
30
using System.Collections.ObjectModel;
 
31
using System.Diagnostics;
 
32
using System.Text;
 
33
using Mono.TextEditor.Highlighting;
 
34
using System.Linq;
 
35
 
 
36
namespace Mono.TextEditor
 
37
{
 
38
        public class Document : AbstractBuffer, IDisposable
 
39
        {
 
40
                IBuffer      buffer;
 
41
                LineSplitter splitter;
 
42
                SyntaxMode   syntaxMode = null;
 
43
                string       eol = null;
 
44
                
 
45
                string mimeType;
 
46
                string fileName;
 
47
                bool   readOnly;
 
48
                ReadOnlyCheckDelegate readOnlyCheckDelegate;
 
49
                
 
50
                /// <value>
 
51
                /// The eol mark used in this document - it's taken from the first line in the document,
 
52
                /// if no eol mark is found it's using the default (Environment.NewLine).
 
53
                /// The value is saved, even when all lines are deleted the eol marker will still be the old eol marker.
 
54
                /// </value>
 
55
                public string EolMarker {
 
56
                        get {
 
57
                                if (eol == null && splitter.LineCount > 0) {
 
58
                                        LineSegment line = splitter.Get (0);
 
59
                                        if (line.DelimiterLength > 0) 
 
60
                                                eol = buffer.GetTextAt (line.EditableLength, line.DelimiterLength);
 
61
                                }
 
62
                                return !String.IsNullOrEmpty (eol) ? eol : Environment.NewLine;
 
63
                        }
 
64
                }
 
65
                
 
66
                public string MimeType {
 
67
                        get {
 
68
                                return mimeType;
 
69
                        }
 
70
                        set {
 
71
                                mimeType = value;
 
72
                                if (this.SyntaxMode == null)
 
73
                                        this.SyntaxMode = SyntaxModeService.GetSyntaxMode (value);
 
74
                        }
 
75
                }
 
76
                
 
77
                public string FileName {
 
78
                        get {
 
79
                                return fileName;
 
80
                        }
 
81
                        set {
 
82
                                fileName = value;
 
83
                        }
 
84
                }
 
85
                
 
86
                public SyntaxMode SyntaxMode {
 
87
                        get {
 
88
                                return syntaxMode;
 
89
                        }
 
90
                        set {
 
91
                                syntaxMode = value;
 
92
                                UpdateHighlighting ();
 
93
                        }
 
94
                }
 
95
                
 
96
                public Document()
 
97
                {
 
98
                        buffer   = new GapBuffer ();
 
99
                        splitter = new LineSplitter (buffer);
 
100
                        
 
101
                        splitter.LineSegmentTree.LineChanged += delegate (object sender, LineEventArgs args) {
 
102
                                if (LineChanged != null) 
 
103
                                        LineChanged (this, args);
 
104
                        };
 
105
                /*      splitter.LineSegmentTree.LineInserted += delegate (object sender, LineEventArgs args) {
 
106
                                if (LineInserted != null) 
 
107
                                        LineInserted (this, args);
 
108
                        };*/
 
109
                }
 
110
                
 
111
                public event EventHandler<LineEventArgs> LineChanged;
 
112
        //      public event EventHandler<LineEventArgs> LineInserted;
 
113
                
 
114
                
 
115
                public override void Dispose ()
 
116
                {
 
117
                        buffer = buffer.Kill ();
 
118
                        splitter = splitter.Kill ();
 
119
                        if (undoStack != null) {
 
120
                                undoStack.Clear ();
 
121
                                undoStack = null;
 
122
                        }
 
123
                        if (redoStack != null) {
 
124
                                redoStack.Clear ();
 
125
                                redoStack = null;
 
126
                        }
 
127
                        currentAtomicOperation = null;
 
128
                        
 
129
                        /*if (foldSegments != null) {
 
130
                                foldSegments.Clear ();
 
131
                                foldSegments = null;
 
132
                        }*/
 
133
                }
 
134
                
 
135
                #region Buffer implementation
 
136
                public override int Length {
 
137
                        get {
 
138
                                return this.buffer.Length;
 
139
                        }
 
140
                }
 
141
                
 
142
                public override string Text {
 
143
                        get {
 
144
                                return this.buffer.Text;
 
145
                        }
 
146
                        set {
 
147
                                splitter.Clear ();
 
148
                                int oldLength = Length;
 
149
                                ReplaceEventArgs args = new ReplaceEventArgs (0, oldLength, value);
 
150
                                this.OnTextReplacing (args);
 
151
                                this.buffer.Text = value;
 
152
                                splitter.TextReplaced (this, args);
 
153
                                UpdateHighlighting ();
 
154
                                this.OnTextReplaced (args);
 
155
                        }
 
156
                }
 
157
                
 
158
                void UpdateHighlighting ()
 
159
                {
 
160
                        if (this.syntaxMode != null) {
 
161
                                Mono.TextEditor.Highlighting.SyntaxModeService.StartUpdate (this, this.syntaxMode, 0, buffer.Length);
 
162
                        //      Mono.TextEditor.Highlighting.SyntaxModeService.WaitForUpdate ();
 
163
                        }
 
164
                }
 
165
                
 
166
                public System.IO.TextReader OpenTextReader ()
 
167
                {
 
168
                        return new BufferedTextReader (this.buffer);
 
169
                }
 
170
                
 
171
                public override void Replace (int offset, int count, string value)
 
172
                {
 
173
                        InterruptFoldWorker ();
 
174
//                      Mono.TextEditor.Highlighting.SyntaxModeService.WaitForUpdate (true);
 
175
//                      Debug.Assert (count >= 0);
 
176
//                      Debug.Assert (0 <= offset && offset + count <= Length);
 
177
                        int oldLineCount = this.LineCount;
 
178
                        ReplaceEventArgs args = new ReplaceEventArgs (offset, count, value);
 
179
                        OnTextReplacing (args);
 
180
/* insert/repla
 
181
                        lock (syncObject) {
 
182
                                int endOffset = offset + count;
 
183
                                foldSegments = new List<FoldSegment> (foldSegments.Where (s => (s.Offset < offset || s.Offset >= endOffset) && 
 
184
                                                                                               (s.EndOffset <= offset || s.EndOffset >= endOffset)));
 
185
                        }*/
 
186
                        UndoOperation operation = null;
 
187
                        if (!isInUndo) {
 
188
                                operation = new UndoOperation (args, GetTextAt (offset, count));
 
189
                                if (currentAtomicOperation != null) {
 
190
                                        currentAtomicOperation.Add (operation);
 
191
                                } else {
 
192
                                        OnBeginUndo ();
 
193
                                        undoStack.Push (operation);
 
194
                                        OnEndUndo (operation);
 
195
                                }
 
196
                                foreach (UndoOperation redoOp in redoStack) {
 
197
                                        redoOp.Dispose ();
 
198
                                }
 
199
                                redoStack.Clear ();
 
200
                        }
 
201
                        
 
202
                        buffer.Replace (offset, count, value);
 
203
                        OnTextReplaced (args);
 
204
                        splitter.TextReplaced (this, args);
 
205
                        
 
206
                        if (operation != null)
 
207
                                operation.Setup (this, args);
 
208
                        if (this.syntaxMode != null)
 
209
                                Mono.TextEditor.Highlighting.SyntaxModeService.StartUpdate (this, this.syntaxMode, offset, value != null ? offset + value.Length : offset + count);
 
210
                        if (oldLineCount != LineCount)
 
211
                                this.CommitLineToEndUpdate (this.OffsetToLocation (offset).Line);
 
212
                }
 
213
                
 
214
                public string GetTextBetween (int startOffset, int endOffset)
 
215
                {
 
216
                        return buffer.GetTextAt (startOffset, endOffset - startOffset);
 
217
                }
 
218
                
 
219
                public override string GetTextAt (int offset, int count)
 
220
                {
 
221
                        return buffer.GetTextAt (offset, count);
 
222
                }
 
223
                
 
224
                public override char GetCharAt (int offset)
 
225
                {
 
226
                        return buffer.GetCharAt (offset);
 
227
                }
 
228
                
 
229
                protected virtual void OnTextReplaced (ReplaceEventArgs args)
 
230
                {
 
231
                        if (TextReplaced != null)
 
232
                                TextReplaced (this, args);
 
233
                }
 
234
                public event EventHandler<ReplaceEventArgs> TextReplaced;
 
235
                
 
236
                protected virtual void OnTextReplacing (ReplaceEventArgs args)
 
237
                {
 
238
                        if (TextReplacing != null)
 
239
                                TextReplacing (this, args);
 
240
                }
 
241
                public event EventHandler<ReplaceEventArgs> TextReplacing;
 
242
                
 
243
                #endregion
 
244
                
 
245
                #region Line Splitter operations
 
246
                public IEnumerable<LineSegment> Lines {
 
247
                        get {
 
248
                                return splitter.Lines;
 
249
                        }
 
250
                }
 
251
                
 
252
                public int LineCount {
 
253
                        get {
 
254
                                return splitter.LineCount;
 
255
                        }
 
256
                }
 
257
                
 
258
                public int LocationToOffset (int line, int column)
 
259
                {
 
260
                        return LocationToOffset (new DocumentLocation (line, column));
 
261
                }
 
262
                
 
263
                public int LocationToOffset (DocumentLocation location)
 
264
                {
 
265
                        if (location.Line >= this.splitter.LineCount) 
 
266
                                return -1;
 
267
                        LineSegment line = GetLine (location.Line);
 
268
                        return System.Math.Min (Length, line.Offset + System.Math.Min (line.EditableLength, location.Column));
 
269
                }
 
270
                
 
271
                public DocumentLocation OffsetToLocation (int offset)
 
272
                {
 
273
                        int lineNr = splitter.OffsetToLineNumber (offset);
 
274
                        if (lineNr < 0)
 
275
                                return DocumentLocation.Empty;
 
276
                        LineSegment line = GetLine (lineNr);
 
277
                        return new DocumentLocation (lineNr, System.Math.Min (line.Length, offset - line.Offset));
 
278
                }
 
279
                
 
280
                public LineSegment GetLine (int lineNumber)
 
281
                {
 
282
                        return splitter.Get (lineNumber);
 
283
                }
 
284
                
 
285
                public LineSegment GetLineByOffset (int offset)
 
286
                {
 
287
                        return splitter.GetLineByOffset (offset);
 
288
                }
 
289
                
 
290
                public int OffsetToLineNumber (int offset)
 
291
                {
 
292
                        return splitter.OffsetToLineNumber (offset);
 
293
                }
 
294
                
 
295
                #endregion
 
296
                
 
297
                #region Undo/Redo operations
 
298
                public class UndoOperation : IDisposable
 
299
                {
 
300
                        Document doc;
 
301
                        ReplaceEventArgs args;
 
302
                        string text;
 
303
                        int startOffset, length;
 
304
                        public virtual string Text {
 
305
                                get {
 
306
                                        return text;
 
307
                                }
 
308
                        }
 
309
                        public virtual ReplaceEventArgs Args {
 
310
                                get {
 
311
                                        return args;
 
312
                                }
 
313
                        }
 
314
                        protected UndoOperation()
 
315
                        {
 
316
                        }
 
317
 
 
318
                        public virtual bool ChangedLine (LineSegment line)
 
319
                        {
 
320
                                if (Args == null || line == null)
 
321
                                        return false;
 
322
                                return startOffset <= line.Offset - line.DelimiterLength && line.Offset <= startOffset + length 
 
323
                                                || line.Offset - line.DelimiterLength <= startOffset && startOffset <= line.Offset + line.EditableLength
 
324
                                                ;
 
325
                                        ; //line.Contains (Args.Offset);
 
326
                        }
 
327
                        
 
328
                        public UndoOperation (ReplaceEventArgs args, string text)
 
329
                        {
 
330
                                this.args = args;
 
331
                                this.text = text;
 
332
                        }
 
333
                        static int GetDelta (ReplaceEventArgs args)
 
334
                        {
 
335
                                int result = -args.Count;
 
336
                                if (!String.IsNullOrEmpty (args.Value))
 
337
                                        result += args.Value.Length;
 
338
                                return result;
 
339
                        }
 
340
                        internal void Setup (Document doc, ReplaceEventArgs args)
 
341
                        {
 
342
                                this.doc = doc;
 
343
                                doc.TextReplaced += TextReplaced;
 
344
                                if (args != null) {
 
345
                                        this.startOffset = args.Offset;
 
346
                                        if (!String.IsNullOrEmpty (args.Value))
 
347
                                                this.length  = args.Value.Length;
 
348
                                }
 
349
                        }
 
350
                        void TextReplaced (object sender, ReplaceEventArgs args)
 
351
                        {
 
352
                                if (args.Offset < startOffset) {
 
353
                                        startOffset = System.Math.Max (startOffset + GetDelta(args), args.Offset);
 
354
                                } else if (args.Offset < startOffset + length) {
 
355
                                        length = System.Math.Max (length + GetDelta(args), startOffset - args.Offset);
 
356
                                }
 
357
                        }
 
358
                        
 
359
                        public virtual void Dispose ()
 
360
                        {
 
361
                                args = null;
 
362
                                if (doc != null) {
 
363
                                        doc.TextReplaced -= TextReplaced;
 
364
                                        doc = null;
 
365
                                }
 
366
                                if (Disposed != null) 
 
367
                                        Disposed (this, EventArgs.Empty);
 
368
                        }
 
369
                        
 
370
                        public virtual void Undo (Document doc)
 
371
                        {
 
372
                                if (args.Value != null && args.Value.Length > 0)
 
373
                                        doc.Remove (args.Offset, args.Value.Length);
 
374
                                if (!String.IsNullOrEmpty (text))
 
375
                                        doc.Insert (args.Offset, text);
 
376
                                OnUndoDone ();
 
377
                        }
 
378
                        
 
379
                        public virtual void Redo (Document doc)
 
380
                        {
 
381
                                doc.Replace (args.Offset, args.Count, args.Value);
 
382
                                OnRedoDone ();
 
383
                        }
 
384
                        
 
385
                        protected virtual void OnUndoDone ()
 
386
                        {
 
387
                                if (UndoDone != null)
 
388
                                        UndoDone (this, EventArgs.Empty);
 
389
                        }
 
390
                        public event EventHandler UndoDone;
 
391
                        
 
392
                        protected virtual void OnRedoDone ()
 
393
                        {
 
394
                                if (RedoDone != null)
 
395
                                        RedoDone (this, EventArgs.Empty);
 
396
                        }
 
397
                        public event EventHandler RedoDone;
 
398
                        
 
399
                        public event EventHandler Disposed;
 
400
                }
 
401
                
 
402
                class AtomicUndoOperation : UndoOperation
 
403
                {
 
404
                        protected List<UndoOperation> operations = new List<UndoOperation> ();
 
405
                        
 
406
                        public List<UndoOperation> Operations {
 
407
                                get {
 
408
                                        return operations;
 
409
                                }
 
410
                        }
 
411
                        
 
412
                        public override string Text {
 
413
                                get {
 
414
                                        return null;
 
415
                                }
 
416
                        }
 
417
                        public override ReplaceEventArgs Args {
 
418
                                get {
 
419
                                        return null;
 
420
                                }
 
421
                        }
 
422
                        
 
423
                        public override void Dispose ()
 
424
                        {
 
425
                                if (operations != null) {
 
426
                                        foreach (UndoOperation operation in operations) {
 
427
                                                operation.Dispose ();
 
428
                                        }
 
429
                                        operations = null;
 
430
                                }
 
431
                                base.Dispose ();
 
432
                        }
 
433
                        
 
434
                        public override bool ChangedLine (LineSegment line)
 
435
                        {
 
436
                                foreach (UndoOperation op in Operations) {
 
437
                                        if (op.ChangedLine (line))
 
438
                                                return true;
 
439
                                }
 
440
                                return false;
 
441
                        }
 
442
                        
 
443
                        public void Add (UndoOperation operation)
 
444
                        {
 
445
                                operations.Add (operation);
 
446
                        }
 
447
                        
 
448
                        public override void Undo (Document doc)
 
449
                        {
 
450
                                for (int i = operations.Count - 1; i >= 0; i--) {
 
451
                                        operations[i].Undo (doc);
 
452
                                }
 
453
                                OnUndoDone ();
 
454
                        }
 
455
                        
 
456
                        public override void Redo (Document doc)
 
457
                        {
 
458
                                foreach (UndoOperation operation in this.operations) {
 
459
                                        operation.Redo (doc);
 
460
                                }
 
461
                                OnRedoDone ();
 
462
                        }
 
463
                }
 
464
                
 
465
                class KeyboardStackUndo : AtomicUndoOperation
 
466
                {
 
467
                        bool isClosed = false;
 
468
                        
 
469
                        public bool IsClosed {
 
470
                                get {
 
471
                                        return isClosed;
 
472
                                }
 
473
                                set {
 
474
                                        isClosed = value;
 
475
                                }
 
476
                        }
 
477
                        
 
478
                        public override string Text {
 
479
                                get {
 
480
                                        return operations.Count > 0 ? operations [operations.Count - 1].Text : "";
 
481
                                }
 
482
                        }
 
483
                        
 
484
                        public override ReplaceEventArgs Args {
 
485
                                get {
 
486
                                        return operations.Count > 0 ? operations [operations.Count - 1].Args : null;
 
487
                                }
 
488
                        }
 
489
                }
 
490
                
 
491
                bool isInUndo = false;
 
492
                Stack<UndoOperation> undoStack = new Stack<UndoOperation> ();
 
493
                Stack<UndoOperation> redoStack = new Stack<UndoOperation> ();
 
494
                AtomicUndoOperation currentAtomicOperation = null;
 
495
                        
 
496
                public bool CanUndo {
 
497
                        get {
 
498
                                return this.undoStack.Count > 0 || currentAtomicOperation != null;
 
499
                        }
 
500
                }
 
501
                
 
502
                UndoOperation[] savePoint = null;
 
503
                public bool IsDirty {
 
504
                        get {
 
505
                                if (this.currentAtomicOperation != null)
 
506
                                        return true;
 
507
                                if (savePoint == null)
 
508
                                        return CanUndo;
 
509
                                if (undoStack.Count != savePoint.Length) 
 
510
                                        return true;
 
511
                                UndoOperation[] currentStack = undoStack.ToArray ();
 
512
                                for (int i = 0; i < currentStack.Length; i++) {
 
513
                                        if (savePoint[i] != currentStack[i])
 
514
                                                return true;
 
515
                                }
 
516
                                return false;
 
517
                        }
 
518
                }
 
519
                
 
520
                public enum LineState {
 
521
                        Unchanged,
 
522
                        Dirty,
 
523
                        Changed
 
524
                }
 
525
                
 
526
                public LineState GetLineState (int lineNumber)
 
527
                {
 
528
                        LineSegment line = GetLine (lineNumber);
 
529
                        foreach (UndoOperation op in undoStack) {
 
530
                                if (op.ChangedLine (line)) {
 
531
                                        if (savePoint != null) {
 
532
                                                foreach (UndoOperation savedUndo in savePoint) {
 
533
                                                        if (op == savedUndo)
 
534
                                                                return LineState.Changed;
 
535
                                                }
 
536
                                        }
 
537
                                        return LineState.Dirty;
 
538
                                }
 
539
                        }
 
540
                        return LineState.Unchanged;
 
541
                }
 
542
                
 
543
                
 
544
                /// <summary>
 
545
                /// Marks the document not dirty at this point (should be called after save).
 
546
                /// </summary>
 
547
                public void SetNotDirtyState ()
 
548
                {
 
549
                        OptimizeTypedUndo ();
 
550
                        if (undoStack.Count > 0 && undoStack.Peek () is KeyboardStackUndo)
 
551
                                ((KeyboardStackUndo)undoStack.Peek ()).IsClosed = true;
 
552
                        savePoint = undoStack.ToArray ();
 
553
                        this.CommitUpdateAll ();
 
554
                }
 
555
                
 
556
                public void OptimizeTypedUndo ()
 
557
                {
 
558
                        if (undoStack.Count == 0)
 
559
                                return;
 
560
                        UndoOperation top = undoStack.Pop ();
 
561
                        if (top.Args == null || top.Args.Value == null || top.Args.Value.Length != 1 || (top is KeyboardStackUndo && ((KeyboardStackUndo)top).IsClosed)) {
 
562
                                undoStack.Push (top);
 
563
                                return;
 
564
                        }
 
565
                        if (undoStack.Count == 0 || !(undoStack.Peek () is KeyboardStackUndo)) 
 
566
                                undoStack.Push (new KeyboardStackUndo ());
 
567
                        KeyboardStackUndo keyUndo = (KeyboardStackUndo)undoStack.Pop ();
 
568
                        if (keyUndo.IsClosed) {
 
569
                                undoStack.Push (keyUndo);
 
570
                                keyUndo = new KeyboardStackUndo ();
 
571
                        }
 
572
                        if (keyUndo.Args != null && keyUndo.Args.Offset + 1 != top.Args.Offset) {
 
573
                                keyUndo.IsClosed = true;
 
574
                                undoStack.Push (keyUndo);
 
575
                                keyUndo = new KeyboardStackUndo ();
 
576
                        }
 
577
                        keyUndo.Add (top);
 
578
                        undoStack.Push (keyUndo);
 
579
                }
 
580
                
 
581
                public void Undo ()
 
582
                {
 
583
                        if (undoStack.Count <= 0)
 
584
                                return;
 
585
                        isInUndo = true;
 
586
                        UndoOperation chunk = undoStack.Pop ();
 
587
                        redoStack.Push (chunk);
 
588
                        chunk.Undo (this);
 
589
                        isInUndo = false;
 
590
                        this.RequestUpdate (new UpdateAll ());
 
591
                        this.CommitDocumentUpdate ();
 
592
                }
 
593
                                
 
594
                public bool CanRedo {
 
595
                        get {
 
596
                                return this.redoStack.Count > 0;
 
597
                        }
 
598
                }
 
599
                
 
600
                public void Redo ()
 
601
                {
 
602
                        if (redoStack.Count <= 0)
 
603
                                return;
 
604
                        isInUndo = true;
 
605
                        UndoOperation chunk = redoStack.Pop ();
 
606
                        undoStack.Push (chunk);
 
607
                        chunk.Redo (this);
 
608
                        isInUndo = false;
 
609
                        this.RequestUpdate (new UpdateAll ());
 
610
                        this.CommitDocumentUpdate ();
 
611
                }
 
612
                int atomicUndoLevel;
 
613
                public void BeginAtomicUndo ()
 
614
                {
 
615
                        if (currentAtomicOperation == null) {
 
616
                                Debug.Assert (atomicUndoLevel == 0); 
 
617
                                currentAtomicOperation = new AtomicUndoOperation ();
 
618
                                OnBeginUndo ();
 
619
                        }
 
620
                        atomicUndoLevel++;
 
621
                }
 
622
                
 
623
                public void EndAtomicUndo ()
 
624
                {
 
625
                        atomicUndoLevel--;
 
626
                        Debug.Assert (atomicUndoLevel >= 0); 
 
627
                        
 
628
                        if (atomicUndoLevel == 0 && currentAtomicOperation != null) {
 
629
                                if (currentAtomicOperation.Operations.Count > 1) {
 
630
                                        undoStack.Push (currentAtomicOperation);
 
631
                                        OnEndUndo (currentAtomicOperation);
 
632
                                } else {
 
633
                                        if (currentAtomicOperation.Operations.Count > 0) {
 
634
                                                undoStack.Push (currentAtomicOperation.Operations [0]);
 
635
                                                OnEndUndo (currentAtomicOperation.Operations [0]);
 
636
                                        } else {
 
637
                                                OnEndUndo (null);
 
638
                                        }
 
639
                                }
 
640
                                currentAtomicOperation = null;
 
641
                        }
 
642
                }
 
643
                
 
644
                protected virtual void OnBeginUndo ()
 
645
                {
 
646
                        if (BeginUndo != null) 
 
647
                                BeginUndo (this, EventArgs.Empty);
 
648
                }
 
649
                
 
650
                protected virtual void OnEndUndo (UndoOperation undo)
 
651
                {
 
652
                        if (EndUndo != null) 
 
653
                                EndUndo (this, undo);
 
654
                }
 
655
                
 
656
                public delegate void UndoOperationHandler (object sender, UndoOperation operation);
 
657
                
 
658
                public event EventHandler         BeginUndo;
 
659
                public event UndoOperationHandler EndUndo;
 
660
                #endregion
 
661
                
 
662
                #region Folding
 
663
                class FoldSegmentTreeNode : IComparable<FoldSegmentTreeNode>
 
664
                {
 
665
                        public FoldSegment FoldSegment {
 
666
                                get;
 
667
                                private set;
 
668
                        }
 
669
                        
 
670
                        public IEnumerable<FoldSegment> FoldSegments {
 
671
                                get {
 
672
                                        return Traverse (x => true);
 
673
                                }
 
674
                        }
 
675
                        
 
676
                        IEnumerable<FoldSegment> Traverse (Predicate<FoldSegment> includeChilds) 
 
677
                        {
 
678
                                Stack<FoldSegmentTreeNode> stack = new Stack<FoldSegmentTreeNode> ();
 
679
                                stack.Push (this);
 
680
                                while (stack.Count > 0) {
 
681
                                        FoldSegmentTreeNode node = stack.Pop ();
 
682
                                        if (node.FoldSegment != null) 
 
683
                                                yield return node.FoldSegment;
 
684
                                        if (node.FoldSegment == null || includeChilds (node.FoldSegment)) {
 
685
                                                for (int i = node.childNodes.Count - 1; i >= 0; i--) {
 
686
                                                        stack.Push (node.childNodes[i]);
 
687
                                                }
 
688
                                        }
 
689
                                }
 
690
                        }
 
691
                        
 
692
                        List<FoldSegmentTreeNode> childNodes = new List<FoldSegmentTreeNode> ();
 
693
                        public List<FoldSegmentTreeNode> ChildNodes {
 
694
                                get {
 
695
                                        return childNodes;
 
696
                                }
 
697
                        }
 
698
                        
 
699
                        public FoldSegmentTreeNode () : this (null)
 
700
                        {
 
701
                        }
 
702
 
 
703
                        public FoldSegmentTreeNode (FoldSegment foldSegment) 
 
704
                        {
 
705
                                this.FoldSegment = foldSegment;
 
706
                        }
 
707
                        
 
708
                        delegate int Comparer (int idx);
 
709
                        
 
710
                        public int CompareTo (FoldSegmentTreeNode node)
 
711
                        {
 
712
                                return FoldSegment.Offset.CompareTo (node.FoldSegment.Offset);
 
713
                        }
 
714
                        
 
715
                        public void AddSegment (FoldSegment segment)
 
716
                        {
 
717
                                if (segment == null)
 
718
                                        return;
 
719
                                int segmentOffset = segment.Offset;
 
720
                                for (int i = 0; i < childNodes.Count; i++) {
 
721
                                        FoldSegmentTreeNode cur = childNodes [i];
 
722
                                        if (cur.FoldSegment.Contains (segmentOffset)) {
 
723
                                                cur.AddSegment (segment);
 
724
                                                return;
 
725
                                        }
 
726
                                }
 
727
                                childNodes.Add (new FoldSegmentTreeNode (segment));
 
728
                                childNodes.Sort ();
 
729
                        }
 
730
                        
 
731
                        public IEnumerable<FoldSegment> GetFoldingsFromOffset (int offset)
 
732
                        {
 
733
//                              return FoldSegments.Where (s => s.Offset <= offset && offset <= s.EndOffset);
 
734
                                
 
735
                                Stack<FoldSegmentTreeNode> stack = new Stack<FoldSegmentTreeNode> ();
 
736
                                stack.Push (this);
 
737
                                while (stack.Count > 0) {
 
738
                                        FoldSegmentTreeNode node = stack.Pop ();
 
739
                                        if (node.FoldSegment != null && node.FoldSegment.Offset < offset && offset < node.FoldSegment.EndOffset)
 
740
                                                yield return node.FoldSegment;
 
741
                                        
 
742
                                        for (int i = node.childNodes.Count - 1; i >= 0; i--) {
 
743
                                                FoldSegmentTreeNode child = node.childNodes[i];
 
744
                                                stack.Push (child);
 
745
                                        }
 
746
                                }
 
747
                        }
 
748
                        
 
749
                        public IEnumerable<FoldSegment> GetFoldingContaining (LineSegment line)
 
750
                        {
 
751
//                              return FoldSegments.Where (s => s.StartLine.Offset <= line.Offset && line.Offset <= s.EndLine.Offset);
 
752
 
 
753
                                Stack<FoldSegmentTreeNode> stack = new Stack<FoldSegmentTreeNode> ();
 
754
                                stack.Push (this);
 
755
                                while (stack.Count > 0) {
 
756
                                        FoldSegmentTreeNode node = stack.Pop ();
 
757
                                        if (node.FoldSegment != null && node.FoldSegment.StartLine.Offset <= line.Offset && line.Offset <= node.FoldSegment.EndLine.Offset)
 
758
                                                yield return node.FoldSegment;
 
759
                                        int start = node.childNodes.FindIndex (delegate (FoldSegmentTreeNode s) {
 
760
                                                return s.FoldSegment.StartLine.Offset <= line.Offset && line.Offset <= s.FoldSegment.EndLine.Offset;
 
761
                                        });
 
762
                                        if (start >= 0) {
 
763
                                                for (int i = node.childNodes.Count - 1; i >= start; i--) {
 
764
                                                        FoldSegmentTreeNode child = node.childNodes[i];
 
765
                                                        if (child.FoldSegment.StartLine.Offset <= line.Offset)
 
766
                                                                stack.Push (child);
 
767
                                                }
 
768
                                        }
 
769
                                }
 
770
                        }
 
771
                        
 
772
                        public IEnumerable<FoldSegment> GetStartFoldings (LineSegment line)
 
773
                        {
 
774
//                              return FoldSegments.Where (s => s.StartLine.Offset == line.Offset);
 
775
 
 
776
                                foreach (FoldSegment segment in GetFoldingContaining (line)) {
 
777
                                        if (segment.StartLine.Offset == line.Offset)
 
778
                                                yield return segment;
 
779
                                        if (segment.StartLine.Offset > line.Offset)
 
780
                                                        break;
 
781
                                }
 
782
                        }
 
783
                        
 
784
                        public int LogicalToVisualLine (Document doc, int logicalLine)
 
785
                        {
 
786
                                int result = logicalLine;
 
787
                                LineSegment line = doc.GetLine (result);
 
788
                                if (line == null)
 
789
                                        return result;
 
790
                                foreach (FoldSegment segment in Traverse (x => !(x.IsFolded && x.StartLine.Offset < line.Offset))) {
 
791
                                        if (segment.IsFolded && segment.StartLine.Offset < line.Offset) {
 
792
                                                result -= doc.GetLineCount (segment);
 
793
                                        }
 
794
                                        if (segment.StartLine.Offset > line.Offset)
 
795
                                                break;
 
796
                                }
 
797
                                return result;
 
798
                        }
 
799
                        
 
800
                        public override string ToString ()
 
801
                        {
 
802
                                return string.Format("[FoldSegmentTreeNode: FoldSegment={0}, FoldSegments={1}, ChildNodes={2}]", FoldSegment, FoldSegments, ChildNodes);
 
803
                        }
 
804
 
 
805
                        public int VisualToLogicalLine (Document doc, int visualLineNumber)
 
806
                        {
 
807
                                int result = visualLineNumber;
 
808
                                LineSegment line = doc.GetLine (result);
 
809
                                if (line == null)
 
810
                                        return result;
 
811
                                foreach (FoldSegment segment in Traverse (x => !(x.IsFolded && x.StartLine.Offset < line.Offset))) {
 
812
                                        if (segment.IsFolded && segment.StartLine.Offset < line.Offset) {
 
813
                                                result += doc.GetLineCount (segment);
 
814
                                                line = doc.GetLine (result);
 
815
                                                if (line == null) 
 
816
                                                        return result;
 
817
                                        }
 
818
                                        if (segment.StartLine.Offset > line.Offset)
 
819
                                                break;
 
820
                                }
 
821
                                return result;
 
822
                        }
 
823
                }
 
824
                
 
825
                FoldSegmentTreeNode foldSegmentTree = new FoldSegmentTreeNode ();
 
826
                
 
827
                public bool HasFoldSegments {
 
828
                        get {
 
829
                                return foldSegmentTree.FoldSegments.Any ();
 
830
                        }
 
831
                }
 
832
                
 
833
                public IEnumerable<FoldSegment> FoldSegments {
 
834
                        get {
 
835
                                return foldSegmentTree.FoldSegments;
 
836
                        }
 
837
                }
 
838
                
 
839
                class FoldSegmentWorkerThread : WorkerThread
 
840
                {
 
841
                        Document doc;
 
842
                        List<FoldSegment> newSegments;
 
843
                        
 
844
                        public FoldSegmentWorkerThread (Document doc, List<FoldSegment> newSegments)
 
845
                        {
 
846
                                this.doc = doc;
 
847
                                this.newSegments = newSegments;
 
848
                        }
 
849
                        
 
850
                        protected override void InnerRun ()
 
851
                        {
 
852
                                newSegments.Sort ();
 
853
                                foreach (FoldSegment foldSegment in newSegments) {
 
854
                                        if (IsStopping)
 
855
                                                return;
 
856
                                        LineSegment startLine = doc.splitter.GetLineByOffset (foldSegment.Offset);
 
857
                                        LineSegment endLine   = doc.splitter.GetLineByOffset (foldSegment.EndOffset);
 
858
                                        foldSegment.EndColumn = foldSegment.EndOffset - endLine.Offset; 
 
859
                                        foldSegment.Column    = foldSegment.Offset - startLine.Offset; 
 
860
                                        foldSegment.EndLine   = endLine;
 
861
                                        foldSegment.StartLine = startLine;
 
862
                                }
 
863
                                FoldSegmentTreeNode newFoldSegmentTree = new FoldSegmentTreeNode ();
 
864
                                foreach (FoldSegment foldSegment in newSegments) {
 
865
                                        newFoldSegmentTree.AddSegment (foldSegment);
 
866
                                }
 
867
                                List<FoldSegment> oldSegments = new List<FoldSegment> (doc.foldSegmentTree.FoldSegments);
 
868
                                        
 
869
                                int i = 0, j = 0;
 
870
                                while (i < oldSegments.Count && j < newSegments.Count) {
 
871
                                        if (IsStopping)
 
872
                                                return;
 
873
                                        int cmp = oldSegments[i].CompareTo (newSegments [j]);
 
874
                                        if (cmp == 0) {
 
875
                                                if (newSegments[j].Length == oldSegments[i].Length) 
 
876
                                                        newSegments[j].IsFolded = oldSegments[i].IsFolded;
 
877
                                                i++;j++;
 
878
                                        } else  if (cmp > 0) {
 
879
                                                j++;
 
880
                                        } else {
 
881
                                                i++;
 
882
                                        }
 
883
                                }
 
884
                                
 
885
                                while (i < oldSegments.Count) {
 
886
                                        newFoldSegmentTree.AddSegment (oldSegments [i]);
 
887
                                        i++;
 
888
                                }
 
889
                        
 
890
                                GLib.Timeout.Add (0, delegate {
 
891
//                                      bool needsUpdate = doc.foldSegments.Count != newSegments.Count;
 
892
                                        doc.foldSegmentTree = newFoldSegmentTree;
 
893
//                                      if (needsUpdate) {
 
894
                                                doc.RequestUpdate (new UpdateAll ());
 
895
                                                doc.CommitDocumentUpdate ();
 
896
//                                      }
 
897
                                        return false;
 
898
                                });
 
899
                                base.Stop ();
 
900
                        }
 
901
                }
 
902
                
 
903
                readonly object syncObject = new object();
 
904
                FoldSegmentWorkerThread foldSegmentWorkerThread = null;
 
905
                
 
906
                public void UpdateFoldSegments (List<FoldSegment> newSegments)
 
907
                {
 
908
                        if (newSegments == null) {
 
909
                                return;
 
910
                        }
 
911
                        
 
912
                        lock (syncObject) {
 
913
                                if (foldSegmentWorkerThread != null) 
 
914
                                        foldSegmentWorkerThread.Stop ();
 
915
                                
 
916
                                foldSegmentWorkerThread = new FoldSegmentWorkerThread (this, newSegments);
 
917
                                foldSegmentWorkerThread.Start ();
 
918
                        }
 
919
                }
 
920
                
 
921
                void InterruptFoldWorker ()
 
922
                {
 
923
                        lock (syncObject) {
 
924
                                if (foldSegmentWorkerThread != null) {
 
925
                                        if (!foldSegmentWorkerThread.IsStopping) {
 
926
                                                foldSegmentWorkerThread.Stop ();
 
927
                                        }
 
928
                                        foldSegmentWorkerThread.WaitForFinish ();
 
929
                                        foldSegmentWorkerThread = null;
 
930
                                }
 
931
                        }
 
932
                }
 
933
                
 
934
                public void ClearFoldSegments ()
 
935
                {
 
936
                        lock (syncObject) {
 
937
                                if (foldSegmentWorkerThread != null) 
 
938
                                        foldSegmentWorkerThread.Stop ();
 
939
                                foldSegmentTree = new FoldSegmentTreeNode ();
 
940
                        }
 
941
                }
 
942
                
 
943
                
 
944
                public IEnumerable<FoldSegment> GetFoldingsFromOffset (int offset)
 
945
                {
 
946
                        if (offset < 0 || offset >= Length)
 
947
                                return new FoldSegment[0];
 
948
                        return foldSegmentTree.GetFoldingsFromOffset (offset);
 
949
                }
 
950
                
 
951
                public IEnumerable<FoldSegment> GetFoldingContaining (int lineNumber)
 
952
                {
 
953
                        return GetFoldingContaining (this.GetLine (lineNumber));
 
954
                }
 
955
                                
 
956
                public IEnumerable<FoldSegment> GetFoldingContaining (LineSegment line)
 
957
                {
 
958
                        if (line == null)
 
959
                                return new FoldSegment[0];
 
960
                        return foldSegmentTree.GetFoldingContaining (line);
 
961
                }
 
962
 
 
963
                public IEnumerable<FoldSegment> GetStartFoldings (int lineNumber)
 
964
                {
 
965
                        return GetStartFoldings (this.GetLine (lineNumber));
 
966
                }
 
967
                
 
968
                public IEnumerable<FoldSegment> GetStartFoldings (LineSegment line)
 
969
                {
 
970
                        if (line == null)
 
971
                                return new FoldSegment[0];
 
972
                        return foldSegmentTree.GetStartFoldings (line);
 
973
                }
 
974
 
 
975
                public IEnumerable<FoldSegment> GetEndFoldings (int lineNumber)
 
976
                {
 
977
                        return GetStartFoldings (this.GetLine (lineNumber));
 
978
                }
 
979
                
 
980
                public IEnumerable<FoldSegment> GetEndFoldings (LineSegment line)
 
981
                {
 
982
                        foreach (FoldSegment segment in GetFoldingContaining (line)) {
 
983
                                if (segment.EndLine.Offset == line.Offset)
 
984
                                        yield return segment;
 
985
                        }
 
986
                }
 
987
                
 
988
                public int GetLineCount (FoldSegment segment)
 
989
                {
 
990
                        return OffsetToLineNumber(segment.EndLine.Offset) - OffsetToLineNumber(segment.StartLine.Offset);
 
991
                }
 
992
                #endregion
 
993
                
 
994
 
 
995
                public void AddMarker (int lineNumber, TextMarker marker)
 
996
                {
 
997
                        AddMarker (this.GetLine (lineNumber), marker);
 
998
                }
 
999
                public void AddMarker (LineSegment line, TextMarker marker)
 
1000
                {
 
1001
                        line.AddMarker (marker);
 
1002
                        this.CommitLineUpdate (line);
 
1003
                }
 
1004
                
 
1005
                public void RemoveMarker (int lineNumber, TextMarker marker)
 
1006
                {
 
1007
                        RemoveMarker (this.GetLine (lineNumber), marker);
 
1008
                }
 
1009
                public void RemoveMarker (LineSegment line, TextMarker marker)
 
1010
                {
 
1011
                        line.RemoveMarker (marker);
 
1012
                        this.CommitLineUpdate (line);
 
1013
                }
 
1014
                public void RemoveMarker (int lineNumber, Type type)
 
1015
                {
 
1016
                        RemoveMarker (this.GetLine (lineNumber), type);
 
1017
                }
 
1018
                public void RemoveMarker (LineSegment line, Type type)
 
1019
                {
 
1020
                        line.RemoveMarker (type);
 
1021
                        this.CommitLineUpdate (line);
 
1022
                }
 
1023
                
 
1024
                public bool Contains (int offset)
 
1025
                {
 
1026
                        return new Segment (0, Length).Contains (offset);
 
1027
                }
 
1028
                
 
1029
                public bool Contains (ISegment segment)
 
1030
                {
 
1031
                        return new Segment (0, Length).Contains (segment);
 
1032
                }
 
1033
                
 
1034
                public int VisualToLogicalLine (int visualLineNumber)
 
1035
                {
 
1036
                        if (visualLineNumber <= 0)
 
1037
                                return 0;
 
1038
                        if (visualLineNumber >= LineCount)
 
1039
                                return visualLineNumber;
 
1040
                        return this.foldSegmentTree.VisualToLogicalLine (this, visualLineNumber);
 
1041
                }
 
1042
                
 
1043
                public DocumentLocation LogicalToVisualLocation (TextEditorData editor, DocumentLocation location)
 
1044
                {
 
1045
                        int line = LogicalToVisualLine (location.Line);
 
1046
                        LineSegment lineSegment = this.GetLine (location.Line);
 
1047
                        int column = lineSegment != null ? lineSegment.GetVisualColumn (editor, this, location.Column) : location.Column;
 
1048
                        return new DocumentLocation (line, column);
 
1049
                }
 
1050
                
 
1051
                public int LogicalToVisualLine (int logicalLineNumber)
 
1052
                {
 
1053
                        return this.foldSegmentTree.LogicalToVisualLine (this, logicalLineNumber);
 
1054
                }
 
1055
                
 
1056
                #region Update logic
 
1057
                List<DocumentUpdateRequest> updateRequests = new List<DocumentUpdateRequest> ();
 
1058
                
 
1059
                public IEnumerable<DocumentUpdateRequest> UpdateRequests {
 
1060
                        get {
 
1061
                                return updateRequests;
 
1062
                        }
 
1063
                }
 
1064
                // Use CanEdit (int lineNumber) instead for getting a request
 
1065
                // if a part of a document can be read. ReadOnly should generally not be used
 
1066
                // for deciding, if a document is readonly or not.
 
1067
                public bool ReadOnly {
 
1068
                        get {
 
1069
                                return readOnly;
 
1070
                        }
 
1071
                        set {
 
1072
                                readOnly = value;
 
1073
                        }
 
1074
                }
 
1075
                
 
1076
                public ReadOnlyCheckDelegate ReadOnlyCheckDelegate {
 
1077
                        get { return readOnlyCheckDelegate; }
 
1078
                        set { readOnlyCheckDelegate = value; }
 
1079
                }
 
1080
 
 
1081
 
 
1082
                public void RequestUpdate (DocumentUpdateRequest request)
 
1083
                {
 
1084
                        lock (syncObject) {
 
1085
                                updateRequests.Add (request);
 
1086
                        }
 
1087
                }
 
1088
                
 
1089
                public void CommitDocumentUpdate ()
 
1090
                {
 
1091
                        lock (syncObject) {
 
1092
                                if (DocumentUpdated != null)
 
1093
                                        DocumentUpdated (this, EventArgs.Empty);
 
1094
                                updateRequests.Clear ();
 
1095
                        }
 
1096
                }
 
1097
                
 
1098
                public void CommitLineToEndUpdate (int line)
 
1099
                {
 
1100
                        RequestUpdate (new LineToEndUpdate (line));
 
1101
                        CommitDocumentUpdate ();
 
1102
                }
 
1103
                                
 
1104
                public void CommitLineUpdate (int line)
 
1105
                {
 
1106
                        RequestUpdate (new LineUpdate (line));
 
1107
                        CommitDocumentUpdate ();
 
1108
                }
 
1109
                
 
1110
                public void CommitLineUpdate (LineSegment line)
 
1111
                {
 
1112
                        CommitLineUpdate (this.OffsetToLineNumber (line.Offset));
 
1113
                }
 
1114
                                        
 
1115
                public void CommitUpdateAll ()
 
1116
                {
 
1117
                        RequestUpdate (new UpdateAll ());
 
1118
                        CommitDocumentUpdate ();
 
1119
                }
 
1120
                                                
 
1121
                public void CommitMultipleLineUpdate (int start, int end)
 
1122
                {
 
1123
                        RequestUpdate (new MultipleLineUpdate (start, end));
 
1124
                        CommitDocumentUpdate ();
 
1125
                }
 
1126
                
 
1127
                public event EventHandler DocumentUpdated;
 
1128
                #endregion
 
1129
        
 
1130
                #region Helper functions
 
1131
                public const string openBrackets    = "([{<";
 
1132
                public const string closingBrackets = ")]}>";
 
1133
                
 
1134
                public static bool IsBracket (char ch)
 
1135
                {
 
1136
                        return (openBrackets + closingBrackets).IndexOf (ch) >= 0;
 
1137
                }
 
1138
                
 
1139
                public static bool IsWordSeparator (char ch)
 
1140
                {
 
1141
                        return Char.IsWhiteSpace (ch) || (Char.IsPunctuation (ch) && ch != '_');
 
1142
                }
 
1143
                
 
1144
                public bool IsWholeWordAt (int offset, int length)
 
1145
                {
 
1146
                        return (offset == 0 || IsWordSeparator (GetCharAt (offset - 1))) &&
 
1147
                                   (offset + length == Length || IsWordSeparator (GetCharAt (offset + length)));
 
1148
                }
 
1149
                
 
1150
                public int GetMatchingBracketOffset (int offset)
 
1151
                {
 
1152
                        if (offset < 0 || offset >= Length)
 
1153
                                return -1;
 
1154
                        char ch = GetCharAt (offset);
 
1155
                        int bracket = openBrackets.IndexOf (ch);
 
1156
                        int result;
 
1157
                        if (bracket >= 0) {
 
1158
                                result = SearchMatchingBracketForward (offset + 1, bracket);
 
1159
                        } else {
 
1160
                                bracket = closingBrackets.IndexOf (ch);
 
1161
                                if (bracket >= 0) {
 
1162
                                        result = SearchMatchingBracketBackward (offset - 1, bracket);
 
1163
                                } else {
 
1164
                                        result = -1;
 
1165
                                }
 
1166
                        }
 
1167
                        return result;
 
1168
                }
 
1169
                
 
1170
                int SearchMatchingBracketForward (int offset, int bracket)
 
1171
                {
 
1172
                        return SearchMatchingBracket (offset, closingBrackets[bracket], openBrackets[bracket], 1);
 
1173
                }
 
1174
                
 
1175
                int SearchMatchingBracketBackward (int offset, int bracket)
 
1176
                {
 
1177
                        return SearchMatchingBracket (offset, openBrackets[bracket], closingBrackets[bracket], -1);
 
1178
                }
 
1179
                
 
1180
                int SearchMatchingBracket (int offset, char openBracket, char closingBracket, int direction)
 
1181
                {
 
1182
                        bool isInString       = false;
 
1183
                        bool isInChar         = false;  
 
1184
                        bool isInBlockComment = false;
 
1185
                        int depth = -1;
 
1186
                        while (offset >= 0 && offset < Length) {
 
1187
                                char ch = GetCharAt (offset);
 
1188
                                switch (ch) {
 
1189
                                        case '/':
 
1190
                                                if (isInBlockComment) 
 
1191
                                                        isInBlockComment = GetCharAt (offset + direction) != '*';
 
1192
                                                if (!isInString && !isInChar && offset - direction < Length) 
 
1193
                                                        isInBlockComment = offset > 0 && GetCharAt (offset - direction) == '*';
 
1194
                                                break;
 
1195
                                        case '"':
 
1196
                                                if (!isInChar && !isInBlockComment) 
 
1197
                                                        isInString = !isInString;
 
1198
                                                break;
 
1199
                                        case '\'':
 
1200
                                                if (!isInString && !isInBlockComment) 
 
1201
                                                        isInChar = !isInChar;
 
1202
                                                break;
 
1203
                                        default :
 
1204
                                                if (ch == closingBracket) {
 
1205
                                                        if (!(isInString || isInChar || isInBlockComment)) 
 
1206
                                                                --depth;
 
1207
                                                } else if (ch == openBracket) {
 
1208
                                                        if (!(isInString || isInChar || isInBlockComment)) {
 
1209
                                                                ++depth;
 
1210
                                                                if (depth == 0) 
 
1211
                                                                        return offset;
 
1212
                                                        }
 
1213
                                                }
 
1214
                                                break;
 
1215
                                }
 
1216
                                offset += direction;
 
1217
                        }
 
1218
                        return -1;
 
1219
                }
 
1220
                
 
1221
                public enum CharacterClass {
 
1222
                        Unknown,
 
1223
                        Whitespace,
 
1224
                        IdentifierPart
 
1225
                }
 
1226
                
 
1227
                public static CharacterClass GetCharacterClass (char ch)
 
1228
                {
 
1229
                        if (Char.IsWhiteSpace (ch))
 
1230
                                return CharacterClass.Whitespace;
 
1231
                        if (Char.IsLetterOrDigit (ch) || ch == '_')
 
1232
                                return CharacterClass.IdentifierPart;
 
1233
                        return CharacterClass.Unknown;
 
1234
                }
 
1235
                
 
1236
                #endregion
 
1237
        }
 
1238
        
 
1239
        public delegate bool ReadOnlyCheckDelegate (int line);
 
1240
}