diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs
index 431d6ab4db..02cbe5ac71 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs
@@ -71,14 +71,24 @@ namespace ICSharpCode.AvalonEdit.Document
public DocumentChangeEventArgs(int offset, string removedText, string insertedText, OffsetChangeMap offsetChangeMap)
: base(offset, removedText, insertedText)
{
- ThrowUtil.CheckNotNegative(offset, "offset");
- ThrowUtil.CheckNotNull(removedText, "removedText");
- ThrowUtil.CheckNotNull(insertedText, "insertedText");
-
+ SetOffsetChangeMap(offsetChangeMap);
+ }
+
+ ///
+ /// Creates a new DocumentChangeEventArgs object.
+ ///
+ public DocumentChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText, OffsetChangeMap offsetChangeMap)
+ : base(offset, removedText, insertedText)
+ {
+ SetOffsetChangeMap(offsetChangeMap);
+ }
+
+ void SetOffsetChangeMap(OffsetChangeMap offsetChangeMap)
+ {
if (offsetChangeMap != null) {
if (!offsetChangeMap.IsFrozen)
throw new ArgumentException("The OffsetChangeMap must be frozen before it can be used in DocumentChangeEventArgs");
- if (!offsetChangeMap.IsValidForDocumentChange(offset, removedText.Length, insertedText.Length))
+ if (!offsetChangeMap.IsValidForDocumentChange(this.Offset, this.RemovalLength, this.InsertionLength))
throw new ArgumentException("OffsetChangeMap is not valid for this document change", "offsetChangeMap");
this.offsetChangeMap = offsetChangeMap;
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs
index 0325fef44b..3f05979544 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.Document
{
@@ -181,7 +182,7 @@ namespace ICSharpCode.AvalonEdit.Document
#endregion
#region Insert
- public void Insert(int offset, string text)
+ public void Insert(int offset, ITextSource text)
{
DocumentLine line = documentLineTree.GetByOffset(offset);
int lineOffset = line.Offset;
@@ -202,7 +203,7 @@ namespace ICSharpCode.AvalonEdit.Document
if (ds == SimpleSegment.Invalid) {
// no newline is being inserted, all text is inserted in a single line
//line.InsertedLinePart(offset - line.Offset, text.Length);
- SetLineLength(line, line.TotalLength + text.Length);
+ SetLineLength(line, line.TotalLength + text.TextLength);
return;
}
//DocumentLine firstLine = line;
@@ -224,9 +225,9 @@ namespace ICSharpCode.AvalonEdit.Document
}
//firstLine.SplitTo(line);
// insert rest after last delimiter
- if (lastDelimiterEnd != text.Length) {
+ if (lastDelimiterEnd != text.TextLength) {
//line.InsertedLinePart(0, text.Length - lastDelimiterEnd);
- SetLineLength(line, line.TotalLength + text.Length - lastDelimiterEnd);
+ SetLineLength(line, line.TotalLength + text.TextLength - lastDelimiterEnd);
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs
index fdc1935066..a33e1cf19d 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs
@@ -11,6 +11,7 @@ namespace ICSharpCode.AvalonEdit.Document
///
/// Implements the ITextSource interface using a rope.
///
+ [Serializable]
public sealed class RopeTextSource : ITextSource
{
readonly Rope rope;
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs
index d2e41154a6..454124df12 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs
@@ -524,6 +524,21 @@ namespace ICSharpCode.AvalonEdit.Document
/// The caret will also move behind the inserted text.
///
public void Insert(int offset, string text)
+ {
+ Replace(offset, 0, new StringTextSource(text), null);
+ }
+
+ ///
+ /// Inserts text.
+ ///
+ /// The offset at which the text is inserted.
+ /// The new text.
+ ///
+ /// 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.
+ ///
+ public void Insert(int offset, ITextSource text)
{
Replace(offset, 0, text, null);
}
@@ -539,6 +554,25 @@ namespace ICSharpCode.AvalonEdit.Document
/// The caret will also move according to the parameter.
///
public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType)
+ {
+ if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) {
+ Replace(offset, 0, new StringTextSource(text), OffsetChangeMappingType.KeepAnchorBeforeInsertion);
+ } else {
+ Replace(offset, 0, new StringTextSource(text), null);
+ }
+ }
+
+ ///
+ /// Inserts text.
+ ///
+ /// The offset at which the text is inserted.
+ /// The new text.
+ ///
+ /// 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 parameter.
+ ///
+ public void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType)
{
if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) {
Replace(offset, 0, text, OffsetChangeMappingType.KeepAnchorBeforeInsertion);
@@ -562,7 +596,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// Length of the text to be removed.
public void Remove(int offset, int length)
{
- Replace(offset, length, string.Empty);
+ Replace(offset, length, StringTextSource.Empty);
}
internal bool inDocumentChanging;
@@ -571,6 +605,16 @@ namespace ICSharpCode.AvalonEdit.Document
/// Replaces text.
///
public void Replace(ISegment segment, string text)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ Replace(segment.Offset, segment.Length, new StringTextSource(text), null);
+ }
+
+ ///
+ /// Replaces text.
+ ///
+ public void Replace(ISegment segment, ITextSource text)
{
if (segment == null)
throw new ArgumentNullException("segment");
@@ -584,6 +628,17 @@ namespace ICSharpCode.AvalonEdit.Document
/// The length of the text to be replaced.
/// The new text.
public void Replace(int offset, int length, string text)
+ {
+ Replace(offset, length, new StringTextSource(text), null);
+ }
+
+ ///
+ /// Replaces text.
+ ///
+ /// The starting offset of the text to be replaced.
+ /// The length of the text to be replaced.
+ /// The new text.
+ public void Replace(int offset, int length, ITextSource text)
{
Replace(offset, length, text, null);
}
@@ -597,6 +652,19 @@ namespace ICSharpCode.AvalonEdit.Document
/// The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text.
/// This affects how the anchors and segments inside the replaced region behave.
public void Replace(int offset, int length, string text, OffsetChangeMappingType offsetChangeMappingType)
+ {
+ Replace(offset, length, new StringTextSource(text), offsetChangeMappingType);
+ }
+
+ ///
+ /// Replaces text.
+ ///
+ /// The starting offset of the text to be replaced.
+ /// The length of the text to be replaced.
+ /// The new text.
+ /// The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text.
+ /// This affects how the anchors and segments inside the replaced region behave.
+ public void Replace(int offset, int length, ITextSource text, OffsetChangeMappingType offsetChangeMappingType)
{
if (text == null)
throw new ArgumentNullException("text");
@@ -607,33 +675,33 @@ namespace ICSharpCode.AvalonEdit.Document
break;
case OffsetChangeMappingType.KeepAnchorBeforeInsertion:
Replace(offset, length, text, OffsetChangeMap.FromSingleElement(
- new OffsetChangeMapEntry(offset, length, text.Length, false, true)));
+ new OffsetChangeMapEntry(offset, length, text.TextLength, false, true)));
break;
case OffsetChangeMappingType.RemoveAndInsert:
- if (length == 0 || text.Length == 0) {
+ if (length == 0 || text.TextLength == 0) {
// only insertion or only removal?
// OffsetChangeMappingType doesn't matter, just use Normal.
Replace(offset, length, text, null);
} else {
OffsetChangeMap map = new OffsetChangeMap(2);
map.Add(new OffsetChangeMapEntry(offset, length, 0));
- map.Add(new OffsetChangeMapEntry(offset, 0, text.Length));
+ map.Add(new OffsetChangeMapEntry(offset, 0, text.TextLength));
map.Freeze();
Replace(offset, length, text, map);
}
break;
case OffsetChangeMappingType.CharacterReplace:
- if (length == 0 || text.Length == 0) {
+ if (length == 0 || text.TextLength == 0) {
// only insertion or only removal?
// OffsetChangeMappingType doesn't matter, just use Normal.
Replace(offset, length, text, null);
- } else if (text.Length > length) {
+ } else if (text.TextLength > length) {
// look at OffsetChangeMappingType.CharacterReplace XML comments on why we need to replace
// the last character
- OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + length - 1, 1, 1 + text.Length - length);
+ OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + length - 1, 1, 1 + text.TextLength - length);
Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
- } else if (text.Length < length) {
- OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + text.Length, length - text.Length, 0, true, false);
+ } else if (text.TextLength < length) {
+ OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + text.TextLength, length - text.TextLength, 0, true, false);
Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
} else {
Replace(offset, length, text, OffsetChangeMap.Empty);
@@ -660,10 +728,30 @@ namespace ICSharpCode.AvalonEdit.Document
/// DocumentChangeEventArgs instance.
///
public void Replace(int offset, int length, string text, OffsetChangeMap offsetChangeMap)
+ {
+ Replace(offset, length, new StringTextSource(text), offsetChangeMap);
+ }
+
+ ///
+ /// Replaces text.
+ ///
+ /// The starting offset of the text to be replaced.
+ /// The length of the text to be replaced.
+ /// The new text.
+ /// The offsetChangeMap determines how offsets inside the old text are mapped to the new text.
+ /// This affects how the anchors and segments inside the replaced region behave.
+ /// If you pass null (the default when using one of the other overloads), the offsets are changed as
+ /// in OffsetChangeMappingType.Normal mode.
+ /// If you pass OffsetChangeMap.Empty, then everything will stay in its old place (OffsetChangeMappingType.CharacterReplace mode).
+ /// The offsetChangeMap must be a valid 'explanation' for the document change. See .
+ /// Passing an OffsetChangeMap to the Replace method will automatically freeze it to ensure the thread safety of the resulting
+ /// DocumentChangeEventArgs instance.
+ ///
+ public void Replace(int offset, int length, ITextSource text, OffsetChangeMap offsetChangeMap)
{
if (text == null)
throw new ArgumentNullException("text");
-
+ text = text.CreateSnapshot();
if (offsetChangeMap != null)
offsetChangeMap.Freeze();
@@ -687,18 +775,26 @@ namespace ICSharpCode.AvalonEdit.Document
}
}
- void DoReplace(int offset, int length, string newText, OffsetChangeMap offsetChangeMap)
+ void DoReplace(int offset, int length, ITextSource newText, OffsetChangeMap offsetChangeMap)
{
- if (length == 0 && newText.Length == 0)
+ if (length == 0 && newText.TextLength == 0)
return;
// trying to replace a single character in 'Normal' mode?
// for single characters, 'CharacterReplace' mode is equivalent, but more performant
// (we don't have to touch the anchorTree at all in 'CharacterReplace' mode)
- if (length == 1 && newText.Length == 1 && offsetChangeMap == null)
+ if (length == 1 && newText.TextLength == 1 && offsetChangeMap == null)
offsetChangeMap = OffsetChangeMap.Empty;
- string removedText = rope.ToString(offset, length);
+ ITextSource removedText;
+ if (length == 0) {
+ removedText = StringTextSource.Empty;
+ } else if (length < 100) {
+ removedText = new StringTextSource(rope.ToString(offset, length));
+ } else {
+ // use a rope if the removed string is long
+ removedText = new RopeTextSource(rope.GetRange(offset, length));
+ }
DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);
// fire DocumentChanging event
@@ -721,7 +817,11 @@ namespace ICSharpCode.AvalonEdit.Document
if (offset == 0 && length == rope.Length) {
// optimize replacing the whole document
rope.Clear();
- rope.InsertText(0, newText);
+ var newRopeTextSource = newText as RopeTextSource;
+ if (newRopeTextSource != null)
+ rope.InsertRange(0, newRopeTextSource.GetRope());
+ else
+ rope.InsertText(0, newText.Text);
lineManager.Rebuild();
} else {
rope.RemoveRange(offset, length);
@@ -729,7 +829,11 @@ namespace ICSharpCode.AvalonEdit.Document
#if DEBUG
lineTree.CheckProperties();
#endif
- rope.InsertText(offset, newText);
+ var newRopeTextSource = newText as RopeTextSource;
+ if (newRopeTextSource != null)
+ rope.InsertRange(offset, newRopeTextSource.GetRope());
+ else
+ rope.InsertText(offset, newText.Text);
lineManager.Insert(offset, newText);
#if DEBUG
lineTree.CheckProperties();
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs
index 4201ce4484..f9a793b911 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs
@@ -109,6 +109,18 @@ namespace ICSharpCode.NRefactory.Editor
///
void Insert(int offset, string text);
+ ///
+ /// Inserts text.
+ ///
+ /// The offset at which the text is inserted.
+ /// The new text.
+ ///
+ /// 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.
+ ///
+ void Insert(int offset, ITextSource text);
+
///
/// Inserts text.
///
@@ -121,6 +133,18 @@ namespace ICSharpCode.NRefactory.Editor
///
void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType);
+ ///
+ /// Inserts text.
+ ///
+ /// The offset at which the text is inserted.
+ /// The new text.
+ ///
+ /// 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 parameter.
+ ///
+ void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType);
+
///
/// Removes text.
///
@@ -136,6 +160,14 @@ namespace ICSharpCode.NRefactory.Editor
/// The new text.
void Replace(int offset, int length, string newText);
+ ///
+ /// Replaces text.
+ ///
+ /// The starting offset of the text to be replaced.
+ /// The length of the text to be replaced.
+ /// The new text.
+ void Replace(int offset, int length, ITextSource newText);
+
///
/// Make the document combine the following actions into a single
/// action for undo purposes.
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs
index a4d76a8394..2a36ccda33 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs
@@ -257,6 +257,21 @@ namespace ICSharpCode.NRefactory.Editor
throw new NotSupportedException();
}
+ void IDocument.Insert(int offset, ITextSource text)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IDocument.Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType)
+ {
+ throw new NotImplementedException();
+ }
+
+ void IDocument.Replace(int offset, int length, ITextSource newText)
+ {
+ throw new NotImplementedException();
+ }
+
void IDocument.StartUndoableAction()
{
}
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs
index 79553a4136..fad9dfcc5b 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs
@@ -104,17 +104,35 @@ namespace ICSharpCode.NRefactory.Editor
Replace(offset, 0, text);
}
+ ///
+ public void Insert(int offset, ITextSource text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ Replace(offset, 0, text.Text);
+ }
+
///
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);
}
+ ///
+ 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
{
@@ -149,6 +167,14 @@ namespace ICSharpCode.NRefactory.Editor
PerformChange(new TextChangeEventArgs(offset, b.ToString(offset, length), newText));
}
+ ///
+ 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)
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringTextSource.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringTextSource.cs
index 3a24db3646..36780954bd 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringTextSource.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/StringTextSource.cs
@@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.Editor
[Serializable]
public class StringTextSource : ITextSource
{
+ ///
+ /// Gets a text source containing the empty string.
+ ///
+ public static readonly StringTextSource Empty = new StringTextSource(string.Empty);
+
readonly string text;
readonly ITextSourceVersion version;
@@ -69,7 +74,7 @@ namespace ICSharpCode.NRefactory.Editor
///
public ITextSource CreateSnapshot()
{
- return this; // StringTextBuffer is immutable
+ return this; // StringTextSource is immutable
}
///
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs
index 936de37aa4..f1eeb4d27f 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs
@@ -28,8 +28,8 @@ namespace ICSharpCode.NRefactory.Editor
public class TextChangeEventArgs : EventArgs
{
readonly int offset;
- readonly string removedText;
- readonly string insertedText;
+ readonly ITextSource removedText;
+ readonly ITextSource insertedText;
///
/// The offset at which the change occurs.
@@ -41,7 +41,7 @@ namespace ICSharpCode.NRefactory.Editor
///
/// The text that was removed.
///
- public string RemovedText {
+ public ITextSource RemovedText {
get { return removedText; }
}
@@ -49,13 +49,13 @@ namespace ICSharpCode.NRefactory.Editor
/// The number of characters removed.
///
public int RemovalLength {
- get { return removedText.Length; }
+ get { return removedText.TextLength; }
}
///
/// The text that was inserted.
///
- public string InsertedText {
+ public ITextSource InsertedText {
get { return insertedText; }
}
@@ -63,7 +63,7 @@ namespace ICSharpCode.NRefactory.Editor
/// The number of characters inserted.
///
public int InsertionLength {
- get { return insertedText.Length; }
+ get { return insertedText.TextLength; }
}
///
@@ -71,9 +71,23 @@ namespace ICSharpCode.NRefactory.Editor
///
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 ?? string.Empty;
- this.insertedText = insertedText ?? string.Empty;
+ this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty;
+ this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty;
+ }
+
+ ///
+ /// Creates a new TextChangeEventArgs object.
+ ///
+ 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;
}
///