diff --git a/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParser.cs b/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParser.cs
index 9a7cabb8e1..90f37f1273 100644
--- a/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParser.cs
+++ b/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParser.cs
@@ -30,7 +30,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
///
/// Fired on the main thread.
///
- public event EventHandler ResultsReady;
+ public event EventHandler ResultsReady;
public bool IsIdle
{
@@ -47,10 +47,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
_main.Cancel();
}
- public void QueueChange(SourceChange change, ITextSnapshot snapshot)
+ public ChangeReference QueueChange(SourceChange change, ITextSnapshot snapshot)
{
- var edit = new Edit(change, snapshot);
- _main.QueueChange(edit);
+ var changeReference = new ChangeReference(change, snapshot);
+ _main.QueueChange(changeReference);
+ return changeReference;
}
public void Dispose()
@@ -63,7 +64,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
return _main.Lock();
}
- protected virtual void OnResultsReady(DocumentStructureChangedEventArgs args)
+ protected virtual void OnResultsReady(BackgroundParserResultsReadyEventArgs args)
{
using (SynchronizeMainThreadState())
{
@@ -115,7 +116,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
private string _fileName;
private readonly object _stateLock = new object();
- private IList _changes = new List();
+ private IList _changes = new List();
public MainThreadState(string fileName)
{
@@ -124,7 +125,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
SetThreadId(Thread.CurrentThread.ManagedThreadId);
}
- public event EventHandler ResultsReady;
+ public event EventHandler ResultsReady;
public CancellationToken CancelToken
{
@@ -154,7 +155,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
return new DisposableAction(() => Monitor.Exit(_stateLock));
}
- public void QueueChange(Edit edit)
+ public void QueueChange(ChangeReference change)
{
// Any thread can queue a change.
@@ -166,7 +167,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
_currentParcelCancelSource.Cancel();
}
- _changes.Add(edit);
+ _changes.Add(change);
_hasParcel.Set();
}
}
@@ -182,12 +183,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
_currentParcelCancelSource = new CancellationTokenSource();
var changes = _changes;
- _changes = new List();
+ _changes = new List();
return new WorkParcel(changes, _currentParcelCancelSource.Token);
}
}
- public void ReturnParcel(DocumentStructureChangedEventArgs args)
+ public void ReturnParcel(BackgroundParserResultsReadyEventArgs args)
{
lock (_stateLock)
{
@@ -242,7 +243,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
private CancellationToken _shutdownToken;
private RazorProjectEngine _projectEngine;
private RazorSyntaxTree _currentSyntaxTree;
- private IList _previouslyDiscarded = new List();
+ private IList _previouslyDiscarded = new List();
public BackgroundThread(MainThreadState main, RazorProjectEngine projectEngine, string filePath, string projectDirectory)
{
@@ -274,30 +275,30 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
// Grab the parcel of work to do
var parcel = _main.GetParcel();
- if (parcel.Edits.Any())
+ if (parcel.Changes.Any())
{
try
{
- DocumentStructureChangedEventArgs args = null;
+ BackgroundParserResultsReadyEventArgs args = null;
using (var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken, parcel.CancelToken))
{
if (!linkedCancel.IsCancellationRequested)
{
// Collect ALL changes
- List allEdits;
+ List allChanges;
if (_previouslyDiscarded != null)
{
- allEdits = Enumerable.Concat(_previouslyDiscarded, parcel.Edits).ToList();
+ allChanges = Enumerable.Concat(_previouslyDiscarded, parcel.Changes).ToList();
}
else
{
- allEdits = parcel.Edits.ToList();
+ allChanges = parcel.Changes.ToList();
}
- var finalEdit = allEdits.Last();
+ var finalChange = allChanges.Last();
- var results = ParseChange(finalEdit.Snapshot, linkedCancel.Token);
+ var results = ParseChange(finalChange.Snapshot, linkedCancel.Token);
if (results != null && !linkedCancel.IsCancellationRequested)
{
@@ -307,15 +308,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
_currentSyntaxTree = results.GetSyntaxTree();
// Build Arguments
- args = new DocumentStructureChangedEventArgs(
- finalEdit.Change,
- finalEdit.Snapshot,
- results);
+ args = new BackgroundParserResultsReadyEventArgs(finalChange, results);
}
else
{
// Parse completed but we were cancelled in the mean time. Add these to the discarded changes set
- _previouslyDiscarded = allEdits;
+ _previouslyDiscarded = allChanges;
}
}
}
@@ -378,20 +376,20 @@ namespace Microsoft.VisualStudio.Editor.Razor
private class WorkParcel
{
- public WorkParcel(IList changes, CancellationToken cancelToken)
+ public WorkParcel(IList changes, CancellationToken cancelToken)
{
- Edits = changes;
+ Changes = changes;
CancelToken = cancelToken;
}
public CancellationToken CancelToken { get; }
- public IList Edits { get; }
+ public IList Changes { get; }
}
- private class Edit
+ internal class ChangeReference
{
- public Edit(SourceChange change, ITextSnapshot snapshot)
+ public ChangeReference(SourceChange change, ITextSnapshot snapshot)
{
Change = change;
Snapshot = snapshot;
@@ -399,7 +397,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
public SourceChange Change { get; }
- public ITextSnapshot Snapshot { get; set; }
+ public ITextSnapshot Snapshot { get; }
}
}
}
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParserResultsReadyEventArgs.cs b/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParserResultsReadyEventArgs.cs
new file mode 100644
index 0000000000..ecb9cef76e
--- /dev/null
+++ b/src/Microsoft.VisualStudio.Editor.Razor/BackgroundParserResultsReadyEventArgs.cs
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Razor.Language;
+using static Microsoft.VisualStudio.Editor.Razor.BackgroundParser;
+
+namespace Microsoft.VisualStudio.Editor.Razor
+{
+ internal class BackgroundParserResultsReadyEventArgs : EventArgs
+ {
+ public BackgroundParserResultsReadyEventArgs(ChangeReference edit, RazorCodeDocument codeDocument)
+ {
+ ChangeReference = edit;
+ CodeDocument = codeDocument;
+ }
+
+ public ChangeReference ChangeReference { get; }
+
+ public RazorCodeDocument CodeDocument { get; }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs
index 0feda57bce..b34901659b 100644
--- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs
+++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioRazorParser.cs
@@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Editor;
using Microsoft.VisualStudio.Text;
+using static Microsoft.VisualStudio.Editor.Razor.BackgroundParser;
using ITextBuffer = Microsoft.VisualStudio.Text.ITextBuffer;
using Timer = System.Threading.Timer;
@@ -317,8 +318,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
_dispatcher.AssertForegroundThread();
- _latestChangeReference = new ChangeReference(change, snapshot);
- _parser.QueueChange(change, snapshot);
+ _latestChangeReference = _parser.QueueChange(change, snapshot);
}
private void OnNotifyForegroundIdle()
@@ -357,7 +357,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
}
- private void OnResultsReady(object sender, DocumentStructureChangedEventArgs args)
+ private void OnResultsReady(object sender, BackgroundParserResultsReadyEventArgs args)
{
_dispatcher.AssertBackgroundThread();
@@ -375,21 +375,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
return;
}
- var args = (DocumentStructureChangedEventArgs)state;
+ var backgroundParserArgs = (BackgroundParserResultsReadyEventArgs)state;
if (_latestChangeReference == null || // extra hardening
- !_latestChangeReference.IsAssociatedWith(args) ||
- args.Snapshot != TextBuffer.CurrentSnapshot)
+ _latestChangeReference != backgroundParserArgs.ChangeReference ||
+ backgroundParserArgs.ChangeReference.Snapshot != TextBuffer.CurrentSnapshot)
{
// In the middle of parsing a newer change or about to parse a newer change.
return;
}
_latestChangeReference = null;
- _codeDocument = args.CodeDocument;
- _snapshot = args.Snapshot;
+ _codeDocument = backgroundParserArgs.CodeDocument;
+ _snapshot = backgroundParserArgs.ChangeReference.Snapshot;
_partialParser = new RazorSyntaxTreePartialParser(CodeDocument.GetSyntaxTree());
- DocumentStructureChanged?.Invoke(this, args);
+ var documentStructureChangedArgs = new DocumentStructureChangedEventArgs(
+ backgroundParserArgs.ChangeReference.Change,
+ backgroundParserArgs.ChangeReference.Snapshot,
+ backgroundParserArgs.CodeDocument);
+ DocumentStructureChanged?.Invoke(this, documentStructureChangedArgs);
}
private void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
@@ -432,25 +436,5 @@ namespace Microsoft.VisualStudio.Editor.Razor
return _tagHelpers;
}
}
-
- // Internal for testing
- internal class ChangeReference
- {
- public ChangeReference(SourceChange change, ITextSnapshot snapshot)
- {
- Change = change;
- Snapshot = snapshot;
- }
-
- public SourceChange Change { get; }
-
- public ITextSnapshot Snapshot { get; }
-
- public bool IsAssociatedWith(DocumentStructureChangedEventArgs other)
- {
- return Change == other.SourceChange &&
- Snapshot == other.Snapshot;
- }
- }
}
}
diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserTest.cs
index d3ec3b9089..3df48c553f 100644
--- a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserTest.cs
+++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioRazorParserTest.cs
@@ -108,10 +108,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
var called = false;
parser.DocumentStructureChanged += (sender, e) => called = true;
- parser._latestChangeReference = new DefaultVisualStudioRazorParser.ChangeReference(null, new StringTextSnapshot(string.Empty));
- var args = new DocumentStructureChangedEventArgs(
- new SourceChange(0, 0, string.Empty),
- new StringTextSnapshot(string.Empty),
+ 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
@@ -138,12 +137,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
parser.DocumentStructureChanged += (sender, e) => called = true;
var latestChange = new SourceChange(0, 0, string.Empty);
var latestSnapshot = documentTracker.TextBuffer.CurrentSnapshot;
- parser._latestChangeReference = new DefaultVisualStudioRazorParser.ChangeReference(latestChange, latestSnapshot);
+ parser._latestChangeReference = new BackgroundParser.ChangeReference(latestChange, latestSnapshot);
var codeDocument = TestRazorCodeDocument.CreateEmpty();
codeDocument.SetSyntaxTree(RazorSyntaxTree.Parse(TestRazorSourceDocument.Create()));
- var args = new DocumentStructureChangedEventArgs(
- latestChange,
- latestSnapshot,
+ var args = new BackgroundParserResultsReadyEventArgs(
+ parser._latestChangeReference,
codeDocument);
// Act
@@ -154,6 +152,46 @@ namespace Microsoft.VisualStudio.Editor.Razor
}
}
+ [ForegroundFact]
+ public void OnDocumentStructureChanged_FiresForOnlyLatestTextBufferReparseEdit()
+ {
+ // Arrange
+ var documentTracker = CreateDocumentTracker();
+ using (var parser = new DefaultVisualStudioRazorParser(
+ Dispatcher,
+ documentTracker,
+ ProjectEngineFactory,
+ new DefaultErrorReporter(),
+ Mock.Of()))
+ {
+ 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);
+ }
+ }
+
[ForegroundFact]
public void StartIdleTimer_DoesNotRestartTimerWhenAlreadyRunning()
{