protected virtual void OnResultsReady(BackgroundParserResultsReadyEventArgs args) { using (SynchronizeMainThreadState()) { ResultsReady?.Invoke(this, args); } }
private void OnResultsReady(object sender, BackgroundParserResultsReadyEventArgs args) { _dispatcher.AssertBackgroundThread(); // Jump back to UI thread to notify structure changes. Task.Factory.StartNew(OnDocumentStructureChanged, args, CancellationToken.None, TaskCreationOptions.None, _dispatcher.ForegroundScheduler); }
public async Task GetLatestCodeDocumentAsync_NoPendingChangesReturnsImmediately() { // Arrange var documentTracker = CreateDocumentTracker(); using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var latestChange = new SourceChange(0, 0, string.Empty); var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot); var codeDocument = TestRazorCodeDocument.CreateEmpty(); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(TestRazorSourceDocument.Create())); var args = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); parser.OnDocumentStructureChanged(args); // Act - 1 var getLatestCodeDocumentTask = parser.GetLatestCodeDocumentAsync(); // Assert - 1 Assert.True(getLatestCodeDocumentTask.IsCompleted); // Act - 2 var latestCodeDocument = await getLatestCodeDocumentTask; // Assert - 2 Assert.Same(latestCodeDocument, codeDocument); } }
public void OnDocumentStructureChanged_FiresForLatestTextBufferEdit() { // Arrange var documentTracker = CreateDocumentTracker(); using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var called = false; parser.DocumentStructureChanged += (sender, e) => called = true; var latestChange = new SourceChange(0, 0, string.Empty); var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot); var codeDocument = TestRazorCodeDocument.CreateEmpty(); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(TestRazorSourceDocument.Create())); var args = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); // Act parser.OnDocumentStructureChanged(args); // Assert Assert.True(called); } }
public void GetLatestCodeDocumentAsync_MultipleCallsWithPendingChangesMemoizesReturnedTasks() { // Arrange var documentTracker = CreateDocumentTracker(); using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var latestChange = new SourceChange(0, 0, string.Empty); var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot); var codeDocument = TestRazorCodeDocument.CreateEmpty(); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(TestRazorSourceDocument.Create())); var args = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); // Act var getLatestCodeDocumentTask1 = parser.GetLatestCodeDocumentAsync(); var getLatestCodeDocumentTask2 = parser.GetLatestCodeDocumentAsync(); // Assert Assert.Same(getLatestCodeDocumentTask1, getLatestCodeDocumentTask2); } }
// Internal for testing internal void OnResultsReady(object sender, BackgroundParserResultsReadyEventArgs args) { _dispatcher.AssertBackgroundThread(); UpdateParserState(args.CodeDocument, args.ChangeReference.Snapshot); // Jump back to UI thread to notify structure changes. _ = Task.Factory.StartNew(OnDocumentStructureChanged, args, CancellationToken.None, TaskCreationOptions.None, _dispatcher.ForegroundScheduler); }
// Internal for testing #pragma warning disable VSTHRD100 // Avoid async void methods internal async void OnResultsReady(object sender, BackgroundParserResultsReadyEventArgs args) #pragma warning restore VSTHRD100 // Avoid async void methods { try { UpdateParserState(args.CodeDocument, args.ChangeReference.Snapshot); // Jump back to UI thread to notify structure changes. await _joinableTaskContext.Factory.SwitchToMainThreadAsync(); OnDocumentStructureChanged(args); } catch (Exception ex) { Debug.Fail("DefaultVisualStudioRazorParser.OnResultsReady threw exception:" + Environment.NewLine + ex.Message + Environment.NewLine + "Stack trace:" + Environment.NewLine + ex.StackTrace); } }
public void ReturnParcel(BackgroundParserResultsReadyEventArgs args) { lock (_stateLock) { // Clear the current parcel cancellation source if (_currentParcelCancelSource != null) { _currentParcelCancelSource.Dispose(); _currentParcelCancelSource = null; } // If there are things waiting to be parsed, just don't fire the event because we're already out of date if (_changes.Any()) { return; } } ResultsReady?.Invoke(this, args); }
public async Task GetLatestSyntaxTreeAsync_WaitsForParse() { // Arrange var documentTracker = CreateDocumentTracker(); using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var latestChange = new SourceChange(0, 0, string.Empty); var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var syntaxTree = RazorSyntaxTree.Parse(TestRazorSourceDocument.Create()); codeDocument.SetSyntaxTree(syntaxTree); var args = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); // Act - 1 var getLatestSyntaxTreeTask = parser.GetLatestSyntaxTreeAsync(StringTextSnapshot.Empty); // Assert - 1 Assert.False(getLatestSyntaxTreeTask.IsCompleted); // Act - 2 await Task.Run(() => parser.OnResultsReady(sender: null, args)); // Assert - 2 Assert.True(getLatestSyntaxTreeTask.IsCompleted); // Act - 3 var latestSyntaxTree = await getLatestSyntaxTreeTask; // Assert - 3 Assert.Same(latestSyntaxTree, syntaxTree); } }
public async Task GetLatestCodeDocumentAsync_ParserDisposed_ReturnsImmediately() { // Arrange var documentTracker = CreateDocumentTracker(); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var syntaxTree = RazorSyntaxTree.Parse(TestRazorSourceDocument.Create()); DefaultVisualStudioRazorParser parser; codeDocument.SetSyntaxTree(syntaxTree); using (parser = new DefaultVisualStudioRazorParser( JoinableTaskContext, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>(MockBehavior.Strict))) { var latestChange = new SourceChange(0, 0, string.Empty); var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot); var args = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); // Initialize the document with some content so we have a syntax tree to return. await Task.Run(() => parser.OnResultsReady(sender: null, args)); } var newerSnapshot = new StringTextSnapshot("Newer", versionNumber: 1337); // Act - 1 var getLatestCodeDocumentTask = parser.GetLatestCodeDocumentAsync(newerSnapshot); // Assert - 1 Assert.True(getLatestCodeDocumentTask.IsCompleted); // Act - 2 var latestCodeDocument = await getLatestCodeDocumentTask; // Assert - 2 Assert.Same(latestCodeDocument, codeDocument); }
public void OnDocumentStructureChanged_FiresForOnlyLatestTextBufferReparseEdit() { // Arrange var documentTracker = CreateDocumentTracker(); using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, documentTracker, ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var called = false; parser.DocumentStructureChanged += (sender, e) => called = true; var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot; parser._latestChangeReference = new BackgroundParser.ChangeReference(null, latestSnapshot); var codeDocument = TestRazorCodeDocument.CreateEmpty(); codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(TestRazorSourceDocument.Create())); var badArgs = new BackgroundParserResultsReadyEventArgs( // This is a different reparse edit, shouldn't be fired for this call new BackgroundParser.ChangeReference(null, latestSnapshot), codeDocument); var goodArgs = new BackgroundParserResultsReadyEventArgs( parser._latestChangeReference, codeDocument); // Act - 1 parser.OnDocumentStructureChanged(badArgs); // Assert - 1 Assert.False(called); // Act - 2 parser.OnDocumentStructureChanged(goodArgs); // Assert - 2 Assert.True(called); } }
public void OnDocumentStructureChanged_IgnoresEditsThatAreOld() { // Arrange using (var parser = new DefaultVisualStudioRazorParser( Dispatcher, CreateDocumentTracker(), ProjectEngineFactory, new DefaultErrorReporter(), Mock.Of <VisualStudioCompletionBroker>())) { var called = false; parser.DocumentStructureChanged += (sender, e) => called = true; parser._latestChangeReference = new BackgroundParser.ChangeReference(null, new StringTextSnapshot(string.Empty)); var args = new BackgroundParserResultsReadyEventArgs( new BackgroundParser.ChangeReference(new SourceChange(0, 0, string.Empty), new StringTextSnapshot(string.Empty)), TestRazorCodeDocument.CreateEmpty()); // Act parser.OnDocumentStructureChanged(args); // Assert Assert.False(called); } }
// **** BACKGROUND THREAD **** private void WorkerLoop() { try { EnsureOnThread(); while (!_shutdownToken.IsCancellationRequested) { // Grab the parcel of work to do var parcel = _main.GetParcel(); if (parcel.Changes.Any()) { try { BackgroundParserResultsReadyEventArgs args = null; using (var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken, parcel.CancelToken)) { if (!linkedCancel.IsCancellationRequested) { // Collect ALL changes List <ChangeReference> allChanges; if (_previouslyDiscarded != null) { allChanges = Enumerable.Concat(_previouslyDiscarded, parcel.Changes).ToList(); } else { allChanges = parcel.Changes.ToList(); } var finalChange = allChanges.Last(); var results = ParseChange(finalChange.Snapshot, linkedCancel.Token); if (results != null && !linkedCancel.IsCancellationRequested) { // Clear discarded changes list _previouslyDiscarded = null; _currentSyntaxTree = results.GetSyntaxTree(); // Build Arguments args = new BackgroundParserResultsReadyEventArgs(finalChange, results); } else { // Parse completed but we were cancelled in the mean time. Add these to the discarded changes set _previouslyDiscarded = allChanges; } } } if (args != null) { _main.ReturnParcel(args); } } catch (OperationCanceledException) { } } else { Thread.Yield(); } } } catch (OperationCanceledException) { // Do nothing. Just shut down. } finally { // Clean up main thread resources _main.Dispose(); } }