Skip to content

Commit

Permalink
add source generator
Browse files Browse the repository at this point in the history
  • Loading branch information
CppCXY committed Jul 8, 2024
1 parent 3d049b9 commit 07739db
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ riderModule.iml
Build/win32
Build/linux
Build/macosx
.vs
6 changes: 6 additions & 0 deletions EmmyLuaAnalyzer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageServer.Framework",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageServer.Test", "LanguageServer.Test\LanguageServer.Test.csproj", "{A270A95D-E1F4-4DB4-85D3-FB5DBBDFA41D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageServer.SourceGenerator", "LanguageServer.SourceGenerator\LanguageServer.SourceGenerator.csproj", "{1EC8175F-B47C-4959-8849-0035D4101B9A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -48,6 +50,10 @@ Global
{A270A95D-E1F4-4DB4-85D3-FB5DBBDFA41D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A270A95D-E1F4-4DB4-85D3-FB5DBBDFA41D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A270A95D-E1F4-4DB4-85D3-FB5DBBDFA41D}.Release|Any CPU.Build.0 = Release|Any CPU
{1EC8175F-B47C-4959-8849-0035D4101B9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1EC8175F-B47C-4959-8849-0035D4101B9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1EC8175F-B47C-4959-8849-0035D4101B9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1EC8175F-B47C-4959-8849-0035D4101B9A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
Expand Down
1 change: 1 addition & 0 deletions EmmyLuaAnalyzer.sln.DotSettings.user
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
&lt;Assembly Path="C:\Users\zc\.nuget\packages\newtonsoft.json\13.0.3\lib\net6.0\Newtonsoft.Json.dll" /&gt;&#xD;
&lt;Assembly Path="C:\Users\zc\.nuget\packages\docfx.build\2.76.0\lib\net8.0\Docfx.Build.dll" /&gt;&#xD;
&lt;Assembly Path="C:\Users\zc\Desktop\github\LuaLanguageServer\LanguageServer.SourceGenerator\bin\Debug\netstandard2.0\LanguageServer.SourceGenerator.dll" /&gt;&#xD;
&lt;/AssemblyExplorer&gt;</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=bfb40515_002D6ac2_002D47b9_002D8d54_002D691333358a60/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
Expand Down
7 changes: 7 additions & 0 deletions LanguageServer.Framework/LanguageServer.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-preview.5.24306.7" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\LanguageServer.SourceGenerator\LanguageServer.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
/>
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion LanguageServer.Framework/Protocol/JsonRpc/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public record Message(string JsonRpc)
[JsonPropertyName("jsonrpc")] public string JsonRpc { get; } = JsonRpc;
}

[JsonConverter(typeof(MethodMessageConverter))]
[JsonConverter(typeof(Generated.JsonRpcMethodMessageConverter))]
public record MethodMessage(
string Method
) : Message("2.0")
Expand Down
90 changes: 45 additions & 45 deletions LanguageServer.Framework/Protocol/JsonRpc/MethodMessageConverter.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace EmmyLua.LanguageServer.Framework.Protocol.JsonRpc;

public class MethodMessageConverter : JsonConverter<MethodMessage>
{
public override MethodMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var jsonDoc = JsonDocument.ParseValue(ref reader);
var root = jsonDoc.RootElement;
var method = root.GetProperty("method").GetString();
var paramsJson = root.GetProperty("params").GetRawText();
if (method is not null)
{
var methodType = JsonRpcHelper.GetMethodType(method);
var methodParams = JsonSerializer.Deserialize(paramsJson, methodType, options);
if (root.TryGetProperty("id", out var id))
{
if (id.ValueKind == JsonValueKind.Number)
{
return new RequestMessage(id.GetInt32(), method, methodParams);
}
else if (id.ValueKind == JsonValueKind.String)
{
return new RequestMessage(id.GetString()!, method, methodParams);
}
}
else
{
return new NotificationMessage(method, methodParams);
}
}

// Fallback to default deserialization for other methods
// This part needs to be adjusted based on your specific needs and structure
return JsonSerializer.Deserialize<MethodMessage>(root.GetRawText(), options)!;
}

public override void Write(Utf8JsonWriter writer, MethodMessage value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}

// using System.Text.Json;
// using System.Text.Json.Serialization;
//
// namespace EmmyLua.LanguageServer.Framework.Protocol.JsonRpc;
//
// public class MethodMessageConverter : JsonConverter<MethodMessage>
// {
// public override MethodMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
// {
// using var jsonDoc = JsonDocument.ParseValue(ref reader);
// var root = jsonDoc.RootElement;
// var method = root.GetProperty("method").GetString();
// var paramsJson = root.GetProperty("params").GetRawText();
// if (method is not null)
// {
// var methodType = JsonRpcHelper.GetMethodType(method);
// var methodParams = JsonSerializer.Deserialize(paramsJson, methodType, options);
// if (root.TryGetProperty("id", out var id))
// {
// if (id.ValueKind == JsonValueKind.Number)
// {
// return new RequestMessage(id.GetInt32(), method, methodParams);
// }
// else if (id.ValueKind == JsonValueKind.String)
// {
// return new RequestMessage(id.GetString()!, method, methodParams);
// }
// }
// else
// {
// return new NotificationMessage(method, methodParams);
// }
// }
//
// // Fallback to default deserialization for other methods
// // This part needs to be adjusted based on your specific needs and structure
// return JsonSerializer.Deserialize<MethodMessage>(root.GetRawText(), options)!;
// }
//
// public override void Write(Utf8JsonWriter writer, MethodMessage value, JsonSerializerOptions options)
// {
// JsonSerializer.Serialize(writer, value, options);
// }
// }
//
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ namespace EmmyLua.LanguageServer.Framework.Protocol.Notification;
public class CancelParams
{
[JsonPropertyName("id")]
public StringOrInt Id { get; } = null!;
public StringOrInt Id { get; } = 0;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace EmmyLua.LanguageServer.Framework.Protocol.Notification;
using EmmyLua.LanguageServer.Framework.Protocol.JsonRpc;

namespace EmmyLua.LanguageServer.Framework.Protocol.Notification;

[JsonRpc("initialized")]
public class InitializedParams
{

}
117 changes: 117 additions & 0 deletions LanguageServer.SourceGenerator/JsonRpcMessageSourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Linq;
using System.Text;

[Generator(LanguageNames.CSharp)]
public class JsonRpcMessageSourceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (s, _) =>
s is ClassDeclarationSyntax declarationSyntax && declarationSyntax.AttributeLists.Count > 0,
transform: (ctx, _) => ctx.Node as ClassDeclarationSyntax)
.Where(m => m != null);
// 2. 合并信息,生成代码
context.RegisterSourceOutput(classDeclarations.Collect(), (spc, classes) =>
{
var sourceBuilder = new StringBuilder(@"
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Collections.Generic;
using EmmyLua.LanguageServer.Framework.Protocol.JsonRpc;
namespace Generated
{
public class JsonRpcMethodMessageConverter : JsonConverter<MethodMessage>
{
private readonly Dictionary<string, Type> _methodMap = new Dictionary<string, Type>();
public JsonRpcMethodMessageConverter()
{
");
var indent = 12;
var indentString = new string(' ', indent);
foreach (var classDeclaration in classes)
{
var className = classDeclaration.Identifier.Text;
var namespaceName = (classDeclaration.Parent as NamespaceDeclarationSyntax)?.Name.ToString();
var attribute = classDeclaration.AttributeLists
.SelectMany(a => a.Attributes)
.FirstOrDefault(it=>it.Name.ToString() == "JsonRpc");
if (attribute == null)
{
continue;
}
var methodName = (attribute.ArgumentList?.Arguments[0].Expression as LiteralExpressionSyntax)?.Token
.ValueText;
if (string.IsNullOrEmpty(namespaceName))
{
sourceBuilder.AppendLine($@"{indentString}_methodMap[""{methodName}""] = typeof({className});");
}
else
{
sourceBuilder.AppendLine(
$@"{indentString}_methodMap[""{methodName}""] = typeof({namespaceName}.{className});");
}
}
sourceBuilder.Append(@"
}
public override MethodMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var jsonDoc = JsonDocument.ParseValue(ref reader);
var root = jsonDoc.RootElement;
var method = root.GetProperty(""method"").GetString();
var paramsJson = root.GetProperty(""params"").GetRawText();
if (method is not null)
{
if (_methodMap.TryGetValue(method, out var methodType))
{
var methodParams = JsonSerializer.Deserialize(paramsJson, methodType, options);
if (root.TryGetProperty(""id"", out var id))
{
if (id.ValueKind == JsonValueKind.Number)
{
return new RequestMessage(id.GetInt32(), method, methodParams);
}
else if (id.ValueKind == JsonValueKind.String)
{
return new RequestMessage(id.GetString()!, method, methodParams);
}
}
else
{
return new NotificationMessage(method, methodParams);
}
}
}
// Fallback to default deserialization for other methods
// This part needs to be adjusted based on your specific needs and structure
return JsonSerializer.Deserialize<MethodMessage>(root.GetRawText(), options)!;
}
public override void Write(Utf8JsonWriter writer, MethodMessage value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
}");
spc.AddSource("JsonRpcMethodMessageConverter.g.cs",
SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0-beta1.24225.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions LanguageServer.SourceGenerator/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"LanguageServer.SourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../LanguageServer.Test/LanguageServer.Test.csproj"
}
}
}

0 comments on commit 07739db

Please sign in to comment.