Skip to content

Commit c199622

Browse files
committed
Add LSP handler to test Gladstone integration
1 parent 20376a8 commit c199622

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Text.Json.Nodes;
6+
using System.Text.Json.Serialization;
7+
using Roslyn.LanguageServer.Protocol;
8+
9+
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CustomMessage;
10+
11+
internal class CustomMessage(JsonNode message, TextDocumentPositionParams[] textDocumentPositions)
12+
{
13+
[JsonPropertyName("message")]
14+
public JsonNode Message { get; } = Requires.NotNull(message);
15+
16+
[JsonPropertyName("textDocumentPositions")]
17+
public TextDocumentPositionParams[] TextDocumentPositions { get; } = Requires.NotNull(textDocumentPositions);
18+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Composition;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Reflection;
10+
using System.Text.Json;
11+
using System.Text.Json.Nodes;
12+
using System.Threading;
13+
using System.Threading.Tasks;
14+
using Microsoft.CodeAnalysis.Host.Mef;
15+
using Roslyn.LanguageServer.Protocol;
16+
17+
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CustomMessage;
18+
19+
[ExportCSharpVisualBasicStatelessLspService(typeof(CustomMessageHandler)), Shared]
20+
[Method(MethodName)]
21+
[method: ImportingConstructor]
22+
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
23+
internal class CustomMessageHandler()
24+
: ILspServiceDocumentRequestHandler<CustomMessageParams, CustomMessage>
25+
{
26+
private const string MethodName = "roslyn/customMessage";
27+
28+
public bool MutatesSolutionState => false;
29+
30+
public bool RequiresLSPSolution => true;
31+
32+
public TextDocumentIdentifier GetTextDocumentIdentifier(CustomMessageParams request)
33+
{
34+
return request.Message.TextDocumentPositions.First().TextDocument;
35+
}
36+
37+
public async Task<CustomMessage> HandleRequestAsync(CustomMessageParams request, RequestContext context, CancellationToken cancellationToken)
38+
{
39+
return request.Message;
40+
41+
#pragma warning disable CS0162 // Unreachable code detected
42+
AppDomain? appDomain = null;
43+
try
44+
{
45+
var basePath = Path.GetDirectoryName(request.AssemblyPath);
46+
appDomain = AppDomain.CreateDomain(request.TypeFullName, null, new AppDomainSetup()
47+
{
48+
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
49+
ApplicationBase = basePath,
50+
});
51+
52+
appDomain.AssemblyResolve += (object sender, ResolveEventArgs args) =>
53+
{
54+
Assembly? assembly = null;
55+
try
56+
{
57+
assembly = Assembly.Load(args.Name);
58+
if (assembly != null)
59+
return assembly;
60+
}
61+
catch { }
62+
63+
var name = new AssemblyName(args.Name);
64+
var possiblePath = Path.Combine(basePath, $"{name.Name}.dll");
65+
assembly = Assembly.LoadFrom(possiblePath);
66+
67+
if (assembly != null)
68+
return assembly;
69+
70+
return null;
71+
};
72+
73+
var assemblyName = AssemblyName.GetAssemblyName(request.AssemblyPath);
74+
var assembly = appDomain.Load(assemblyName);
75+
76+
var handlerType = assembly.GetType(request.TypeFullName);
77+
var handlerMethod = handlerType.GetMethod("ExecuteAsync", BindingFlags.Static);
78+
79+
var messageType = handlerMethod.GetParameters()[0].ParameterType;
80+
var deserializedMessage = JsonSerializer.Deserialize(request.Message.Message, messageType);
81+
82+
var linePositions = request.Message.TextDocumentPositions.Select(tdp => ProtocolConversions.PositionToLinePosition(tdp.Position)).ToArray();
83+
84+
var parameters = new object?[] { deserializedMessage, context.Document, linePositions, cancellationToken };
85+
var resultTask = (Task)handlerMethod.Invoke(null, parameters);
86+
87+
await resultTask.ConfigureAwait(false);
88+
89+
var resultProperty = resultTask.GetType().GetProperty("Result");
90+
var result = resultProperty.GetValue(resultTask);
91+
92+
var resultJson = JsonSerializer.Serialize(result, resultProperty.PropertyType);
93+
94+
return new CustomMessage(JsonNode.Parse(resultJson)!, []);
95+
}
96+
finally
97+
{
98+
AppDomain.Unload(appDomain);
99+
}
100+
#pragma warning restore CS0162 // Unreachable code detected
101+
}
102+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Text.Json.Serialization;
6+
7+
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CustomMessage;
8+
9+
internal class CustomMessageParams(string assemblyPath, string typeFullName, CustomMessage message)
10+
{
11+
[JsonPropertyName("assemblyPath")]
12+
public string AssemblyPath { get; } = Requires.NotNull(assemblyPath);
13+
14+
[JsonPropertyName("typeFullName")]
15+
public string TypeFullName { get; } = Requires.NotNull(typeFullName);
16+
17+
[JsonPropertyName("message")]
18+
public CustomMessage Message { get; } = Requires.NotNull(message);
19+
}

0 commit comments

Comments
 (0)