public TocItemInfo(FileAndType file, TocItemViewModel item) { Content = item; File = file; IsResolved = false; IsReferenceToc = false; }
private void BuildCore(TocItemViewModel item, FileModel model, IHostService hostService) { if (item == null) { return; } var linkToUids = new HashSet<string>(); var linkToFiles = new HashSet<string>(); if (Utility.IsSupportedRelativeHref(item.Href)) { linkToFiles.Add(ParseFile(item.Href)); } if (Utility.IsSupportedRelativeHref(item.Homepage)) { linkToFiles.Add(ParseFile(item.Homepage)); } if (!string.IsNullOrEmpty(item.TopicUid)) { linkToUids.Add(item.TopicUid); } model.LinkToUids = model.LinkToUids.Union(linkToUids); model.LinkToFiles = model.LinkToFiles.Union(linkToFiles); if (item.Items != null) { foreach (var i in item.Items) { BuildCore(i, model, hostService); } } }
public override FileModel Load(FileAndType file, ImmutableDictionary<string, object> metadata) { var filePath = file.FullPath; var tocViewModel = Utility.LoadSingleToc(filePath); var toc = new TocItemViewModel { Items = tocViewModel }; var repoDetail = GitUtility.GetGitDetail(filePath); var displayLocalPath = TypeForwardedToPathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, file.FullPath); // todo : metadata. return new FileModel(file, toc) { Uids = new[] { new UidDefinition(file.File, displayLocalPath) }.ToImmutableArray(), LocalPathFromRepoRoot = repoDetail?.RelativePath ?? filePath, LocalPathFromRoot = displayLocalPath }; }
private static void AssertTocEqual(TocItemViewModel expected, TocItemViewModel actual) { using (var swForExpected = new StringWriter()) { YamlUtility.Serialize(swForExpected, expected); using (var swForActual = new StringWriter()) { YamlUtility.Serialize(swForActual, actual); Assert.Equal(swForExpected.ToString(), swForActual.ToString()); } } }
public void ProcessMarkdownTocWithAbsoluteHrefShouldSucceed() { var content = @" #[Topic1](/href1) ##Topic1.1 ###[Topic1.1.1](/href1.1.1) ##[Topic1.2]() #[Topic2](http://href.com) "; var toc = _fileCreator.CreateFile(content, FileType.MarkdownToc); FileCollection files = new FileCollection(_inputFolder); files.Add(DocumentType.Article, new[] { toc }); BuildDocument(files); var outputRawModelPath = Path.Combine(_outputFolder, Path.ChangeExtension(toc, RawModelFileExtension)); Assert.True(File.Exists(outputRawModelPath)); var model = JsonUtility.Deserialize<TocItemViewModel>(outputRawModelPath); var expectedModel = new TocItemViewModel { Items = new TocViewModel { new TocItemViewModel { Name = "Topic1", Href = "/href1", TopicHref = "/href1", Items = new TocViewModel { new TocItemViewModel { Name = "Topic1.1", Items = new TocViewModel { new TocItemViewModel { Name = "Topic1.1.1", Href = "/href1.1.1", TopicHref = "/href1.1.1" } } }, new TocItemViewModel { Name = "Topic1.2", Href = string.Empty, TopicHref = string.Empty } } }, new TocItemViewModel { Name = "Topic2", Href = "http://href.com", TopicHref = "http://href.com" } } }; AssertTocEqual(expectedModel, model); }
public void ProcessYamlTocWithTocHrefShouldSucceed() { var file1 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent); var file2 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent, "sub1/sub2"); var referencedToc = _fileCreator.CreateFile($@" - name: Topic href: {Path.GetFileName(file2)} ", FileType.YamlToc, "sub1/sub2"); var content = $@" - name: Topic1 tocHref: /Topic1/ topicHref: /Topic1/index.html items: - name: Topic1.1 tocHref: /Topic1.1/ topicHref: /Topic1.1/index.html - name: Topic1.2 tocHref: /Topic1.2/ topicHref: /Topic1.2/index.html - name: Topic2 tocHref: {referencedToc} topicHref: {file2} "; var toc = _fileCreator.CreateFile(content, FileType.YamlToc); FileCollection files = new FileCollection(_inputFolder); files.Add(DocumentType.Article, new[] { file1, file2, toc, referencedToc }); BuildDocument(files); var outputRawModelPath = Path.Combine(_outputFolder, Path.ChangeExtension(toc, RawModelFileExtension)); Assert.True(File.Exists(outputRawModelPath)); var model = JsonUtility.Deserialize<TocItemViewModel>(outputRawModelPath); var expectedModel = new TocItemViewModel { Items = new TocViewModel { new TocItemViewModel { Name = "Topic1", Href = "/Topic1/", TocHref = "/Topic1/", Homepage = "/Topic1/index.html", TopicHref = "/Topic1/index.html", Items = new TocViewModel { new TocItemViewModel { Name = "Topic1.1", Href = "/Topic1.1/", TocHref = "/Topic1.1/", Homepage = "/Topic1.1/index.html", TopicHref = "/Topic1.1/index.html", }, new TocItemViewModel { Name = "Topic1.2", Href = "/Topic1.2/", TocHref = "/Topic1.2/", Homepage = "/Topic1.2/index.html", TopicHref = "/Topic1.2/index.html", } } }, new TocItemViewModel { Name = "Topic2", TocHref = referencedToc, Href = referencedToc, TopicHref = file2, Homepage = file2, } } }; AssertTocEqual(expectedModel, model); }
public void ProcessYamlTocWithReferencedTocShouldSucceed() { var file1 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent); var file2 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent, "sub1"); var file3 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent, "sub1/sub2"); var referencedToc = _fileCreator.CreateFile($@" - name: Topic href: {Path.GetFileName(file3)} ", FileType.YamlToc, "sub1/sub2"); var subToc = _fileCreator.CreateFile($@" #[Topic]({Path.GetFileName(file2)}) #[ReferencedToc](sub2/{Path.GetFileName(referencedToc)}) ", FileType.MarkdownToc, "sub1"); var content = $@" - name: Topic1 href: {file1} items: - name: Topic1.1 href: {subToc} items: - name: Topic1.1.1 - name: Topic1.1.2 - name: Topic1.2 href: {subToc} homepage: {file1} - name: Topic2 href: {referencedToc} "; var toc = _fileCreator.CreateFile(content, FileType.YamlToc); FileCollection files = new FileCollection(_inputFolder); files.Add(DocumentType.Article, new[] { file1, file2, file3, toc, subToc }); BuildDocument(files); var outputRawModelPath = Path.Combine(_outputFolder, Path.ChangeExtension(toc, RawModelFileExtension)); Assert.True(File.Exists(outputRawModelPath)); var model = JsonUtility.Deserialize<TocItemViewModel>(outputRawModelPath); var expectedModel = new TocItemViewModel { Items = new TocViewModel { new TocItemViewModel { Name = "Topic1", Href = file1, TopicHref = file1, Items = new TocViewModel { new TocItemViewModel { Name = "Topic1.1", Href = null, // For referenced toc, the content from the referenced toc is expanded as the items of current toc, and href is cleared TopicHref = null, Items = new TocViewModel { new TocItemViewModel { Name = "Topic", Href = file2, TopicHref = file2, }, new TocItemViewModel { Name = "ReferencedToc", Items = new TocViewModel { new TocItemViewModel { Name = "Topic", Href = file3, TopicHref = file3, } } } } }, new TocItemViewModel { Name = "Topic1.2", Href = file1, // For referenced toc, href should be overwritten by homepage TopicHref = file1, Homepage = file1, Items = new TocViewModel { new TocItemViewModel { Name = "Topic", Href = file2, TopicHref = file2, }, new TocItemViewModel { Name = "ReferencedToc", Items = new TocViewModel { new TocItemViewModel { Name = "Topic", Href = file3, TopicHref = file3, } } } } } } }, new TocItemViewModel { Name = "Topic2", Href = null, Items = new TocViewModel { new TocItemViewModel { Name = "Topic", Href = file3, TopicHref = file3, } } } } }; AssertTocEqual(expectedModel, model); // Referenced TOC File should not exist var referencedTocPath = Path.Combine(_outputFolder, Path.ChangeExtension(subToc, RawModelFileExtension)); Assert.False(File.Exists(referencedTocPath)); }
public void ProcessYamlTocWithFolderShouldSucceed() { var file1 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent); var file2 = _fileCreator.CreateFile(string.Empty, FileType.MarkdownContent, "sub"); var subToc = _fileCreator.CreateFile($@" #[Topic]({Path.GetFileName(file2)}) ", FileType.MarkdownToc, "sub"); var content = $@" - name: Topic1 href: {file1} items: - name: Topic1.1 href: {file1} homepage: {file2} - name: Topic1.2 href: sub/ homepage: {file1} - name: Topic2 href: sub/ "; var toc = _fileCreator.CreateFile(content, FileType.YamlToc); FileCollection files = new FileCollection(_inputFolder); files.Add(DocumentType.Article, new[] { file1, file2, toc, subToc }); BuildDocument(files); var outputRawModelPath = Path.Combine(_outputFolder, Path.ChangeExtension(toc, RawModelFileExtension)); Assert.True(File.Exists(outputRawModelPath)); var model = JsonUtility.Deserialize<TocItemViewModel>(outputRawModelPath); var expectedModel = new TocItemViewModel { Items = new TocViewModel { new TocItemViewModel { Name = "Topic1", Href = file1, TopicHref = file1, Items = new TocViewModel { new TocItemViewModel { Name = "Topic1.1", Href = file1, // For relative file, href keeps unchanged Homepage = file2, // Homepage always keeps unchanged TopicHref = file2, }, new TocItemViewModel { Name = "Topic1.2", Href = file1, // For relative folder, href should be overwritten by homepage Homepage = file1, TopicHref = file1, TocHref = "sub/toc.md", } } }, new TocItemViewModel { Name = "Topic2", Href = file2, TopicHref = file2, TocHref = "sub/toc.md", } } }; AssertTocEqual(expectedModel, model); }
/// <summary> /// Valid homepage href should: /// 1. relative file path /// 2. refer to a file /// 3. folder is not supported /// 4. refer to an `uid` /// </summary> /// <param name="href"></param> /// <returns></returns> private bool IsValidHomepageLink(TocItemViewModel tocItem) { if (!string.IsNullOrEmpty(tocItem.TopicUid)) { return true; } var hrefType = Utility.GetHrefType(tocItem.Href); if (hrefType == HrefType.RelativeFile) { return true; } return false; }
private TocItemViewModel GetDefaultHomepageItem(TocItemViewModel toc) { if (toc == null || toc.Items == null) { return null; } foreach (var item in toc.Items) { var tocItem = TreeIterator.PreorderFirstOrDefault(item, s => s.Items, s => IsValidHomepageLink(s)); if (tocItem != null) { return tocItem; } } return null; }
private void UpdateTocItemHref(TocItemViewModel toc, FileModel model, IDocumentBuildContext context) { if (toc.IsHrefUpdated) return; ResolveUid(toc, model, context); // Have to register TocMap after uid is resolved RegisterTocMap(toc, model.Key, context); toc.Homepage = ResolveHref(toc.Homepage, toc.OriginalHomepage, model, context, nameof(toc.Homepage)); toc.Href = ResolveHref(toc.Href, toc.OriginalHref, model, context, nameof(toc.Href)); toc.TocHref = ResolveHref(toc.TocHref, toc.OriginalTocHref, model, context, nameof(toc.TocHref)); toc.TopicHref = ResolveHref(toc.TopicHref, toc.OriginalTopicHref, model, context, nameof(toc.TopicHref)); if (toc.Items != null && toc.Items.Count > 0) { foreach (var item in toc.Items) { UpdateTocItemHref(item, model, context); } } toc.IsHrefUpdated = true; }
private void RegisterTocMap(TocItemViewModel item, string key, IDocumentBuildContext context) { // If tocHref is set, href is originally RelativeFolder type, and href is set to the homepage of TocHref, // So in this case, TocHref should be used to in TocMap // TODO: what if user wants to set TocHref? var tocHref = item.TocHref; var tocHrefType = Utility.GetHrefType(tocHref); if (tocHrefType == HrefType.MarkdownTocFile || tocHrefType == HrefType.YamlTocFile) { context.RegisterToc(key, tocHref); } else { var href = item.Href; // Should be original href from working folder starting with ~ if (Utility.IsSupportedRelativeHref(href)) { context.RegisterToc(key, href); } } }
private void ResolveUid(TocItemViewModel item, FileModel model, IDocumentBuildContext context) { if (item.TopicUid != null) { var xref = GetXrefFromUid(item.TopicUid, model, context); if (xref != null) { item.Href = item.TopicHref = xref.Href; if (string.IsNullOrEmpty(item.Name)) { item.Name = xref.Name; } string nameForCSharp; if (string.IsNullOrEmpty(item.NameForCSharp) && xref.TryGetValue("name.csharp", out nameForCSharp)) { item.NameForCSharp = nameForCSharp; } string nameForVB; if (string.IsNullOrEmpty(item.NameForVB) && xref.TryGetValue("name.vb", out nameForVB)) { item.NameForVB = nameForVB; } } } }
protected ParseState ApplyCore(ParseState state, int level, string text, string href, string uid = null) { if (level > state.Level + 1) { return new ErrorState(state, level, $"Skip level is not allowed. Toc content: {text}"); } // If current node is another node in higher or same level for (int i = state.Level; i >= level; --i) { state.Parents.Pop(); } var item = new TocItemViewModel { Name = text, Href = href, Uid = uid }; if (state.Parents.Count > 0) { var parent = state.Parents.Peek(); if (parent.Items == null) { parent.Items = new TocViewModel(); } parent.Items.Add(item); } else { state.Root.Add(item); } state.Parents.Push(item); if (state.Level == level) { return state; } return new NodeState(state, level); }