public ProcessChange ( |
||
textChange | ||
treeChanges | Microsoft.R.Editor.Tree.EditorTreeChangeCollection | Collection of tree changes to apply /// from the main thread |
return | void |
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, _editorTree.ExpressionTermFilter);
return;
}
AstRoot newTree = RParser.Parse(newSnapshot, _editorTree.ExpressionTermFilter);
treeChanges.ChangeQueue.Enqueue(new EditorTreeChange_NewTree(newTree));
}
/// <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(); } } } }