1. 重新设计了Generate项目及相关特性的命名,避免与其他类型混淆。

2. 补充了部分注释。
3. 修改了删除容器节点时,容器内子节点未正确删除的问题。
This commit is contained in:
fengjiayi
2025-07-30 21:15:07 +08:00
parent 93148b11a5
commit 152077e9b5
188 changed files with 2713 additions and 1406 deletions

View File

@@ -40,20 +40,25 @@ namespace Serein.Library
/// 标识一个类中的某些字段需要生成相应代码
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class NodePropertyAttribute : Attribute
public sealed class FlowDataPropertyAttribute : Attribute
{
/// <summary>
/// <para>属性路径</para>
/// <para>CustomNode : 自定义节点</para>
/// </summary>
public NodeValuePath ValuePath = NodeValuePath.None;
/// <summary>
/// 表示该类是否为节点实现类
/// </summary>
public bool IsNodeImp = false;
}
/// <summary>
/// 自动生成环境的属性
/// </summary>
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public sealed class PropertyInfoAttribute : Attribute
public sealed class DataInfoAttribute : Attribute
{
/// <summary>
/// 是否通知远程环境(如果在远程环境下)

View File

@@ -4,24 +4,25 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace Serein.Library.NodeGenerator
{
/// <summary>
/// 一个增量源生成器,用于为带有自定义 MyClassAttribute 特性的类中的字段生成带有自定义 set 行为的属性。
/// 增量源生成器
/// </summary>
[Generator]
public class MyPropertyGenerator : IIncrementalGenerator
public class FlowDataPropertyGenerator : IIncrementalGenerator
{
internal static NodePropertyAttribute NodeProperty = new NodePropertyAttribute();
internal static PropertyInfoAttribute PropertyInfo = new PropertyInfoAttribute();
internal static FlowDataPropertyAttribute FlowDataProperty = new FlowDataPropertyAttribute();
internal static DataInfoAttribute DataInfo = new DataInfoAttribute();
/// <summary>
/// 初始化生成器,定义需要执行的生成逻辑。
@@ -29,12 +30,16 @@ namespace Serein.Library.NodeGenerator
/// <param name="context">增量生成器的上下文,用于注册生成逻辑。</param>
public void Initialize(IncrementalGeneratorInitializationContext context)
{
/*
* //Debugger.Launch();
CreateSyntaxProvider :
SemanticModel : (SemanticModel)
AddSource : 使 AddSource
*/
// 通过 SyntaxProvider 查找所有带有任意特性修饰的类声明语法节点
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
// 定义要查找的语法节点类型,这里我们只关心类声明 (ClassDeclarationSyntax) 并且它们有至少一个特性 (Attribute)
@@ -50,7 +55,7 @@ namespace Serein.Library.NodeGenerator
// 检查类的特性列表,看看是否存在 MyClassAttribute
if (classDeclaration.AttributeLists
.SelectMany(attrList => attrList.Attributes)
.Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(NodePropertyAttribute)))
.Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(FlowDataPropertyAttribute)))
{
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); // 获取类的符号
var classInfo = classSymbol.BuildCacheOfClass();
@@ -77,7 +82,7 @@ namespace Serein.Library.NodeGenerator
// 获取类的命名空间和类名
var namespaceName = GetNamespace(classSyntax);
var className = classSyntax.Identifier.Text;
Debug.WriteLine($"Generator Class Name : {className}");
// 生成属性代码
var generatedCode = GenerateProperties(classSyntax, result.classInfo, namespaceName, className);
@@ -93,6 +98,7 @@ namespace Serein.Library.NodeGenerator
/// </summary>
/// <param name="classSyntax">类的语法树节点。</param>
/// <param name="namespaceName">类所在的命名空间。</param>
/// <param name="classInfo">。</param>
/// <param name="className">类的名称。</param>
/// <returns>生成的 C# 属性代码。</returns>
private string GenerateProperties(ClassDeclarationSyntax classSyntax,
@@ -144,9 +150,8 @@ namespace Serein.Library.NodeGenerator
var fieldType = field.Declaration.Type.ToString(); // 获取字段类型
var propertyName = field.ToPropertyName(); // 转为合适的属性名称
var attributeInfo = fieldKV.Value; // 缓存的特性信息
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段
var isVerify = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsVerify), value => bool.Parse(value)); // 是否为保护字段
var isProtection = attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段
var isVerify = attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsVerify), value => bool.Parse(value)); // 是否为保护字段
//sb.AppendLine(leadingTrivia);
sb.AppendLine($" partial void On{propertyName}Changed({fieldType} oldValue, {fieldType} newValue);");
@@ -194,7 +199,7 @@ namespace Serein.Library.NodeGenerator
//sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); ");
sb.AppendLine($" On{propertyName}Changed(value);");
sb.AppendLine($" On{propertyName}Changed(__oldValue, value);");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), value => bool.Parse(value)))
if (attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsPrint), value => bool.Parse(value)))
{
sb.AddCode(5, $"Console.WriteLine({fieldName});");
} // 是否打印
@@ -206,7 +211,7 @@ namespace Serein.Library.NodeGenerator
// NodeValuePath.Method 节点 → 方法描述
// NodeValuePath.Parameter 节点 → 方法描述 → 参数描述
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), value => bool.Parse(value))) // 是否通知
if (attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsNotification), value => bool.Parse(value))) // 是否通知
{
if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点
@@ -250,33 +255,46 @@ namespace Serein.Library.NodeGenerator
}
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 略");
sb.AppendLine(" /// <para>此事件为自动生成</para>");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
var isNodeImp = false;
sb.AppendLine(" protected bool SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
sb.AppendLine(" { ");
sb.AppendLine(" if (Equals(storage, value)) ");
sb.AppendLine(" { ");
sb.AppendLine(" return false; ");
sb.AppendLine(" } ");
sb.AppendLine(" ");
sb.AppendLine(" storage = value; ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" return true; ");
sb.AppendLine(" } ");
if (classInfo.TryGetValue(nameof(FlowDataPropertyAttribute), out var values)
&& values.TryGetValue(nameof(FlowDataPropertyAttribute.IsNodeImp), out object data)
&& bool.TryParse(data.ToString(), out var isNodeImpTemp)
&& isNodeImpTemp)
{
isNodeImp = true;
}
sb.AppendLine(" public void OnPropertyChanged(string propertyName) => ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" ");
sb.AppendLine(" ");
sb.AppendLine(" ");
if (!isNodeImp)
{
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 略");
sb.AppendLine(" /// <para>此事件为自动生成</para>");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
// 生成变量修改
sb.AppendLine(" protected bool SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
sb.AppendLine(" { ");
sb.AppendLine(" if (Equals(storage, value)) ");
sb.AppendLine(" { ");
sb.AppendLine(" return false; ");
sb.AppendLine(" } ");
sb.AppendLine(" ");
sb.AppendLine(" storage = value; ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" return true; ");
sb.AppendLine(" } ");
sb.AppendLine(" public void OnPropertyChanged(string propertyName) => ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" ");
sb.AppendLine(" ");
sb.AppendLine(" ");
}
//sb.AppendLine(" /// <summary> ");
//sb.AppendLine(" /// 略 ");
@@ -302,38 +320,6 @@ namespace Serein.Library.NodeGenerator
}
/*private void ModifyValue(string path, string value)
{
if (string.IsNullOrWhiteSpace(path))
{
return;
}
else if (path.Equals(nameof(MaxChildrenCount), StringComparison.OrdinalIgnoreCase))
{
if (typeof(int) == typeof(string))
{
this.MaxChildrenCount = value;
}
else
{
this.MaxChildrenCount = ConvertHelper.ValueParse<int>(value);
}
}
else if (path.Equals(nameof(Guid), StringComparison.OrdinalIgnoreCase))
{
if (typeof(string) == typeof(string))
{
this.Guid = value;
}
else
{
this.Guid = ConvertHelper.ValueParse<string>(value);
}
}
}*/
/// <summary>
/// 获取类所在的命名空间。
/// </summary>
@@ -347,21 +333,16 @@ namespace Serein.Library.NodeGenerator
}
private void SetterIsProtection()
{
}
private void SetterNotIsProtection()
{
}
}
/// <summary>
/// 扩展方法,用于处理 XML 文档注释中的 summary 标签
/// </summary>
public static class DocumentationCommentExtensions
{
@@ -431,10 +412,18 @@ namespace Serein.Library.NodeGenerator
}
}
/// <summary>
/// MyAttributeResolver
/// </summary>
public static class MyAttributeResolver
{
/// <summary>
/// 构建类的特性缓存信息
/// </summary>
/// <param name="classSymbol"></param>
/// <returns></returns>
public static Dictionary<string, Dictionary<string, object>> BuildCacheOfClass(this INamedTypeSymbol classSymbol)
{
Dictionary<string, Dictionary<string, object>> attributesOfClass = new Dictionary<string, Dictionary<string, object>>();
@@ -452,10 +441,19 @@ namespace Serein.Library.NodeGenerator
{
var key = cata.Key;
var value = cata.Value.Value;
if (nameof(NodePropertyAttribute).Equals(attributeName))
if (nameof(FlowDataPropertyAttribute).Equals(attributeName))
{
string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value);
attributeInfo.Add(key, literal);
if(cata.Key == nameof(FlowDataPropertyAttribute.ValuePath))
{
string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value);
attributeInfo.Add(key, literal);
}
else if (cata.Key == nameof(FlowDataPropertyAttribute.IsNodeImp))
{
string literal = cata.Value.Value.ToString();
attributeInfo.Add(key, literal);
}
}
else
{
@@ -463,8 +461,8 @@ namespace Serein.Library.NodeGenerator
}
//Console.WriteLine("key:" + cata.Key);// 类特性的属性名
//Console.WriteLine("value:" + cata.Value.Value); // 类特性的属性值
Debug.WriteLine("key : " + cata.Key);// 类特性的属性名
Debug.WriteLine("value : " + cata.Value.Value); // 类特性的属性值
}
}
return attributesOfClass;
@@ -632,15 +630,21 @@ namespace Serein.Library.NodeGenerator
}
/// <summary>
/// 检查类信息中是否存在指定的路径
/// </summary>
/// <param name="classInfo"></param>
/// <param name="valuePath"></param>
/// <returns></returns>
public static bool ExitsPath(this Dictionary<string, Dictionary<string, object>> classInfo, string valuePath)
{
if (!classInfo.TryGetValue(nameof(NodePropertyAttribute), out var keyValuePairs))
if (!classInfo.TryGetValue(nameof(FlowDataPropertyAttribute), out var keyValuePairs))
{
return false;
}
if (!keyValuePairs.TryGetValue(nameof(MyPropertyGenerator.NodeProperty.ValuePath), out var value))
if (!keyValuePairs.TryGetValue(nameof(FlowDataPropertyGenerator.FlowDataProperty.ValuePath), out var value))
{
return false;
}

View File

@@ -2,8 +2,9 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.2.1</Version>
<IsRoslynComponent>true</IsRoslynComponent>
<Version>1.2.3</Version>
<IsRoslynComponent>false</IsRoslynComponent><!--控制代码生成器-->
<!--<IsRoslynComponent>true</IsRoslynComponent>-->
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>SereinFow</Title>
@@ -13,6 +14,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>