Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions XmlDocMarkdown.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpWithNulls", "tests\FSharpWithNulls\FSharpWithNulls.fsproj", "{3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlDocMarkdown.Docusaurus", "src\XmlDocMarkdown.Docusaurus\XmlDocMarkdown.Docusaurus.csproj", "{ED68E96C-561D-4541-9607-632A33FEA07C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -79,6 +81,10 @@ Global
{3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Release|Any CPU.Build.0 = Release|Any CPU
{ED68E96C-561D-4541-9607-632A33FEA07C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED68E96C-561D-4541-9607-632A33FEA07C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED68E96C-561D-4541-9607-632A33FEA07C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED68E96C-561D-4541-9607-632A33FEA07C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 2 additions & 0 deletions src/XmlDocMarkdown.Core/CommonArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@ internal static class CommonArgs
_ => throw new ArgsReaderException($"Invalid new line '{value}'. (Should be 'auto', 'lf', or 'crlf'.)"),
};
}

public static bool ReadUseTypeFoldersFlag(this ArgsReader args) => args.ReadFlag("type-folders");
}
}
6 changes: 6 additions & 0 deletions src/XmlDocMarkdown.Core/DefaultPathBuilderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace XmlDocMarkdown.Core;

internal class DefaultPathBuilderFactory : IPathBuilderFactory
{
public IPathBuilder Create() => new JekyllPathBuilder();
}
13 changes: 13 additions & 0 deletions src/XmlDocMarkdown.Core/IPathBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Reflection;

namespace XmlDocMarkdown.Core;

public interface IPathBuilder
{
public IPathBuilder WithNamespace(string @namespace);
public IPathBuilder WithType(TypeInfo typeInfo);
public IPathBuilder WithMemberName(string name);
public IPathBuilder WithPermalinkPretty();
public IPathBuilder WithTypeFolders();
public string Build();
}
6 changes: 6 additions & 0 deletions src/XmlDocMarkdown.Core/IPathBuilderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace XmlDocMarkdown.Core;

public interface IPathBuilderFactory
{
public IPathBuilder Create();
}
79 changes: 79 additions & 0 deletions src/XmlDocMarkdown.Core/JekyllPathBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Reflection;

namespace XmlDocMarkdown.Core;

internal class JekyllPathBuilder : IPathBuilder
{
private string? @namespace;
private string? typeName;
private string? memberName;
private bool hasPermalinkPretty;
private bool useTypeFolders;

public IPathBuilder WithNamespace(string @namespace)
{
this.@namespace = @namespace;
return this;
}

public IPathBuilder WithType(TypeInfo typeInfo)
{
typeName = typeInfo.Name;
return this;
}

public IPathBuilder WithMemberName(string name)
{
memberName = name;
return this;
}

public IPathBuilder WithPermalinkPretty()
{
hasPermalinkPretty = true;
return this;
}

public IPathBuilder WithTypeFolders()
{
useTypeFolders = true;
return this;
}

public string Build()
{
var relative = GetPermalink(typeName!);
var safeRelative = GetSafeName(relative);

if (hasPermalinkPretty)
safeRelative += "Type.md";
var path = $"{GetNamespaceUriName(@namespace)}/{safeRelative}";
if (memberName is not null)
path += $"/{memberName}";
else if (useTypeFolders)
path += $"/{safeRelative}";
return $"{path}.md";
}

private string GetPermalink(string path)
{
if (hasPermalinkPretty)
{
// permalinks paths cannot end in .md
var pos = path.LastIndexOf('.');
if (pos > 0)
path = path.Substring(0, pos);
}
return path.Replace("\\", "/");
}

private string GetSafeName(string name)
{
if (hasPermalinkPretty)
return name.Replace(".", ""); // Jekyll can't handle dots in file name.
return name;
}

private static string GetNamespaceUriName(string? namespaceName)
=> namespaceName ?? "global";
}
86 changes: 61 additions & 25 deletions src/XmlDocMarkdown.Core/MarkdownGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
Expand All @@ -14,6 +10,7 @@ namespace XmlDocMarkdown.Core
{
internal sealed class MarkdownGenerator
{
private readonly IPathBuilderFactory pathBuilderFactory;
public string? NewLine { get; set; }

public string? SourceCodePath { get; set; }
Expand All @@ -32,6 +29,13 @@ internal sealed class MarkdownGenerator

public IReadOnlyList<ExternalDocumentation>? ExternalDocs { get; set; }

public bool UseTypeFolders { get; set; }

public MarkdownGenerator(IPathBuilderFactory pathBuilderFactory)
{
this.pathBuilderFactory = pathBuilderFactory;
}

public IReadOnlyList<NamedText> GenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) =>
DoGenerateOutput(assembly, xmlDocAssembly).ToList();

Expand Down Expand Up @@ -116,11 +120,19 @@ private IEnumerable<NamedText> DoGenerateOutput(Assembly assembly, XmlDocAssembl
writer.WriteLine("| --- | --- |");
foreach (var typeInfo in typeGroup)
{
var relative = GetPermalink(typeInfo.Path);
var safeRelative = GetSafeName(relative);
var builder = pathBuilderFactory
.Create()
.WithNamespace(group.Namespace)
.WithType(typeInfo.TypeInfo);

if (UseTypeFolders)
builder.WithTypeFolders();

if (PermalinkPretty)
safeRelative += "Type";
var rel = MakeRelative(context.PageLocation, $"{GetNamespaceUriName(group.Namespace)}/{safeRelative}");
builder.WithPermalinkPretty();

var rel = builder.Build();

var typeText = GetShortSignatureMarkdown(typeInfo.ShortSignature, rel);
var summaryText = GetShortSummaryMarkdown(xmlDocAssembly, typeInfo.TypeInfo, context);
writer.WriteLine($"| {typeText} | {summaryText} |");
Expand Down Expand Up @@ -159,11 +171,19 @@ private IEnumerable<NamedText> DoGenerateOutput(Assembly assembly, XmlDocAssembl
writer.WriteLine("| --- | --- |");
foreach (var typeInfo in typeGroup)
{
var relative = GetPermalink(typeInfo.Path);
var safeRelative = GetSafeName(relative);
var builder =
pathBuilderFactory
.Create()
.WithNamespace(group.Namespace)
.WithType(typeInfo.TypeInfo);

if (UseTypeFolders)
builder.WithTypeFolders();

if (PermalinkPretty)
safeRelative += "Type";
var rel = MakeRelative(context.PageLocation, $"{GetNamespaceUriName(group.Namespace)}/{safeRelative}");
builder.WithPermalinkPretty();

var rel = builder.Build();
var typeText = GetShortSignatureMarkdown(typeInfo.ShortSignature, rel);
var summaryText = GetShortSummaryMarkdown(xmlDocAssembly, typeInfo.TypeInfo, context);
writer.WriteLine($"| {typeText} | {summaryText} |");
Expand All @@ -179,11 +199,19 @@ private IEnumerable<NamedText> DoGenerateOutput(Assembly assembly, XmlDocAssembl
{
if (visibleTypeRecord.Namespace == group.Namespace)
{
var relative = GetPermalink(visibleTypeRecord.Path);
var safeRelative = GetSafeName(relative);
var builder = pathBuilderFactory
.Create()
.WithNamespace(visibleTypeRecord.Namespace)
.WithType(visibleTypeRecord.TypeInfo);

if (UseTypeFolders)
builder.WithTypeFolders();

if (PermalinkPretty)
safeRelative += "Type.md";
var typePage = $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{safeRelative}";
builder.WithPermalinkPretty();

var typePage = builder.Build();

yield return WriteMemberPage(
path: typePage,
title: GetFullMemberName(visibleTypeRecord.TypeInfo),
Expand All @@ -207,8 +235,15 @@ private IEnumerable<NamedText> DoGenerateOutput(Assembly assembly, XmlDocAssembl

foreach (var memberGroup in memberGroups)
{
var path = pathBuilderFactory
.Create()
.WithNamespace(visibleTypeRecord.Namespace)
.WithType(visibleTypeRecord.TypeInfo)
.WithMemberName(memberGroup.MemberUriName)
.Build();

yield return WriteMemberPage(
path: $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{GetTypeUriName(visibleTypeRecord.TypeInfo)}/{memberGroup.MemberUriName}.md",
path: path,
parent: typePage,
title: memberGroup.MemberUriName,
memberGroup: memberGroup.Members,
Expand Down Expand Up @@ -474,7 +509,8 @@ private NamedText WriteMemberPage(string path, string parent, string title, IRea
{
var innerMembers = innerMemberGroup.Members;
var firstInnerMember = innerMembers[0];
var memberPath = firstInnerMember is TypeInfo ?

var memberPath = firstInnerMember is TypeInfo || UseTypeFolders ?
$"{GetMemberUriName(firstInnerMember)}{extension}" :
$"{GetTypeUriName(typeInfo)}/{GetMemberUriName(firstInnerMember)}{extension}";
var memberText = GetShortSignatureMarkdown(innerMemberGroup.ShortSignature, memberPath);
Expand Down Expand Up @@ -580,7 +616,7 @@ private NamedText WriteMemberPage(string path, string parent, string title, IRea
}
else
{
writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}](../{(typeInfo != null ? "" : "../")}{GetAssemblyUriName((declaringType ?? typeInfo!).Assembly)}{extension})");
writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}](../{(typeInfo != null ? (UseTypeFolders ? "../" : "") : "../")}{GetAssemblyUriName((declaringType ?? typeInfo!).Assembly)}{extension})");
}

if (typeInfo != null && declaringType == null && !string.IsNullOrEmpty(context.SourceCodePath) && !string.IsNullOrEmpty(context.RootNamespace))
Expand Down Expand Up @@ -960,12 +996,12 @@ private string GetPropertyGetSet(PropertyInfo propertyInfo)
var setVisibility = setMethod == null ? XmlDocVisibilityLevel.Private : GetMethodVisibility(setMethod);

if (getMethod != null && (setMethod == null || IsMorePrivateThan(setVisibility, Visibility)))
return " { get; }";
return " \\{ get; }";
if (getMethod == null || IsMorePrivateThan(getVisibility, Visibility))
return " { set; }";
return " \\{ set; }";

if (getVisibility == setVisibility)
return " { get; set; }";
return " \\{ get; set; }";
if (IsMorePrivateThan(getVisibility, setVisibility))
return $" {{ {GetAccessModifier(getMethod)} get; set; }}";
return $" {{ get; {GetAccessModifier(setMethod!)} set; }}";
Expand Down Expand Up @@ -2067,21 +2103,21 @@ private string WrapMarkdownRefLink(string text, MemberInfo? memberInfo, Markdown
if (context.MemberInfo != null)
{
if (typeInfo != null)
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}";
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{(UseTypeFolders ? "/" + GetSafeTypeUriName(typeInfo) : "")}{extension}";
else
path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}";
}
else if (context.TypeInfo != null)
{
if (typeInfo != null)
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}";
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{(UseTypeFolders ? "/" + GetSafeTypeUriName(typeInfo) : "")}{extension}";
else
path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}";
}
else
{
if (typeInfo != null)
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}";
path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{(UseTypeFolders ? "/" + GetSafeTypeUriName(typeInfo) : "")}{extension}";
else
path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}";
}
Expand Down
8 changes: 6 additions & 2 deletions src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public sealed class XmlDocMarkdownApp
/// Run the command-line application.
/// </summary>
/// <param name="args">The command-line arguments.</param>
/// <param name="pathBuilderFactory">The factory to create a Path Builder.</param>
/// <returns>The exit code.</returns>
public static int Run(IReadOnlyList<string> args)
public static int Run(IReadOnlyList<string> args, IPathBuilderFactory? pathBuilderFactory = null)
{
try
{
Expand Down Expand Up @@ -44,6 +45,7 @@ public static int Run(IReadOnlyList<string> args)
GenerateToc = argsReader.ReadTocFlag(),
TocPrefix = argsReader.ReadTocPrefix(),
NamespacePages = argsReader.ReadNamespacePagesFlag(),
UseTypeFolders = argsReader.ReadUseTypeFoldersFlag(),
};

var externalDocs = new List<ExternalDocumentation>();
Expand All @@ -67,7 +69,7 @@ public static int Run(IReadOnlyList<string> args)

argsReader.VerifyComplete();

var result = XmlDocMarkdownGenerator.Generate(input, outputPath, settings);
var result = XmlDocMarkdownGenerator.Generate(input, outputPath, settings, pathBuilderFactory);

foreach (var message in result.Messages)
Console.WriteLine(message);
Expand Down Expand Up @@ -143,6 +145,8 @@ private static void WriteUsage(TextWriter textWriter)
textWriter.WriteLine(" File containing table of contents in .yml format.");
textWriter.WriteLine(" --newline (auto|lf|crlf)");
textWriter.WriteLine(" The newline used in the output (default auto).");
textWriter.WriteLine(" --type-folders");
textWriter.WriteLine(" Type markdown will be inside the same folder has it's members.");
}
}
}
Loading