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

XFEExtension.NetCore.AutoPath

【DLL】自动路径创建工具,自动创建不存在的文件夹,对路径统一管理

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

XFEstudio/XFEExtension.NetCore.AutoPath

完成迁移,设置nuget包信息

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

代码差异

10 个文件 +326 -3
Modified LICENSE.txt +1 -1
@@ -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
Added README.md +65 -0
@@ -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 ```
Added XFEExtension.NetCore.AutoPath.Analyzer/Generator/AutoPathSyntaxReceiver.cs +23 -0
@@ -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 }
Added XFEExtension.NetCore.AutoPath.Analyzer/Generator/PathPropertyAutoGenerator.cs +180 -0
@@ -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 }
Added XFEExtension.NetCore.AutoPath.Analyzer/XFEExtension.NetCore.AutoPath.Analyzer.csproj +20 -0
@@ -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>
Modified XFEExtension.NetCore.AutoPath.sln +9 -0
@@ -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
Modified XFEExtension.NetCore.AutoPath/AutoPathAttribute.cs +1 -1
@@ -1,4 +1,4 @@
1 namespace XFEExtension.NetCore.PathExtension;
1 namespace XFEExtension.NetCore.AutoPath;
2 2
3 3 /// <summary>
4 4 /// 自动检测并生成对应文件夹
Modified XFEExtension.NetCore.AutoPath/XFEAutoPath.cs +1 -1
@@ -1,4 +1,4 @@
1 namespace XFEExtension.NetCore.PathExtension;
1 namespace XFEExtension.NetCore.AutoPath;
2 2
3 3 /// <summary>
4 4 /// 自动目录
Modified XFEExtension.NetCore.AutoPath/XFEExtension.NetCore.AutoPath.csproj +26 -0
@@ -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>
Added logoIcon.png +0 -0
二进制文件已变更,无法进行逐行预览。