diff --git a/src/DocNet/Config.cs b/src/DocNet/Config.cs index 2ff4195..482e665 100644 --- a/src/DocNet/Config.cs +++ b/src/DocNet/Config.cs @@ -263,7 +263,7 @@ public NavigationLevel Pages if(_pages == null) { JObject rawPages = _configData.Pages; - _pages = new NavigationLevel() {Name = "Home", IsRoot = true}; + _pages = new NavigationLevel(Source) {Name = "Home", IsRoot = true}; _pages.Load(rawPages); } return _pages; diff --git a/src/DocNet/NavigationLevel.cs b/src/DocNet/NavigationLevel.cs index 57bc38b..1ad43ed 100644 --- a/src/DocNet/NavigationLevel.cs +++ b/src/DocNet/NavigationLevel.cs @@ -33,30 +33,65 @@ namespace Docnet { public class NavigationLevel : NavigationElement> { - public NavigationLevel() : base() + #region Members + private readonly string _rootDirectory; + #endregion + + public NavigationLevel(string rootDirectory) + : base() { + this._rootDirectory = rootDirectory; this.Value = new List(); } public void Load(JObject dataFromFile) { - foreach(KeyValuePair child in dataFromFile) + foreach (KeyValuePair child in dataFromFile) { INavigationElement toAdd; - if(child.Value.Type == JTokenType.String) + if (child.Value.Type == JTokenType.String) { var nameToUse = child.Key; + var isIndexElement = child.Key == "__index"; - if(isIndexElement) + if (isIndexElement) { nameToUse = this.Name; } - toAdd = new SimpleNavigationElement() { Name = nameToUse, Value = child.Value.ToObject(), IsIndexElement = isIndexElement}; + + var childValue = child.Value.ToObject(); + if (childValue.EndsWith("**")) + { + var path = childValue.Replace("**", string.Empty) + .Replace('\\', Path.DirectorySeparatorChar) + .Replace('/', Path.DirectorySeparatorChar); + + if (!Path.IsPathRooted(path)) + { + path = Path.Combine(_rootDirectory, path); + } + + toAdd = CreateGeneratedLevel(path); + toAdd.Name = nameToUse; + } + else + { + toAdd = new SimpleNavigationElement + { + Name = nameToUse, + Value = childValue, + IsIndexElement = isIndexElement + }; + } } else { - var subLevel = new NavigationLevel() { Name = child.Key, IsRoot = false}; + var subLevel = new NavigationLevel(_rootDirectory) + { + Name = child.Key, + IsRoot = false + }; subLevel.Load((JObject)child.Value); toAdd = subLevel; } @@ -74,7 +109,7 @@ public void Load(JObject dataFromFile) public override void CollectSearchIndexEntries(List collectedEntries, NavigatedPath activePath) { activePath.Push(this); - foreach(var element in this.Value) + foreach (var element in this.Value) { element.CollectSearchIndexEntries(collectedEntries, activePath); } @@ -91,7 +126,7 @@ public override void GenerateOutput(Config activeConfig, NavigatedPath activePat { activePath.Push(this); int i = 0; - while(i(); - if(!this.IsRoot) + if (!this.IsRoot) { fragments.Add("
  • "); } - if(navigatedPath.Contains(this)) + if (navigatedPath.Contains(this)) { // we're expanded. If we're not root and on the top of the navigated path stack, our index page is the page we're currently generating the ToC for, so // we have to mark the entry as 'current' - if(navigatedPath.Peek() == this && !this.IsRoot) + if (navigatedPath.Peek() == this && !this.IsRoot) { fragments.Add("
      "); } @@ -130,24 +165,24 @@ public override string GenerateToCFragment(NavigatedPath navigatedPath, string r // 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.IndexElement; - if(indexElement == null) + if (indexElement == null) { fragments.Add(string.Format("{0}{1}
    • ", elementStartTag, this.Name)); } else { - if(this.IsRoot) + if (this.IsRoot) { fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot)); } else { - fragments.Add(string.Format("{0}{3}", elementStartTag, relativePathToRoot, HttpUtility.UrlPathEncode(indexElement.TargetURL), + fragments.Add(string.Format("{0}{3}", elementStartTag, relativePathToRoot, HttpUtility.UrlPathEncode(indexElement.TargetURL), this.Name)); } } // then the elements in the container. Index elements are skipped here. - foreach(var element in this.Value) + foreach (var element in this.Value) { fragments.Add(element.GenerateToCFragment(navigatedPath, relativePathToRoot)); } @@ -156,16 +191,94 @@ public override string GenerateToCFragment(NavigatedPath navigatedPath, string r else { // just a link - fragments.Add(string.Format(" {2}", + fragments.Add(string.Format(" {2}", relativePathToRoot, HttpUtility.UrlPathEncode(this.TargetURL), this.Name)); } - if(!this.IsRoot) + if (!this.IsRoot) { fragments.Add(""); } return string.Join(Environment.NewLine, fragments.ToArray()); } + private NavigationLevel CreateGeneratedLevel(string path) + { + var root = new NavigationLevel(_rootDirectory) + { + ParentContainer = this + }; + + foreach (var mdFile in Directory.GetFiles(path, "*.md", SearchOption.TopDirectoryOnly)) + { + var name = FindTitleInMdFile(mdFile); + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + var relativeFilePath = Utils.MakeRelativePath(mdFile, _rootDirectory); + + var item = new SimpleNavigationElement + { + Name = name, + Value = relativeFilePath, + ParentContainer = root + }; + + root.Value.Add(item); + } + + foreach (var directory in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly)) + { + var directoryInfo = new DirectoryInfo(directory); + + var subDirectoryNavigationElement = CreateGeneratedLevel(directory); + + subDirectoryNavigationElement.Name = directoryInfo.Name; + subDirectoryNavigationElement.ParentContainer = root; + + root.Value.Add(subDirectoryNavigationElement); + } + + return root; + } + + private string FindTitleInMdFile(string path) + { + var title = string.Empty; + + using (var fileStream = File.OpenRead(path)) + { + using (var streamReader = new StreamReader(fileStream)) + { + var line = string.Empty; + + while (string.IsNullOrWhiteSpace(line)) + { + line = streamReader.ReadLine(); + + if (!string.IsNullOrWhiteSpace(line)) + { + line = line.Trim(); + + while (line.StartsWith("#")) + { + line = line.Substring(1).Trim(); + } + + if (!string.IsNullOrWhiteSpace(line)) + { + title = line; + break; + } + } + } + } + } + + return title; + } + #region Properties public override string TargetURL @@ -173,7 +286,7 @@ public override string TargetURL get { var defaultElement = this.IndexElement; - if(defaultElement == null) + if (defaultElement == null) { return string.Empty; } @@ -187,20 +300,20 @@ public SimpleNavigationElement IndexElement get { var toReturn = this.Value.FirstOrDefault(e => e.IsIndexElement) as SimpleNavigationElement; - if(toReturn == null) + if (toReturn == null) { // no index element, add an artificial one. var path = string.Empty; - if(this.ParentContainer != null) + if (this.ParentContainer != null) { path = Path.GetDirectoryName(this.ParentContainer.TargetURL); } var nameToUse = this.Name.Replace(".", "").Replace('/', '_').Replace("\\", "_").Replace(":", "").Replace(" ", ""); - if(string.IsNullOrWhiteSpace(nameToUse)) + if (string.IsNullOrWhiteSpace(nameToUse)) { return null; } - toReturn = new SimpleNavigationElement() {ParentContainer = this, Value = string.Format("{0}{1}.md", path, nameToUse), Name = this.Name, IsIndexElement = true}; + toReturn = new SimpleNavigationElement() { ParentContainer = this, Value = string.Format("{0}{1}.md", path, nameToUse), Name = this.Name, IsIndexElement = true }; this.Value.Add(toReturn); } @@ -216,7 +329,8 @@ public override bool IsIndexElement { // never an index get { return false; } - set { + set + { // nop; } }