diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WrapWithTag/WrapWithTagParams.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WrapWithTag/WrapWithTagParams.cs index 65892849b52..41700e4a53e 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WrapWithTag/WrapWithTagParams.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WrapWithTag/WrapWithTagParams.cs @@ -3,20 +3,19 @@ using System.Text.Json.Serialization; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Newtonsoft.Json; namespace Microsoft.AspNetCore.Razor.LanguageServer.WrapWithTag; /// /// Class representing the parameters sent for a textDocument/_vsweb_wrapWithTag request. /// -internal class WrapWithTagParams +internal class WrapWithTagParams(TextDocumentIdentifier textDocument) { /// /// Gets or sets the identifier for the text document to be operate on. /// [JsonPropertyName("_vs_textDocument")] - public TextDocumentIdentifier TextDocument { get; set; } + public TextDocumentIdentifier TextDocument { get; set; } = textDocument; /// /// Gets or sets the selection range to be wrapped. @@ -35,11 +34,6 @@ internal class WrapWithTagParams /// [JsonPropertyName("_vs_options")] public FormattingOptions? Options { get; set; } - - public WrapWithTagParams(TextDocumentIdentifier textDocument) - { - TextDocument = textDocument; - } } internal class DelegatedWrapWithTagParams : WrapWithTagParams diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs index a6c511d1911..9422f42ece7 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs @@ -8,15 +8,14 @@ using System.Text.Json; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; namespace Microsoft.CodeAnalysis.Razor.Completion; internal static class CompletionListMerger { - private static readonly string Data1Key = nameof(MergedCompletionListData.Data1); - private static readonly string Data2Key = nameof(MergedCompletionListData.Data2); - private static readonly object EmptyData = new object(); + private static readonly string s_data1Key = nameof(MergedCompletionListData.Data1); + private static readonly string s_data2Key = nameof(MergedCompletionListData.Data2); + private static readonly object s_emptyData = new(); public static VSInternalCompletionList? Merge(VSInternalCompletionList? razorCompletionList, VSInternalCompletionList? delegatedCompletionList) { @@ -109,10 +108,7 @@ private static void Split(object data, ref PooledArrayBuilder colle return; } - // We have to be agnostic to which serialization method the delegated servers use, including - // the scenario where they use different ones, so we normalize the data to JObject. TrySplitJsonElement(data, ref collector); - TrySplitJObject(data, ref collector); } private static void TrySplitJsonElement(object data, ref PooledArrayBuilder collector) @@ -122,8 +118,8 @@ private static void TrySplitJsonElement(object data, ref PooledArrayBuilder(); @@ -143,39 +139,10 @@ private static void TrySplitJsonElement(object data, ref PooledArrayBuilder collector) - { - if (data is not JObject jObject) - { - return; - } - - if ((jObject.ContainsKey(Data1Key) || jObject.ContainsKey(Data1Key.ToLowerInvariant())) && - (jObject.ContainsKey(Data2Key) || jObject.ContainsKey(Data2Key.ToLowerInvariant()))) - { - // Merged data - var mergedCompletionListData = jObject.ToObject(); - - if (mergedCompletionListData is null) - { - Debug.Fail("Merged completion list data is null, this should never happen."); - return; - } - - Split(mergedCompletionListData.Data1, ref collector); - Split(mergedCompletionListData.Data2, ref collector); - } - else - { - // Normal, non-merged data - collector.Add(JsonDocument.Parse(jObject.ToString()).RootElement); - } - } - private static void EnsureMergeableData(VSInternalCompletionList completionListA, VSInternalCompletionList completionListB) { if (completionListA.Data != completionListB.Data && - completionListA.Data is null || completionListB.Data is null) + (completionListA.Data is null || completionListB.Data is null)) { // One of the completion lists have data while the other does not, we need to ensure that any non-data centric items don't get incorrect data associated @@ -185,10 +152,7 @@ private static void EnsureMergeableData(VSInternalCompletionList completionListA for (var i = 0; i < candidateCompletionList.Items.Length; i++) { var item = candidateCompletionList.Items[i]; - if (item.Data is null) - { - item.Data = EmptyData; - } + item.Data ??= s_emptyData; } } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/JsonHelpers.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/JsonHelpers.cs index 628b46695ef..67fd12b5d1c 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/JsonHelpers.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/JsonHelpers.cs @@ -5,44 +5,14 @@ using System.Text.Json; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; namespace Microsoft.CodeAnalysis.Razor.Protocol; internal static class JsonHelpers { - private const string s_convertedFlag = "__convertedFromJObject"; private static readonly Lazy s_roslynLspJsonSerializerOptions = new(CreateRoslynLspJsonSerializerOptions); private static readonly Lazy s_vsLspJsonSerializerOptions = new(CreateVsLspJsonSerializerOptions); - /// - /// Normalizes data from JObject to JsonElement as thats what we expect to process - /// - internal static object? TryConvertFromJObject(object? data) - { - if (data is JObject jObject) - { - jObject[s_convertedFlag] = true; - return JsonDocument.Parse(jObject.ToString()).RootElement; - } - - return data; - } - - /// - /// Converts from JObject back to JsonElement, but only if the original conversion was done with - /// - internal static object? TryConvertBackToJObject(object? data) - { - if (data is JsonElement jsonElement && - jsonElement.TryGetProperty(s_convertedFlag, out _)) - { - data = JObject.Parse(jsonElement.ToString()); - } - - return data; - } - /// /// Serializer options to use when serializing or deserializing a Roslyn LSP type /// diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostCodeActionsEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostCodeActionsEndpoint.cs index 143084c6827..75ac8add9c0 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostCodeActionsEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostCodeActionsEndpoint.cs @@ -80,8 +80,7 @@ public ImmutableArray GetRegistrations(VSInternalClientCapabilitie (service, solutionInfo, cancellationToken) => service.GetCodeActionRequestInfoAsync(solutionInfo, razorDocument.Id, request, cancellationToken), cancellationToken).ConfigureAwait(false); - if (requestInfo is null || - requestInfo.LanguageKind == RazorLanguageKind.CSharp && requestInfo.CSharpRequest is null) + if (requestInfo is null or { LanguageKind: RazorLanguageKind.CSharp, CSharpRequest: null }) { return null; } @@ -137,18 +136,7 @@ private async Task GetHtmlCodeActionsAsync(TextDocu request, cancellationToken).ConfigureAwait(false); - if (result?.Response is null) - { - return []; - } - - // WebTools is still using Newtonsoft, so we have to convert to STJ - foreach (var codeAction in result.Response) - { - codeAction.Data = JsonHelpers.TryConvertFromJObject(codeAction.Data); - } - - return result.Response; + return result?.Response ?? []; } finally { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs index 49510c508eb..965a98be7a0 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs @@ -74,11 +74,7 @@ internal partial class RazorCustomMessageTarget if (response.Response != null) { - foreach (var codeAction in response.Response) - { - codeAction.Data = JsonHelpers.TryConvertFromJObject(codeAction.Data); - codeActions.Add(codeAction); - } + codeActions.AddRange(response.Response); } } @@ -124,7 +120,6 @@ internal partial class RazorCustomMessageTarget var textBuffer = virtualDocumentSnapshot.Snapshot.TextBuffer; var codeAction = resolveCodeActionParams.CodeAction; - codeAction.Data = JsonHelpers.TryConvertBackToJObject(codeAction.Data); var requests = _requestInvoker.ReinvokeRequestOnMultipleServersAsync( textBuffer, @@ -137,10 +132,7 @@ internal partial class RazorCustomMessageTarget if (response.Response is not null) { // Only take the first response from a resolution - var resolved = response.Response; - resolved.Data = JsonHelpers.TryConvertFromJObject(resolved.Data); - - return resolved; + return response.Response; } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs index 2ab78534f45..bc3ebe07147 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs @@ -157,9 +157,6 @@ internal partial class RazorCustomMessageTarget completionList.Items = builder.ToArray(); - completionList.Data = JsonHelpers.TryConvertFromJObject(completionList.Data); - ConvertJsonElementToJObject(completionList); - return completionList; } finally @@ -173,14 +170,6 @@ internal partial class RazorCustomMessageTarget } } - private void ConvertJsonElementToJObject(VSInternalCompletionList completionList) - { - foreach (var item in completionList.Items) - { - item.Data = JsonHelpers.TryConvertFromJObject(item.Data); - } - } - private static TextEdit BuildRevertedEdit(TextEdit provisionalTextEdit) { TextEdit? revertedProvisionalTextEdit; @@ -222,7 +211,7 @@ private void UpdateVirtualDocument( trackingDocumentManager.UpdateVirtualDocument( documentSnapshotUri, virtualDocumentUri, - new[] { textChange }, + [textChange], hostDocumentVersion, state: null); } @@ -231,7 +220,7 @@ private void UpdateVirtualDocument( trackingDocumentManager.UpdateVirtualDocument( documentSnapshotUri, virtualDocumentUri, - new[] { textChange }, + [textChange], hostDocumentVersion, state: null); } @@ -291,25 +280,15 @@ private void UpdateVirtualDocument( return null; } - var completionResolveParams = request.CompletionItem; - - completionResolveParams.Data = JsonHelpers.TryConvertBackToJObject(completionResolveParams.Data); - var textBuffer = virtualDocumentSnapshot.Snapshot.TextBuffer; var response = await _requestInvoker.ReinvokeRequestOnServerAsync( textBuffer, Methods.TextDocumentCompletionResolve.Name, languageServerName, - completionResolveParams, + request.CompletionItem, cancellationToken).ConfigureAwait(false); - var item = response?.Response; - if (item is not null) - { - item.Data = JsonHelpers.TryConvertFromJObject(item.Data); - } - - return item; + return response?.Response; } [JsonRpcMethod(LanguageServerConstants.RazorGetFormattingOptionsEndpointName, UseSingleObjectParameterDeserialization = true)] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/CSharpTestLspServer.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/CSharpTestLspServer.cs index f095947c978..cc3760b27c9 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/CSharpTestLspServer.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/CSharpTestLspServer.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.LanguageServer.Protocol; using Nerdbank.Streams; @@ -77,8 +78,7 @@ static SystemTextJsonFormatter CreateSystemTextJsonMessageFormatter(AbstractRazo // Roslyn has its own converters since it doesn't use MS.VS.LS.Protocol languageServerFactory.AddJsonConverters(messageFormatter.JsonSerializerOptions); - // In its infinite wisdom, the LSP client has a public method that takes Newtonsoft.Json types, but an internal method that takes System.Text.Json types. - typeof(VSInternalExtensionUtilities).GetMethod("AddVSInternalExtensionConverters", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)!.Invoke(null, [messageFormatter.JsonSerializerOptions]); + JsonHelpers.AddVSInternalExtensionConverters(messageFormatter.JsonSerializerOptions); return messageFormatter; }