mirror of https://github.com/icsharpcode/ILSpy.git
23 changed files with 4 additions and 2119 deletions
@ -1,206 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.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 : ITextSource, IServiceProvider |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Creates an immutable snapshot of this document.
|
|
||||||
/// </summary>
|
|
||||||
IDocument CreateDocumentSnapshot(); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets/Sets the text of the whole document..
|
|
||||||
/// </summary>
|
|
||||||
new string Text { get; set; } // hides ITextSource.Text to add the setter
|
|
||||||
|
|
||||||
/// <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> TextChanging; |
|
||||||
|
|
||||||
/// <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> TextChanged; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This event is called after a group of changes is completed.
|
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="EndUndoableAction"/>
|
|
||||||
event EventHandler ChangeCompleted; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of lines in the document.
|
|
||||||
/// </summary>
|
|
||||||
int LineCount { 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 GetLineByNumber(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.
|
|
||||||
/// The caret will also 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>
|
|
||||||
/// <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.
|
|
||||||
/// The caret will also move behind the inserted text.
|
|
||||||
/// </remarks>
|
|
||||||
void Insert(int offset, ITextSource 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.
|
|
||||||
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
|
|
||||||
/// </param>
|
|
||||||
void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType); |
|
||||||
|
|
||||||
/// <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.
|
|
||||||
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
|
|
||||||
/// </param>
|
|
||||||
void Insert(int offset, ITextSource 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>
|
|
||||||
/// 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, ITextSource 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>
|
|
||||||
/// Gets the name of the file the document is stored in.
|
|
||||||
/// Could also be a non-existent dummy file name or null if no name has been set.
|
|
||||||
/// </summary>
|
|
||||||
string FileName { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when the file name of the document changes.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler FileNameChanged; |
|
||||||
} |
|
||||||
} |
|
@ -1,59 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// A line inside a <see cref="IDocument"/>.
|
|
||||||
/// </summary>
|
|
||||||
public interface IDocumentLine : ISegment |
|
||||||
{ |
|
||||||
/// <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 previous line. Returns null if this is the first line in the document.
|
|
||||||
/// </summary>
|
|
||||||
IDocumentLine PreviousLine { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the next line. Returns null if this is the last line in the document.
|
|
||||||
/// </summary>
|
|
||||||
IDocumentLine NextLine { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the line was deleted.
|
|
||||||
/// </summary>
|
|
||||||
bool IsDeleted { get; } |
|
||||||
} |
|
||||||
} |
|
@ -1,70 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// An (Offset,Length)-pair.
|
|
||||||
/// </summary>
|
|
||||||
public interface ISegment |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Gets the start offset of the segment.
|
|
||||||
/// </summary>
|
|
||||||
int Offset { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the length of the segment.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>For line segments (IDocumentLine), the length does not include the line delimeter.</remarks>
|
|
||||||
int Length { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the end offset of the segment.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>EndOffset = Offset + Length;</remarks>
|
|
||||||
int EndOffset { get; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for <see cref="ISegment"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static class ISegmentExtensions |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Gets whether <paramref name="segment"/> fully contains the specified segment.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Use <c>segment.Contains(offset, 0)</c> to detect whether a segment (end inclusive) contains offset;
|
|
||||||
/// use <c>segment.Contains(offset, 1)</c> to detect whether a segment (end exclusive) contains offset.
|
|
||||||
/// </remarks>
|
|
||||||
public static bool Contains (this ISegment segment, int offset, int length) |
|
||||||
{ |
|
||||||
return segment.Offset <= offset && offset + length <= segment.EndOffset; |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether <paramref name="thisSegment"/> fully contains the specified segment.
|
|
||||||
/// </summary>
|
|
||||||
public static bool Contains (this ISegment thisSegment, ISegment segment) |
|
||||||
{ |
|
||||||
return segment != null && thisSegment.Offset <= segment.Offset && segment.EndOffset <= thisSegment.EndOffset; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,140 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.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>
|
|
||||||
/// <remarks>Anchor movement is ambiguous if text is inserted exactly at the anchor's location.
|
|
||||||
/// Does the anchor stay before the inserted text, or does it move after it?
|
|
||||||
/// The property <see cref="MovementType"/> will be used to determine which of these two options the anchor will choose.
|
|
||||||
/// The default value is <see cref="AnchorMovementType.Default"/>.</remarks>
|
|
||||||
AnchorMovementType MovementType { get; set; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Specifies whether the anchor survives deletion of the text containing it.
|
|
||||||
/// </para><para>
|
|
||||||
/// <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.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks><inheritdoc cref="IsDeleted" /></remarks>
|
|
||||||
bool SurviveDeletion { get; set; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the anchor was deleted.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>When a piece of text containing an anchor is removed, then that anchor will be deleted.
|
|
||||||
/// First, the <see cref="IsDeleted"/> property is set to true on all deleted anchors,
|
|
||||||
/// then the <see cref="Deleted"/> events are raised.
|
|
||||||
/// You cannot retrieve the offset from an anchor that has been deleted.</para>
|
|
||||||
/// <para>This deletion behavior might be useful when using anchors for building a bookmark feature,
|
|
||||||
/// but in other cases you want to still be able to use the anchor. For those cases, set <c><see cref="SurviveDeletion"/> = true</c>.</para>
|
|
||||||
/// </remarks>
|
|
||||||
bool IsDeleted { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs after the anchor was deleted.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <inheritdoc cref="IsDeleted" />
|
|
||||||
/// <para>Due to the 'weak reference' nature of text anchors, you will receive
|
|
||||||
/// the Deleted event only while your code holds a reference to the TextAnchor object.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
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 move
|
|
||||||
/// after 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 |
|
||||||
} |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
// ITextPasteHandler.cs
|
|
||||||
//
|
|
||||||
// Author:
|
|
||||||
// Mike Krüger <mkrueger@novell.com>
|
|
||||||
//
|
|
||||||
// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// The text paste handler can do formattings to a text that is about to be pasted
|
|
||||||
/// into the text document.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITextPasteHandler |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Formats plain text that is inserted at a specified offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// The text that will get inserted at that position.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="offset">The offset where the text will be inserted.</param>
|
|
||||||
/// <param name="text">The text to be inserted.</param>
|
|
||||||
/// <param name="copyData">Additional data in case the text was copied from a Mono.TextEditor.</param>
|
|
||||||
string FormatPlainText(int offset, string text, byte[] copyData); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the copy data for a specific segment inside the document. This can contain additional information.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="segment">The text segment that is about to be copied.</param>
|
|
||||||
byte[] GetCopyData(ISegment segment); |
|
||||||
} |
|
||||||
} |
|
@ -1,218 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.IO; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// A read-only view on a (potentially mutable) text source.
|
|
||||||
/// The IDocument interface derives from this interface.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITextSource |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Gets a version identifier for this text source.
|
|
||||||
/// Returns null for unversioned text sources.
|
|
||||||
/// </summary>
|
|
||||||
ITextSourceVersion Version { get; } |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an immutable snapshot of this text source.
|
|
||||||
/// Unlike all other methods in this interface, this method is thread-safe.
|
|
||||||
/// </summary>
|
|
||||||
ITextSource CreateSnapshot(); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an immutable snapshot of a part of this text source.
|
|
||||||
/// Unlike all other methods in this interface, this method is thread-safe.
|
|
||||||
/// </summary>
|
|
||||||
ITextSource CreateSnapshot(int offset, int length); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new TextReader to read from this text source.
|
|
||||||
/// </summary>
|
|
||||||
TextReader CreateReader(); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new TextReader to read from this text source.
|
|
||||||
/// </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>
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] |
|
||||||
string Text { get; } |
|
||||||
|
|
||||||
/// <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>
|
|
||||||
/// Retrieves the text for a portion of the document.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
|
|
||||||
string GetText(ISegment segment); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the text from this document into the TextWriter.
|
|
||||||
/// </summary>
|
|
||||||
void WriteTextTo(TextWriter writer); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the text from this document into the TextWriter.
|
|
||||||
/// </summary>
|
|
||||||
void WriteTextTo(TextWriter writer, int offset, int length); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the first occurrence of the character in the specified array.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="c">Character to search for</param>
|
|
||||||
/// <param name="startIndex">Start index of the area to search.</param>
|
|
||||||
/// <param name="count">Length of the area to search.</param>
|
|
||||||
/// <returns>The first index where the character was found; or -1 if no occurrence was found.</returns>
|
|
||||||
int IndexOf(char c, int startIndex, int count); |
|
||||||
|
|
||||||
/// <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 area to 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>
|
|
||||||
/// Gets the index of the first occurrence of the specified search text in this text source.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="searchText">The search text</param>
|
|
||||||
/// <param name="startIndex">Start index of the area to search.</param>
|
|
||||||
/// <param name="count">Length of the area to search.</param>
|
|
||||||
/// <param name="comparisonType">String comparison to use.</param>
|
|
||||||
/// <returns>The first index where the search term was found; or -1 if no occurrence was found.</returns>
|
|
||||||
int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the last occurrence of the specified character in this text source.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="c">The search character</param>
|
|
||||||
/// <param name="startIndex">Start index of the area to search.</param>
|
|
||||||
/// <param name="count">Length of the area to search.</param>
|
|
||||||
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
|
|
||||||
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
|
|
||||||
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
|
|
||||||
int LastIndexOf(char c, int startIndex, int count); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the last occurrence of the specified search text in this text source.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="searchText">The search text</param>
|
|
||||||
/// <param name="startIndex">Start index of the area to search.</param>
|
|
||||||
/// <param name="count">Length of the area to search.</param>
|
|
||||||
/// <param name="comparisonType">String comparison to use.</param>
|
|
||||||
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
|
|
||||||
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
|
|
||||||
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
|
|
||||||
int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType); |
|
||||||
|
|
||||||
/* What about: |
|
||||||
void Insert (int offset, string value); |
|
||||||
void Remove (int offset, int count); |
|
||||||
void Remove (ISegment segment); |
|
||||||
|
|
||||||
void Replace (int offset, int count, string value); |
|
||||||
|
|
||||||
Or more search operations: |
|
||||||
|
|
||||||
IEnumerable<int> SearchForward (string pattern, int startIndex); |
|
||||||
IEnumerable<int> SearchForwardIgnoreCase (string pattern, int startIndex); |
|
||||||
|
|
||||||
IEnumerable<int> SearchBackward (string pattern, int startIndex); |
|
||||||
IEnumerable<int> SearchBackwardIgnoreCase (string pattern, int startIndex); |
|
||||||
*/ |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a version identifier for a text source.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Verions can be used to efficiently detect whether a document has changed and needs reparsing;
|
|
||||||
/// or even to implement incremental parsers.
|
|
||||||
/// It is a separate class from ITextSource to allow the GC to collect the text source while
|
|
||||||
/// the version checkpoint is still in use.
|
|
||||||
/// </remarks>
|
|
||||||
public interface ITextSourceVersion |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Gets whether this checkpoint belongs to the same document as the other checkpoint.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Returns false when given <c>null</c>.
|
|
||||||
/// </remarks>
|
|
||||||
bool BelongsToSameDocumentAs(ITextSourceVersion 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(ITextSourceVersion 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(ITextSourceVersion 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(ITextSourceVersion other, int oldOffset, AnchorMovementType movement = AnchorMovementType.Default); |
|
||||||
} |
|
||||||
} |
|
@ -1,448 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Read-only implementation of <see cref="IDocument"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable] |
|
||||||
public sealed class ReadOnlyDocument : IDocument |
|
||||||
{ |
|
||||||
readonly ITextSource textSource; |
|
||||||
readonly string fileName; |
|
||||||
int[] lines; |
|
||||||
|
|
||||||
static readonly char[] newline = { '\r', '\n' }; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new ReadOnlyDocument from the given text source.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDocument(ITextSource textSource) |
|
||||||
{ |
|
||||||
if (textSource == null) |
|
||||||
throw new ArgumentNullException("textSource"); |
|
||||||
// ensure that underlying buffer is immutable
|
|
||||||
this.textSource = textSource.CreateSnapshot(); |
|
||||||
List<int> lines = new List<int>(); |
|
||||||
lines.Add(0); |
|
||||||
int offset = 0; |
|
||||||
int textLength = textSource.TextLength; |
|
||||||
while ((offset = textSource.IndexOfAny(newline, offset, textLength - offset)) >= 0) { |
|
||||||
offset++; |
|
||||||
if (textSource.GetCharAt(offset - 1) == '\r' && offset < textLength && textSource.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 StringTextSource(text)) |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new ReadOnlyDocument from the given text source;
|
|
||||||
/// and sets IDocument.FileName to the specified file name.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDocument(ITextSource textSource, string fileName) |
|
||||||
: this(textSource) |
|
||||||
{ |
|
||||||
this.fileName = fileName; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByNumber(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 override int GetHashCode() |
|
||||||
{ |
|
||||||
return doc.GetHashCode() ^ lineNumber; |
|
||||||
} |
|
||||||
|
|
||||||
public override bool Equals(object obj) |
|
||||||
{ |
|
||||||
ReadOnlyDocumentLine other = obj as ReadOnlyDocumentLine; |
|
||||||
return other != null && doc == other.doc && lineNumber == other.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 IDocumentLine PreviousLine { |
|
||||||
get { |
|
||||||
if (lineNumber == 1) |
|
||||||
return null; |
|
||||||
else |
|
||||||
return new ReadOnlyDocumentLine(doc, lineNumber - 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public IDocumentLine NextLine { |
|
||||||
get { |
|
||||||
if (lineNumber == doc.LineCount) |
|
||||||
return null; |
|
||||||
else |
|
||||||
return new ReadOnlyDocumentLine(doc, lineNumber + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public bool IsDeleted { |
|
||||||
get { return false; } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int GetStartOffset(int lineNumber) |
|
||||||
{ |
|
||||||
return lines[lineNumber-1]; |
|
||||||
} |
|
||||||
|
|
||||||
int GetTotalEndOffset(int lineNumber) |
|
||||||
{ |
|
||||||
return lineNumber < lines.Length ? lines[lineNumber] : textSource.TextLength; |
|
||||||
} |
|
||||||
|
|
||||||
int GetEndOffset(int lineNumber) |
|
||||||
{ |
|
||||||
if (lineNumber == lines.Length) |
|
||||||
return textSource.TextLength; |
|
||||||
int off = lines[lineNumber] - 1; |
|
||||||
if (off > 0 && textSource.GetCharAt(off - 1) == '\r' && textSource.GetCharAt(off) == '\n') |
|
||||||
off--; |
|
||||||
return off; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByOffset(int offset) |
|
||||||
{ |
|
||||||
return GetLineByNumber(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 <= 1) |
|
||||||
return lineStart; |
|
||||||
int lineEnd = GetEndOffset(line); |
|
||||||
if (column - 1 >= 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 > textSource.TextLength) |
|
||||||
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + textSource.TextLength); |
|
||||||
int line = GetLineNumberForOffset(offset); |
|
||||||
return new TextLocation(line, offset-GetStartOffset(line)+1); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Text { |
|
||||||
get { return textSource.Text; } |
|
||||||
set { |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LineCount { |
|
||||||
get { return lines.Length; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSourceVersion Version { |
|
||||||
get { return textSource.Version; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int TextLength { |
|
||||||
get { return textSource.TextLength; } |
|
||||||
} |
|
||||||
|
|
||||||
event EventHandler<TextChangeEventArgs> IDocument.TextChanging { add {} remove {} } |
|
||||||
|
|
||||||
event EventHandler<TextChangeEventArgs> IDocument.TextChanged { add {} remove {} } |
|
||||||
|
|
||||||
event EventHandler IDocument.ChangeCompleted { 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.Insert(int offset, ITextSource text) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Replace(int offset, int length, ITextSource 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 ITextSource CreateSnapshot() |
|
||||||
{ |
|
||||||
return textSource; // textBuffer is immutable
|
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.CreateSnapshot(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocument CreateDocumentSnapshot() |
|
||||||
{ |
|
||||||
return this; // ReadOnlyDocument is immutable
|
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public System.IO.TextReader CreateReader() |
|
||||||
{ |
|
||||||
return textSource.CreateReader(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public System.IO.TextReader CreateReader(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.CreateReader(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(System.IO.TextWriter writer) |
|
||||||
{ |
|
||||||
textSource.WriteTextTo(writer); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(System.IO.TextWriter writer, int offset, int length) |
|
||||||
{ |
|
||||||
textSource.WriteTextTo(writer, offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public char GetCharAt(int offset) |
|
||||||
{ |
|
||||||
return textSource.GetCharAt(offset); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.GetText(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(ISegment segment) |
|
||||||
{ |
|
||||||
return textSource.GetText(segment); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.IndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.IndexOfAny(anyOf, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return textSource.IndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.LastIndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return textSource.LastIndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
object IServiceProvider.GetService(Type serviceType) |
|
||||||
{ |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// <remarks>Will never be raised on <see cref="ReadOnlyDocument" />.</remarks>
|
|
||||||
public event EventHandler FileNameChanged { add {} remove {} } |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string FileName { |
|
||||||
get { return fileName; } |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,494 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.IO; |
|
||||||
using System.Text; |
|
||||||
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
||||||
using ICSharpCode.Decompiler.Util; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Document based on a string builder.
|
|
||||||
/// This class serves as a reference implementation for the IDocument interface.
|
|
||||||
/// </summary>
|
|
||||||
public class StringBuilderDocument : IDocument |
|
||||||
{ |
|
||||||
readonly StringBuilder b; |
|
||||||
readonly TextSourceVersionProvider versionProvider = new TextSourceVersionProvider(); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StringBuilderDocument.
|
|
||||||
/// </summary>
|
|
||||||
public StringBuilderDocument() |
|
||||||
{ |
|
||||||
b = new StringBuilder(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StringBuilderDocument with the specified initial text.
|
|
||||||
/// </summary>
|
|
||||||
public StringBuilderDocument(string text) |
|
||||||
{ |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
b = new StringBuilder(text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StringBuilderDocument with the initial text copied from the specified text source.
|
|
||||||
/// </summary>
|
|
||||||
public StringBuilderDocument(ITextSource textSource) |
|
||||||
{ |
|
||||||
if (textSource == null) |
|
||||||
throw new ArgumentNullException("textSource"); |
|
||||||
b = new StringBuilder(textSource.TextLength); |
|
||||||
textSource.WriteTextTo(new StringWriter(b)); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event EventHandler<TextChangeEventArgs> TextChanging; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event EventHandler<TextChangeEventArgs> TextChanged; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event EventHandler ChangeCompleted; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSourceVersion Version { |
|
||||||
get { return versionProvider.CurrentVersion; } |
|
||||||
} |
|
||||||
|
|
||||||
#region Line<->Offset
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LineCount { |
|
||||||
get { return CreateDocumentSnapshot().LineCount; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByNumber(int lineNumber) |
|
||||||
{ |
|
||||||
return CreateDocumentSnapshot().GetLineByNumber(lineNumber); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByOffset(int offset) |
|
||||||
{ |
|
||||||
return CreateDocumentSnapshot().GetLineByOffset(offset); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int GetOffset(int line, int column) |
|
||||||
{ |
|
||||||
return CreateDocumentSnapshot().GetOffset(line, column); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int GetOffset(TextLocation location) |
|
||||||
{ |
|
||||||
return CreateDocumentSnapshot().GetOffset(location); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TextLocation GetLocation(int offset) |
|
||||||
{ |
|
||||||
return CreateDocumentSnapshot().GetLocation(offset); |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Insert/Remove/Replace
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Insert(int offset, string text) |
|
||||||
{ |
|
||||||
Replace(offset, 0, text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Insert(int offset, ITextSource text) |
|
||||||
{ |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
Replace(offset, 0, text.Text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType) |
|
||||||
{ |
|
||||||
if (offset < 0 || offset > this.TextLength) |
|
||||||
throw new ArgumentOutOfRangeException("offset"); |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) |
|
||||||
PerformChange(new InsertionWithMovementBefore(offset, text)); |
|
||||||
else |
|
||||||
Replace(offset, 0, text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType) |
|
||||||
{ |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
Insert(offset, text.Text, defaultAnchorMovementType); |
|
||||||
} |
|
||||||
|
|
||||||
[Serializable] |
|
||||||
sealed class InsertionWithMovementBefore : TextChangeEventArgs |
|
||||||
{ |
|
||||||
public InsertionWithMovementBefore(int offset, string newText) : base(offset, string.Empty, newText) |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
public override int GetNewOffset(int offset, AnchorMovementType movementType) |
|
||||||
{ |
|
||||||
if (offset == this.Offset && movementType == AnchorMovementType.Default) |
|
||||||
return offset; |
|
||||||
else |
|
||||||
return base.GetNewOffset(offset, movementType); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Remove(int offset, int length) |
|
||||||
{ |
|
||||||
Replace(offset, length, string.Empty); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Replace(int offset, int length, string newText) |
|
||||||
{ |
|
||||||
if (offset < 0 || offset > this.TextLength) |
|
||||||
throw new ArgumentOutOfRangeException("offset"); |
|
||||||
if (length < 0 || length > this.TextLength - offset) |
|
||||||
throw new ArgumentOutOfRangeException("length"); |
|
||||||
if (newText == null) |
|
||||||
throw new ArgumentNullException("newText"); |
|
||||||
PerformChange(new TextChangeEventArgs(offset, b.ToString(offset, length), newText)); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Replace(int offset, int length, ITextSource newText) |
|
||||||
{ |
|
||||||
if (newText == null) |
|
||||||
throw new ArgumentNullException("newText"); |
|
||||||
Replace(offset, length, newText.Text); |
|
||||||
} |
|
||||||
|
|
||||||
bool isInChange; |
|
||||||
|
|
||||||
void PerformChange(TextChangeEventArgs change) |
|
||||||
{ |
|
||||||
// Ensure that all changes take place inside an update group.
|
|
||||||
// Will also take care of throwing an exception if isInChange is set.
|
|
||||||
StartUndoableAction(); |
|
||||||
try { |
|
||||||
isInChange = true; |
|
||||||
try { |
|
||||||
if (TextChanging != null) |
|
||||||
TextChanging(this, change); |
|
||||||
|
|
||||||
// Perform changes to document and Version property
|
|
||||||
documentSnapshot = null; |
|
||||||
cachedText = null; |
|
||||||
b.Remove(change.Offset, change.RemovalLength); |
|
||||||
b.Insert(change.Offset, change.InsertedText.Text); |
|
||||||
versionProvider.AppendChange(change); |
|
||||||
|
|
||||||
// Update anchors and fire Deleted events
|
|
||||||
UpdateAnchors(change); |
|
||||||
|
|
||||||
if (TextChanged != null) |
|
||||||
TextChanged(this, change); |
|
||||||
} finally { |
|
||||||
isInChange = false; |
|
||||||
} |
|
||||||
} finally { |
|
||||||
EndUndoableAction(); |
|
||||||
} |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Undo
|
|
||||||
int undoGroupNesting = 0; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void StartUndoableAction() |
|
||||||
{ |
|
||||||
// prevent changes from within the TextChanging/TextChanged event handlers
|
|
||||||
if (isInChange) |
|
||||||
throw new InvalidOperationException(); |
|
||||||
undoGroupNesting++; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void EndUndoableAction() |
|
||||||
{ |
|
||||||
undoGroupNesting--; |
|
||||||
if (undoGroupNesting == 0) { |
|
||||||
if (ChangeCompleted != null) |
|
||||||
ChangeCompleted(this, EventArgs.Empty); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDisposable OpenUndoGroup() |
|
||||||
{ |
|
||||||
StartUndoableAction(); |
|
||||||
return new CallbackOnDispose(EndUndoableAction); |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region CreateSnapshot/CreateReader
|
|
||||||
ReadOnlyDocument documentSnapshot; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocument CreateDocumentSnapshot() |
|
||||||
{ |
|
||||||
if (documentSnapshot == null) |
|
||||||
documentSnapshot = new ReadOnlyDocument(this, this.FileName); |
|
||||||
return documentSnapshot; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot() |
|
||||||
{ |
|
||||||
return new StringTextSource(this.Text, versionProvider.CurrentVersion); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot(int offset, int length) |
|
||||||
{ |
|
||||||
return new StringTextSource(GetText(offset, length)); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TextReader CreateReader() |
|
||||||
{ |
|
||||||
return new StringReader(this.Text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TextReader CreateReader(int offset, int length) |
|
||||||
{ |
|
||||||
return new StringReader(GetText(offset, length)); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(TextWriter writer) |
|
||||||
{ |
|
||||||
if (writer == null) |
|
||||||
throw new ArgumentNullException("writer"); |
|
||||||
writer.Write(this.Text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(TextWriter writer, int offset, int length) |
|
||||||
{ |
|
||||||
if (writer == null) |
|
||||||
throw new ArgumentNullException("writer"); |
|
||||||
writer.Write(GetText(offset, length)); |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region GetText / IndexOf
|
|
||||||
string cachedText; |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Text { |
|
||||||
get { |
|
||||||
if (cachedText == null) |
|
||||||
cachedText = b.ToString(); |
|
||||||
return cachedText; |
|
||||||
} |
|
||||||
set { |
|
||||||
Replace(0, b.Length, value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int TextLength { |
|
||||||
get { return b.Length; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public char GetCharAt(int offset) |
|
||||||
{ |
|
||||||
return b[offset]; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(int offset, int length) |
|
||||||
{ |
|
||||||
return b.ToString(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(ISegment segment) |
|
||||||
{ |
|
||||||
if (segment == null) |
|
||||||
throw new ArgumentNullException("segment"); |
|
||||||
return b.ToString(segment.Offset, segment.Length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return this.Text.IndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
|
||||||
{ |
|
||||||
return this.Text.IndexOfAny(anyOf, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return this.Text.IndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return this.Text.LastIndexOf(c, startIndex + count - 1, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return this.Text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType); |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region CreateAnchor
|
|
||||||
readonly List<WeakReference> anchors = new List<WeakReference>(); |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextAnchor CreateAnchor(int offset) |
|
||||||
{ |
|
||||||
var newAnchor = new SimpleAnchor(this, offset); |
|
||||||
for (int i = 0; i < anchors.Count; i++) { |
|
||||||
if (!anchors[i].IsAlive) |
|
||||||
anchors[i] = new WeakReference(newAnchor); |
|
||||||
} |
|
||||||
anchors.Add(new WeakReference(newAnchor)); |
|
||||||
return newAnchor; |
|
||||||
} |
|
||||||
|
|
||||||
void UpdateAnchors(TextChangeEventArgs change) |
|
||||||
{ |
|
||||||
// First update all anchors, then fire the deleted events.
|
|
||||||
List<int> deletedAnchors = new List<int>(); |
|
||||||
for (int i = 0; i < anchors.Count; i++) { |
|
||||||
var anchor = anchors[i].Target as SimpleAnchor; |
|
||||||
if (anchor != null) { |
|
||||||
anchor.Update(change); |
|
||||||
if (anchor.IsDeleted) |
|
||||||
deletedAnchors.Add(i); |
|
||||||
} |
|
||||||
} |
|
||||||
deletedAnchors.Reverse(); |
|
||||||
foreach (var index in deletedAnchors) { |
|
||||||
var anchor = anchors[index].Target as SimpleAnchor; |
|
||||||
if (anchor != null) |
|
||||||
anchor.RaiseDeletedEvent(); |
|
||||||
anchors.RemoveAt(index); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
sealed class SimpleAnchor : ITextAnchor |
|
||||||
{ |
|
||||||
readonly StringBuilderDocument document; |
|
||||||
int offset; |
|
||||||
|
|
||||||
public SimpleAnchor(StringBuilderDocument document, int offset) |
|
||||||
{ |
|
||||||
this.document = document; |
|
||||||
this.offset = offset; |
|
||||||
} |
|
||||||
|
|
||||||
public event EventHandler Deleted; |
|
||||||
|
|
||||||
public TextLocation Location { |
|
||||||
get { |
|
||||||
if (IsDeleted) |
|
||||||
throw new InvalidOperationException(); |
|
||||||
return document.GetLocation(offset); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int Offset { |
|
||||||
get { |
|
||||||
if (IsDeleted) |
|
||||||
throw new InvalidOperationException(); |
|
||||||
return offset; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public AnchorMovementType MovementType { get; set; } |
|
||||||
|
|
||||||
public bool SurviveDeletion { get; set; } |
|
||||||
|
|
||||||
public bool IsDeleted { |
|
||||||
get { return offset < 0; } |
|
||||||
} |
|
||||||
|
|
||||||
public void Update(TextChangeEventArgs change) |
|
||||||
{ |
|
||||||
if (SurviveDeletion || offset <= change.Offset || offset >= change.Offset + change.RemovalLength) { |
|
||||||
offset = change.GetNewOffset(offset, MovementType); |
|
||||||
} else { |
|
||||||
offset = -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void RaiseDeletedEvent() |
|
||||||
{ |
|
||||||
if (Deleted != null) |
|
||||||
Deleted(this, EventArgs.Empty); |
|
||||||
} |
|
||||||
|
|
||||||
public int Line { |
|
||||||
get { return this.Location.Line; } |
|
||||||
} |
|
||||||
|
|
||||||
public int Column { |
|
||||||
get { return this.Location.Column; } |
|
||||||
} |
|
||||||
} |
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public virtual object GetService(Type serviceType) |
|
||||||
{ |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public virtual event EventHandler FileNameChanged { add {} remove {} } |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public virtual string FileName { |
|
||||||
get { return string.Empty; } |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,160 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.IO; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Implements the ITextSource interface using a string.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable] |
|
||||||
public class StringTextSource : ITextSource |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Gets a text source containing the empty string.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly StringTextSource Empty = new StringTextSource(string.Empty); |
|
||||||
|
|
||||||
readonly string text; |
|
||||||
readonly ITextSourceVersion version; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StringTextSource with the given text.
|
|
||||||
/// </summary>
|
|
||||||
public StringTextSource(string text) |
|
||||||
{ |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
this.text = text; |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StringTextSource with the given text.
|
|
||||||
/// </summary>
|
|
||||||
public StringTextSource(string text, ITextSourceVersion version) |
|
||||||
{ |
|
||||||
if (text == null) |
|
||||||
throw new ArgumentNullException("text"); |
|
||||||
this.text = text; |
|
||||||
this.version = version; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSourceVersion Version { |
|
||||||
get { return version; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int TextLength { |
|
||||||
get { return text.Length; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Text { |
|
||||||
get { return text; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot() |
|
||||||
{ |
|
||||||
return this; // StringTextSource is immutable
|
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot(int offset, int length) |
|
||||||
{ |
|
||||||
return new StringTextSource(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 void WriteTextTo(TextWriter writer) |
|
||||||
{ |
|
||||||
writer.Write(text); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(TextWriter writer, int offset, int length) |
|
||||||
{ |
|
||||||
writer.Write(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 string GetText(ISegment segment) |
|
||||||
{ |
|
||||||
if (segment == null) |
|
||||||
throw new ArgumentNullException("segment"); |
|
||||||
return text.Substring(segment.Offset, segment.Length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return text.IndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
|
||||||
{ |
|
||||||
return text.IndexOfAny(anyOf, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return text.IndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return text.LastIndexOf(c, startIndex + count - 1, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,118 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Describes a change of the document text.
|
|
||||||
/// This class is thread-safe.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable] |
|
||||||
public class TextChangeEventArgs : EventArgs |
|
||||||
{ |
|
||||||
readonly int offset; |
|
||||||
readonly ITextSource removedText; |
|
||||||
readonly ITextSource insertedText; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The offset at which the change occurs.
|
|
||||||
/// </summary>
|
|
||||||
public int Offset { |
|
||||||
get { return offset; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The text that was removed.
|
|
||||||
/// </summary>
|
|
||||||
public ITextSource RemovedText { |
|
||||||
get { return removedText; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of characters removed.
|
|
||||||
/// </summary>
|
|
||||||
public int RemovalLength { |
|
||||||
get { return removedText.TextLength; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The text that was inserted.
|
|
||||||
/// </summary>
|
|
||||||
public ITextSource InsertedText { |
|
||||||
get { return insertedText; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of characters inserted.
|
|
||||||
/// </summary>
|
|
||||||
public int InsertionLength { |
|
||||||
get { return insertedText.TextLength; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new TextChangeEventArgs object.
|
|
||||||
/// </summary>
|
|
||||||
public TextChangeEventArgs(int offset, string removedText, string insertedText) |
|
||||||
{ |
|
||||||
if (offset < 0) |
|
||||||
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); |
|
||||||
this.offset = offset; |
|
||||||
this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty; |
|
||||||
this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty; |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new TextChangeEventArgs object.
|
|
||||||
/// </summary>
|
|
||||||
public TextChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText) |
|
||||||
{ |
|
||||||
if (offset < 0) |
|
||||||
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); |
|
||||||
this.offset = offset; |
|
||||||
this.removedText = removedText ?? StringTextSource.Empty; |
|
||||||
this.insertedText = insertedText ?? StringTextSource.Empty; |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the new offset where the specified offset moves after this document change.
|
|
||||||
/// </summary>
|
|
||||||
public virtual int GetNewOffset(int offset, AnchorMovementType movementType = AnchorMovementType.Default) |
|
||||||
{ |
|
||||||
if (offset >= this.Offset && offset <= this.Offset + this.RemovalLength) { |
|
||||||
if (movementType == AnchorMovementType.BeforeInsertion) |
|
||||||
return this.Offset; |
|
||||||
else |
|
||||||
return this.Offset + this.InsertionLength; |
|
||||||
} else if (offset > this.Offset) { |
|
||||||
return offset + this.InsertionLength - this.RemovalLength; |
|
||||||
} else { |
|
||||||
return offset; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates TextChangeEventArgs for the reverse change.
|
|
||||||
/// </summary>
|
|
||||||
public virtual TextChangeEventArgs Invert() |
|
||||||
{ |
|
||||||
return new TextChangeEventArgs(offset, insertedText, removedText); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,131 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Diagnostics; |
|
||||||
using System.Linq; |
|
||||||
using ICSharpCode.Decompiler.Util; |
|
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.Editor |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Provides ITextSourceVersion instances.
|
|
||||||
/// </summary>
|
|
||||||
public class TextSourceVersionProvider |
|
||||||
{ |
|
||||||
Version currentVersion; |
|
||||||
|
|
||||||
public TextSourceVersionProvider() |
|
||||||
{ |
|
||||||
this.currentVersion = new Version(this); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current version.
|
|
||||||
/// </summary>
|
|
||||||
public ITextSourceVersion CurrentVersion { |
|
||||||
get { return currentVersion; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Replaces the current version with a new version.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="change">Change from current version to new version</param>
|
|
||||||
public void AppendChange(TextChangeEventArgs change) |
|
||||||
{ |
|
||||||
if (change == null) |
|
||||||
throw new ArgumentNullException("change"); |
|
||||||
currentVersion.change = change; |
|
||||||
currentVersion.next = new Version(currentVersion); |
|
||||||
currentVersion = currentVersion.next; |
|
||||||
} |
|
||||||
|
|
||||||
[DebuggerDisplay("Version #{id}")] |
|
||||||
sealed class Version : ITextSourceVersion |
|
||||||
{ |
|
||||||
// Reference back to the provider.
|
|
||||||
// Used to determine if two checkpoints belong to the same document.
|
|
||||||
readonly TextSourceVersionProvider provider; |
|
||||||
// ID used for CompareAge()
|
|
||||||
readonly int id; |
|
||||||
|
|
||||||
// the change from this version to the next version
|
|
||||||
internal TextChangeEventArgs change; |
|
||||||
internal Version next; |
|
||||||
|
|
||||||
internal Version(TextSourceVersionProvider provider) |
|
||||||
{ |
|
||||||
this.provider = provider; |
|
||||||
} |
|
||||||
|
|
||||||
internal Version(Version prev) |
|
||||||
{ |
|
||||||
this.provider = prev.provider; |
|
||||||
this.id = unchecked( prev.id + 1 ); |
|
||||||
} |
|
||||||
|
|
||||||
public bool BelongsToSameDocumentAs(ITextSourceVersion other) |
|
||||||
{ |
|
||||||
Version o = other as Version; |
|
||||||
return o != null && provider == o.provider; |
|
||||||
} |
|
||||||
|
|
||||||
public int CompareAge(ITextSourceVersion other) |
|
||||||
{ |
|
||||||
if (other == null) |
|
||||||
throw new ArgumentNullException("other"); |
|
||||||
Version o = other as Version; |
|
||||||
if (o == null || provider != o.provider) |
|
||||||
throw new ArgumentException("Versions do not belong to the same document."); |
|
||||||
// We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
|
|
||||||
// This is guaranteed on x86 because so many checkpoints don't fit into memory.
|
|
||||||
return Math.Sign(unchecked( this.id - o.id )); |
|
||||||
} |
|
||||||
|
|
||||||
public IEnumerable<TextChangeEventArgs> GetChangesTo(ITextSourceVersion other) |
|
||||||
{ |
|
||||||
int result = CompareAge(other); |
|
||||||
Version o = (Version)other; |
|
||||||
if (result < 0) |
|
||||||
return GetForwardChanges(o); |
|
||||||
else if (result > 0) |
|
||||||
return o.GetForwardChanges(this).Reverse().Select(change => change.Invert()); |
|
||||||
else |
|
||||||
return EmptyList<TextChangeEventArgs>.Instance; |
|
||||||
} |
|
||||||
|
|
||||||
IEnumerable<TextChangeEventArgs> GetForwardChanges(Version other) |
|
||||||
{ |
|
||||||
// Return changes from this(inclusive) to other(exclusive).
|
|
||||||
for (Version node = this; node != other; node = node.next) { |
|
||||||
yield return node.change; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int MoveOffsetTo(ITextSourceVersion other, int oldOffset, AnchorMovementType movement) |
|
||||||
{ |
|
||||||
int offset = oldOffset; |
|
||||||
foreach (var e in GetChangesTo(other)) { |
|
||||||
offset = e.GetNewOffset(offset, movement); |
|
||||||
} |
|
||||||
return offset; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue