Remove ProjectSnapshotManagerDispatcher usage in Visual Studio layer (#10211)

Long ago, `ProjectSnapshotManagerDispatcher` was introduced as a
replacement for a lot of code in Razor that synchronized by running on
the UI thread. This abstraction was needed for the language server,
where there isn't a UI thread. However, there's a lot of code that was
refactored to use `ProjectSnapshotManagerDispatcher` that doesn't really
need it. After all the dispatcher is intended for scheduling updates to
the project snapshot manager. So, using it for services that don't
depend on the project snapshot manager is overuse.

This change removes usage of `ProjectSnapshotManagerDispatcher` in most
of the Visual Studio layer. It's still used by the
`ProjectWorkspaceStateGenerator`, but that requires a larger
refactoring. Most services that were using
`ProjectSnapshotManagerDispatcher` did so to control updates to shared
data structures, and made these free-threaded.
This commit is contained in:
Dustin Campbell 2024-04-04 12:29:25 -07:00 коммит произвёл GitHub
Родитель c46ec54964 dc981a6747
Коммит badd53cb7b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
35 изменённых файлов: 328 добавлений и 477 удалений

Просмотреть файл

@ -2,9 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
@ -17,7 +15,6 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents;
internal sealed class EditorDocument : IDisposable
{
private readonly IEditorDocumentManager _documentManager;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly JoinableTaskContext _joinableTaskContext;
private readonly IFileChangeTracker _fileTracker;
private readonly SnapshotChangeTracker _snapshotTracker;
@ -30,7 +27,6 @@ internal sealed class EditorDocument : IDisposable
public EditorDocument(
IEditorDocumentManager documentManager,
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
JoinableTaskContext joinableTaskContext,
string projectFilePath,
string documentFilePath,
@ -43,43 +39,7 @@ internal sealed class EditorDocument : IDisposable
EventHandler? opened,
EventHandler? closed)
{
if (documentManager is null)
{
throw new ArgumentNullException(nameof(documentManager));
}
if (projectSnapshotManagerDispatcher is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
}
if (joinableTaskContext is null)
{
throw new ArgumentNullException(nameof(joinableTaskContext));
}
if (projectFilePath is null)
{
throw new ArgumentNullException(nameof(projectFilePath));
}
if (documentFilePath is null)
{
throw new ArgumentNullException(nameof(documentFilePath));
}
if (textLoader is null)
{
throw new ArgumentNullException(nameof(textLoader));
}
if (fileTracker is null)
{
throw new ArgumentNullException(nameof(fileTracker));
}
_documentManager = documentManager;
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_joinableTaskContext = joinableTaskContext;
ProjectFilePath = projectFilePath;
DocumentFilePath = documentFilePath;
@ -97,8 +57,7 @@ internal sealed class EditorDocument : IDisposable
// Only one of these should be active at a time.
if (textBuffer is null)
{
_ = _projectSnapshotManagerDispatcher.RunAsync(
_fileTracker.StartListening, CancellationToken.None).ConfigureAwait(false);
_fileTracker.StartListening();
}
else
{
@ -131,8 +90,7 @@ internal sealed class EditorDocument : IDisposable
throw new ArgumentNullException(nameof(textBuffer));
}
_ = _projectSnapshotManagerDispatcher.RunAsync(
_fileTracker.StopListening, CancellationToken.None).ConfigureAwait(false);
_fileTracker.StopListening();
_snapshotTracker.StartTracking(textBuffer);
EditorTextBuffer = textBuffer;
@ -152,8 +110,7 @@ internal sealed class EditorDocument : IDisposable
EditorTextContainer = null;
EditorTextBuffer = null;
_ = _projectSnapshotManagerDispatcher.RunAsync(
_fileTracker.StartListening, CancellationToken.None);
_fileTracker.StartListening();
}
private void ChangeTracker_Changed(object sender, FileChangeEventArgs e)
@ -177,8 +134,7 @@ internal sealed class EditorDocument : IDisposable
{
_fileTracker.Changed -= ChangeTracker_Changed;
_ = _projectSnapshotManagerDispatcher.RunAsync(
_fileTracker.StopListening, CancellationToken.None);
_fileTracker.StopListening();
if (EditorTextBuffer is not null)
{

Просмотреть файл

@ -23,15 +23,12 @@ internal abstract class EditorDocumentManager : IEditorDocumentManager
protected readonly object Lock;
protected ProjectSnapshotManagerDispatcher Dispatcher { get; }
protected JoinableTaskContext JoinableTaskContext { get; }
protected EditorDocumentManager(
IFileChangeTrackerFactory fileChangeTrackerFactory,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext)
{
Dispatcher = dispatcher;
JoinableTaskContext = joinableTaskContext;
_fileChangeTrackerFactory = fileChangeTrackerFactory;
@ -101,7 +98,6 @@ internal abstract class EditorDocumentManager : IEditorDocumentManager
var textBuffer = GetTextBufferForOpenDocument(key.DocumentFilePath);
document = new EditorDocument(
this,
Dispatcher,
JoinableTaskContext,
projectFilePath,
key.DocumentFilePath,

Просмотреть файл

@ -26,8 +26,7 @@ internal sealed class VisualStudioEditorDocumentManager(
SVsServiceProvider serviceProvider,
IVsEditorAdaptersFactoryService editorAdaptersFactory,
IFileChangeTrackerFactory fileChangeTrackerFactory,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext) : EditorDocumentManager(fileChangeTrackerFactory, dispatcher, joinableTaskContext)
JoinableTaskContext joinableTaskContext) : EditorDocumentManager(fileChangeTrackerFactory, joinableTaskContext)
{
private readonly IServiceProvider _serviceProvider = serviceProvider;
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory = editorAdaptersFactory;

Просмотреть файл

@ -16,9 +16,10 @@ internal class VisualStudioFileChangeTracker : IFileChangeTracker, IVsFreeThread
private readonly IErrorReporter _errorReporter;
private readonly IVsAsyncFileChangeEx _fileChangeService;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly JoinableTaskContext _joinableTaskContext;
private readonly object _gate = new();
// Internal for testing
internal JoinableTask<uint>? _fileChangeAdviseTask;
internal JoinableTask? _fileChangeUnadviseTask;
@ -32,7 +33,6 @@ internal class VisualStudioFileChangeTracker : IFileChangeTracker, IVsFreeThread
string filePath,
IErrorReporter errorReporter,
IVsAsyncFileChangeEx fileChangeService,
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
JoinableTaskContext joinableTaskContext)
{
if (string.IsNullOrEmpty(filePath))
@ -43,85 +43,86 @@ internal class VisualStudioFileChangeTracker : IFileChangeTracker, IVsFreeThread
FilePath = filePath;
_errorReporter = errorReporter;
_fileChangeService = fileChangeService;
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_joinableTaskContext = joinableTaskContext;
}
public void StartListening()
{
_projectSnapshotManagerDispatcher.AssertRunningOnDispatcher();
if (_fileChangeAdviseTask is not null)
lock (_gate)
{
// Already listening
return;
if (_fileChangeAdviseTask is not null)
{
// Already listening
return;
}
if (_fileChangeUnadviseTask is not { IsCompleted: false } fileChangeUnadviseTaskToJoin)
{
fileChangeUnadviseTaskToJoin = null;
}
_fileChangeAdviseTask = _joinableTaskContext.Factory.RunAsync(async () =>
{
try
{
// If an unadvise operation is still processing, we don't start listening until it completes.
if (fileChangeUnadviseTaskToJoin is not null)
await fileChangeUnadviseTaskToJoin.JoinAsync().ConfigureAwait(true);
return await _fileChangeService.AdviseFileChangeAsync(FilePath, FileChangeFlags, this).ConfigureAwait(true);
}
catch (PathTooLongException)
{
// Don't report PathTooLongExceptions but don't fault either.
}
catch (Exception exception)
{
// Don't explode on actual exceptions, just report gracefully.
_errorReporter.ReportError(exception);
}
return VSConstants.VSCOOKIE_NIL;
});
}
if (_fileChangeUnadviseTask is not { IsCompleted: false } fileChangeUnadviseTaskToJoin)
{
fileChangeUnadviseTaskToJoin = null;
}
_fileChangeAdviseTask = _joinableTaskContext.Factory.RunAsync(async () =>
{
try
{
// If an unadvise operation is still processing, we don't start listening until it completes.
if (fileChangeUnadviseTaskToJoin is not null)
await fileChangeUnadviseTaskToJoin.JoinAsync().ConfigureAwait(true);
return await _fileChangeService.AdviseFileChangeAsync(FilePath, FileChangeFlags, this).ConfigureAwait(true);
}
catch (PathTooLongException)
{
// Don't report PathTooLongExceptions but don't fault either.
}
catch (Exception exception)
{
// Don't explode on actual exceptions, just report gracefully.
_errorReporter.ReportError(exception);
}
return VSConstants.VSCOOKIE_NIL;
});
}
public void StopListening()
{
_projectSnapshotManagerDispatcher.AssertRunningOnDispatcher();
if (_fileChangeAdviseTask is null || _fileChangeUnadviseTask?.IsCompleted == false)
lock (_gate)
{
// Already not listening or trying to stop listening
return;
}
_fileChangeUnadviseTask = _joinableTaskContext.Factory.RunAsync(async () =>
{
try
if (_fileChangeAdviseTask is null || _fileChangeUnadviseTask?.IsCompleted == false)
{
var fileChangeCookie = await _fileChangeAdviseTask;
// Already not listening or trying to stop listening
return;
}
if (fileChangeCookie == VSConstants.VSCOOKIE_NIL)
_fileChangeUnadviseTask = _joinableTaskContext.Factory.RunAsync(async () =>
{
try
{
// Wasn't able to listen for file change events. This typically happens when some sort of exception (i.e. access exceptions)
// is thrown when attempting to listen for file changes.
return;
}
var fileChangeCookie = await _fileChangeAdviseTask;
await _fileChangeService.UnadviseFileChangeAsync(fileChangeCookie).ConfigureAwait(true);
_fileChangeAdviseTask = null;
}
catch (PathTooLongException)
{
// Don't report PathTooLongExceptions but don't fault either.
}
catch (Exception exception)
{
// Don't explode on actual exceptions, just report gracefully.
_errorReporter.ReportError(exception);
}
});
if (fileChangeCookie == VSConstants.VSCOOKIE_NIL)
{
// Wasn't able to listen for file change events. This typically happens when some sort of exception (i.e. access exceptions)
// is thrown when attempting to listen for file changes.
return;
}
await _fileChangeService.UnadviseFileChangeAsync(fileChangeCookie).ConfigureAwait(true);
_fileChangeAdviseTask = null;
}
catch (PathTooLongException)
{
// Don't report PathTooLongExceptions but don't fault either.
}
catch (Exception exception)
{
// Don't explode on actual exceptions, just report gracefully.
_errorReporter.ReportError(exception);
}
});
}
}
public int FilesChanged(uint fileCount, string[] filePaths, uint[] fileChangeFlags)

Просмотреть файл

@ -13,7 +13,6 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents;
[Export(typeof(IFileChangeTrackerFactory))]
internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
{
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly JoinableTaskContext _joinableTaskContext;
private readonly IErrorReporter _errorReporter;
private readonly JoinableTask<IVsAsyncFileChangeEx> _getFileChangeServiceTask;
@ -22,10 +21,8 @@ internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
public VisualStudioFileChangeTrackerFactory(
[Import(typeof(SAsyncServiceProvider))] IAsyncServiceProvider serviceProvider,
JoinableTaskContext joinableTaskContext,
ProjectSnapshotManagerDispatcher dispatcher,
IErrorReporter errorReporter)
{
_dispatcher = dispatcher;
_joinableTaskContext = joinableTaskContext;
_errorReporter = errorReporter;
@ -43,6 +40,6 @@ internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
// TODO: Make IFileChangeTrackerFactory.Create(...) asynchronous to avoid blocking here.
var fileChangeService = _getFileChangeServiceTask.Join();
return new VisualStudioFileChangeTracker(filePath, _errorReporter, fileChangeService, _dispatcher, _joinableTaskContext);
return new VisualStudioFileChangeTracker(filePath, _errorReporter, fileChangeService, _joinableTaskContext);
}
}

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
@ -9,5 +10,5 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor;
internal interface IProjectWorkspaceStateGenerator
{
void Update(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken);
Task UpdateAsync(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken);
}

Просмотреть файл

@ -16,7 +16,6 @@ internal class ProjectSnapshotSynchronizationService(
CollaborationSession sessionContext,
IProjectSnapshotManagerProxy hostProjectManagerProxy,
IProjectSnapshotManager projectManager,
ProjectSnapshotManagerDispatcher dispatcher,
IErrorReporter errorReporter,
JoinableTaskFactory jtf) : ICollaborationService, IAsyncDisposable, System.IAsyncDisposable
{
@ -25,7 +24,6 @@ internal class ProjectSnapshotSynchronizationService(
private readonly IProjectSnapshotManagerProxy _hostProjectManagerProxy = hostProjectManagerProxy;
private readonly IProjectSnapshotManager _projectManager = projectManager;
private readonly IErrorReporter _errorReporter = errorReporter;
private readonly ProjectSnapshotManagerDispatcher _dispatcher = dispatcher;
public async Task InitializeAsync(CancellationToken cancellationToken)
{
@ -42,22 +40,27 @@ internal class ProjectSnapshotSynchronizationService(
{
_hostProjectManagerProxy.Changed -= HostProxyProjectManager_Changed;
await _dispatcher.Scheduler;
var projects = _projectManager.GetProjects();
foreach (var project in projects)
{
try
await _projectManager.UpdateAsync(
static (updater, state) =>
{
_projectManager.Update(
static (updater, key) => updater.ProjectRemoved(key),
state: project.Key);
}
catch (Exception ex)
{
_errorReporter.ReportError(ex, project);
}
}
var (projects, errorReporter) = state;
foreach (var project in projects)
{
try
{
updater.ProjectRemoved(project.Key);
}
catch (Exception ex)
{
errorReporter.ReportError(ex, project);
}
}
},
state: (projects, _errorReporter),
CancellationToken.None);
}
ValueTask System.IAsyncDisposable.DisposeAsync()
@ -68,15 +71,13 @@ internal class ProjectSnapshotSynchronizationService(
// Internal for testing
internal async ValueTask UpdateGuestProjectManagerAsync(ProjectChangeEventProxyArgs args)
{
await _dispatcher.Scheduler;
if (args.Kind == ProjectProxyChangeKind.ProjectAdded)
{
var guestPath = ResolveGuestPath(args.ProjectFilePath);
var guestIntermediateOutputPath = ResolveGuestPath(args.IntermediateOutputPath);
var hostProject = new HostProject(guestPath, guestIntermediateOutputPath, args.Newer!.Configuration, args.Newer.RootNamespace);
_projectManager.Update(
await _projectManager.UpdateAsync(
static (updater, state) =>
{
updater.ProjectAdded(state.hostProject);
@ -86,12 +87,13 @@ internal class ProjectSnapshotSynchronizationService(
updater.ProjectWorkspaceStateChanged(state.hostProject.Key, state.projectWorkspaceState);
}
},
state: (hostProject, projectWorkspaceState: args.Newer.ProjectWorkspaceState));
state: (hostProject, projectWorkspaceState: args.Newer.ProjectWorkspaceState),
CancellationToken.None);
}
else if (args.Kind == ProjectProxyChangeKind.ProjectRemoved)
{
var guestPath = ResolveGuestPath(args.ProjectFilePath);
_projectManager.Update(
await _projectManager.UpdateAsync(
static (updater, guestPath) =>
{
var projectKeys = updater.GetAllProjectKeys(guestPath);
@ -100,7 +102,8 @@ internal class ProjectSnapshotSynchronizationService(
updater.ProjectRemoved(projectKey);
}
},
state: guestPath);
state: guestPath,
CancellationToken.None);
}
else if (args.Kind == ProjectProxyChangeKind.ProjectChanged)
{
@ -109,15 +112,16 @@ internal class ProjectSnapshotSynchronizationService(
var guestPath = ResolveGuestPath(args.Newer.FilePath);
var guestIntermediateOutputPath = ResolveGuestPath(args.Newer.IntermediateOutputPath);
var hostProject = new HostProject(guestPath, guestIntermediateOutputPath, args.Newer.Configuration, args.Newer.RootNamespace);
_projectManager.Update(
await _projectManager.UpdateAsync(
static (updater, hostProject) => updater.ProjectConfigurationChanged(hostProject),
state: hostProject);
state: hostProject,
CancellationToken.None);
}
else if (args.Older.ProjectWorkspaceState != args.Newer.ProjectWorkspaceState ||
args.Older.ProjectWorkspaceState?.Equals(args.Newer.ProjectWorkspaceState) == false)
{
var guestPath = ResolveGuestPath(args.Newer.FilePath);
_projectManager.Update(
await _projectManager.UpdateAsync(
static (updater, state) =>
{
var projectKeys = updater.GetAllProjectKeys(state.guestPath);
@ -127,21 +131,20 @@ internal class ProjectSnapshotSynchronizationService(
updater.ProjectWorkspaceStateChanged(projectKey, state.projectWorkspaceState);
}
},
state: (guestPath, projectWorkspaceState: args.Newer.ProjectWorkspaceState));
state: (guestPath, projectWorkspaceState: args.Newer.ProjectWorkspaceState),
CancellationToken.None);
}
}
}
private async Task InitializeGuestProjectManagerAsync(IReadOnlyList<ProjectSnapshotHandleProxy> projectHandles)
{
await _dispatcher.Scheduler;
foreach (var projectHandle in projectHandles)
{
var guestPath = ResolveGuestPath(projectHandle.FilePath);
var guestIntermediateOutputPath = ResolveGuestPath(projectHandle.IntermediateOutputPath);
var hostProject = new HostProject(guestPath, guestIntermediateOutputPath, projectHandle.Configuration, projectHandle.RootNamespace);
_projectManager.Update(
await _projectManager.UpdateAsync(
static (updater, state) =>
{
updater.ProjectAdded(state.hostProject);
@ -151,7 +154,8 @@ internal class ProjectSnapshotSynchronizationService(
updater.ProjectWorkspaceStateChanged(state.hostProject.Key, state.projectWorkspaceState);
}
},
state: (hostProject, projectWorkspaceState: projectHandle.ProjectWorkspaceState));
state: (hostProject, projectWorkspaceState: projectHandle.ProjectWorkspaceState),
CancellationToken.None);
}
}

Просмотреть файл

@ -16,7 +16,6 @@ namespace Microsoft.VisualStudio.LiveShare.Razor.Guest;
[method: ImportingConstructor]
internal class ProjectSnapshotSynchronizationServiceFactory(
IProjectSnapshotManager projectManager,
ProjectSnapshotManagerDispatcher dispatcher,
IErrorReporter errorReporter,
JoinableTaskContext joinableTaskContext) : ICollaborationServiceFactory
{
@ -32,7 +31,6 @@ internal class ProjectSnapshotSynchronizationServiceFactory(
sessionContext,
projectSnapshotManagerProxy,
projectManager,
dispatcher,
errorReporter,
joinableTaskContext.Factory);

Просмотреть файл

@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.Threading;
@ -16,7 +15,6 @@ namespace Microsoft.VisualStudio.LiveShare.Razor.Host;
internal class ProjectSnapshotManagerProxy : IProjectSnapshotManagerProxy, ICollaborationService, IDisposable
{
private readonly CollaborationSession _session;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly IProjectSnapshotManager _projectSnapshotManager;
private readonly JoinableTaskFactory _jtf;
private readonly AsyncSemaphore _latestStateSemaphore;
@ -28,11 +26,9 @@ internal class ProjectSnapshotManagerProxy : IProjectSnapshotManagerProxy, IColl
public ProjectSnapshotManagerProxy(
CollaborationSession session,
IProjectSnapshotManager projectSnapshotManager,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskFactory jtf)
{
_session = session;
_dispatcher = dispatcher;
_projectSnapshotManager = projectSnapshotManager;
_jtf = jtf;
@ -110,8 +106,6 @@ internal class ProjectSnapshotManagerProxy : IProjectSnapshotManagerProxy, IColl
private void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
{
_dispatcher.AssertRunningOnDispatcher();
if (_disposed)
{
return;

Просмотреть файл

@ -4,7 +4,6 @@
using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LiveShare.Razor.Serialization;
using Microsoft.VisualStudio.Threading;
@ -20,7 +19,6 @@ namespace Microsoft.VisualStudio.LiveShare.Razor.Host;
[method: ImportingConstructor]
internal class ProjectSnapshotManagerProxyFactory(
IProjectSnapshotManager projectManager,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext) : ICollaborationServiceFactory
{
public Task<ICollaborationService> CreateServiceAsync(CollaborationSession session, CancellationToken cancellationToken)
@ -29,7 +27,7 @@ internal class ProjectSnapshotManagerProxyFactory(
serializer.Converters.RegisterRazorLiveShareConverters();
var service = new ProjectSnapshotManagerProxy(
session, projectManager, dispatcher, joinableTaskContext.Factory);
session, projectManager, joinableTaskContext.Factory);
return Task.FromResult<ICollaborationService>(service);
}
}

Просмотреть файл

@ -46,40 +46,43 @@ internal sealed class ProjectWorkspaceStateGenerator(
// Used in unit tests to ensure we can know when background work finishes.
public ManualResetEventSlim? NotifyBackgroundWorkCompleted { get; set; }
public void Update(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
public Task UpdateAsync(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
{
if (projectSnapshot is null)
{
throw new ArgumentNullException(nameof(projectSnapshot));
}
_dispatcher.AssertRunningOnDispatcher();
return _dispatcher.RunAsync(
() =>
{
if (_disposed)
{
return;
}
if (_disposed)
{
return;
}
if (Updates.TryGetValue(projectSnapshot.Key, out var updateItem) &&
!updateItem.Task.IsCompleted &&
!updateItem.Cts.IsCancellationRequested)
{
updateItem.Cts.Cancel();
}
if (Updates.TryGetValue(projectSnapshot.Key, out var updateItem) &&
!updateItem.Task.IsCompleted &&
!updateItem.Cts.IsCancellationRequested)
{
updateItem.Cts.Cancel();
}
if (updateItem?.Cts.IsCancellationRequested == false)
{
updateItem?.Cts.Dispose();
}
if (updateItem?.Cts.IsCancellationRequested == false)
{
updateItem?.Cts.Dispose();
}
var lcts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var updateTask = Task.Factory.StartNew(
() => UpdateWorkspaceStateAsync(workspaceProject, projectSnapshot, lcts.Token),
lcts.Token,
TaskCreationOptions.None,
TaskScheduler.Default).Unwrap();
updateItem = new UpdateItem(updateTask, lcts);
Updates[projectSnapshot.Key] = updateItem;
var lcts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var updateTask = Task.Factory.StartNew(
() => UpdateWorkspaceStateAsync(workspaceProject, projectSnapshot, lcts.Token),
lcts.Token,
TaskCreationOptions.None,
TaskScheduler.Default).Unwrap();
updateItem = new UpdateItem(updateTask, lcts);
Updates[projectSnapshot.Key] = updateItem;
},
cancellationToken);
}
public void Dispose()

Просмотреть файл

@ -7,7 +7,6 @@ using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.Editor.Razor;
@ -25,7 +24,6 @@ internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : IRazorStartupServ
private readonly IProjectSnapshotManager _projectManager;
private readonly IProjectWorkspaceStateGenerator _workspaceStateGenerator;
private readonly IWorkspaceProvider _workspaceProvider;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly JoinableTaskFactory _jtf;
private readonly JoinableTask _initializeTask;
@ -41,14 +39,12 @@ internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : IRazorStartupServ
IProjectSnapshotManager projectManager,
IProjectWorkspaceStateGenerator workspaceStateGenerator,
IWorkspaceProvider workspaceProvider,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext)
{
_serviceProvider = serviceProvider;
_projectManager = projectManager;
_workspaceStateGenerator = workspaceStateGenerator;
_workspaceProvider = workspaceProvider;
_dispatcher = dispatcher;
_jtf = joinableTaskContext.Factory;
_projectManager.Changed += ProjectManager_Changed;
@ -127,29 +123,26 @@ internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : IRazorStartupServ
return;
}
await _dispatcher.RunAsync(() =>
if (_projectManager is null)
{
if (_projectManager is null)
{
return;
}
return;
}
var projectKeys = _projectManager.GetAllProjectKeys(projectFilePath);
foreach (var projectKey in projectKeys)
var projectKeys = _projectManager.GetAllProjectKeys(projectFilePath);
foreach (var projectKey in projectKeys)
{
if (_projectManager.TryGetLoadedProject(projectKey, out var projectSnapshot))
{
if (_projectManager.TryGetLoadedProject(projectKey, out var projectSnapshot))
var workspace = _workspaceProvider.GetWorkspace();
var workspaceProject = workspace.CurrentSolution.Projects.FirstOrDefault(wp => ProjectKey.From(wp) == projectSnapshot.Key);
if (workspaceProject is not null)
{
var workspace = _workspaceProvider.GetWorkspace();
var workspaceProject = workspace.CurrentSolution.Projects.FirstOrDefault(wp => ProjectKey.From(wp) == projectSnapshot.Key);
if (workspaceProject is not null)
{
// Trigger a tag helper update by forcing the project manager to see the workspace Project
// from the current solution.
_workspaceStateGenerator.Update(workspaceProject, projectSnapshot, cancellationToken);
}
// Trigger a tag helper update by forcing the project manager to see the workspace Project
// from the current solution.
await _workspaceStateGenerator.UpdateAsync(workspaceProject, projectSnapshot, cancellationToken);
}
}
}, cancellationToken);
}
}
internal TestAccessor GetTestAccessor() => new(this);

Просмотреть файл

@ -28,7 +28,6 @@ internal partial class WorkspaceProjectStateChangeDetector : IRazorStartupServic
private readonly IProjectSnapshotManager _projectManager;
private readonly LanguageServerFeatureOptions _options;
private readonly Workspace _workspace;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly CancellationTokenSource _disposeTokenSource;
private readonly AsyncBatchingWorkQueue<(Project?, IProjectSnapshot)> _workQueue;
@ -40,13 +39,11 @@ internal partial class WorkspaceProjectStateChangeDetector : IRazorStartupServic
IProjectWorkspaceStateGenerator generator,
IProjectSnapshotManager projectManager,
LanguageServerFeatureOptions options,
IWorkspaceProvider workspaceProvider,
ProjectSnapshotManagerDispatcher dispatcher)
IWorkspaceProvider workspaceProvider)
{
_generator = generator;
_projectManager = projectManager;
_options = options;
_dispatcher = dispatcher;
_disposeTokenSource = new();
_workQueue = new AsyncBatchingWorkQueue<(Project?, IProjectSnapshot)>(
@ -82,14 +79,7 @@ internal partial class WorkspaceProjectStateChangeDetector : IRazorStartupServic
return;
}
await _dispatcher.RunAsync(
static state =>
{
var (generator, project, projectSnapshot, token) = state;
generator.Update(project, projectSnapshot, token);
},
state: (_generator, project, projectSnapshot, token),
token);
await _generator.UpdateAsync(project, projectSnapshot, token);
}
}

Просмотреть файл

@ -10,7 +10,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Core.Imaging;
@ -44,17 +43,14 @@ internal class RazorDirectiveAttributeCompletionSource : IAsyncCompletionSource
private readonly ICompletionBroker _completionBroker;
private readonly IVisualStudioDescriptionFactory _descriptionFactory;
private readonly JoinableTaskFactory _joinableTaskFactory;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
public RazorDirectiveAttributeCompletionSource(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
IVisualStudioRazorParser parser,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskFactory joinableTaskFactory)
{
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_completionFactsService = completionFactsService ?? throw new ArgumentNullException(nameof(completionFactsService));
_completionBroker = completionBroker;

Просмотреть файл

@ -23,13 +23,11 @@ namespace Microsoft.VisualStudio.LegacyEditor.Razor.Completion;
[ContentType(RazorConstants.LegacyCoreContentType)]
[method: ImportingConstructor]
internal sealed class RazorDirectiveAttributeCompletionSourceProvider(
ProjectSnapshotManagerDispatcher dispatcher,
IRazorCompletionFactsService completionFactsService,
ICompletionBroker completionBroker,
IVisualStudioDescriptionFactory descriptionFactory,
JoinableTaskContext joinableTaskContext) : IAsyncCompletionSourceProvider
{
private readonly ProjectSnapshotManagerDispatcher _dispatcher = dispatcher;
private readonly IRazorCompletionFactsService _completionFactsService = completionFactsService;
private readonly ICompletionBroker _completionBroker = completionBroker;
private readonly IVisualStudioDescriptionFactory _descriptionFactory = descriptionFactory;
@ -63,7 +61,6 @@ internal sealed class RazorDirectiveAttributeCompletionSourceProvider(
}
return new RazorDirectiveAttributeCompletionSource(
_dispatcher,
parser,
_completionFactsService,
_completionBroker,

Просмотреть файл

@ -2,14 +2,14 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.VisualStudio.Editor.Razor.Documents;
namespace Microsoft.VisualStudio.LegacyEditor.Razor;
internal class ImportChangedEventArgs(string filePath, FileChangeKind kind, IEnumerable<string> associatedDocuments) : EventArgs
internal class ImportChangedEventArgs(string filePath, FileChangeKind kind, ImmutableArray<string> associatedDocuments) : EventArgs
{
public string FilePath => filePath;
public FileChangeKind Kind => kind;
public IEnumerable<string> AssociatedDocuments => associatedDocuments;
public ImmutableArray<string> AssociatedDocuments => associatedDocuments;
}

Просмотреть файл

@ -3,11 +3,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.Editor.Razor.Documents;
@ -15,12 +15,11 @@ namespace Microsoft.VisualStudio.LegacyEditor.Razor;
[Export(typeof(IImportDocumentManager))]
[method: ImportingConstructor]
internal sealed class ImportDocumentManager(
ProjectSnapshotManagerDispatcher dispatcher,
IFileChangeTrackerFactory fileChangeTrackerFactory) : IImportDocumentManager
internal sealed class ImportDocumentManager(IFileChangeTrackerFactory fileChangeTrackerFactory) : IImportDocumentManager
{
private readonly IFileChangeTrackerFactory _fileChangeTrackerFactory = fileChangeTrackerFactory;
private readonly ProjectSnapshotManagerDispatcher _dispatcher = dispatcher;
private readonly object _gate = new();
private readonly Dictionary<string, ImportTracker> _importTrackerCache = new(StringComparer.OrdinalIgnoreCase);
public event EventHandler<ImportChangedEventArgs>? Changed;
@ -32,8 +31,6 @@ internal sealed class ImportDocumentManager(
throw new ArgumentNullException(nameof(documentTracker));
}
_dispatcher.AssertRunningOnDispatcher();
var filePath = documentTracker.FilePath;
var projectSnapshot = documentTracker.ProjectSnapshot.AssumeNotNull();
@ -48,18 +45,21 @@ internal sealed class ImportDocumentManager(
continue;
}
if (!_importTrackerCache.TryGetValue(importFilePath, out var importTracker))
lock (_gate)
{
// First time seeing this import. Start tracking it.
var fileChangeTracker = _fileChangeTrackerFactory.Create(importFilePath);
importTracker = new ImportTracker(fileChangeTracker);
_importTrackerCache[importFilePath] = importTracker;
if (!_importTrackerCache.TryGetValue(importFilePath, out var importTracker))
{
// First time seeing this import. Start tracking it.
var fileChangeTracker = _fileChangeTrackerFactory.Create(importFilePath);
importTracker = new ImportTracker(fileChangeTracker);
_importTrackerCache[importFilePath] = importTracker;
fileChangeTracker.Changed += FileChangeTracker_Changed;
fileChangeTracker.StartListening();
fileChangeTracker.Changed += FileChangeTracker_Changed;
fileChangeTracker.StartListening();
}
importTracker.AddAssociatedDocument(documentTracker.FilePath);
}
importTracker.AssociatedDocuments.Add(documentTracker.FilePath);
}
}
@ -70,8 +70,6 @@ internal sealed class ImportDocumentManager(
throw new ArgumentNullException(nameof(documentTracker));
}
_dispatcher.AssertRunningOnDispatcher();
var filePath = documentTracker.FilePath;
var projectSnapshot = documentTracker.ProjectSnapshot.AssumeNotNull();
@ -79,15 +77,18 @@ internal sealed class ImportDocumentManager(
{
var importPhysicalPath = import.PhysicalPath.AssumeNotNull();
if (_importTrackerCache.TryGetValue(importPhysicalPath, out var importTracker))
lock (_gate)
{
importTracker.AssociatedDocuments.Remove(documentTracker.FilePath);
if (importTracker.AssociatedDocuments.Count == 0)
if (_importTrackerCache.TryGetValue(importPhysicalPath, out var importTracker))
{
// There are no open documents that care about this import. We no longer need to track it.
importTracker.FileChangeTracker.StopListening();
_importTrackerCache.Remove(importPhysicalPath);
importTracker.RemoveAssociatedDocument(documentTracker.FilePath);
if (importTracker.AssociatedDocumentCount == 0)
{
// There are no open documents that care about this import. We no longer need to track it.
importTracker.FileChangeTracker.StopListening();
_importTrackerCache.Remove(importPhysicalPath);
}
}
}
}
@ -120,18 +121,31 @@ internal sealed class ImportDocumentManager(
private void FileChangeTracker_Changed(object sender, FileChangeEventArgs args)
{
_dispatcher.AssertRunningOnDispatcher();
if (_importTrackerCache.TryGetValue(args.FilePath, out var importTracker))
lock (_gate)
{
Changed?.Invoke(this, new ImportChangedEventArgs(importTracker.FilePath, args.Kind, importTracker.AssociatedDocuments));
if (_importTrackerCache.TryGetValue(args.FilePath, out var importTracker))
{
Changed?.Invoke(this, new ImportChangedEventArgs(importTracker.FilePath, args.Kind, importTracker.GetAssociatedDocuments()));
}
}
}
private sealed class ImportTracker(IFileChangeTracker fileChangeTracker)
{
private readonly HashSet<string> _associatedDocuments = new(StringComparer.OrdinalIgnoreCase);
public IFileChangeTracker FileChangeTracker => fileChangeTracker;
public string FilePath => fileChangeTracker.FilePath;
public HashSet<string> AssociatedDocuments { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public int AssociatedDocumentCount => _associatedDocuments.Count;
public void AddAssociatedDocument(string filePath)
=> _associatedDocuments.Add(filePath);
public void RemoveAssociatedDocument(string filePath)
=> _associatedDocuments.Remove(filePath);
public ImmutableArray<string> GetAssociatedDocuments()
=> _associatedDocuments.ToImmutableArray();
}
}

Просмотреть файл

@ -4,9 +4,7 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Threading;
@ -17,10 +15,8 @@ namespace Microsoft.VisualStudio.LegacyEditor.Razor;
[method: ImportingConstructor]
internal sealed class RazorDocumentManager(
IRazorEditorFactoryService editorFactoryService,
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext) : IRazorDocumentManager
{
private readonly ProjectSnapshotManagerDispatcher _dispatcher = dispatcher;
private readonly JoinableTaskFactory _jtf = joinableTaskContext.Factory;
private readonly IRazorEditorFactoryService _editorFactoryService = editorFactoryService;
@ -46,9 +42,7 @@ internal sealed class RazorDocumentManager(
if (documentTracker.TextViews.Count == 1)
{
// tracker.Subscribe() accesses the project snapshot manager, which needs to be run on the
// project snapshot manager's specialized thread.
await _dispatcher.RunAsync(() => tracker.Subscribe(), CancellationToken.None).ConfigureAwait(false);
tracker.Subscribe();
}
}
}
@ -70,9 +64,7 @@ internal sealed class RazorDocumentManager(
if (documentTracker.TextViews.Count == 0)
{
// tracker.Unsubscribe() should be in sync with tracker.Subscribe(). The latter of needs to be run
// on the project snapshot manager's specialized thread, so we run both on it.
await _dispatcher.RunAsync(() => documentTracker.Unsubscribe(), CancellationToken.None).ConfigureAwait(false);
documentTracker.Unsubscribe();
}
}
}

Просмотреть файл

@ -5,10 +5,10 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Settings;
using Microsoft.VisualStudio.Editor.Razor;
@ -22,7 +22,6 @@ namespace Microsoft.VisualStudio.LegacyEditor.Razor;
internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
{
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly JoinableTaskContext _joinableTaskContext;
private readonly string _filePath;
private readonly string _projectPath;
@ -34,12 +33,12 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
private readonly IProjectEngineFactoryProvider _projectEngineFactoryProvider;
private bool _isSupportedProject;
private IProjectSnapshot? _projectSnapshot;
private int _subscribeCount;
public event EventHandler<ContextChangeEventArgs>? ContextChanged;
public VisualStudioDocumentTracker(
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext,
string filePath,
string projectPath,
@ -54,7 +53,6 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
throw new ArgumentException(SR.ArgumentCannotBeNullOrEmpty, nameof(filePath));
}
_dispatcher = dispatcher;
_joinableTaskContext = joinableTaskContext;
_filePath = filePath;
_projectPath = projectPath;
@ -143,9 +141,7 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
public void Subscribe()
{
_dispatcher.AssertRunningOnDispatcher();
if (_subscribeCount++ > 0)
if (Interlocked.Increment(ref _subscribeCount) != 1)
{
return;
}
@ -164,8 +160,6 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
private IProjectSnapshot GetOrCreateProject(string projectPath)
{
_dispatcher.AssertRunningOnDispatcher();
var projectKeys = _projectManager.GetAllProjectKeys(projectPath);
if (projectKeys.Length == 0 ||
@ -179,9 +173,7 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
public void Unsubscribe()
{
_dispatcher.AssertRunningOnDispatcher();
if (_subscribeCount == 0 || _subscribeCount-- > 1)
if (Interlocked.Decrement(ref _subscribeCount) != 0)
{
return;
}
@ -214,8 +206,6 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
return;
}
_dispatcher.AssertRunningOnDispatcher();
if (_projectPath is not null &&
string.Equals(_projectPath, e.ProjectFilePath, StringComparison.OrdinalIgnoreCase))
{
@ -268,8 +258,6 @@ internal sealed class VisualStudioDocumentTracker : IVisualStudioDocumentTracker
// Internal for testing
internal void Import_Changed(object sender, ImportChangedEventArgs args)
{
_dispatcher.AssertRunningOnDispatcher();
foreach (var path in args.AssociatedDocuments)
{
if (string.Equals(_filePath, path, StringComparison.OrdinalIgnoreCase))

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LegacyEditor.Razor.Settings;
using Microsoft.VisualStudio.Text;
@ -16,7 +15,6 @@ namespace Microsoft.VisualStudio.LegacyEditor.Razor;
[Export(typeof(IVisualStudioDocumentTrackerFactory))]
[method: ImportingConstructor]
internal sealed class VisualStudioDocumentTrackerFactory(
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext,
IProjectSnapshotManager projectManager,
IWorkspaceEditorSettings workspaceEditorSettings,
@ -25,7 +23,6 @@ internal sealed class VisualStudioDocumentTrackerFactory(
IImportDocumentManager importDocumentManager,
IProjectEngineFactoryProvider projectEngineFactoryProvider) : IVisualStudioDocumentTrackerFactory
{
private readonly ProjectSnapshotManagerDispatcher _dispatcher = dispatcher;
private readonly JoinableTaskContext _joinableTaskContext = joinableTaskContext;
private readonly ITextDocumentFactoryService _textDocumentFactory = textDocumentFactory;
private readonly IProjectPathProvider _projectPathProvider = projectPathProvider;
@ -54,7 +51,7 @@ internal sealed class VisualStudioDocumentTrackerFactory(
var filePath = textDocument.FilePath;
var tracker = new VisualStudioDocumentTracker(
_dispatcher, _joinableTaskContext, filePath, projectPath, _projectManager, _workspaceEditorSettings, _projectEngineFactoryProvider, textBuffer, _importDocumentManager);
_joinableTaskContext, filePath, projectPath, _projectManager, _workspaceEditorSettings, _projectEngineFactoryProvider, textBuffer, _importDocumentManager);
return tracker;
}

Просмотреть файл

@ -192,8 +192,14 @@ public class EditorDocumentManagerListenerTest(ITestOutputHelper testOutput) : V
private EditorDocument GetEditorDocument(bool isOpen = false, IEditorDocumentManager? documentManager = null)
{
var fileChangeTracker = StrictMock.Of<IFileChangeTracker>(x =>
x.FilePath == s_hostDocument.FilePath);
var fileChangeTrackerMock = new StrictMock<IFileChangeTracker>();
fileChangeTrackerMock
.SetupGet(x => x.FilePath)
.Returns(s_hostDocument.FilePath);
fileChangeTrackerMock
.Setup(x => x.StartListening());
fileChangeTrackerMock
.Setup(x => x.StopListening());
var textBuffer = isOpen
? new TestTextBuffer(new StringTextSnapshot("Hello"))
@ -201,13 +207,12 @@ public class EditorDocumentManagerListenerTest(ITestOutputHelper testOutput) : V
return new EditorDocument(
documentManager ?? StrictMock.Of<IEditorDocumentManager>(),
Dispatcher,
JoinableTaskContext,
s_hostProject.FilePath,
s_hostDocument.FilePath,
s_hostProject.Key,
StrictMock.Of<TextLoader>(),
fileChangeTracker,
fileChangeTrackerMock.Object,
textBuffer,
changedOnDisk: null,
changedInEditor: null,

Просмотреть файл

@ -30,7 +30,7 @@ public class EditorDocumentManagerTest : VisualStudioTestBase
public EditorDocumentManagerTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_manager = new TestEditorDocumentManager(Dispatcher, JoinableTaskFactory.Context);
_manager = new TestEditorDocumentManager(JoinableTaskFactory.Context);
_projectKey1 = TestProjectData.SomeProject.Key;
_projectKey2 = TestProjectData.AnotherProject.Key;
_projectFile1 = TestProjectData.SomeProject.FilePath;
@ -179,9 +179,8 @@ public class EditorDocumentManagerTest : VisualStudioTestBase
}
private class TestEditorDocumentManager(
ProjectSnapshotManagerDispatcher dispatcher,
JoinableTaskContext joinableTaskContext)
: EditorDocumentManager(CreateFileChangeTrackerFactory(), dispatcher, joinableTaskContext)
: EditorDocumentManager(CreateFileChangeTrackerFactory(), joinableTaskContext)
{
public List<EditorDocument> Opened { get; } = new List<EditorDocument>();
@ -191,10 +190,19 @@ public class EditorDocumentManagerTest : VisualStudioTestBase
private static IFileChangeTrackerFactory CreateFileChangeTrackerFactory()
{
var mock = new Mock<IFileChangeTrackerFactory>(MockBehavior.Strict);
var mock = new StrictMock<IFileChangeTrackerFactory>();
mock.Setup(x => x.Create(It.IsAny<string>()))
.Returns((string filePath) => Mock.Of<IFileChangeTracker>(x => x.FilePath == filePath, MockBehavior.Strict));
.Returns((string filePath) =>
{
var mock = new StrictMock<IFileChangeTracker>();
mock.SetupGet(x => x.FilePath)
.Returns(filePath);
mock.Setup(x => x.StartListening());
mock.Setup(x => x.StopListening());
return mock.Object;
});
return mock.Object;
}

Просмотреть файл

@ -26,13 +26,23 @@ public class EditorDocumentTest : VisualStudioTestBase
public EditorDocumentTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_documentManager = new Mock<IEditorDocumentManager>(MockBehavior.Strict).Object;
Mock.Get(_documentManager).Setup(m => m.RemoveDocument(It.IsAny<EditorDocument>())).Verifiable();
_documentManager = StrictMock.Of<IEditorDocumentManager>();
Mock.Get(_documentManager)
.Setup(m => m.RemoveDocument(It.IsAny<EditorDocument>()))
.Verifiable();
_projectFilePath = TestProjectData.SomeProject.FilePath;
_projectKey = TestProjectData.SomeProject.Key;
_documentFilePath = TestProjectData.SomeProjectFile1.FilePath;
_textLoader = TextLoader.From(TextAndVersion.Create(SourceText.From("FILE"), VersionStamp.Default));
_fileChangeTracker = Mock.Of<IFileChangeTracker>(x => x.FilePath == _documentFilePath, MockBehavior.Strict);
var mock = new StrictMock<IFileChangeTracker>();
mock.SetupGet(x => x.FilePath)
.Returns(_documentFilePath);
mock.Setup(x => x.StartListening());
mock.Setup(x => x.StopListening());
_fileChangeTracker = mock.Object;
_textBuffer = new TestTextBuffer(new StringTextSnapshot("Hello"));
}
@ -43,7 +53,6 @@ public class EditorDocumentTest : VisualStudioTestBase
// Arrange & Act
using var document = new EditorDocument(
_documentManager,
Dispatcher,
JoinableTaskFactory.Context,
_projectFilePath,
_documentFilePath,
@ -68,7 +77,6 @@ public class EditorDocumentTest : VisualStudioTestBase
// Arrange & Act
using var document = new EditorDocument(
_documentManager,
Dispatcher,
JoinableTaskFactory.Context,
_projectFilePath,
_documentFilePath,

Просмотреть файл

@ -30,11 +30,10 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
TestProjectData.SomeProjectImportFile.FilePath,
ErrorReporter,
fileChangeService.Object,
Dispatcher,
JoinableTaskFactory.Context);
// Act
await RunOnDispatcherAsync(tracker.StartListening);
tracker.StartListening();
await tracker._fileChangeAdviseTask!;
@ -56,13 +55,12 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
TestProjectData.SomeProjectImportFile.FilePath,
ErrorReporter,
fileChangeService.Object,
Dispatcher,
JoinableTaskFactory.Context);
await RunOnDispatcherAsync(tracker.StartListening);
tracker.StartListening();
// Act
await RunOnDispatcherAsync(tracker.StartListening);
tracker.StartListening();
await tracker._fileChangeAdviseTask!;
@ -86,15 +84,14 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
TestProjectData.SomeProjectImportFile.FilePath,
ErrorReporter,
fileChangeService.Object,
Dispatcher,
JoinableTaskFactory.Context);
await RunOnDispatcherAsync(tracker.StartListening);
tracker.StartListening();
await tracker._fileChangeAdviseTask!;
// Act
await RunOnDispatcherAsync(tracker.StopListening);
tracker.StopListening();
await tracker._fileChangeUnadviseTask!;
@ -103,7 +100,7 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
}
[UIFact]
public async Task StopListening_NotListening_DoesNothing()
public void StopListening_NotListening_DoesNothing()
{
// Arrange
var fileChangeService = new StrictMock<IVsAsyncFileChangeEx>();
@ -114,11 +111,10 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
TestProjectData.SomeProjectImportFile.FilePath,
ErrorReporter,
fileChangeService.Object,
Dispatcher,
JoinableTaskFactory.Context);
// Act
await RunOnDispatcherAsync(tracker.StopListening);
tracker.StopListening();
// Assert
Assert.Null(tracker._fileChangeUnadviseTask);
@ -134,7 +130,7 @@ public class VisualStudioFileChangeTrackerTest(ITestOutputHelper testOutput) : V
// Arrange
var filePath = TestProjectData.SomeProjectImportFile.FilePath;
var fileChangeService = Mock.Of<IVsAsyncFileChangeEx>(MockBehavior.Strict);
var tracker = new VisualStudioFileChangeTracker(filePath, ErrorReporter, fileChangeService, Dispatcher, JoinableTaskFactory.Context);
var tracker = new VisualStudioFileChangeTracker(filePath, ErrorReporter, fileChangeService, JoinableTaskFactory.Context);
var called = false;
tracker.Changed += (sender, args) =>

Просмотреть файл

@ -54,7 +54,6 @@ public class ProjectSnapshotSynchronizationServiceTest : VisualStudioWorkspaceTe
_sessionContext,
hostProjectManagerProxyMock.Object,
_projectManager,
Dispatcher,
ErrorReporter,
JoinableTaskFactory);
@ -89,7 +88,6 @@ public class ProjectSnapshotSynchronizationServiceTest : VisualStudioWorkspaceTe
_sessionContext,
StrictMock.Of<IProjectSnapshotManagerProxy>(),
_projectManager,
Dispatcher,
ErrorReporter,
JoinableTaskFactory);
var args = new ProjectChangeEventProxyArgs(older: null, newHandle, ProjectProxyChangeKind.ProjectAdded);
@ -125,7 +123,6 @@ public class ProjectSnapshotSynchronizationServiceTest : VisualStudioWorkspaceTe
_sessionContext,
StrictMock.Of<IProjectSnapshotManagerProxy>(),
_projectManager,
Dispatcher,
ErrorReporter,
JoinableTaskFactory);
var hostProject = new HostProject("/guest/path/project.csproj", "/guest/path/obj", RazorConfiguration.Default, "project");
@ -166,7 +163,6 @@ public class ProjectSnapshotSynchronizationServiceTest : VisualStudioWorkspaceTe
_sessionContext,
StrictMock.Of<IProjectSnapshotManagerProxy>(),
_projectManager,
Dispatcher,
ErrorReporter,
JoinableTaskFactory);
var hostProject = new HostProject("/guest/path/project.csproj", "/guest/path/obj", RazorConfiguration.Default, "project");
@ -211,7 +207,6 @@ public class ProjectSnapshotSynchronizationServiceTest : VisualStudioWorkspaceTe
_sessionContext,
StrictMock.Of<IProjectSnapshotManagerProxy>(),
_projectManager,
Dispatcher,
ErrorReporter,
JoinableTaskFactory);
var hostProject = new HostProject("/guest/path/project.csproj", "/guest/path/obj", RazorConfiguration.Default, "project");

Просмотреть файл

@ -54,7 +54,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
using var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
// Act
@ -85,7 +84,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
using var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
var proxyAccessor = proxy.GetTestAccessor();
@ -134,7 +132,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
var proxyAccessor = proxy.GetTestAccessor();
@ -174,7 +171,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
using var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
// Act
@ -210,7 +206,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
using var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
// Act
@ -241,7 +236,6 @@ public class ProjectSnapshotManagerProxyTest(ITestOutputHelper testOutput) : Vis
using var proxy = new ProjectSnapshotManagerProxy(
new TestCollaborationSession(true),
projectManager,
Dispatcher,
JoinableTaskFactory);
// Act

Просмотреть файл

@ -2,11 +2,11 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis;
using System.Threading;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test;
@ -21,10 +21,12 @@ internal class TestProjectWorkspaceStateGenerator : IProjectWorkspaceStateGenera
public IReadOnlyList<TestUpdate> Updates => _updates;
public void Update(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
public Task UpdateAsync(Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
{
var update = new TestUpdate(workspaceProject, projectSnapshot, cancellationToken);
_updates.Add(update);
return Task.CompletedTask;
}
public void Clear()

Просмотреть файл

@ -124,7 +124,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
using var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
using var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
var workspaceChangedTask = detectorAccessor.ListenForWorkspaceChangesAsync(
@ -166,7 +166,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
using var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
using var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -207,7 +207,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -250,7 +250,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -284,7 +284,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -324,7 +324,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -360,7 +360,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
Workspace.TryApplyChanges(_solutionWithTwoProjects);
@ -392,7 +392,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
Workspace.TryApplyChanges(_solutionWithTwoProjects);
@ -424,7 +424,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
Workspace.TryApplyChanges(_solutionWithTwoProjects);
@ -456,7 +456,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
Workspace.TryApplyChanges(_solutionWithTwoProjects);
@ -505,7 +505,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>
@ -533,7 +533,7 @@ public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTest
// Arrange
var generator = new TestProjectWorkspaceStateGenerator();
var projectManager = CreateProjectSnapshotManager();
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, Dispatcher);
var detector = new WorkspaceProjectStateChangeDetector(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider);
var detectorAccessor = detector.GetTestAccessor();
await projectManager.UpdateAsync(updater =>

Просмотреть файл

@ -61,10 +61,7 @@ public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase
// Act
stateGenerator.Dispose();
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(_workspaceProject, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(_workspaceProject, _projectSnapshot, DisposalToken);
// Assert
Assert.Empty(stateGenerator.Updates);
@ -79,10 +76,7 @@ public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase
stateGenerator.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false);
// Act
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(_workspaceProject, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(_workspaceProject, _projectSnapshot, DisposalToken);
// Assert
var update = Assert.Single(stateGenerator.Updates);
@ -97,18 +91,12 @@ public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase
_projectManager, _tagHelperResolver, Dispatcher, ErrorReporter, NoOpTelemetryReporter.Instance);
stateGenerator.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false);
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(_workspaceProject, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(_workspaceProject, _projectSnapshot, DisposalToken);
var initialUpdate = stateGenerator.Updates.Single().Value;
// Act
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(_workspaceProject, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(_workspaceProject, _projectSnapshot, DisposalToken);
// Assert
Assert.True(initialUpdate.Cts.IsCancellationRequested);
@ -129,10 +117,7 @@ public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase
});
// Act
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(workspaceProject: null, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(workspaceProject: null, _projectSnapshot, DisposalToken);
// Jump off the UI thread so the background work can complete.
await Task.Run(() => stateGenerator.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));
@ -157,10 +142,7 @@ public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase
});
// Act
await RunOnDispatcherAsync(() =>
{
stateGenerator.Update(_workspaceProject, _projectSnapshot, DisposalToken);
});
await stateGenerator.UpdateAsync(_workspaceProject, _projectSnapshot, DisposalToken);
// Jump off the UI thread so the background work can complete.
await Task.Run(() => stateGenerator.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

Просмотреть файл

@ -96,7 +96,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
StrictMock.Of<IProjectWorkspaceStateGenerator>(),
_workspaceProvider,
Dispatcher,
JoinableTaskContext))
{
var testAccessor = trigger.GetTestAccessor();
@ -136,7 +135,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
StrictMock.Of<IProjectWorkspaceStateGenerator>(),
_workspaceProvider,
Dispatcher,
JoinableTaskContext);
var testAccessor = trigger.GetTestAccessor();
@ -178,7 +176,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
workspaceStateGenerator,
_workspaceProvider,
Dispatcher,
JoinableTaskContext);
var testAccessor = trigger.GetTestAccessor();
@ -226,7 +223,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
workspaceStateGenerator,
_workspaceProvider,
Dispatcher,
JoinableTaskContext);
var vsHierarchyMock = new StrictMock<IVsHierarchy>();
@ -278,7 +274,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
workspaceStateGenerator,
_workspaceProvider,
Dispatcher,
JoinableTaskContext);
var testAccessor = trigger.GetTestAccessor();
@ -315,7 +310,6 @@ public class VsSolutionUpdatesProjectSnapshotChangeTriggerTest : VisualStudioTes
projectManager,
workspaceStateGenerator,
_workspaceProvider,
Dispatcher,
JoinableTaskContext);
var testAccessor = trigger.GetTestAccessor();

Просмотреть файл

@ -44,7 +44,6 @@ public class RazorDirectiveAttributeCompletionSourceTest(ITestOutputHelper testO
var descriptionFactory = StrictMock.Of<IVisualStudioDescriptionFactory>(f =>
f.CreateClassifiedDescription(description) == expectedResult);
var source = new RazorDirectiveAttributeCompletionSource(
Dispatcher,
StrictMock.Of<IVisualStudioRazorParser>(),
StrictMock.Of<IRazorCompletionFactsService>(),
StrictMock.Of<ICompletionBroker>(),
@ -234,7 +233,6 @@ public class RazorDirectiveAttributeCompletionSourceTest(ITestOutputHelper testO
private RazorDirectiveAttributeCompletionSource CreateCompletionSource()
{
var source = new RazorDirectiveAttributeCompletionSource(
Dispatcher,
StrictMock.Of<IVisualStudioRazorParser>(),
StrictMock.Of<IRazorCompletionFactsService>(),
StrictMock.Of<ICompletionBroker>(),

Просмотреть файл

@ -76,17 +76,11 @@ public class ImportDocumentManagerIntegrationTest : VisualStudioTestBase
.Returns(StrictMock.Of<IFileChangeTracker>());
var called = false;
var manager = new ImportDocumentManager(Dispatcher, fileChangeTrackerFactoryMock.Object);
var manager = new ImportDocumentManager(fileChangeTrackerFactoryMock.Object);
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(tracker);
});
manager.OnSubscribed(tracker);
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(anotherTracker);
});
manager.OnSubscribed(anotherTracker);
manager.Changed += (sender, args) =>
{

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.VisualStudio;
@ -37,7 +36,7 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
}
[UIFact]
public async Task OnSubscribed_StartsFileChangeTrackers()
public void OnSubscribed_StartsFileChangeTrackers()
{
// Arrange
var tracker = StrictMock.Of<IVisualStudioDocumentTracker>(t =>
@ -71,13 +70,10 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
.Returns(fileChangeTracker3Mock.Object)
.Verifiable();
var manager = new ImportDocumentManager(Dispatcher, fileChangeTrackerFactoryMock.Object);
var manager = new ImportDocumentManager(fileChangeTrackerFactoryMock.Object);
// Act
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(tracker);
});
manager.OnSubscribed(tracker);
// Assert
fileChangeTrackerFactoryMock.Verify();
@ -87,7 +83,7 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
}
[UIFact]
public async Task OnSubscribed_AlreadySubscribed_DoesNothing()
public void OnSubscribed_AlreadySubscribed_DoesNothing()
{
// Arrange
var tracker = StrictMock.Of<IVisualStudioDocumentTracker>(t =>
@ -113,25 +109,19 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
.Returns(fileChangeTrackerMock.Object)
.Callback(() => callCount++);
var manager = new ImportDocumentManager(Dispatcher, fileChangeTrackerFactoryMock.Object);
var manager = new ImportDocumentManager(fileChangeTrackerFactoryMock.Object);
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(tracker); // Start tracking the import.
});
manager.OnSubscribed(tracker); // Start tracking the import.
// Act
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(anotherTracker);
});
manager.OnSubscribed(anotherTracker);
// Assert
Assert.Equal(1, callCount);
}
[UIFact]
public async Task OnUnsubscribed_StopsFileChangeTracker()
public void OnUnsubscribed_StopsFileChangeTracker()
{
// Arrange
var tracker = StrictMock.Of<IVisualStudioDocumentTracker>(t =>
@ -152,18 +142,12 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
.Returns(fileChangeTrackerMock.Object)
.Verifiable();
var manager = new ImportDocumentManager(Dispatcher, fileChangeTrackerFactoryMock.Object);
var manager = new ImportDocumentManager(fileChangeTrackerFactoryMock.Object);
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(tracker); // Start tracking the import.
});
manager.OnSubscribed(tracker); // Start tracking the import.
// Act
await RunOnDispatcherAsync(() =>
{
manager.OnUnsubscribed(tracker);
});
manager.OnUnsubscribed(tracker);
// Assert
fileChangeTrackerFactoryMock.Verify();
@ -171,7 +155,7 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
}
[UIFact]
public async Task OnUnsubscribed_AnotherDocumentTrackingImport_DoesNotStopFileChangeTracker()
public void OnUnsubscribed_AnotherDocumentTrackingImport_DoesNotStopFileChangeTracker()
{
// Arrange
var tracker = StrictMock.Of<IVisualStudioDocumentTracker>(t =>
@ -198,20 +182,14 @@ public class ImportDocumentManagerTest : VisualStudioTestBase
.Setup(f => f.Create(It.IsAny<string>()))
.Returns(fileChangeTrackerMock.Object);
var manager = new ImportDocumentManager(Dispatcher, fileChangeTrackerFactoryMock.Object);
var manager = new ImportDocumentManager(fileChangeTrackerFactoryMock.Object);
await RunOnDispatcherAsync(() =>
{
manager.OnSubscribed(tracker); // Starts tracking import for the first document.
manager.OnSubscribed(tracker); // Starts tracking import for the first document.
manager.OnSubscribed(anotherTracker); // Starts tracking import for the second document.
});
manager.OnSubscribed(anotherTracker); // Starts tracking import for the second document.
// Act & Assert (Does not throw)
await RunOnDispatcherAsync(() =>
{
manager.OnUnsubscribed(tracker);
manager.OnUnsubscribed(tracker);
});
manager.OnUnsubscribed(tracker);
manager.OnUnsubscribed(tracker);
}
}

Просмотреть файл

@ -48,7 +48,7 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
{
// Arrange
var editorFactoryService = StrictMock.Of<IRazorEditorFactoryService>();
var documentManager = new RazorDocumentManager(editorFactoryService, Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(editorFactoryService, JoinableTaskContext);
var textView = StrictMock.Of<ITextView>();
var nonCoreTextBuffer = VsMocks.CreateTextBuffer(core: false);
@ -64,7 +64,6 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
var coreTextBuffer = VsMocks.CreateTextBuffer(core: true);
IVisualStudioDocumentTracker? documentTracker = new VisualStudioDocumentTracker(
Dispatcher,
JoinableTaskContext,
FilePath,
ProjectPath,
@ -75,7 +74,7 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
_importDocumentManager);
var editorFactoryService = StrictMock.Of<IRazorEditorFactoryService>(f =>
f.TryGetDocumentTracker(coreTextBuffer, out documentTracker) == true);
var documentManager = new RazorDocumentManager(editorFactoryService, Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(editorFactoryService, JoinableTaskContext);
// Act
await documentManager.OnTextViewOpenedAsync(textView, [coreTextBuffer]);
@ -93,7 +92,6 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
var nonCoreTextBuffer = VsMocks.CreateTextBuffer(core: false);
IVisualStudioDocumentTracker? documentTracker = new VisualStudioDocumentTracker(
Dispatcher,
JoinableTaskContext,
FilePath,
ProjectPath,
@ -104,7 +102,7 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
_importDocumentManager);
var editorFactoryService = StrictMock.Of<IRazorEditorFactoryService>(f =>
f.TryGetDocumentTracker(It.IsAny<ITextBuffer>(), out documentTracker) == true);
var documentManager = new RazorDocumentManager(editorFactoryService, Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(editorFactoryService, JoinableTaskContext);
// Assert 1
Assert.False(documentTracker.IsSupportedProject);
@ -120,7 +118,7 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
public async Task OnTextViewClosed_TextViewWithoutDocumentTracker_DoesNothing()
{
// Arrange
var documentManager = new RazorDocumentManager(StrictMock.Of<IRazorEditorFactoryService>(), Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(StrictMock.Of<IRazorEditorFactoryService>(), JoinableTaskContext);
var textView = StrictMock.Of<ITextView>();
var coreTextBuffer = VsMocks.CreateTextBuffer(core: true);
@ -142,7 +140,6 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
// Preload the buffer's properties with a tracker, so it's like we've already tracked this one.
var documentTracker = new VisualStudioDocumentTracker(
Dispatcher,
JoinableTaskContext,
FilePath,
ProjectPath,
@ -156,14 +153,14 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
coreTextBuffer.Properties.AddProperty(typeof(IVisualStudioDocumentTracker), documentTracker);
documentTracker = new VisualStudioDocumentTracker(
Dispatcher, JoinableTaskContext, FilePath, ProjectPath, _projectManager, _workspaceEditorSettings,
JoinableTaskContext, FilePath, ProjectPath, _projectManager, _workspaceEditorSettings,
ProjectEngineFactories.DefaultProvider, nonCoreTextBuffer, _importDocumentManager);
documentTracker.AddTextView(textView1);
documentTracker.AddTextView(textView2);
nonCoreTextBuffer.Properties.AddProperty(typeof(IVisualStudioDocumentTracker), documentTracker);
var editorFactoryService = StrictMock.Of<IRazorEditorFactoryService>();
var documentManager = new RazorDocumentManager(editorFactoryService, Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(editorFactoryService, JoinableTaskContext);
// Act
await documentManager.OnTextViewClosedAsync(textView2, [coreTextBuffer, nonCoreTextBuffer]);
@ -186,7 +183,6 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
var nonCoreTextBuffer = VsMocks.CreateTextBuffer(core: false);
var documentTracker = new VisualStudioDocumentTracker(
Dispatcher,
JoinableTaskContext,
FilePath,
ProjectPath,
@ -198,10 +194,10 @@ public class RazorDocumentManagerTest : VisualStudioTestBase
coreTextBuffer.Properties.AddProperty(typeof(IVisualStudioDocumentTracker), documentTracker);
var editorFactoryService = StrictMock.Of<IRazorEditorFactoryService>();
var documentManager = new RazorDocumentManager(editorFactoryService, Dispatcher, JoinableTaskContext);
var documentManager = new RazorDocumentManager(editorFactoryService, JoinableTaskContext);
// Populate the text views
await RunOnDispatcherAsync(documentTracker.Subscribe);
documentTracker.Subscribe();
documentTracker.AddTextView(textView1);
documentTracker.AddTextView(textView2);

Просмотреть файл

@ -59,7 +59,6 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
_otherHostProject = new HostProject(TestProjectData.AnotherProject.FilePath, TestProjectData.AnotherProject.IntermediateOutputPath, FallbackRazorConfiguration.MVC_2_0, TestProjectData.AnotherProject.RootNamespace);
_documentTracker = new VisualStudioDocumentTracker(
Dispatcher,
JoinableTaskFactory.Context,
_filePath,
projectPath,
@ -82,53 +81,53 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
}
[UIFact]
public async Task Subscribe_NoopsIfAlreadySubscribed()
public void Subscribe_NoopsIfAlreadySubscribed()
{
// Arrange
var callCount = 0;
_documentTracker.ContextChanged += (sender, args) => callCount++;
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
// Act
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
// Assert
Assert.Equal(1, callCount);
}
[UIFact]
public async Task Unsubscribe_NoopsIfAlreadyUnsubscribed()
public void Unsubscribe_NoopsIfAlreadyUnsubscribed()
{
// Arrange
var callCount = 0;
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
_documentTracker.ContextChanged += (sender, args) => callCount++;
await RunOnDispatcherAsync(_documentTracker.Unsubscribe);
_documentTracker.Unsubscribe();
// Act
await RunOnDispatcherAsync(_documentTracker.Unsubscribe);
_documentTracker.Unsubscribe();
// Assert
Assert.Equal(1, callCount);
}
[UIFact]
public async Task Unsubscribe_NoopsIfSubscribeHasBeenCalledMultipleTimes()
public void Unsubscribe_NoopsIfSubscribeHasBeenCalledMultipleTimes()
{
// Arrange
var callCount = 0;
await RunOnDispatcherAsync(_documentTracker.Subscribe);
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
_documentTracker.Subscribe();
_documentTracker.ContextChanged += (sender, args) => callCount++;
// Act - 1
await RunOnDispatcherAsync(_documentTracker.Unsubscribe);
_documentTracker.Unsubscribe();
// Assert - 1
Assert.Equal(0, callCount);
// Act - 2
await RunOnDispatcherAsync(_documentTracker.Unsubscribe);
_documentTracker.Unsubscribe();
// Assert - 2
Assert.Equal(1, callCount);
@ -273,7 +272,7 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
}
[UIFact]
public async Task Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged()
public void Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged()
{
// Arrange
var called = false;
@ -286,17 +285,14 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, [_filePath]);
// Act
await RunOnDispatcherAsync(() =>
{
_documentTracker.Import_Changed(null!, importChangedArgs);
});
_documentTracker.Import_Changed(null!, importChangedArgs);
// Assert
Assert.True(called);
}
[UIFact]
public async Task Import_Changed_UnrelatedImport_DoesNothing()
public void Import_Changed_UnrelatedImport_DoesNothing()
{
// Arrange
_documentTracker.ContextChanged += (sender, args) => throw new InvalidOperationException();
@ -304,21 +300,18 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
var importChangedArgs = new ImportChangedEventArgs("path/to/import", FileChangeKind.Changed, ["path/to/differentfile"]);
// Act & Assert (Does not throw)
await RunOnDispatcherAsync(() =>
{
_documentTracker.Import_Changed(null!, importChangedArgs);
});
_documentTracker.Import_Changed(null!, importChangedArgs);
}
[UIFact]
public async Task Subscribe_SetsSupportedProjectAndTriggersContextChanged()
public void Subscribe_SetsSupportedProjectAndTriggersContextChanged()
{
// Arrange
var called = false;
_documentTracker.ContextChanged += (sender, args) => called = true;
// Act
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
// Assert
Assert.True(called);
@ -326,12 +319,12 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
}
[UIFact]
public async Task Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged()
public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged()
{
// Arrange
// Subscribe once to set supported project
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
var called = false;
_documentTracker.ContextChanged += (sender, args) =>
@ -341,7 +334,7 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
};
// Act
await RunOnDispatcherAsync(_documentTracker.Unsubscribe);
_documentTracker.Unsubscribe();
// Assert
Assert.False(_documentTracker.IsSupportedProject);
@ -447,12 +440,12 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
}
[UIFact]
public async Task Subscribed_InitializesEphemeralProjectSnapshot()
public void Subscribed_InitializesEphemeralProjectSnapshot()
{
// Arrange
// Act
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
// Assert
Assert.IsType<EphemeralProjectSnapshot>(_documentTracker.ProjectSnapshot);
@ -468,7 +461,7 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
});
// Act
await RunOnDispatcherAsync(_documentTracker.Subscribe);
_documentTracker.Subscribe();
// Assert
Assert.IsType<ProjectSnapshot>(_documentTracker.ProjectSnapshot);
@ -483,10 +476,7 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
updater.ProjectAdded(_hostProject);
});
await RunOnDispatcherAsync(() =>
{
_documentTracker.Subscribe();
});
_documentTracker.Subscribe();
var args = new List<ContextChangeEventArgs>();
_documentTracker.ContextChanged += (sender, e) => args.Add(e);
@ -515,10 +505,7 @@ public class VisualStudioDocumentTrackerTest : VisualStudioWorkspaceTestBase
updater.ProjectAdded(_hostProject);
});
await RunOnDispatcherAsync(() =>
{
_documentTracker.Subscribe();
});
_documentTracker.Subscribe();
var args = new List<ContextChangeEventArgs>();
_documentTracker.ContextChanged += (sender, e) => args.Add(e);