private NotifyTextChange ( int start, int oldLength, int newLength ) : void | ||
start | int | |
oldLength | int | |
newLength | int | |
return | void |
/// <summary> /// Handles non-trivial changes like changes that delete elements, /// change identifier names, introducing new braces: changes /// that cannot be handled without background parse. /// </summary> private void ProcessComplexChange(TextChangeContext context) { // Cancel background parse if it is running Cancel(); TextChange textChange = new TextChange() { OldTextProvider = context.OldTextProvider, NewTextProvider = context.NewTextProvider }; try { // Get write lock since there may be concurrent readers // of the tree. Note that there are no concurrent writers // since changes can only come from a background parser // and are always applied from the main thread. _editorTree.AcquireWriteLock(); if (_pendingChanges.FullParseRequired) { // When full parse is required, change is like replace the entire file textChange.OldRange = TextRange.FromBounds(0, context.OldText.Length); textChange.NewRange = TextRange.FromBounds(0, context.NewText.Length); // Remove damaged elements if any and reflect text change. // Although we are invalidating the AST next, old copy will // be kept for operations that may need it such as smart indent. bool elementsChanged; _editorTree.InvalidateInRange(_editorTree.AstRoot, context.OldRange, out elementsChanged); _editorTree.NotifyTextChange(context.NewStart, context.OldLength, context.NewLength); // Invalidate will store existing AST as previous snapshot // and create temporary empty AST until the next async parse. _editorTree.Invalidate(); } else { textChange.OldRange = context.OldRange; textChange.NewRange = context.NewRange; DeleteAndShiftElements(context); Debug.Assert(_editorTree.AstRoot.Children.Count > 0); } _pendingChanges.Combine(textChange); _pendingChanges.Version = TextBuffer != null ? TextBuffer.CurrentSnapshot.Version.VersionNumber : 1; UpdateTreeTextSnapshot(); } finally { // Lock must be released before firing events otherwise we may hang _editorTree.ReleaseWriteLock(); } _editorTree.FireOnUpdateCompleted(TreeUpdateType.NodesRemoved); }
/// <summary> /// Handles non-trivial changes like changes that delete elements, /// change identifier names, introducing new braces: changes /// that cannot be handled without background parse. /// </summary> private void ProcessComplexChange(TextChangeContext context) { // Cancel background parse if it is running Cancel(); var c = context.PendingChanges; try { // Get write lock since there may be concurrent readers // of the tree. Note that there are no concurrent writers // since changes can only come from a background parser // and are always applied from the main thread. _editorTree.AcquireWriteLock(); int start, oldLength, newLength; if (Changes.FullParseRequired) { // When full parse is required, change is like replace the entire file start = 0; oldLength = c.OldTextProvider.Length; newLength = c.NewTextProvider.Length; // Remove damaged elements if any and reflect text change. // the tree remains usable outside of the damaged scope. _editorTree.InvalidateInRange(c.OldRange); _editorTree.NotifyTextChange(c.Start, c.OldLength, c.NewLength); } else { start = c.Start; oldLength = c.OldLength; newLength = c.NewLength; DeleteAndShiftElements(context); Debug.Assert(_editorTree.AstRoot.Children.Count > 0); } var ttc = new TreeTextChange(start, oldLength, newLength, _editorTree.BufferSnapshot, EditorBuffer.CurrentSnapshot); Changes.Combine(ttc); Changes.Version = EditorBuffer?.CurrentSnapshot?.Version ?? 1; _editorTree.BufferSnapshot = EditorBuffer.CurrentSnapshot; } finally { // Lock must be released before firing events otherwise we may hang _editorTree.ReleaseWriteLock(); } _editorTree.FireOnUpdateCompleted(TreeUpdateType.NodesRemoved); }
// internal for unit tests internal bool DeleteAndShiftElements(TextChangeContext context) { if (Thread.CurrentThread.ManagedThreadId != _ownerThreadId) { throw new ThreadStateException("Method should only be called on the main thread"); } TextChange textChange = context.PendingChanges; var changeType = textChange.TextChangeType; bool elementsChanged = false; if (changeType == TextChangeType.Structure) { IAstNode changedElement = context.ChangedNode; int start = context.NewStart; // We delete change nodes unless node is a token node // which range can be modified such as string or comment var positionType = PositionType.Undefined; if (changedElement != null) { IAstNode node; positionType = changedElement.GetPositionNode(context.NewStart, out node); } bool deleteElements = (context.OldLength > 0) || (positionType != PositionType.Token); // In case of delete or replace we need to invalidate elements that were // damaged by the delete operation. We need to remove elements and their keys // so they won't be found by validator and incremental change analysis // will not be looking at zombies. if (deleteElements) { _pendingChanges.FullParseRequired = _editorTree.InvalidateInRange(_editorTree.AstRoot, context.OldRange, out elementsChanged); } } _editorTree.NotifyTextChange(context.NewStart, context.OldLength, context.NewLength); return(elementsChanged); }