Skip to content

Commit

Permalink
Update Roslyn again for System.Text.Json changeover
Browse files Browse the repository at this point in the history
  • Loading branch information
mhutch committed May 17, 2024
1 parent 4550d9e commit 08bb5f1
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 39 deletions.
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Reflection.Metadata" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.24209.3" />
<PackageVersion Include="StreamJsonRpc" Version="2.17.11" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all" />
Expand Down
52 changes: 29 additions & 23 deletions MSBuildLanguageServer/Import/MSBuildLanguageServer.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
// modified copy of
// https://github.com/dotnet/roslyn/blob/c5b98ae61ab894230786db2cde711ce4525a2597/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs
// https://github.com/dotnet/roslyn/blob/b292a51d13e020e350366ac305706367cd9db5c2/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs
// changes annotated inline with // MODIFICATION
//

// 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.Collections.Immutable;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Utilities;
using StreamJsonRpc;

namespace Microsoft.CodeAnalysis.LanguageServer
{
internal sealed class MSBuildLanguageServer : AbstractLanguageServer<RequestContext>, IOnInitialized
internal sealed class MSBuildLanguageServer : SystemTextJsonLanguageServer<RequestContext>, IOnInitialized
{
private readonly AbstractLspServiceProvider _lspServiceProvider;
private readonly ImmutableDictionary<Type, ImmutableArray<Func<ILspServices, object>>> _baseServices;
Expand All @@ -32,26 +31,31 @@ internal sealed class MSBuildLanguageServer : AbstractLanguageServer<RequestCont
public MSBuildLanguageServer(
AbstractLspServiceProvider lspServiceProvider,
JsonRpc jsonRpc,
JsonSerializer serializer,
JsonSerializerOptions serializerOptions,
ICapabilitiesProvider capabilitiesProvider,
AbstractLspLogger logger,
HostServices hostServices,
ImmutableArray<string> supportedLanguages,
WellKnownLspServerKinds serverKind)
: base(jsonRpc, serializer, logger)
: base(jsonRpc, serializerOptions, logger)
{
_lspServiceProvider = lspServiceProvider;
_serverKind = serverKind;

VSCodeInternalExtensionUtilities.AddVSCodeInternalExtensionConverters(serializer);

// Create services that require base dependencies (jsonrpc) or are more complex to create to the set manually.
_baseServices = GetBaseServices(jsonRpc, logger, capabilitiesProvider, hostServices, serverKind, supportedLanguages);

// This spins up the queue and ensure the LSP is ready to start receiving requests
Initialize();
}

public static SystemTextJsonFormatter CreateJsonMessageFormatter()
{
var messageFormatter = new SystemTextJsonFormatter();
messageFormatter.JsonSerializerOptions.AddLspSerializerOptions();
return messageFormatter;
}

protected override ILspServices ConstructLspServices()
{
return _lspServiceProvider.CreateServices(_serverKind, _baseServices);
Expand Down Expand Up @@ -89,9 +93,9 @@ private ImmutableDictionary<Type, ImmutableArray<Func<ILspServices, object>>> Ge
AddBaseService<IMethodHandler>(new InitializedHandler());
AddBaseService<IOnInitialized>(this);

//MODIFICATION 1
// MODIFICATION 1
/*
AddBaseService<ILanguageInfoProvider>(new LanguageInfoProvider());
AddBaseService<ILanguageInfoProvider>(new LanguageInfoProvider());
// In all VS cases, we already have a misc workspace. Specifically
// Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.MiscellaneousFilesWorkspace. In
Expand All @@ -102,7 +106,7 @@ private ImmutableDictionary<Type, ImmutableArray<Func<ILspServices, object>>> Ge
*/
// END MODIFICATION 1

return baseServices.ToImmutableDictionary();
return baseServices.ToImmutableDictionary();

void AddBaseService<T>(T instance) where T : class
{
Expand All @@ -122,9 +126,9 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon
return Task.CompletedTask;
}

// MODIFICATION 3
// MODIFICATION 2
/*
protected override string GetLanguageForRequest(string methodName, JToken? parameters)
protected override string GetLanguageForRequest(string methodName, JsonElement? parameters)
{
if (parameters == null)
{
Expand All @@ -145,12 +149,11 @@ protected override string GetLanguageForRequest(string methodName, JToken? param
// { "textDocument": { "uri": "<uri>" ... } ... }
//
// We can easily identify the URI for the request by looking for this structure
var textDocumentToken = parameters["textDocument"] ?? parameters["_vs_textDocument"];
if (textDocumentToken is not null)
if (parameters.Value.TryGetProperty("textDocument", out var textDocumentToken) ||
parameters.Value.TryGetProperty("_vs_textDocument", out textDocumentToken))
{
var uriToken = textDocumentToken["uri"];
Contract.ThrowIfNull(uriToken, "textDocument does not have a uri property");
var uri = uriToken.ToObject<Uri>(_jsonSerializer);
var uriToken = textDocumentToken.GetProperty("uri");
var uri = JsonSerializer.Deserialize<Uri>(uriToken, ProtocolConversions.LspJsonSerializerOptions);
Contract.ThrowIfNull(uri, "Failed to deserialize uri property");
var language = lspWorkspaceManager.GetLanguageForUri(uri);
Logger.LogInformation($"Using {language} from request text document");
Expand All @@ -161,10 +164,10 @@ protected override string GetLanguageForRequest(string methodName, JToken? param
// { "data": { "TextDocument": { "uri": "<uri>" ... } ... } ... }
//
// We can deserialize the data object using our unified DocumentResolveData.
var dataToken = parameters["data"];
if (dataToken is not null)
//var dataToken = parameters["data"];
if (parameters.Value.TryGetProperty("data", out var dataToken))
{
var data = dataToken.ToObject<DocumentResolveData>(_jsonSerializer);
var data = JsonSerializer.Deserialize<DocumentResolveData>(dataToken, ProtocolConversions.LspJsonSerializerOptions);
Contract.ThrowIfNull(data, "Failed to document resolve data object");
var language = lspWorkspaceManager.GetLanguageForUri(data.TextDocument.Uri);
Logger.LogInformation($"Using {language} from data text document");
Expand All @@ -176,7 +179,8 @@ protected override string GetLanguageForRequest(string methodName, JToken? param
return LanguageServerConstants.DefaultLanguageName;
static bool ShouldUseDefaultLanguage(string methodName)
=> methodName switch
{
return methodName switch
{
Methods.InitializeName => true,
Methods.InitializedName => true,
Expand All @@ -188,7 +192,9 @@ static bool ShouldUseDefaultLanguage(string methodName)
Methods.ExitName => true,
_ => false,
};
}
}
*/
// END MODIFICATION 2
}
}
42 changes: 32 additions & 10 deletions MSBuildLanguageServer/Import/ProtocolConversions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// modified copy of
// https://raw.githubusercontent.com/dotnet/roslyn/fca6e1fcdcded85e69cc32e15acfb6820cd45597/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs// changes annotated inline with // MODIFICATION
// https://raw.githubusercontent.com/dotnet/roslyn/044acb4ec888bf080b707e3db6818107e018d80b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs
// changes annotated inline with // MODIFICATION
// with portions commented out using /* */ comments

// Licensed to the .NET Foundation under one or more agreements.
Expand All @@ -12,10 +13,11 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
/*
using System.Threading;
using System.Threading.Tasks;
/*
using Microsoft.CodeAnalysis.DocumentHighlighting;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Formatting;
Expand Down Expand Up @@ -111,7 +113,23 @@ internal static partial class ProtocolConversions
{
{ WellKnownTags.Deprecated, ImmutableArray.Create(LSP.CompletionItemTag.Deprecated) },
}.ToImmutableDictionary();
*/

public static JsonSerializerOptions AddLspSerializerOptions(this JsonSerializerOptions options)
{
LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(options);
options.Converters.Add(new NaturalObjectConverter());
return options;
}

/// <summary>
/// Options that know how to serialize / deserialize basic LSP types.
/// Useful when there are particular fields that are not serialized or deserialized by normal request handling (for example
/// deserializing a field that is typed as object instead of a concrete type).
/// </summary>
public static JsonSerializerOptions LspJsonSerializerOptions = new JsonSerializerOptions().AddLspSerializerOptions();

/*
// TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds.
// https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726
public static async Task<Completion.CompletionTrigger> LSPToRoslynCompletionTriggerAsync(
Expand Down Expand Up @@ -176,8 +194,9 @@ static async Task<char> GetInsertionCharacterAsync(Document document, int positi
return triggerCharacter;
}
}
*/
public static string GetDocumentFilePathFromUri(Uri uri)
*/

public static string GetDocumentFilePathFromUri(Uri uri)
=> uri.IsFile ? uri.LocalPath : uri.AbsoluteUri;

/// <summary>
Expand Down Expand Up @@ -207,9 +226,9 @@ public static Uri CreateAbsoluteUri(string absolutePath)

internal static Uri CreateRelativePatternBaseUri(string path)
{
// According to VSCode LSP RelativePattern spec,
// According to VSCode LSP RelativePattern spec,
// found at https://github.com/microsoft/vscode/blob/9e1974682eb84eebb073d4ae775bad1738c281f6/src/vscode-dts/vscode.d.ts#L2226
// the baseUri should not end in a trailing separator, nor should it
// the baseUri should not end in a trailing separator, nor should it
// have any relative segmeents (., ..)
if (path[^1] == System.IO.Path.DirectorySeparatorChar)
{
Expand Down Expand Up @@ -289,7 +308,8 @@ private static bool IsAscii(string filePath)

return true;
}
/*

/*
public static LSP.TextDocumentPositionParams PositionToTextDocumentPositionParams(int position, SourceText text, Document document)
{
return new LSP.TextDocumentPositionParams()
Expand Down Expand Up @@ -535,10 +555,12 @@ static LSP.Location ConvertTextSpanWithTextToLocation(TextSpan span, SourceText
return location;
}
}
*/
*/

public static LSP.CodeDescription? HelpLinkToCodeDescription(Uri? uri)
=> (uri != null) ? new LSP.CodeDescription { Href = uri } : null;
/*
/*
public static LSP.SymbolKind NavigateToKindToSymbolKind(string kind)
{
if (Enum.TryParse<LSP.SymbolKind>(kind, out var symbolKind))
Expand Down Expand Up @@ -958,7 +980,7 @@ static string GetStyledText(TaggedText taggedText, bool isInCodeBlock)
if (!string.IsNullOrEmpty(taggedText.NavigationHint) && taggedText.NavigationHint == taggedText.NavigationTarget)
return $"[{text}]({taggedText.NavigationHint})";
// Markdown ignores spaces at the start of lines outside of code blocks,
// Markdown ignores spaces at the start of lines outside of code blocks,
// so we replace regular spaces with non-breaking spaces to ensure structural space is retained.
// We want to use regular spaces everywhere else to allow the client to wrap long text.
if (!isCode && taggedText.Tag is TextTags.Space or TextTags.ContainerStart)
Expand Down
12 changes: 10 additions & 2 deletions MSBuildLanguageServer/MSBuildLanguageServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<Compile Include="../external/roslyn/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs" />
<Compile Include="../external/roslyn/src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs" />
<Compile Include="../external/roslyn/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/ExtensionAssemblyManager.cs" />
<Compile Remove="../external/roslyn/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs"/>
</ItemGroup>

<Import Project="../external/roslyn/src/Dependencies/PooledObjects/Microsoft.CodeAnalysis.PooledObjects.projitems" Label="Shared" />
Expand All @@ -109,6 +110,13 @@
GenerateSource="true" ClassName="Microsoft.CodeAnalysis.CodeAnalysisResources" />
</ItemGroup>

<ItemGroup>
<Compile Update="@(Compile->WithMetadataValue('DefiningProjectName', 'MSBuildLanguageServer'))"
Link="roslyn/$([System.String]::new('%(Identity)').Substring(23))" />
<EmbeddedResource Update="@(EmbeddedResource->WithMetadataValue('DefiningProjectName', 'MSBuildLanguageServer'))"
Link="roslyn/$([System.String]::new('%(Identity)').Substring(23))" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../MonoDevelop.MSBuild.Editor/MonoDevelop.MSBuild.Editor.csproj" />
<ProjectReference Include="../MonoDevelop.MSBuild/MonoDevelop.MSBuild.csproj" />
Expand All @@ -122,7 +130,8 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.VisualStudio.Composition" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="StreamJsonRpc" />
</ItemGroup>

<ItemGroup>
Expand All @@ -132,7 +141,6 @@
StronglyTypedClassName="$([System.String]::Copy('%(ClassName)').Substring($([MSBuild]::Add($([System.String]::Copy('%(ClassName)').LastIndexOf('.')), 1))))"
StronglyTypedNamespace="$([System.String]::Copy('%(ClassName)').Substring(0, $([System.String]::Copy('%(ClassName)').LastIndexOf('.'))))"
Generator="MSBuild:Compile"
Link="sdk\resource\%(ClassName).resx"
StronglyTypedFileName="$(IntermediateOutputPath)\%(ClassName).Designer.cs"
LogicalName="%(ClassName).resources" />
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions MSBuildLanguageServer/MSBuildLanguageServerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Composition;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.CodeAnalysis.Host;
using Newtonsoft.Json;
using System.Text.Json;

[Export(typeof(ILanguageServerFactory)), Shared]
[method: ImportingConstructor]
Expand All @@ -17,7 +17,7 @@ class MSBuildLanguageServerFactory (CSharpVisualBasicLspServiceProvider lspServi

public AbstractLanguageServer<RequestContext> Create (
JsonRpc jsonRpc,
JsonSerializer jsonSerializer,
JsonSerializerOptions options,
ICapabilitiesProvider capabilitiesProvider,
WellKnownLspServerKinds serverKind,
AbstractLspLogger logger,
Expand All @@ -26,7 +26,7 @@ public AbstractLanguageServer<RequestContext> Create (
return new Microsoft.CodeAnalysis.LanguageServer.MSBuildLanguageServer (
lspServiceProvider,
jsonRpc,
jsonSerializer,
options,
capabilitiesProvider,
logger,
hostServices,
Expand Down
2 changes: 1 addition & 1 deletion external/roslyn
Submodule roslyn updated 999 files

0 comments on commit 08bb5f1

Please sign in to comment.