~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegment.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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)
 
3
 
 
4
using System;
 
5
using System.Diagnostics;
 
6
 
 
7
namespace ICSharpCode.AvalonEdit.Document
 
8
{
 
9
        /// <summary>
 
10
        /// A segment that can be put into a <see cref="TextSegmentCollection{T}"/>.
 
11
        /// </summary>
 
12
        /// <remarks>
 
13
        /// <para>
 
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.
 
16
        /// </para>
 
17
        /// <para>
 
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>
 
22
        /// <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>.
 
26
        /// </para>
 
27
        /// <para>
 
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.
 
32
        /// </para>
 
33
        /// </remarks>
 
34
        /// <seealso cref="ISegment"/>
 
35
        /// <seealso cref="AnchorSegment"/>
 
36
        /// <seealso cref="TextSegmentCollection{T}"/>
 
37
        public class TextSegment : ISegment
 
38
        {
 
39
                internal ISegmentTree ownerTree;
 
40
                internal TextSegment left, right, parent;
 
41
                
 
42
                /// <summary>
 
43
                /// The color of the segment in the red/black tree.
 
44
                /// </summary>
 
45
                internal bool color;
 
46
                
 
47
                /// <summary>
 
48
                /// The "length" of the node (distance to previous node)
 
49
                /// </summary>
 
50
                internal int nodeLength;
 
51
                
 
52
                /// <summary>
 
53
                /// The total "length" of this subtree.
 
54
                /// </summary>
 
55
                internal int totalNodeLength; // totalNodeLength = nodeLength + left.totalNodeLength + right.totalNodeLength
 
56
                
 
57
                /// <summary>
 
58
                /// The length of the segment (do not confuse with nodeLength).
 
59
                /// </summary>
 
60
                internal int segmentLength;
 
61
                
 
62
                /// <summary>
 
63
                /// distanceToMaxEnd = Max(segmentLength,
 
64
                ///                        left.distanceToMaxEnd + left.Offset - Offset,
 
65
                ///                        left.distanceToMaxEnd + right.Offset - Offset)
 
66
                /// </summary>
 
67
                internal int distanceToMaxEnd;
 
68
                
 
69
                int ISegment.Offset {
 
70
                        get { return StartOffset; }
 
71
                }
 
72
                
 
73
                /// <summary>
 
74
                /// Gets whether this segment is connected to a TextSegmentCollection and will automatically
 
75
                /// update its offsets.
 
76
                /// </summary>
 
77
                protected bool IsConnectedToCollection {
 
78
                        get {
 
79
                                return ownerTree != null;
 
80
                        }
 
81
                }
 
82
                
 
83
                /// <summary>
 
84
                /// Gets/Sets the start offset of the segment.
 
85
                /// </summary>
 
86
                /// <remarks>
 
87
                /// When setting the start offset, the end offset will change, too: the Length of the segment will stay constant.
 
88
                /// </remarks>
 
89
                public int StartOffset {
 
90
                        get {
 
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));
 
95
                                
 
96
                                TextSegment n = this;
 
97
                                int offset = n.nodeLength;
 
98
                                if (n.left != null)
 
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;
 
105
                                        }
 
106
                                        n = n.parent;
 
107
                                }
 
108
                                return offset;
 
109
                        }
 
110
                        set {
 
111
                                if (value < 0)
 
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);
 
118
                                                nodeLength = value;
 
119
                                                ownerTree.Add(this);
 
120
                                        } else {
 
121
                                                nodeLength = value;
 
122
                                        }
 
123
                                }
 
124
                        }
 
125
                }
 
126
                
 
127
                /// <summary>
 
128
                /// Gets/Sets the end offset of the segment.
 
129
                /// </summary>
 
130
                /// <remarks>
 
131
                /// Setting the end offset will change the length, the start offset will stay constant.
 
132
                /// </remarks>
 
133
                public int EndOffset {
 
134
                        get {
 
135
                                return StartOffset + Length;
 
136
                        }
 
137
                        set {
 
138
                                int newLength = value - StartOffset;
 
139
                                if (newLength < 0)
 
140
                                        throw new ArgumentOutOfRangeException("value", "EndOffset must be greater or equal to StartOffset");
 
141
                                Length = newLength;
 
142
                        }
 
143
                }
 
144
                
 
145
                /// <summary>
 
146
                /// Gets/Sets the length of the segment.
 
147
                /// </summary>
 
148
                /// <remarks>
 
149
                /// Setting the length will change the end offset, the start offset will stay constant.
 
150
                /// </remarks>
 
151
                public int Length {
 
152
                        get {
 
153
                                return segmentLength;
 
154
                        }
 
155
                        set {
 
156
                                if (value < 0)
 
157
                                        throw new ArgumentOutOfRangeException("value", "Length must not be negative");
 
158
                                segmentLength = value;
 
159
                                if (ownerTree != null)
 
160
                                        ownerTree.UpdateAugmentedData(this);
 
161
                        }
 
162
                }
 
163
                
 
164
                internal TextSegment LeftMost {
 
165
                        get {
 
166
                                TextSegment node = this;
 
167
                                while (node.left != null)
 
168
                                        node = node.left;
 
169
                                return node;
 
170
                        }
 
171
                }
 
172
                
 
173
                internal TextSegment RightMost {
 
174
                        get {
 
175
                                TextSegment node = this;
 
176
                                while (node.right != null)
 
177
                                        node = node.right;
 
178
                                return node;
 
179
                        }
 
180
                }
 
181
                
 
182
                /// <summary>
 
183
                /// Gets the inorder successor of the node.
 
184
                /// </summary>
 
185
                internal TextSegment Successor {
 
186
                        get {
 
187
                                if (right != null) {
 
188
                                        return right.LeftMost;
 
189
                                } else {
 
190
                                        TextSegment node = this;
 
191
                                        TextSegment oldNode;
 
192
                                        do {
 
193
                                                oldNode = node;
 
194
                                                node = node.parent;
 
195
                                                // go up until we are coming out of a left subtree
 
196
                                        } while (node != null && node.right == oldNode);
 
197
                                        return node;
 
198
                                }
 
199
                        }
 
200
                }
 
201
                
 
202
                /// <summary>
 
203
                /// Gets the inorder predecessor of the node.
 
204
                /// </summary>
 
205
                internal TextSegment Predecessor {
 
206
                        get {
 
207
                                if (left != null) {
 
208
                                        return left.RightMost;
 
209
                                } else {
 
210
                                        TextSegment node = this;
 
211
                                        TextSegment oldNode;
 
212
                                        do {
 
213
                                                oldNode = node;
 
214
                                                node = node.parent;
 
215
                                                // go up until we are coming out of a right subtree
 
216
                                        } while (node != null && node.left == oldNode);
 
217
                                        return node;
 
218
                                }
 
219
                        }
 
220
                }
 
221
                
 
222
                #if DEBUG
 
223
                internal string ToDebugString()
 
224
                {
 
225
                        return "[nodeLength=" + nodeLength + " totalNodeLength=" + totalNodeLength
 
226
                                + " distanceToMaxEnd=" + distanceToMaxEnd + " MaxEndOffset=" + (StartOffset + distanceToMaxEnd) + "]";
 
227
                }
 
228
                #endif
 
229
                
 
230
                /// <inheritdoc/>
 
231
                public override string ToString()
 
232
                {
 
233
                        return "[" + GetType().Name + " Offset=" + StartOffset + " Length=" + Length + " EndOffset=" + EndOffset + "]";
 
234
                }
 
235
        }
 
236
}