void OnDocumentChanged(DocumentChangeEventArgs e) { OffsetChangeMap map = e.OffsetChangeMapOrNull; if (map != null) { foreach (OffsetChangeMapEntry entry in map) { UpdateOffsetsInternal(entry); } } else { UpdateOffsetsInternal(e.CreateSingleChangeMapEntry()); } }
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)); } DocumentChangeEventArgs 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; DelayedEvents 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 (OffsetChangeMapEntry 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); } }