Generator: WIP docs generation.

This commit is contained in:
Amer Koleci
2022-09-01 17:17:31 +02:00
parent 36c2f20ea0
commit 953fbfb207
8 changed files with 5875 additions and 6 deletions

View File

@@ -12,10 +12,12 @@ public sealed class CodeWriter : IDisposable
public int IndentLevel { get; private set; }
public string Api { get; }
public string DocFileName { get; }
public CodeWriter(string fileName, string api, string ns, params string[] usingNamespaces)
public CodeWriter(string fileName, string api, string docFileName, string ns, params string[] usingNamespaces)
{
Api = api;
DocFileName = docFileName;
_indentStrings = new string[10];
for (int i = 0; i < _indentStrings.Length; i++)

View File

@@ -8,7 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.4.35" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
<PackageReference Include="Microsoft.Windows.SDK.Win32Docs" Version="0.1.8-alpha" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,12 @@
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using MessagePack;
using Microsoft.Windows.SDK.Win32Docs;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Generator;
@@ -138,9 +143,211 @@ public static class Program
Generate(api!, outputPath, jsonFile);
}
// Generate docs
using FileStream docsStream = File.OpenRead(@"C:\Users\amerk\.nuget\packages\microsoft.windows.sdk.win32docs\0.1.8-alpha\apidocs.msgpack");
var data = MessagePackSerializer.Deserialize<Dictionary<string, ApiDetails>>(docsStream);
var documentationData = new Dictionary<string, ApiDetails>();
string[] prefixes = new[] { "DXGI" };
foreach (string key in data.Keys)
{
//Debug.WriteLine(key);
foreach (string prefix in prefixes)
{
if (key.StartsWith(prefix) || key.StartsWith("I" + prefix))
{
documentationData.Add(key, data[key]);
}
}
}
XmlWriterSettings settings = new()
{
Indent = true,
};
//settings.ConformanceLevel = ConformanceLevel.Fragment;
using (var writer = XmlWriter.Create(Path.Combine(outputPath, "DXGI.xml"), settings))
{
writer.WriteStartDocument();
writer.WriteStartElement(null, "doc", null);
foreach (var item in documentationData.Keys)
{
var doc = documentationData[item];
if (!string.IsNullOrEmpty(doc.Description) || doc.Parameters.Count > 0)
{
writer.WriteStartElement(null, "member", null);
writer.WriteAttributeString("name", item.Replace(".", "::"));
{
writer.WriteStartElement(null, "summary", null);
{
if (!string.IsNullOrEmpty(doc.Description))
{
writer.WriteStartElement(null, "para", null);
writer.WriteRaw(FormatMd(doc.Description));
writer.WriteEndElement(); // para
}
if (doc.HelpLink != null)
{
writer.WriteStartElement(null, "para", null);
writer.WriteString("Microsoft Docs: ");
writer.WriteStartElement(null, "see", null);
writer.WriteAttributeString("href", doc.HelpLink.ToString());
writer.WriteEndElement(); // see
writer.WriteEndElement(); // para
}
// Write params
foreach (var param in doc.Parameters)
{
if (!string.IsNullOrEmpty(param.Value))
{
writer.WriteStartElement(null, "param", null);
string paramName = param.Key;
if (paramName.StartsWith("pp") && char.IsUpper(paramName[2]))
{
paramName = paramName.Substring(2);
paramName = paramName[0].ToString().ToLower() + paramName.Substring(1);
}
else if (paramName.StartsWith("p") && char.IsUpper(paramName[1]))
{
paramName = paramName.Substring(1);
paramName = paramName[0].ToString().ToLower() + paramName.Substring(1);
}
else if (paramName.StartsWith("u") && char.IsUpper(paramName[1]))
{
paramName = paramName.Substring(1);
paramName = paramName[0].ToString().ToLower() + paramName.Substring(1);
}
else if (paramName.StartsWith("b") && char.IsUpper(paramName[1])) // bEnable
{
paramName = paramName.Substring(1);
paramName = paramName[0].ToString().ToLower() + paramName.Substring(1);
}
else if (char.IsUpper(paramName[0]) && paramName.Length > 1 && char.IsLower(paramName[1]))
{
paramName = paramName[0].ToString().ToLower() + paramName.Substring(1);
}
else if (paramName == "ID")
{
paramName = "id";
}
else if (paramName == "dwCookie")
{
paramName = "cookie";
}
writer.WriteAttributeString("name", paramName);
if (!param.Value.StartsWith("Type:"))
{
writer.WriteRaw(FormatMd(param.Value));
}
else
{
var lines = param.Value.Split('\n');
writer.WriteRaw(FormatMd(string.Join("\r\n", lines.Skip(2))));
}
writer.WriteEndElement(); // param
}
}
}
writer.WriteEndElement(); // summary
}
writer.WriteEndElement(); // comment
}
// Write fields
foreach (var fieldName in doc.Fields.Keys)
{
var field = doc.Fields[fieldName];
if (string.IsNullOrEmpty(field))
{
continue;
}
if (!field.StartsWith("Type:"))
{
// Enum value
writer.WriteStartElement(null, "member", null);
writer.WriteAttributeString("name", $"{item.Replace(".", "::")}::{fieldName}");
{
writer.WriteStartElement(null, "summary", null);
{
var a = FormatMd(field);
writer.WriteRaw(FormatMd(field));
}
writer.WriteEndElement(); // summary
}
writer.WriteEndElement(); // comment
}
else
{
// Struct field
writer.WriteStartElement(null, "member", null);
writer.WriteAttributeString("name", $"{item.Replace(".", "::")}::{fieldName}");
{
writer.WriteStartElement(null, "summary", null);
{
var lines = field.Split('\n');
writer.WriteRaw(FormatMd(string.Join("\r\n", lines.Skip(2))));
}
writer.WriteEndElement(); // summary
}
writer.WriteEndElement(); // comment
}
}
}
writer.WriteEndElement(); // comments
writer.WriteEndDocument();
}
return 0;
}
public static Regex MDLink = new(@"\[([A-z0-9<>\\]+)\]\(([^\)]+)\)");
public static Regex ImgLink = new(@"!\[([A-z0-9<>\\]+)\]\(([^\)]+)\)");
public static Regex Bold = new(@"\*\*([^ ^\*][^\*^\n]*)\*\*");
public static Regex Italics = new(@"\*([^ ^\*][^\*^\n]*)\*");
public static Regex MultilineCode = new(@"```[A-z]*([^`]+)```");
public static Regex InlineCode = new(@"`([^`]+)`");
public static Regex Struct = new Regex(@"struct DML_[A-z0-9_]+_OPERATOR_DESC\r\n{[^}]+};", RegexOptions.Multiline);
public static string FormatMd(string value)
{
value = ImgLink.Replace(value, "");
value = MDLink.Replace(value, "<a href=\"https://docs.microsoft.com$2\">$1</a>");
value = Bold.Replace(value, "<b>$1</b>");
value = Italics.Replace(value, "<i>$1</i>");
value = MultilineCode.Replace(value, "<code>$1</code>");
value = InlineCode.Replace(value, "<c>$1</c>");
value = value.Replace("<code>s<code>", "<c>s</c>");
value = value.Replace("ns-d3d12video-d3d12_video_process_luma_key\"\"", "\"ns-d3d12video-d3d12_video_process_luma_key\"");
value = value.Replace("&L", "&amp;l");
value = value.Replace("& ", "&amp; ");
value = value.Replace(" > ", " &gt; ");
value = value.Replace(" < ", " &lt; ");
value = value.Replace(" >= ", " &gt;= ");
value = value.Replace(" <= ", " &lt;= ");
value = value.Replace("<-", "&lt;-");
value = value.Replace("->", "-&gt;");
value = value.Replace("\n>", "\n&gt;");
value = value.Replace("&mdash;", "—");
return value;
}
private static void Generate(ApiData api, string outputPath, string jsonFile)
{
string[] splits = jsonFile.Split(".", StringSplitOptions.RemoveEmptyEntries);
@@ -168,6 +375,7 @@ public static class Program
using var writer = new CodeWriter(
Path.Combine(outputFolder, fileName),
$"{folderRoot}.{ns}",
$"DXGI",
$"Win32.{folderRoot}.{ns}");
GenerateConstants(writer, api);
@@ -219,7 +427,7 @@ public static class Program
writer.WriteLine($"#region Enums");
foreach (ApiType enumType in api.Types.Where(item => item.Kind.ToLowerInvariant() == "enum"))
{
GenerateEnum(writer, enumType);
GenerateEnum(writer, enumType, false);
}
writer.WriteLine($"#endregion Enums");
writer.WriteLine();
@@ -271,7 +479,7 @@ public static class Program
foreach (ApiType enumType in createdEnums.Values)
{
GenerateEnum(writer, enumType);
GenerateEnum(writer, enumType, true);
}
writer.WriteLine($"#endregion Generated Enums");
@@ -286,12 +494,17 @@ public static class Program
writer.WriteLine();
}
private static void GenerateEnum(CodeWriter writer, ApiType enumType)
private static void GenerateEnum(CodeWriter writer, ApiType enumType, bool autoGenerated)
{
string csTypeName = GetDataTypeName(enumType.Name, out string enumPrefix);
string baseTypeName = GetTypeName(enumType.IntegerBase);
AddCsMapping(writer.Api, enumType.Name, csTypeName);
if (!autoGenerated)
{
writer.WriteLine($"/// <include file='../{writer.DocFileName}.xml' path='doc/member[@name=\"{enumType.Name}\"]/*' />");
}
if (s_generateUnmanagedDocs)
writer.WriteLine($"/// <unmanaged>{enumType.Name}</unmanaged>");
@@ -317,6 +530,12 @@ public static class Program
continue;
string enumValueName = GetPrettyFieldName(value.Name, enumPrefix);
if (!autoGenerated)
{
writer.WriteLine($"/// <include file='../{writer.DocFileName}.xml' path='doc/member[@name=\"{enumType.Name}::{value.Name}\"]/*' />");
}
if (s_generateUnmanagedDocs)
{
writer.WriteLine($"/// <unmanaged>{value.Name}</unmanaged>");
@@ -339,6 +558,8 @@ public static class Program
string csTypeName = GetDataTypeName(structType.Name, out string structPrefix);
AddCsMapping(writer.Api, structType.Name, csTypeName);
writer.WriteLine($"/// <include file='../{writer.DocFileName}.xml' path='doc/member[@name=\"{structType.Name}\"]/*' />");
if (s_generateUnmanagedDocs)
{
writer.WriteLine($"/// <unmanaged>{structType.Name}</unmanaged>");
@@ -353,6 +574,9 @@ public static class Program
string fieldValueName = GetPrettyFieldName(field.Name, structPrefix);
string fieldTypeName = GetTypeName(field.Type);
writer.WriteLine($"/// <include file='../{writer.DocFileName}.xml' path='doc/member[@name=\"{structType.Name}::{field.Name}\"]/*' />");
if (s_generateUnmanagedDocs)
{
//writer.WriteLine($"/// <unmanaged>{field.Name}</unmanaged>");