private void DoReplace(int offset, int length, ITextSource newText, OffsetChangeMap offsetChangeMap)
{
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.TextLength == 1 && offsetChangeMap == null)
{
offsetChangeMap = OffsetChangeMap.Empty;
}
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));
}
var args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);
// fire DocumentChanging event
if (Changing != null)
{
Changing(this, args);
}
if (textChanging != null)
{
textChanging(this, args);
}
undoStack.Push(this, args);
cachedText = null; // reset cache of complete document text
fireTextChanged = true;
var delayedEvents = new DelayedEvents();
lock (lockObject)
{
// create linked list of checkpoints
versionProvider.AppendChange(args);
// now update the textBuffer and lineTree
if (offset == 0 && length == rope.Length)
{
// optimize replacing the whole document
rope.Clear();
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);
lineManager.Remove(offset, length);
#if DEBUG
lineTree.CheckProperties();
#endif
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();
#endif
}
}
// update text anchors
if (offsetChangeMap == null)
{
anchorTree.HandleTextChange(args.CreateSingleChangeMapEntry(), delayedEvents);
}
else
{
foreach (var entry in offsetChangeMap)
{
anchorTree.HandleTextChange(entry, delayedEvents);
}
}
lineManager.ChangeComplete(args);
// raise delayed events after our data structures are consistent again
delayedEvents.RaiseEvents();
// fire DocumentChanged event
if (Changed != null)
{
Changed(this, args);
}
if (textChanged != null)
{
textChanged(this, args);
}
}