/// <summary> /// Handles simple (safe) changes. /// </summary> private void ProcessSimpleChange(TextChangeContext context) { bool elementsRemoved = false; try { _editorTree.AcquireWriteLock(); elementsRemoved = DeleteAndShiftElements(context); UpdateTreeTextSnapshot(); // If no elements were invalidated and full parse is not required, clear pending changes if (!elementsRemoved) { ClearChanges(); } } finally { _editorTree.ReleaseWriteLock(); } if (!elementsRemoved) { if (context.ChangedNode != null || context.PendingChanges.TextChangeType == TextChangeType.Trivial) { _editorTree.FireOnPositionsOnlyChanged(); } _editorTree.FireOnUpdateCompleted(TreeUpdateType.PositionsOnly); } else { _editorTree.FireOnUpdateCompleted(TreeUpdateType.NodesRemoved); } DebugTree.VerifyTree(_editorTree); Debug.Assert(_editorTree.AstRoot.Children.Count > 0); }
/// <summary> /// Applies queued changes to the tree. Must only be called in a main thread context. /// </summary> /// <param name="o"></param> internal void ApplyBackgroundProcessingResults() { if (_ownerThreadId != Thread.CurrentThread.ManagedThreadId) { throw new ThreadStateException("Method should only be called on the main thread"); } if (_disposed) { return; } EditorTreeChangeCollection treeChanges; var eventsToFire = new List <TreeChangeEventRecord>(); bool changed = false; bool fullParse = false; bool staleChanges = false; while (_backgroundParsingResults.TryDequeue(out treeChanges)) { // If no changes are pending, then main thread already processes // everything in EnsureProcessingComplete call. Changes are pending // until they are applied to the tree. If queue is not empty // it either contains stale changes or main thread had to handle // changes in sync per request from, say, intellisense or formatting. if (ChangesPending) { // Check if background processing result matches current text buffer snapshot version staleChanges = (TextBuffer != null && treeChanges.SnapshotVersion < TextBuffer.CurrentSnapshot.Version.VersionNumber); if (!staleChanges) { // We can't fire events when appying changes since listeners may // attempt to access tree which is not fully updated and/or may // try to acquire read lock and hang since ApplyTreeChanges // hols write lock. eventsToFire = ApplyTreeChanges(treeChanges); fullParse = _pendingChanges.FullParseRequired; // Queue must be empty by now since only most recent changes are not stale // Added local variable as I hit this assert, but _backgroundParsingResults.Count was zero // by the time I broke into the debugger. If this hits again, we may need to // think through this code and whether we need to be protecting against concurrent access. int count = _backgroundParsingResults.Count; Debug.Assert(count == 0); // Clear pending changes as we are done ClearChanges(); changed = true; // No need for further processing as queue must be empty break; } } } if (!staleChanges) { // Now that tree is fully updated, fire events if (_editorTree != null) { if (changed) { _editorTree.FirePostUpdateEvents(eventsToFire, fullParse); DebugTree.VerifyTree(_editorTree); } if (!ChangesPending) { Debug.Assert(_editorTree.AstRoot.Children.Count > 0); } } } }