Skip to content

Commit

Permalink
fic toc template
Browse files Browse the repository at this point in the history
1. encode toc name
2. fix test.js

Fix md bug and support homepage in toc

update template
  • Loading branch information
chenkennt authored and vicancy committed Nov 5, 2015
1 parent 7dcfec6 commit 0ef467d
Show file tree
Hide file tree
Showing 22 changed files with 197 additions and 85 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
artifacts/
target/
/**/_site/
.vscode/

###############
# temp file #
Expand All @@ -27,7 +28,7 @@ target/
*.suo
*.user
*.userprefs
*.suo
*.sdf
*.sln.ide
*.cache
*.sln.metaproj
Expand Down
3 changes: 3 additions & 0 deletions Documentation/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"overwrite": "apispec/*.md",
"externalReference": [
],
"globalMetadata": {
"_appTitle": "docfx website"
},
"dest": "_site",
"template": "default"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public DocumentBuildContext(string buildOutputFolder) : this(buildOutputFolder,
public DocumentBuildContext(string buildOutputFolder, IEnumerable<FileAndType> allSourceFiles, ImmutableArray<string> externalReferencePackages)
{
BuildOutputFolder = buildOutputFolder;
AllSourceFiles = allSourceFiles.Select(ft => (string)(HostService.RootSymbol + (RelativePath)ft.File)).ToImmutableHashSet(FilePathComparer.OSPlatformSensitiveStringComparer);
AllSourceFiles = allSourceFiles.ToImmutableDictionary(ft => ((string)(HostService.RootSymbol + (RelativePath)ft.File)), FilePathComparer.OSPlatformSensitiveStringComparer);
ExternalReferencePackages = externalReferencePackages;
}

public string BuildOutputFolder { get; }

public ImmutableArray<string> ExternalReferencePackages { get; }

public ImmutableHashSet<string> AllSourceFiles { get; }
public ImmutableDictionary<string, FileAndType> AllSourceFiles { get; }

public Dictionary<string, string> FileMap { get; private set; } = new Dictionary<string, string>(FilePathComparer.OSPlatformSensitiveStringComparer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private static void CheckFileLink(HostService hostService, FileModel model, Save
result.LinkToFiles.RunAll(
fileLink =>
{
if (!hostService.SourceFiles.Contains(fileLink))
if (!hostService.SourceFiles.ContainsKey(fileLink))
{
if (fileLink.StartsWith("~/")) fileLink = fileLink.Substring(2);
var message = $"Invalid file link({fileLink}) in file \"{model.LocalPathFromRepoRoot}\"";
Expand Down
30 changes: 17 additions & 13 deletions src/Microsoft.DocAsCode.EntityModel/Builders/HostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ internal sealed class HostService : IHostService, IDisposable
{
public static readonly RelativePath RootSymbol = (RelativePath)"~/";
private readonly Dictionary<string, List<FileModel>> _uidIndex = new Dictionary<string, List<FileModel>>();
private readonly LruList<FileModel> _lru = LruList<FileModel>.Create(0x400, OnLruRemoving);
private readonly LruList<FileModel> _lru = LruList<FileModel>.Create(0xC00, OnLruRemoving);

public ImmutableList<FileModel> Models { get; private set; }

public ImmutableHashSet<string> SourceFiles { get; set; }
public ImmutableDictionary<string, FileAndType> SourceFiles { get; set; }

public Dictionary<FileAndType, FileAndType> FileMap { get; } = new Dictionary<FileAndType, FileAndType>();

Expand Down Expand Up @@ -88,18 +88,22 @@ where string.Equals(attr.Name, "src", StringComparison.OrdinalIgnoreCase) ||
{
string linkFile;
string anchor = null;
var index = link.Value.IndexOf('#');
if (index == -1)
{
linkFile = link.Value;
}
else
{
linkFile = link.Value.Remove(index);
anchor = link.Value.Substring(index);
}
if (PathUtility.IsRelativePath(linkFile))
if (PathUtility.IsRelativePath(link.Value))
{
var index = link.Value.IndexOf('#');
if (index == -1)
{
linkFile = link.Value;
}
else if (index == 0)
{
continue;
}
else
{
linkFile = link.Value.Remove(index);
anchor = link.Value.Substring(index);
}
var path = (RelativePath)ft.File + (RelativePath)linkFile;
if (path.ParentDirectoryCount > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public FileModel Load(FileAndType file, ImmutableDictionary<string, object> meta
{
foreach (var item in metadata)
{
if (page.Metadata.ContainsKey(item.Key))
if (!page.Metadata.ContainsKey(item.Key))
{
page.Metadata[item.Key] = item.Value;
}
Expand Down
135 changes: 101 additions & 34 deletions src/Microsoft.DocAsCode.EntityModel/Plugins/TocDocumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,8 @@ public ProcessingPriority GetProcessingPriority(FileAndType file)

public FileModel Load(FileAndType file, ImmutableDictionary<string, object> metadata)
{
TocViewModel toc = null;
var filePath = Path.Combine(file.BaseDir, file.File);
if ("toc.md".Equals(Path.GetFileName(file.File), StringComparison.OrdinalIgnoreCase))
{
toc = MarkdownTocReader.LoadToc(File.ReadAllText(filePath), file.File);
}
else if ("toc.yml".Equals(Path.GetFileName(file.File), StringComparison.OrdinalIgnoreCase))
{
toc = YamlUtility.Deserialize<TocViewModel>(filePath);
}
if (toc == null)
{
throw new NotSupportedException();
}
TocViewModel toc = LoadSingleToc(filePath);

var repoDetail = GitUtility.GetGitDetail(filePath);

Expand All @@ -76,35 +64,64 @@ public SaveResult Save(FileModel model)
};
}

public IEnumerable<FileModel> Prebuild(ImmutableList<FileModel> models, IHostService host)
{
return models;
}

public void Build(FileModel model, IHostService host)
{
model.File = Path.ChangeExtension(model.File, ".yml");
var toc = (TocViewModel)model.Content;
HashSet<string> links = new HashSet<string>();
Dictionary<string, HashSet<string>> tocMap = new Dictionary<string, HashSet<string>>();
UpdateRelativePathAndAddTocMap(toc, model, links, tocMap, host);
model.Properties.LinkToFiles = links.ToImmutableArray();
model.Properties.TocMap = tocMap.ToImmutableDictionary();
// todo : metadata.
}

public IEnumerable<FileModel> Postbuild(ImmutableList<FileModel> models, IHostService host)
{
return models;
}

private void UpdateRelativePathAndAddTocMap(TocViewModel toc, FileModel model, HashSet<string> links, Dictionary<string, HashSet<string>> tocMap, IHostService hostService)
{
if (toc == null) return;
var file = model.File;
var originalFile = model.OriginalFileAndType.File;
foreach(var item in toc)
foreach (var item in toc)
{
if (PathUtility.IsRelativePath(item.Href))
{
// Special handle for folder ends with '/'
FileAndType originalTocFile = null;

string fileName = Path.GetFileName(item.Href);
if (string.IsNullOrEmpty(fileName))
{
var href = item.Href + "toc.yml";
var tocPath = (RelativePath)"~/" + (RelativePath)href;
if (!hostService.SourceFiles.Contains(tocPath))
var absHref = ((RelativePath)href).BasedOn((RelativePath)file);
var tocPath = (RelativePath)"~/" + absHref;
if (!hostService.SourceFiles.TryGetValue(tocPath, out originalTocFile))
{
href = item.Href + "toc.md";
tocPath = (RelativePath)"~/" + (RelativePath)href;
if (!hostService.SourceFiles.Contains(tocPath))
absHref = ((RelativePath)href).BasedOn((RelativePath)file);
tocPath = (RelativePath)"~/" + absHref;
if (!hostService.SourceFiles.TryGetValue(tocPath, out originalTocFile))
{
Logger.LogError($"Unable to find either toc.yml or toc.md inside {item.Href}");
href = item.Href + "index.md"; // TODO: what if index.html exists?
var error = $"Unable to find either toc.yml or toc.md inside {item.Href}";
Logger.LogError(error, file: model.LocalPathFromRepoRoot);
throw new DocumentException(error);
}
}

Logger.LogInfo($"TOC file {href} inside {item.Href} is used", file: model.LocalPathFromRepoRoot);
item.Href = href;
}

// Add toc.yml to tocMap before change item.Href to home page
item.Href = (RelativePath)"~/" + ((RelativePath)item.Href).BasedOn((RelativePath)file);
HashSet<string> value;
if (tocMap.TryGetValue(item.Href, out value))
Expand All @@ -115,34 +132,84 @@ private void UpdateRelativePathAndAddTocMap(TocViewModel toc, FileModel model, H
{
tocMap[item.Href] = new HashSet<string>(FilePathComparer.OSPlatformSensitiveComparer) { originalFile };
}

links.Add(item.Href);

SetHomepage(item, originalTocFile, model);
}

UpdateRelativePathAndAddTocMap(item.Items, model, links, tocMap, hostService);
}
}

public IEnumerable<FileModel> Prebuild(ImmutableList<FileModel> models, IHostService host)
private void SetHomepage(TocItemViewModel item, FileAndType originalTocFile, FileModel model)
{
return models;
if (!string.IsNullOrEmpty(item.Homepage))
{
if (PathUtility.IsRelativePath(item.Homepage))
{
item.Href = (RelativePath)"~/" + ((RelativePath)item.Homepage).BasedOn((RelativePath)model.File);
}
else
{
item.Href = item.Homepage;
}

Logger.LogInfo($"Homepage {item.Homepage} is used.", file: model.LocalPathFromRepoRoot);
}
else if (originalTocFile != null)
{
var subTocPath = Path.Combine(originalTocFile.BaseDir, originalTocFile.File);
var subToc = LoadSingleToc(subTocPath);
var href = GetDefaultHomepage(subToc);

if (href == null)
{
var error = $"Unable to get default page for {item.Href}: no item containing relative file link is defined inside TOC {subTocPath}";
Logger.LogError(error, file: model.LocalPathFromRepoRoot);
throw new DocumentException(error);
}

item.Href = (RelativePath)item.Href + (RelativePath)href;
}
}

public void Build(FileModel model, IHostService host)
private string GetDefaultHomepage(TocViewModel toc)
{
model.File = Path.ChangeExtension(model.File, ".yml");
var toc = (TocViewModel)model.Content;
HashSet<string> links = new HashSet<string>();
Dictionary<string, HashSet<string>> tocMap = new Dictionary<string, HashSet<string>>();
UpdateRelativePathAndAddTocMap(toc, model, links, tocMap, host);
model.Properties.LinkToFiles = links.ToImmutableArray();
model.Properties.TocMap = tocMap.ToImmutableDictionary();
// todo : metadata.
foreach (var item in toc)
{
var href = TreeIterator.PreorderFirstOrDefault(item, s => s.Items, s => IsValidHomepageLink(item.Href));
if (href != null)
{
return href.Href;
}
}
return null;
}

public IEnumerable<FileModel> Postbuild(ImmutableList<FileModel> models, IHostService host)
/// <summary>
/// Valid homepage href should:
/// 1. relative file path
/// 2. refer to a file
/// 3. folder is not supported
/// </summary>
/// <param name="href"></param>
/// <returns></returns>
private bool IsValidHomepageLink(string href) {
return PathUtility.IsRelativePath(href) && !string.IsNullOrEmpty(Path.GetFileName(href));
}

private TocViewModel LoadSingleToc(string filePath)
{
return models;
if ("toc.md".Equals(Path.GetFileName(filePath), StringComparison.OrdinalIgnoreCase))
{
return MarkdownTocReader.LoadToc(File.ReadAllText(filePath), filePath);
}
else if ("toc.yml".Equals(Path.GetFileName(filePath), StringComparison.OrdinalIgnoreCase))
{
return YamlUtility.Deserialize<TocViewModel>(filePath);
}

throw new NotSupportedException($"{filePath} is not a valid TOC file, supported toc files could be \"toc.md\" or \"toc.yml\".");
}
}
}
18 changes: 18 additions & 0 deletions src/Microsoft.DocAsCode.EntityModel/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ public static void Preorder<T>(T current, T parent, Func<T, IEnumerable<T>> chil
}
}
}

public static T PreorderFirstOrDefault<T>(T current, Func<T, IEnumerable<T>> childrenGetter, Func<T, bool> predicate)
{
if (predicate(current)) return current;
if (childrenGetter == null) return default(T);
var children = childrenGetter(current);
if (children == null) return default(T);
foreach(var child in children)
{
var result = PreorderFirstOrDefault(child, childrenGetter, predicate);
if (!object.Equals(result, default(T)))
{
return result;
}
}

return default(T);
}
}

public static class YamlViewModelExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class TocItemViewModel
public string NameForVB { get; set; }
[YamlMember(Alias = "href")]
public string Href { get; set; }
[YamlMember(Alias = "homepage")]
public string Homepage { get; set; }
[YamlMember(Alias = "items")]
public TocViewModel Items { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ protected virtual StringBuffer Mangle(string text)

for (int i = 0; i < text.Length; i++)
{
var ch = text[i].ToString();
var ch = text[i];
if ((_mangleCounter++ & 1) == 0)
{
result = result + "&#x" + Convert.ToString(ch[0], 16) + ";";
result = result + "&#x" + Convert.ToString(ch, 16) + ";";
}
else
{
result = result + "&#" + ch + ";";
result = result + "&#" + Convert.ToString(ch, 10) + ";";
}
}
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.DocAsCode.Plugins/IHostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.DocAsCode.Plugins
public interface IHostService
{
MarkupResult Markup(string markdown, FileAndType ft);
ImmutableHashSet<string> SourceFiles { get; }
ImmutableDictionary<string, FileAndType> SourceFiles { get; }
ImmutableHashSet<string> GetAllUids();
ImmutableList<FileModel> GetModels(DocumentType? type = null);
ImmutableList<FileModel> LookupByUid(string uid);
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.DocAsCode.Utility/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.DocAsCode.Utility
using System.ComponentModel;
using System.Globalization;
using System.Collections.Generic;
using System.Web;

public static class TypeExtension
{
Expand Down Expand Up @@ -297,6 +298,8 @@ public static bool IsRelativePath(string path)
if (string.IsNullOrEmpty(path)) return false;
if (Uri.IsWellFormedUriString(path, UriKind.Absolute)) return false;
if (path.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) return false;
// it is possible that mailto: is mangled(encoded) to prevent spammers
if (HttpUtility.HtmlDecode(path).StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) return false;
return !Path.IsPathRooted(path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<HintPath>..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Web" />
<Reference Include="Tamir.SharpSSH, Version=1.1.1.13, Culture=neutral">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Tamir.SharpSSH.1.1.1.13\lib\Tamir.SharpSSH.dll</HintPath>
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.DocAsCode.Utility/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"dnx451": {
"frameworkAssemblies": {
"Microsoft.CSharp": "",
"System.Core": ""
"System.Core": "",
"System.Web": ""
}
}
}
Expand Down
Loading

0 comments on commit 0ef467d

Please sign in to comment.