// 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 ICSharpCode.AvalonEdit.Utils; using System; using System.IO; namespace ICSharpCode.AvalonEdit.Document { /// /// Interface for read-only access to a text source. /// /// /// public interface ITextSource { /// /// Gets the whole text as string. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] string Text { get; } /// /// Is raised when the Text property changes. /// event EventHandler TextChanged; /// /// Gets the total text length. /// /// The length of the text, in characters. /// This is the same as Text.Length, but is more efficient because /// it doesn't require creating a String object. int TextLength { get; } /// /// Gets a character at the specified position in the document. /// /// The index of the character to get. /// Offset is outside the valid range (0 to TextLength-1). /// The character at the specified position. /// This is the same as Text[offset], but is more efficient because /// it doesn't require creating a String object. char GetCharAt(int offset); /// /// Gets the index of the first occurrence of any character in the specified array. /// /// /// Start index of the search. /// Length of the area to search. /// The first index where any character was found; or -1 if no occurrence was found. int IndexOfAny(char[] anyOf, int startIndex, int count); /// /// Retrieves the text for a portion of the document. /// /// offset or length is outside the valid range. /// This is the same as Text.Substring, but is more efficient because /// it doesn't require creating a String object for the whole document. string GetText(int offset, int length); /// /// Creates a snapshot of the current text. /// /// /// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe. /// However, some implementing classes may provide additional thread-safety guarantees, see TextDocument.CreateSnapshot. /// ITextSource CreateSnapshot(); /// /// Creates a snapshot of a part of the current text. /// /// /// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe. /// However, some implementing classes may provide additional thread-safety guarantees, see TextDocument.CreateSnapshot. /// ITextSource CreateSnapshot(int offset, int length); /// /// Creates a text reader. /// If the text is changed while a reader is active, the reader will continue to read from the old text version. /// TextReader CreateReader(); } /// /// Implements the ITextSource interface by wrapping another TextSource /// and viewing only a part of the text. /// [Obsolete("This class will be removed in a future version of AvalonEdit")] public sealed class TextSourceView : ITextSource { readonly ITextSource baseTextSource; readonly ISegment viewedSegment; /// /// Creates a new TextSourceView object. /// /// The base text source. /// A text segment from the base text source public TextSourceView(ITextSource baseTextSource, ISegment viewedSegment) { if (baseTextSource == null) throw new ArgumentNullException("baseTextSource"); if (viewedSegment == null) throw new ArgumentNullException("viewedSegment"); this.baseTextSource = baseTextSource; this.viewedSegment = viewedSegment; } /// public event EventHandler TextChanged { add { baseTextSource.TextChanged += value; } remove { baseTextSource.TextChanged -= value; } } /// public string Text { get { return baseTextSource.GetText(viewedSegment.Offset, viewedSegment.Length); } } /// public int TextLength { get { return viewedSegment.Length; } } /// public char GetCharAt(int offset) { return baseTextSource.GetCharAt(viewedSegment.Offset + offset); } /// public string GetText(int offset, int length) { return baseTextSource.GetText(viewedSegment.Offset + offset, length); } /// public ITextSource CreateSnapshot() { return baseTextSource.CreateSnapshot(viewedSegment.Offset, viewedSegment.Length); } /// public ITextSource CreateSnapshot(int offset, int length) { return baseTextSource.CreateSnapshot(viewedSegment.Offset + offset, length); } /// public TextReader CreateReader() { return CreateSnapshot().CreateReader(); } /// public int IndexOfAny(char[] anyOf, int startIndex, int count) { int offset = viewedSegment.Offset; int result = baseTextSource.IndexOfAny(anyOf, startIndex + offset, count); return result >= 0 ? result - offset : result; } } /// /// Implements the ITextSource interface using a string. /// public sealed class StringTextSource : ITextSource { readonly string text; /// /// Creates a new StringTextSource. /// public StringTextSource(string text) { if (text == null) throw new ArgumentNullException("text"); this.text = text; } // Text can never change event EventHandler ITextSource.TextChanged { add {} remove {} } /// public string Text { get { return text; } } /// public int TextLength { get { return text.Length; } } /// public char GetCharAt(int offset) { // GetCharAt must throw ArgumentOutOfRangeException, not IndexOutOfRangeException if (offset < 0 || offset >= text.Length) throw new ArgumentOutOfRangeException("offset", offset, "offset must be between 0 and " + (text.Length - 1)); return text[offset]; } /// public string GetText(int offset, int length) { return text.Substring(offset, length); } /// public TextReader CreateReader() { return new StringReader(text); } /// public ITextSource CreateSnapshot() { return this; // StringTextSource already is immutable } /// public ITextSource CreateSnapshot(int offset, int length) { return new StringTextSource(text.Substring(offset, length)); } /// public int IndexOfAny(char[] anyOf, int startIndex, int count) { return text.IndexOfAny(anyOf, startIndex, count); } } /// /// Implements the ITextSource interface using a rope. /// public sealed class RopeTextSource : ITextSource { readonly Rope rope; /// /// Creates a new RopeTextSource. /// public RopeTextSource(Rope rope) { if (rope == null) throw new ArgumentNullException("rope"); this.rope = rope; } /// /// Returns a clone of the rope used for this text source. /// /// /// RopeTextSource only publishes a copy of the contained rope to ensure that the underlying rope cannot be modified. /// Unless the creator of the RopeTextSource still has a reference on the rope, RopeTextSource is immutable. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification="Not a property because it creates a clone")] public Rope GetRope() { return rope.Clone(); } // Change event is not supported event EventHandler ITextSource.TextChanged { add {} remove {} } /// public string Text { get { return rope.ToString(); } } /// public int TextLength { get { return rope.Length; } } /// public char GetCharAt(int offset) { return rope[offset]; } /// public string GetText(int offset, int length) { return rope.ToString(offset, length); } /// public TextReader CreateReader() { return new RopeTextReader(rope); } /// public ITextSource CreateSnapshot() { // we clone the underlying rope because the creator of the RopeTextSource might be modifying it return new RopeTextSource(rope.Clone()); } /// public ITextSource CreateSnapshot(int offset, int length) { return new RopeTextSource(rope.GetRange(offset, length)); } /// public int IndexOfAny(char[] anyOf, int startIndex, int count) { return rope.IndexOfAny(anyOf, startIndex, count); } } }