/// <summary> /// Processes a single text change incrementally. Enqueues resulting /// tree changes in the supplied queue. Does not modify the tree. /// Changes are to be sent to the main thread and applied from there. /// Caller is responsible for the tree read lock acquisition. /// </summary> /// <param name="start">Start position of the change</param> /// <param name="oldLength">Length of the original text (0 if insertion)</param> /// <param name="newLength">Length of the new text (0 if deletion)</param> /// <param name="oldSnapshot">Text snapshot before the change</param> /// <param name="newSnapshot">Text snapshot after the change</param> /// <param name="treeChanges">Collection of tree changes to apply /// from the main thread</param> public void ProcessChange(TextChange textChange, EditorTreeChangeCollection treeChanges) { IAstNode startNode = null, endNode = null; PositionType startPositionType = PositionType.Undefined; PositionType endPositionType = PositionType.Undefined; IAstNode commonParent = null; int start = textChange.OldRange.Start; int oldLength = textChange.OldRange.Length; int newLength = textChange.NewRange.Length; int offset = newLength - oldLength; ITextProvider oldSnapshot = textChange.OldTextProvider; ITextProvider newSnapshot = textChange.NewTextProvider; // Find position type and the enclosing element node. Note that element // positions have been adjusted already (it happens immediately in OnTextChange) // so we should be looking at the new range even that tree hasn't // been fully updated yet. For example,if we delete a node, subsequent // elements were already shifted up and damaged nodes have been removed // so current node positions reflect text buffer state after the change. _astRoot.GetElementsEnclosingRange(start, newLength, out startNode, out startPositionType, out endNode, out endPositionType); if (startNode is AstRoot) { commonParent = _astRoot; } else if (startNode == endNode) { if (startPositionType == PositionType.Token) { // Change in comment or string content. commonParent = OnTokenNodeChange(startNode as TokenNode, start, oldLength, newLength); } } else { //if (commonParent == null) //{ // // Find parent that still has well formed curly braces. // commonParent = FindWellFormedOuterScope(startNode); //} if (commonParent == null) { commonParent = _astRoot; } } if (IsCancellationRequested()) return; if (!(commonParent is AstRoot)) { Debug.Assert(commonParent is IScope); AstRoot subTree = RParser.Parse(newSnapshot, commonParent); return; } AstRoot newTree = RParser.Parse(newSnapshot); treeChanges.ChangeQueue.Enqueue(new EditorTreeChange_NewTree(newTree)); }
List <TreeChangeEventRecord> ApplyTreeChanges(EditorTreeChangeCollection changesToApply) { // Check editor tree reference since document could have been // closed before parsing was completed if (!_disposed && _editorTree != null) { if (TextBuffer != null) { _editorTree.TextSnapshot = TextBuffer.CurrentSnapshot; } return(_editorTree.ApplyChangesFromQueue(changesToApply.ChangeQueue)); } return(new List <TreeChangeEventRecord>()); }
/// <summary> /// Main asyncronous task body /// </summary> void ProcessTextChanges(TextChange changeToProcess, bool async, Func <bool> isCancelledCallback) { lock (_disposeLock) { if (_editorTree == null || _disposed || isCancelledCallback()) { return; } EditorTreeChangeCollection treeChanges = null; // Cache id since it can change if task is canceled long taskId = TaskId; try { AstRoot rootNode; // We only need read lock since changes will be applied // from the main thread if (async) { rootNode = _editorTree.AcquireReadLock(_treeUserId); } else { rootNode = _editorTree.GetAstRootUnsafe(); } treeChanges = new EditorTreeChangeCollection(changeToProcess.Version, changeToProcess.FullParseRequired); TextChangeProcessor changeProcessor = new TextChangeProcessor(_editorTree, rootNode, isCancelledCallback); bool fullParseRequired = changeToProcess.FullParseRequired; if (fullParseRequired) { changeProcessor.FullParse(treeChanges, changeToProcess.NewTextProvider); } else { changeProcessor.ProcessChange(changeToProcess, treeChanges); } } finally { if (async && _editorTree != null) { _editorTree.ReleaseReadLock(_treeUserId); } } // Lock should be released at this point since actual application // of tree changes is going to be happen from the main thread. if (!isCancelledCallback() && treeChanges != null) { // Queue results for the main thread application. This must be done before // signaling that the task is complete since if EnsureProcessingComplete // is waiting it will want to apply changes itself rather than wait for // the DispatchOnUIThread to go though and hence it will need all changes // stored and ready for application. _backgroundParsingResults.Enqueue(treeChanges); } // Signal task complete now so if main thread is waiting // it can proceed and appy the changes immediately. SignalTaskComplete(taskId); if (_backgroundParsingResults.Count > 0) { _uiThreadTransitionRequestTime = DateTime.UtcNow; // It is OK to post results while main thread might be working // on them since if if it does, by the time posted request comes // queue will already be empty. if (async) { // Post request to apply tree changes to the main thread. // This must NOT block or else task will never enter 'RanToCompletion' state. EditorShell.DispatchOnUIThread(() => ApplyBackgroundProcessingResults()); } else { // When processing is synchronous, apply changes and fire events right away. ApplyBackgroundProcessingResults(); } } } }
/// <summary> /// Invokes full parse pass. Called from a background tree updating task. /// </summary> public void FullParse(EditorTreeChangeCollection changes, ITextProvider newSnapshot) { AstRoot newTree = RParser.Parse(newSnapshot); changes.ChangeQueue.Enqueue(new EditorTreeChange_NewTree(newTree)); }
List<TreeChangeEventRecord> ApplyTreeChanges(EditorTreeChangeCollection changesToApply) { // Check editor tree reference since document could have been // closed before parsing was completed if (!_disposed && _editorTree != null) { if (TextBuffer != null) _editorTree.TextSnapshot = TextBuffer.CurrentSnapshot; return _editorTree.ApplyChangesFromQueue(changesToApply.ChangeQueue); } return new List<TreeChangeEventRecord>(); }
/// <summary> /// Main asyncronous task body /// </summary> void ProcessTextChanges(TextChange changeToProcess, bool async, Func<bool> isCancelledCallback) { lock (_disposeLock) { if (_editorTree == null || _disposed || isCancelledCallback()) return; EditorTreeChangeCollection treeChanges = null; // Cache id since it can change if task is canceled long taskId = TaskId; try { AstRoot rootNode; // We only need read lock since changes will be applied // from the main thread if (async) { rootNode = _editorTree.AcquireReadLock(_treeUserId); } else { rootNode = _editorTree.GetAstRootUnsafe(); } treeChanges = new EditorTreeChangeCollection(changeToProcess.Version, changeToProcess.FullParseRequired); TextChangeProcessor changeProcessor = new TextChangeProcessor(_editorTree, rootNode, isCancelledCallback); bool fullParseRequired = changeToProcess.FullParseRequired; if (fullParseRequired) { changeProcessor.FullParse(treeChanges, changeToProcess.NewTextProvider); } else { changeProcessor.ProcessChange(changeToProcess, treeChanges); } } finally { if (async && _editorTree != null) _editorTree.ReleaseReadLock(_treeUserId); } // Lock should be released at this point since actual application // of tree changes is going to be happen from the main thread. if (!isCancelledCallback() && treeChanges != null) { // Queue results for the main thread application. This must be done before // signaling that the task is complete since if EnsureProcessingComplete // is waiting it will want to apply changes itself rather than wait for // the DispatchOnUIThread to go though and hence it will need all changes // stored and ready for application. _backgroundParsingResults.Enqueue(treeChanges); } // Signal task complete now so if main thread is waiting // it can proceed and appy the changes immediately. SignalTaskComplete(taskId); if (_backgroundParsingResults.Count > 0) { _uiThreadTransitionRequestTime = DateTime.UtcNow; // It is OK to post results while main thread might be working // on them since if if it does, by the time posted request comes // queue will already be empty. if (async) { // Post request to apply tree changes to the main thread. // This must NOT block or else task will never enter 'RanToCompletion' state. EditorShell.DispatchOnUIThread(() => ApplyBackgroundProcessingResults()); } else { // When processing is synchronous, apply changes and fire events right away. ApplyBackgroundProcessingResults(); } } } }
/// <summary> /// Processes a single text change incrementally. Enqueues resulting /// tree changes in the supplied queue. Does not modify the tree. /// Changes are to be sent to the main thread and applied from there. /// Caller is responsible for the tree read lock acquisition. /// </summary> /// <param name="start">Start position of the change</param> /// <param name="oldLength">Length of the original text (0 if insertion)</param> /// <param name="newLength">Length of the new text (0 if deletion)</param> /// <param name="oldSnapshot">Text snapshot before the change</param> /// <param name="newSnapshot">Text snapshot after the change</param> /// <param name="treeChanges">Collection of tree changes to apply /// from the main thread</param> public void ProcessChange(TextChange textChange, EditorTreeChangeCollection treeChanges) { IAstNode startNode = null, endNode = null; PositionType startPositionType = PositionType.Undefined; PositionType endPositionType = PositionType.Undefined; IAstNode commonParent = null; int start = textChange.OldRange.Start; int oldLength = textChange.OldRange.Length; int newLength = textChange.NewRange.Length; int offset = newLength - oldLength; ITextProvider oldSnapshot = textChange.OldTextProvider; ITextProvider newSnapshot = textChange.NewTextProvider; // Find position type and the enclosing element node. Note that element // positions have been adjusted already (it happens immediately in OnTextChange) // so we should be looking at the new range even that tree hasn't // been fully updated yet. For example,if we delete a node, subsequent // elements were already shifted up and damaged nodes have been removed // so current node positions reflect text buffer state after the change. _astRoot.GetElementsEnclosingRange(start, newLength, out startNode, out startPositionType, out endNode, out endPositionType); if (startNode is AstRoot) { commonParent = _astRoot; } else if (startNode == endNode) { if (startPositionType == PositionType.Token) { // Change in comment or string content. commonParent = OnTokenNodeChange(startNode as TokenNode, start, oldLength, newLength); } } else { //if (commonParent == null) //{ // // Find parent that still has well formed curly braces. // commonParent = FindWellFormedOuterScope(startNode); //} if (commonParent == null) { commonParent = _astRoot; } } if (IsCancellationRequested()) { return; } if (!(commonParent is AstRoot)) { Debug.Assert(commonParent is IScope); AstRoot subTree = RParser.Parse(newSnapshot, commonParent); return; } AstRoot newTree = RParser.Parse(newSnapshot); treeChanges.ChangeQueue.Enqueue(new EditorTreeChange_NewTree(newTree)); }
/// <summary> /// Main asyncronous task body /// </summary> private void ProcessTextChange(IEditorBufferSnapshot snapshot, bool async, Func <bool> isCancelledCallback) { lock (_disposeLock) { if (_editorTree == null || _disposed || isCancelledCallback()) { return; } EditorTreeChangeCollection treeChanges; // Cache id since it can change if task is canceled var taskId = TaskId; try { // We only need read lock since changes will be applied from the main thread if (async) { _editorTree.AcquireReadLock(_treeUserId); } else { _editorTree.GetAstRootUnsafe(); } treeChanges = new EditorTreeChangeCollection(snapshot.Version, true); var newTree = RParser.Parse(snapshot, _editorTree.ExpressionTermFilter); treeChanges.Append(new EditorTreeChange_NewTree(newTree)); } finally { if (async) { _editorTree?.ReleaseReadLock(_treeUserId); } } // Lock should be released at this point since actual application // of tree changes is going to be happen from the main thread. if (!isCancelledCallback() && treeChanges.Changes.Any()) { // Queue results for the main thread application. This must be done before // signaling that the task is complete since if EnsureProcessingComplete // is waiting it will want to apply changes itself rather than wait for // the DispatchOnUIThread to go though and hence it will need all changes // stored and ready for application. _backgroundParsingResults.Enqueue(treeChanges); } // Signal task complete now so if main thread is waiting // it can proceed and appy the changes immediately. SignalTaskComplete(taskId); if (_backgroundParsingResults.Count > 0) { // It is OK to post results while main thread might be working // on them since if if it does, by the time posted request comes // queue will already be empty. if (async) { // Post request to apply tree changes to the main thread. // This must NOT block or else task will never enter 'RanToCompletion' state. _services.MainThread().Post(ApplyBackgroundProcessingResults); } else { // When processing is synchronous, apply changes and fire events right away. ApplyBackgroundProcessingResults(); } } } }