GFramework/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
GwWuYou f87c9cf421 style(csharp): 格式化代码样式和优化代码结构
- 统一调整代码注释的缩进格式,保持文档注释的一致性
- 简化对象初始化语法,移除不必要的参数名称指定
- 优化条件语句结构,移除多余的花括号
- 调整方法实现格式,使用表达式主体语法简化代码
- 标准化代码缩进和空格使用,提升代码可读性
- [skip ci]
2026-01-04 22:14:34 +08:00

234 lines
7.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Linq;
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 属性(无 global::,与测试一致)
// =========================
/// <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();
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 自动获取的架构上下文(懒加载,默认使用第一个架构)");
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(
" _context = global::GFramework.Core.architecture.GameContext.GetFirstArchitectureContext();");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" return _context;");
sb.AppendLine(" }");
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;
}
}
}