XFEExtension
【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等
关注
0
Fork
0
Star
0
返回提交历史
Added
XFEExtension.NetCore.Analyzer.Test/AppPath.cs
+9
-0
Modified
XFEExtension.NetCore.Analyzer.Test/Program.cs
+4
-4
Modified
XFEExtension.NetCore.Analyzer.Test/SystemProfile.cs
+4
-2
Modified
XFEExtension.NetCore.Analyzer/CodeFix/ProfileExtensionCodeFixProvider.cs
+2
-2
Added
XFEExtension.NetCore.Analyzer/Generator/PathPropertyAutoGenerator.cs
+168
-0
Modified
XFEExtension.NetCore.Analyzer/Generator/ProfilePropertyAutoGenerator.cs
+71
-63
Added
XFEExtension.NetCore/PathExtension/AutoPathAttribute.cs
+27
-0
Added
XFEExtension.NetCore/PathExtension/XFEAutoPath.cs
+18
-0
Modified
XFEExtension.NetCore/ProfileExtension/ProfileInfo.cs
+30
-11
Added
XFEExtension.NetCore/ProfileExtension/ProfileInstanceAttribute.cs
+7
-0
Modified
XFEExtension.NetCore/ProfileExtension/XFEProfile.cs
+23
-15
Modified
XFEExtension.NetCore/XFEExtension.NetCore.csproj
+3
-3
XFEstudio/XFEExtension
change-配置文件自动实现:现在配置文件有实例了,外部静态访问内部实例数据,添加了属性Get、Set通知的部分声明方法 add-新增文件路径管理自动实现:现在,不必为了担心文件夹路径不存在而烦恼了,现在管理器会自动创建文件夹
183a480
代码差异
12 个文件
+366
-100
@@ -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
}
@@ -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
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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(
@@ -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
}
@@ -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
}
@@ -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>
@@ -0,0 +1,7 @@
1
namespace XFEExtension.NetCore.ProfileExtension;
2
3
/// <summary>
4
/// 配置文件实例
5
/// </summary>
6
[AttributeUsage(AttributeTargets.Property)]
7
public class ProfileInstanceAttribute : Attribute { }
@@ -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)
@@ -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