-
Notifications
You must be signed in to change notification settings - Fork 398
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[dev-v5] Refactoring the generator of API documentation (#3322)
- Loading branch information
Showing
34 changed files
with
1,481 additions
and
309 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
242 changes: 242 additions & 0 deletions
242
examples/Tools/FluentUI.Demo.DocApiGen/ApiClassGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
// ------------------------------------------------------------------------ | ||
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. | ||
// ------------------------------------------------------------------------ | ||
|
||
using FluentUI.Demo.DocApiGen.Extensions; | ||
using FluentUI.Demo.DocApiGen.Models; | ||
using System.Globalization; | ||
using System.Reflection; | ||
using System.Text; | ||
|
||
namespace FluentUI.Demo.DocApiGen; | ||
|
||
/// <summary> | ||
/// Engine to generate the documentation classes. | ||
/// </summary> | ||
public class ApiClassGenerator | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ApiClassOptions"/> class. | ||
/// </summary> | ||
/// <param name="assembly"></param> | ||
/// <param name="xmlDocumentation"></param> | ||
public ApiClassGenerator(Assembly assembly, FileInfo xmlDocumentation) | ||
{ | ||
Assembly = assembly; | ||
DocXmlReader = new LoxSmoke.DocXml.DocXmlReader(xmlDocumentation.FullName); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the assembly to generate the documentation. | ||
/// </summary> | ||
public Assembly Assembly { get; } | ||
|
||
/// <summary> | ||
/// Gets the summary reader. | ||
/// </summary> | ||
public LoxSmoke.DocXml.DocXmlReader DocXmlReader { get; } | ||
|
||
/// <summary> | ||
/// Gets the <see cref="ApiClass"/> for the specified component. | ||
/// </summary> | ||
/// <param name="type"></param> | ||
/// <returns></returns> | ||
public ApiClass FromTypeName(Type type) | ||
{ | ||
var options = new ApiClassOptions(Assembly, DocXmlReader) | ||
{ | ||
PropertyParameterOnly = false, | ||
}; | ||
|
||
return new ApiClass(type, options); | ||
} | ||
|
||
/// <summary> | ||
/// Generates the C# code for the documentation. | ||
/// </summary> | ||
/// <returns></returns> | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Not necessary")] | ||
public string GenerateCSharp() | ||
{ | ||
var code = new StringBuilder(); | ||
var assemblyInfo = GetAssemblyInfo(Assembly); | ||
|
||
code.AppendLine("// ------------------------------------------------------------------------"); | ||
code.AppendLine("// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. "); | ||
code.AppendLine("// ------------------------------------------------------------------------"); | ||
code.AppendLine(); | ||
code.AppendLine("//------------------------------------------------------------------------------"); | ||
code.AppendLine("// <auto-generated>"); | ||
code.AppendLine("// This code was generated by a tool."); | ||
code.AppendLine("//"); | ||
code.AppendLine("// Changes to this file may cause incorrect behavior and will be lost if"); | ||
code.AppendLine("// the code is regenerated."); | ||
code.AppendLine("//"); | ||
code.AppendLine("// Version: " + assemblyInfo.Version + " - " + assemblyInfo.Date); | ||
code.AppendLine("// </auto-generated>"); | ||
code.AppendLine("//------------------------------------------------------------------------------"); | ||
code.AppendLine(); | ||
code.AppendLine("using System.Reflection;"); | ||
code.AppendLine(); | ||
code.AppendLine("/// <summary />"); | ||
code.AppendLine("public class CodeComments"); | ||
code.AppendLine("{"); | ||
|
||
code.AppendLine(" /// <summary />"); | ||
code.AppendLine(" public static readonly IDictionary<string, IDictionary<string, string>> SummaryData = new Dictionary<string, IDictionary<string, string>>"); | ||
code.AppendLine(" {"); | ||
|
||
foreach (var type in Assembly.GetTypes().Where(i => i.IsValidType())) | ||
{ | ||
var apiClass = FromTypeName(type); | ||
var apiClassMembers = apiClass.ToDictionary(); | ||
|
||
if (apiClassMembers.Any()) | ||
{ | ||
code.AppendLine($" {{ \"{apiClass.Name}\", new Dictionary<string, string>"); | ||
code.AppendLine($" {{"); | ||
code.AppendLine($" {{ \"__summary__\", \"{FormatDescription(apiClass.Summary)}\" }},"); | ||
|
||
foreach (var member in apiClass.ToDictionary()) | ||
{ | ||
code.AppendLine($" {{ \"{member.Key}\", \"{FormatDescription(member.Value)}\" }},"); | ||
} | ||
|
||
code.AppendLine($" }}"); | ||
code.AppendLine($" }},"); | ||
} | ||
} | ||
|
||
code.AppendLine(" };"); | ||
code.AppendLine(); | ||
code.AppendLine(" /// <summary />"); | ||
code.AppendLine(" public static string GetSignature(MemberInfo member)"); | ||
code.AppendLine(" {"); | ||
code.AppendLine(" return member.MemberType == MemberTypes.Method"); | ||
code.AppendLine(" ? $\"{member.Name}({string.Join(\", \", ((MethodInfo)member).GetParameters().Select(p => p.ParameterType.Name))})\""); | ||
code.AppendLine(" : member.Name;"); | ||
code.AppendLine(" }"); | ||
code.AppendLine(); | ||
code.AppendLine(" /// <summary />"); | ||
code.AppendLine(" public static string GetSummary(MemberInfo member)"); | ||
code.AppendLine(" {"); | ||
code.AppendLine(" var name = member.Name;"); | ||
code.AppendLine(" var signature = GetSignature(member);"); | ||
code.AppendLine(); | ||
code.AppendLine(" return SummaryData.TryGetValue(name, out var comments) && comments.TryGetValue(signature, out var comment)"); | ||
code.AppendLine(" ? comment"); | ||
code.AppendLine(" : string.Empty;"); | ||
code.AppendLine(" }"); | ||
code.AppendLine("}"); | ||
code.AppendLine(); | ||
|
||
return code.ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Generates the JSON for the documentation. | ||
/// </summary> | ||
/// <returns></returns> | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Not necessary")] | ||
public string GenerateJson() | ||
{ | ||
var code = new StringBuilder(); | ||
var assemblyInfo = GetAssemblyInfo(Assembly); | ||
|
||
code.AppendLine("{"); | ||
code.AppendLine($" \"__Generated__\": {{"); | ||
code.AppendLine($" \"AssemblyVersion\": \"{assemblyInfo.Version}\","); | ||
code.AppendLine($" \"DateUtc\": \"{assemblyInfo.Date}\""); | ||
code.AppendLine($" }},"); | ||
|
||
foreach (var type in Assembly.GetTypes().Where(i => i.IsValidType())) | ||
{ | ||
var apiClass = FromTypeName(type); | ||
var apiClassMembers = apiClass.ToDictionary(); | ||
|
||
if (apiClassMembers.Any()) | ||
{ | ||
code.AppendLine($" \"{apiClass.Name}\": {{"); | ||
code.AppendLine($" \"__summary__\": \"{FormatDescription(apiClass.Summary)}\","); | ||
|
||
foreach (var member in apiClass.ToDictionary()) | ||
{ | ||
code.AppendLine($" \"{member.Key}\": \"{FormatDescription(member.Value)}\","); | ||
} | ||
|
||
RemoveLastComma(code); // Remove the last comma | ||
code.AppendLine($" }},"); | ||
} | ||
} | ||
|
||
RemoveLastComma(code); // Remove the last comma | ||
code.AppendLine("}"); | ||
code.AppendLine(); | ||
|
||
return code.ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Saves the documentation to a file. | ||
/// </summary> | ||
/// <param name="fileName"></param> | ||
/// <param name="format"></param> | ||
public void SaveToFile(string fileName, string format) | ||
{ | ||
if (File.Exists(fileName)) | ||
{ | ||
File.Delete(fileName); | ||
} | ||
|
||
if (format == "json") | ||
{ | ||
File.WriteAllText(fileName, GenerateJson()); | ||
} | ||
else | ||
{ | ||
File.WriteAllText(fileName, GenerateCSharp()); | ||
} | ||
} | ||
|
||
/// <summary /> | ||
private static string FormatDescription(string description) | ||
{ | ||
return description.Replace("\r\n", " ").Replace("\n", " ").Replace("\"", "\\\""); | ||
} | ||
|
||
/// <summary /> | ||
private static void RemoveLastComma(StringBuilder sb) | ||
{ | ||
if (sb == null || sb.Length == 0) | ||
{ | ||
return; | ||
} | ||
|
||
var lastIndex = sb.ToString().LastIndexOf(','); | ||
sb.Remove(lastIndex, sb.Length - lastIndex); | ||
sb.AppendLine(); | ||
} | ||
|
||
internal static (string Version, string Date) GetAssemblyInfo(Assembly assembly) | ||
{ | ||
// Assembly version | ||
string strVersion = default!; | ||
var versionAttribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>(); | ||
if (versionAttribute != null) | ||
{ | ||
var version = versionAttribute.InformationalVersion; | ||
var plusIndex = version.IndexOf('+'); | ||
if (plusIndex >= 0 && plusIndex + 9 < version.Length) | ||
{ | ||
strVersion = version[..(plusIndex + 9)]; | ||
} | ||
else | ||
{ | ||
strVersion = version; | ||
} | ||
} | ||
|
||
// Date | ||
return (strVersion, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture)); | ||
} | ||
} |
Oops, something went wrong.