diff --git a/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md b/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md
index cb8e1c2..58de45e 100644
--- a/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md
+++ b/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md
@@ -3,7 +3,6 @@
### New Rules
- Rule ID | Category | Severity | Notes
-------------|--------------------------|----------|------------------------
- GFLOG001 | GFramework.Godot.Logging | Error | GodotLoggerDiagnostics
- GFW_LOG001 | GFramework.Godot.Logging | Warning | GodotLoggerDiagnostics
\ No newline at end of file
+ Rule ID | Category | Severity | Notes
+----------------------|--------------------------|----------|------------------------
+ GF_Godot_Logging_001 | GFramework.Godot.Logging | Warning | GodotLoggerDiagnostics
\ No newline at end of file
diff --git a/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj b/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj
index cc6e2c8..fd688e5 100644
--- a/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj
+++ b/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj
@@ -23,15 +23,11 @@
-
-
-
-
-
+
@@ -41,4 +37,8 @@
+
+
+
+
diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerDiagnostic.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerDiagnostic.cs
index 66a046d..740b285 100644
--- a/GFramework.Godot.SourceGenerators/logging/GodotLoggerDiagnostic.cs
+++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerDiagnostic.cs
@@ -7,25 +7,6 @@ namespace GFramework.Godot.SourceGenerators.logging;
///
internal static class GodotLoggerDiagnostics
{
- ///
- /// 诊断描述符:标识使用[GodotLog]特性的类必须声明为partial
- ///
- ///
- /// ID: GFLOG001
- /// 严重性: Error
- /// 分类: GFramework.Godot.Logging
- ///
- public static readonly DiagnosticDescriptor MustBePartial =
- new(
- "GFLOG001",
- "Class must be partial",
- "Class '{0}' must be declared as partial to use [GodotLog]",
- "GFramework.Godot.Logging",
- DiagnosticSeverity.Error,
- true
- );
-
-
///
/// 诊断描述符:标识GodotLogAttribute无法在指定类上生成Logger
///
@@ -35,7 +16,7 @@ internal static class GodotLoggerDiagnostics
/// 分类: GFramework.Godot.Logging
///
public static readonly DiagnosticDescriptor LogAttributeInvalid = new(
- "GFW_LOG001",
+ "GF_Godot_Logging_001",
"GodotLogAttribute cannot generate Logger",
"GodotLogAttribute on class '{0}' is ineffective: {1}",
"GFramework.Godot.Logging",
diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs
index 89b04e4..db47829 100644
--- a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs
+++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text;
+using GFramework.SourceGenerators.Common.diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -63,7 +64,7 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator
if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
{
spc.ReportDiagnostic(Diagnostic.Create(
- GodotLoggerDiagnostics.MustBePartial,
+ CommonDiagnostics.ClassMustBePartial,
classDecl.Identifier.GetLocation(),
classSymbol.Name));
diff --git a/GFramework.SourceGenerators.Attributes/rule/ContextAwareAttribute.cs b/GFramework.SourceGenerators.Attributes/rule/ContextAwareAttribute.cs
new file mode 100644
index 0000000..d1060d0
--- /dev/null
+++ b/GFramework.SourceGenerators.Attributes/rule/ContextAwareAttribute.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace GFramework.SourceGenerators.Attributes.rule;
+
+///
+/// 标记该类需要自动实现 IContextAware
+///
+[AttributeUsage(AttributeTargets.Class, Inherited = false)]
+public sealed class ContextAwareAttribute : Attribute
+{
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators.Common/AnalyzerReleases.Shipped.md b/GFramework.SourceGenerators.Common/AnalyzerReleases.Shipped.md
new file mode 100644
index 0000000..60b59dd
--- /dev/null
+++ b/GFramework.SourceGenerators.Common/AnalyzerReleases.Shipped.md
@@ -0,0 +1,3 @@
+; Shipped analyzer releases
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
diff --git a/GFramework.SourceGenerators.Common/AnalyzerReleases.Unshipped.md b/GFramework.SourceGenerators.Common/AnalyzerReleases.Unshipped.md
new file mode 100644
index 0000000..4586830
--- /dev/null
+++ b/GFramework.SourceGenerators.Common/AnalyzerReleases.Unshipped.md
@@ -0,0 +1,8 @@
+; Unshipped analyzer release
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+
+ Rule ID | Category | Severity | Notes
+---------|-------------------|----------|-------------------
+ GFC001 | GFramework.Common | Error | CommonDiagnostics
\ No newline at end of file
diff --git a/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
new file mode 100644
index 0000000..20d6f77
--- /dev/null
+++ b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GFramework.SourceGenerators.Common/diagnostics/CommonDiagnostics.cs b/GFramework.SourceGenerators.Common/diagnostics/CommonDiagnostics.cs
new file mode 100644
index 0000000..9c0cadc
--- /dev/null
+++ b/GFramework.SourceGenerators.Common/diagnostics/CommonDiagnostics.cs
@@ -0,0 +1,29 @@
+using Microsoft.CodeAnalysis;
+
+namespace GFramework.SourceGenerators.Common.diagnostics;
+
+///
+/// 提供通用诊断描述符的静态类
+///
+public static class CommonDiagnostics
+{
+ ///
+ /// 定义类必须为partial的诊断描述符
+ ///
+ ///
+ /// 诊断ID: GF001
+ /// 诊断消息: "Class '{0}' must be declared partial for code generation"
+ /// 分类: GFramework.Common
+ /// 严重性: Error
+ /// 是否启用: true
+ ///
+ public static readonly DiagnosticDescriptor ClassMustBePartial =
+ new(
+ "GFC001",
+ "Class must be partial",
+ "Class '{0}' must be declared partial for code generation",
+ "GFramework.Common",
+ DiagnosticSeverity.Error,
+ true
+ );
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
index 88d2578..0ba5463 100644
--- a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
+++ b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
@@ -3,7 +3,6 @@
### New Rules
-| Rule ID | Category | Severity | Notes |
-|------------|--------------------|----------|-------------|
-| GFLOG001 | GFramework.Logging | Error | Diagnostics |
-| GFW_LOG001 | GFramework.Logging | Warning | Diagnostics |
\ No newline at end of file
+ Rule ID | Category | Severity | Notes
+----------------|--------------------------|----------|-------------------
+ GF_Logging_001 | GFramework.Godot.Logging | Warning | LoggerDiagnostics
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj b/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj
index c0ed0f2..7cc61f7 100644
--- a/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj
+++ b/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj
@@ -16,6 +16,7 @@
true
Generated
true
+ enable
@@ -27,6 +28,7 @@
+
diff --git a/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs b/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
index 8448556..8a2d945 100644
--- a/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
+++ b/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
@@ -7,28 +7,12 @@ namespace GFramework.SourceGenerators.logging;
///
internal static class LoggerDiagnostics
{
- ///
- /// 定义诊断描述符:要求使用[Log]特性的类必须声明为partial
- ///
- ///
- /// 当类使用[Log]特性但未声明为partial时,编译器将报告此错误
- ///
- public static readonly DiagnosticDescriptor MustBePartial =
- new(
- "GFLOG001",
- "Class must be partial",
- "Class '{0}' must be declared partial to use [Log]",
- "GFramework.Logging",
- DiagnosticSeverity.Error,
- true
- );
-
///
/// 定义诊断描述符:LogAttribute无法生成Logger的错误情况
///
public static readonly DiagnosticDescriptor LogAttributeInvalid =
new(
- "GFW_LOG001",
+ "GF_Logging_001",
"LogAttribute cannot generate Logger",
"LogAttribute on class '{0}' is ineffective: {1}",
"GFramework.Godot.Logging",
diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs
index d4899d9..b298ce5 100644
--- a/GFramework.SourceGenerators/logging/LoggerGenerator.cs
+++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Text;
+using GFramework.SourceGenerators.Common.diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -64,7 +65,7 @@ public sealed class LoggerGenerator : IIncrementalGenerator
if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
{
spc.ReportDiagnostic(Diagnostic.Create(
- LoggerDiagnostics.MustBePartial,
+ CommonDiagnostics.ClassMustBePartial,
classDecl.Identifier.GetLocation(),
classSymbol.Name));
diff --git a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
new file mode 100644
index 0000000..0fd1cb7
--- /dev/null
+++ b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
@@ -0,0 +1,114 @@
+using System.Linq;
+using System.Text;
+using GFramework.SourceGenerators.Common.diagnostics;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace GFramework.SourceGenerators.rule;
+
+[Generator]
+public sealed class ContextAwareGenerator : IIncrementalGenerator
+{
+ private const string AttributeMetadataName =
+ "GFramework.SourceGenerators.Attributes.rule.ContextAwareAttribute";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // 1. 找到所有 class 声明
+ var classDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (node, _) => node is ClassDeclarationSyntax,
+ transform: static (ctx, _) => GetCandidate(ctx)
+ )
+ .Where(static s => s is not null);
+
+ // 2. 生成代码
+ context.RegisterSourceOutput(
+ classDeclarations,
+ static (spc, source) => Generate(spc, source!)
+ );
+ }
+
+ private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext context)
+ {
+ var classDecl = (ClassDeclarationSyntax)context.Node;
+
+ if (classDecl.AttributeLists.Count == 0)
+ return null;
+
+ if (context.SemanticModel.GetDeclaredSymbol(classDecl)
+ is not { } symbol)
+ return null;
+
+ return Enumerable.Any(symbol.GetAttributes(),
+ attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName)
+ ? symbol
+ : null;
+ }
+
+
+ private static void Generate(
+ SourceProductionContext context,
+ INamedTypeSymbol symbol)
+ {
+ var syntax = symbol.DeclaringSyntaxReferences
+ .FirstOrDefault()?
+ .GetSyntax() as ClassDeclarationSyntax;
+
+ if (syntax is null || !syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
+ {
+ context.ReportDiagnostic(
+ Diagnostic.Create(
+ CommonDiagnostics.ClassMustBePartial,
+ syntax?.Identifier.GetLocation(),
+ symbol.Name
+ )
+ );
+ return;
+ }
+
+ var ns = symbol.ContainingNamespace.IsGlobalNamespace
+ ? null
+ : symbol.ContainingNamespace.ToDisplayString();
+
+ var source = GenerateSource(ns, symbol);
+
+ context.AddSource(
+ $"{symbol.Name}.ContextAware.g.cs",
+ source
+ );
+ }
+
+ private static string GenerateSource(string? ns, INamedTypeSymbol symbol)
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("// ");
+ sb.AppendLine("#nullable enable");
+
+ if (ns is not null)
+ {
+ sb.AppendLine($"namespace {ns};");
+ sb.AppendLine();
+ }
+
+ sb.AppendLine($"partial class {symbol.Name} : GFramework.Core.rule.IContextAware");
+ sb.AppendLine("{");
+
+ sb.AppendLine(
+ " protected GFramework.Core.architecture.IArchitectureContext Context { get; private set; } = null!;");
+
+ sb.AppendLine("""
+ void GFramework.Core.rule.IContextAware.SetContext(
+ GFramework.Core.architecture.IArchitectureContext context)
+ {
+ Context = context;
+ }
+ """);
+
+ sb.AppendLine("}");
+
+ return sb.ToString();
+ }
+}
\ No newline at end of file
diff --git a/GFramework.csproj b/GFramework.csproj
index 5a873a8..286960a 100644
--- a/GFramework.csproj
+++ b/GFramework.csproj
@@ -44,6 +44,7 @@
+
@@ -68,6 +69,7 @@
+
@@ -78,5 +80,13 @@
+
+
+
+
+
+
+
+
diff --git a/GFramework.sln b/GFramework.sln
index a1f30d9..ea86a8c 100644
--- a/GFramework.sln
+++ b/GFramework.sln
@@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGene
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGenerators.Attributes", "GFramework.Godot.SourceGenerators.Attributes\GFramework.Godot.SourceGenerators.Attributes.csproj", "{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators.Common", "GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj", "{3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -54,5 +56,9 @@ Global
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3DB57A3A-ACCF-47BE-A17B-2ADD68B6C8AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/GFramework.sln.DotSettings.user b/GFramework.sln.DotSettings.user
index c18f938..56537ed 100644
--- a/GFramework.sln.DotSettings.user
+++ b/GFramework.sln.DotSettings.user
@@ -1,2 +1,3 @@
+ ForceIncluded
ForceIncluded
\ No newline at end of file