From f4ced08ec4d7ecdd223c6669e274851d44f59cad Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 22 Nov 2024 16:01:56 -0800 Subject: [PATCH 01/14] Allow dynamic file response to apply edits --- .../RazorDynamicFileChangedParams.cs | 14 ++++ .../RazorDynamicFileInfoProvider.cs | 83 ++++++++++--------- .../RazorProvideDynamicFileParams.cs | 14 ++++ .../RazorProvideDynamicFileResponse.cs | 17 ++++ .../RazorRemoveDynamicFileParams.cs | 14 ++++ .../HostWorkspace/ServerTextChange.cs | 17 ++++ .../HostWorkspace/ServerTextSpan.cs | 20 +++++ 7 files changed, 139 insertions(+), 40 deletions(-) create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs new file mode 100644 index 0000000000000..44f916af90ab5 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs @@ -0,0 +1,14 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class RazorDynamicFileChangedParams +{ + [JsonPropertyName("razorDocument")] + public required TextDocumentIdentifier CSharpDocument { get; set; } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index 4e3d57878fdb8..2a45ed2c608c7 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -3,60 +3,45 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.Text; -using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; -[Export(typeof(IDynamicFileInfoProvider)), Shared] +[Shared] +[Export(typeof(IDynamicFileInfoProvider))] [ExportMetadata("Extensions", new string[] { "cshtml", "razor", })] -internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider +[ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileInfoProvider))] +[Method("razor/dynamicFileInfoChanged")] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class RazorDynamicFileInfoProvider(Lazy razorWorkspaceListenerInitializer) : IDynamicFileInfoProvider, ILspServiceNotificationHandler { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; - - private class ProvideDynamicFileParams - { - [JsonPropertyName("razorDocument")] - public required TextDocumentIdentifier RazorDocument { get; set; } - } - - private class ProvideDynamicFileResponse - { - [JsonPropertyName("csharpDocument")] - public required TextDocumentIdentifier CSharpDocument { get; set; } - } - private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; - private class RemoveDynamicFileParams - { - [JsonPropertyName("csharpDocument")] - public required TextDocumentIdentifier CSharpDocument { get; set; } - } + private readonly Lazy _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; -#pragma warning disable CS0067 // We won't fire the Updated event -- we expect Razor to send us textual changes via didChange instead - public event EventHandler? Updated; -#pragma warning restore CS0067 + public bool MutatesSolutionState => false; + public bool RequiresLSPSolution => false; - private readonly Lazy _razorWorkspaceListenerInitializer; + public event EventHandler? Updated; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorDynamicFileInfoProvider(Lazy razorWorkspaceListenerInitializer) + public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RequestContext requestContext, CancellationToken cancellationToken) { - _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; + Updated?.Invoke(this, ProtocolConversions.GetDocumentFilePathFromUri(request.CSharpDocument.Uri)); + return Task.CompletedTask; } public async Task GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) { _razorWorkspaceListenerInitializer.Value.NotifyDynamicFile(projectId); - var requestParams = new ProvideDynamicFileParams + var requestParams = new RazorProvideDynamicFileParams { RazorDocument = new() { @@ -67,26 +52,44 @@ public RazorDynamicFileInfoProvider(Lazy razo Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - var response = await clientLanguageServerManager.SendRequestAsync( + var response = await clientLanguageServerManager.SendRequestAsync( ProvideRazorDynamicFileInfoMethodName, requestParams, cancellationToken); - // Since we only sent one file over, we should get either zero or one URI back - var responseUri = response.CSharpDocument?.Uri; - - if (responseUri == null) + if (response.CSharpDocument is null) { return null; } - else + + // Since we only sent one file over, we should get either zero or one URI back + var responseUri = response.CSharpDocument.Uri; + var dynamicFileInfoFilePath = ProtocolConversions.GetDocumentFilePathFromUri(responseUri); + + if (response.Edits is not null) { - var dynamicFileInfoFilePath = ProtocolConversions.GetDocumentFilePathFromUri(responseUri); - return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, EmptyStringTextLoader.Instance, designTimeOnly: true, documentServiceProvider: null); + var workspaceManager = LanguageServerHost.Instance.GetRequiredLspService(); ; + var (workspace, solution, document) = await workspaceManager.GetLspDocumentInfoAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); + + var sourceText = document is null + ? SourceText.From("") + : await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var version = document is null + ? VersionStamp.Default + : await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + + var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); + var newText = sourceText.WithChanges(textChanges); + + var textAndVersion = TextAndVersion.Create(newText, version); + return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, TextLoader.From(textAndVersion), designTimeOnly: true, documentServiceProvider: null); } + + return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, EmptyStringTextLoader.Instance, designTimeOnly: true, documentServiceProvider: null); } public Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) { - var notificationParams = new RemoveDynamicFileParams + var notificationParams = new RazorRemoveDynamicFileParams { CSharpDocument = new() { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs new file mode 100644 index 0000000000000..15cbcfa39abe1 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs @@ -0,0 +1,14 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class RazorProvideDynamicFileParams +{ + [JsonPropertyName("razorDocument")] + public required TextDocumentIdentifier RazorDocument { get; set; } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs new file mode 100644 index 0000000000000..71faffeae1192 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs @@ -0,0 +1,17 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class RazorProvideDynamicFileResponse +{ + [JsonPropertyName("csharpDocument")] + public required TextDocumentIdentifier CSharpDocument { get; set; } + + [JsonPropertyName("edits")] + public ServerTextChange[]? Edits { get; set; } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs new file mode 100644 index 0000000000000..7b69daf08c1ba --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs @@ -0,0 +1,14 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class RazorRemoveDynamicFileParams +{ + [JsonPropertyName("csharpDocument")] + public required TextDocumentIdentifier CSharpDocument { get; set; } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs new file mode 100644 index 0000000000000..5e8f7db2822d2 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs @@ -0,0 +1,17 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class ServerTextChange +{ + [JsonPropertyName("span")] + public required ServerTextSpan Span { get; set; } + + [JsonPropertyName("newText")] + public required string NewText { get; set; } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs new file mode 100644 index 0000000000000..cc3017f980b09 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs @@ -0,0 +1,20 @@ +// 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 file in the project root for more information. + +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +internal class ServerTextSpan +{ + [JsonPropertyName("start")] + public int Start { get; set; } + + [JsonPropertyName("length")] + public int Length { get; set; } + + public TextSpan ToTextSpan() + => new(Start, Length); +} From 71e4fdee85b76e9a7e01a1c261aa6daa3f4f5199 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 22 Nov 2024 17:45:59 -0800 Subject: [PATCH 02/14] Use a queue to reduce noise --- .../RazorDynamicFileInfoProvider.cs | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index 2a45ed2c608c7..9a1dd1ea30229 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Composition; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -17,14 +19,27 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; [ExportMetadata("Extensions", new string[] { "cshtml", "razor", })] [ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileInfoProvider))] [Method("razor/dynamicFileInfoChanged")] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal class RazorDynamicFileInfoProvider(Lazy razorWorkspaceListenerInitializer) : IDynamicFileInfoProvider, ILspServiceNotificationHandler +internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider, ILspServiceNotificationHandler { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; - private readonly Lazy _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; + private readonly Lazy _razorWorkspaceListenerInitializer; + private readonly AsyncBatchingWorkQueue _updateWorkQueue; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorDynamicFileInfoProvider( + Lazy razorWorkspaceListenerInitializer, + IAsynchronousOperationListenerProvider listenerProvider) + { + _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; + _updateWorkQueue = new AsyncBatchingWorkQueue( + TimeSpan.FromMilliseconds(200), + UpdateAsync, + listenerProvider.GetListener(nameof(RazorDynamicFileInfoProvider)), + CancellationToken.None); + } public bool MutatesSolutionState => false; public bool RequiresLSPSolution => false; @@ -33,7 +48,8 @@ internal class RazorDynamicFileInfoProvider(Lazy(); ; - var (workspace, solution, document) = await workspaceManager.GetLspDocumentInfoAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); + var workspaceManager = LanguageServerHost.Instance.GetRequiredLspService(); + var (_, _1, document) = await workspaceManager.GetLspDocumentInfoAsync(response.CSharpDocument, cancellationToken); var sourceText = document is null ? SourceText.From("") @@ -104,6 +120,17 @@ public Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFileP RemoveRazorDynamicFileInfoMethodName, notificationParams, cancellationToken).AsTask(); } + private ValueTask UpdateAsync(ImmutableSegmentedList paths, CancellationToken token) + { + foreach (var path in paths) + { + token.ThrowIfCancellationRequested(); + Updated?.Invoke(this, path); + } + + return ValueTask.CompletedTask; + } + private sealed class EmptyStringTextLoader : TextLoader { public static readonly TextLoader Instance = new EmptyStringTextLoader(); From 4a6af623ef782bb4f7de10008dd7e91e5e1192fe Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Fri, 22 Nov 2024 17:48:08 -0800 Subject: [PATCH 03/14] >.> --- .../HostWorkspace/RazorDynamicFileChangedParams.cs | 2 +- .../HostWorkspace/RazorDynamicFileInfoProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs index 44f916af90ab5..2470919aad036 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs @@ -10,5 +10,5 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; internal class RazorDynamicFileChangedParams { [JsonPropertyName("razorDocument")] - public required TextDocumentIdentifier CSharpDocument { get; set; } + public required TextDocumentIdentifier RazorDocument { get; set; } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index 9a1dd1ea30229..606142ef8465d 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -48,7 +48,7 @@ public RazorDynamicFileInfoProvider( public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RequestContext requestContext, CancellationToken cancellationToken) { - var path = ProtocolConversions.GetDocumentFilePathFromUri(request.CSharpDocument.Uri); + var path = ProtocolConversions.GetDocumentFilePathFromUri(request.RazorDocument.Uri); _updateWorkQueue.AddWork(path); return Task.CompletedTask; } From a975b8a81f59a82a6e1601bcb73b0ea7ec8b2f8f Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 25 Nov 2024 17:46:39 -0800 Subject: [PATCH 04/14] Get document from workspace correctly --- .../RazorDynamicFileChangedHandler.cs | 34 +++++++++++++++++++ .../RazorDynamicFileInfoProvider.cs | 23 +++++++------ 2 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs new file mode 100644 index 0000000000000..6e8d8fd2d95ce --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs @@ -0,0 +1,34 @@ +// 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 file in the project root for more information. + +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; + +[Shared] +[ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileChangedHandler))] +[Method("razor/dynamicFileInfoChanged")] +internal class RazorDynamicFileChangedHandler : ILspServiceNotificationHandler +{ + private readonly RazorDynamicFileInfoProvider _razorDynamicFileInfoProvider; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorDynamicFileChangedHandler(RazorDynamicFileInfoProvider razorDynamicFileInfoProvider) + { + _razorDynamicFileInfoProvider = razorDynamicFileInfoProvider; + } + + public bool MutatesSolutionState => false; + public bool RequiresLSPSolution => false; + + public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RequestContext requestContext, CancellationToken cancellationToken) + { + var filePath = ProtocolConversions.GetDocumentFilePathFromUri(request.RazorDocument.Uri); + _razorDynamicFileInfoProvider.Update(filePath); + return Task.CompletedTask; + } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index 606142ef8465d..76f0537109833 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -16,21 +16,22 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; [Shared] [Export(typeof(IDynamicFileInfoProvider))] +[Export(typeof(RazorDynamicFileInfoProvider))] [ExportMetadata("Extensions", new string[] { "cshtml", "razor", })] -[ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileInfoProvider))] -[Method("razor/dynamicFileInfoChanged")] -internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider, ILspServiceNotificationHandler +internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; private readonly Lazy _razorWorkspaceListenerInitializer; + private readonly LanguageServerWorkspaceFactory _workspaceFactory; private readonly AsyncBatchingWorkQueue _updateWorkQueue; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public RazorDynamicFileInfoProvider( Lazy razorWorkspaceListenerInitializer, + LanguageServerWorkspaceFactory workspaceFactory, IAsynchronousOperationListenerProvider listenerProvider) { _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; @@ -39,6 +40,7 @@ public RazorDynamicFileInfoProvider( UpdateAsync, listenerProvider.GetListener(nameof(RazorDynamicFileInfoProvider)), CancellationToken.None); + _workspaceFactory = workspaceFactory; } public bool MutatesSolutionState => false; @@ -46,11 +48,9 @@ public RazorDynamicFileInfoProvider( public event EventHandler? Updated; - public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RequestContext requestContext, CancellationToken cancellationToken) + public void Update(string filePath) { - var path = ProtocolConversions.GetDocumentFilePathFromUri(request.RazorDocument.Uri); - _updateWorkQueue.AddWork(path); - return Task.CompletedTask; + _updateWorkQueue.AddWork(filePath); } public async Task GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) @@ -82,8 +82,9 @@ public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, Reque if (response.Edits is not null) { - var workspaceManager = LanguageServerHost.Instance.GetRequiredLspService(); - var (_, _1, document) = await workspaceManager.GetLspDocumentInfoAsync(response.CSharpDocument, cancellationToken); + var project = _workspaceFactory.Workspace.CurrentSolution.GetRequiredProject(projectId); + var document = project.Documents.FirstOrDefault( + d => d.FilePath is not null && PathUtilities.PathsEqual(d.FilePath, dynamicFileInfoFilePath)); var sourceText = document is null ? SourceText.From("") @@ -96,7 +97,7 @@ public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, Reque var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); var newText = sourceText.WithChanges(textChanges); - var textAndVersion = TextAndVersion.Create(newText, version); + var textAndVersion = TextAndVersion.Create(newText, version.GetNewerVersion()); return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, TextLoader.From(textAndVersion), designTimeOnly: true, documentServiceProvider: null); } From a4877b2fa3ae6cebf9a5c99b0adfb50bd39a9706 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 25 Nov 2024 18:02:29 -0800 Subject: [PATCH 05/14] Use a custom text loader --- .../RazorDynamicFileInfoProvider.cs | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index 76f0537109833..03e4165b54d20 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -86,22 +86,23 @@ public void Update(string filePath) var document = project.Documents.FirstOrDefault( d => d.FilePath is not null && PathUtilities.PathsEqual(d.FilePath, dynamicFileInfoFilePath)); - var sourceText = document is null - ? SourceText.From("") - : await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - - var version = document is null - ? VersionStamp.Default - : await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); - var newText = sourceText.WithChanges(textChanges); - - var textAndVersion = TextAndVersion.Create(newText, version.GetNewerVersion()); - return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, TextLoader.From(textAndVersion), designTimeOnly: true, documentServiceProvider: null); + var textLoader = new TextChangesTextLoader(document, textChanges); + + return new DynamicFileInfo( + dynamicFileInfoFilePath, + SourceCodeKind.Regular, + textLoader, + designTimeOnly: true, + documentServiceProvider: null); } - return new DynamicFileInfo(dynamicFileInfoFilePath, SourceCodeKind.Regular, EmptyStringTextLoader.Instance, designTimeOnly: true, documentServiceProvider: null); + return new DynamicFileInfo( + dynamicFileInfoFilePath, + SourceCodeKind.Regular, + EmptyStringTextLoader.Instance, + designTimeOnly: true, + documentServiceProvider: null); } public Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) @@ -132,15 +133,31 @@ private ValueTask UpdateAsync(ImmutableSegmentedList paths, Cancellation return ValueTask.CompletedTask; } - private sealed class EmptyStringTextLoader : TextLoader + private sealed class EmptyStringTextLoader() : TextLoader { public static readonly TextLoader Instance = new EmptyStringTextLoader(); - private EmptyStringTextLoader() { } - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Default)); } } + + private sealed class TextChangesTextLoader(Document? document, IEnumerable changes) : TextLoader + { + public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + { + var sourceText = document is null + ? SourceText.From("") + : await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var version = document is null + ? VersionStamp.Default + : await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + + var newText = sourceText.WithChanges(changes); + return TextAndVersion.Create(newText, version.GetNewerVersion()); + } + } + } From 9a3edc8346d604a295bcd659aa41208a4b56679c Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 11:49:06 -0800 Subject: [PATCH 06/14] Move to subfolder --- .../{ => Razor}/RazorDynamicFileChangedHandler.cs | 0 .../HostWorkspace/{ => Razor}/RazorDynamicFileChangedParams.cs | 2 +- .../HostWorkspace/{ => Razor}/RazorDynamicFileInfoProvider.cs | 3 --- .../HostWorkspace/{ => Razor}/RazorInitializeHandler.cs | 2 +- .../HostWorkspace/{ => Razor}/RazorProvideDynamicFileParams.cs | 2 +- .../{ => Razor}/RazorProvideDynamicFileResponse.cs | 2 +- .../HostWorkspace/{ => Razor}/RazorRemoveDynamicFileParams.cs | 2 +- .../{ => Razor}/RazorWorkspaceListenerInitializer.cs | 2 +- 8 files changed, 6 insertions(+), 9 deletions(-) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorDynamicFileChangedHandler.cs (100%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorDynamicFileChangedParams.cs (86%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorDynamicFileInfoProvider.cs (98%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorInitializeHandler.cs (95%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorProvideDynamicFileParams.cs (86%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorProvideDynamicFileResponse.cs (88%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorRemoveDynamicFileParams.cs (86%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/RazorWorkspaceListenerInitializer.cs (97%) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs similarity index 100% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedHandler.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs similarity index 86% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs index 2470919aad036..73a2cf4dd186c 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileChangedParams.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class RazorDynamicFileChangedParams { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs similarity index 98% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index 03e4165b54d20..011295a88ce81 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -43,9 +43,6 @@ public RazorDynamicFileInfoProvider( _workspaceFactory = workspaceFactory; } - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => false; - public event EventHandler? Updated; public void Update(string filePath) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorInitializeHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs similarity index 95% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorInitializeHandler.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs index c872b2303ef39..546b0a3f6edd7 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorInitializeHandler.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [ExportCSharpVisualBasicStatelessLspService(typeof(RazorInitializeHandler)), Shared] [Method("razor/initialize")] diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs similarity index 86% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs index 15cbcfa39abe1..56f4f16ca799c 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileParams.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class RazorProvideDynamicFileParams { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs similarity index 88% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs index 71faffeae1192..5d6ab63287211 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorProvideDynamicFileResponse.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class RazorProvideDynamicFileResponse { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs similarity index 86% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs index 7b69daf08c1ba..2388208108769 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorRemoveDynamicFileParams.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class RazorRemoveDynamicFileParams { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorWorkspaceListenerInitializer.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs similarity index 97% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorWorkspaceListenerInitializer.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs index b0d2ab8a94e4c..80d366ca58c13 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorWorkspaceListenerInitializer.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs @@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.Extensions.Logging; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [Export(typeof(RazorWorkspaceListenerInitializer)), Shared] internal sealed class RazorWorkspaceListenerInitializer From 4d4167866efb3114f1acd8a0b2c09ddc141ed766 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 11:52:20 -0800 Subject: [PATCH 07/14] Use TextDocument --- .../Razor/RazorDynamicFileChangedHandler.cs | 2 +- .../Razor/RazorDynamicFileInfoProvider.cs | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs index 6e8d8fd2d95ce..48c1c3fa8595f 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs @@ -6,7 +6,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [Shared] [ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileChangedHandler))] diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index 011295a88ce81..73d5ed0560323 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -12,7 +13,7 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [Shared] [Export(typeof(IDynamicFileInfoProvider))] @@ -79,12 +80,9 @@ public void Update(string filePath) if (response.Edits is not null) { - var project = _workspaceFactory.Workspace.CurrentSolution.GetRequiredProject(projectId); - var document = project.Documents.FirstOrDefault( - d => d.FilePath is not null && PathUtilities.PathsEqual(d.FilePath, dynamicFileInfoFilePath)); - + var textDocument = await _workspaceFactory.Workspace.CurrentSolution.GetTextDocumentAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); - var textLoader = new TextChangesTextLoader(document, textChanges); + var textLoader = new TextChangesTextLoader(textDocument, textChanges); return new DynamicFileInfo( dynamicFileInfoFilePath, @@ -140,7 +138,7 @@ public override Task LoadTextAndVersionAsync(LoadTextOptions opt } } - private sealed class TextChangesTextLoader(Document? document, IEnumerable changes) : TextLoader + private sealed class TextChangesTextLoader(TextDocument? document, IEnumerable changes) : TextLoader { public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { From 125d83e97b0cc18558fc1695360009262f3933bc Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 14:32:22 -0800 Subject: [PATCH 08/14] Move --- .../HostWorkspace/{ => Razor}/ServerTextChange.cs | 0 .../HostWorkspace/{ => Razor}/ServerTextSpan.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/ServerTextChange.cs (100%) rename src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/{ => Razor}/ServerTextSpan.cs (88%) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs similarity index 100% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextChange.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.cs similarity index 88% rename from src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs rename to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.cs index cc3017f980b09..80d3d035aeb3e 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ServerTextSpan.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.cs @@ -5,7 +5,7 @@ using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class ServerTextSpan { From 0ee2e821548120a58c042936270b2c6f4d3db9a9 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 17:19:58 -0800 Subject: [PATCH 09/14] Use checksum to validate contents --- ...cFileInfoProvider.TextChangesTextLoader.cs | 88 +++++++++++++++++++ .../Razor/RazorDynamicFileInfoProvider.cs | 32 +++---- .../Razor/RazorProvideDynamicFileParams.cs | 7 ++ .../Razor/RazorProvideDynamicFileResponse.cs | 13 +++ 4 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs new file mode 100644 index 0000000000000..bddc516293d6f --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs @@ -0,0 +1,88 @@ +// 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 file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; + +internal partial class RazorDynamicFileInfoProvider +{ + private sealed class TextChangesTextLoader( + TextDocument? document, + IEnumerable changes, + byte[] checksum, + SourceHashAlgorithm checksumAlgorithm, + string? encoding, + Uri razorUri) : TextLoader + { + public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + { + if (document is null) + { + var text = SourceText.From("").WithChanges(changes); + return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); + } + + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // Validate the checksum information so the edits are known to be correct + if (IsSourceTextMatching(sourceText)) + { + var version = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + var newText = sourceText.WithChanges(changes); + return TextAndVersion.Create(newText, version.GetNewerVersion()); + } + + return await GetFullDocumentFromServerAsync(razorUri, cancellationToken).ConfigureAwait(false); + } + + private bool IsSourceTextMatching(SourceText sourceText) + { + if (sourceText.ChecksumAlgorithm != checksumAlgorithm) + { + return false; + } + + if (sourceText.Encoding?.WebName.Equals(encoding) == false) + { + return false; + } + + if (!sourceText.GetChecksum().SequenceEqual(checksum)) + { + return false; + } + + return true; + } + + private static async Task GetFullDocumentFromServerAsync(Uri razorUri, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); + var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); + + var response = await clientLanguageServerManager.SendRequestAsync( + ProvideRazorDynamicFileInfoMethodName, + new RazorProvideDynamicFileParams + { + RazorDocument = new() + { + Uri = razorUri, + }, + FullText = true + }, + cancellationToken); + + RoslynDebug.AssertNotNull(response.Edits); + + var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); + var text = SourceText.From("").WithChanges(textChanges); + return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); + } + } + +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index 73d5ed0560323..6cd83979f2b68 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -19,7 +18,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [Export(typeof(IDynamicFileInfoProvider))] [Export(typeof(RazorDynamicFileInfoProvider))] [ExportMetadata("Extensions", new string[] { "cshtml", "razor", })] -internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider +internal partial class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; @@ -55,11 +54,12 @@ public void Update(string filePath) { _razorWorkspaceListenerInitializer.Value.NotifyDynamicFile(projectId); + var razorUri = ProtocolConversions.CreateAbsoluteUri(filePath); var requestParams = new RazorProvideDynamicFileParams { RazorDocument = new() { - Uri = ProtocolConversions.CreateAbsoluteUri(filePath) + Uri = razorUri } }; @@ -82,7 +82,13 @@ public void Update(string filePath) { var textDocument = await _workspaceFactory.Workspace.CurrentSolution.GetTextDocumentAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); - var textLoader = new TextChangesTextLoader(textDocument, textChanges); + var textLoader = new TextChangesTextLoader( + textDocument, + textChanges, + response.Checksum, + response.ChecksumAlgorithm, + response.EncodingName, + razorUri); return new DynamicFileInfo( dynamicFileInfoFilePath, @@ -137,22 +143,4 @@ public override Task LoadTextAndVersionAsync(LoadTextOptions opt return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Default)); } } - - private sealed class TextChangesTextLoader(TextDocument? document, IEnumerable changes) : TextLoader - { - public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) - { - var sourceText = document is null - ? SourceText.From("") - : await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - - var version = document is null - ? VersionStamp.Default - : await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - - var newText = sourceText.WithChanges(changes); - return TextAndVersion.Create(newText, version.GetNewerVersion()); - } - } - } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs index 56f4f16ca799c..1d6f4a24dbd22 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs @@ -11,4 +11,11 @@ internal class RazorProvideDynamicFileParams { [JsonPropertyName("razorDocument")] public required TextDocumentIdentifier RazorDocument { get; set; } + + /// + /// When true, the full text of the document will be sent over as a single + /// edit instead of diff edits + /// + [JsonPropertyName("fullText")] + public bool FullText { get; set; } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs index 5d6ab63287211..b40399d31d5d0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; @@ -14,4 +15,16 @@ internal class RazorProvideDynamicFileResponse [JsonPropertyName("edits")] public ServerTextChange[]? Edits { get; set; } + + [JsonPropertyName("checksum")] + public required byte[] Checksum { get; set; } + + [JsonPropertyName("checksumAlgorithm")] + public SourceHashAlgorithm ChecksumAlgorithm { get; set; } + + /// + /// The IANA name associated with the encoding of the source text + /// + [JsonPropertyName("encodingName")] + public string? EncodingName { get; set; } } From 81cc78737bb709aef0dcc148d24249398d852500 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 17:48:35 -0800 Subject: [PATCH 10/14] Don't forget encoding/checksum info. Change to codepage --- ...micFileInfoProvider.TextChangesTextLoader.cs | 17 ++++++++++++----- .../Razor/RazorDynamicFileInfoProvider.cs | 2 +- .../Razor/RazorProvideDynamicFileResponse.cs | 7 ++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs index bddc516293d6f..edad64bccee75 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.Text; @@ -16,14 +17,20 @@ private sealed class TextChangesTextLoader( IEnumerable changes, byte[] checksum, SourceHashAlgorithm checksumAlgorithm, - string? encoding, + int? codePage, Uri razorUri) : TextLoader { + private readonly Lazy _emptySourceText = new Lazy(() => + { + var encoding = codePage is null ? null : Encoding.GetEncoding(codePage.Value); + return SourceText.From("", checksumAlgorithm: checksumAlgorithm, encoding: encoding); + }); + public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { if (document is null) { - var text = SourceText.From("").WithChanges(changes); + var text = _emptySourceText.Value.WithChanges(changes); return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); } @@ -47,7 +54,7 @@ private bool IsSourceTextMatching(SourceText sourceText) return false; } - if (sourceText.Encoding?.WebName.Equals(encoding) == false) + if (sourceText.Encoding?.CodePage == codePage) { return false; } @@ -60,7 +67,7 @@ private bool IsSourceTextMatching(SourceText sourceText) return true; } - private static async Task GetFullDocumentFromServerAsync(Uri razorUri, CancellationToken cancellationToken) + private async Task GetFullDocumentFromServerAsync(Uri razorUri, CancellationToken cancellationToken) { Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); @@ -80,7 +87,7 @@ private static async Task GetFullDocumentFromServerAsync(Uri raz RoslynDebug.AssertNotNull(response.Edits); var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); - var text = SourceText.From("").WithChanges(textChanges); + var text = _emptySourceText.Value.WithChanges(textChanges); return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index 6cd83979f2b68..a0884e0d6947e 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -87,7 +87,7 @@ public void Update(string filePath) textChanges, response.Checksum, response.ChecksumAlgorithm, - response.EncodingName, + response.SourceEncodingCodePage, razorUri); return new DynamicFileInfo( diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs index b40399d31d5d0..ad9e2f3396c7c 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs @@ -22,9 +22,6 @@ internal class RazorProvideDynamicFileResponse [JsonPropertyName("checksumAlgorithm")] public SourceHashAlgorithm ChecksumAlgorithm { get; set; } - /// - /// The IANA name associated with the encoding of the source text - /// - [JsonPropertyName("encodingName")] - public string? EncodingName { get; set; } + [JsonPropertyName("encodingCodePage")] + public int? SourceEncodingCodePage { get; set; } } From 87d3241afc74837db339edfbc28198e280c7de63 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 18:54:56 -0800 Subject: [PATCH 11/14] != --- .../Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs index edad64bccee75..8507b14a63bc5 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs @@ -54,7 +54,7 @@ private bool IsSourceTextMatching(SourceText sourceText) return false; } - if (sourceText.Encoding?.CodePage == codePage) + if (sourceText.Encoding?.CodePage != codePage) { return false; } From 361dd43dfff8fdccf836e3bd90750cf613638a13 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 19:35:50 -0800 Subject: [PATCH 12/14] One namespace didn't update --- .../Razor/RazorDynamicFileInfoProviderTests.cs | 16 ++++++++++++++++ .../HostWorkspace/Razor/ServerTextChange.cs | 3 +-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs new file mode 100644 index 0000000000000..3cbb77d52fd3e --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs @@ -0,0 +1,16 @@ +// 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 file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Razor +{ + internal class RazorDynamicFileInfoProviderTests + { + } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs index 5e8f7db2822d2..272a69316d4b0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs @@ -3,9 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; internal class ServerTextChange { From 379a1d6c2d398b6b09f8228d33ab70d24e5ba9cd Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 2 Dec 2024 19:36:12 -0800 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=91=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Razor/RazorDynamicFileInfoProviderTests.cs | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs deleted file mode 100644 index 3cbb77d52fd3e..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Razor/RazorDynamicFileInfoProviderTests.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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 file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Razor -{ - internal class RazorDynamicFileInfoProviderTests - { - } -} From 1e81723c3a6910f69674057d5edacf4d5aa6f205 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 3 Dec 2024 18:20:20 -0800 Subject: [PATCH 14/14] Base64 encode --- .../RazorDynamicFileInfoProvider.TextChangesTextLoader.cs | 1 + .../HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs | 4 +++- .../HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs index 8507b14a63bc5..a4d5b99895f50 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs @@ -85,6 +85,7 @@ private async Task GetFullDocumentFromServerAsync(Uri razorUri, cancellationToken); RoslynDebug.AssertNotNull(response.Edits); + RoslynDebug.Assert(response.Edits.IsSingle()); var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); var text = _emptySourceText.Value.WithChanges(textChanges); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index a0884e0d6947e..f6a7c1f4147b6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; @@ -82,10 +83,11 @@ public void Update(string filePath) { var textDocument = await _workspaceFactory.Workspace.CurrentSolution.GetTextDocumentAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); var textChanges = response.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); + var checksum = Convert.FromBase64String(response.Checksum); var textLoader = new TextChangesTextLoader( textDocument, textChanges, - response.Checksum, + checksum, response.ChecksumAlgorithm, response.SourceEncodingCodePage, razorUri); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs index ad9e2f3396c7c..371624091e313 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs @@ -17,7 +17,7 @@ internal class RazorProvideDynamicFileResponse public ServerTextChange[]? Edits { get; set; } [JsonPropertyName("checksum")] - public required byte[] Checksum { get; set; } + public required string Checksum { get; set; } [JsonPropertyName("checksumAlgorithm")] public SourceHashAlgorithm ChecksumAlgorithm { get; set; }