XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension

【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEExtension

change-配置文件自动实现:现在配置文件有实例了,外部静态访问内部实例数据,添加了属性Get、Set通知的部分声明方法 add-新增文件路径管理自动实现:现在,不必为了担心文件夹路径不存在而烦恼了,现在管理器会自动创建文件夹

183a480
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

12 个文件 +366 -100
Added XFEExtension.NetCore.Analyzer.Test/AppPath.cs +9 -0
@@ -0,0 +1,9 @@
1 using XFEExtension.NetCore.PathExtension;
2
3 namespace XFEExtension.NetCore.Analyzer.Test;
4
5 public partial class AppPath
6 {
7 [AutoPath]
8 readonly string myTestPath = "MyTestPath/Test";
9 }
Modified XFEExtension.NetCore.Analyzer.Test/Program.cs +4 -4
@@ -1,5 +1,4 @@
1 using System.Diagnostics;
2 using XFEExtension.NetCore.StringExtension;
1 using XFEExtension.NetCore.FileExtension;
3 2
4 3 namespace XFEExtension.NetCore.Analyzer.Test;
5 4
@@ -12,8 +11,9 @@ internal class Program
12 11 Tags = [["123", "321"], ["1234567", "7654321"]],
13 12 Enum = MyEnum.Test1
14 13 };
15 var process = new Process();
16 process.X();
14 Console.WriteLine(SystemProfile.Age);
15 Console.WriteLine(SystemProfile.Name);
16 SystemProfile.Name.WriteIn(AppPath.MyTestPath + "/test1.txt");
17 17 }
18 18 }
19 19 enum MyEnum
Modified XFEExtension.NetCore.Analyzer.Test/SystemProfile.cs +4 -2
@@ -2,8 +2,10 @@
2 2
3 3 namespace XFEExtension.NetCore.Analyzer.Test;
4 4
5 public static partial class SystemProfile
5 public partial class SystemProfile
6 6 {
7 7 [ProfileProperty]
8 private static int _age;
8 int _age = 1;
9 [ProfileProperty]
10 string name = "12";
9 11 }
Modified XFEExtension.NetCore.Analyzer/CodeFix/ProfileExtensionCodeFixProvider.cs +2 -2
@@ -49,7 +49,7 @@ namespace XFEExtension.NetCore.Analyzer.CodeFix
49 49 var root = await document.GetSyntaxRootAsync(c);
50 50 var fieldDeclaration = root.FindToken(sourceSpan.Start).Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().First();
51 51 var fieldName = fieldDeclaration.Declaration.Variables.First().Identifier.ValueText;
52 var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddGet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"return {fieldName}"))));
52 var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddGet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"return Current.{fieldName}"))));
53 53 var newRoot = root.ReplaceNode(fieldDeclaration, fieldDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(newAttribute))));
54 54 return document.WithSyntaxRoot(newRoot);
55 55 }
@@ -59,7 +59,7 @@ namespace XFEExtension.NetCore.Analyzer.CodeFix
59 59 var root = await document.GetSyntaxRootAsync(c);
60 60 var fieldDeclaration = root.FindToken(sourceSpan.Start).Parent.AncestorsAndSelf().OfType<FieldDeclarationSyntax>().First();
61 61 var fieldName = fieldDeclaration.Declaration.Variables.First().Identifier.ValueText;
62 var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddSet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"{fieldName} = value"))));
62 var newAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("ProfilePropertyAddSet")).AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal($"Current.{fieldName} = value"))));
63 63 var newRoot = root.ReplaceNode(fieldDeclaration, fieldDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(newAttribute))));
64 64 return document.WithSyntaxRoot(newRoot);
65 65 }
Added XFEExtension.NetCore.Analyzer/Generator/PathPropertyAutoGenerator.cs +168 -0
@@ -0,0 +1,168 @@
1 using Microsoft.CodeAnalysis;
2 using Microsoft.CodeAnalysis.CSharp;
3 using Microsoft.CodeAnalysis.CSharp.Syntax;
4 using System.Collections.Generic;
5 using System.Linq;
6
7 namespace XFEExtension.NetCore.Analyzer.Generator
8 {
9 [Generator]
10 public class PathPropertyAutoGenerator : ISourceGenerator
11 {
12 public void Initialize(GeneratorInitializationContext context)
13 {
14 }
15
16 public void Execute(GeneratorExecutionContext context)
17 {
18 var syntaxTrees = context.Compilation.SyntaxTrees;
19 foreach (var syntaxTree in syntaxTrees)
20 {
21 var root = syntaxTree.GetRoot();
22 var classDeclarations = GetClassDeclarations(root);
23 var fileScopedNamespaceDeclarationSyntax = GetFileScopedNamespaceDeclaration(root);
24 foreach (var classDeclaration in classDeclarations)
25 {
26 var fieldDeclarationSyntaxes = GetFieldDeclarations(classDeclaration);
27 if (fieldDeclarationSyntaxes is null || !fieldDeclarationSyntaxes.Any())
28 {
29 continue;
30 }
31 var className = classDeclaration.Identifier.ValueText;
32 var properties = new List<PropertyDeclarationSyntax>();
33 foreach (var fieldDeclarationSyntax in fieldDeclarationSyntaxes)
34 {
35 var variableDeclaration = fieldDeclarationSyntax.Declaration.Variables.First();
36 var fieldName = variableDeclaration.Identifier.Text;
37 var propertyName = fieldName[0] == '_' ? fieldName[1].ToString().ToUpper() + fieldName.Substring(2) : fieldName[0].ToString().ToUpper() + fieldName.Substring(1);
38 var enableCheckPropertyName = $"{propertyName}EnableCheck";
39 GetAutoPathAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
40 {
41 if (attribute.ArgumentList is null)
42 {
43 return;
44 }
45 var argument = attribute.ArgumentList.Arguments.First();
46 if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax)
47 {
48 propertyName = literalExpressionSyntax.Token.ValueText;
49 }
50 });
51 var propertyType = fieldDeclarationSyntax.Declaration.Type;
52 var triviaText = $@"/// <inheritdoc cref=""{fieldName}""/>
53 /// <remarks>
54 /// <seealso cref=""{propertyName}""/> 是根据 <seealso cref=""{fieldName}""/> 自动生成的路径属性<br/><br/>
55 /// </remarks>
56 ";
57 var checkEnableTriviaText = $@"/// <summary>
58 /// 是否为 <seealso cref=""{fieldName}""/> 启用检测路径<br/><br/>
59 /// </summary>
60 ";
61 var property = SyntaxFactory.PropertyDeclaration(propertyType, propertyName)
62 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
63 .WithAccessorList(SyntaxFactory.AccessorList(
64 SyntaxFactory.List(new[]
65 {
66 SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
67 .WithBody(SyntaxFactory.Block(
68 SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"global::XFEExtension.NetCore.PathExtension.XFEAutoPath.CheckPathExistAndCreate(Options.{fieldName}, Options.{enableCheckPropertyName})")),
69 SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression($"Options.{fieldName}"))))
70 })))
71 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText));
72 var enableCheckProperty = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName("bool"), enableCheckPropertyName)
73 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
74 .WithAccessorList(SyntaxFactory.AccessorList(
75 SyntaxFactory.List(new[]
76 {
77 SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
78 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
79 })))
80 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(checkEnableTriviaText))
81 .WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("true")))
82 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
83 properties.Add(property.NormalizeWhitespace());
84 properties.Add(enableCheckProperty.NormalizeWhitespace());
85 }
86 var profileClassSyntaxTree = GeneratePathClassSyntaxTree(classDeclaration, properties, fileScopedNamespaceDeclarationSyntax);
87 context.AddSource($"{className}.g.cs", profileClassSyntaxTree.ToString());
88 }
89 }
90 }
91
92 public static bool IsAutoPathAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "AutoPath");
93
94 public static List<AttributeSyntax> GetAutoPathAttributeList(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsAutoPathAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
95
96 public static FileScopedNamespaceDeclarationSyntax GetFileScopedNamespaceDeclaration(SyntaxNode rootNode)
97 {
98 var namespaceResults = rootNode.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
99 if (namespaceResults != null && namespaceResults.Count() > 0)
100 return namespaceResults.First();
101 return null;
102 }
103
104 public static IEnumerable<FieldDeclarationSyntax> GetFieldDeclarations(ClassDeclarationSyntax classDeclaration) => classDeclaration.DescendantNodes()
105 .OfType<FieldDeclarationSyntax>()
106 .Where(fieldDeclarationSyntax => fieldDeclarationSyntax.AttributeLists.Any(IsAutoPathAttribute) && !fieldDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword));
107
108 public static IEnumerable<ClassDeclarationSyntax> GetClassDeclarations(SyntaxNode rootNode) => rootNode.DescendantNodes()
109 .OfType<ClassDeclarationSyntax>()
110 .Where(classDeclaration => classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword));
111
112 private static SyntaxTree GeneratePathClassSyntaxTree(ClassDeclarationSyntax classDeclaration, IEnumerable<PropertyDeclarationSyntax> propertyDeclarationSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
113 {
114 var className = classDeclaration.Identifier.ValueText;
115 var triviaText = $@"/// <remarks>
116 /// <code><seealso cref=""{className}""/> 已生成以下路径:</code><br/>
117 /// <code>
118 ";
119 triviaText += string.Join("<br/>\n", propertyDeclarationSyntaxes.Select(propertyDeclarationSyntax => $"/// ○ <seealso cref=\"{propertyDeclarationSyntax.Identifier}\"/>")) + "\n/// </code><br/>\n/// <code>来自<seealso cref=\"global::XFEExtension.NetCore.ProfileExtension.XFEProfile\"/></code>\n/// </remarks>\n";
120 var memberDeclarations = new List<MemberDeclarationSyntax>()
121 {
122 SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(className), "Options")
123 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
124 .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileInstanceAttribute")))))
125 .WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(
126 new[]
127 {
128 SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
129 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
130 SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
131 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
132 })))
133 .WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression($"new {className}()")))
134 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
135 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
136 /// 配置选项<br/>
137 /// <seealso cref=""Current""/> 是 <seealso cref=""{className}""/> 类的配置选项
138 /// </summary>
139 "))
140 };
141 memberDeclarations.AddRange(propertyDeclarationSyntaxes);
142 var pathClass = SyntaxFactory.ClassDeclaration(className)
143 .AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword))
144 .AddMembers(memberDeclarations.ToArray())
145 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText))
146 .NormalizeWhitespace();
147 MemberDeclarationSyntax memberDeclaration;
148 if (fileScopedNamespaceDeclarationSyntax is null)
149 {
150 var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
151 if (namespaceDeclaration is null)
152 memberDeclaration = pathClass;
153 else
154 memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
155 .AddMembers(pathClass);
156 }
157 else
158 {
159 memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
160 .AddMembers(pathClass);
161 }
162 var profileClassCompilationUnit = SyntaxFactory.CompilationUnit()
163 .AddMembers(memberDeclaration)
164 .NormalizeWhitespace();
165 return SyntaxFactory.SyntaxTree(profileClassCompilationUnit);
166 }
167 }
168 }
Modified XFEExtension.NetCore.Analyzer/Generator/ProfilePropertyAutoGenerator.cs +71 -63
@@ -4,8 +4,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
4 4 using System;
5 5 using System.Collections.Generic;
6 6 using System.Linq;
7 using System.Runtime.InteropServices;
8 using System.Text;
9 7
10 8 namespace XFEExtension.NetCore.Analyzer.Generator
11 9 {
@@ -36,11 +34,15 @@ namespace XFEExtension.NetCore.Analyzer.Generator
36 34 var attributeSyntax = SyntaxFactory.AttributeList(
37 35 SyntaxFactory.SingletonSeparatedList(
38 36 SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileFieldAutoGenerateAttribute"))));
39 var properties = fieldDeclarationSyntaxes.Select(fieldDeclarationSyntax =>
37 var properties = new List<PropertyDeclarationSyntax>();
38 var methods = new List<MethodDeclarationSyntax>();
39 foreach (var fieldDeclarationSyntax in fieldDeclarationSyntaxes)
40 40 {
41 41 var variableDeclaration = fieldDeclarationSyntax.Declaration.Variables.First();
42 42 var fieldName = variableDeclaration.Identifier.Text;
43 43 var propertyName = fieldName[0] == '_' ? fieldName[1].ToString().ToUpper() + fieldName.Substring(2) : fieldName[0].ToString().ToUpper() + fieldName.Substring(1);
44 var getMethodName = $"Get{propertyName}Property";
45 var setMethodName = $"Set{propertyName}Property";
44 46 GetProfilePropertyAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
45 47 {
46 48 if (attribute.ArgumentList is null)
@@ -58,11 +60,12 @@ namespace XFEExtension.NetCore.Analyzer.Generator
58 60 var triviaText = $@"/// <inheritdoc cref=""{fieldName}""/>
59 61 /// <remarks>
60 62 /// <seealso cref=""{propertyName}""/> 是根据 <seealso cref=""{fieldName}""/> 自动生成的属性<br/><br/>
61 /// <code><seealso langword=""get""/>方法已生成以下代码:";
63 /// <code><seealso langword=""get""/>方法已生成以下代码: ○ <seealso cref=""{className}.{getMethodName}()""/>;<br/>";
62 64 #endregion
63 var getExpressionStatements = new List<StatementSyntax>();
64 var getIndex = 0;
65 var setIndex = 0;
65 var getExpressionStatements = new List<StatementSyntax>()
66 {
67 SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{getMethodName}()")).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
68 };
66 69 if (fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAddGetAttribute))
67 70 {
68 71 GetProfilePropertyAddGetAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
@@ -79,37 +82,30 @@ namespace XFEExtension.NetCore.Analyzer.Generator
79 82 funcText = interpolatedStringExpressionSyntax.Contents.ToString();
80 83 if (argument.Expression is InvocationExpressionSyntax invocationExpressionSyntax)
81 84 funcText = invocationExpressionSyntax.GetText().ToString();
82 getExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)));
83 if (getIndex == 0)
84 {
85 #region Get方法首个注释
86 triviaText += $"\t○ {funcText.Replace("\n", "<br/>").Replace("return", "<seealso langword=\"return\"/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>")};<br/>";
87 #endregion
88 }
89 else
90 {
91 #region Get剩余方法注释
92 triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace("return", "<seealso langword=\"return\"/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>")};<br/>";
93 #endregion
94 }
95 getIndex++;
85 getExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
86 #region Get方法注释
87 triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace("return", "<seealso langword=\"return\"/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>")};<br/>";
88 #endregion
96 89 });
97 90 }
98 91 else
99 92 {
100 getExpressionStatements.Add(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(fieldName)));
93 getExpressionStatements.Add(SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression($"Current.{fieldName}")));
101 94 #region Get方默认注释
102 triviaText += $@" ○ <seealso langword=""return""/> <seealso langword=""{fieldName}""/>;";
95 triviaText += $@"
96 /// ○ <seealso langword=""return""/> <seealso langword=""{fieldName}""/>;";
103 97 #endregion
104 getIndex++;
105 98 }
106 99 #region Get方法尾及Set方法头注释
107 triviaText += @"
100 triviaText += $@"
108 101 /// </code>
109 102 /// <br/>
110 /// <code><seealso langword=""set""/>方法已生成以下代码:";
103 /// <code><seealso langword=""set""/>方法已生成以下代码: ○ <seealso cref=""{className}.{setMethodName}({propertyType})""/>;<br/>";
111 104 #endregion
112 var setExpressionStatements = new List<StatementSyntax>();
105 var setExpressionStatements = new List<StatementSyntax>()
106 {
107 SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{setMethodName}(value)")).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
108 };
113 109 if (fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAddSetAttribute))
114 110 {
115 111 GetProfilePropertyAddSetAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
@@ -126,43 +122,25 @@ namespace XFEExtension.NetCore.Analyzer.Generator
126 122 funcText = interpolatedStringExpressionSyntax.Contents.ToString();
127 123 else if (argument.Expression is InvocationExpressionSyntax invocationExpressionSyntax)
128 124 funcText = invocationExpressionSyntax.GetText().ToString();
129 setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)));
130 if (setIndex == 0)
131 {
132 #region Set方法首个注释
133 triviaText += $"\t○ {funcText.Replace("\n", "<br/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>").Replace("value", "<seealso langword=\"value\"/>")};<br/>";
134 #endregion
135 }
136 else
137 {
138 #region Set剩余方法注释
139 triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>").Replace("value", "<seealso langword=\"value\"/>")};<br/>";
140 #endregion
141 }
142 setIndex++;
125 setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(funcText)).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
126 #region Set方法注释
127 triviaText += $"\n///\t\t\t\t○ {funcText.Replace("\n", "<br/>").Replace(fieldName, $"<seealso langword=\"{fieldName}\"/>").Replace("value", "<seealso langword=\"value\"/>")};<br/>";
128 #endregion
143 129 });
144 130 }
145 131 else
146 132 {
147 setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{fieldName} = value")));
133 setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"Current.{fieldName} = value")));
148 134 #region Set方法默认注释
149 triviaText += $@" ○ <seealso langword=""{fieldName}""/> = <seealso langword=""value""/>;<br/>";
135 triviaText += $@"
136 /// ○ <seealso langword=""{fieldName}""/> = <seealso langword=""value""/>;<br/>";
150 137 #endregion
151 setIndex++;
152 138 }
153 139 setExpressionStatements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(typeof({className}))")));
154 if (setIndex == 0)
155 {
156 #region Set方法中的保存方法在第一位情况的注释
157 triviaText += $@" ○ <seealso cref=""global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(ProfileInfo)""/>";
158 #endregion
159 }
160 else
161 {
162 #region Set方法中的保存方法在非第一位情况的注释
163 triviaText += $"\n///\t\t\t\t○ <seealso cref=\"global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(ProfileInfo)\"/>;";
164 #endregion
165 }
140 #region Set方法中的保存方法的注释
141 triviaText += $@"
142 /// ○ <seealso cref=""global::XFEExtension.NetCore.ProfileExtension.XFEProfile.SaveProfile(ProfileInfo)""/>";
143 #endregion
166 144 #region Trivia尾
167 145 triviaText += @"
168 146 /// </code>
@@ -181,9 +159,18 @@ namespace XFEExtension.NetCore.Analyzer.Generator
181 159 .WithBody(SyntaxFactory.Block(setExpressionStatements))
182 160 })))
183 161 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText));
184 return property.NormalizeWhitespace();
185 });
186 var profileClassSyntaxTree = GenerateProfileClassSyntaxTree(classDeclaration, usingDirectives, properties, fileScopedNamespaceDeclarationSyntax);
162 var getMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), getMethodName)
163 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)))
164 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
165 var setMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), setMethodName)
166 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)))
167 .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("value")).WithType(propertyType))
168 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
169 methods.Add(getMethod);
170 methods.Add(setMethod);
171 properties.Add(property.NormalizeWhitespace());
172 }
173 var profileClassSyntaxTree = GenerateProfileClassSyntaxTree(classDeclaration, usingDirectives, properties, methods, fileScopedNamespaceDeclarationSyntax);
187 174 context.AddSource($"{className}.g.cs", profileClassSyntaxTree.ToString());
188 175 }
189 176 }
@@ -215,13 +202,13 @@ namespace XFEExtension.NetCore.Analyzer.Generator
215 202
216 203 public static IEnumerable<FieldDeclarationSyntax> GetFieldDeclarations(ClassDeclarationSyntax classDeclaration) => classDeclaration.DescendantNodes()
217 204 .OfType<FieldDeclarationSyntax>()
218 .Where(fieldDeclarationSyntax => fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAttribute) && fieldDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword));
205 .Where(fieldDeclarationSyntax => fieldDeclarationSyntax.AttributeLists.Any(IsProfilePropertyAttribute) && !fieldDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword));
219 206
220 207 public static IEnumerable<ClassDeclarationSyntax> GetClassDeclarations(SyntaxNode rootNode) => rootNode.DescendantNodes()
221 208 .OfType<ClassDeclarationSyntax>()
222 .Where(classDeclaration => classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword));
209 .Where(classDeclaration => classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword) && !classDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword));
223 210
224 private static SyntaxTree GenerateProfileClassSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, IEnumerable<PropertyDeclarationSyntax> propertyDeclarationSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
211 private static SyntaxTree GenerateProfileClassSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, List<PropertyDeclarationSyntax> propertyDeclarationSyntaxes, List<MethodDeclarationSyntax> methodDeclarationSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
225 212 {
226 213 var className = classDeclaration.Identifier.ValueText;
227 214 var triviaText = $@"/// <remarks>
@@ -229,8 +216,29 @@ namespace XFEExtension.NetCore.Analyzer.Generator
229 216 /// <code>
230 217 ";
231 218 triviaText += string.Join("<br/>\n", propertyDeclarationSyntaxes.Select(propertyDeclarationSyntax => $"/// ○ <seealso cref=\"{propertyDeclarationSyntax.Identifier}\"/>")) + "\n/// </code><br/>\n/// <code>来自<seealso cref=\"global::XFEExtension.NetCore.ProfileExtension.XFEProfile\"/></code>\n/// </remarks>\n";
232 var memberDeclarations = new List<MemberDeclarationSyntax>();
219 var memberDeclarations = new List<MemberDeclarationSyntax>
220 {
221 SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(className), "Current")
222 .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
223 .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileInstanceAttribute")))))
224 .WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(
225 new[]
226 {
227 SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
228 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
229 SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
230 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
231 })))
232 .WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression($"new {className}()")))
233 .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
234 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
235 /// 该配置文件的实例<br/>
236 /// <seealso cref=""Current""/> 是 <seealso cref=""{className}""/> 配置文件类的实例数据
237 /// </summary>
238 "))
239 };
233 240 memberDeclarations.AddRange(propertyDeclarationSyntaxes);
241 memberDeclarations.AddRange(methodDeclarationSyntaxes);
234 242 var staticConstructorSyntax = SyntaxFactory.ConstructorDeclaration(className)
235 243 .AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword))
236 244 .WithBody(SyntaxFactory.Block(
Added XFEExtension.NetCore/PathExtension/AutoPathAttribute.cs +27 -0
@@ -0,0 +1,27 @@
1 namespace XFEExtension.NetCore.PathExtension;
2
3 /// <summary>
4 /// 自动检测并生成对应文件夹
5 /// </summary>
6 [AttributeUsage(AttributeTargets.Field)]
7 public sealed class AutoPathAttribute : Attribute
8 {
9 /// <summary>
10 /// 生成的属性名称
11 /// </summary>
12 public string? PropertyName { get; set; }
13 /// <summary>
14 /// 自动检测并生成对应文件夹
15 /// </summary>
16 public AutoPathAttribute()
17 {
18 }
19 /// <summary>
20 /// 自动检测并生成对应文件夹
21 /// </summary>
22 /// <param name="propertyName"></param>
23 public AutoPathAttribute(string propertyName)
24 {
25 PropertyName = propertyName;
26 }
27 }
Added XFEExtension.NetCore/PathExtension/XFEAutoPath.cs +18 -0
@@ -0,0 +1,18 @@
1 namespace XFEExtension.NetCore.PathExtension;
2
3 /// <summary>
4 /// 自动目录
5 /// </summary>
6 public static class XFEAutoPath
7 {
8 /// <summary>
9 /// 检测文件夹路径是否存在否则创建
10 /// </summary>
11 /// <param name="path">文件夹路径</param>
12 /// <param name="enableCheck">是否启用检测</param>
13 public static void CheckPathExistAndCreate(string path, bool enableCheck = true)
14 {
15 if (enableCheck && !Path.Exists(path))
16 Directory.CreateDirectory(path);
17 }
18 }
Modified XFEExtension.NetCore/ProfileExtension/ProfileInfo.cs +30 -11
@@ -5,37 +5,56 @@ namespace XFEExtension.NetCore.ProfileExtension;
5 5 /// <summary>
6 6 /// 配置文件信息
7 7 /// </summary>
8 /// <param name="profileType">配置文件类型</param>
9 /// <param name="path">配置文件储存位置</param>
10 /// <param name="description">配置文件描述</param>
11 public class ProfileInfo(Type profileType, string path = "", string description = "")
8 public class ProfileInfo
12 9 {
13 10 /// <summary>
14 11 /// 配置文件类型
15 12 /// </summary>
16 public Type Profile { get; init; } = profileType;
13 public Type Profile { get; init; }
17 14 /// <summary>
18 15 /// 配置文件储存位置
19 16 /// </summary>
20 public string Path { get; init; } = path == "" ? $"{((XFEProfile.ProfilesRootPath[^1] == '/' || XFEProfile.ProfilesRootPath[^1] == '\\') ? $"{XFEProfile.ProfilesRootPath}{profileType.Name}.xfe" : $"{XFEProfile.ProfilesRootPath}/{profileType.Name}.xfe")}" : path;
17 public string Path { get; init; }
21 18 /// <summary>
22 19 /// 配置文件描述
23 20 /// </summary>
24 public string? Description { get; set; } = description;
21 public string? Description { get; set; }
22 /// <summary>
23 /// 实例成员信息
24 /// </summary>
25 public PropertyInfo? InstancePropertyInfo { get; private set; }
25 26 /// <summary>
26 27 /// 配置文件属性列表
27 28 /// </summary>
28 public List<ProfileEntryInfo> MemberInfo { get; init; } = GetMemberWithProfileAttribute(profileType);
29 internal static List<ProfileEntryInfo> GetMemberWithProfileAttribute(Type type)
29 public List<ProfileEntryInfo> MemberInfo { get; private set; }
30 /// <summary>
31 /// 配置文件信息
32 /// </summary>
33 /// <param name="profileType">配置文件类型</param>
34 /// <param name="path">配置文件储存位置</param>
35 /// <param name="description">配置文件描述</param>
36 public ProfileInfo(Type profileType, string path = "", string description = "")
30 37 {
38 Profile = profileType;
39 Path = path == "" ? $"{((XFEProfile.ProfilesRootPath[^1] == '/' || XFEProfile.ProfilesRootPath[^1] == '\\') ? $"{XFEProfile.ProfilesRootPath}{profileType.Name}.xfe" : $"{XFEProfile.ProfilesRootPath}/{profileType.Name}.xfe")}" : path;
40 Description = description;
31 41 var profileEntryList = new List<ProfileEntryInfo>();
32 foreach (var memberInfo in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
42 foreach (var memberInfo in profileType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
33 43 if (memberInfo is not MethodInfo)
44 {
34 45 if (memberInfo.GetCustomAttribute<ProfilePropertyAttribute>() is not null)
35 46 profileEntryList.Add(new ProfileEntryInfo(memberInfo.Name, memberInfo));
36 return profileEntryList;
47 if (memberInfo.GetCustomAttribute<ProfileInstanceAttribute>() is not null)
48 InstancePropertyInfo = memberInfo as PropertyInfo;
49 }
50 MemberInfo = profileEntryList;
37 51 }
38 52 /// <summary>
53 /// 获取当前配置文件的数据实例
54 /// </summary>
55 /// <returns></returns>
56 public object? GetProfileInstance() => InstancePropertyInfo?.GetValue(null);
57 /// <summary>
39 58 /// 配置文件类型生成
40 59 /// </summary>
41 60 /// <param name="profileType"></param>
Added XFEExtension.NetCore/ProfileExtension/ProfileInstanceAttribute.cs +7 -0
@@ -0,0 +1,7 @@
1 namespace XFEExtension.NetCore.ProfileExtension;
2
3 /// <summary>
4 /// 配置文件实例
5 /// </summary>
6 [AttributeUsage(AttributeTargets.Property)]
7 public class ProfileInstanceAttribute : Attribute { }
Modified XFEExtension.NetCore/ProfileExtension/XFEProfile.cs +23 -15
@@ -1,6 +1,7 @@
1 1 using System.Reflection;
2 2 using System.Text.Json;
3 3 using XFEExtension.NetCore.FormatExtension;
4 using static System.Runtime.InteropServices.JavaScript.JSType;
4 5
5 6 namespace XFEExtension.NetCore.ProfileExtension;
6 7
@@ -9,12 +10,12 @@ namespace XFEExtension.NetCore.ProfileExtension;
9 10 /// </summary>
10 11 public abstract class XFEProfile
11 12 {
12 private static Func<ProfileEntryInfo, string> SaveProfilesFunc { get; set; } = p =>
13 private static Func<object?, ProfileEntryInfo, string> SaveProfilesFunc { get; set; } = (i, p) =>
13 14 {
14 15 if (p.MemberInfo is FieldInfo fieldInfo)
15 return JsonSerializer.Serialize(fieldInfo.GetValue(null));
16 return JsonSerializer.Serialize(fieldInfo.GetValue(i));
16 17 else if (p.MemberInfo is PropertyInfo propertyInfo)
17 return JsonSerializer.Serialize(propertyInfo.GetValue(null));
18 return JsonSerializer.Serialize(propertyInfo.GetValue(i));
18 19 else
19 20 return string.Empty;
20 21 };
@@ -41,13 +42,14 @@ public abstract class XFEProfile
41 42 /// <summary>
42 43 /// 加载配置文件
43 44 /// </summary>
44 /// <param name="profileInfo"></param>
45 /// <param name="profileInfo">配置文件信息</param>
45 46 /// <returns></returns>
46 47 public static void LoadProfiles(params ProfileInfo[] profileInfo)
47 48 {
48 49 Profiles.AddRange(profileInfo);
49 50 foreach (var profile in Profiles)
50 51 {
52 var instance = profile.GetProfileInstance();
51 53 if (!File.Exists(profile.Path))
52 54 continue;
53 55 XFEDictionary propertyFileContent = File.ReadAllText(profile.Path);
@@ -60,9 +62,9 @@ public abstract class XFEProfile
60 62 if (property.Header == memberInfo.Name)
61 63 {
62 64 if (memberInfo.MemberInfo is FieldInfo fieldInfo)
63 fieldInfo.SetValue(null, LoadProfilesFunc(property.Content, memberInfo));
65 fieldInfo.SetValue(instance, LoadProfilesFunc(property.Content, memberInfo));
64 66 else if (memberInfo.MemberInfo is PropertyInfo propertyInfo)
65 propertyInfo.SetValue(null, LoadProfilesFunc(property.Content, memberInfo));
67 propertyInfo.SetValue(instance, LoadProfilesFunc(property.Content, memberInfo));
66 68 continue;
67 69 }
68 70 foreach (var propertySecFind in propertyFileContent)
@@ -70,9 +72,9 @@ public abstract class XFEProfile
70 72 if (propertySecFind.Header == memberInfo.Name)
71 73 {
72 74 if (profile.MemberInfo[i].MemberInfo is FieldInfo fieldInfo)
73 fieldInfo.SetValue(null, LoadProfilesFunc(propertySecFind.Content, memberInfo));
75 fieldInfo.SetValue(instance, LoadProfilesFunc(propertySecFind.Content, memberInfo));
74 76 else if (profile.MemberInfo[i].MemberInfo is PropertyInfo propertyInfo)
75 propertyInfo.SetValue(null, LoadProfilesFunc(propertySecFind.Content, memberInfo));
77 propertyInfo.SetValue(instance, LoadProfilesFunc(propertySecFind.Content, memberInfo));
76 78 break;
77 79 }
78 80 }
@@ -84,7 +86,7 @@ public abstract class XFEProfile
84 86 /// <summary>
85 87 /// 加载配置文件
86 88 /// </summary>
87 /// <param name="profileInfo"></param>
89 /// <param name="profileInfo">配置文件信息</param>
88 90 /// <returns></returns>
89 91 public static async Task LoadProfilesAsync(params ProfileInfo[] profileInfo) => await Task.Run(() => LoadProfiles(profileInfo));
90 92
@@ -99,8 +101,11 @@ public abstract class XFEProfile
99 101 if (waitSaveProfile is null)
100 102 return;
101 103 var saveProfileDictionary = new XFEDictionary();
104 var instance = profileInfo.GetProfileInstance();
102 105 foreach (var property in waitSaveProfile.MemberInfo)
103 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(property));
106 {
107 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(instance, property));
108 }
104 109 var fileSavePath = Path.GetDirectoryName(waitSaveProfile.Path);
105 110 if (!Directory.Exists(fileSavePath) && fileSavePath is not null && fileSavePath != string.Empty)
106 111 Directory.CreateDirectory(fileSavePath);
@@ -128,8 +133,9 @@ public abstract class XFEProfile
128 133 if (waitSaveProfile is null)
129 134 return;
130 135 var saveProfileDictionary = new XFEDictionary();
136 var instance = profileInfo.GetProfileInstance();
131 137 foreach (var property in waitSaveProfile.MemberInfo)
132 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(property));
138 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(instance, property));
133 139 var fileSavePath = Path.GetDirectoryName(waitSaveProfile.Path);
134 140 if (!Directory.Exists(fileSavePath) && fileSavePath is not null && fileSavePath != string.Empty)
135 141 Directory.CreateDirectory(fileSavePath);
@@ -199,7 +205,7 @@ public abstract class XFEProfile
199 205 /// 设置储存配置文件的方法
200 206 /// </summary>
201 207 /// <param name="saveProfilesFunc">储存方法</param>
202 public static void SetSaveProfilesFunction(Func<ProfileEntryInfo, string> saveProfilesFunc) => SaveProfilesFunc = saveProfilesFunc;
208 public static void SetSaveProfilesFunction(Func<object?, ProfileEntryInfo, string> saveProfilesFunc) => SaveProfilesFunc = saveProfilesFunc;
203 209
204 210 /// <summary>
205 211 /// 设置加载配置文件的方法
@@ -232,8 +238,9 @@ public abstract class XFEProfile
232 238 if (waitSaveProfile is null)
233 239 return string.Empty;
234 240 var saveProfileDictionary = new XFEDictionary();
241 var instance = profileInfo.GetProfileInstance();
235 242 foreach (var property in waitSaveProfile.MemberInfo)
236 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(property));
243 saveProfileDictionary.Add(property.Name, SaveProfilesFunc(instance, property));
237 244 return saveProfileDictionary.ToString();
238 245 }
239 246
@@ -259,6 +266,7 @@ public abstract class XFEProfile
259 266 /// <param name="autoSave">导入后是否自动储存</param>
260 267 public static void ImportProfile(ProfileInfo profileInfo, string profileString, bool autoSave = true)
261 268 {
269 var instance = profileInfo.GetProfileInstance();
262 270 var waitSaveProfile = Profiles.Find(x => x.Profile == profileInfo.Profile);
263 271 if (waitSaveProfile is null)
264 272 return;
@@ -268,9 +276,9 @@ public abstract class XFEProfile
268 276 if (importProfileDictionary[property.Name] is not null)
269 277 {
270 278 if (property.MemberInfo is FieldInfo fieldInfo)
271 fieldInfo.SetValue(null, LoadProfilesFunc(importProfileDictionary[property.Name]!, property));
279 fieldInfo.SetValue(instance, LoadProfilesFunc(importProfileDictionary[property.Name]!, property));
272 280 else if (property.MemberInfo is PropertyInfo propertyInfo)
273 propertyInfo.SetValue(null, LoadProfilesFunc(importProfileDictionary[property.Name]!, property));
281 propertyInfo.SetValue(instance, LoadProfilesFunc(importProfileDictionary[property.Name]!, property));
274 282 }
275 283 }
276 284 if (autoSave)
Modified XFEExtension.NetCore/XFEExtension.NetCore.csproj +3 -3
@@ -19,11 +19,11 @@
19 19 <PackageTags>XFE;拓展;GPT;Server;服务器;测试;XFEExtension</PackageTags>
20 20 <PackageReleaseNotes>## 调整
21 21
22 修复自动生成实现类的构造函数带有其父类构造函数体的bug
22 配置文件自动实现:现在配置文件有实例了,外部静态访问内部实例数据,添加了属性Get、Set通知的部分声明方法
23 23
24 24 ## 新增
25 25
26
26 新增文件路径管理自动实现:现在,不必为了担心文件夹路径不存在而烦恼了,现在管理器会自动创建文件夹
27 27
28 28 ## 严重
29 29
@@ -32,7 +32,7 @@
32 32 <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
33 33 <PackageLicenseFile>LICENSE</PackageLicenseFile>
34 34 <AssemblyOriginatorKeyFile>..\XFEstudio.pfx</AssemblyOriginatorKeyFile>
35 <Version>2.9.1</Version>
35 <Version>2.10.0</Version>
36 36 <Authors>XFEstudio</Authors>
37 37 </PropertyGroup>
38 38