11 changed files with 1080 additions and 4 deletions
@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// A document representing a source code file for refactoring.
|
||||
/// Line and column counting starts at 1.
|
||||
/// Offset counting starts at 0.
|
||||
/// </summary>
|
||||
public interface IDocument : ITextBuffer, IServiceProvider |
||||
{ |
||||
/// <summary>
|
||||
/// Gets/Sets the text of the whole document..
|
||||
/// </summary>
|
||||
new string Text { get; set; } // hides TextBuffer.Text to add the setter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of lines in the document.
|
||||
/// </summary>
|
||||
int TotalNumberOfLines { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the document line with the specified number.
|
||||
/// </summary>
|
||||
/// <param name="lineNumber">The number of the line to retrieve. The first line has number 1.</param>
|
||||
IDocumentLine GetLine(int lineNumber); |
||||
|
||||
/// <summary>
|
||||
/// Gets the document line that contains the specified offset.
|
||||
/// </summary>
|
||||
IDocumentLine GetLineByOffset(int offset); |
||||
|
||||
/// <summary>
|
||||
/// Gets the offset from a text location.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetLocation"/>
|
||||
int GetOffset(int line, int column); |
||||
|
||||
/// <summary>
|
||||
/// Gets the offset from a text location.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetLocation"/>
|
||||
int GetOffset(TextLocation location); |
||||
|
||||
/// <summary>
|
||||
/// Gets the location from an offset.
|
||||
/// </summary>
|
||||
/// <seealso cref="GetOffset(TextLocation)"/>
|
||||
TextLocation GetLocation(int offset); |
||||
|
||||
/// <summary>
|
||||
/// Inserts text.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset at which the text is inserted.</param>
|
||||
/// <param name="text">The new text.</param>
|
||||
/// <remarks>
|
||||
/// Anchors positioned exactly at the insertion offset will move according to their movement type.
|
||||
/// For AnchorMovementType.Default, they will move behind the inserted text.
|
||||
/// </remarks>
|
||||
void Insert(int offset, string text); |
||||
|
||||
/// <summary>
|
||||
/// Inserts text.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset at which the text is inserted.</param>
|
||||
/// <param name="text">The new text.</param>
|
||||
/// <param name="defaultAnchorMovementType">
|
||||
/// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type.
|
||||
/// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter.
|
||||
/// </param>
|
||||
void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType); |
||||
|
||||
/// <summary>
|
||||
/// Removes text.
|
||||
/// </summary>
|
||||
/// <param name="offset">Starting offset of the text to be removed.</param>
|
||||
/// <param name="length">Length of the text to be removed.</param>
|
||||
void Remove(int offset, int length); |
||||
|
||||
/// <summary>
|
||||
/// Replaces text.
|
||||
/// </summary>
|
||||
/// <param name="offset">The starting offset of the text to be replaced.</param>
|
||||
/// <param name="length">The length of the text to be replaced.</param>
|
||||
/// <param name="newText">The new text.</param>
|
||||
void Replace(int offset, int length, string newText); |
||||
|
||||
/// <summary>
|
||||
/// Make the document combine the following actions into a single
|
||||
/// action for undo purposes.
|
||||
/// </summary>
|
||||
void StartUndoableAction(); |
||||
|
||||
/// <summary>
|
||||
/// Ends the undoable action started with <see cref="StartUndoableAction"/>.
|
||||
/// </summary>
|
||||
void EndUndoableAction(); |
||||
|
||||
/// <summary>
|
||||
/// Creates an undo group. Dispose the returned value to close the undo group.
|
||||
/// </summary>
|
||||
/// <returns>An object that closes the undo group when Dispose() is called.</returns>
|
||||
IDisposable OpenUndoGroup(); |
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ITextAnchor"/> at the specified offset.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="ITextAnchor" select="remarks|example"/>
|
||||
ITextAnchor CreateAnchor(int offset); |
||||
|
||||
/// <summary>
|
||||
/// This event is called directly before a change is applied to the document.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is invalid to modify the document within this event handler.
|
||||
/// Aborting the change (by throwing an exception) is likely to cause corruption of data structures
|
||||
/// that listen to the Changing and Changed events.
|
||||
/// </remarks>
|
||||
event EventHandler<TextChangeEventArgs> Changing; |
||||
|
||||
/// <summary>
|
||||
/// This event is called directly after a change is applied to the document.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is invalid to modify the document within this event handler.
|
||||
/// Aborting the event handler (by throwing an exception) is likely to cause corruption of data structures
|
||||
/// that listen to the Changing and Changed events.
|
||||
/// </remarks>
|
||||
event EventHandler<TextChangeEventArgs> Changed; |
||||
} |
||||
} |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// A line inside a <see cref="IDocument"/>.
|
||||
/// </summary>
|
||||
public interface IDocumentLine |
||||
{ |
||||
/// <summary>
|
||||
/// Gets the starting offset of the line in the document's text.
|
||||
/// </summary>
|
||||
int Offset { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line (=the number of characters on the line).
|
||||
/// </summary>
|
||||
int Length { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the ending offset of the line in the document's text (= Offset + Length).
|
||||
/// </summary>
|
||||
int EndOffset { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this line, including the line delimiter.
|
||||
/// </summary>
|
||||
int TotalLength { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the line terminator.
|
||||
/// Returns 1 or 2; or 0 at the end of the document.
|
||||
/// </summary>
|
||||
int DelimiterLength { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the number of this line.
|
||||
/// The first line has the number 1.
|
||||
/// </summary>
|
||||
int LineNumber { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the text on this line.
|
||||
/// </summary>
|
||||
string Text { get; } |
||||
} |
||||
} |
||||
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// The TextAnchor class references an offset (a position between two characters).
|
||||
/// It automatically updates the offset when text is inserted/removed in front of the anchor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Use the <see cref="ITextAnchor.Offset"/> property to get the offset from a text anchor.
|
||||
/// Use the <see cref="IDocument.CreateAnchor"/> method to create an anchor from an offset.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The document will automatically update all text anchors; and because it uses weak references to do so,
|
||||
/// the garbage collector can simply collect the anchor object when you don't need it anymore.
|
||||
/// </para>
|
||||
/// <para>Moreover, the document is able to efficiently update a large number of anchors without having to look
|
||||
/// at each anchor object individually. Updating the offsets of all anchors usually only takes time logarithmic
|
||||
/// to the number of anchors. Retrieving the <see cref="ITextAnchor.Offset"/> property also runs in O(lg N).</para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// Usage:
|
||||
/// <code>TextAnchor anchor = document.CreateAnchor(offset);
|
||||
/// ChangeMyDocument();
|
||||
/// int newOffset = anchor.Offset;
|
||||
/// </code>
|
||||
/// </example>
|
||||
public interface ITextAnchor |
||||
{ |
||||
/// <summary>
|
||||
/// Gets the text location of this anchor.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
|
||||
TextLocation Location { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the offset of the text anchor.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
|
||||
int Offset { get; } |
||||
|
||||
/// <summary>
|
||||
/// Controls how the anchor moves.
|
||||
/// </summary>
|
||||
AnchorMovementType MovementType { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Specifies whether the anchor survives deletion of the text containing it.
|
||||
/// <c>false</c>: The anchor is deleted when the a selection that includes the anchor is deleted.
|
||||
/// <c>true</c>: The anchor is not deleted.
|
||||
/// </summary>
|
||||
bool SurviveDeletion { get; set; } |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the anchor was deleted.
|
||||
/// </summary>
|
||||
bool IsDeleted { get; } |
||||
|
||||
/// <summary>
|
||||
/// Occurs after the anchor was deleted.
|
||||
/// </summary>
|
||||
event EventHandler Deleted; |
||||
|
||||
/// <summary>
|
||||
/// Gets the line number of the anchor.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
|
||||
int Line { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the column number of this anchor.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
|
||||
int Column { get; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Defines how a text anchor moves.
|
||||
/// </summary>
|
||||
public enum AnchorMovementType |
||||
{ |
||||
/// <summary>
|
||||
/// When text is inserted at the anchor position, the type of the insertion
|
||||
/// determines where the caret moves to. For normal insertions, the anchor will stay
|
||||
/// behind the inserted text.
|
||||
/// </summary>
|
||||
Default, |
||||
/// <summary>
|
||||
/// Behaves like a start marker - when text is inserted at the anchor position, the anchor will stay
|
||||
/// before the inserted text.
|
||||
/// </summary>
|
||||
BeforeInsertion, |
||||
/// <summary>
|
||||
/// Behave like an end marker - when text is insered at the anchor position, the anchor will move
|
||||
/// after the inserted text.
|
||||
/// </summary>
|
||||
AfterInsertion |
||||
} |
||||
} |
||||
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// A read-only view on a (potentially mutable) text buffer.
|
||||
/// The IDocument interfaces derives from this interface.
|
||||
/// </summary>
|
||||
public interface ITextBuffer |
||||
{ |
||||
/// <summary>
|
||||
/// Gets a version identifier for this text buffer.
|
||||
/// Returns null for unversioned text buffers.
|
||||
/// </summary>
|
||||
ITextBufferVersion Version { get; } |
||||
|
||||
/// <summary>
|
||||
/// Creates an immutable snapshot of this text buffer.
|
||||
/// Unlike all other methods in this interface, this method is thread-safe.
|
||||
/// </summary>
|
||||
ITextBuffer CreateSnapshot(); |
||||
|
||||
/// <summary>
|
||||
/// Creates an immutable snapshot of a part of this text buffer.
|
||||
/// Unlike all other methods in this interface, this method is thread-safe.
|
||||
/// </summary>
|
||||
ITextBuffer CreateSnapshot(int offset, int length); |
||||
|
||||
/// <summary>
|
||||
/// Creates a new TextReader to read from this text buffer.
|
||||
/// </summary>
|
||||
TextReader CreateReader(); |
||||
|
||||
/// <summary>
|
||||
/// Creates a new TextReader to read from this text buffer.
|
||||
/// </summary>
|
||||
TextReader CreateReader(int offset, int length); |
||||
|
||||
/// <summary>
|
||||
/// Gets the total text length.
|
||||
/// </summary>
|
||||
/// <returns>The length of the text, in characters.</returns>
|
||||
/// <remarks>This is the same as Text.Length, but is more efficient because
|
||||
/// it doesn't require creating a String object.</remarks>
|
||||
int TextLength { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the whole text as string.
|
||||
/// </summary>
|
||||
string Text { get; } |
||||
|
||||
/// <summary>
|
||||
/// Is raised when the Text property changes.
|
||||
/// </summary>
|
||||
event EventHandler TextChanged; |
||||
|
||||
/// <summary>
|
||||
/// Gets a character at the specified position in the document.
|
||||
/// </summary>
|
||||
/// <paramref name="offset">The index of the character to get.</paramref>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception>
|
||||
/// <returns>The character at the specified position.</returns>
|
||||
/// <remarks>This is the same as Text[offset], but is more efficient because
|
||||
/// it doesn't require creating a String object.</remarks>
|
||||
char GetCharAt(int offset); |
||||
|
||||
/// <summary>
|
||||
/// Retrieves the text for a portion of the document.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
|
||||
/// <remarks>This is the same as Text.Substring, but is more efficient because
|
||||
/// it doesn't require creating a String object for the whole document.</remarks>
|
||||
string GetText(int offset, int length); |
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the first occurrence of any character in the specified array.
|
||||
/// </summary>
|
||||
/// <param name="anyOf">Characters to search for</param>
|
||||
/// <param name="startIndex">Start index of the search.</param>
|
||||
/// <param name="count">Length of the area to search.</param>
|
||||
/// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
|
||||
int IndexOfAny(char[] anyOf, int startIndex, int count); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Represents a version identifier for a text buffer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is SharpDevelop's equivalent to AvalonEdit ChangeTrackingCheckpoint.
|
||||
/// It is used by the ParserService to efficiently detect whether a document has changed and needs reparsing.
|
||||
/// It is a separate class from ITextBuffer to allow the GC to collect the text buffer while the version checkpoint
|
||||
/// is still in use.
|
||||
/// </remarks>
|
||||
public interface ITextBufferVersion |
||||
{ |
||||
/// <summary>
|
||||
/// Gets whether this checkpoint belongs to the same document as the other checkpoint.
|
||||
/// </summary>
|
||||
bool BelongsToSameDocumentAs(ITextBufferVersion other); |
||||
|
||||
/// <summary>
|
||||
/// Compares the age of this checkpoint to the other checkpoint.
|
||||
/// </summary>
|
||||
/// <remarks>This method is thread-safe.</remarks>
|
||||
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this version.</exception>
|
||||
/// <returns>-1 if this version is older than <paramref name="other"/>.
|
||||
/// 0 if <c>this</c> version instance represents the same version as <paramref name="other"/>.
|
||||
/// 1 if this version is newer than <paramref name="other"/>.</returns>
|
||||
int CompareAge(ITextBufferVersion other); |
||||
|
||||
/// <summary>
|
||||
/// Gets the changes from this checkpoint to the other checkpoint.
|
||||
/// If 'other' is older than this checkpoint, reverse changes are calculated.
|
||||
/// </summary>
|
||||
/// <remarks>This method is thread-safe.</remarks>
|
||||
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
|
||||
IEnumerable<TextChangeEventArgs> GetChangesTo(ITextBufferVersion other); |
||||
|
||||
/// <summary>
|
||||
/// Calculates where the offset has moved in the other buffer version.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
|
||||
int MoveOffsetTo(ITextBufferVersion other, int oldOffset, AnchorMovementType movement); |
||||
} |
||||
} |
||||
@ -0,0 +1,321 @@
@@ -0,0 +1,321 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// Read-only implementation of <see cref="IDocument"/>.
|
||||
/// </summary>
|
||||
public sealed class ReadOnlyDocument : IDocument |
||||
{ |
||||
readonly ITextBuffer textBuffer; |
||||
int[] lines; |
||||
|
||||
static readonly char[] newline = { '\r', '\n' }; |
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReadOnlyDocument from the given text buffer.
|
||||
/// </summary>
|
||||
public ReadOnlyDocument(ITextBuffer textBuffer) |
||||
{ |
||||
if (textBuffer == null) |
||||
throw new ArgumentNullException("textBuffer"); |
||||
// ensure that underlying buffer is immutable
|
||||
this.textBuffer = textBuffer.CreateSnapshot(); |
||||
List<int> lines = new List<int>(); |
||||
lines.Add(0); |
||||
int offset = 0; |
||||
int textLength = textBuffer.TextLength; |
||||
while ((offset = textBuffer.IndexOfAny(newline, offset, textLength - offset)) >= 0) { |
||||
offset++; |
||||
if (textBuffer.GetCharAt(offset - 1) == '\r' && offset < textLength && textBuffer.GetCharAt(offset) == '\n') { |
||||
offset++; |
||||
} |
||||
lines.Add(offset); |
||||
} |
||||
this.lines = lines.ToArray(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReadOnlyDocument from the given string.
|
||||
/// </summary>
|
||||
public ReadOnlyDocument(string text) |
||||
: this(new StringTextBuffer(text)) |
||||
{ |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public IDocumentLine GetLine(int lineNumber) |
||||
{ |
||||
if (lineNumber < 1 || lineNumber > lines.Length) |
||||
throw new ArgumentOutOfRangeException("lineNumber", lineNumber, "Value must be between 1 and " + lines.Length); |
||||
return new ReadOnlyDocumentLine(this, lineNumber); |
||||
} |
||||
|
||||
sealed class ReadOnlyDocumentLine : IDocumentLine |
||||
{ |
||||
readonly ReadOnlyDocument doc; |
||||
readonly int lineNumber; |
||||
readonly int offset, endOffset; |
||||
|
||||
public ReadOnlyDocumentLine(ReadOnlyDocument doc, int lineNumber) |
||||
{ |
||||
this.doc = doc; |
||||
this.lineNumber = lineNumber; |
||||
this.offset = doc.GetStartOffset(lineNumber); |
||||
this.endOffset = doc.GetEndOffset(lineNumber); |
||||
} |
||||
|
||||
public int Offset { |
||||
get { return offset; } |
||||
} |
||||
|
||||
public int Length { |
||||
get { return endOffset - offset; } |
||||
} |
||||
|
||||
public int EndOffset { |
||||
get { return endOffset; } |
||||
} |
||||
|
||||
public int TotalLength { |
||||
get { |
||||
return doc.GetTotalEndOffset(lineNumber) - offset; |
||||
} |
||||
} |
||||
|
||||
public int DelimiterLength { |
||||
get { |
||||
return doc.GetTotalEndOffset(lineNumber) - endOffset; |
||||
} |
||||
} |
||||
|
||||
public int LineNumber { |
||||
get { return lineNumber; } |
||||
} |
||||
|
||||
public string Text { |
||||
get { |
||||
return doc.GetText(this.Offset, this.Length); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int GetStartOffset(int lineNumber) |
||||
{ |
||||
return lines[lineNumber-1]; |
||||
} |
||||
|
||||
int GetTotalEndOffset(int lineNumber) |
||||
{ |
||||
return lineNumber < lines.Length ? lines[lineNumber] : textBuffer.TextLength; |
||||
} |
||||
|
||||
int GetEndOffset(int lineNumber) |
||||
{ |
||||
if (lineNumber == lines.Length) |
||||
return textBuffer.TextLength; |
||||
int off = lines[lineNumber] - 1; |
||||
if (off > 0 && textBuffer.GetCharAt(off - 1) == '\r' && textBuffer.GetCharAt(off) == '\n') |
||||
off--; |
||||
return off; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public IDocumentLine GetLineByOffset(int offset) |
||||
{ |
||||
return GetLine(GetLineNumberForOffset(offset)); |
||||
} |
||||
|
||||
int GetLineNumberForOffset(int offset) |
||||
{ |
||||
int r = Array.BinarySearch(lines, offset); |
||||
return r < 0 ? ~r : r + 1; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetOffset(int line, int column) |
||||
{ |
||||
if (line < 1 || line > lines.Length) |
||||
throw new ArgumentOutOfRangeException("line", line, "Value must be between 1 and " + lines.Length); |
||||
int lineStart = GetStartOffset(line); |
||||
if (column <= 0) |
||||
return lineStart; |
||||
int lineEnd = GetEndOffset(line); |
||||
if (column >= lineEnd - lineStart) |
||||
return lineEnd; |
||||
return lineStart + column - 1; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetOffset(TextLocation location) |
||||
{ |
||||
return GetOffset(location.Line, location.Column); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public TextLocation GetLocation(int offset) |
||||
{ |
||||
if (offset < 0 || offset > textBuffer.TextLength) |
||||
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + textBuffer.TextLength); |
||||
int line = GetLineNumberForOffset(offset); |
||||
return new TextLocation(offset-GetStartOffset(line)+1, line); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public string Text { |
||||
get { return textBuffer.Text; } |
||||
set { |
||||
throw new NotSupportedException(); |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int TotalNumberOfLines { |
||||
get { return lines.Length; } |
||||
} |
||||
|
||||
ITextBufferVersion ITextBuffer.Version { |
||||
get { return null; } |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int TextLength { |
||||
get { return textBuffer.TextLength; } |
||||
} |
||||
|
||||
event EventHandler<TextChangeEventArgs> IDocument.Changing { add {} remove {} } |
||||
|
||||
event EventHandler<TextChangeEventArgs> IDocument.Changed { add {} remove {} } |
||||
|
||||
event EventHandler ITextBuffer.TextChanged { add {} remove {} } |
||||
|
||||
void IDocument.Insert(int offset, string text) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
|
||||
void IDocument.Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
|
||||
void IDocument.Remove(int offset, int length) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
|
||||
void IDocument.Replace(int offset, int length, string newText) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
|
||||
void IDocument.StartUndoableAction() |
||||
{ |
||||
} |
||||
|
||||
void IDocument.EndUndoableAction() |
||||
{ |
||||
} |
||||
|
||||
IDisposable IDocument.OpenUndoGroup() |
||||
{ |
||||
return null; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ITextAnchor CreateAnchor(int offset) |
||||
{ |
||||
return new ReadOnlyDocumentTextAnchor(GetLocation(offset), offset); |
||||
} |
||||
|
||||
sealed class ReadOnlyDocumentTextAnchor : ITextAnchor |
||||
{ |
||||
readonly TextLocation location; |
||||
readonly int offset; |
||||
|
||||
public ReadOnlyDocumentTextAnchor(TextLocation location, int offset) |
||||
{ |
||||
this.location = location; |
||||
this.offset = offset; |
||||
} |
||||
|
||||
public event EventHandler Deleted { add {} remove {} } |
||||
|
||||
public TextLocation Location { |
||||
get { return location; } |
||||
} |
||||
|
||||
public int Offset { |
||||
get { return offset; } |
||||
} |
||||
|
||||
public AnchorMovementType MovementType { get; set; } |
||||
|
||||
public bool SurviveDeletion { get; set; } |
||||
|
||||
public bool IsDeleted { |
||||
get { return false; } |
||||
} |
||||
|
||||
public int Line { |
||||
get { return location.Line; } |
||||
} |
||||
|
||||
public int Column { |
||||
get { return location.Column; } |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ITextBuffer CreateSnapshot() |
||||
{ |
||||
return textBuffer; // textBuffer is immutable
|
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ITextBuffer CreateSnapshot(int offset, int length) |
||||
{ |
||||
return textBuffer.CreateSnapshot(offset, length); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public System.IO.TextReader CreateReader() |
||||
{ |
||||
return textBuffer.CreateReader(); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public System.IO.TextReader CreateReader(int offset, int length) |
||||
{ |
||||
return textBuffer.CreateReader(offset, length); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public char GetCharAt(int offset) |
||||
{ |
||||
return textBuffer.GetCharAt(offset); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetText(int offset, int length) |
||||
{ |
||||
return textBuffer.GetText(offset, length); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
||||
{ |
||||
return textBuffer.IndexOfAny(anyOf, startIndex, count); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public object GetService(Type serviceType) |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// Implements the ITextBuffer interface using a string.
|
||||
/// </summary>
|
||||
[Serializable] |
||||
public class StringTextBuffer : ITextBuffer |
||||
{ |
||||
readonly string text; |
||||
|
||||
/// <summary>
|
||||
/// Creates a new StringTextBuffer with the given text.
|
||||
/// </summary>
|
||||
public StringTextBuffer(string text) |
||||
{ |
||||
if (text == null) |
||||
throw new ArgumentNullException("text"); |
||||
this.text = text; |
||||
} |
||||
|
||||
event EventHandler ITextBuffer.TextChanged { add {} remove {} } |
||||
|
||||
ITextBufferVersion ITextBuffer.Version { |
||||
get { return null; } |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int TextLength { |
||||
get { return text.Length; } |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public string Text { |
||||
get { return text; } |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ITextBuffer CreateSnapshot() |
||||
{ |
||||
return this; // StringTextBuffer is immutable
|
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public ITextBuffer CreateSnapshot(int offset, int length) |
||||
{ |
||||
return new StringTextBuffer(text.Substring(offset, length)); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public TextReader CreateReader() |
||||
{ |
||||
return new StringReader(text); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public TextReader CreateReader(int offset, int length) |
||||
{ |
||||
return new StringReader(text.Substring(offset, length)); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public char GetCharAt(int offset) |
||||
{ |
||||
return text[offset]; |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetText(int offset, int length) |
||||
{ |
||||
return text.Substring(offset, length); |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
||||
{ |
||||
return text.IndexOfAny(anyOf, startIndex, count); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// Describes a change of the document text.
|
||||
/// This class is thread-safe.
|
||||
/// </summary>
|
||||
[Serializable] |
||||
public class TextChangeEventArgs : EventArgs |
||||
{ |
||||
readonly int offset; |
||||
readonly string removedText; |
||||
readonly string insertedText; |
||||
|
||||
/// <summary>
|
||||
/// The offset at which the change occurs.
|
||||
/// </summary>
|
||||
public int Offset { |
||||
get { return offset; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// The text that was inserted.
|
||||
/// </summary>
|
||||
public string RemovedText { |
||||
get { return removedText; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// The number of characters removed.
|
||||
/// </summary>
|
||||
public int RemovalLength { |
||||
get { return removedText.Length; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// The text that was inserted.
|
||||
/// </summary>
|
||||
public string InsertedText { |
||||
get { return insertedText; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// The number of characters inserted.
|
||||
/// </summary>
|
||||
public int InsertionLength { |
||||
get { return insertedText.Length; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new TextChangeEventArgs object.
|
||||
/// </summary>
|
||||
public TextChangeEventArgs(int offset, string removedText, string insertedText) |
||||
{ |
||||
this.offset = offset; |
||||
this.removedText = removedText ?? string.Empty; |
||||
this.insertedText = insertedText ?? string.Empty; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,163 @@
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Globalization; |
||||
|
||||
namespace ICSharpCode.Editor |
||||
{ |
||||
/// <summary>
|
||||
/// A line/column position.
|
||||
/// Text editor lines/columns are counted started from one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The document provides the methods <see cref="IDocument.GetLocation"/> and
|
||||
/// <see cref="IDocument.GetOffset(TextLocation)"/> to convert between offsets and TextLocations.
|
||||
/// </remarks>
|
||||
[Serializable] |
||||
public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation> |
||||
{ |
||||
/// <summary>
|
||||
/// Represents no text location (0, 0).
|
||||
/// </summary>
|
||||
public static readonly TextLocation Empty = new TextLocation(0, 0); |
||||
|
||||
/// <summary>
|
||||
/// Creates a TextLocation instance.
|
||||
/// </summary>
|
||||
public TextLocation(int line, int column) |
||||
{ |
||||
this.line = line; |
||||
this.column = column; |
||||
} |
||||
|
||||
int column, line; |
||||
|
||||
/// <summary>
|
||||
/// Gets the line number.
|
||||
/// </summary>
|
||||
public int Line { |
||||
get { return line; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets the column number.
|
||||
/// </summary>
|
||||
public int Column { |
||||
get { return column; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the TextLocation instance is empty.
|
||||
/// </summary>
|
||||
public bool IsEmpty { |
||||
get { |
||||
return column <= 0 && line <= 0; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation for debugging purposes.
|
||||
/// </summary>
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.column, this.line); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets a hash code.
|
||||
/// </summary>
|
||||
public override int GetHashCode() |
||||
{ |
||||
return unchecked (191 * column.GetHashCode() ^ line.GetHashCode()); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Equality test.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) |
||||
{ |
||||
if (!(obj is TextLocation)) return false; |
||||
return (TextLocation)obj == this; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Equality test.
|
||||
/// </summary>
|
||||
public bool Equals(TextLocation other) |
||||
{ |
||||
return this == other; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Equality test.
|
||||
/// </summary>
|
||||
public static bool operator ==(TextLocation left, TextLocation right) |
||||
{ |
||||
return left.column == right.column && left.line == right.line; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Inequality test.
|
||||
/// </summary>
|
||||
public static bool operator !=(TextLocation left, TextLocation right) |
||||
{ |
||||
return left.column != right.column || left.line != right.line; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares two text locations.
|
||||
/// </summary>
|
||||
public static bool operator <(TextLocation left, TextLocation right) |
||||
{ |
||||
if (left.line < right.line) |
||||
return true; |
||||
else if (left.line == right.line) |
||||
return left.column < right.column; |
||||
else |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares two text locations.
|
||||
/// </summary>
|
||||
public static bool operator >(TextLocation left, TextLocation right) |
||||
{ |
||||
if (left.line > right.line) |
||||
return true; |
||||
else if (left.line == right.line) |
||||
return left.column > right.column; |
||||
else |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares two text locations.
|
||||
/// </summary>
|
||||
public static bool operator <=(TextLocation left, TextLocation right) |
||||
{ |
||||
return !(left > right); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares two text locations.
|
||||
/// </summary>
|
||||
public static bool operator >=(TextLocation left, TextLocation right) |
||||
{ |
||||
return !(left < right); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares two text locations.
|
||||
/// </summary>
|
||||
public int CompareTo(TextLocation other) |
||||
{ |
||||
if (this == other) |
||||
return 0; |
||||
if (this < other) |
||||
return -1; |
||||
else |
||||
return 1; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue