1
ļ»æ// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
20
using System.Collections.Generic;
23
using ICSharpCode.NRefactory.Utils;
25
namespace ICSharpCode.NRefactory.Editor
28
/// Document based on a string builder.
29
/// This class serves as a reference implementation for the IDocument interface.
31
public class StringBuilderDocument : IDocument
33
readonly StringBuilder b;
34
readonly TextSourceVersionProvider versionProvider = new TextSourceVersionProvider();
37
/// Creates a new StringBuilderDocument.
39
public StringBuilderDocument()
41
b = new StringBuilder();
44
public StringBuilderDocument(string text)
46
b = new StringBuilder(text);
50
public event EventHandler<TextChangeEventArgs> TextChanging;
53
public event EventHandler<TextChangeEventArgs> TextChanged;
56
public event EventHandler ChangeCompleted;
59
public ITextSourceVersion Version {
60
get { return versionProvider.CurrentVersion; }
65
public int LineCount {
66
get { return CreateDocumentSnapshot().LineCount; }
70
public IDocumentLine GetLineByNumber(int lineNumber)
72
return CreateDocumentSnapshot().GetLineByNumber(lineNumber);
76
public IDocumentLine GetLineByOffset(int offset)
78
return CreateDocumentSnapshot().GetLineByOffset(offset);
82
public int GetOffset(int line, int column)
84
return CreateDocumentSnapshot().GetOffset(line, column);
88
public int GetOffset(TextLocation location)
90
return CreateDocumentSnapshot().GetOffset(location);
94
public TextLocation GetLocation(int offset)
96
return CreateDocumentSnapshot().GetLocation(offset);
100
#region Insert/Remove/Replace
102
public void Insert(int offset, string text)
104
Replace(offset, 0, text);
108
public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType)
110
if (offset < 0 || offset > this.TextLength)
111
throw new ArgumentOutOfRangeException("offset");
112
if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion)
113
PerformChange(new InsertionWithMovementBefore(offset, text));
115
Replace(offset, 0, text);
119
sealed class InsertionWithMovementBefore : TextChangeEventArgs
121
public InsertionWithMovementBefore(int offset, string newText) : base(offset, string.Empty, newText)
125
public override int GetNewOffset(int offset, AnchorMovementType movementType)
127
if (offset == this.Offset && movementType == AnchorMovementType.Default)
130
return base.GetNewOffset(offset, movementType);
135
public void Remove(int offset, int length)
137
Replace(offset, length, string.Empty);
141
public void Replace(int offset, int length, string newText)
143
if (offset < 0 || offset > this.TextLength)
144
throw new ArgumentOutOfRangeException("offset");
145
if (length < 0 || length > this.TextLength - offset)
146
throw new ArgumentOutOfRangeException("length");
148
throw new ArgumentNullException("newText");
149
PerformChange(new TextChangeEventArgs(offset, b.ToString(offset, length), newText));
154
void PerformChange(TextChangeEventArgs change)
156
// Ensure that all changes take place inside an update group.
157
// Will also take care of throwing an exception if isInChange is set.
158
StartUndoableAction();
162
if (TextChanging != null)
163
TextChanging(this, change);
165
// Perform changes to document and Version property
166
documentSnapshot = null;
168
b.Remove(change.Offset, change.RemovalLength);
169
b.Insert(change.Offset, change.InsertedText);
170
versionProvider.AppendChange(change);
172
// Update anchors and fire Deleted events
173
UpdateAnchors(change);
175
if (TextChanged != null)
176
TextChanged(this, change);
187
int undoGroupNesting = 0;
190
public void StartUndoableAction()
192
// prevent changes from within the TextChanging/TextChanged event handlers
194
throw new InvalidOperationException();
199
public void EndUndoableAction()
202
if (undoGroupNesting == 0) {
203
if (ChangeCompleted != null)
204
ChangeCompleted(this, EventArgs.Empty);
209
public IDisposable OpenUndoGroup()
211
StartUndoableAction();
212
return new CallbackOnDispose(EndUndoableAction);
216
#region CreateSnapshot/CreateReader
217
ReadOnlyDocument documentSnapshot;
220
public IDocument CreateDocumentSnapshot()
222
if (documentSnapshot == null)
223
documentSnapshot = new ReadOnlyDocument(this);
224
return documentSnapshot;
228
public ITextSource CreateSnapshot()
230
return new StringTextSource(this.Text, versionProvider.CurrentVersion);
234
public ITextSource CreateSnapshot(int offset, int length)
236
return new StringTextSource(GetText(offset, length));
240
public TextReader CreateReader()
242
return new StringReader(this.Text);
246
public TextReader CreateReader(int offset, int length)
248
return new StringReader(GetText(offset, length));
252
#region GetText / IndexOf
258
if (cachedText == null)
259
cachedText = b.ToString();
263
Replace(0, b.Length, value);
268
public int TextLength {
269
get { return b.Length; }
273
public char GetCharAt(int offset)
279
public string GetText(int offset, int length)
281
return b.ToString(offset, length);
285
public string GetText(ISegment segment)
287
return b.ToString(segment.Offset, segment.Length);
291
public int IndexOf(char c, int startIndex, int count)
293
return this.Text.IndexOf(c, startIndex, count);
297
public int IndexOfAny(char[] anyOf, int startIndex, int count)
299
return this.Text.IndexOfAny(anyOf, startIndex, count);
303
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
305
return this.Text.IndexOf(searchText, startIndex, count, comparisonType);
309
public int LastIndexOf(char c, int startIndex, int count)
311
return this.Text.LastIndexOf(c, startIndex + count - 1, count);
315
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
317
return this.Text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType);
322
readonly List<WeakReference> anchors = new List<WeakReference>();
325
public ITextAnchor CreateAnchor(int offset)
327
var newAnchor = new SimpleAnchor(this, offset);
328
for (int i = 0; i < anchors.Count; i++) {
329
if (!anchors[i].IsAlive)
330
anchors[i] = new WeakReference(newAnchor);
332
anchors.Add(new WeakReference(newAnchor));
336
void UpdateAnchors(TextChangeEventArgs change)
338
// First update all anchors, then fire the deleted events.
339
List<int> deletedAnchors = new List<int>();
340
for (int i = 0; i < anchors.Count; i++) {
341
var anchor = anchors[i].Target as SimpleAnchor;
342
if (anchor != null) {
343
anchor.Update(change);
344
if (anchor.IsDeleted)
345
deletedAnchors.Add(i);
348
deletedAnchors.Reverse();
349
foreach (var index in deletedAnchors) {
350
var anchor = anchors[index].Target as SimpleAnchor;
352
anchor.RaiseDeletedEvent();
353
anchors.RemoveAt(index);
357
sealed class SimpleAnchor : ITextAnchor
359
readonly StringBuilderDocument document;
362
public SimpleAnchor(StringBuilderDocument document, int offset)
364
this.document = document;
365
this.offset = offset;
368
public event EventHandler Deleted;
370
public TextLocation Location {
373
throw new InvalidOperationException();
374
return document.GetLocation(offset);
381
throw new InvalidOperationException();
386
public AnchorMovementType MovementType { get; set; }
388
public bool SurviveDeletion { get; set; }
390
public bool IsDeleted {
391
get { return offset < 0; }
394
public void Update(TextChangeEventArgs change)
396
if (SurviveDeletion || offset <= change.Offset || offset >= change.Offset + change.RemovalLength) {
397
offset = change.GetNewOffset(offset, MovementType);
403
public void RaiseDeletedEvent()
406
Deleted(this, EventArgs.Empty);
410
get { return this.Location.Line; }
414
get { return this.Location.Column; }
420
public virtual object GetService(Type serviceType)