From 6b2aebe2a084bf7558575de712904ae521743cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BB=BA=E8=B6=85?= Date: Mon, 6 Nov 2023 17:01:51 +0800 Subject: [PATCH] feat: init --- .gitignore | 2 + DocGenerator.sln | 25 +++++ DocGenerator/DocGenerator.csproj | 18 ++++ DocGenerator/Program.cs | 170 +++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 .gitignore create mode 100644 DocGenerator.sln create mode 100644 DocGenerator/DocGenerator.csproj create mode 100644 DocGenerator/Program.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d4a6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin +obj \ No newline at end of file diff --git a/DocGenerator.sln b/DocGenerator.sln new file mode 100644 index 0000000..5e919ca --- /dev/null +++ b/DocGenerator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocGenerator", "DocGenerator\DocGenerator.csproj", "{098EC561-C5F6-430B-A3C4-D0D61CD8EF84}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {098EC561-C5F6-430B-A3C4-D0D61CD8EF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {098EC561-C5F6-430B-A3C4-D0D61CD8EF84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {098EC561-C5F6-430B-A3C4-D0D61CD8EF84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {098EC561-C5F6-430B-A3C4-D0D61CD8EF84}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {47DB9E36-610A-47B9-BC95-E2390B492CCE} + EndGlobalSection +EndGlobal diff --git a/DocGenerator/DocGenerator.csproj b/DocGenerator/DocGenerator.csproj new file mode 100644 index 0000000..38ec646 --- /dev/null +++ b/DocGenerator/DocGenerator.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/DocGenerator/Program.cs b/DocGenerator/Program.cs new file mode 100644 index 0000000..af5474b --- /dev/null +++ b/DocGenerator/Program.cs @@ -0,0 +1,170 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Security.Cryptography.X509Certificates; +using System.Xml; + +namespace DocGenerator +{ + internal class Program + { + internal class CommentBlock + { + public string MethodComment { get; set; } = string.Empty; + public Dictionary ParametersComment { get; set; } = new Dictionary(); + public string ReturnComment { get; set; } = string.Empty; + } + + private static CommentBlock ParseComment(string comment) + { + CommentBlock block = new(); + + string newComment = "\n" + comment.Replace("///", "") + ""; + + XmlDocument? xmlDoc = new(); + try + { + xmlDoc.LoadXml(newComment); + } + catch + { + return block; + } + + XmlNode? summaryNode = xmlDoc.SelectSingleNode("//summary"); + string? methodSummary = summaryNode?.InnerText.Trim(); + + XmlNodeList? paramNodes = xmlDoc.SelectNodes("//param"); + if (paramNodes != null) + { + foreach (XmlNode paramNode in paramNodes) + { + string? paramName = paramNode?.Attributes?["name"]?.Value; + string? paramDescription = paramNode?.InnerText.Trim(); + if (paramName != null) + { + if (block.ParametersComment.ContainsKey(paramName)) + { + block.ParametersComment[paramName] = paramDescription ?? ""; + } + else + { + block.ParametersComment.Add(paramName, paramDescription ?? ""); + } + } + } + } + + XmlNode? returnsNode = xmlDoc.SelectSingleNode("//returns"); + string? returnsDescription = returnsNode?.InnerText.Trim(); + + block.MethodComment = methodSummary ?? ""; + block.ReturnComment = returnsDescription ?? ""; + + return block; + } + + private static void ParseClass(ClassDeclarationSyntax classDeclaration) + { + string className = classDeclaration.Identifier.ValueText; + Directory.CreateDirectory(className); + + var methodDeclarations = classDeclaration.Members.OfType(); + foreach (MethodDeclarationSyntax methodDeclaration in methodDeclarations) + { + // 忽略非 public 方法 + if (methodDeclaration.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.PublicKeyword))) + { + string methodName = methodDeclaration.Identifier.ValueText; + string ReturnType = methodDeclaration.ReturnType.ToString(); + + // 忽略点击事件 + if (methodName.EndsWith("Click")) + { + continue; + } + + // 获取注释 + string comment = methodDeclaration.GetLeadingTrivia().ToString(); + CommentBlock commentBlock = ParseComment(comment); + + using var streamerWriter = File.CreateText(Path.Combine(className, $"{methodName}.md")); + + streamerWriter.WriteLine($"# {methodName}\n"); + + streamerWriter.WriteLine("## 定义\n"); + streamerWriter.WriteLine(commentBlock.MethodComment + "\n"); + + /// 方法原型 + streamerWriter.WriteLine("```CSharp"); + streamerWriter.WriteLine(methodDeclaration.ToString().Split("\n")[0]); + streamerWriter.WriteLine("```"); + + streamerWriter.WriteLine(); + + streamerWriter.WriteLine("## 请求参数\n"); + + if (methodDeclaration.ParameterList.Parameters.Count > 0) + { + streamerWriter.WriteLine("| 参数 | 类型 | 约束 | 说明 |"); + streamerWriter.WriteLine("| - | - | - | - |"); + } + + foreach (var parameter in methodDeclaration.ParameterList.Parameters) + { + var paramName = parameter.Identifier.ValueText; + var paramType = parameter.Type.ToString(); + var desc = string.Empty; + if (commentBlock.ParametersComment.ContainsKey(paramName)) + { + desc = commentBlock.ParametersComment[paramName]; + } + + streamerWriter.WriteLine($"| {paramName} | {paramType} | | {desc} |"); + } + streamerWriter.WriteLine("\n"); + + streamerWriter.WriteLine("## 返回\n"); + streamerWriter.WriteLine($"类型: {ReturnType}"); + streamerWriter.WriteLine(commentBlock.ReturnComment); + streamerWriter.WriteLine("\n"); + + streamerWriter.WriteLine("## 请求示例\n"); + streamerWriter.WriteLine("## 错误处理\n"); + streamerWriter.WriteLine("## 注意事项\n"); + } + } + } + + private static void ParseFile(string filePath) + { + string code = File.ReadAllText(filePath); + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code); + CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); + + var classDeclarations = root.Members.OfType() + .SelectMany(namespaceDeclaration => namespaceDeclaration.Members) + .OfType(); + + foreach (var classDeclaration in classDeclarations) + { + ParseClass(classDeclaration); + } + } + + static void Main(string[] args) + { + /* + if (args.Length == 0) + { + Console.WriteLine($"Usage: {Process.GetCurrentProcess().ProcessName} "); + return; + } + */ + + ParseFile(@"D:\project\Drawer\Drawer\UCDraw\UCDraw\UC\MainView.cs"); + } + } +} \ No newline at end of file