1
ļ»æ// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
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:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
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.
20
using System.Collections.Generic;
22
namespace ICSharpCode.NRefactory.Editor
25
/// Read-only implementation of <see cref="IDocument"/>.
28
public sealed class ReadOnlyDocument : IDocument
30
readonly ITextSource textSource;
33
static readonly char[] newline = { '\r', '\n' };
36
/// Creates a new ReadOnlyDocument from the given text source.
38
public ReadOnlyDocument(ITextSource textSource)
40
if (textSource == null)
41
throw new ArgumentNullException("textSource");
42
// ensure that underlying buffer is immutable
43
this.textSource = textSource.CreateSnapshot();
44
List<int> lines = new List<int>();
47
int textLength = textSource.TextLength;
48
while ((offset = textSource.IndexOfAny(newline, offset, textLength - offset)) >= 0) {
50
if (textSource.GetCharAt(offset - 1) == '\r' && offset < textLength && textSource.GetCharAt(offset) == '\n') {
55
this.lines = lines.ToArray();
59
/// Creates a new ReadOnlyDocument from the given string.
61
public ReadOnlyDocument(string text)
62
: this(new StringTextSource(text))
67
public IDocumentLine GetLineByNumber(int lineNumber)
69
if (lineNumber < 1 || lineNumber > lines.Length)
70
throw new ArgumentOutOfRangeException("lineNumber", lineNumber, "Value must be between 1 and " + lines.Length);
71
return new ReadOnlyDocumentLine(this, lineNumber);
74
sealed class ReadOnlyDocumentLine : IDocumentLine
76
readonly ReadOnlyDocument doc;
77
readonly int lineNumber;
78
readonly int offset, endOffset;
80
public ReadOnlyDocumentLine(ReadOnlyDocument doc, int lineNumber)
83
this.lineNumber = lineNumber;
84
this.offset = doc.GetStartOffset(lineNumber);
85
this.endOffset = doc.GetEndOffset(lineNumber);
88
public override int GetHashCode()
90
return doc.GetHashCode() ^ lineNumber;
93
public override bool Equals(object obj)
95
ReadOnlyDocumentLine other = obj as ReadOnlyDocumentLine;
96
return other != null && doc == other.doc && lineNumber == other.lineNumber;
100
get { return offset; }
104
get { return endOffset - offset; }
107
public int EndOffset {
108
get { return endOffset; }
111
public int TotalLength {
113
return doc.GetTotalEndOffset(lineNumber) - offset;
117
public int DelimiterLength {
119
return doc.GetTotalEndOffset(lineNumber) - endOffset;
123
public int LineNumber {
124
get { return lineNumber; }
127
public IDocumentLine PreviousLine {
132
return new ReadOnlyDocumentLine(doc, lineNumber - 1);
136
public IDocumentLine NextLine {
138
if (lineNumber == doc.LineCount)
141
return new ReadOnlyDocumentLine(doc, lineNumber + 1);
145
public bool IsDeleted {
146
get { return false; }
150
int GetStartOffset(int lineNumber)
152
return lines[lineNumber-1];
155
int GetTotalEndOffset(int lineNumber)
157
return lineNumber < lines.Length ? lines[lineNumber] : textSource.TextLength;
160
int GetEndOffset(int lineNumber)
162
if (lineNumber == lines.Length)
163
return textSource.TextLength;
164
int off = lines[lineNumber] - 1;
165
if (off > 0 && textSource.GetCharAt(off - 1) == '\r' && textSource.GetCharAt(off) == '\n')
171
public IDocumentLine GetLineByOffset(int offset)
173
return GetLineByNumber(GetLineNumberForOffset(offset));
176
int GetLineNumberForOffset(int offset)
178
int r = Array.BinarySearch(lines, offset);
179
return r < 0 ? ~r : r + 1;
183
public int GetOffset(int line, int column)
185
if (line < 1 || line > lines.Length)
186
throw new ArgumentOutOfRangeException("line", line, "Value must be between 1 and " + lines.Length);
187
int lineStart = GetStartOffset(line);
190
int lineEnd = GetEndOffset(line);
191
if (column - 1 >= lineEnd - lineStart)
193
return lineStart + column - 1;
197
public int GetOffset(TextLocation location)
199
return GetOffset(location.Line, location.Column);
203
public TextLocation GetLocation(int offset)
205
if (offset < 0 || offset > textSource.TextLength)
206
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + textSource.TextLength);
207
int line = GetLineNumberForOffset(offset);
208
return new TextLocation(line, offset-GetStartOffset(line)+1);
213
get { return textSource.Text; }
215
throw new NotSupportedException();
220
public int LineCount {
221
get { return lines.Length; }
225
public ITextSourceVersion Version {
226
get { return textSource.Version; }
230
public int TextLength {
231
get { return textSource.TextLength; }
234
event EventHandler<TextChangeEventArgs> IDocument.TextChanging { add {} remove {} }
236
event EventHandler<TextChangeEventArgs> IDocument.TextChanged { add {} remove {} }
238
event EventHandler IDocument.ChangeCompleted { add {} remove {} }
240
void IDocument.Insert(int offset, string text)
242
throw new NotSupportedException();
245
void IDocument.Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType)
247
throw new NotSupportedException();
250
void IDocument.Remove(int offset, int length)
252
throw new NotSupportedException();
255
void IDocument.Replace(int offset, int length, string newText)
257
throw new NotSupportedException();
260
void IDocument.StartUndoableAction()
264
void IDocument.EndUndoableAction()
268
IDisposable IDocument.OpenUndoGroup()
274
public ITextAnchor CreateAnchor(int offset)
276
return new ReadOnlyDocumentTextAnchor(GetLocation(offset), offset);
279
sealed class ReadOnlyDocumentTextAnchor : ITextAnchor
281
readonly TextLocation location;
284
public ReadOnlyDocumentTextAnchor(TextLocation location, int offset)
286
this.location = location;
287
this.offset = offset;
290
public event EventHandler Deleted { add {} remove {} }
292
public TextLocation Location {
293
get { return location; }
297
get { return offset; }
300
public AnchorMovementType MovementType { get; set; }
302
public bool SurviveDeletion { get; set; }
304
public bool IsDeleted {
305
get { return false; }
309
get { return location.Line; }
313
get { return location.Column; }
318
public ITextSource CreateSnapshot()
320
return textSource; // textBuffer is immutable
324
public ITextSource CreateSnapshot(int offset, int length)
326
return textSource.CreateSnapshot(offset, length);
330
public IDocument CreateDocumentSnapshot()
332
return this; // ReadOnlyDocument is immutable
336
public System.IO.TextReader CreateReader()
338
return textSource.CreateReader();
342
public System.IO.TextReader CreateReader(int offset, int length)
344
return textSource.CreateReader(offset, length);
348
public char GetCharAt(int offset)
350
return textSource.GetCharAt(offset);
354
public string GetText(int offset, int length)
356
return textSource.GetText(offset, length);
360
public string GetText(ISegment segment)
362
return textSource.GetText(segment);
366
public int IndexOf(char c, int startIndex, int count)
368
return textSource.IndexOf(c, startIndex, count);
372
public int IndexOfAny(char[] anyOf, int startIndex, int count)
374
return textSource.IndexOfAny(anyOf, startIndex, count);
378
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
380
return textSource.IndexOf(searchText, startIndex, count, comparisonType);
384
public int LastIndexOf(char c, int startIndex, int count)
386
return textSource.LastIndexOf(c, startIndex, count);
390
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
392
return textSource.LastIndexOf(searchText, startIndex, count, comparisonType);
395
object IServiceProvider.GetService(Type serviceType)