返回提交历史
Modified
LICENSE.txt
+1
-1
Added
README.md
+65
-0
Added
XFEExtension.NetCore.AutoPath.Analyzer/Generator/AutoPathSyntaxReceiver.cs
+23
-0
Added
XFEExtension.NetCore.AutoPath.Analyzer/Generator/PathPropertyAutoGenerator.cs
+180
-0
Added
XFEExtension.NetCore.AutoPath.Analyzer/XFEExtension.NetCore.AutoPath.Analyzer.csproj
+20
-0
Modified
XFEExtension.NetCore.AutoPath.sln
+9
-0
Modified
XFEExtension.NetCore.AutoPath/AutoPathAttribute.cs
+1
-1
Modified
XFEExtension.NetCore.AutoPath/XFEAutoPath.cs
+1
-1
Modified
XFEExtension.NetCore.AutoPath/XFEExtension.NetCore.AutoPath.csproj
+26
-0
Added
logoIcon.png
+0
-0
XFEstudio/XFEExtension.NetCore.AutoPath
完成迁移,设置nuget包信息
d0f243f
代码差异
10 个文件
+326
-3
@@ -1,6 +1,6 @@
1
1
MIT License
2
2
3
Copyright (c) [year] [fullname]
3
Copyright (c) [2024] [XFEstudio]
4
4
5
5
Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,65 @@
1
# XFEExtension.NetCore.AutoPath
2
3
## 描述
4
5
自动路径创建工具,自动创建不存在的文件夹,对路径统一管理
6
7
## 自动实现路径管理
8
9
#### 基础用法
10
11
```csharp
12
//创建路径管理类
13
public partial class AppPath
14
{
15
[AutoPath]
16
readonly static string myTestPath = "MyTestPath/Test";
17
[AutoPath]
18
readonly static string mySecTestPath = $"{MyTestPath}/Sec";//路径可以引用自动生成的路径
19
}
20
21
22
//使用统一管理的路径
23
class Program
24
{
25
static void Main(string[] args)
26
{
27
File.WriteAllText($"{AppPath.MyTestPath}/test.txt", "Hello World");//此时如果没有MyTestPath则会自动创建
28
var exist = Directory.Exists(AppPath.MySecTestPath);
29
Console.WriteLine(exist);//结果为True
30
}
31
}
32
```
33
34
#### 为路径添加注释
35
36
```csharp
37
public partial class AppPath
38
{
39
/// <summary>
40
/// 测试路径
41
/// 这段注释会自动添加至自动生成的Name属性上
42
/// </summary>
43
[AutoPath]
44
readonly static string myTestPath = "MyTestPath/Test";
45
[AutoPath]
46
readonly static string mySecTestPath = $"{MyTestPath}/Sec";
47
}
48
```
49
50
#### 使用部分方法来设置get方法
51
52
```csharp
53
public partial class AppPath
54
{
55
[AutoPath]
56
readonly static string myTestPath = "MyTestPath/Test";
57
[AutoPath]
58
readonly static string mySecTestPath = $"{MyTestPath}/Sec";
59
60
static partial void GetMyTestPathProperty()
61
{
62
Console.WriteLine("获取了MyTestPath");
63
}
64
}
65
```
@@ -0,0 +1,23 @@
1
using Microsoft.CodeAnalysis;
2
using Microsoft.CodeAnalysis.CSharp.Syntax;
3
using System.Collections.Generic;
4
using System.Linq;
5
6
namespace XFEExtension.NetCore.AutoPath.Analyzer.Generator
7
{
8
public class AutoPathSyntaxReceiver : ISyntaxReceiver
9
{
10
public List<FieldDeclarationSyntax> CandidateFields { get; } = new List<FieldDeclarationSyntax>();
11
12
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
13
{
14
if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax)
15
{
16
if (fieldDeclarationSyntax.AttributeLists.Any(PathPropertyAutoGenerator.IsAutoPathAttribute))
17
{
18
CandidateFields.Add(fieldDeclarationSyntax);
19
}
20
}
21
}
22
}
23
}
@@ -0,0 +1,180 @@
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.AutoPath.Analyzer.Generator
8
{
9
[Generator]
10
public class PathPropertyAutoGenerator : ISourceGenerator
11
{
12
public void Initialize(GeneratorInitializationContext context)
13
{
14
context.RegisterForSyntaxNotifications(() => new AutoPathSyntaxReceiver());
15
}
16
17
public void Execute(GeneratorExecutionContext context)
18
{
19
if (!(context.SyntaxReceiver is AutoPathSyntaxReceiver receiver))
20
return;
21
var syntaxTrees = context.Compilation.SyntaxTrees;
22
foreach (var syntaxTree in syntaxTrees)
23
{
24
var root = syntaxTree.GetRoot();
25
var classDeclarations = GetClassDeclarations(root);
26
var fileScopedNamespaceDeclarationSyntax = GetFileScopedNamespaceDeclaration(root);
27
foreach (var classDeclaration in classDeclarations)
28
{
29
var fieldDeclarationSyntaxes = GetFieldDeclarations(classDeclaration);
30
if (fieldDeclarationSyntaxes is null || !fieldDeclarationSyntaxes.Any())
31
{
32
continue;
33
}
34
var className = classDeclaration.Identifier.ValueText;
35
var properties = new List<PropertyDeclarationSyntax>();
36
var methods = new List<MethodDeclarationSyntax>();
37
var enableCheckProperties = new List<PropertyDeclarationSyntax>();
38
foreach (var fieldDeclarationSyntax in fieldDeclarationSyntaxes)
39
{
40
var variableDeclaration = fieldDeclarationSyntax.Declaration.Variables.First();
41
var fieldName = variableDeclaration.Identifier.Text;
42
var propertyName = fieldName[0] == '_' ? fieldName[1].ToString().ToUpper() + fieldName.Substring(2) : fieldName[0].ToString().ToUpper() + fieldName.Substring(1);
43
var getMethodName = $"Get{propertyName}Property";
44
var enableCheckPropertyName = $"{propertyName}EnableCheck";
45
GetAutoPathAttributeList(fieldDeclarationSyntax).ForEach(attribute =>
46
{
47
if (attribute.ArgumentList is null)
48
{
49
return;
50
}
51
var argument = attribute.ArgumentList.Arguments.First();
52
if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax)
53
{
54
propertyName = literalExpressionSyntax.Token.ValueText;
55
}
56
});
57
var propertyType = fieldDeclarationSyntax.Declaration.Type;
58
var triviaText = $@"/// <inheritdoc cref=""{fieldName}""/>
59
/// <remarks>
60
/// <seealso cref=""{propertyName}""/> 是根据 <seealso cref=""{fieldName}""/> 自动生成的路径属性<br/><br/>
61
/// </remarks>
62
";
63
var checkEnableTriviaText = $@"/// <summary>
64
/// 是否为 <seealso cref=""{fieldName}""/> 启用检测路径<br/><br/>
65
/// </summary>
66
";
67
var property = SyntaxFactory.PropertyDeclaration(propertyType, propertyName)
68
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
69
.WithAccessorList(SyntaxFactory.AccessorList(
70
SyntaxFactory.List(new[]
71
{
72
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
73
.WithBody(SyntaxFactory.Block(
74
SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"Options ??= new {className}()")),
75
SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"global::XFEExtension.NetCore.AutoPath.XFEAutoPath.CheckPathExistAndCreate({fieldName}, Options.{enableCheckPropertyName})")),
76
SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression($"{getMethodName}()")),
77
SyntaxFactory.ReturnStatement(SyntaxFactory.ParseExpression($"{fieldName}"))))
78
})))
79
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText));
80
var enableCheckProperty = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName("bool"), enableCheckPropertyName)
81
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
82
.WithAccessorList(SyntaxFactory.AccessorList(
83
SyntaxFactory.List(new[]
84
{
85
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
86
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
87
})))
88
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(checkEnableTriviaText))
89
.WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("true")))
90
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
91
var getMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), getMethodName)
92
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)))
93
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
94
properties.Add(property.NormalizeWhitespace());
95
methods.Add(getMethod);
96
enableCheckProperties.Add(enableCheckProperty.NormalizeWhitespace());
97
}
98
var profileClassSyntaxTree = GeneratePathClassSyntaxTree(classDeclaration, properties, enableCheckProperties, methods, fileScopedNamespaceDeclarationSyntax);
99
context.AddSource($"{className}.g.cs", profileClassSyntaxTree.ToString());
100
}
101
}
102
}
103
104
public static bool IsAutoPathAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "AutoPath");
105
106
public static List<AttributeSyntax> GetAutoPathAttributeList(FieldDeclarationSyntax fieldDeclaration) => fieldDeclaration.AttributeLists.Where(IsAutoPathAttribute).SelectMany(attributeList => attributeList.Attributes).ToList();
107
108
public static FileScopedNamespaceDeclarationSyntax GetFileScopedNamespaceDeclaration(SyntaxNode rootNode)
109
{
110
var namespaceResults = rootNode.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
111
if (namespaceResults != null && namespaceResults.Count() > 0)
112
return namespaceResults.First();
113
return null;
114
}
115
116
public static IEnumerable<FieldDeclarationSyntax> GetFieldDeclarations(ClassDeclarationSyntax classDeclaration) => classDeclaration.DescendantNodes()
117
.OfType<FieldDeclarationSyntax>()
118
.Where(fieldDeclarationSyntax => fieldDeclarationSyntax.AttributeLists.Any(IsAutoPathAttribute) && fieldDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword));
119
120
public static IEnumerable<ClassDeclarationSyntax> GetClassDeclarations(SyntaxNode rootNode) => rootNode.DescendantNodes()
121
.OfType<ClassDeclarationSyntax>()
122
.Where(classDeclaration => classDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword));
123
124
private static SyntaxTree GeneratePathClassSyntaxTree(ClassDeclarationSyntax classDeclaration, List<PropertyDeclarationSyntax> propertyDeclarationSyntaxes, List<PropertyDeclarationSyntax> enableCheckDeclarationSyntaxes, List<MethodDeclarationSyntax> methodDeclarationSyntaxes, FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax)
125
{
126
var className = classDeclaration.Identifier.ValueText;
127
var triviaText = $@"/// <remarks>
128
/// <code><seealso cref=""{className}""/> 已生成以下路径:</code><br/>
129
/// <code>
130
";
131
triviaText += string.Join("<br/>\n", propertyDeclarationSyntaxes.Select(propertyDeclarationSyntax => $"/// ○ <seealso cref=\"{propertyDeclarationSyntax.Identifier}\"/>")) + "\n/// </code><br/>\n/// <code>来自<seealso cref=\"global::XFEExtension.NetCore.AutoPath\"/></code>\n/// </remarks>\n";
132
var memberDeclarations = new List<MemberDeclarationSyntax>()
133
{
134
SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(className), "Options")
135
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
136
.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::XFEExtension.NetCore.ProfileExtension.ProfileInstanceAttribute")))))
137
.WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(
138
new[]
139
{
140
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
141
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
142
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
143
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
144
})))
145
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
146
/// 配置选项<br/>
147
/// <seealso cref=""Options""/> 是 <seealso cref=""{className}""/> 类的配置选项
148
/// </summary>
149
"))
150
};
151
memberDeclarations.AddRange(propertyDeclarationSyntaxes);
152
memberDeclarations.AddRange(methodDeclarationSyntaxes);
153
memberDeclarations.AddRange(enableCheckDeclarationSyntaxes);
154
var pathClass = SyntaxFactory.ClassDeclaration(className)
155
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword))
156
.AddMembers(memberDeclarations.ToArray())
157
.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(triviaText))
158
.NormalizeWhitespace();
159
MemberDeclarationSyntax memberDeclaration;
160
if (fileScopedNamespaceDeclarationSyntax is null)
161
{
162
var namespaceDeclaration = classDeclaration.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
163
if (namespaceDeclaration is null)
164
memberDeclaration = pathClass;
165
else
166
memberDeclaration = SyntaxFactory.NamespaceDeclaration(namespaceDeclaration.Name)
167
.AddMembers(pathClass);
168
}
169
else
170
{
171
memberDeclaration = SyntaxFactory.FileScopedNamespaceDeclaration(fileScopedNamespaceDeclarationSyntax.Name)
172
.AddMembers(pathClass);
173
}
174
var profileClassCompilationUnit = SyntaxFactory.CompilationUnit()
175
.AddMembers(memberDeclaration)
176
.NormalizeWhitespace();
177
return SyntaxFactory.SyntaxTree(profileClassCompilationUnit);
178
}
179
}
180
}
@@ -0,0 +1,20 @@
1
<Project Sdk="Microsoft.NET.Sdk">
2
3
<PropertyGroup>
4
<TargetFramework>netstandard2.0</TargetFramework>
5
</PropertyGroup>
6
7
<PropertyGroup>
8
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
9
<!-- Generates a package at build -->
10
<IncludeBuildOutput>false</IncludeBuildOutput>
11
<!-- Do not include the generator as a lib dependency -->
12
</PropertyGroup>
13
14
<ItemGroup>
15
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
16
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
17
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.2" />
18
</ItemGroup>
19
20
</Project>
@@ -3,6 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
3
3
VisualStudioVersion = 17.11.34929.205
4
4
MinimumVisualStudioVersion = 10.0.40219.1
5
5
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFEExtension.NetCore.AutoPath", "XFEExtension.NetCore.AutoPath\XFEExtension.NetCore.AutoPath.csproj", "{5AE91819-3D1B-497A-8910-28182ACE3053}"
6
ProjectSection(ProjectDependencies) = postProject
7
{F1215C28-AB1D-4DE3-8546-31F21E283AA1} = {F1215C28-AB1D-4DE3-8546-31F21E283AA1}
8
EndProjectSection
9
EndProject
10
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFEExtension.NetCore.AutoPath.Analyzer", "XFEExtension.NetCore.AutoPath.Analyzer\XFEExtension.NetCore.AutoPath.Analyzer.csproj", "{F1215C28-AB1D-4DE3-8546-31F21E283AA1}"
6
11
EndProject
7
12
Global
8
13
GlobalSection(ExtensibilityGlobals) = postSolution
@@ -13,6 +18,10 @@ Global
13
18
{5AE91819-3D1B-497A-8910-28182ACE3053}.Debug|Any CPU.Build.0 = Debug|Any CPU
14
19
{5AE91819-3D1B-497A-8910-28182ACE3053}.Release|Any CPU.ActiveCfg = Release|Any CPU
15
20
{5AE91819-3D1B-497A-8910-28182ACE3053}.Release|Any CPU.Build.0 = Release|Any CPU
21
{F1215C28-AB1D-4DE3-8546-31F21E283AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22
{F1215C28-AB1D-4DE3-8546-31F21E283AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
23
{F1215C28-AB1D-4DE3-8546-31F21E283AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
24
{F1215C28-AB1D-4DE3-8546-31F21E283AA1}.Release|Any CPU.Build.0 = Release|Any CPU
16
25
EndGlobalSection
17
26
GlobalSection(SolutionConfigurationPlatforms) = preSolution
18
27
Debug|Any CPU = Debug|Any CPU
@@ -1,4 +1,4 @@
1
namespace XFEExtension.NetCore.PathExtension;
1
namespace XFEExtension.NetCore.AutoPath;
2
2
3
3
/// <summary>
4
4
/// 自动检测并生成对应文件夹
@@ -1,4 +1,4 @@
1
namespace XFEExtension.NetCore.PathExtension;
1
namespace XFEExtension.NetCore.AutoPath;
2
2
3
3
/// <summary>
4
4
/// 自动目录
@@ -4,6 +4,32 @@
4
4
<TargetFramework>net8.0</TargetFramework>
5
5
<ImplicitUsings>enable</ImplicitUsings>
6
6
<Nullable>enable</Nullable>
7
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
8
<Title>AutoPath</Title>
9
<Authors>XFEstudio</Authors>
10
<Company>寰宇朽力网络科技有限公司</Company>
11
<Copyright>寰宇朽力网络科技有限公司版权所有</Copyright>
12
<Description>自动路径创建工具,自动创建不存在的文件夹,对路径统一管理</Description>
13
<PackageProjectUrl>https://github.com/XFEstudio/XFEExtension.NetCore.AutoPath</PackageProjectUrl>
14
<RepositoryUrl>https://github.com/XFEstudio/XFEExtension.NetCore.AutoPath</RepositoryUrl>
15
<PackageTags>XFE;path;directory;auto</PackageTags>
16
<PackageReleaseNotes>First Release</PackageReleaseNotes>
17
<PackageReadmeFile>README.md</PackageReadmeFile>
18
<PackageIcon>logoIcon.png</PackageIcon>
19
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
20
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
7
21
</PropertyGroup>
8
22
23
<ItemGroup>
24
<None Include="..\LICENSE.txt">
25
<Pack>True</Pack>
26
<PackagePath>\</PackagePath>
27
</None>
28
<None Include="..\README.md">
29
<Pack>True</Pack>
30
<PackagePath>\</PackagePath>
31
</None>
32
<None Include="..\XFEExtension.NetCore.AutoPath.Analyzer\bin\Release\netstandard2.0\XFEExtension.NetCore.AutoPath.Analyzer.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
33
</ItemGroup>
34
9
35
</Project>
二进制文件已变更,无法进行逐行预览。