Skip to content

Commit 5bce5e4

Browse files
author
Andrew Hall
committed
Working...
1 parent f964da7 commit 5bce5e4

File tree

11 files changed

+138
-33
lines changed

11 files changed

+138
-33
lines changed

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostDocumentPullDiagnosticsEndpoint.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Threading.Tasks;
1111
using Microsoft.AspNetCore.Razor;
1212
using Microsoft.AspNetCore.Razor.PooledObjects;
13-
using Microsoft.AspNetCore.Razor.ProjectSystem;
1413
using Microsoft.CodeAnalysis;
1514
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
1615
using Microsoft.CodeAnalysis.Razor.Logging;

src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Endpoints/RazorDynamicFileChangedEndpoint.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77
namespace Microsoft.VisualStudioCode.RazorExtension.Endpoints;
88

99
[Shared]
10-
[RazorVSCodeEndpoint("razor/dynamicFileInfoChanged")]
10+
[ExportRazorStatelessLspService(typeof(RazorDynamicFileChangedEndpoint))]
11+
[RazorEndpoint("razor/dynamicFileInfoChanged")]
1112
internal class RazorDynamicFileChangedEndpoint : AbstractRazorNotificationHandler<RazorDynamicFileChangedParams>
1213
{
1314
public override bool MutatesSolutionState => false;
1415

15-
public override bool RequiresLSPSolution => throw new NotImplementedException();
16+
public override bool RequiresLSPSolution => false;
1617

1718
protected override Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RazorRequestContext context, CancellationToken cancellationToken)
1819
{
19-
var dynamicFileInfoProvider = context.GetRequiredService<IRazorLspDynamicFileInfoProvider>();
20+
var dynamicFileInfoProvider = context.GetRequiredService<RazorLspDynamicFileInfoProvider>();
2021
dynamicFileInfoProvider.Update(request.RazorDocument.Uri);
2122

2223
return Task.CompletedTask;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
using System.Composition;
5+
using System.Text.Json.Serialization;
6+
using Microsoft.AspNetCore.Razor;
7+
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
8+
9+
namespace Microsoft.VisualStudioCode.RazorExtension.Endpoints;
10+
11+
[Shared]
12+
[ExportRazorStatelessLspService(typeof(RazorInitializeEndpoint))]
13+
[RazorEndpoint("razor/initialize")]
14+
internal class RazorInitializeEndpoint : AbstractRazorNotificationHandler<RazorInitializeParams>
15+
{
16+
public override bool MutatesSolutionState => false;
17+
18+
public override bool RequiresLSPSolution => true;
19+
20+
protected override Task HandleNotificationAsync(RazorInitializeParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
21+
{
22+
var workspaceService = requestContext.GetRequiredService<RazorWorkspaceService>();
23+
workspaceService.Initialize(requestContext.Workspace.AssumeNotNull(), request.PipeName);
24+
return Task.CompletedTask;
25+
}
26+
}
27+
28+
internal class RazorInitializeParams
29+
{
30+
[JsonPropertyName("pipeName")]
31+
public required string PipeName { get; set; }
32+
}

src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Microsoft.VisualStudioCode.RazorExtension.csproj

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
<!-- Ensure we run our custom target, below, to copy compiler project references -->
99
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
1010
</PropertyGroup>
11+
<ItemGroup>
12+
<Compile Remove="S:\razor\src\Shared\files\LanguageSupport\IsExternalInit.cs" />
13+
</ItemGroup>
1114

1215
<ItemGroup>
1316
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
1417
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
1518
<PackageReference Include="Microsoft.Extensions.ObjectPool" />
1619
<PackageReference Include="Microsoft.Extensions.Logging" />
1720
<PackageReference Include="Microsoft.CodeAnalysis.ExternalAccess.Razor.Features" />
21+
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol" GeneratePathProperty="True" />
1822
</ItemGroup>
1923

2024
<ItemGroup>
@@ -27,14 +31,17 @@
2731
<InternalsVisibleTo Include="Microsoft.VisualStudioCode.RazorExtension.Test" Key="$(RazorKey)" />
2832
</ItemGroup>
2933

34+
<ItemGroup>
35+
<Content Include="$(PkgMicrosoft_VisualStudio_LanguageServer_Protocol)\lib\netstandard2.0\Microsoft.VisualStudio.LanguageServer.Protocol.dll" Pack="true" PackagePath="content" CopyToOutputDirectory="PreserveNewest" />
36+
</ItemGroup>
37+
3038
<!--
3139
Include the build output of project references in the package created. We need to do this because the compiler packages
3240
are published via a separate process to tooling, so anything that references this package will try to transitively pull
3341
in the compiler packages, but will be unable to find packages with the same version.
3442
3543
This means we end up with the package for this project including the following additional DLLs:
3644
* Microsoft.CodeAnalysis.Razor.Compiler.dll
37-
* Microsoft.AspNetCore.Razor.ProjectEngineHost.dll
3845
* Microsoft.AspNetCore.Razor.Utilities.Shared.dll
3946
4047
(and Microsoft.VisualStudioCode.RazorExtension.dll obviously)
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
#nullable enable
2-
Microsoft.VisualStudioCode.RazorExtension.RazorWorkspaceListener
3-
Microsoft.VisualStudioCode.RazorExtension.RazorWorkspaceListener.EnsureInitialized(Microsoft.CodeAnalysis.Workspace! workspace, string! pipeName) -> void
4-
Microsoft.VisualStudioCode.RazorExtension.RazorWorkspaceListener.RazorWorkspaceListener(Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void
5-
Microsoft.VisualStudioCode.RazorExtension.RazorWorkspaceListenerBase
6-
Microsoft.VisualStudioCode.RazorExtension.RazorWorkspaceListenerBase.NotifyDynamicFile(Microsoft.CodeAnalysis.ProjectId! projectId) -> void
1+
#nullable enable

src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListener.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
namespace Microsoft.VisualStudioCode.RazorExtension;
99

10-
public sealed class RazorWorkspaceListener : RazorWorkspaceListenerBase
10+
internal sealed class RazorWorkspaceListener : RazorWorkspaceListenerBase
1111
{
12-
public RazorWorkspaceListener(ILoggerFactory loggerFactory) : base(loggerFactory.CreateLogger(nameof(RazorWorkspaceListener)))
12+
public RazorWorkspaceListener(ILoggerFactory loggerFactory) : base(loggerFactory.CreateLogger<RazorWorkspaceListener>())
1313
{
1414
}
1515

src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListenerBase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace Microsoft.VisualStudioCode.RazorExtension;
1212

13-
public abstract class RazorWorkspaceListenerBase : IDisposable
13+
internal abstract class RazorWorkspaceListenerBase : IDisposable
1414
{
1515
private static readonly TimeSpan s_debounceTime = TimeSpan.FromMilliseconds(500);
1616
private readonly CancellationTokenSource _disposeTokenSource = new();
@@ -30,15 +30,15 @@ internal record Work(ProjectId ProjectId);
3030
internal record UpdateWork(ProjectId ProjectId) : Work(ProjectId);
3131
internal record RemovalWork(ProjectId ProjectId, string IntermediateOutputPath) : Work(ProjectId);
3232

33-
private protected RazorWorkspaceListenerBase(ILogger logger)
33+
protected RazorWorkspaceListenerBase(ILogger logger)
3434
{
3535
_logger = logger;
3636
_workQueue = new(s_debounceTime, ProcessWorkAsync, EqualityComparer<Work>.Default, _disposeTokenSource.Token);
3737
}
3838

3939
private protected abstract Task CheckConnectionAsync(Stream stream, CancellationToken cancellationToken);
4040

41-
void IDisposable.Dispose()
41+
public void Dispose()
4242
{
4343
if (_workspace is not null)
4444
{

src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/RazorLspDynamicFileInfoProvider.cs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@
55
using Microsoft.CodeAnalysis;
66
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
77
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
8-
using Microsoft.CodeAnalysis.Razor.Workspaces;
98

109
namespace Microsoft.VisualStudioCode.RazorExtension.Services;
1110

12-
[Export(typeof(IRazorLspDynamicFileInfoProvider)), Shared]
13-
internal sealed class RazorLspDynamicFileInfoProvider(IRazorClientLanguageServerManager clientLanguageServerManager, IWorkspaceProvider workspaceProvider) : IRazorLspDynamicFileInfoProvider
11+
[ExportRazorLspServiceFactory(typeof(RazorLspDynamicFileInfoProvider)), Shared]
12+
internal sealed class DynamicFileProviderFactory : AbstractRazorLspServiceFactory
13+
{
14+
protected override AbstractRazorLspService CreateService(IRazorLspServices lspServices)
15+
{
16+
var clientLanguageServerManager = lspServices.GetRequiredService<IRazorClientLanguageServerManager>();
17+
return new LspDynamicFileProvider(clientLanguageServerManager);
18+
}
19+
}
20+
21+
file sealed class LspDynamicFileProvider(IRazorClientLanguageServerManager clientLanguageServerManager) : RazorLspDynamicFileInfoProvider
1422
{
1523
private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo";
24+
private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo";
1625

1726
private readonly IRazorClientLanguageServerManager _clientLanguageServerManager = clientLanguageServerManager;
18-
private readonly IWorkspaceProvider _workspaceProvider = workspaceProvider;
19-
20-
public event EventHandler<string>? Updated;
2127

22-
public async Task<RazorDynamicFileInfo?> GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken)
28+
public override async Task<RazorDynamicFileInfo?> GetDynamicFileInfoAsync(Workspace workspace, ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken)
2329
{
2430
var razorUri = new Uri(filePath);
2531

@@ -41,7 +47,6 @@ internal sealed class RazorLspDynamicFileInfoProvider(IRazorClientLanguageServer
4147
return null;
4248
}
4349

44-
var workspace = _workspaceProvider.GetWorkspace();
4550
var textDocument = await WorkspaceExtensions.GetTextDocumentAsync(workspace, response.CSharpDocument.Uri, cancellationToken).ConfigureAwait(false);
4651
var checksum = Convert.FromBase64String(response.Checksum);
4752
var textLoader = new LspTextChangesTextLoader(
@@ -60,14 +65,17 @@ internal sealed class RazorLspDynamicFileInfoProvider(IRazorClientLanguageServer
6065
documentServiceProvider: EmptyServiceProvider.Instance);
6166
}
6267

63-
public Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken)
68+
public override Task RemoveDynamicFileInfoAsync(Workspace workspace, ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken)
6469
{
65-
throw new NotImplementedException();
66-
}
67-
68-
public void Update(Uri razorUri)
69-
{
70-
Updated?.Invoke(this, razorUri.ToString());
70+
var notificationParams = new RazorRemoveDynamicFileParams
71+
{
72+
CSharpDocument = new()
73+
{
74+
Uri = new Uri(filePath)
75+
}
76+
};
77+
return _clientLanguageServerManager.SendNotificationAsync(
78+
RemoveRazorDynamicFileInfoMethodName, notificationParams, cancellationToken).AsTask();
7179
}
7280
}
7381

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
using System.Text.Json.Serialization;
5+
using Microsoft.VisualStudio.LanguageServer.Protocol;
6+
7+
namespace Microsoft.VisualStudioCode.RazorExtension.Services;
8+
9+
internal class RazorRemoveDynamicFileParams
10+
{
11+
[JsonPropertyName("csharpDocument")]
12+
public required TextDocumentIdentifier CSharpDocument { get; set; }
13+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
using System.Composition;
5+
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace Microsoft.VisualStudioCode.RazorExtension.Services;
10+
11+
[ExportRazorStatelessLspService(typeof(RazorWorkspaceService)), Shared]
12+
[method: ImportingConstructor]
13+
file class WorkspaceService(ILoggerFactory loggerFactory) : RazorWorkspaceService
14+
{
15+
private readonly ILoggerFactory _loggerFactory = loggerFactory;
16+
private readonly ILogger _logger = loggerFactory.CreateLogger<RazorWorkspaceService>();
17+
private Lock _initializeLock = new();
18+
private RazorWorkspaceListener? _razorWorkspaceListener;
19+
private HashSet<ProjectId> _projectIdWithDynamicFiles = [];
20+
21+
public override void Initialize(Workspace workspace, string pipeName)
22+
{
23+
HashSet<ProjectId> projectsToInitialize;
24+
lock (_initializeLock)
25+
{
26+
// Only initialize once
27+
if (_razorWorkspaceListener is not null)
28+
{
29+
return;
30+
}
31+
32+
//_logger.LogTrace("Initializing the Razor workspace listener with pipe name {0}", pipeName);
33+
_razorWorkspaceListener = new RazorWorkspaceListener(_loggerFactory);
34+
_razorWorkspaceListener.EnsureInitialized(workspace, pipeName);
35+
36+
projectsToInitialize = _projectIdWithDynamicFiles;
37+
// May as well clear out the collection, it will never get used again anyway.
38+
_projectIdWithDynamicFiles = [];
39+
}
40+
41+
foreach (var projectId in projectsToInitialize)
42+
{
43+
_logger.LogTrace("{projectId} notifying a dynamic file for the first time", projectId);
44+
_razorWorkspaceListener.NotifyDynamicFile(projectId);
45+
}
46+
}
47+
48+
public override void NotifyDynamicFile(ProjectId projectId)
49+
{
50+
_razorWorkspaceListener?.NotifyDynamicFile(projectId);
51+
}
52+
}

0 commit comments

Comments
 (0)