From f690ac744f255b90bdf67f3aad368949a409e516 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Mon, 3 Jul 2017 13:20:26 +0200 Subject: [PATCH] Introduce NavigationContext to make it easier to add new navigation properties in the future --- src/DocNet/Config.cs | 15 ++++++----- src/DocNet/Docnet.csproj | 1 + src/DocNet/Engine.cs | 15 ++++++++--- src/DocNet/INavigationElement.cs | 12 ++++----- src/DocNet/NavigatedPath.cs | 6 ++--- src/DocNet/NavigationContext.cs | 21 +++++++++++++++ src/DocNet/NavigationElement.cs | 12 ++++----- src/DocNet/NavigationLevel.cs | 26 +++++++++---------- src/DocNet/SimpleNavigationElement.cs | 48 ++++++++++++++++++----------------- 9 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 src/DocNet/NavigationContext.cs diff --git a/src/DocNet/Config.cs b/src/DocNet/Config.cs index fdfdf05..0b639b9 100644 --- a/src/DocNet/Config.cs +++ b/src/DocNet/Config.cs @@ -72,10 +72,11 @@ namespace Docnet /// Generates the search data, which is the json file called 'search_index.json' with search data of all pages as well as the docnet_search.htm file in the output. /// The search index is written to the root of the output folder. /// - internal void GenerateSearchData() + /// The navigation context. + internal void GenerateSearchData(NavigationContext navigationContext) { - GenerateSearchPage(); - GenerateSearchDataIndex(); + GenerateSearchPage(navigationContext); + GenerateSearchDataIndex(navigationContext); } internal void CopyThemeToDestination() @@ -128,10 +129,10 @@ namespace Docnet /// /// Generates the index of the search data. this is a json file with per page which has markdown a couple of data elements. /// - private void GenerateSearchDataIndex() + private void GenerateSearchDataIndex(NavigationContext navigationContext) { var collectedSearchEntries = new List(); - this.Pages.CollectSearchIndexEntries(collectedSearchEntries, new NavigatedPath(), this.PathSpecification); + this.Pages.CollectSearchIndexEntries(collectedSearchEntries, new NavigatedPath(), navigationContext); JObject searchIndex = new JObject(new JProperty("docs", new JArray( collectedSearchEntries.Select(e=>new JObject( @@ -145,7 +146,7 @@ namespace Docnet } - private void GenerateSearchPage() + private void GenerateSearchPage(NavigationContext navigationContext) { var activePath = new NavigatedPath(); activePath.Push(this.Pages); @@ -164,7 +165,7 @@ namespace Docnet searchSimpleElement.ExtraScriptProducerFunc = e=> @" "; - searchSimpleElement.GenerateOutput(this, activePath, this.PathSpecification); + searchSimpleElement.GenerateOutput(this, activePath, navigationContext); activePath.Pop(); } diff --git a/src/DocNet/Docnet.csproj b/src/DocNet/Docnet.csproj index 99b3653..917c8f0 100644 --- a/src/DocNet/Docnet.csproj +++ b/src/DocNet/Docnet.csproj @@ -58,6 +58,7 @@ + diff --git a/src/DocNet/Engine.cs b/src/DocNet/Engine.cs index 5078bd1..0857ab7 100644 --- a/src/DocNet/Engine.cs +++ b/src/DocNet/Engine.cs @@ -44,7 +44,14 @@ namespace Docnet { return 1; } - GeneratePages(); + + var navigationContext = new NavigationContext + { + MaxLevel = _loadedConfig.MaxLevelInToC, + PathSpecification = _loadedConfig.PathSpecification + }; + + GeneratePages(navigationContext); return 0; } @@ -86,7 +93,7 @@ namespace Docnet /// Generates the pages from the md files in the source, using the page template loaded and the loaded config. /// /// true if everything went ok, false otherwise - private void GeneratePages() + private void GeneratePages(NavigationContext navigationContext) { if(_input.ClearDestinationFolder) { @@ -98,9 +105,9 @@ namespace Docnet Console.WriteLine("Copying source folders to copy."); _loadedConfig.CopySourceFoldersToCopy(); Console.WriteLine("Generating pages in '{0}'", _loadedConfig.Destination); - _loadedConfig.Pages.GenerateOutput(_loadedConfig, new NavigatedPath(), _loadedConfig.PathSpecification); + _loadedConfig.Pages.GenerateOutput(_loadedConfig, new NavigatedPath(), navigationContext); Console.WriteLine("Generating search index"); - _loadedConfig.GenerateSearchData(); + _loadedConfig.GenerateSearchData(navigationContext); Console.WriteLine("Done!"); } } diff --git a/src/DocNet/INavigationElement.cs b/src/DocNet/INavigationElement.cs index 6a524be..c36c64b 100644 --- a/src/DocNet/INavigationElement.cs +++ b/src/DocNet/INavigationElement.cs @@ -35,24 +35,24 @@ namespace Docnet /// /// The active configuration to use for the output. /// The active path navigated through the ToC to reach this element. - /// The path specification. - void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification); + /// The navigation context. + void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext); /// /// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu. /// /// The navigated path to the current element, which doesn't necessarily have to be this element. /// The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path. - /// The path specification. + /// The navigation context. /// - string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification); + string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext); /// /// Collects the search index entries. These are created from simple navigation elements found in this container, which aren't index element. /// /// The collected entries. /// The active path currently navigated. - /// The path specification. - void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification); + /// The navigation context. + void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, NavigationContext navigationContext); /// /// Gets the target URL with respect to the . diff --git a/src/DocNet/NavigatedPath.cs b/src/DocNet/NavigatedPath.cs index c93f21c..920ac65 100644 --- a/src/DocNet/NavigatedPath.cs +++ b/src/DocNet/NavigatedPath.cs @@ -77,9 +77,9 @@ namespace Docnet /// aren't, are not expanded. /// /// The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path. - /// The path specification. + /// The navigation context. /// - public string CreateToCHTML(string relativePathToRoot, PathSpecification pathSpecification) + public string CreateToCHTML(string relativePathToRoot, NavigationContext navigationContext) { // the root container is the bottom element of this path. We use that container to build the root and navigate any node open along the navigated path. var rootContainer = this.Reverse().FirstOrDefault() as NavigationLevel; @@ -88,7 +88,7 @@ namespace Docnet // no root container, no TOC return string.Empty; } - return rootContainer.GenerateToCFragment(this, relativePathToRoot, pathSpecification); + return rootContainer.GenerateToCFragment(this, relativePathToRoot, navigationContext); } } } diff --git a/src/DocNet/NavigationContext.cs b/src/DocNet/NavigationContext.cs new file mode 100644 index 0000000..271c081 --- /dev/null +++ b/src/DocNet/NavigationContext.cs @@ -0,0 +1,21 @@ +namespace Docnet +{ + public class NavigationContext + { + public NavigationContext() + { + MaxLevel = 2; + } + + public NavigationContext(PathSpecification pathSpecification, int maxLevel) + : this() + { + PathSpecification = pathSpecification; + MaxLevel = maxLevel; + } + + public int MaxLevel { get; set; } + + public PathSpecification PathSpecification { get; set; } + } +} \ No newline at end of file diff --git a/src/DocNet/NavigationElement.cs b/src/DocNet/NavigationElement.cs index 11f210c..8638772 100644 --- a/src/DocNet/NavigationElement.cs +++ b/src/DocNet/NavigationElement.cs @@ -36,23 +36,23 @@ namespace Docnet /// /// The active configuration to use for the output. /// The active path navigated through the ToC to reach this element. - /// The path specification. - public abstract void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification); + /// The navigation context. + public abstract void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext); /// /// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu. /// /// The navigated path to the current element, which doesn't necessarily have to be this element. /// The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path. - /// The path specification. + /// The navigation context. /// - public abstract string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification); + public abstract string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext); /// /// Collects the search index entries. These are created from simple navigation elements found in this container, which aren't index element. /// /// The collected entries. /// The active path currently navigated. - /// The path specification. - public abstract void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification); + /// The navigation context. + public abstract void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, NavigationContext navigationContext); /// /// Gets the target URL with respect to the . diff --git a/src/DocNet/NavigationLevel.cs b/src/DocNet/NavigationLevel.cs index ba1d781..b81b4b8 100644 --- a/src/DocNet/NavigationLevel.cs +++ b/src/DocNet/NavigationLevel.cs @@ -106,13 +106,13 @@ namespace Docnet /// /// The collected entries. /// The active path currently navigated. - /// The path specification. - public override void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification) + /// The navigation context. + public override void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, NavigationContext navigationContext) { activePath.Push(this); foreach (var element in this.Value) { - element.CollectSearchIndexEntries(collectedEntries, activePath, pathSpecification); + element.CollectSearchIndexEntries(collectedEntries, activePath, navigationContext); } activePath.Pop(); } @@ -123,15 +123,15 @@ namespace Docnet /// /// The active configuration to use for the output. /// The active path navigated through the ToC to reach this element. - /// The path specification. - public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification) + /// The navigation context. + public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext) { activePath.Push(this); int i = 0; while (i < this.Value.Count) { var element = this.Value[i]; - element.GenerateOutput(activeConfig, activePath, pathSpecification); + element.GenerateOutput(activeConfig, activePath, navigationContext); i++; } activePath.Pop(); @@ -143,9 +143,9 @@ namespace Docnet /// /// The navigated path to the current element, which doesn't necessarily have to be this element. /// The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path. - /// The path specification. + /// The navigation context. /// - public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification) + public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { var fragments = new List(); if (!this.IsRoot) @@ -167,7 +167,7 @@ namespace Docnet // first render the level header, which is the index element, if present or a label. The root always has an __index element otherwise we'd have stopped at load. var elementStartTag = "
  • "; - var indexElement = this.GetIndexElement(pathSpecification); + var indexElement = this.GetIndexElement(navigationContext.PathSpecification); if (indexElement == null) { fragments.Add(string.Format("{0}{1}
  • ", elementStartTag, this.Name)); @@ -176,18 +176,18 @@ namespace Docnet { if (this.IsRoot) { - fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification)); + fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext)); } else { fragments.Add(string.Format("{0}{3}", - elementStartTag, relativePathToRoot, indexElement.GetFinalTargetUrl(pathSpecification), this.Name)); + elementStartTag, relativePathToRoot, indexElement.GetFinalTargetUrl(navigationContext.PathSpecification), this.Name)); } } // then the elements in the container. Index elements are skipped here. foreach (var element in this.Value) { - fragments.Add(element.GenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification)); + fragments.Add(element.GenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext)); } fragments.Add(""); } @@ -195,7 +195,7 @@ namespace Docnet { // just a link fragments.Add(string.Format(" {2}", - relativePathToRoot, this.GetFinalTargetUrl(pathSpecification), this.Name)); + relativePathToRoot, this.GetFinalTargetUrl(navigationContext.PathSpecification), this.Name)); } if (!this.IsRoot) { diff --git a/src/DocNet/SimpleNavigationElement.cs b/src/DocNet/SimpleNavigationElement.cs index db9df9b..e9fe7d0 100644 --- a/src/DocNet/SimpleNavigationElement.cs +++ b/src/DocNet/SimpleNavigationElement.cs @@ -27,6 +27,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; +using MarkdownDeep; namespace Docnet { @@ -34,13 +35,13 @@ namespace Docnet { #region Members private string _targetURLForHTML; - private List> _relativeH2LinksOnPage; // first element in Tuple is anchor name, second is name for ToC. + private readonly List _relativeLinksOnPage; #endregion public SimpleNavigationElement() { - _relativeH2LinksOnPage = new List>(); + _relativeLinksOnPage = new List(); } @@ -49,18 +50,19 @@ namespace Docnet /// /// The active configuration to use for the output. /// The active path navigated through the ToC to reach this element. - /// The path specification. + /// The navigation context. + /// /// - public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, PathSpecification pathSpecification) + public override void GenerateOutput(Config activeConfig, NavigatedPath activePath, NavigationContext navigationContext) { // if we're the __index element, we're not pushing ourselves on the path, as we're representing the container we're in, which is already on the path. if(!this.IsIndexElement) { activePath.Push(this); } - _relativeH2LinksOnPage.Clear(); + _relativeLinksOnPage.Clear(); var sourceFile = Utils.MakeAbsolutePath(activeConfig.Source, this.Value); - var destinationFile = Utils.MakeAbsolutePath(activeConfig.Destination, this.GetTargetURL(pathSpecification)); + var destinationFile = Utils.MakeAbsolutePath(activeConfig.Destination, this.GetTargetURL(navigationContext.PathSpecification)); var sb = new StringBuilder(activeConfig.PageTemplateContents.Length + 2048); var content = string.Empty; this.MarkdownFromFile = string.Empty; @@ -70,7 +72,7 @@ namespace Docnet this.MarkdownFromFile = File.ReadAllText(sourceFile, Encoding.UTF8); // Check if the content contains @@include tag content = Utils.IncludeProcessor(this.MarkdownFromFile, Utils.MakeAbsolutePath(activeConfig.Source, activeConfig.IncludeFolder)); - content = Utils.ConvertMarkdownToHtml(content, Path.GetDirectoryName(destinationFile), activeConfig.Destination, sourceFile, _relativeH2LinksOnPage, activeConfig.ConvertLocalLinks); + content = Utils.ConvertMarkdownToHtml(content, Path.GetDirectoryName(destinationFile), activeConfig.Destination, sourceFile, _relativeLinksOnPage, activeConfig.ConvertLocalLinks); } else { @@ -90,10 +92,10 @@ namespace Docnet continue; } defaultMarkdown.AppendFormat("* [{0}]({1}{2}){3}", sibling.Name, relativePathToRoot, - sibling.GetFinalTargetUrl(pathSpecification), Environment.NewLine); + sibling.GetFinalTargetUrl(navigationContext.PathSpecification), Environment.NewLine); } defaultMarkdown.Append(Environment.NewLine); - content = Utils.ConvertMarkdownToHtml(defaultMarkdown.ToString(), Path.GetDirectoryName(destinationFile), activeConfig.Destination, string.Empty, _relativeH2LinksOnPage, activeConfig.ConvertLocalLinks); + content = Utils.ConvertMarkdownToHtml(defaultMarkdown.ToString(), Path.GetDirectoryName(destinationFile), activeConfig.Destination, string.Empty, _relativeLinksOnPage, activeConfig.ConvertLocalLinks); } else { @@ -112,8 +114,8 @@ namespace Docnet sb.Replace("{{Path}}", relativePathToRoot); sb.Replace("{{RelativeSourceFileName}}", Utils.MakeRelativePathForUri(activeConfig.Destination, sourceFile).TrimEnd('/')); sb.Replace("{{RelativeTargetFileName}}", Utils.MakeRelativePathForUri(activeConfig.Destination, destinationFile).TrimEnd('/')); - sb.Replace("{{Breadcrumbs}}", activePath.CreateBreadCrumbsHTML(relativePathToRoot, pathSpecification)); - sb.Replace("{{ToC}}", activePath.CreateToCHTML(relativePathToRoot, pathSpecification)); + sb.Replace("{{Breadcrumbs}}", activePath.CreateBreadCrumbsHTML(relativePathToRoot, navigationContext.PathSpecification)); + sb.Replace("{{ToC}}", activePath.CreateToCHTML(relativePathToRoot, navigationContext)); sb.Replace("{{ExtraScript}}", (this.ExtraScriptProducerFunc == null) ? string.Empty : this.ExtraScriptProducerFunc(this)); // the last action has to be replacing the content marker, so markers in the content which we have in the template as well aren't replaced @@ -132,15 +134,15 @@ namespace Docnet /// /// The collected entries. /// The active path currently navigated. - /// The path specification. - public override void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, PathSpecification pathSpecification) + /// The navigation context. + public override void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath, NavigationContext navigationContext) { activePath.Push(this); // simply convert ourselves into an entry if we're not an index if(!this.IsIndexElement) { var toAdd = new SearchIndexEntry(); - toAdd.Fill(this.MarkdownFromFile, this.GetTargetURL(pathSpecification), this.Name, activePath); + toAdd.Fill(this.MarkdownFromFile, this.GetTargetURL(navigationContext.PathSpecification), this.Name, activePath); collectedEntries.Add(toAdd); } activePath.Pop(); @@ -152,9 +154,9 @@ namespace Docnet /// /// The navigated path to the current element, which doesn't necessarily have to be this element. /// The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path. - /// The path specification. + /// The navigation context. /// - public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification) + public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { // index elements are rendered in the parent container. if(this.IsIndexElement) @@ -162,7 +164,7 @@ namespace Docnet return string.Empty; } - return PerformGenerateToCFragment(navigatedPath, relativePathToRoot, pathSpecification); + return PerformGenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext); } @@ -172,9 +174,9 @@ namespace Docnet /// /// The navigated path. /// The relative path to root. - /// The path specification. + /// The navigation context. /// - public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, PathSpecification pathSpecification) + public string PerformGenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext) { // we can't navigate deeper from here. If we are the element being navigated to, we are the current and will have to emit any additional relative URLs too. bool isCurrent = navigatedPath.Contains(this); @@ -190,15 +192,15 @@ namespace Docnet string.IsNullOrWhiteSpace(liClass) ? string.Empty : string.Format(" class=\"{0}\"", liClass), string.IsNullOrWhiteSpace(aClass) ? string.Empty : string.Format(" class=\"{0}\"", aClass), relativePathToRoot, - this.GetFinalTargetUrl(pathSpecification), + this.GetFinalTargetUrl(navigationContext.PathSpecification), this.Name)); - if(isCurrent && _relativeH2LinksOnPage.Any()) + if(isCurrent && _relativeLinksOnPage.Any()) { // generate relative links fragments.Add(string.Format("
      ", this.ParentContainer.IsRoot ? "currentrelativeroot" : "currentrelative")); - foreach(var p in _relativeH2LinksOnPage) + foreach(var p in _relativeLinksOnPage) { - fragments.Add(string.Format("
    • {1}
    • ", p.Item1, p.Item2)); + fragments.Add(string.Format("
    • {1}
    • ", p.Id, p.Name)); } fragments.Add("
    "); }