/// <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);
}