mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
refactoring part 2, use source generators for GameData
This commit is contained in:
20
EpinelPS.Analyzers/EpinelPS.Analyzers.csproj
Normal file
20
EpinelPS.Analyzers/EpinelPS.Analyzers.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
104
EpinelPS.Analyzers/LoadRecordInitializerGenerator.cs
Normal file
104
EpinelPS.Analyzers/LoadRecordInitializerGenerator.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
|
||||
namespace EpinelPS.Analyzers;
|
||||
|
||||
[Generator]
|
||||
public class LoadRecordInitializerGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Step 1: Filter for field declarations with attributes
|
||||
var fieldDeclarations = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => node is FieldDeclarationSyntax fds && fds.AttributeLists.Count > 0,
|
||||
transform: static (ctx, _) => GetTargetFieldInfo(ctx)
|
||||
)
|
||||
.Where(static m => m is not null)
|
||||
.Collect();
|
||||
|
||||
|
||||
// Step 2: Generate the code
|
||||
context.RegisterSourceOutput(fieldDeclarations, (spc, fieldInfos) =>
|
||||
{
|
||||
var source = GenerateInitializerCode(fieldInfos!);
|
||||
spc.AddSource("GameDataInitializer.g.cs", SourceText.From(source, Encoding.UTF8));
|
||||
});
|
||||
}
|
||||
|
||||
private static LoadFieldInfo? GetTargetFieldInfo(GeneratorSyntaxContext context)
|
||||
{
|
||||
if (context.Node is not FieldDeclarationSyntax fieldDecl)
|
||||
return null;
|
||||
|
||||
var variable = fieldDecl.Declaration.Variables.FirstOrDefault();
|
||||
if (variable == null)
|
||||
return null;
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol symbol)
|
||||
return null;
|
||||
|
||||
foreach (var attr in symbol.GetAttributes())
|
||||
{
|
||||
|
||||
if (attr.ConstructorArguments.Length == 3)
|
||||
{
|
||||
if (attr.ConstructorArguments[0].Value is not string fileName || attr.ConstructorArguments[1].Value is not string key || attr.ConstructorArguments[2].Value is not INamedTypeSymbol recordType)
|
||||
return null;
|
||||
|
||||
return new LoadFieldInfo
|
||||
{
|
||||
ContainingClass = symbol.ContainingType.ToDisplayString(),
|
||||
FieldName = symbol.Name,
|
||||
FileName = fileName,
|
||||
Key = key,
|
||||
RecordTypeName = recordType.ToDisplayString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GenerateInitializerCode(ImmutableArray<LoadFieldInfo> fieldInfos)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("using System.Collections.Generic;");
|
||||
sb.AppendLine("using System.Threading.Tasks;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("namespace EpinelPS.Data;");
|
||||
sb.AppendLine("public static class GameDataInitializer");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendFormat($"public static int TotalFiles = {fieldInfos.Length};");
|
||||
sb.AppendLine(" public static async Task InitializeGameData(IProgress<double> progress = null)");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
foreach (var info in fieldInfos)
|
||||
{
|
||||
var tempVar = $"data_{info.FieldName}";
|
||||
sb.AppendLine($" var {tempVar} = await {info.ContainingClass}.Instance.LoadZip<{info.RecordTypeName}>(\"{info.FileName}\", progress);");
|
||||
sb.AppendLine($" foreach (var obj in {tempVar}.records)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine($" {info.ContainingClass}.Instance.{info.FieldName}.Add(obj.{info.Key}, obj);");
|
||||
sb.AppendLine(" }");
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private class LoadFieldInfo
|
||||
{
|
||||
public string ContainingClass = "";
|
||||
public string FieldName = "";
|
||||
public string FileName = "";
|
||||
public string Key = "";
|
||||
public string RecordTypeName = "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user