Skip to content

Commit

Permalink
Merge pull request #39 from rwrife/split-child-toc
Browse files Browse the repository at this point in the history
Option to split root TOC into multiple child TOC files
  • Loading branch information
mtirionMSFT authored Nov 9, 2023
2 parents eeaf2fe + 023c5f4 commit 217f56c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 12 deletions.
9 changes: 8 additions & 1 deletion src/DocFxTocGenerator/Domain/CommandlineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class CommandlineOptions
public bool UseOrder { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the .order files are used.
/// Gets or sets a value indicating whether the .override files are used.
/// </summary>
[Option('r', "override", Required = false, HelpText = "Use the .override files for TOC file name override. Format are raws of: filename-without-extension;Title you want")]
public bool UseOverride { get; set; }
Expand All @@ -58,5 +58,12 @@ public class CommandlineOptions
/// </summary>
[Option('n', "notwithone", Required = false, HelpText = "Do not auto-generate a file index when only contains 1 file. Additional to -i flag.")]
public bool NoAutoIndexWithOneFile { get; set; }

/// <summary>
/// Gets or sets a value indicating whether multiple toc files should be generated for each of the first child subfolders instead of building
/// one large toc in the root output folder.
/// </summary>
[Option('m', "multitoc", Required = false, HelpText = "Indicates how deep in the tree toc files should be generated for child subfolders, a depth of 0 is the root only.")]
public int SplitTocDepth { get; set; } = 0;
}
}
23 changes: 14 additions & 9 deletions src/DocFxTocGenerator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ This tool allow to generate a yaml compatible `toc.yml` file for DocFX.
```text
TocGenerator -d <docs folder> [-o <output folder>] [-vsi]
-d, --docfolder Required. Folder containing the documents.
-o, --outputfolder Folder to write the resulting toc.yml in.
-v, --verbose Show verbose messages.
-s, --sequence Use the .order files for TOC sequence. Format are raws of: filename-without-extension
-r, --override Use the .override files for TOC file name override. Format are raws of: filename-without-extension;Title you want
-i, --index Auto-generate a file index in each folder.
-g, --ignore Use the .ignore files for TOC directory ignore. Format are raws of directory names: directory-to-ignore
--help Display this help screen.
--version Display version information.
-d, --docfolder Required. Folder containing the documents.
-o, --outputfolder Folder to write the resulting toc.yml in.
-v, --verbose Show verbose messages.
-s, --sequence Use the .order files for TOC sequence. Format are raws of: filename-without-extension
-r, --override Use the .override files for TOC file name override. Format are raws of: filename-without-extension;Title you want
-i, --index Auto-generate a file index in each folder.
-g, --ignore Use the .ignore files for TOC directory ignore. Format are raws of directory names: directory-to-ignore
-m, --multitoc <depth> Generate multiple toc files for child folders down to a certain child depth, default is 0 (root only generation).
--help Display this help screen.
--version Display version information.
```

If the `-o or --outputfolder` is not provided, the output folder is set to the docfolder.
Expand Down Expand Up @@ -62,3 +63,7 @@ If there are files or directories which are not in the .order file, they will be
If the `-i or --index` parameter is provided, for every folder that doesn't have a README.md or INDEX.md, an INDEX.md is generated with the contents of that folder. That file is also added to the top of the list of files and directories in that folder.

The generated INDEX.md contains of an H1-header with the name of the folder, followed by a list of files and directories using their title and a link to the item.

## Generating mutiple child toc files

The `-m or --multitoc` option will control how far down the folder tree structure to generating toc files and allows you to generate multiple smaller, more managable TOC files for large DocFX projects. If the parameter is omitted, the default of 0 is assumed, which means only one large TOC at the root level will generated. Any value greater than 0 indicates how deep into the child folder structure TOC files will be generated, with the parent TOC having references to those located in the child folders.
66 changes: 64 additions & 2 deletions src/DocFxTocGenerator/TocGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ private static void RunLogic(CommandlineOptions o)
_message.Verbose($"Use .override : {_options.UseOverride}");
_message.Verbose($"Use .ignore : {_options.UseIgnore}");
_message.Verbose($"Auto index : {_options.AutoIndex}\n");
_message.Verbose($"Split toc depth : {_options.SplitTocDepth}\n");

if (!Directory.Exists(_options.DocFolder))
{
Expand All @@ -86,6 +87,67 @@ private static void RunLogic(CommandlineOptions o)
DirectoryInfo rootDir = new DirectoryInfo(_options.DocFolder);
WalkDirectoryTree(rootDir, tocRootItems);

if (_options.SplitTocDepth > 0)
{
WriteChildTocItems(tocRootItems, string.Empty, 0);
}
else
{
// write the tocitems to disk as one large file
WriteToc(tocRootItems, _options.OutputFolder);
}
}

/// <summary>
/// Walks the yaml tree looking for child nodes that are parents to other children
/// and corrects the paths to be relative the number toc files that should be generated.
/// </summary>
/// <param name="parentTocItem">Parent toc item to walk.</param>
/// <param name="parentFolder">Location where the toc should be written.</param>
/// <param name="treeDepth">Indicates the level the toc item is at in the tree.</param>
private static ICollection<TocItem> WriteChildTocItems(TocItem parentTocItem, string parentFolder, int treeDepth = 0)
{
var childTocItems = new TocItem();
foreach (var tocItem in parentTocItem.Items.OrderBy(x => x.Sequence))
{
ICollection<TocItem> childItems = null;

// split the href, may need the folder or filename later on
var hrefParts = tocItem.Href.Split('/');

// if the child is a leaf, use the href and remove the parent folder so it's relative
// if the child has children then the href will point to the toc in the child folder
var childHref = ((tocItem.Items?.Any() ?? false) && treeDepth < _options.SplitTocDepth)
? string.Join('/', hrefParts.Take(hrefParts.Length == 1 ? 1 : hrefParts.Length - 1))
: tocItem.Href.Substring(parentFolder.Length).TrimStart('/');

if (tocItem.Items?.Any() ?? false)
{
childItems = WriteChildTocItems(tocItem, treeDepth < _options.SplitTocDepth ? childHref : parentFolder, treeDepth + 1);
}

childTocItems.AddItem(new TocItem()
{
Title = tocItem.Title.Trim(),
Filename = tocItem.Filename,
Sequence = tocItem.Sequence,
SortableTitle = tocItem.SortableTitle,
Href = childItems != null && treeDepth < _options.SplitTocDepth ? null : childHref,
Items = childItems,
});
}

if (treeDepth <= _options.SplitTocDepth)
{
WriteToc(childTocItems, Path.Combine(_options.OutputFolder, parentFolder));
return null;
}

return childTocItems.Items;
}

private static void WriteToc(TocItem tocRootItems, string outputFolder)
{
// we have the TOC, so serialize to a string
using (StringWriter sw = new StringWriter())
{
Expand All @@ -96,10 +158,10 @@ private static void RunLogic(CommandlineOptions o)
}

// now write the TOC to disc
File.WriteAllText(Path.Combine(_options.OutputFolder, "toc.yml"), sw.ToString());
File.WriteAllText(Path.Combine(outputFolder, "toc.yml"), sw.ToString());
}

_message.Verbose($"{Path.Combine(_options.OutputFolder, "toc.yml")} created.");
_message.Verbose($"{Path.Combine(outputFolder, "toc.yml")} created.");
}

/// <summary>
Expand Down

0 comments on commit 217f56c

Please sign in to comment.