You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
6.6 KiB
C#

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<string, string> ParametersComment { get; set; } = new Dictionary<string, string>();
public string ReturnComment { get; set; } = string.Empty;
}
private static CommentBlock ParseComment(string comment)
{
CommentBlock block = new();
string newComment = "<root>\n" + comment.Replace("///", "") + "</root>";
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<MethodDeclarationSyntax>();
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");
string methodSignature = $"{methodDeclaration.Modifiers} {methodDeclaration.ReturnType} {methodDeclaration.Identifier}({methodDeclaration.ParameterList})";
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<NamespaceDeclarationSyntax>()
.SelectMany(namespaceDeclaration => namespaceDeclaration.Members)
.OfType<ClassDeclarationSyntax>();
foreach (var classDeclaration in classDeclarations)
{
ParseClass(classDeclaration);
}
}
static void Main(string[] args)
{
/*
if (args.Length == 0)
{
Console.WriteLine($"Usage: {Process.GetCurrentProcess().ProcessName} <xxx.cs>");
return;
}
*/
ParseFile(@"D:\project\Drawer\Drawer\UCDraw\UCDraw\UC\MainView.cs");
}
}
}