mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 将 GFramework.Godot.SourceGenerators 项目的目标框架从 netstandard2.1 更改为 netstandard2.0 - 将 GFramework.SourceGenerators.Abstractions 项目的目标框架从 netstandard2.1 更改为 netstandard2.0 - 将 GFramework.SourceGenerators 项目的目标框架从 netstandard2.1 更改为 netstandard2.0 - 将 GFramework.SourceGenerators.Common 项目的目标框架从 netstandard2.1 更改为 netstandard2.0 - 从 GFramework.SourceGenerators 项目中移除对 GFramework.Core.Abstractions 的引用 - 从 GFramework.SourceGenerators.Abstractions 项目中移除对 GFramework.Core.Abstractions 的引用 - 更新 PriorityGenerator 中的语法检查逻辑,使用 Any 替代 All 进行 partial 关键字检查 - 更新 PriorityAttribute 文档注释中的 cref 格式为 c 标签
153 lines
5.0 KiB
C#
153 lines
5.0 KiB
C#
using System.Text;
|
|
using GFramework.SourceGenerators.Common.Constants;
|
|
using GFramework.SourceGenerators.Common.Generator;
|
|
using GFramework.SourceGenerators.Diagnostics;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
namespace GFramework.SourceGenerators.Bases;
|
|
|
|
/// <summary>
|
|
/// Priority 特性生成器,为标记了 [Priority] 的类自动生成 IPrioritized 接口实现
|
|
/// </summary>
|
|
[Generator]
|
|
public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
|
{
|
|
/// <summary>
|
|
/// 获取特性的元数据名称
|
|
/// </summary>
|
|
protected override string AttributeMetadataName =>
|
|
$"{PathContests.SourceGeneratorsAbstractionsPath}.Bases.PriorityAttribute";
|
|
|
|
/// <summary>
|
|
/// 获取特性的短名称(不包含后缀)
|
|
/// </summary>
|
|
protected override string AttributeShortNameWithoutSuffix => "Priority";
|
|
|
|
/// <summary>
|
|
/// 验证符号是否符合生成条件
|
|
/// </summary>
|
|
protected override bool ValidateSymbol(
|
|
SourceProductionContext context,
|
|
Compilation compilation,
|
|
ClassDeclarationSyntax syntax,
|
|
INamedTypeSymbol symbol,
|
|
AttributeData attr)
|
|
{
|
|
// 1. 必须是 class
|
|
if (symbol.TypeKind != TypeKind.Class)
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
PriorityDiagnostic.OnlyApplyToClass,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.ToDisplayString()));
|
|
return false;
|
|
}
|
|
|
|
// 2. 不支持嵌套类
|
|
if (symbol.ContainingType != null)
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
PriorityDiagnostic.NestedClassNotSupported,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.Name));
|
|
return false;
|
|
}
|
|
|
|
// 3. 必须是 partial
|
|
if (!syntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
PriorityDiagnostic.MustBePartial,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.Name));
|
|
return false;
|
|
}
|
|
|
|
// 4. 检查是否已手动实现 IPrioritized
|
|
var iPrioritized = compilation.GetTypeByMetadataName(
|
|
$"{PathContests.CoreAbstractionsNamespace}.Bases.IPrioritized");
|
|
|
|
if (iPrioritized != null && symbol.AllInterfaces.Contains(iPrioritized, SymbolEqualityComparer.Default))
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
PriorityDiagnostic.AlreadyImplemented,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.Name));
|
|
return false;
|
|
}
|
|
|
|
// 5. 验证特性参数
|
|
if (attr.ConstructorArguments.Length == 0 ||
|
|
attr.ConstructorArguments[0].Value is not int)
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
PriorityDiagnostic.InvalidValue,
|
|
syntax.Identifier.GetLocation()));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成源代码
|
|
/// </summary>
|
|
protected override string Generate(
|
|
SourceProductionContext context,
|
|
Compilation compilation,
|
|
INamedTypeSymbol symbol,
|
|
AttributeData attr)
|
|
{
|
|
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
|
? null
|
|
: symbol.ContainingNamespace.ToDisplayString();
|
|
|
|
var priorityValue = (int)attr.ConstructorArguments[0].Value!;
|
|
|
|
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 typeParameters = symbol.TypeParameters.Length > 0
|
|
? $"<{string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name))}>"
|
|
: string.Empty;
|
|
|
|
sb.AppendLine(
|
|
$"partial class {symbol.Name}{typeParameters} : global::GFramework.Core.Abstractions.Bases.IPrioritized");
|
|
sb.AppendLine("{");
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine($" /// 获取优先级值: {priorityValue}");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine($" public int Priority => {priorityValue};");
|
|
sb.AppendLine("}");
|
|
|
|
return sb.ToString().TrimEnd();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取生成文件的提示名称
|
|
/// </summary>
|
|
protected override string GetHintName(INamedTypeSymbol symbol)
|
|
{
|
|
// 使用完整的元数据名称以避免冲突
|
|
var metadataName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
|
.Replace("global::", "")
|
|
.Replace("<", "_")
|
|
.Replace(">", "_")
|
|
.Replace(",", "_")
|
|
.Replace(" ", "")
|
|
.Replace(".", "_");
|
|
|
|
return $"{metadataName}.Priority.g.cs";
|
|
}
|
|
} |