using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace Serein.Library.NodeGenerator
{
///
/// 一个增量源生成器,用于为带有自定义 MyClassAttribute 特性的类中的字段生成带有自定义 set 行为的属性。
///
[Generator]
public class MyPropertyGenerator : IIncrementalGenerator
{
internal static NodePropertyAttribute NodeProperty = new NodePropertyAttribute();
internal static PropertyInfoAttribute PropertyInfo = new PropertyInfoAttribute();
///
/// 初始化生成器,定义需要执行的生成逻辑。
///
/// 增量生成器的上下文,用于注册生成逻辑。
public void Initialize(IncrementalGeneratorInitializationContext context)
{
/*
CreateSyntaxProvider : 第一个参数用于筛选特定语法节点,第二个参数则用于转换筛选出来的节点。
SemanticModel : 通过 语义模型 (SemanticModel) 来解析代码中的符号信息,获取类、方法、属性等更具体的类型和特性信息。例如某个特性属于哪个类型。
AddSource : 生成器的最终目标是生成代码。使用 AddSource 将生成的代码以字符串形式注入到编译过程当中。通常会通过字符串拼接或 StringBuilder 来构建生成的 C# 代码。
*/
// 通过 SyntaxProvider 查找所有带有任意特性修饰的类声明语法节点
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
// 定义要查找的语法节点类型,这里我们只关心类声明 (ClassDeclarationSyntax) 并且它们有至少一个特性 (Attribute)
(node, _) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0,
// 提供一个函数来进一步分析这些类,并且只返回带有 MyClassAttribute 特性的类声明
(tmpContext, _) =>
{
var classDeclaration = (ClassDeclarationSyntax)tmpContext.Node;
var semanticModel = tmpContext.SemanticModel;
// 检查类的特性列表,看看是否存在 MyClassAttribute
if (classDeclaration.AttributeLists
.SelectMany(attrList => attrList.Attributes)
.Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(NodePropertyAttribute)))
{
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); // 获取类的符号
var classInfo = classSymbol.BuildCacheOfClass();
return (classDeclaration, classInfo);
}
return (null, null);
})
// 过滤掉空结果
.Where(cds => cds.classDeclaration != null);
// 注册一个源生成任务,使用找到的类生成代码
context.RegisterSourceOutput(classDeclarations, (sourceProductionContext, result) =>
{
// 获取 MyDataAttribute 中的 Type 参数,可以获取多个,这里为了记录代码获取了第一个
//var typeArgument = attributeData.ConstructorArguments.FirstOrDefault();
//var dataType = typeArgument.Value as INamedTypeSymbol;
//Console.WriteLine(dataType);
if (result.classDeclaration is ClassDeclarationSyntax classSyntax)
{
// 获取类的命名空间和类名
var namespaceName = GetNamespace(classSyntax);
var className = classSyntax.Identifier.Text;
// 生成属性代码
var generatedCode = GenerateProperties(classSyntax, result.classInfo, namespaceName, className);
// 将生成的代码添加为源文件
sourceProductionContext.AddSource($"{className}.g.cs", SourceText.From(generatedCode, Encoding.UTF8));
}
});
}
private int myProperty;
public int MyProperty { get => myProperty; set
{
if(myProperty == null)
{
myProperty = value;
}
} }
///
/// 为给定的类生成带有自定义 set 行为的属性。
///
///
/// 类的语法树节点。
/// 类所在的命名空间。
/// 类的名称。
/// 生成的 C# 属性代码。
private string GenerateProperties(ClassDeclarationSyntax classSyntax,
Dictionary> classInfo,
string namespaceName,
string className)
{
var sb = new StringBuilder();
// 生成命名空间和类的开始部分
sb.AppendLine($"using System;");
sb.AppendLine($"using System.Linq;");
sb.AppendLine($"using System.Threading;");
sb.AppendLine($"using System.Threading.Tasks;");
sb.AppendLine($"using System.Collections.Concurrent;");
sb.AppendLine($"using System.Collections.Generic;");
sb.AppendLine($"using Serein.Library;");
sb.AppendLine($"using Serein.Library.Api;");
//sb.AppendLine($"using static Serein.Library.Utils.ChannelFlowInterrupt;");
sb.AppendLine($"");
sb.AppendLine($"namespace {namespaceName}");
sb.AppendLine("{");
sb.AppendLine($" public partial class {className} : System.ComponentModel.INotifyPropertyChanged");
sb.AppendLine(" {");
//object path = null ;
//if(classInfo.TryGetValue(nameof(NodePropertyAttribute), out var values))
//{
// values.TryGetValue(nameof(NodePropertyAttribute.ValuePath), out path); // 获取路径
//}
//
//
// "ParameterDetails";
// "MethodDetails";
try
{
var expInfo = MyAttributeResolver.BuildCacheOfField(classSyntax.Members.OfType());
foreach (var fieldKV in expInfo)
{
var field = fieldKV.Key;
if (field.IsReadonly())
{
continue;
}
var leadingTrivia = field.GetLeadingTrivia().InsertSummaryComment("(此属性为自动生成)").ToString(); // 获取注释
var fieldName = field.Declaration.Variables.First().Identifier.Text; // 获取字段名称
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)); // 是否为保护字段
// 生成 getter / setter
sb.AppendLine(leadingTrivia);
sb.AppendLine($" public {fieldType} {propertyName}");
sb.AppendLine(" {");
sb.AppendLine($" get => {fieldName};"); // getter方法
sb.AppendLine(" set");
sb.AppendLine(" {");
sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter
sb.AppendLine(" {");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.CustomCodeAtStart), value => !string.IsNullOrEmpty(value))) // 自定义代码
{
var customCode = attributeInfo[nameof(PropertyInfo)][nameof(PropertyInfo.CustomCodeAtStart)] as string;
customCode = customCode.Trim().Substring(1, customCode.Length - 2);
sb.AddCode(5, $"{customCode} // 添加的自定义代码");
}
sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), value => bool.Parse(value))) // 是否打印
{
sb.AddCode(5, $"Console.WriteLine({fieldName});");
}
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), value => bool.Parse(value))) // 是否通知
{
if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点
{
sb.AddCode(5, $"if (this?.Env?.IsControlRemoteEnv == true) // 正在控制远程环境时才触发");
sb.AddCode(5, $"{{");
sb.AddCode(6, $"this.Env?.NotificationNodeValueChangeAsync(this.Guid, nameof({propertyName}), value); // 通知远程环境属性发生改变了");
sb.AddCode(5, $"}}");
}
else
{
sb.AddCode(5, $"if (NodeModel?.Env?.IsControlRemoteEnv == true) // 正在控制远程环境时才触发");
sb.AddCode(5, $"{{");
if (classInfo.ExitsPath(nameof(NodeValuePath.Method))) // 节点方法详情
{
sb.AddCode(6, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
}
else if (classInfo.ExitsPath(nameof(NodeValuePath.Parameter))) // 节点方法入参参数描述
{
sb.AddCode(6, "NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value); // 通知远程环境属性发生改变了");
}
else if (classInfo.ExitsPath(nameof(NodeValuePath.DebugSetting))) // 节点的调试信息
{
sb.AddCode(6, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
}
sb.AddCode(5, $"}}");
}
}
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.CustomCodeAtEnd), value => !string.IsNullOrEmpty(value))) // 自定义代码
{
var customCode = attributeInfo[nameof(PropertyInfo)][nameof(PropertyInfo.CustomCodeAtEnd)] as string;
customCode = customCode.Trim().Substring(1, customCode.Length - 2);
sb.AddCode(5, $"{customCode} // 添加的自定义代码");
}
//sb.AppendLine($" {fieldName} = value;");
//sb.AppendLine($" OnPropertyChanged(); // 通知UI属性发生改变了");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine(" }"); // 属性的结尾大括号
//if (!isProtection && field.TryGetDefaultValue(out var defaultValue))
//{
// sb.AppendLine($" }} = {defaultValue}");
//}
}
sb.AppendLine(" /// ");
sb.AppendLine(" /// 略");
sb.AppendLine(" /// 此事件为自动生成");
sb.AppendLine(" /// ");
sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
sb.AppendLine(" protected void SetProperty(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; ");
sb.AppendLine(" //} ");
sb.AppendLine(" ");
sb.AppendLine(" storage = value; ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" } ");
//sb.AppendLine(" /// ");
//sb.AppendLine(" /// 略 ");
//sb.AppendLine(" /// 此方法为自动生成 ");
//sb.AppendLine(" /// ");
//sb.AppendLine(" /// ");
//sb.AppendLine(" ");
//sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
//sb.AppendLine(" {");
//sb.AppendLine(" Console.WriteLine(\"测试:\"+ propertyName);");
//sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
//sb.AppendLine(" }");
}
finally
{
// 生成类的结束部分
sb.AppendLine(" }"); // 类的结尾大括号
sb.AppendLine("}"); // 命名空间的结尾大括号
}
return sb.ToString(); // 返回生成的代码
}
///
/// 获取类所在的命名空间。
///
/// 类的语法节点。
/// 命名空间的名称,或者 "GlobalNamespace" 如果没有命名空间声明。
private string GetNamespace(SyntaxNode classSyntax)
{
// 查找最近的命名空间声明
var namespaceDeclaration = classSyntax.Ancestors().OfType().FirstOrDefault();
return namespaceDeclaration?.Name.ToString() ?? "GlobalNamespace";
}
private void SetterIsProtection()
{
}
private void SetterNotIsProtection()
{
}
}
public static class DocumentationCommentExtensions
{
///
/// 为 XML 文档注释中的 标签插入指定的文本
///
/// 语法节点的 LeadingTrivia 或 TrailingTrivia 列表
/// 要插入的注释文本
/// 修改后的 Trivia 列表
public static SyntaxTriviaList InsertSummaryComment(this SyntaxTriviaList triviaList, string comment)
{
var docCommentTrivia = triviaList.FirstOrDefault(trivia =>
trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ||
trivia.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia));
if (docCommentTrivia.HasStructure)
{
//var structuredTrivia = docCommentTrivia.GetStructure();
var structuredTrivia = docCommentTrivia.GetStructure() as StructuredTriviaSyntax;
// 查找 标签
var summaryNode = structuredTrivia.DescendantNodes()
.OfType()
.FirstOrDefault(e => e.StartTag.Name.LocalName.Text == "summary");
if (summaryNode != null)
{
//// 在 标签内插入指定的注释文本
//var generatorComment = SyntaxFactory.XmlText(comment).WithLeadingTrivia(SyntaxFactory.Whitespace(" "));
//var updatedSummaryNode = summaryNode.AddContent(generatorComment);
//// 用新的 标签替换原来的
//var updatedStructuredTrivia = structuredTrivia.ReplaceNode(summaryNode, updatedSummaryNode);
//// 用新的注释替换原来的
//var updatedTrivia = SyntaxFactory.Trivia(updatedStructuredTrivia);
//triviaList = triviaList.Replace(docCommentTrivia, updatedTrivia);
// 创建 段落注释
var paraElement = SyntaxFactory.XmlElement(
SyntaxFactory.XmlElementStartTag(SyntaxFactory.XmlName("para")), // 起始标签
SyntaxFactory.SingletonList( // 内容
SyntaxFactory.XmlText(comment).WithLeadingTrivia(SyntaxFactory.Whitespace(" "))
),
SyntaxFactory.XmlElementEndTag(SyntaxFactory.XmlName("para")) // 结束标签
);
// 将 插入到 中
var updatedSummaryNode = summaryNode.AddContent(paraElement);
// 用新的 标签替换原来的
var updatedStructuredTrivia = structuredTrivia.ReplaceNode(summaryNode, updatedSummaryNode);
// 用新的注释替换原来的 (确保转换为 StructuredTriviaSyntax 类型)
var updatedTrivia = SyntaxFactory.Trivia(updatedStructuredTrivia);
triviaList = triviaList.Replace(docCommentTrivia, updatedTrivia);
}
}
return triviaList;
}
}
public static class MyAttributeResolver
{
public static Dictionary> BuildCacheOfClass(this INamedTypeSymbol classSymbol)
{
Dictionary> attributesOfClass = new Dictionary>();
var tattribute = classSymbol.GetAttributes();
foreach (var cad in tattribute)
{
var attributeName = cad.AttributeClass?.Name;
if (!attributesOfClass.TryGetValue(attributeName, out var attributeInfo))
{
attributeInfo = new Dictionary();
attributesOfClass.Add(attributeName, attributeInfo);
}
foreach (var cata in cad.NamedArguments)
{
var key = cata.Key;
var value = cata.Value.Value;
if (nameof(NodePropertyAttribute).Equals(attributeName))
{
string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value);
attributeInfo.Add(key, literal);
}
else
{
attributeInfo.Add(key, value);
}
//Console.WriteLine("key:" + cata.Key);// 类特性的属性名
//Console.WriteLine("value:" + cata.Value.Value); // 类特性的属性值
}
}
return attributesOfClass;
}
///
/// 字段名称转换为属性名称
///
///
/// 遵循属性命名规范的新名称
public static string ToPropertyName(this FieldDeclarationSyntax field)
{
var fieldName = field.Declaration.Variables.First().Identifier.Text;
var propertyName = fieldName.StartsWith("_") ? char.ToUpper(fieldName[1]) + fieldName.Substring(2) : char.ToUpper(fieldName[0]) + fieldName.Substring(1); // 创建属性名称
return propertyName;
}
///
/// 判断字段是否有默认值
///
///
///
///
public static bool TryGetDefaultValue(this FieldDeclarationSyntax field ,out string defaultValue)
{
if (field.Declaration.Variables.First().Initializer != null)
{
defaultValue = field.Declaration.Variables.First().Initializer.Value.ToString();
return true;
}
else
{
defaultValue = null;
return false;
}
}
///
/// 判断字段是否为只读
///
/// 字段的语法节点
/// 如果字段是只读的,返回 true;否则返回 false
public static bool IsReadonly(this FieldDeclarationSyntax fieldDeclaration)
{
// 判断字段是否有 readonly 修饰符
return fieldDeclaration.Modifiers.Any(SyntaxKind.ReadOnlyKeyword);
}
///
/// 构建字段的缓存信息
/// 第1层:字段名称 - 特性集合
/// 第2层:特性名称 - 特性属性集合
/// 第3层:特性属性名称 - 对应的字面量
///
///
/// 关于字段的特性缓存信息
public static Dictionary>> BuildCacheOfField(IEnumerable fieldDeclarationSyntaxes)
{
Dictionary>> FieldData = new Dictionary>>();
foreach (var field in fieldDeclarationSyntaxes)
{
// 获取字段名称和类型
var variable = field.Declaration.Variables.First();
var fieldName = variable.Identifier.Text;
var fieldType = field.Declaration.Type.ToString();
var attributeInfo = new Dictionary>(); // 发现一个新字段
FieldData.Add(field, attributeInfo);
var attributes = field.AttributeLists;
foreach (var attributeList in attributes)
{
// 解析特性参数
foreach (var attribute in attributeList.Attributes)
{
var attributeName = attribute.Name.ToString(); // 特性名称
var arguments = attribute.ArgumentList?.Arguments;
if (arguments == null)
{
continue;
}
var attributeValue = new Dictionary();
attributeInfo.Add(attributeName, attributeValue); // 找到特性
// 解析命名属性
foreach (var argument in arguments)
{
// Console.WriteLine($" - Constructor Argument: {argument.ToString()}");
if (argument is AttributeArgumentSyntax attributeArgument && attributeArgument.NameEquals != null)
{
var propertyName = attributeArgument.NameEquals.Name.ToString();
var propertyValue = attributeArgument.Expression.ToString();
attributeValue.Add(propertyName, propertyValue); // 记录属性
}
}
}
}
}
return FieldData;
}
///
/// 通过条件检查缓存的信息,决定是否添加代码
/// 首先检查是否存在该特性,如果不存在,返回 false。
/// 然后检查是否存在属性,如果不存在,返回 false
/// 如果存在属性,则返回属性对应的值与 comparisonValue 进行比较,返回
///
/// 若只传入 attributeName 参数,则只会检查是否存在该特性
/// 若只传入 attributeName与attributePropertyName 参数,则只会检查是否存在该特性的该属性
///
/// 缓存的特性信息
/// 查询的特性名称
/// 查询的特性属性名称
/// 比较值
/// 如果存在查询项,返回 true ,否则返回 false
public static bool Search(this Dictionary> dict,
string attributeName = null,
string attributePropertyName = null,
Func judgeFunc = null)
{
if (string.IsNullOrWhiteSpace(attributeName))
return false;
if (!dict.TryGetValue(attributeName, out var abs))
return false;
if (string.IsNullOrWhiteSpace(attributePropertyName))
return true;
if (!abs.TryGetValue(attributePropertyName, out var absValue))
return false;
if (judgeFunc == null)
return true;
return judgeFunc.Invoke(absValue); ;
//return absValue.Equals(comparisonValue);
}
///
/// 添加代码
///
/// 字符串构建器
/// 缩进次数(4个空格)
/// 要添加的代码
/// 字符串构建器本身
public static StringBuilder AddCode(this StringBuilder sb,
int retractCount = 0,
string code = null)
{
if (!string.IsNullOrWhiteSpace(code))
{
var retract = new string(' ', retractCount * 4);
sb.AppendLine(retract + code);
}
return sb;
}
public static bool ExitsPath(this Dictionary> classInfo, string valuePath)
{
if (!classInfo.TryGetValue(nameof(NodePropertyAttribute), out var keyValuePairs))
{
return false;
}
if (!keyValuePairs.TryGetValue(nameof(MyPropertyGenerator.NodeProperty.ValuePath), out var value))
{
return false;
}
return value.Equals(valuePath);
}
}
}