Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use an OOP service for getting compilation info from Roslyn #11143

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/targets/Services.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
<ServiceHubService Include="Microsoft.VisualStudio.Razor.SpellCheck" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteSpellCheckService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Diagnostics" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteDiagnosticsService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Completion" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCompletionService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.CompilationInfo" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCompilationInfoService+Factory" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Runtime.Serialization;

namespace Microsoft.CodeAnalysis.Razor.Workspaces;

/// <summary>
/// Information that we need from a Roslyn compilation
/// </summary>
[DataContract]
internal readonly record struct CompilationInfo(
[property: DataMember(Order = 0)]
bool HasAddComponentParameter);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CodeAnalysis.Razor.Workspaces;

internal interface ICompilationInfoProvider
{
Task<CompilationInfo> GetCompilationInfoAsync(Project workspaceProject, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces;

namespace Microsoft.CodeAnalysis.Razor.Remote;

internal interface IRemoteCompilationInfoService
{
ValueTask<CompilationInfo> GetCompilationInfoAsync(RazorPinnedSolutionInfoWrapper solutionInfo, ProjectId projectId, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal static class RazorServices
(typeof(IRemoteAutoInsertService), null),
(typeof(IRemoteFormattingService), null),
(typeof(IRemoteSpellCheckService), null),
(typeof(IRemoteCompilationInfoService), null),
];

// Internal for testing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Compiler.CSharp;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.Workspaces;

namespace Microsoft.CodeAnalysis.Remote.Razor;

internal sealed partial class RemoteCompilationInfoService(in ServiceArgs args) : RazorBrokeredServiceBase(in args), IRemoteCompilationInfoService
{
internal sealed class Factory : FactoryBase<IRemoteCompilationInfoService>
{
protected override IRemoteCompilationInfoService CreateService(in ServiceArgs args)
=> new RemoteCompilationInfoService(in args);
}

public ValueTask<CompilationInfo> GetCompilationInfoAsync(RazorPinnedSolutionInfoWrapper solutionInfo, ProjectId projectId, CancellationToken cancellationToken)
=> RunServiceAsync(
solutionInfo,
solution => GetCompilationInfoAsync(solution, projectId, cancellationToken),
cancellationToken);

private async ValueTask<CompilationInfo> GetCompilationInfoAsync(Solution solution, ProjectId projectId, CancellationToken cancellationToken)
{
var project = solution.GetProject(projectId);
if (project is not null)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
if (compilation is not null)
{
return new CompilationInfo(HasAddComponentParameter: compilation.HasAddComponentParameter());
}
}

return new CompilationInfo(HasAddComponentParameter: false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.Workspaces;

namespace Microsoft.VisualStudio.Razor.Remote;

[Export(typeof(ICompilationInfoProvider))]
[method: ImportingConstructor]
internal class OutOfProcCompilationInfoProvider(IRemoteServiceInvoker remoteServiceInvoker) : ICompilationInfoProvider
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;

public async Task<CompilationInfo> GetCompilationInfoAsync(Project project, CancellationToken cancellationToken)
{
var result = await _remoteServiceInvoker.TryInvokeAsync<IRemoteCompilationInfoService, CompilationInfo>(
project.Solution,
(service, solutionInfo, innerCancellationToken) =>
service.GetCompilationInfoAsync(solutionInfo, project.Id, innerCancellationToken),
cancellationToken);

if (result is { } info)
{
return info;
}

return new CompilationInfo(HasAddComponentParameter: false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace Microsoft.VisualStudio.Razor;
internal sealed partial class RoslynProjectChangeProcessor(
IProjectSnapshotManager projectManager,
ITagHelperResolver tagHelperResolver,
ICompilationInfoProvider compilationInfoProvider,
ILoggerFactory loggerFactory,
ITelemetryReporter telemetryReporter)
: IRoslynProjectChangeProcessor, IDisposable
Expand All @@ -33,6 +34,7 @@ internal sealed partial class RoslynProjectChangeProcessor(

private readonly IProjectSnapshotManager _projectManager = projectManager;
private readonly ITagHelperResolver _tagHelperResolver = tagHelperResolver;
private readonly ICompilationInfoProvider _compilationInfoProvider = compilationInfoProvider;
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RoslynProjectChangeProcessor>();
private readonly ITelemetryReporter _telemetryReporter = telemetryReporter;

Expand Down Expand Up @@ -288,11 +290,11 @@ private void ReleaseSemaphore(ProjectKey projectKey)
var csharpLanguageVersion = csharpParseOptions.LanguageVersion;
var useRoslynTokenizer = csharpParseOptions.UseRoslynTokenizer();

var compilation = await workspaceProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var suppressAddComponentParameter = compilation is not null && !compilation.HasAddComponentParameter();
var compilationInfo = await _compilationInfoProvider.GetCompilationInfoAsync(workspaceProject, cancellationToken).ConfigureAwait(false);

var configuration = projectSnapshot.Configuration with
{
SuppressAddComponentParameter = suppressAddComponentParameter,
SuppressAddComponentParameter = !compilationInfo.HasAddComponentParameter,
UseRoslynTokenizer = useRoslynTokenizer,
CSharpLanguageVersion = csharpLanguageVersion
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using Microsoft.AspNetCore.Razor.Test.Common.VisualStudio;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Moq;
using Xunit;
using Xunit.Abstractions;

Expand All @@ -25,6 +27,7 @@ public class RoslynProjectChangeProcessorTest : VisualStudioWorkspaceTestBase
private readonly ProjectSnapshot _projectSnapshot;
private readonly ProjectWorkspaceState _projectWorkspaceStateWithTagHelpers;
private readonly TestProjectSnapshotManager _projectManager;
private readonly ICompilationInfoProvider _compilationInfoProvider;

public RoslynProjectChangeProcessorTest(ITestOutputHelper testOutput)
: base(testOutput)
Expand All @@ -47,14 +50,18 @@ public RoslynProjectChangeProcessorTest(ITestOutputHelper testOutput)
[TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build()]);

_projectManager = CreateProjectSnapshotManager();

_compilationInfoProvider = StrictMock.Of<ICompilationInfoProvider>(p =>
p.GetCompilationInfoAsync(It.IsAny<Project>(), It.IsAny<CancellationToken>()) == Task.FromResult(new CompilationInfo(true))
);
}

[UIFact]
public void Dispose_MakesUpdateNoop()
{
// Arrange
using var processor = new RoslynProjectChangeProcessor(
_projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance);
_projectManager, _tagHelperResolver, _compilationInfoProvider, LoggerFactory, NoOpTelemetryReporter.Instance);

var processorAccessor = processor.GetTestAccessor();
processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false);
Expand All @@ -73,7 +80,7 @@ public void Update_StartsUpdateTask()
{
// Arrange
using var processor = new RoslynProjectChangeProcessor(
_projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance);
_projectManager, _tagHelperResolver, _compilationInfoProvider, LoggerFactory, NoOpTelemetryReporter.Instance);

var processorAccessor = processor.GetTestAccessor();
processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false);
Expand All @@ -91,7 +98,7 @@ public void Update_SoftCancelsIncompleteTaskForSameProject()
{
// Arrange
using var processor = new RoslynProjectChangeProcessor(
_projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance);
_projectManager, _tagHelperResolver, _compilationInfoProvider, LoggerFactory, NoOpTelemetryReporter.Instance);

var processorAccessor = processor.GetTestAccessor();
processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false);
Expand All @@ -112,7 +119,7 @@ public async Task Update_NullWorkspaceProject_ClearsProjectWorkspaceState()
{
// Arrange
using var processor = new RoslynProjectChangeProcessor(
_projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance);
_projectManager, _tagHelperResolver, _compilationInfoProvider, LoggerFactory, NoOpTelemetryReporter.Instance);

var processorAccessor = processor.GetTestAccessor();
processorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false);
Expand Down Expand Up @@ -140,7 +147,7 @@ public async Task Update_ResolvesTagHelpersAndUpdatesWorkspaceState()
{
// Arrange
using var processor = new RoslynProjectChangeProcessor(
_projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance);
_projectManager, _tagHelperResolver, _compilationInfoProvider, LoggerFactory, NoOpTelemetryReporter.Instance);

var processorAccessor = processor.GetTestAccessor();
processorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false);
Expand Down
Loading