예제 #1
0
        /// <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));
        }
예제 #2
0
        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>());
        }
예제 #3
0
        /// <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();
                    }
                }
            }
        }
예제 #4
0
 /// <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));
 }
예제 #5
0
        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>();
        }
예제 #6
0
        /// <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();
                    }
                }
            }
        }
예제 #7
0
        /// <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));
        }
예제 #8
0
        /// <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));
        }
예제 #9
0
        /// <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();
                    }
                }
            }
        }