From 48fd8a22bb5891062e9b4cd2ebc983ff697db136 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 3 Apr 2026 09:25:06 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0AI-First?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=B3=BB=E7=BB=9F=E5=8F=8A=E6=BA=90=E7=94=9F?=
=?UTF-8?q?=E6=88=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现YAML配置文件加载和JSON Schema校验功能
- 提供Source Generator自动生成配置类型和表包装类
- 添加VS Code插件支持配置浏览和表单编辑
- 支持跨表引用校验和开发期热重载功能
- 生成强类型的配置访问辅助方法和注册绑定
- 实现嵌套对象和对象数组的类型安全访问
---
.../SchemaConfigGeneratorSnapshotTests.cs | 30 +++-
.../MonsterConfigBindings.g.txt | 89 +++++++++
.../Config/SchemaConfigGenerator.cs | 170 ++++++++++++++++++
docs/zh-CN/game/config-system.md | 29 +--
4 files changed, 307 insertions(+), 11 deletions(-)
create mode 100644 GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
diff --git a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs
index 561555d7..b02b1307 100644
--- a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs
+++ b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs
@@ -9,7 +9,7 @@ namespace GFramework.SourceGenerators.Tests.Config;
public class SchemaConfigGeneratorSnapshotTests
{
///
- /// 验证一个最小 monster schema 能生成配置类型和表包装。
+ /// 验证一个最小 monster schema 能生成配置类型、表包装和注册辅助。
///
[Test]
public async Task Snapshot_SchemaConfigGenerator()
@@ -35,6 +35,32 @@ public class SchemaConfigGeneratorSnapshotTests
bool ContainsKey(TKey key);
IReadOnlyCollection All();
}
+
+ public interface IConfigRegistry
+ {
+ IConfigTable GetTable(string name)
+ where TKey : notnull;
+
+ bool TryGetTable(string name, out IConfigTable? table)
+ where TKey : notnull;
+ }
+ }
+
+ namespace GFramework.Game.Config
+ {
+ public sealed class YamlConfigLoader
+ {
+ public YamlConfigLoader RegisterTable(
+ string tableName,
+ string relativePath,
+ string schemaRelativePath,
+ Func keySelector,
+ IEqualityComparer? comparer = null)
+ where TKey : notnull
+ {
+ return this;
+ }
+ }
}
""";
@@ -130,6 +156,8 @@ public class SchemaConfigGeneratorSnapshotTests
await AssertSnapshotAsync(generatedSources, snapshotFolder, "MonsterConfig.g.cs", "MonsterConfig.g.txt");
await AssertSnapshotAsync(generatedSources, snapshotFolder, "MonsterTable.g.cs", "MonsterTable.g.txt");
+ await AssertSnapshotAsync(generatedSources, snapshotFolder, "MonsterConfigBindings.g.cs",
+ "MonsterConfigBindings.g.txt");
}
///
diff --git a/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
new file mode 100644
index 00000000..9893f455
--- /dev/null
+++ b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
@@ -0,0 +1,89 @@
+//
+#nullable enable
+
+namespace GFramework.Game.Config.Generated;
+
+///
+/// Auto-generated registration and lookup helpers for schema file 'monster.schema.json'.
+/// The helper centralizes table naming, config directory, schema path, and strong-typed registry access so consumer projects do not need to duplicate the same conventions.
+///
+public static class MonsterConfigBindings
+{
+ ///
+ /// Gets the runtime registration name of the generated config table.
+ ///
+ public const string TableName = "monster";
+
+ ///
+ /// Gets the config directory path expected by the generated registration helper.
+ ///
+ public const string ConfigRelativePath = "monster";
+
+ ///
+ /// Gets the schema file path expected by the generated registration helper.
+ ///
+ public const string SchemaRelativePath = "schemas/monster.schema.json";
+
+ ///
+ /// Registers the generated config table using the schema-derived runtime conventions.
+ ///
+ /// The target YAML config loader.
+ /// Optional key comparer for the generated table registration.
+ /// The same loader instance so registration can keep chaining.
+ public static global::GFramework.Game.Config.YamlConfigLoader RegisterMonsterTable(
+ this global::GFramework.Game.Config.YamlConfigLoader loader,
+ global::System.Collections.Generic.IEqualityComparer? comparer = null)
+ {
+ if (loader is null)
+ {
+ throw new global::System.ArgumentNullException(nameof(loader));
+ }
+
+ return loader.RegisterTable(
+ TableName,
+ ConfigRelativePath,
+ SchemaRelativePath,
+ static config => config.Id,
+ comparer);
+ }
+
+ ///
+ /// Gets the generated config table wrapper from the registry.
+ ///
+ /// The source config registry.
+ /// The generated strong-typed table wrapper.
+ /// When is null.
+ public static MonsterTable GetMonsterTable(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry)
+ {
+ if (registry is null)
+ {
+ throw new global::System.ArgumentNullException(nameof(registry));
+ }
+
+ return new MonsterTable(registry.GetTable(TableName));
+ }
+
+ ///
+ /// Tries to get the generated config table wrapper from the registry.
+ ///
+ /// The source config registry.
+ /// The generated strong-typed table wrapper when lookup succeeds; otherwise null.
+ /// True when the generated table is registered and type-compatible; otherwise false.
+ /// When is null.
+ public static bool TryGetMonsterTable(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry, out MonsterTable? table)
+ {
+ if (registry is null)
+ {
+ throw new global::System.ArgumentNullException(nameof(registry));
+ }
+
+ if (registry.TryGetTable(TableName, out var innerTable) && innerTable is not null)
+ {
+ table = new MonsterTable(innerTable);
+ return true;
+ }
+
+ table = null;
+ return false;
+ }
+}
diff --git a/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs b/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
index 663f5313..6428f624 100644
--- a/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
+++ b/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
@@ -41,6 +41,9 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
productionContext.AddSource(
$"{result.Schema.TableName}.g.cs",
SourceText.From(GenerateTableClass(result.Schema), Encoding.UTF8));
+ productionContext.AddSource(
+ $"{result.Schema.EntityName}ConfigBindings.g.cs",
+ SourceText.From(GenerateBindingsClass(result.Schema), Encoding.UTF8));
});
}
@@ -128,12 +131,18 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
idProperty.TypeSpec.SchemaType));
}
+ var schemaBaseName = GetSchemaBaseName(file.Path);
var schema = new SchemaFileSpec(
Path.GetFileName(file.Path),
+ entityName,
schemaObject.ClassName,
$"{entityName}Table",
GeneratedNamespace,
idProperty.TypeSpec.ClrType.TrimEnd('?'),
+ idProperty.PropertyName,
+ schemaBaseName,
+ schemaBaseName,
+ GetSchemaRelativePath(file.Path),
TryGetMetadataString(root, "title"),
TryGetMetadataString(root, "description"),
schemaObject);
@@ -607,6 +616,131 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
return builder.ToString().TrimEnd();
}
+ ///
+ /// 生成运行时注册与访问辅助源码。
+ /// 该辅助类型把 schema 命名约定、配置目录和 schema 相对路径固化为生成代码,
+ /// 让消费端无需重复手写字符串常量和主键提取逻辑。
+ ///
+ /// 已解析的 schema 模型。
+ /// 辅助类型源码。
+ private static string GenerateBindingsClass(SchemaFileSpec schema)
+ {
+ var registerMethodName = $"Register{schema.EntityName}Table";
+ var getMethodName = $"Get{schema.EntityName}Table";
+ var tryGetMethodName = $"TryGet{schema.EntityName}Table";
+ var bindingsClassName = $"{schema.EntityName}ConfigBindings";
+
+ var builder = new StringBuilder();
+ builder.AppendLine("// ");
+ builder.AppendLine("#nullable enable");
+ builder.AppendLine();
+ builder.AppendLine($"namespace {schema.Namespace};");
+ builder.AppendLine();
+ builder.AppendLine("/// ");
+ builder.AppendLine(
+ $"/// Auto-generated registration and lookup helpers for schema file '{schema.FileName}'.");
+ builder.AppendLine(
+ "/// The helper centralizes table naming, config directory, schema path, and strong-typed registry access so consumer projects do not need to duplicate the same conventions.");
+ builder.AppendLine("/// ");
+ builder.AppendLine($"public static class {bindingsClassName}");
+ builder.AppendLine("{");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Gets the runtime registration name of the generated config table.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ $" public const string TableName = {SymbolDisplay.FormatLiteral(schema.TableRegistrationName, true)};");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Gets the config directory path expected by the generated registration helper.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ $" public const string ConfigRelativePath = {SymbolDisplay.FormatLiteral(schema.ConfigRelativePath, true)};");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Gets the schema file path expected by the generated registration helper.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ $" public const string SchemaRelativePath = {SymbolDisplay.FormatLiteral(schema.SchemaRelativePath, true)};");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(
+ " /// Registers the generated config table using the schema-derived runtime conventions.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// The target YAML config loader.");
+ builder.AppendLine(
+ " /// Optional key comparer for the generated table registration.");
+ builder.AppendLine(" /// The same loader instance so registration can keep chaining.");
+ builder.AppendLine(
+ $" public static global::GFramework.Game.Config.YamlConfigLoader {registerMethodName}(");
+ builder.AppendLine(" this global::GFramework.Game.Config.YamlConfigLoader loader,");
+ builder.AppendLine(
+ $" global::System.Collections.Generic.IEqualityComparer<{schema.KeyClrType}>? comparer = null)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" if (loader is null)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(loader));");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(
+ $" return loader.RegisterTable<{schema.KeyClrType}, {schema.ClassName}>(");
+ builder.AppendLine(" TableName,");
+ builder.AppendLine(" ConfigRelativePath,");
+ builder.AppendLine(" SchemaRelativePath,");
+ builder.AppendLine($" static config => config.{schema.KeyPropertyName},");
+ builder.AppendLine(" comparer);");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Gets the generated config table wrapper from the registry.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// The source config registry.");
+ builder.AppendLine(" /// The generated strong-typed table wrapper.");
+ builder.AppendLine(
+ " /// When is null.");
+ builder.AppendLine(
+ $" public static {schema.TableName} {getMethodName}(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" if (registry is null)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(registry));");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(
+ $" return new {schema.TableName}(registry.GetTable<{schema.KeyClrType}, {schema.ClassName}>(TableName));");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// Tries to get the generated config table wrapper from the registry.");
+ builder.AppendLine(" /// ");
+ builder.AppendLine(" /// The source config registry.");
+ builder.AppendLine(
+ " /// The generated strong-typed table wrapper when lookup succeeds; otherwise null.");
+ builder.AppendLine(
+ " /// True when the generated table is registered and type-compatible; otherwise false.");
+ builder.AppendLine(
+ " /// When is null.");
+ builder.AppendLine(
+ $" public static bool {tryGetMethodName}(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry, out {schema.TableName}? table)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" if (registry is null)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(registry));");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(
+ $" if (registry.TryGetTable<{schema.KeyClrType}, {schema.ClassName}>(TableName, out var innerTable) && innerTable is not null)");
+ builder.AppendLine(" {");
+ builder.AppendLine($" table = new {schema.TableName}(innerTable);");
+ builder.AppendLine(" return true;");
+ builder.AppendLine(" }");
+ builder.AppendLine();
+ builder.AppendLine(" table = null;");
+ builder.AppendLine(" return false;");
+ builder.AppendLine(" }");
+ builder.AppendLine("}");
+ return builder.ToString().TrimEnd();
+ }
+
///
/// 递归生成配置对象类型。
///
@@ -773,6 +907,32 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
return Path.GetFileNameWithoutExtension(fileName);
}
+ ///
+ /// 解析生成注册辅助时要使用的 schema 相对路径。
+ /// 生成器优先保留 `schemas/` 目录以下的相对路径,以便消费端默认约定和 MSBuild AdditionalFiles 约定保持一致。
+ ///
+ /// Schema 文件路径。
+ /// 用于运行时注册的 schema 相对路径。
+ private static string GetSchemaRelativePath(string path)
+ {
+ var normalizedPath = path.Replace('\\', '/');
+ const string rootMarker = "schemas/";
+ const string nestedMarker = "/schemas/";
+
+ if (normalizedPath.StartsWith(rootMarker, StringComparison.OrdinalIgnoreCase))
+ {
+ return normalizedPath;
+ }
+
+ var nestedMarkerIndex = normalizedPath.LastIndexOf(nestedMarker, StringComparison.OrdinalIgnoreCase);
+ if (nestedMarkerIndex >= 0)
+ {
+ return normalizedPath.Substring(nestedMarkerIndex + 1);
+ }
+
+ return $"schemas/{Path.GetFileName(path)}";
+ }
+
///
/// 将 schema 名称转换为 PascalCase 标识符。
///
@@ -996,19 +1156,29 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
/// 生成器级 schema 模型。
///
/// Schema 文件名。
+ /// 实体名基础标识。
/// 根配置类型名。
/// 配置表包装类型名。
/// 目标命名空间。
/// 主键 CLR 类型。
+ /// 生成配置类型中的主键属性名。
+ /// 运行时注册名。
+ /// 配置目录相对路径。
+ /// Schema 文件相对路径。
/// 根标题元数据。
/// 根描述元数据。
/// 根对象模型。
private sealed record SchemaFileSpec(
string FileName,
+ string EntityName,
string ClassName,
string TableName,
string Namespace,
string KeyClrType,
+ string KeyPropertyName,
+ string TableRegistrationName,
+ string ConfigRelativePath,
+ string SchemaRelativePath,
string? Title,
string? Description,
SchemaObjectSpec RootObject);
diff --git a/docs/zh-CN/game/config-system.md b/docs/zh-CN/game/config-system.md
index 64eb7726..c2f1138c 100644
--- a/docs/zh-CN/game/config-system.md
+++ b/docs/zh-CN/game/config-system.md
@@ -12,7 +12,7 @@
- JSON Schema 作为结构描述
- 一对象一文件的目录组织
- 运行时只读查询
-- Source Generator 生成配置类型和表包装
+- Source Generator 生成配置类型、表包装和注册/访问辅助
- VS Code 插件提供配置浏览、raw 编辑、schema 打开、递归轻量校验和嵌套对象表单入口
## 推荐目录结构
@@ -83,27 +83,31 @@ dropItems:
## 运行时接入
-当你希望加载后的配置在运行时以只读表形式暴露时,可以使用 `YamlConfigLoader` 和 `ConfigRegistry`:
+当你希望加载后的配置在运行时以只读表形式暴露时,优先使用生成器产出的注册与访问辅助:
```csharp
using GFramework.Game.Config;
+using GFramework.Game.Config.Generated;
var registry = new ConfigRegistry();
var loader = new YamlConfigLoader("config-root")
- .RegisterTable(
- "monster",
- "monster",
- "schemas/monster.schema.json",
- static config => config.Id);
+ .RegisterMonsterTable();
await loader.LoadAsync(registry);
-var monsterTable = registry.GetTable("monster");
+var monsterTable = registry.GetMonsterTable();
var slime = monsterTable.Get(1);
```
-这个重载会先按 schema 校验,再进行反序列化和注册。
+这组辅助会把以下约定固化到生成代码里:
+
+- 表注册名,例如 `monster`
+- 配置目录相对路径,例如 `monster`
+- schema 相对路径,例如 `schemas/monster.schema.json`
+- 主键提取逻辑,例如 `config => config.Id`
+
+如果你需要自定义目录、表名或 key selector,仍然可以直接调用 `YamlConfigLoader.RegisterTable(...)` 原始重载。
## 运行时校验行为
@@ -187,7 +191,12 @@ var hotReload = loader.EnableHotReload(
## 生成器接入约定
-配置生成器会从 `*.schema.json` 生成配置类型和表包装类。
+配置生成器会从 `*.schema.json` 生成以下代码:
+
+- 配置类型
+- 表包装类型
+- `YamlConfigLoader` 注册辅助
+- `IConfigRegistry` 强类型访问辅助
通过已打包的 Source Generator 使用时,默认会自动收集 `schemas/**/*.schema.json` 作为 `AdditionalFiles`。
From 3bca6390ce1d0c652948783458ae9bff991a105a Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 3 Apr 2026 09:53:51 +0800
Subject: [PATCH 2/3] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0JSON=20sc?=
=?UTF-8?q?hema=E9=85=8D=E7=BD=AE=E7=94=9F=E6=88=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现了根据JSON schema自动生成配置类型和配置表包装的功能
- 支持嵌套对象、对象数组、标量数组的类型生成
- 提供可映射的default/enum/ref-table元数据支持
- 生成强类型的配置表包装器和运行时绑定辅助类
- 实现了完整的schema解析和C#代码生成功能
- 添加了详细的XML文档注释和错误诊断功能
---
.../SchemaConfigGenerator/MonsterConfigBindings.g.txt | 6 +++---
.../Config/SchemaConfigGenerator.cs | 10 +++-------
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
index 9893f455..a2954ce7 100644
--- a/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
+++ b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfigBindings.g.txt
@@ -5,7 +5,7 @@ namespace GFramework.Game.Config.Generated;
///
/// Auto-generated registration and lookup helpers for schema file 'monster.schema.json'.
-/// The helper centralizes table naming, config directory, schema path, and strong-typed registry access so consumer projects do not need to duplicate the same conventions.
+/// The helper centralizes table naming, config directory, schema path, and strongly-typed registry access so consumer projects do not need to duplicate the same conventions.
///
public static class MonsterConfigBindings
{
@@ -51,7 +51,7 @@ public static class MonsterConfigBindings
/// Gets the generated config table wrapper from the registry.
///
/// The source config registry.
- /// The generated strong-typed table wrapper.
+ /// The generated strongly-typed table wrapper.
/// When is null.
public static MonsterTable GetMonsterTable(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry)
{
@@ -67,7 +67,7 @@ public static class MonsterConfigBindings
/// Tries to get the generated config table wrapper from the registry.
///
/// The source config registry.
- /// The generated strong-typed table wrapper when lookup succeeds; otherwise null.
+ /// The generated strongly-typed table wrapper when lookup succeeds; otherwise null.
/// True when the generated table is registered and type-compatible; otherwise false.
/// When is null.
public static bool TryGetMonsterTable(this global::GFramework.Game.Abstractions.Config.IConfigRegistry registry, out MonsterTable? table)
diff --git a/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs b/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
index 6428f624..29d52fdb 100644
--- a/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
+++ b/GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs
@@ -1,7 +1,3 @@
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Text.Json;
using GFramework.SourceGenerators.Diagnostics;
namespace GFramework.SourceGenerators.Config;
@@ -640,7 +636,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
builder.AppendLine(
$"/// Auto-generated registration and lookup helpers for schema file '{schema.FileName}'.");
builder.AppendLine(
- "/// The helper centralizes table naming, config directory, schema path, and strong-typed registry access so consumer projects do not need to duplicate the same conventions.");
+ "/// The helper centralizes table naming, config directory, schema path, and strongly-typed registry access so consumer projects do not need to duplicate the same conventions.");
builder.AppendLine("/// ");
builder.AppendLine($"public static class {bindingsClassName}");
builder.AppendLine("{");
@@ -694,7 +690,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
builder.AppendLine(" /// Gets the generated config table wrapper from the registry.");
builder.AppendLine(" /// ");
builder.AppendLine(" /// The source config registry.");
- builder.AppendLine(" /// The generated strong-typed table wrapper.");
+ builder.AppendLine(" /// The generated strongly-typed table wrapper.");
builder.AppendLine(
" /// When is null.");
builder.AppendLine(
@@ -714,7 +710,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
builder.AppendLine(" /// ");
builder.AppendLine(" /// The source config registry.");
builder.AppendLine(
- " /// The generated strong-typed table wrapper when lookup succeeds; otherwise null.");
+ " /// The generated strongly-typed table wrapper when lookup succeeds; otherwise null.");
builder.AppendLine(
" /// True when the generated table is registered and type-compatible; otherwise false.");
builder.AppendLine(
From 76479eb9f8f22037cb829f1adb851e9b82bfdde7 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 3 Apr 2026 10:04:18 +0800
Subject: [PATCH 3/3] =?UTF-8?q?refactor(GFramework.SourceGenerators):=20?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=A8=E5=B1=80=E5=91=BD=E5=90=8D=E7=A9=BA?=
=?UTF-8?q?=E9=97=B4=E5=BC=95=E7=94=A8=E4=BB=A5=E6=94=AF=E6=8C=81=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 引入 System.Globalization 用于区域设置相关操作
- 添加 System.IO 支持文件输入输出功能
- 集成 System.Text 提供文本处理能力
- 包含 System.Text.Json 用于 JSON 序列化反序列化
---
GFramework.SourceGenerators/GlobalUsings.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/GFramework.SourceGenerators/GlobalUsings.cs b/GFramework.SourceGenerators/GlobalUsings.cs
index a23ec429..f098c130 100644
--- a/GFramework.SourceGenerators/GlobalUsings.cs
+++ b/GFramework.SourceGenerators/GlobalUsings.cs
@@ -20,4 +20,8 @@ global using System.Collections.Immutable;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.CSharp;
-global using Microsoft.CodeAnalysis.Text;
\ No newline at end of file
+global using Microsoft.CodeAnalysis.Text;
+global using System.Globalization;
+global using System.IO;
+global using System.Text;
+global using System.Text.Json;
\ No newline at end of file