1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Diagnostics;
7
namespace ICSharpCode.AvalonEdit.Document
10
/// A segment that can be put into a <see cref="TextSegmentCollection{T}"/>.
14
/// A <see cref="TextSegment"/> can be stand-alone or part of a <see cref="TextSegmentCollection{T}"/>.
15
/// If the segment is stored inside a TextSegmentCollection, its Offset and Length will be updated by that collection.
18
/// When the document changes, the offsets of all text segments in the TextSegmentCollection will be adjusted accordingly.
19
/// Start offsets move like <see cref="AnchorMovementType">AnchorMovementType.AfterInsertion</see>,
20
/// end offsets move like <see cref="AnchorMovementType">AnchorMovementType.BeforeInsertion</see>
21
/// (i.e. the segment will always stay as small as possible).</para>
23
/// If a document change causes a segment to be deleted completely, it will be reduced to length 0, but segments are
24
/// never automatically removed from the collection.
25
/// Segments with length 0 will never expand due to document changes, and they move as <c>AfterInsertion</c>.
28
/// Thread-safety: a TextSegmentCollection that is connected to a <see cref="TextDocument"/> may only be used on that document's owner thread.
29
/// A disconnected TextSegmentCollection is safe for concurrent reads, but concurrent access is not safe when there are writes.
30
/// Keep in mind that reading the Offset properties of a text segment inside the collection is a read access on the
31
/// collection; and setting an Offset property of a text segment is a write access on the collection.
34
/// <seealso cref="ISegment"/>
35
/// <seealso cref="AnchorSegment"/>
36
/// <seealso cref="TextSegmentCollection{T}"/>
37
public class TextSegment : ISegment
39
internal ISegmentTree ownerTree;
40
internal TextSegment left, right, parent;
43
/// The color of the segment in the red/black tree.
48
/// The "length" of the node (distance to previous node)
50
internal int nodeLength;
53
/// The total "length" of this subtree.
55
internal int totalNodeLength; // totalNodeLength = nodeLength + left.totalNodeLength + right.totalNodeLength
58
/// The length of the segment (do not confuse with nodeLength).
60
internal int segmentLength;
63
/// distanceToMaxEnd = Max(segmentLength,
64
/// left.distanceToMaxEnd + left.Offset - Offset,
65
/// left.distanceToMaxEnd + right.Offset - Offset)
67
internal int distanceToMaxEnd;
70
get { return StartOffset; }
74
/// Gets whether this segment is connected to a TextSegmentCollection and will automatically
75
/// update its offsets.
77
protected bool IsConnectedToCollection {
79
return ownerTree != null;
84
/// Gets/Sets the start offset of the segment.
87
/// When setting the start offset, the end offset will change, too: the Length of the segment will stay constant.
89
public int StartOffset {
91
// If the segment is not connected to a tree, we store the offset in "nodeLength".
92
// Otherwise, "nodeLength" contains the distance to the start offset of the previous node
93
Debug.Assert(!(ownerTree == null && parent != null));
94
Debug.Assert(!(ownerTree == null && left != null));
97
int offset = n.nodeLength;
99
offset += n.left.totalNodeLength;
100
while (n.parent != null) {
101
if (n == n.parent.right) {
102
if (n.parent.left != null)
103
offset += n.parent.left.totalNodeLength;
104
offset += n.parent.nodeLength;
112
throw new ArgumentOutOfRangeException("value", "Offset must not be negative");
113
if (this.StartOffset != value) {
114
// need a copy of the variable because ownerTree.Remove() sets this.ownerTree to null
115
ISegmentTree ownerTree = this.ownerTree;
116
if (ownerTree != null) {
117
ownerTree.Remove(this);
128
/// Gets/Sets the end offset of the segment.
131
/// Setting the end offset will change the length, the start offset will stay constant.
133
public int EndOffset {
135
return StartOffset + Length;
138
int newLength = value - StartOffset;
140
throw new ArgumentOutOfRangeException("value", "EndOffset must be greater or equal to StartOffset");
146
/// Gets/Sets the length of the segment.
149
/// Setting the length will change the end offset, the start offset will stay constant.
153
return segmentLength;
157
throw new ArgumentOutOfRangeException("value", "Length must not be negative");
158
segmentLength = value;
159
if (ownerTree != null)
160
ownerTree.UpdateAugmentedData(this);
164
internal TextSegment LeftMost {
166
TextSegment node = this;
167
while (node.left != null)
173
internal TextSegment RightMost {
175
TextSegment node = this;
176
while (node.right != null)
183
/// Gets the inorder successor of the node.
185
internal TextSegment Successor {
188
return right.LeftMost;
190
TextSegment node = this;
195
// go up until we are coming out of a left subtree
196
} while (node != null && node.right == oldNode);
203
/// Gets the inorder predecessor of the node.
205
internal TextSegment Predecessor {
208
return left.RightMost;
210
TextSegment node = this;
215
// go up until we are coming out of a right subtree
216
} while (node != null && node.left == oldNode);
223
internal string ToDebugString()
225
return "[nodeLength=" + nodeLength + " totalNodeLength=" + totalNodeLength
226
+ " distanceToMaxEnd=" + distanceToMaxEnd + " MaxEndOffset=" + (StartOffset + distanceToMaxEnd) + "]";
231
public override string ToString()
233
return "[" + GetType().Name + " Offset=" + StartOffset + " Length=" + Length + " EndOffset=" + EndOffset + "]";