mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 18:52:08 +08:00
- 将所有小写的命名空间导入更正为首字母大写格式 - 统一 GFramework 框架的命名空间引用规范 - 修复 core、ecs、godot 等模块的命名空间导入错误 - 标准化文档示例代码中的 using 语句格式 - 确保所有文档中的命名空间引用保持一致性 - 更新 global using 语句以匹配正确的命名空间格式
254 lines
9.0 KiB
C#
254 lines
9.0 KiB
C#
using System.Text;
|
||
using GFramework.SourceGenerators.Common.Constants;
|
||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||
using GFramework.SourceGenerators.Common.Generator;
|
||
using GFramework.SourceGenerators.Diagnostics;
|
||
using Microsoft.CodeAnalysis;
|
||
using Microsoft.CodeAnalysis.CSharp;
|
||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||
|
||
namespace GFramework.SourceGenerators.Rule;
|
||
|
||
/// <summary>
|
||
/// 上下文感知生成器,用于为标记了ContextAware特性的类自动生成IContextAware接口实现
|
||
/// </summary>
|
||
[Generator]
|
||
public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
|
||
{
|
||
/// <summary>
|
||
/// 获取特性的元数据名称
|
||
/// </summary>
|
||
protected override string AttributeMetadataName =>
|
||
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.ContextAwareAttribute";
|
||
|
||
/// <summary>
|
||
/// 获取特性的短名称(不包含后缀)
|
||
/// </summary>
|
||
protected override string AttributeShortNameWithoutSuffix => "ContextAware";
|
||
|
||
/// <summary>
|
||
/// 验证符号是否符合生成条件
|
||
/// </summary>
|
||
/// <param name="context">源生产上下文</param>
|
||
/// <param name="compilation">编译对象</param>
|
||
/// <param name="syntax">类声明语法节点</param>
|
||
/// <param name="symbol">命名类型符号</param>
|
||
/// <param name="attr">特性数据</param>
|
||
/// <returns>验证是否通过</returns>
|
||
protected override bool ValidateSymbol(
|
||
SourceProductionContext context,
|
||
Compilation compilation,
|
||
ClassDeclarationSyntax syntax,
|
||
INamedTypeSymbol symbol,
|
||
AttributeData attr)
|
||
{
|
||
// 1. 必须是 partial
|
||
if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||
{
|
||
context.ReportDiagnostic(Diagnostic.Create(
|
||
CommonDiagnostics.ClassMustBePartial,
|
||
syntax.Identifier.GetLocation(),
|
||
symbol.Name));
|
||
return false;
|
||
}
|
||
|
||
// 2. 必须是 class
|
||
if (symbol.TypeKind != TypeKind.Class)
|
||
{
|
||
context.ReportDiagnostic(Diagnostic.Create(
|
||
ContextAwareDiagnostic.ContextAwareOnlyForClass,
|
||
syntax.Identifier.GetLocation(),
|
||
symbol.Name));
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 生成源代码
|
||
/// </summary>
|
||
/// <param name="context">源生产上下文</param>
|
||
/// <param name="compilation">编译对象</param>
|
||
/// <param name="symbol">命名类型符号</param>
|
||
/// <param name="attr">特性数据</param>
|
||
/// <returns>生成的源代码字符串</returns>
|
||
protected override string Generate(
|
||
SourceProductionContext context,
|
||
Compilation compilation,
|
||
INamedTypeSymbol symbol,
|
||
AttributeData attr)
|
||
{
|
||
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||
? null
|
||
: symbol.ContainingNamespace.ToDisplayString();
|
||
|
||
var iContextAware = compilation.GetTypeByMetadataName(
|
||
$"{PathContests.CoreAbstractionsNamespace}.Rule.IContextAware")!;
|
||
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine("// <auto-generated/>");
|
||
sb.AppendLine("#nullable enable");
|
||
sb.AppendLine();
|
||
|
||
if (ns is not null)
|
||
{
|
||
sb.AppendLine($"namespace {ns};");
|
||
sb.AppendLine();
|
||
}
|
||
|
||
var interfaceName = iContextAware.ToDisplayString(
|
||
SymbolDisplayFormat.FullyQualifiedFormat);
|
||
sb.AppendLine($"partial class {symbol.Name} : {interfaceName}");
|
||
sb.AppendLine("{");
|
||
|
||
GenerateContextProperty(sb);
|
||
GenerateInterfaceImplementations(sb, iContextAware);
|
||
|
||
sb.AppendLine("}");
|
||
return sb.ToString().TrimEnd();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取生成文件的提示名称
|
||
/// </summary>
|
||
/// <param name="symbol">命名类型符号</param>
|
||
/// <returns>生成文件的提示名称</returns>
|
||
protected override string GetHintName(INamedTypeSymbol symbol)
|
||
{
|
||
return $"{symbol.Name}.ContextAware.g.cs";
|
||
}
|
||
|
||
// =========================
|
||
// Context 属性(使用 IArchitectureContextProvider)
|
||
// =========================
|
||
/// <summary>
|
||
/// 生成Context属性
|
||
/// </summary>
|
||
/// <param name="sb">字符串构建器</param>
|
||
private static void GenerateContextProperty(StringBuilder sb)
|
||
{
|
||
sb.AppendLine(" private global::GFramework.Core.Abstractions.Architecture.IArchitectureContext? _context;");
|
||
sb.AppendLine(
|
||
" private static global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider? _contextProvider;");
|
||
sb.AppendLine();
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine(" /// 自动获取的架构上下文(懒加载,默认使用 GameContextProvider)");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine(" protected global::GFramework.Core.Abstractions.Architecture.IArchitectureContext Context");
|
||
sb.AppendLine(" {");
|
||
sb.AppendLine(" get");
|
||
sb.AppendLine(" {");
|
||
sb.AppendLine(" if (_context == null)");
|
||
sb.AppendLine(" {");
|
||
sb.AppendLine(
|
||
" _contextProvider ??= new global::GFramework.Core.Architecture.GameContextProvider();");
|
||
sb.AppendLine(" _context = _contextProvider.GetContext();");
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine();
|
||
sb.AppendLine(" return _context;");
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine();
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine(" /// 配置上下文提供者(用于测试或多架构场景)");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine(" /// <param name=\"provider\">上下文提供者实例</param>");
|
||
sb.AppendLine(
|
||
" public static void SetContextProvider(global::GFramework.Core.Abstractions.Architecture.IArchitectureContextProvider provider)");
|
||
sb.AppendLine(" {");
|
||
sb.AppendLine(" _contextProvider = provider;");
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine();
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine(" /// 重置上下文提供者为默认值(用于测试清理)");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine(" public static void ResetContextProvider()");
|
||
sb.AppendLine(" {");
|
||
sb.AppendLine(" _contextProvider = null;");
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine();
|
||
}
|
||
|
||
|
||
// =========================
|
||
// 显式接口实现(使用 global::)
|
||
// =========================
|
||
/// <summary>
|
||
/// 生成接口实现
|
||
/// </summary>
|
||
/// <param name="sb">字符串构建器</param>
|
||
/// <param name="interfaceSymbol">接口符号</param>
|
||
private static void GenerateInterfaceImplementations(
|
||
StringBuilder sb,
|
||
INamedTypeSymbol interfaceSymbol)
|
||
{
|
||
var interfaceName = interfaceSymbol.ToDisplayString(
|
||
SymbolDisplayFormat.FullyQualifiedFormat);
|
||
|
||
foreach (var method in interfaceSymbol.GetMembers().OfType<IMethodSymbol>())
|
||
{
|
||
if (method.MethodKind != MethodKind.Ordinary)
|
||
continue;
|
||
|
||
GenerateMethod(sb, interfaceName, method);
|
||
sb.AppendLine();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成方法实现
|
||
/// </summary>
|
||
/// <param name="sb">字符串构建器</param>
|
||
/// <param name="interfaceName">接口名称</param>
|
||
/// <param name="method">方法符号</param>
|
||
private static void GenerateMethod(
|
||
StringBuilder sb,
|
||
string interfaceName,
|
||
IMethodSymbol method)
|
||
{
|
||
var returnType = method.ReturnType.ToDisplayString(
|
||
SymbolDisplayFormat.FullyQualifiedFormat);
|
||
|
||
var parameters = string.Join(", ",
|
||
method.Parameters.Select(p =>
|
||
$"{p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {p.Name}"));
|
||
|
||
sb.AppendLine(
|
||
$" {returnType} {interfaceName}.{method.Name}({parameters})");
|
||
sb.AppendLine(" {");
|
||
|
||
GenerateMethodBody(sb, method);
|
||
|
||
sb.AppendLine(" }");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成方法体
|
||
/// </summary>
|
||
/// <param name="sb">字符串构建器</param>
|
||
/// <param name="method">方法符号</param>
|
||
private static void GenerateMethodBody(
|
||
StringBuilder sb,
|
||
IMethodSymbol method)
|
||
{
|
||
switch (method.Name)
|
||
{
|
||
case "SetContext":
|
||
sb.AppendLine(" _context = context;");
|
||
break;
|
||
|
||
case "GetContext":
|
||
sb.AppendLine(" return Context;");
|
||
break;
|
||
|
||
default:
|
||
if (!method.ReturnsVoid)
|
||
sb.AppendLine(
|
||
$" throw new System.NotImplementedException(\"Method '{method.Name}' is not supported.\");");
|
||
|
||
break;
|
||
}
|
||
}
|
||
} |