Skip to content

Commit 6ab4c6b

Browse files
authored
feat: Add categoryLayout option for metadata generation (#9965)
feat: add categoryLayout options
1 parent 798e748 commit 6ab4c6b

File tree

8 files changed

+96
-6
lines changed

8 files changed

+96
-6
lines changed

docs/reference/docfx-json-reference.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,17 @@ Specifies an optional set of MSBuild properties used when interpreting project f
401401

402402
Do not run `dotnet restore` before building the projects.
403403

404+
### `categoryLayout`
405+
406+
Specifies how categories in TOC are organized:
407+
408+
- `flattened` (default): Renders the namespaces as a plain label.
409+
- `nested`: Renders the categories in a nested tree form.
410+
- `none`: Don't render categoriy labels.
411+
412+
> [!NOTE]
413+
> This setting is valid when using `apiPage` or `markdown` output format. `mref` format don't support categories.
414+
404415
### `namespaceLayout`
405416

406417
Specifies how namespaces in TOC are organized:

schemas/docfx.schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,16 @@
647647
"default": false,
648648
"description": "Do not run dotnet restore before building the projects."
649649
},
650+
"categoryLayout": {
651+
"type": "string",
652+
"description": "Defines how categories in TOC are organized. This setting is valid when using `apiPage` or `markdown` output format. `mref` format don't support categories.",
653+
"default": "flattened",
654+
"enum": [
655+
"flattened",
656+
"nested",
657+
"none"
658+
]
659+
},
650660
"namespaceLayout": {
651661
"type": "string",
652662
"description": "Defines how namespaces in TOC are organized.",

src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private static List<TocNode> CreateToc(List<(IAssemblySymbol symbol, Compilation
4949
var ext = config.OutputFormat is MetadataOutputFormat.Markdown ? ".md" : ".yml";
5050
var toc = assemblies.SelectMany(a => CreateToc(a.symbol.GlobalNamespace, a.compilation)).ToList();
5151

52-
SortToc(toc, root: true);
52+
SortToc(toc, null);
5353

5454
YamlUtility.Serialize(Path.Combine(config.OutputFolder, "toc.yml"), toc, YamlMime.TableOfContent);
5555
return toc;
@@ -211,11 +211,11 @@ IMethodSymbol method when SymbolHelper.IsOperator(method) => TocNodeType.Operato
211211
}
212212
}
213213

214-
static void SortToc(List<TocNode> items, bool root)
214+
void SortToc(List<TocNode> items, TocNode? parentTocNode)
215215
{
216216
items.Sort((a, b) => a.type.CompareTo(b.type) is var r && r is 0 ? a.name.CompareTo(b.name) : r);
217217

218-
if (!root)
218+
if (parentTocNode != null)
219219
{
220220
InsertCategory(TocNodeType.Class, "Classes");
221221
InsertCategory(TocNodeType.Struct, "Structs");
@@ -233,13 +233,43 @@ static void SortToc(List<TocNode> items, bool root)
233233
foreach (var item in items)
234234
{
235235
if (item.items is not null)
236-
SortToc(item.items, root: false);
236+
SortToc(item.items, item);
237237
}
238238

239239
void InsertCategory(TocNodeType type, string name)
240240
{
241-
if (items.FirstOrDefault(i => i.type == type) is { } node)
242-
items.Insert(items.IndexOf(node), new() { name = name });
241+
switch (config.CategoryLayout)
242+
{
243+
// Don't insert category.
244+
case CategoryLayout.None:
245+
return;
246+
247+
// Insert category as clickable TocNode.
248+
case CategoryLayout.Nested:
249+
{
250+
// Skip when parent node is category node.
251+
if (parentTocNode != null && parentTocNode.type == TocNodeType.None)
252+
return;
253+
254+
// If items contains specified type node. Create new TocNode for category. and move related node to child node.
255+
if (items.FirstOrDefault(i => i.type == type) is { } node)
256+
{
257+
var head = new TocNode { name = name, items = items.Where(x => x.type == type).ToList() };
258+
items.Insert(items.IndexOf(node), head);
259+
items.RemoveAll(x => x.type == type);
260+
}
261+
return;
262+
}
263+
264+
// Insert category as text label.
265+
case CategoryLayout.Flattened:
266+
default:
267+
{
268+
if (items.FirstOrDefault(i => i.type == type) is { } node)
269+
items.Insert(items.IndexOf(node), new() { name = name });
270+
return;
271+
}
272+
}
243273
}
244274
}
245275
}

src/Docfx.Dotnet/DotnetApiCatalog.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ private static ExtractMetadataConfig ConvertConfig(MetadataJsonItemConfig config
162162
DisableDefaultFilter = configModel?.DisableDefaultFilter ?? false,
163163
DisableGitFeatures = configModel?.DisableGitFeatures ?? false,
164164
NoRestore = configModel?.NoRestore ?? false,
165+
CategoryLayout = configModel?.CategoryLayout ?? default,
165166
NamespaceLayout = configModel?.NamespaceLayout ?? default,
166167
MemberLayout = configModel?.MemberLayout ?? default,
167168
EnumSortOrder = configModel?.EnumSortOrder ?? default,

src/Docfx.Dotnet/ManagedReference/ExtractMetadataConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ internal class ExtractMetadataConfig
3131

3232
public bool NoRestore { get; init; }
3333

34+
public CategoryLayout CategoryLayout { get; init; }
35+
3436
public NamespaceLayout NamespaceLayout { get; init; }
3537

3638
public MemberLayout MemberLayout { get; init; }

src/Docfx.Dotnet/MetadataJsonConfig.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,27 @@ public enum MemberLayout
2222
SeparatePages,
2323
}
2424

25+
/// <summary>
26+
/// Specifies the layout of categories.
27+
/// </summary>
28+
internal enum CategoryLayout
29+
{
30+
/// <summary>
31+
/// Renders the categories as a a plain label.
32+
/// </summary>
33+
Flattened,
34+
35+
/// <summary>
36+
/// Renders the categories in a nested tree form.
37+
/// </summary>
38+
Nested,
39+
40+
/// <summary>
41+
/// Don't render category labels.
42+
/// </summary>
43+
None,
44+
}
45+
2546
/// <summary>
2647
/// Specifies the layout of namepsaces.
2748
/// </summary>
@@ -194,6 +215,16 @@ internal class MetadataJsonItemConfig
194215
[JsonPropertyName("noRestore")]
195216
public bool NoRestore { get; set; }
196217

218+
/// <summary>
219+
/// Defines how categories in TOC are organized:
220+
/// - `flattened` (default): Renders categories as a plain label.
221+
/// - `nested`: Renders categories in a nested tree form.
222+
/// - `none`: Don't render category labels.
223+
/// </summary>
224+
[JsonProperty("categoryLayout")]
225+
[JsonPropertyName("categoryLayout")]
226+
public CategoryLayout CategoryLayout { get; set; }
227+
197228
/// <summary>
198229
/// Defines how namespaces in TOC are organized:
199230
/// - `flattened` (default): Renders namespaces as a single flat list.

src/docfx/Models/MetadataCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ private static void MergeOptionsToConfig(MetadataCommandOptions options, DocfxCo
2727
item.ShouldSkipMarkup |= options.ShouldSkipMarkup;
2828
item.DisableGitFeatures |= options.DisableGitFeatures;
2929
item.DisableDefaultFilter |= options.DisableDefaultFilter;
30+
item.CategoryLayout = options.CategoryLayout ?? item.CategoryLayout;
3031
item.NamespaceLayout = options.NamespaceLayout ?? item.NamespaceLayout;
3132
item.MemberLayout = options.MemberLayout ?? item.MemberLayout;
3233
item.OutputFormat = options.OutputFormat ?? item.OutputFormat;

src/docfx/Models/MetadataCommandOptions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ internal class MetadataCommandOptions : LogOptions
4545
[CommandOption("--disableDefaultFilter")]
4646
public bool DisableDefaultFilter { get; set; }
4747

48+
[Description("Determines the category layout in table of contents.")]
49+
[CommandOption("--categoryLayout")]
50+
public CategoryLayout? CategoryLayout { get; set; }
51+
4852
[Description("Determines the namespace layout in table of contents.")]
4953
[CommandOption("--namespaceLayout")]
5054
public NamespaceLayout? NamespaceLayout { get; set; }

0 commit comments

Comments
 (0)