Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HiveProjectManagement/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Document/TextSegment.cs @ 15377

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

#2077: created branch and added first version

File size: 8.4 KB
Line 
1// Copyright (c) 2014 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.Diagnostics;
21#if NREFACTORY
22using ICSharpCode.NRefactory.Editor;
23#endif
24
25namespace ICSharpCode.AvalonEdit.Document
26{
27  /// <summary>
28  /// A segment that can be put into a <see cref="TextSegmentCollection{T}"/>.
29  /// </summary>
30  /// <remarks>
31  /// <para>
32  /// A <see cref="TextSegment"/> can be stand-alone or part of a <see cref="TextSegmentCollection{T}"/>.
33  /// If the segment is stored inside a TextSegmentCollection, its Offset and Length will be updated by that collection.
34  /// </para>
35  /// <para>
36  /// When the document changes, the offsets of all text segments in the TextSegmentCollection will be adjusted accordingly.
37  /// Start offsets move like <see cref="AnchorMovementType">AnchorMovementType.AfterInsertion</see>,
38  /// end offsets move like <see cref="AnchorMovementType">AnchorMovementType.BeforeInsertion</see>
39  /// (i.e. the segment will always stay as small as possible).</para>
40  /// <para>
41  /// If a document change causes a segment to be deleted completely, it will be reduced to length 0, but segments are
42  /// never automatically removed from the collection.
43  /// Segments with length 0 will never expand due to document changes, and they move as <c>AfterInsertion</c>.
44  /// </para>
45  /// <para>
46  /// Thread-safety: a TextSegmentCollection that is connected to a <see cref="TextDocument"/> may only be used on that document's owner thread.
47  /// A disconnected TextSegmentCollection is safe for concurrent reads, but concurrent access is not safe when there are writes.
48  /// Keep in mind that reading the Offset properties of a text segment inside the collection is a read access on the
49  /// collection; and setting an Offset property of a text segment is a write access on the collection.
50  /// </para>
51  /// </remarks>
52  /// <seealso cref="ISegment"/>
53  /// <seealso cref="AnchorSegment"/>
54  /// <seealso cref="TextSegmentCollection{T}"/>
55  public class TextSegment : ISegment
56  {
57    internal ISegmentTree ownerTree;
58    internal TextSegment left, right, parent;
59   
60    /// <summary>
61    /// The color of the segment in the red/black tree.
62    /// </summary>
63    internal bool color;
64   
65    /// <summary>
66    /// The "length" of the node (distance to previous node)
67    /// </summary>
68    internal int nodeLength;
69   
70    /// <summary>
71    /// The total "length" of this subtree.
72    /// </summary>
73    internal int totalNodeLength; // totalNodeLength = nodeLength + left.totalNodeLength + right.totalNodeLength
74   
75    /// <summary>
76    /// The length of the segment (do not confuse with nodeLength).
77    /// </summary>
78    internal int segmentLength;
79   
80    /// <summary>
81    /// distanceToMaxEnd = Max(segmentLength,
82    ///                        left.distanceToMaxEnd + left.Offset - Offset,
83    ///                        left.distanceToMaxEnd + right.Offset - Offset)
84    /// </summary>
85    internal int distanceToMaxEnd;
86   
87    int ISegment.Offset {
88      get { return StartOffset; }
89    }
90   
91    /// <summary>
92    /// Gets whether this segment is connected to a TextSegmentCollection and will automatically
93    /// update its offsets.
94    /// </summary>
95    protected bool IsConnectedToCollection {
96      get {
97        return ownerTree != null;
98      }
99    }
100   
101    /// <summary>
102    /// Gets/Sets the start offset of the segment.
103    /// </summary>
104    /// <remarks>
105    /// When setting the start offset, the end offset will change, too: the Length of the segment will stay constant.
106    /// </remarks>
107    public int StartOffset {
108      get {
109        // If the segment is not connected to a tree, we store the offset in "nodeLength".
110        // Otherwise, "nodeLength" contains the distance to the start offset of the previous node
111        Debug.Assert(!(ownerTree == null && parent != null));
112        Debug.Assert(!(ownerTree == null && left != null));
113       
114        TextSegment n = this;
115        int offset = n.nodeLength;
116        if (n.left != null)
117          offset += n.left.totalNodeLength;
118        while (n.parent != null) {
119          if (n == n.parent.right) {
120            if (n.parent.left != null)
121              offset += n.parent.left.totalNodeLength;
122            offset += n.parent.nodeLength;
123          }
124          n = n.parent;
125        }
126        return offset;
127      }
128      set {
129        if (value < 0)
130          throw new ArgumentOutOfRangeException("value", "Offset must not be negative");
131        if (this.StartOffset != value) {
132          // need a copy of the variable because ownerTree.Remove() sets this.ownerTree to null
133          ISegmentTree ownerTree = this.ownerTree;
134          if (ownerTree != null) {
135            ownerTree.Remove(this);
136            nodeLength = value;
137            ownerTree.Add(this);
138          } else {
139            nodeLength = value;
140          }
141          OnSegmentChanged();
142        }
143      }
144    }
145   
146    /// <summary>
147    /// Gets/Sets the end offset of the segment.
148    /// </summary>
149    /// <remarks>
150    /// Setting the end offset will change the length, the start offset will stay constant.
151    /// </remarks>
152    public int EndOffset {
153      get {
154        return StartOffset + Length;
155      }
156      set {
157        int newLength = value - StartOffset;
158        if (newLength < 0)
159          throw new ArgumentOutOfRangeException("value", "EndOffset must be greater or equal to StartOffset");
160        Length = newLength;
161      }
162    }
163   
164    /// <summary>
165    /// Gets/Sets the length of the segment.
166    /// </summary>
167    /// <remarks>
168    /// Setting the length will change the end offset, the start offset will stay constant.
169    /// </remarks>
170    public int Length {
171      get {
172        return segmentLength;
173      }
174      set {
175        if (value < 0)
176          throw new ArgumentOutOfRangeException("value", "Length must not be negative");
177        if (segmentLength != value) {
178          segmentLength = value;
179          if (ownerTree != null)
180            ownerTree.UpdateAugmentedData(this);
181          OnSegmentChanged();
182        }
183      }
184    }
185   
186    /// <summary>
187    /// This method gets called when the StartOffset/Length/EndOffset properties are set.
188    /// It is not called when StartOffset/Length/EndOffset change due to document changes
189    /// </summary>
190    protected virtual void OnSegmentChanged()
191    {
192    }
193   
194    internal TextSegment LeftMost {
195      get {
196        TextSegment node = this;
197        while (node.left != null)
198          node = node.left;
199        return node;
200      }
201    }
202   
203    internal TextSegment RightMost {
204      get {
205        TextSegment node = this;
206        while (node.right != null)
207          node = node.right;
208        return node;
209      }
210    }
211   
212    /// <summary>
213    /// Gets the inorder successor of the node.
214    /// </summary>
215    internal TextSegment Successor {
216      get {
217        if (right != null) {
218          return right.LeftMost;
219        } else {
220          TextSegment node = this;
221          TextSegment oldNode;
222          do {
223            oldNode = node;
224            node = node.parent;
225            // go up until we are coming out of a left subtree
226          } while (node != null && node.right == oldNode);
227          return node;
228        }
229      }
230    }
231   
232    /// <summary>
233    /// Gets the inorder predecessor of the node.
234    /// </summary>
235    internal TextSegment Predecessor {
236      get {
237        if (left != null) {
238          return left.RightMost;
239        } else {
240          TextSegment node = this;
241          TextSegment oldNode;
242          do {
243            oldNode = node;
244            node = node.parent;
245            // go up until we are coming out of a right subtree
246          } while (node != null && node.left == oldNode);
247          return node;
248        }
249      }
250    }
251   
252    #if DEBUG
253    internal string ToDebugString()
254    {
255      return "[nodeLength=" + nodeLength + " totalNodeLength=" + totalNodeLength
256        + " distanceToMaxEnd=" + distanceToMaxEnd + " MaxEndOffset=" + (StartOffset + distanceToMaxEnd) + "]";
257    }
258    #endif
259   
260    /// <inheritdoc/>
261    public override string ToString()
262    {
263      return "[" + GetType().Name + " Offset=" + StartOffset + " Length=" + Length + " EndOffset=" + EndOffset + "]";
264    }
265  }
266}
Note: See TracBrowser for help on using the repository browser.