diff --git a/global.json b/global.json
deleted file mode 100644
index 391ba3c2..00000000
--- a/global.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "sdk": {
- "version": "8.0.100",
- "rollForward": "latestFeature"
- }
-}
diff --git a/src/XmlDocMarkdown.Core/ArgsReader.cs b/src/XmlDocMarkdown.Core/ArgsReader.cs
index adb73ed6..fbaf75e4 100644
--- a/src/XmlDocMarkdown.Core/ArgsReader.cs
+++ b/src/XmlDocMarkdown.Core/ArgsReader.cs
@@ -18,7 +18,7 @@ internal sealed class ArgsReader
/// args is null.
public ArgsReader(IEnumerable args)
{
- m_args = (args ?? throw new ArgumentNullException(nameof(args))).ToList();
+ m_args = [.. args ?? throw new ArgumentNullException(nameof(args))];
}
///
diff --git a/src/XmlDocMarkdown.Core/MarkdownGenerator.cs b/src/XmlDocMarkdown.Core/MarkdownGenerator.cs
index c9f445e0..29396e65 100644
--- a/src/XmlDocMarkdown.Core/MarkdownGenerator.cs
+++ b/src/XmlDocMarkdown.Core/MarkdownGenerator.cs
@@ -5,6 +5,7 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
+using System.Xml.Linq;
namespace XmlDocMarkdown.Core
{
@@ -31,7 +32,7 @@ internal sealed class MarkdownGenerator
public IReadOnlyList? ExternalDocs { get; set; }
public IReadOnlyList GenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) =>
- DoGenerateOutput(assembly, xmlDocAssembly).ToList();
+ [.. DoGenerateOutput(assembly, xmlDocAssembly)];
public static string GetCodeGenComment(string assemblyName) => $"";
@@ -326,7 +327,7 @@ private string GetPermalink(string path)
return path.Replace("\\", "/");
}
- private NamedText WriteMemberPage(string path, string parent, string title, IReadOnlyList memberGroup, MarkdownContext context)
+ private NamedText WriteMemberPage(string path, string parent, string title, List memberGroup, MarkdownContext context)
{
var extension = GetFileExtension();
@@ -682,7 +683,7 @@ private bool IsVisible(MemberInfo memberInfo)
public static XmlDocVisibilityLevel GetMostPrivate(params XmlDocVisibilityLevel[] visibilityLevels) => (XmlDocVisibilityLevel) visibilityLevels.Min(x => (int) x);
- private static string GetMemberHeading(IReadOnlyList membersInfos, int index)
+ private static string GetMemberHeading(List membersInfos, int index)
{
var heading = $"{GetFullMemberName(membersInfos[index])} {GetMemberGroupNoun([membersInfos[index]])}";
if (membersInfos.Count > 1)
@@ -690,7 +691,7 @@ private static string GetMemberHeading(IReadOnlyList membersInfos, i
return heading;
}
- private static string GetMemberGroupNoun(IReadOnlyList memberInfos)
+ private static string GetMemberGroupNoun(List memberInfos)
{
var plural = memberInfos.Count != 1;
@@ -709,11 +710,11 @@ private static string GetMemberGroupNoun(IReadOnlyList memberInfos)
if (memberInfos.All(x => x is FieldInfo))
return plural ? "fields" : "field";
if (memberInfos.All(x => x is TypeInfo))
- return GetTypeGroupNoun(memberInfos.Cast().ToList());
+ return GetTypeGroupNoun([.. memberInfos.Cast()]);
return plural ? "members" : "member";
}
- private static string GetTypeGroupNoun(IReadOnlyList typeInfos)
+ private static string GetTypeGroupNoun(List typeInfos)
{
var plural = typeInfos.Count != 1;
@@ -1250,7 +1251,7 @@ private IEnumerable GetFullSignatureParts(MemberInfo memberInfo, ICollec
yield return RenderTypeName(parameterInfo.ParameterType.GetTypeInfo(),
seeAlso: seeAlsoMembers,
- attributes: parameterInfo.GetCustomAttributes().ToList(),
+ attributes: [.. parameterInfo.GetCustomAttributes()],
nullableContextFlags: nullableContextFlags);
yield return " ";
@@ -1974,7 +1975,7 @@ private static Type[] GetGenericArguments(MemberInfo memberInfo)
return type.GenericTypeParameters;
var method = memberInfo as MethodInfo;
- return method?.GetGenericArguments() ?? Array.Empty();
+ return method?.GetGenericArguments() ?? [];
}
private static ParameterInfo[] GetParameters(MemberInfo memberInfo)
@@ -1988,7 +1989,7 @@ private static ParameterInfo[] GetParameters(MemberInfo memberInfo)
return propertyInfo.GetIndexParameters();
var method = memberInfo as MethodBase;
- return method?.GetParameters() ?? Array.Empty();
+ return method?.GetParameters() ?? [];
}
private static string GetParameterShortNames(MemberInfo memberInfo) =>
@@ -2119,7 +2120,7 @@ private string MakeRelative(string baseUri, string path)
private string? ToMarkdown(IEnumerable? inlines, MarkdownContext context) =>
inlines == null ? null : string.Concat(inlines.Select(x => ToMarkdown(x, context))).Trim();
- private IEnumerable ToMarkdown(IReadOnlyList blocks, MarkdownContext context)
+ private IEnumerable ToMarkdown(Collection blocks, MarkdownContext context)
{
for (var index = 0; index < blocks.Count; index++)
{
@@ -2158,6 +2159,61 @@ private IEnumerable ToMarkdown(IReadOnlyList blocks, Markdo
index++;
}
}
+ else if (block.ListKind == XmlDocListKind.Table)
+ {
+ if (string.IsNullOrWhiteSpace(block.Inlines[0].Text))
+ throw new ArgumentException("Input XML cannot be null or empty.", nameof(blocks));
+
+ if (block.Inlines[0].Text is { } text)
+ {
+ var doc = XDocument.Parse(text);
+ var table = (doc.Root?.Name.LocalName == "table"
+ ? doc.Root
+ : doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "table")) ?? throw new InvalidOperationException("No element was found.");
+ var thead = table.Elements().FirstOrDefault(e => e.Name.LocalName == "thead");
+ var tbody = table.Elements().FirstOrDefault(e => e.Name.LocalName == "tbody");
+
+ var headerRow = thead?.Descendants().FirstOrDefault(e => e.Name.LocalName == "tr");
+ var headers = headerRow != null ? GetCells(headerRow) : new List();
+
+ // Fallback: if there is no , use first data row as header.
+ var bodyRows = (tbody?.Elements().Where(e => e.Name.LocalName == "tr")
+ ?? table.Elements().Where(e => e.Name.LocalName == "tr")).ToList();
+
+ if (headers.Count == 0 && bodyRows.Count > 0)
+ {
+ headers = GetCells(bodyRows[0]);
+ bodyRows.RemoveAt(0);
+ }
+
+ if (headers.Count == 0)
+ throw new InvalidOperationException("No header row could be determined.");
+
+ var sb = new StringBuilder();
+
+ sb.AppendLine("| " + string.Join(" | ", headers.Select(EscapeCell)) + " |");
+ sb.AppendLine("| " + string.Join(" | ", headers.Select(_ => "---")) + " |");
+
+ foreach (var cells in bodyRows.Select(GetCells))
+ {
+ var list = cells;
+
+ // Keep markdown shape stable if rows have fewer/more columns.
+ if (cells.Count < headers.Count)
+ {
+ list.AddRange(Enumerable.Repeat("", headers.Count - cells.Count));
+ }
+ else if (cells.Count > headers.Count)
+ {
+ list = [.. cells.Take(headers.Count)];
+ }
+
+ sb.AppendLine("| " + string.Join(" | ", list.Select(EscapeCell)) + " |");
+ }
+
+ yield return sb.ToString();
+ }
+ }
else
{
if (block.IsCode)
@@ -2178,6 +2234,24 @@ private IEnumerable ToMarkdown(IReadOnlyList blocks, Markdo
}
}
+ private static List GetCells(XElement row) =>
+ [
+ .. row.Elements()
+ .Where(e => e.Name.LocalName == "th" || e.Name.LocalName == "td")
+ .Select(e => NormalizeWhitespace(e.Value))
+ ];
+
+ private static string NormalizeWhitespace(string value) =>
+ Regex.Replace(value ?? "", @"\s+", " ").Trim();
+
+ private static string EscapeCell(string value)
+ {
+ value ??= "";
+ value = value.Replace("|", "\\|");
+ value = value.Replace("\r\n", "
").Replace("\n", "
");
+ return value;
+ }
+
private sealed class MarkdownContext
{
public MarkdownContext(XmlDocAssembly xmlDocAssembly, IReadOnlyDictionary membersByXmlDocName, string assemblyFileName, string? sourceCodePath, string rootNamespace, string pageLocation)
diff --git a/src/XmlDocMarkdown.Core/XmlDocMember.cs b/src/XmlDocMarkdown.Core/XmlDocMember.cs
index ab44c814..a6b57832 100644
--- a/src/XmlDocMarkdown.Core/XmlDocMember.cs
+++ b/src/XmlDocMarkdown.Core/XmlDocMember.cs
@@ -208,6 +208,15 @@ private void AddElement(XElement xElement)
m_block?.Inlines.Add(new XmlDocInline { Text = (string) xElement.Attribute("name"), IsTypeParamRef = true });
break;
+ case "table":
+ // Insert the raw table
+ m_listKinds.Push(XmlDocListKind.Table);
+ NextBlock();
+ m_block?.Inlines.Add(new XmlDocInline { Text = xElement.ToString() });
+ m_listKinds.Pop();
+ NextBlock();
+ break;
+
default:
AddNodes(xElement.Nodes());
break;
@@ -241,7 +250,7 @@ private static XmlDocListKind GetListKind(XElement xElement) =>
private static string TrimCode(string text)
{
// trimming logic adapted from https://github.com/kzu/NuDoq
- var lines = text.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None).ToList();
+ var lines = text.Split([Environment.NewLine, "\n"], StringSplitOptions.None).ToList();
if (lines.Count != 0 && lines[0].Trim().Length == 0)
lines.RemoveAt(0);
diff --git a/src/XmlDocMarkdown.Core/XmlDocToc.cs b/src/XmlDocMarkdown.Core/XmlDocToc.cs
index 4fdc1532..5409ef00 100644
--- a/src/XmlDocMarkdown.Core/XmlDocToc.cs
+++ b/src/XmlDocMarkdown.Core/XmlDocToc.cs
@@ -67,7 +67,7 @@ private XmlDocToc GetOrCreate(string path, string title)
internal void Save(string tocPath)
{
- Directory.CreateDirectory(System.IO.Path.GetDirectoryName(tocPath)!);
+ Directory.CreateDirectory(System.IO.Path.GetDirectoryName(tocPath) ?? "");
using (var writer = new StreamWriter(tocPath, false, Encoding.UTF8))
{
writer.WriteLine("toc:");
diff --git a/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs b/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs
index ee384d57..590b9063 100644
--- a/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs
+++ b/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs
@@ -5,7 +5,7 @@
namespace XmlDocMarkdown.Tests
{
- public class MarkdownGeneratorTests
+ internal sealed class MarkdownGeneratorTests
{
[Test]
public void ExampleAssembly()
diff --git a/tools/ExampleAssembly/IsExternalInit.cs b/tools/ExampleAssembly/IsExternalInit.cs
index 50093a56..64e185f1 100644
--- a/tools/ExampleAssembly/IsExternalInit.cs
+++ b/tools/ExampleAssembly/IsExternalInit.cs
@@ -1,6 +1,8 @@
using System.ComponentModel;
+#pragma warning disable IDE0130
namespace System.Runtime.CompilerServices
+#pragma warning restore IDE0130
{
///
/// Bug fix for C# 9 record when not using .NET 5.