Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/Editor/StringBuilderDocument.cs @ 16752

Last change on this file since 16752 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 12.6 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
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:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
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.
18
19using System;
20using System.Collections.Generic;
21using System.IO;
22using System.Text;
23using ICSharpCode.NRefactory.Utils;
24
25namespace ICSharpCode.NRefactory.Editor
26{
27  /// <summary>
28  /// Document based on a string builder.
29  /// This class serves as a reference implementation for the IDocument interface.
30  /// </summary>
31  public class StringBuilderDocument : IDocument
32  {
33    readonly StringBuilder b;
34    readonly TextSourceVersionProvider versionProvider = new TextSourceVersionProvider();
35   
36    /// <summary>
37    /// Creates a new StringBuilderDocument.
38    /// </summary>
39    public StringBuilderDocument()
40    {
41      b = new StringBuilder();
42    }
43   
44    /// <summary>
45    /// Creates a new StringBuilderDocument with the specified initial text.
46    /// </summary>
47    public StringBuilderDocument(string text)
48    {
49      if (text == null)
50        throw new ArgumentNullException("text");
51      b = new StringBuilder(text);
52    }
53   
54    /// <summary>
55    /// Creates a new StringBuilderDocument with the initial text copied from the specified text source.
56    /// </summary>
57    public StringBuilderDocument(ITextSource textSource)
58    {
59      if (textSource == null)
60        throw new ArgumentNullException("textSource");
61      b = new StringBuilder(textSource.TextLength);
62      textSource.WriteTextTo(new StringWriter(b));
63    }
64   
65    /// <inheritdoc/>
66    public event EventHandler<TextChangeEventArgs> TextChanging;
67   
68    /// <inheritdoc/>
69    public event EventHandler<TextChangeEventArgs> TextChanged;
70   
71    /// <inheritdoc/>
72    public event EventHandler ChangeCompleted;
73   
74    /// <inheritdoc/>
75    public ITextSourceVersion Version {
76      get { return versionProvider.CurrentVersion; }
77    }
78   
79    #region Line<->Offset
80    /// <inheritdoc/>
81    public int LineCount {
82      get { return CreateDocumentSnapshot().LineCount; }
83    }
84   
85    /// <inheritdoc/>
86    public IDocumentLine GetLineByNumber(int lineNumber)
87    {
88      return CreateDocumentSnapshot().GetLineByNumber(lineNumber);
89    }
90   
91    /// <inheritdoc/>
92    public IDocumentLine GetLineByOffset(int offset)
93    {
94      return CreateDocumentSnapshot().GetLineByOffset(offset);
95    }
96   
97    /// <inheritdoc/>
98    public int GetOffset(int line, int column)
99    {
100      return CreateDocumentSnapshot().GetOffset(line, column);
101    }
102   
103    /// <inheritdoc/>
104    public int GetOffset(TextLocation location)
105    {
106      return CreateDocumentSnapshot().GetOffset(location);
107    }
108   
109    /// <inheritdoc/>
110    public TextLocation GetLocation(int offset)
111    {
112      return CreateDocumentSnapshot().GetLocation(offset);
113    }
114    #endregion
115   
116    #region Insert/Remove/Replace
117    /// <inheritdoc/>
118    public void Insert(int offset, string text)
119    {
120      Replace(offset, 0, text);
121    }
122   
123    /// <inheritdoc/>
124    public void Insert(int offset, ITextSource text)
125    {
126      if (text == null)
127        throw new ArgumentNullException("text");
128      Replace(offset, 0, text.Text);
129    }
130   
131    /// <inheritdoc/>
132    public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType)
133    {
134      if (offset < 0 || offset > this.TextLength)
135        throw new ArgumentOutOfRangeException("offset");
136      if (text == null)
137        throw new ArgumentNullException("text");
138      if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion)
139        PerformChange(new InsertionWithMovementBefore(offset, text));
140      else
141        Replace(offset, 0, text);
142    }
143   
144    /// <inheritdoc/>
145    public void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType)
146    {
147      if (text == null)
148        throw new ArgumentNullException("text");
149      Insert(offset, text.Text, defaultAnchorMovementType);
150    }
151   
152    [Serializable]
153    sealed class InsertionWithMovementBefore : TextChangeEventArgs
154    {
155      public InsertionWithMovementBefore(int offset, string newText) : base(offset, string.Empty, newText)
156      {
157      }
158     
159      public override int GetNewOffset(int offset, AnchorMovementType movementType)
160      {
161        if (offset == this.Offset && movementType == AnchorMovementType.Default)
162          return offset;
163        else
164          return base.GetNewOffset(offset, movementType);
165      }
166    }
167   
168    /// <inheritdoc/>
169    public void Remove(int offset, int length)
170    {
171      Replace(offset, length, string.Empty);
172    }
173   
174    /// <inheritdoc/>
175    public void Replace(int offset, int length, string newText)
176    {
177      if (offset < 0 || offset > this.TextLength)
178        throw new ArgumentOutOfRangeException("offset");
179      if (length < 0 || length > this.TextLength - offset)
180        throw new ArgumentOutOfRangeException("length");
181      if (newText == null)
182        throw new ArgumentNullException("newText");
183      PerformChange(new TextChangeEventArgs(offset, b.ToString(offset, length), newText));
184    }
185   
186    /// <inheritdoc/>
187    public void Replace(int offset, int length, ITextSource newText)
188    {
189      if (newText == null)
190        throw new ArgumentNullException("newText");
191      Replace(offset, length, newText.Text);
192    }
193   
194    bool isInChange;
195   
196    void PerformChange(TextChangeEventArgs change)
197    {
198      // Ensure that all changes take place inside an update group.
199      // Will also take care of throwing an exception if isInChange is set.
200      StartUndoableAction();
201      try {
202        isInChange = true;
203        try {
204          if (TextChanging != null)
205            TextChanging(this, change);
206         
207          // Perform changes to document and Version property
208          documentSnapshot = null;
209          cachedText = null;
210          b.Remove(change.Offset, change.RemovalLength);
211          b.Insert(change.Offset, change.InsertedText.Text);
212          versionProvider.AppendChange(change);
213         
214          // Update anchors and fire Deleted events
215          UpdateAnchors(change);
216         
217          if (TextChanged != null)
218            TextChanged(this, change);
219        } finally {
220          isInChange = false;
221        }
222      } finally {
223        EndUndoableAction();
224      }
225    }
226    #endregion
227   
228    #region Undo
229    int undoGroupNesting = 0;
230   
231    /// <inheritdoc/>
232    public void StartUndoableAction()
233    {
234      // prevent changes from within the TextChanging/TextChanged event handlers
235      if (isInChange)
236        throw new InvalidOperationException();
237      undoGroupNesting++;
238    }
239   
240    /// <inheritdoc/>
241    public void EndUndoableAction()
242    {
243      undoGroupNesting--;
244      if (undoGroupNesting == 0) {
245        if (ChangeCompleted != null)
246          ChangeCompleted(this, EventArgs.Empty);
247      }
248    }
249   
250    /// <inheritdoc/>
251    public IDisposable OpenUndoGroup()
252    {
253      StartUndoableAction();
254      return new CallbackOnDispose(EndUndoableAction);
255    }
256    #endregion
257   
258    #region CreateSnapshot/CreateReader
259    ReadOnlyDocument documentSnapshot;
260   
261    /// <inheritdoc/>
262    public IDocument CreateDocumentSnapshot()
263    {
264      if (documentSnapshot == null)
265        documentSnapshot = new ReadOnlyDocument(this, this.FileName);
266      return documentSnapshot;
267    }
268   
269    /// <inheritdoc/>
270    public ITextSource CreateSnapshot()
271    {
272      return new StringTextSource(this.Text, versionProvider.CurrentVersion);
273    }
274   
275    /// <inheritdoc/>
276    public ITextSource CreateSnapshot(int offset, int length)
277    {
278      return new StringTextSource(GetText(offset, length));
279    }
280   
281    /// <inheritdoc/>
282    public TextReader CreateReader()
283    {
284      return new StringReader(this.Text);
285    }
286   
287    /// <inheritdoc/>
288    public TextReader CreateReader(int offset, int length)
289    {
290      return new StringReader(GetText(offset, length));
291    }
292   
293    /// <inheritdoc/>
294    public void WriteTextTo(TextWriter writer)
295    {
296      if (writer == null)
297        throw new ArgumentNullException("writer");
298      writer.Write(this.Text);
299    }
300   
301    /// <inheritdoc/>
302    public void WriteTextTo(TextWriter writer, int offset, int length)
303    {
304      if (writer == null)
305        throw new ArgumentNullException("writer");
306      writer.Write(GetText(offset, length));
307    }
308    #endregion
309   
310    #region GetText / IndexOf
311    string cachedText;
312   
313    /// <inheritdoc/>
314    public string Text {
315      get {
316        if (cachedText == null)
317          cachedText = b.ToString();
318        return cachedText;
319      }
320      set {
321        Replace(0, b.Length, value);
322      }
323    }
324   
325    /// <inheritdoc/>
326    public int TextLength {
327      get { return b.Length; }
328    }
329   
330    /// <inheritdoc/>
331    public char GetCharAt(int offset)
332    {
333      return b[offset];
334    }
335   
336    /// <inheritdoc/>
337    public string GetText(int offset, int length)
338    {
339      return b.ToString(offset, length);
340    }
341   
342    /// <inheritdoc/>
343    public string GetText(ISegment segment)
344    {
345      if (segment == null)
346        throw new ArgumentNullException("segment");
347      return b.ToString(segment.Offset, segment.Length);
348    }
349   
350    /// <inheritdoc/>
351    public int IndexOf(char c, int startIndex, int count)
352    {
353      return this.Text.IndexOf(c, startIndex, count);
354    }
355   
356    /// <inheritdoc/>
357    public int IndexOfAny(char[] anyOf, int startIndex, int count)
358    {
359      return this.Text.IndexOfAny(anyOf, startIndex, count);
360    }
361   
362    /// <inheritdoc/>
363    public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
364    {
365      return this.Text.IndexOf(searchText, startIndex, count, comparisonType);
366    }
367   
368    /// <inheritdoc/>
369    public int LastIndexOf(char c, int startIndex, int count)
370    {
371      return this.Text.LastIndexOf(c, startIndex + count - 1, count);
372    }
373   
374    /// <inheritdoc/>
375    public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
376    {
377      return this.Text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType);
378    }
379    #endregion
380   
381    #region CreateAnchor
382    readonly List<WeakReference> anchors = new List<WeakReference>();
383   
384    /// <inheritdoc/>
385    public ITextAnchor CreateAnchor(int offset)
386    {
387      var newAnchor = new SimpleAnchor(this, offset);
388      for (int i = 0; i < anchors.Count; i++) {
389        if (!anchors[i].IsAlive)
390          anchors[i] = new WeakReference(newAnchor);
391      }
392      anchors.Add(new WeakReference(newAnchor));
393      return newAnchor;
394    }
395   
396    void UpdateAnchors(TextChangeEventArgs change)
397    {
398      // First update all anchors, then fire the deleted events.
399      List<int> deletedAnchors = new List<int>();
400      for (int i = 0; i < anchors.Count; i++) {
401        var anchor = anchors[i].Target as SimpleAnchor;
402        if (anchor != null) {
403          anchor.Update(change);
404          if (anchor.IsDeleted)
405            deletedAnchors.Add(i);
406        }
407      }
408      deletedAnchors.Reverse();
409      foreach (var index in deletedAnchors) {
410        var anchor = anchors[index].Target as SimpleAnchor;
411        if (anchor != null)
412          anchor.RaiseDeletedEvent();
413        anchors.RemoveAt(index);
414      }
415    }
416   
417    sealed class SimpleAnchor : ITextAnchor
418    {
419      readonly StringBuilderDocument document;
420      int offset;
421     
422      public SimpleAnchor(StringBuilderDocument document, int offset)
423      {
424        this.document = document;
425        this.offset = offset;
426      }
427     
428      public event EventHandler Deleted;
429     
430      public TextLocation Location {
431        get {
432          if (IsDeleted)
433            throw new InvalidOperationException();
434          return document.GetLocation(offset);
435        }
436      }
437     
438      public int Offset {
439        get {
440          if (IsDeleted)
441            throw new InvalidOperationException();
442          return offset;
443        }
444      }
445     
446      public AnchorMovementType MovementType { get; set; }
447     
448      public bool SurviveDeletion { get; set; }
449     
450      public bool IsDeleted {
451        get { return offset < 0; }
452      }
453     
454      public void Update(TextChangeEventArgs change)
455      {
456        if (SurviveDeletion || offset <= change.Offset || offset >= change.Offset + change.RemovalLength) {
457          offset = change.GetNewOffset(offset, MovementType);
458        } else {
459          offset = -1;
460        }
461      }
462     
463      public void RaiseDeletedEvent()
464      {
465        if (Deleted != null)
466          Deleted(this, EventArgs.Empty);
467      }
468     
469      public int Line {
470        get { return this.Location.Line; }
471      }
472     
473      public int Column {
474        get { return this.Location.Column; }
475      }
476    }
477    #endregion
478   
479    /// <inheritdoc/>
480    public virtual object GetService(Type serviceType)
481    {
482      return null;
483    }
484   
485    /// <inheritdoc/>
486    public virtual event EventHandler FileNameChanged { add {} remove {} }
487   
488    /// <inheritdoc/>
489    public virtual string FileName {
490      get { return string.Empty; }
491    }
492  }
493}
Note: See TracBrowser for help on using the repository browser.