Example #1
0
        /// <summary>
        /// Determines if a change will cause a structural change to the document and if not, applies it to the existing tree.
        /// If a structural change would occur, automatically starts a reparse
        /// </summary>
        /// <remarks>
        /// NOTE: The initial incremental parsing check and actual incremental parsing (if possible) occurs
        /// on the callers thread.  However, if a full reparse is needed, this occurs on a background thread.
        /// </remarks>
        /// <param name="change">The change to apply to the parse tree</param>
        /// <returns>A PartialParseResult value indicating the result of the incremental parse</returns>
        public virtual PartialParseResult CheckForStructureChanges(TextChange change)
        {
            // Validate the change
            long?elapsedMs = null;

#if EDITOR_TRACING
            Stopwatch sw = new Stopwatch();
            sw.Start();
#endif
            RazorEditorTrace.TraceLine(RazorResources.Trace_EditorReceivedChange, Path.GetFileName(FileName), change);
            if (change.NewBuffer == null)
            {
                throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture,
                                                          RazorResources.Structure_Member_CannotBeNull,
                                                          "Buffer",
                                                          "TextChange"), "change");
            }

            PartialParseResult result = PartialParseResult.Rejected;

            // If there isn't already a parse underway, try partial-parsing
            string changeString = String.Empty;
            using (_parser.SynchronizeMainThreadState())
            {
                // Capture the string value of the change while we're synchronized
                changeString = change.ToString();

                // Check if we can partial-parse
                if (CurrentParseTree != null && _parser.IsIdle)
                {
                    result = TryPartialParse(change);
                }
            }

            // If partial parsing failed or there were outstanding parser tasks, start a full reparse
            if (result.HasFlag(PartialParseResult.Rejected))
            {
                _parser.QueueChange(change);
            }

            // Otherwise, remember if this was provisionally accepted for next partial parse
            LastResultProvisional = result.HasFlag(PartialParseResult.Provisional);
            VerifyFlagsAreValid(result);

#if EDITOR_TRACING
            sw.Stop();
            elapsedMs = sw.ElapsedMilliseconds;
            sw.Reset();
#endif
            RazorEditorTrace.TraceLine(RazorResources.Trace_EditorProcessedChange, Path.GetFileName(FileName), changeString, elapsedMs.HasValue ? elapsedMs.Value.ToString() : "?", result.ToString());
            return(result);
        }
Example #2
0
            public void QueueChange(TextChange change)
            {
#if EDITOR_TRACING
                RazorEditorTrace.TraceLine(RazorResources.Trace_QueuingParse, Path.GetFileName(_fileName), change);
#endif
                EnsureOnThread();
                lock (_stateLock)
                {
                    // CurrentParcel token source is not null ==> There's a parse underway
                    if (_currentParcelCancelSource != null)
                    {
                        _currentParcelCancelSource.Cancel();
                    }

                    _changes.Add(change);
                    _hasParcel.Set();
                }
            }
Example #3
0
            // **** BACKGROUND THREAD ****
            private void WorkerLoop()
            {
                long?  elapsedMs    = null;
                string fileNameOnly = Path.GetFileName(_fileName);

#if EDITOR_TRACING
                Stopwatch sw = new Stopwatch();
#endif

                try
                {
                    RazorEditorTrace.TraceLine(RazorResources.Trace_BackgroundThreadStart, fileNameOnly);
                    EnsureOnThread();
                    while (!_shutdownToken.IsCancellationRequested)
                    {
                        // Grab the parcel of work to do
                        WorkParcel parcel = _main.GetParcel();
                        if (parcel.Changes.Any())
                        {
                            RazorEditorTrace.TraceLine(RazorResources.Trace_ChangesArrived, fileNameOnly, parcel.Changes.Count);
                            try
                            {
                                DocumentParseCompleteEventArgs args = null;
                                using (var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken, parcel.CancelToken))
                                {
                                    if (parcel != null && !linkedCancel.IsCancellationRequested)
                                    {
                                        // Collect ALL changes
#if EDITOR_TRACING
                                        if (_previouslyDiscarded != null && _previouslyDiscarded.Any())
                                        {
                                            RazorEditorTrace.TraceLine(RazorResources.Trace_CollectedDiscardedChanges, fileNameOnly, _previouslyDiscarded.Count);
                                        }
#endif
                                        var allChanges = Enumerable.Concat(
                                            _previouslyDiscarded ?? Enumerable.Empty <TextChange>(), parcel.Changes).ToList();
                                        var finalChange = allChanges.LastOrDefault();
                                        if (finalChange != default(TextChange))
                                        {
#if EDITOR_TRACING
                                            sw.Start();
#endif
                                            GeneratorResults results = ParseChange(finalChange.NewBuffer, linkedCancel.Token);
#if EDITOR_TRACING
                                            sw.Stop();
                                            elapsedMs = sw.ElapsedMilliseconds;
                                            sw.Reset();
#endif
                                            RazorEditorTrace.TraceLine(
                                                RazorResources.Trace_ParseComplete,
                                                fileNameOnly,
                                                elapsedMs.HasValue ? elapsedMs.Value.ToString() : "?");

                                            if (results != null && !linkedCancel.IsCancellationRequested)
                                            {
                                                // Clear discarded changes list
                                                _previouslyDiscarded = null;

                                                // Take the current tree and check for differences
#if EDITOR_TRACING
                                                sw.Start();
#endif
                                                bool treeStructureChanged = _currentParseTree == null || TreesAreDifferent(_currentParseTree, results.Document, allChanges, parcel.CancelToken);
#if EDITOR_TRACING
                                                sw.Stop();
                                                elapsedMs = sw.ElapsedMilliseconds;
                                                sw.Reset();
#endif
                                                _currentParseTree = results.Document;
                                                RazorEditorTrace.TraceLine(RazorResources.Trace_TreesCompared,
                                                                           fileNameOnly,
                                                                           elapsedMs.HasValue ? elapsedMs.Value.ToString() : "?",
                                                                           treeStructureChanged);

                                                // Build Arguments
                                                args = new DocumentParseCompleteEventArgs()
                                                {
                                                    GeneratorResults     = results,
                                                    SourceChange         = finalChange,
                                                    TreeStructureChanged = treeStructureChanged
                                                };
                                            }
                                            else
                                            {
                                                // Parse completed but we were cancelled in the mean time. Add these to the discarded changes set
                                                RazorEditorTrace.TraceLine(RazorResources.Trace_ChangesDiscarded, fileNameOnly, allChanges.Count);
                                                _previouslyDiscarded = allChanges;
                                            }

#if CHECK_TREE
                                            if (args != null)
                                            {
                                                // Rewind the buffer and sanity check the line mappings
                                                finalChange.NewBuffer.Position = 0;
                                                int lineCount = finalChange.NewBuffer.ReadToEnd().Split(new string[] { Environment.NewLine, "\r", "\n" }, StringSplitOptions.None).Count();
                                                Debug.Assert(
                                                    !args.GeneratorResults.DesignTimeLineMappings.Any(pair => pair.Value.StartLine > lineCount),
                                                    "Found a design-time line mapping referring to a line outside the source file!");
                                                Debug.Assert(
                                                    !args.GeneratorResults.Document.Flatten().Any(span => span.Start.LineIndex > lineCount),
                                                    "Found a span with a line number outside the source file");
                                                Debug.Assert(
                                                    !args.GeneratorResults.Document.Flatten().Any(span => span.Start.AbsoluteIndex > parcel.NewBuffer.Length),
                                                    "Found a span with an absolute offset outside the source file");
                                            }
#endif
                                        }
                                    }
                                }
                                if (args != null)
                                {
                                    _main.ReturnParcel(args);
                                }
                            }
                            catch (OperationCanceledException)
                            {
                            }
                        }
                        else
                        {
                            RazorEditorTrace.TraceLine(RazorResources.Trace_NoChangesArrived, fileNameOnly, parcel.Changes.Count);
                            Thread.Yield();
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    // Do nothing. Just shut down.
                }
                catch (Exception ex)
                {
                    MonoDevelop.Core.LoggingService.LogError("Internal error in Razor parser", ex);
                    MonoDevelop.Core.LogReporting.LogReportingService.ReportUnhandledException(ex, false);
                }
                finally
                {
                    RazorEditorTrace.TraceLine(RazorResources.Trace_BackgroundThreadShutdown, fileNameOnly);

                    // Clean up main thread resources
                    _main.Dispose();
                }
            }