From 7a7f78176e7fd6820da35d6d8854076059d976da Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Fri, 19 Feb 2021 14:30:27 +1100 Subject: [PATCH] Use newer free-threaded language service API Fixes #353 Roslyn recently implemented a free-threaded version of this API, meaning we no longer need to switch to the UI thread before calling it. This commit bumps the package version, removes the thread switch and calls the new API. --- build/import/Packages.targets | 19 +++++++++---------- .../Packaging/DebuggerTraceListener.cs | 8 ++++---- .../WorkspaceProjectContextProvider.cs | 12 ++++-------- .../IWorkspaceProjectContextFactoryFactory.cs | 8 +++++--- .../WorkspaceProjectContextProviderTests.cs | 17 ++++++++--------- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/build/import/Packages.targets b/build/import/Packages.targets index d2c7dd7a98a..bce065c81cf 100644 --- a/build/import/Packages.targets +++ b/build/import/Packages.targets @@ -19,7 +19,7 @@ - + @@ -47,8 +47,8 @@ - - + + @@ -66,10 +66,10 @@ - - - - + + + + @@ -87,7 +87,6 @@ - @@ -99,8 +98,8 @@ - - + + diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs index 8a17dfcba1c..eaa6695a2a2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs @@ -38,17 +38,17 @@ public Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) public override void Write(string message) { - if (Debugger.IsLogging()) + if (System.Diagnostics.Debugger.IsLogging()) { - Debugger.Log(0, null, message); + System.Diagnostics.Debugger.Log(0, null, message); } } public override void WriteLine(string message) { - if (Debugger.IsLogging()) + if (System.Diagnostics.Debugger.IsLogging()) { - Debugger.Log(0, null, message + Environment.NewLine); + System.Diagnostics.Debugger.Log(0, null, message + Environment.NewLine); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/WorkspaceProjectContextProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/WorkspaceProjectContextProvider.cs index 526e66cce68..cf488aef5be 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/WorkspaceProjectContextProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/WorkspaceProjectContextProvider.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Properties; @@ -17,20 +18,17 @@ namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices internal partial class WorkspaceProjectContextProvider : IWorkspaceProjectContextProvider { private readonly UnconfiguredProject _project; - private readonly IProjectThreadingService _threadingService; private readonly IProjectFaultHandlerService _faultHandlerService; private readonly ISafeProjectGuidService _projectGuidService; private readonly Lazy _workspaceProjectContextFactory; [ImportingConstructor] public WorkspaceProjectContextProvider(UnconfiguredProject project, - IProjectThreadingService threadingService, ISafeProjectGuidService projectGuidService, IProjectFaultHandlerService faultHandlerService, Lazy workspaceProjectContextFactory) // From Roslyn, so lazy { _project = project; - _threadingService = threadingService; _faultHandlerService = faultHandlerService; _workspaceProjectContextFactory = workspaceProjectContextFactory; _projectGuidService = projectGuidService; @@ -69,19 +67,17 @@ public async Task ReleaseProjectContextAsync(IWorkspaceProjectContextAccessor ac private async Task CreateProjectContextHandlingFaultAsync(ProjectContextInitData data, object? hostObject) { - // TODO: https://github.com/dotnet/project-system/issues/353. - await _threadingService.SwitchToUIThread(); - try { // Call into Roslyn to init language service for this project - IWorkspaceProjectContext context = _workspaceProjectContextFactory.Value.CreateProjectContext( + IWorkspaceProjectContext context = await _workspaceProjectContextFactory.Value.CreateProjectContextAsync( data.LanguageName, data.WorkspaceProjectContextId, data.ProjectFilePath, data.ProjectGuid, hostObject, - data.BinOutputPath); + data.BinOutputPath, + CancellationToken.None); context.LastDesignTimeBuildSucceeded = false; // By default, turn off diagnostics until the first design time build succeeds for this project. diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs index 654b6d5dc0a..34e07d55ce2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Threading; +using System.Threading.Tasks; using Moq; namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem @@ -12,12 +14,12 @@ public static IWorkspaceProjectContextFactory Create() return Mock.Of(); } - public static IWorkspaceProjectContextFactory ImplementCreateProjectContext(Func action) + public static IWorkspaceProjectContextFactory ImplementCreateProjectContextAsync(Func> action) { var mock = new Mock(); - mock.Setup(c => c.CreateProjectContext(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(action!); + mock.Setup(c => c.CreateProjectContextAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(action); return mock.Object; } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/WorkspaceProjectContextProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/WorkspaceProjectContextProviderTests.cs index 99f2fedbe6b..46d882b04ef 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/WorkspaceProjectContextProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/WorkspaceProjectContextProviderTests.cs @@ -86,7 +86,7 @@ public async Task CreateProjectContextAsync_WhenEmptyOrMissingMSBuildProperties_ var snapshot = IProjectRuleSnapshotFactory.FromJson(json); int callCount = 0; - var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContext((_, _, _, _, _, _) => { callCount++; return null; }); + var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContextAsync(delegate { callCount++; return Task.FromResult(null!); }); var provider = CreateInstance(workspaceProjectContextFactory: workspaceProjectContextFactory, projectRuleSnapshot: snapshot); var project = ConfiguredProjectFactory.ImplementProjectConfiguration("Debug|AnyCPU"); @@ -106,7 +106,7 @@ public async Task CreateProjectContextAsync_UniquelyIdentifiesContext(string con var projectGuidService = ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(new Guid(guid)); string? result = null; - var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContext((_, id, _, _, _, _) => { result = id; return null; }); + var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContextAsync((_, id, _, _, _, _, _) => { result = id; return Task.FromResult(null!); }); var provider = CreateInstance(workspaceProjectContextFactory: workspaceProjectContextFactory, projectGuidService: projectGuidService); var project = ConfiguredProjectFactory.ImplementProjectConfiguration(configuration); @@ -128,14 +128,14 @@ public async Task CreateProjectContextAsync_PassesThroughDataToCreateProjectCont string? languageNameResult = null, projectFilePathResult = null, binOutputPathResult = null; Guid? projectGuidResult = null; object? hierarchyResult = null; - var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContext((languageName, _, projectFilePath, guid, hierarchy, binOutputPath) => + var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContextAsync((languageName, _, projectFilePath, guid, hierarchy, binOutputPath, _) => { languageNameResult = languageName; projectFilePathResult = projectFilePath; projectGuidResult = guid; hierarchyResult = hierarchy; binOutputPathResult = binOutputPath; - return null; + return Task.FromResult(null!); }); var provider = CreateInstance(project: unconfiguredProject, workspaceProjectContextFactory: workspaceProjectContextFactory, projectGuidService: projectGuidService); @@ -154,7 +154,7 @@ public async Task CreateProjectContextAsync_PassesThroughDataToCreateProjectCont public async Task CreateProjectContextAsync_ReturnsContextWithLastDesignTimeBuildSucceededSetToFalse() { var context = IWorkspaceProjectContextMockFactory.Create(); - var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContext((_, _, _, _, _, _) => context); + var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContextAsync((_, _, _, _, _, _, _) => Task.FromResult(context)); var provider = CreateInstance(workspaceProjectContextFactory: workspaceProjectContextFactory); var project = ConfiguredProjectFactory.ImplementProjectConfiguration("Debug|AnyCPU"); @@ -168,7 +168,7 @@ public async Task CreateProjectContextAsync_ReturnsContextWithLastDesignTimeBuil [Fact] public async Task CreateProjectContextAsync_WhenCreateProjectContextThrows_ReturnsNull() { - var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContext((_, _, _, _, _, _) => { throw new Exception(); }); + var workspaceProjectContextFactory = IWorkspaceProjectContextFactoryFactory.ImplementCreateProjectContextAsync((_, _, _, _, _, _, _) => { throw new(); }); var provider = CreateInstance(workspaceProjectContextFactory: workspaceProjectContextFactory); var project = ConfiguredProjectFactory.ImplementProjectConfiguration("Debug|AnyCPU"); @@ -203,7 +203,7 @@ public async Task ReleaseProjectContextAsync_WhenContextThrows_SwallowsException await provider.ReleaseProjectContextAsync(accessor); } - private static WorkspaceProjectContextProvider CreateInstance(UnconfiguredProject? project = null, IProjectThreadingService? threadingService = null, IWorkspaceProjectContextFactory? workspaceProjectContextFactory = null, ISafeProjectGuidService? projectGuidService = null, IProjectRuleSnapshot? projectRuleSnapshot = null) + private static WorkspaceProjectContextProvider CreateInstance(UnconfiguredProject? project = null, IWorkspaceProjectContextFactory? workspaceProjectContextFactory = null, ISafeProjectGuidService? projectGuidService = null, IProjectRuleSnapshot? projectRuleSnapshot = null) { projectRuleSnapshot ??= IProjectRuleSnapshotFactory.FromJson( @"{ @@ -216,11 +216,10 @@ private static WorkspaceProjectContextProvider CreateInstance(UnconfiguredProjec var projectFaultService = IProjectFaultHandlerServiceFactory.Create(); project ??= UnconfiguredProjectFactory.Create(); - threadingService ??= IProjectThreadingServiceFactory.Create(); workspaceProjectContextFactory ??= IWorkspaceProjectContextFactoryFactory.Create(); projectGuidService ??= ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(Guid.NewGuid()); - var mock = new Mock(project, threadingService, projectGuidService, projectFaultService, workspaceProjectContextFactory.AsLazy()); + var mock = new Mock(project, projectGuidService, projectFaultService, workspaceProjectContextFactory.AsLazy()); mock.Protected().Setup>("GetLatestSnapshotAsync", ItExpr.IsAny()) .ReturnsAsync(projectRuleSnapshot);