1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using ICSharpCode.AvalonEdit.Utils;
using System.Globalization;
namespace ICSharpCode.AvalonEdit.Document
{
/// <summary>
/// An (Offset,Length)-pair.
/// </summary>
/// <seealso cref="TextSegment"/>
/// <seealso cref="AnchorSegment"/>
public interface ISegment
{
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
int Offset { get; }
/// <summary>
/// Gets the length of the segment.
/// </summary>
/// <remarks>Must not be negative.</remarks>
int Length { get; }
/// <summary>
/// Gets the end offset of the segment.
/// </summary>
/// <remarks>EndOffset = Offset + Length;</remarks>
int EndOffset { get; }
}
static class SegmentExtensions
{
/// <summary>
/// Gets whether the segment contains the offset.
/// </summary>
/// <returns>
/// True, if offset is between segment.Start and segment.End (inclusive); otherwise, false.
/// </returns>
public static bool Contains(this ISegment segment, int offset)
{
int start = segment.Offset;
int end = start + segment.Length;
return offset >= start && offset <= end;
}
/// <summary>
/// Gets the overlapping portion of the segments.
/// Returns SimpleSegment.Invalid if the segments don't overlap.
/// </summary>
public static SimpleSegment GetOverlap(this ISegment segment, ISegment other)
{
int start = Math.Max(segment.Offset, other.Offset);
int end = Math.Min(segment.EndOffset, other.EndOffset);
if (end < start)
return SimpleSegment.Invalid;
else
return new SimpleSegment(start, end - start);
}
}
/// <summary>
/// Represents a simple segment (Offset,Length pair) that is not automatically updated
/// on document changes.
/// </summary>
struct SimpleSegment : IEquatable<SimpleSegment>, ISegment
{
public static readonly SimpleSegment Invalid = new SimpleSegment(-1, -1);
public readonly int Offset, Length;
int ISegment.Offset {
get { return Offset; }
}
int ISegment.Length {
get { return Length; }
}
public int EndOffset {
get {
return Offset + Length;
}
}
public SimpleSegment(int offset, int length)
{
this.Offset = offset;
this.Length = length;
}
public SimpleSegment(ISegment segment)
{
Debug.Assert(segment != null);
this.Offset = segment.Offset;
this.Length = segment.Length;
}
public override int GetHashCode()
{
unchecked {
return Offset + 10301 * Length;
}
}
public override bool Equals(object obj)
{
return (obj is SimpleSegment) && Equals((SimpleSegment)obj);
}
public bool Equals(SimpleSegment other)
{
return this.Offset == other.Offset && this.Length == other.Length;
}
public static bool operator ==(SimpleSegment left, SimpleSegment right)
{
return left.Equals(right);
}
public static bool operator !=(SimpleSegment left, SimpleSegment right)
{
return !left.Equals(right);
}
public override string ToString()
{
return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", Length=" + Length.ToString(CultureInfo.InvariantCulture) + "]";
}
}
/// <summary>
/// A segment using <see cref="TextAnchor"/>s as start and end positions.
/// </summary>
/// <remarks>
/// <para>
/// For the constructors creating new anchors, the start position will be AfterInsertion and the end position will be BeforeInsertion.
/// Should the end position move before the start position, the segment will have length 0.
/// </para>
/// </remarks>
/// <seealso cref="ISegment"/>
/// <seealso cref="TextSegment"/>
public sealed class AnchorSegment : ISegment
{
readonly TextAnchor start, end;
/// <inheritdoc/>
public int Offset {
get { return start.Offset; }
}
/// <inheritdoc/>
public int Length {
get {
// Math.Max takes care of the fact that end.Offset might move before start.Offset.
return Math.Max(0, end.Offset - start.Offset);
}
}
/// <inheritdoc/>
public int EndOffset {
get {
// Math.Max takes care of the fact that end.Offset might move before start.Offset.
return Math.Max(start.Offset, end.Offset);
}
}
/// <summary>
/// Creates a new AnchorSegment using the specified anchors.
/// The anchors must have <see cref="TextAnchor.SurviveDeletion"/> set to true.
/// </summary>
public AnchorSegment(TextAnchor start, TextAnchor end)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
if (!start.SurviveDeletion)
throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "start");
if (!end.SurviveDeletion)
throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "end");
this.start = start;
this.end = end;
}
/// <summary>
/// Creates a new AnchorSegment that creates new anchors.
/// </summary>
public AnchorSegment(TextDocument document, ISegment segment)
: this(document, ThrowUtil.CheckNotNull(segment, "segment").Offset, segment.Length)
{
}
/// <summary>
/// Creates a new AnchorSegment that creates new anchors.
/// </summary>
public AnchorSegment(TextDocument document, int offset, int length)
{
if (document == null)
throw new ArgumentNullException("document");
this.start = document.CreateAnchor(offset);
this.start.SurviveDeletion = true;
this.start.MovementType = AnchorMovementType.AfterInsertion;
this.end = document.CreateAnchor(offset + length);
this.end.SurviveDeletion = true;
this.end.MovementType = AnchorMovementType.BeforeInsertion;
}
/// <inheritdoc/>
public override string ToString()
{
return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", EndOffset=" + EndOffset.ToString(CultureInfo.InvariantCulture) + "]";
}
}
}
|