Skip to content

Commit 5c30140

Browse files
josefpihrtJochemHarmes
authored andcommitted
Generate documentation that can be published with Docusaurus (dotnet#918)
1 parent 621b11f commit 5c30140

40 files changed

+1103
-503
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Convert more syntax to implicit object creation (RCS1250) ([#910](https://github.com/josefpihrt/roslynator/pull/910)).
1313
- Add code fix for CS0037 ([#929](https://github.com/josefpihrt/roslynator/pull/929)).
14+
- [CLI] Generate reference documentation that can be published with Docusaurus ([#918](https://github.com/josefpihrt/roslynator/pull/918)).
15+
- `roslynator generate-doc --host docusaurus`
1416

1517
### Changed
1618

src/CommandLine.DocumentationGenerator/CommandLine.DocumentationGenerator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</PropertyGroup>
2121

2222
<ItemGroup>
23-
<PackageReference Include="DotMarkdown" Version="0.1.1" />
23+
<PackageReference Include="DotMarkdown" Version="0.2.0" />
2424
<PackageReference Include="CommandLineParser" Version="2.8.0" />
2525
</ItemGroup>
2626

src/CommandLine/Commands/GenerateDocCommand.cs

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Text;
99
using System.Threading;
1010
using System.Threading.Tasks;
11+
using DotMarkdown;
1112
using Microsoft.CodeAnalysis;
1213
using Roslynator.Documentation;
1314
using Roslynator.Documentation.Markdown;
@@ -26,9 +27,14 @@ public GenerateDocCommand(
2627
NamespaceDocumentationParts ignoredNamespaceParts,
2728
TypeDocumentationParts ignoredTypeParts,
2829
MemberDocumentationParts ignoredMemberParts,
30+
CommonDocumentationParts ignoredCommonParts,
2931
OmitMemberParts omitMemberParts,
3032
IncludeContainingNamespaceFilter includeContainingNamespaceFilter,
3133
Visibility visibility,
34+
DocumentationHost documentationHost,
35+
FilesLayout filesLayout,
36+
bool groupByCommonNamespace,
37+
InheritanceStyle inheritanceStyle,
3238
in ProjectFilter projectFilter) : base(projectFilter)
3339
{
3440
Options = options;
@@ -37,9 +43,14 @@ public GenerateDocCommand(
3743
IgnoredNamespaceParts = ignoredNamespaceParts;
3844
IgnoredTypeParts = ignoredTypeParts;
3945
IgnoredMemberParts = ignoredMemberParts;
46+
IgnoredCommonParts = ignoredCommonParts;
4047
OmitMemberParts = omitMemberParts;
4148
IncludeContainingNamespaceFilter = includeContainingNamespaceFilter;
4249
Visibility = visibility;
50+
DocumentationHost = documentationHost;
51+
FilesLayout = filesLayout;
52+
GroupByCommonNamespace = groupByCommonNamespace;
53+
InheritanceStyle = inheritanceStyle;
4354
}
4455

4556
public GenerateDocCommandLineOptions Options { get; }
@@ -54,17 +65,28 @@ public GenerateDocCommand(
5465

5566
public MemberDocumentationParts IgnoredMemberParts { get; }
5667

68+
public CommonDocumentationParts IgnoredCommonParts { get; }
69+
5770
public OmitMemberParts OmitMemberParts { get; }
5871

5972
public IncludeContainingNamespaceFilter IncludeContainingNamespaceFilter { get; }
6073

6174
public Visibility Visibility { get; }
6275

76+
public DocumentationHost DocumentationHost { get; }
77+
78+
public FilesLayout FilesLayout { get; }
79+
80+
public bool GroupByCommonNamespace { get; }
81+
82+
public InheritanceStyle InheritanceStyle { get; }
83+
6384
public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution projectOrSolution, CancellationToken cancellationToken = default)
6485
{
6586
AssemblyResolver.Register();
6687

6788
var documentationOptions = new DocumentationOptions(
89+
rootFileHeading: Options.Heading,
6890
ignoredNames: Options.IgnoredNames,
6991
preferredCultureName: Options.PreferredCulture,
7092
maxDerivedTypes: Options.MaxDerivedTypes,
@@ -82,26 +104,90 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
82104
includeInheritedAttributes: !Options.OmitInheritedAttributes,
83105
omitIEnumerable: !Options.IncludeIEnumerable,
84106
depth: Depth,
85-
inheritanceStyle: Options.InheritanceStyle,
107+
inheritanceStyle: InheritanceStyle,
86108
ignoredRootParts: IgnoredRootParts,
87109
ignoredNamespaceParts: IgnoredNamespaceParts,
88110
ignoredTypeParts: IgnoredTypeParts,
89111
ignoredMemberParts: IgnoredMemberParts,
112+
ignoredCommonParts: IgnoredCommonParts,
90113
includeContainingNamespaceFilter: IncludeContainingNamespaceFilter,
114+
filesLayout: FilesLayout,
91115
scrollToContent: Options.ScrollToContent);
92116

93117
ImmutableArray<Compilation> compilations = await GetCompilationsAsync(projectOrSolution, cancellationToken);
94118

95119
var documentationModel = new DocumentationModel(compilations, DocumentationFilterOptions.Instance, Options.AdditionalXmlDocumentation);
96-
#if DEBUG
97-
SourceReferenceProvider sourceReferenceProvider = (Options.SourceReferences.Any())
98-
? SourceReferenceProvider.Load(Options.SourceReferences)
99-
: null;
100120

101-
var generator = new MarkdownDocumentationGenerator(documentationModel, WellKnownUrlProviders.GitHub, documentationOptions, sourceReferenceProvider);
102-
#else
103-
var generator = new MarkdownDocumentationGenerator(documentationModel, WellKnownUrlProviders.GitHub, documentationOptions);
121+
List<INamespaceSymbol> commonNamespaces = null;
122+
123+
if (GroupByCommonNamespace)
124+
{
125+
commonNamespaces = DocumentationUtility.FindCommonNamespaces(
126+
documentationModel.Types.Concat(documentationModel.GetExtendedExternalTypes()));
127+
}
128+
129+
UrlSegmentProvider urlSegmentProvider = new DefaultUrlSegmentProvider(FilesLayout, commonNamespaces);
130+
131+
var externalProviders = new MicrosoftDocsUrlProvider[] { MicrosoftDocsUrlProvider.Instance };
132+
133+
DocumentationUrlProvider GetUrlProvider()
134+
{
135+
switch (DocumentationHost)
136+
{
137+
case DocumentationHost.GitHub:
138+
return new GitHubDocumentationUrlProvider(urlSegmentProvider, externalProviders);
139+
case DocumentationHost.Docusaurus:
140+
return new DocusaurusDocumentationUrlProvider(urlSegmentProvider, externalProviders);
141+
default:
142+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
143+
}
144+
}
145+
146+
MarkdownWriterSettings GetMarkdownWriterSettings()
147+
{
148+
switch (DocumentationHost)
149+
{
150+
case DocumentationHost.GitHub:
151+
return MarkdownWriterSettings.Default;
152+
case DocumentationHost.Docusaurus:
153+
return new MarkdownWriterSettings(new MarkdownFormat(angleBracketEscapeStyle: AngleBracketEscapeStyle.EntityRef));
154+
default:
155+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
156+
}
157+
}
158+
159+
MarkdownWriterSettings markdownWriterSettings = GetMarkdownWriterSettings();
160+
161+
DocumentationWriter CreateDocumentationWriter(DocumentationContext context)
162+
{
163+
MarkdownWriter writer = MarkdownWriter.Create(new StringBuilder(), markdownWriterSettings);
164+
165+
switch (DocumentationHost)
166+
{
167+
case DocumentationHost.GitHub:
168+
return new MarkdownDocumentationWriter(context, writer);
169+
case DocumentationHost.Docusaurus:
170+
return new DocusaurusDocumentationWriter(context, writer);
171+
default:
172+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
173+
}
174+
}
175+
176+
SourceReferenceProvider sourceReferenceProvider = null;
177+
#if DEBUG
178+
if (Options.SourceReferences.Any())
179+
sourceReferenceProvider = SourceReferenceProvider.Load(Options.SourceReferences);
104180
#endif
181+
var context = new DocumentationContext(
182+
documentationModel,
183+
GetUrlProvider(),
184+
documentationOptions,
185+
c => CreateDocumentationWriter(c),
186+
sourceReferenceProvider: sourceReferenceProvider,
187+
commonNamespaces: commonNamespaces);
188+
189+
var generator = new DocumentationGenerator(context);
190+
105191
string directoryPath = Options.Output;
106192

107193
if (!Options.NoDelete

src/CommandLine/Commands/GenerateDocRootCommand.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text;
88
using System.Threading;
99
using System.Threading.Tasks;
10+
using DotMarkdown;
1011
using Microsoft.CodeAnalysis;
1112
using Roslynator.Documentation;
1213
using Roslynator.Documentation.Markdown;
@@ -24,13 +25,15 @@ public GenerateDocRootCommand(
2425
RootDocumentationParts ignoredParts,
2526
IncludeContainingNamespaceFilter includeContainingNamespaceFilter,
2627
Visibility visibility,
28+
DocumentationHost documentationHost,
2729
in ProjectFilter projectFilter) : base(projectFilter)
2830
{
2931
Options = options;
3032
Depth = depth;
3133
IgnoredParts = ignoredParts;
3234
IncludeContainingNamespaceFilter = includeContainingNamespaceFilter;
3335
Visibility = visibility;
36+
DocumentationHost = documentationHost;
3437
}
3538

3639
public GenerateDocRootCommandLineOptions Options { get; }
@@ -43,11 +46,14 @@ public GenerateDocRootCommand(
4346

4447
public Visibility Visibility { get; }
4548

49+
public DocumentationHost DocumentationHost { get; }
50+
4651
public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution projectOrSolution, CancellationToken cancellationToken = default)
4752
{
4853
AssemblyResolver.Register();
4954

5055
var documentationOptions = new DocumentationOptions(
56+
rootFileHeading: Options.Heading,
5157
ignoredNames: Options.IgnoredNames,
5258
rootDirectoryUrl: Options.RootDirectoryUrl,
5359
placeSystemNamespaceFirst: !Options.NoPrecedenceForSystem,
@@ -62,7 +68,60 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
6268

6369
var documentationModel = new DocumentationModel(compilations, DocumentationFilterOptions.Instance);
6470

65-
var generator = new MarkdownDocumentationGenerator(documentationModel, WellKnownUrlProviders.GitHub, documentationOptions);
71+
UrlSegmentProvider urlSegmentProvider = DefaultUrlSegmentProvider.Hierarchical;
72+
73+
var externalProviders = new MicrosoftDocsUrlProvider[] { MicrosoftDocsUrlProvider.Instance };
74+
75+
DocumentationUrlProvider GetUrlProvider()
76+
{
77+
switch (DocumentationHost)
78+
{
79+
case DocumentationHost.GitHub:
80+
return new GitHubDocumentationUrlProvider(urlSegmentProvider, externalProviders);
81+
case DocumentationHost.Docusaurus:
82+
return new DocusaurusDocumentationUrlProvider(urlSegmentProvider, externalProviders);
83+
default:
84+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
85+
}
86+
}
87+
88+
MarkdownWriterSettings GetMarkdownWriterSettings()
89+
{
90+
switch (DocumentationHost)
91+
{
92+
case DocumentationHost.GitHub:
93+
return MarkdownWriterSettings.Default;
94+
case DocumentationHost.Docusaurus:
95+
return new MarkdownWriterSettings(new MarkdownFormat(angleBracketEscapeStyle: AngleBracketEscapeStyle.EntityRef));
96+
default:
97+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
98+
}
99+
}
100+
101+
MarkdownWriterSettings markdownWriterSettings = GetMarkdownWriterSettings();
102+
103+
DocumentationWriter CreateDocumentationWriter(DocumentationContext context)
104+
{
105+
MarkdownWriter writer = MarkdownWriter.Create(new StringBuilder(), markdownWriterSettings);
106+
107+
switch (DocumentationHost)
108+
{
109+
case DocumentationHost.GitHub:
110+
return new MarkdownDocumentationWriter(context, writer);
111+
case DocumentationHost.Docusaurus:
112+
return new DocusaurusDocumentationWriter(context, writer);
113+
default:
114+
throw new InvalidOperationException($"Unknown value '{DocumentationHost}'.");
115+
}
116+
}
117+
118+
var context = new DocumentationContext(
119+
documentationModel,
120+
GetUrlProvider(),
121+
documentationOptions,
122+
c => CreateDocumentationWriter(c));
123+
124+
var generator = new DocumentationGenerator(context);
66125

67126
string path = Options.Output;
68127

src/CommandLine/Commands/ListSymbolsCommand.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
165165
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
166166
using (var streamWriter = new StreamWriter(fileStream, Encodings.UTF8NoBom))
167167
using (MarkdownWriter markdownWriter = MarkdownWriter.Create(streamWriter, markdownWriterSettings))
168-
using (SymbolDefinitionWriter writer = new SymbolDefinitionMarkdownWriter(markdownWriter, SymbolFilterOptions, format, hierarchyRoot: hierarchyRoot, urlProvider: WellKnownUrlProviders.GitHub))
168+
using (SymbolDefinitionWriter writer = new SymbolDefinitionMarkdownWriter(
169+
markdownWriter,
170+
SymbolFilterOptions,
171+
format,
172+
hierarchyRoot: hierarchyRoot,
173+
urlProvider: new GitHubDocumentationUrlProvider(DefaultUrlSegmentProvider.Hierarchical, new MicrosoftDocsUrlProvider[] { MicrosoftDocsUrlProvider.Instance })))
169174
{
170175
writer.WriteDocument(assemblies, cancellationToken);
171176
}
@@ -351,7 +356,13 @@ private void TestOutput(
351356
WriteLine();
352357

353358
using (MarkdownWriter markdownWriter = MarkdownWriter.Create(ConsoleOut))
354-
using (SymbolDefinitionWriter writer = new SymbolDefinitionMarkdownWriter(markdownWriter, SymbolFilterOptions, format, default(SymbolDocumentationProvider), hierarchyRoot, WellKnownUrlProviders.GitHub))
359+
using (SymbolDefinitionWriter writer = new SymbolDefinitionMarkdownWriter(
360+
markdownWriter,
361+
SymbolFilterOptions,
362+
format,
363+
default(SymbolDocumentationProvider),
364+
hierarchyRoot,
365+
new GitHubDocumentationUrlProvider(DefaultUrlSegmentProvider.Hierarchical, new MicrosoftDocsUrlProvider[] { MicrosoftDocsUrlProvider.Instance })))
355366
{
356367
writer.WriteDocument(assemblies, cancellationToken);
357368
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
namespace Roslynator.CommandLine
4+
{
5+
internal enum DocumentationHost
6+
{
7+
GitHub,
8+
Docusaurus,
9+
}
10+
}

src/CommandLine/OptionNames.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@ internal static class OptionNames
1111
public const string EndOfLine = "end-of-line";
1212
public const string FixScope = "fix-scope";
1313
public const string Help = "help";
14+
public const string Host = "host";
1415
public const string IgnoredCompilerDiagnostics = "ignored-compiler-diagnostics";
1516
public const string IgnoredMemberParts = "ignored-member-parts";
1617
public const string IgnoredNamespaceParts = "ignored-namespace-parts";
1718
public const string IgnoredParts = "ignored-parts";
19+
public const string IgnoredCommonParts = "ignored-common-parts";
1820
public const string IgnoredProjects = "ignored-projects";
1921
public const string IgnoredRootParts = "ignored-root-parts";
2022
public const string IgnoredScope = "ignored-scope";
2123
public const string IgnoredTypeParts = "ignored-type-parts";
2224
public const string IncludeContainingNamespace = "include-containing-namespace";
2325
public const string IncludeSystemNamespace = "include-system-namespace";
26+
public const string InheritanceStyle = "inheritance-style";
2427
public const string Interactive = "interactive";
2528
public const string Layout = "layout";
2629
public const string Match = "match";

src/CommandLine/Options/AbstractGenerateDocCommandLineOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ public abstract class AbstractGenerateDocCommandLineOptions : MSBuildCommandLine
3434
MetaValue = "<DEPTH>")]
3535
public string Depth { get; set; }
3636

37+
[Option(
38+
longName: OptionNames.Host,
39+
Required = true,
40+
HelpText = "Defines a host where the content will be published. Allowed values are github or docusaurus.",
41+
MetaValue = "<HOST>")]
42+
public string Host { get; set; }
43+
3744
[Option(
3845
longName: "ignored-names",
3946
HelpText = "Defines a list of metadata names that should be excluded from a documentation. Namespace of type names can be specified.",

0 commit comments

Comments
 (0)