private void WriteAttachedBy(RenderingState state) { if (!state.Id.Type.StartsWith("AttachedUx")) { return; } if (!state.EntityCache.ContainsKey(state.Uri.Href)) { throw new ArgumentException($"TOC item {state.Uri.Href} was not found in entity cache"); } var underlyingEntity = state.EntityCache[state.Uri.Href]; if (!state.EntityCache.ContainsKey(underlyingEntity.Id.ParentId)) { throw new ArgumentException($"Parent of TOC item {state.Id.Id} ({underlyingEntity.Id.ParentId} was not found in entity cache"); } var attachedBy = state.EntityCache[underlyingEntity.Id.ParentId]; var attachedByName = attachedBy.Titles.IndexTitle; var attachedByHref = attachedBy.Uri.Href; state.AppendString($"<span class=\"table-of-contents-item-inline-attached-by\">"); state.AppendString($"(attached by <a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.CurrentPath, attachedByHref, "html"))}\">{EscapeHtml(attachedByName)}</a>)"); state.AppendString($"</span>"); }
private async Task WriteTocAsync(RenderingState state) { if (state.Document.TableOfContents.Count == 0) { return; } state.AppendString($"<section class=\"table-of-contents\">"); state.AppendString($"<input class=\"advance-items\" type=\"checkbox\"><span class=\"advance-items-label\">Show Uno properties and methods</span>"); if (state.Document.Entity.Id.Type == "Namespace" || state.Document.Entity.Id.Type == "Root") { // Handle root namespace pages differently than others; // Instead of grouping things based on where they are inherited from, we instead show everything // in a single table split by entity type. var byType = _tocOrganizer.SplitByType(state.Document.TableOfContents); await WriteTocByTypeAsync(byType, state); } else { // Split elements within each group based on where they are inherited from var byDeclaredIn = _tocOrganizer.SplitByDeclaredIn(state.Document.Entity, state.Document.TableOfContents); await WriteTocByDeclaredInAsync(byDeclaredIn, state); } state.AppendString($"</section>"); }
public async Task <ApiTocRenderingResult> RenderAsync(ApiReferenceId id, ApiReferenceUri uri, ApiReferenceTitle title, ApiReferenceFlags flags, ApiReferenceReturns returns, ApiReferenceComment comment, Dictionary <string, ApiReferenceEntity> entityCache, string currentPath, bool isAdvanced) { var state = new RenderingState(new StringBuilder(), id, uri, title, flags, returns, comment, entityCache, currentPath); var cssClasses = new List <string> { "table-of-contents-item" }; if (isAdvanced) { cssClasses.Add("is-advanced"); } state.AppendString($"<article class=\"{string.Join(" ", cssClasses)}\">"); WriteType(state); WriteLink(state); await WriteCommentAsync(state); state.AppendString($"</article>"); return(new ApiTocRenderingResult(state.Builder.ToString(), state.DeferredMarkdownIds, state.NumberOfCommentLines)); }
private void WriteReturns(RenderingState state) { if (state.Id.Type == "JsMethod" || state.Returns == null) { return; } state.AppendString($"<span class=\"table-of-contents-item-inline-returns\"> : "); if (!string.IsNullOrWhiteSpace(state.Returns.Href) && !state.Returns.IsVirtual) { state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.CurrentPath, state.Returns.Href, "html"))}\">"); } var title = state.Returns.Title; // If this is a List<T> or IList<T>, transform the return value to read "List of T" or "IList of T" if (title.StartsWith("List<") || title.StartsWith("IList<")) { var prefix = title.Substring(0, title.IndexOf("<")); var generics = title.Substring(prefix.Length + 1).TrimEnd('>'); title = $"{prefix} of {generics}"; } state.AppendString(EscapeHtml(title)); if (!string.IsNullOrWhiteSpace(state.Returns.Href) && !state.Returns.IsVirtual) { state.AppendString($"</a>"); } state.AppendString($"</span>"); }
private async Task WriteSeeAlsoAsync(RenderingState state) { if ((state.Document.Entity.Comment?.Attributes?.SeeAlso?.Count ?? 0) == 0) { return; } var tocItems = new List <ApiReferenceTocItem>(); foreach (var item in state.Document.Entity.Comment.Attributes.SeeAlso) { // Use the raw referenced entity if if an entity with the right id exists, // otherwise attempt to look up the keyword using the reference map var key = item; if (!state.EntityCache.ContainsKey(key)) { key = _referenceMap.GetTarget(item); } if (string.IsNullOrWhiteSpace(key) || !state.EntityCache.ContainsKey(key)) { _logger.LogError($"Unable to resolve seealso entry for {state.Document.Entity.Uri.Href} - no match in file system nor reference map: '{item}'"); continue; } var cached = state.EntityCache[key]; tocItems.Add(new ApiReferenceTocItem { Id = cached.Id, Uri = cached.Uri, Titles = cached.Titles, Comment = cached.Comment, Returns = cached.Returns, Parameters = cached.Parameters, Flags = cached.Flags }); } if (tocItems.Count == 0) { return; } _logger.LogInformation($"adding seealso to {state.Document.Entity.Uri.Href}"); state.AppendString($"<section class=\"see-also\">"); state.AppendString($"<section class=\"table-of-contents\">"); state.AppendString($"<section class=\"table-of-contents-section\">"); state.AppendString($"<h4 id=\"section-see-also\">See Also</h4>"); foreach (var item in tocItems) { var result = await _tocRenderer.RenderAsync(item.Id, item.Uri, item.Titles, item.Comment, state.EntityCache, state.Document.Entity.Uri.Href, false); state.MarkdownDeferredIds.AddRange(result.DeferredMarkdownIds); state.AppendString(result.Html); } state.AppendString($"</section>"); state.AppendString($"</section>"); state.AppendString($"</section>"); }
private async Task WriteMarkdownSectionAsync(RenderingState state, string className, string title, string markdown) { var markdownResult = await _preformatter.ApplyPreformattingAsync(markdown, state.Document.Entity.Uri.Href); state.MarkdownDeferredIds.AddRange(markdownResult.DeferredMarkdownIds); var result = await RenderMarkdownAsync(markdownResult.Markdown); state.MarkdownDeferredIds.Add(result.Id); state.AppendString($"<section class=\"documentation-{className}\">"); state.AppendString($"<h3 id=\"section-{className}\">{EscapeHtml(title)}</h3>"); state.AppendString(result.Html); state.AppendString($"</section>"); }
private void WriteLanguage(RenderingState state) { string language; switch (state.Id.Type.ToLowerInvariant()) { case "jsmodule": case "jsproperty": case "jsevent": case "jsmethod": language = "js"; break; case "uxclass": case "uxproperty": case "uxevent": case "attacheduxproperty": case "attacheduxevent": language = "ux"; break; default: language = "uno"; break; } var cssClasses = new List <string> { "table-of-contents-item-language", $"table-of-contents-item-language-{language}" }; state.AppendString($"<span class=\"{string.Join(" ", cssClasses)}\">{EscapeHtml(language)}</span>"); }
private void WriteAttachedAttributeDetails(RenderingState state) { var attachedInfo = ApiRenderingHelper.GetAttachedAttributeInfo(state.Document.Entity.Id, state.Document.Entity.Titles, state.Document.Entity.Parameters, state.Document.Entity.Attributes, state.EntityCache); if (attachedInfo == null) { return; } state.AppendString($"<p><em>"); state.AppendString($"Attached by <a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, attachedInfo.AttachedByHref, "html"))}\">{EscapeHtml(attachedInfo.AttachedByType)}</a>."); state.AppendString($"Use full name <code>{EscapeHtml(attachedInfo.FullName)}</code> in UX markup if ambiguous."); state.AppendString("</em></p>"); }
private void WriteLink(RenderingState state) { var title = ApiRenderingHelper.GetTitle(state.Id, state.Title, state.Comment, null, null, new Dictionary <string, ApiReferenceEntity>(), true); if (string.IsNullOrWhiteSpace(title)) { throw new ArgumentException($"No title could be generated for {state.Id.Id} in {state.CurrentPath} (type {state.Id.Type})"); } state.AppendString($"<h5>"); state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.CurrentPath, state.Uri.Href, "html"))}\">"); state.AppendString(EscapeHtml(title)); state.AppendString($"</a>"); WriteAttachedBy(state); WriteReturns(state); WriteLanguage(state); state.AppendString($"</h5>"); }
private async Task WriteReturnsAsync(RenderingState state) { var returns = BuildReturns(state.Document.Entity); if (returns == null) { return; } state.AppendString($"<section class=\"returns\">"); state.AppendString($"<h3 id=\"section-returns\">Returns</h3>"); state.AppendString($"<p>"); if (!string.IsNullOrWhiteSpace(returns.ReturnsTitle)) { if (!string.IsNullOrWhiteSpace(returns.ReturnsHref)) { state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, returns.ReturnsHref, "html"))}\">"); } state.AppendString(EscapeHtml(returns.ReturnsTitle)); if (!string.IsNullOrWhiteSpace(returns.ReturnsHref)) { state.AppendString($"</a>"); } } else if (!string.IsNullOrWhiteSpace(returns.TypeHint)) { state.AppendString(EscapeHtml(returns.TypeHint)); } state.AppendString($"</p>"); if (!string.IsNullOrWhiteSpace(returns.Comment)) { var result = await RenderMarkdownAsync(returns.Comment); state.MarkdownDeferredIds.Add(result.Id); state.AppendString(result.Html); } state.AppendString($"</section>"); }
private void WriteNotifications(RenderingState state) { if (state.Document.Entity.Comment?.Attributes?.Deprecated ?? false) { state.AppendString($"<section class=\"notifications\">"); state.AppendString($"<div class=\"alert alert-warning alert-api-deprecated\">"); state.AppendString($"This entity is deprecated and will be removed in a future release."); state.AppendString($"</div>"); state.AppendString($"</section>"); } else if (state.Document.Entity.Comment?.Attributes?.Experimental ?? false) { state.AppendString($"<section class=\"notifications\">"); state.AppendString($"<div class=\"alert alert-warning alert-api-experimental\">"); state.AppendString($"This entity is experimental and might be changed or removed in a future release."); state.AppendString($"</div>"); state.AppendString($"</section>"); } }
private async Task WriteTocByTypeAsync(List <TocTypeGroup> groups, RenderingState state) { foreach (var group in groups) { state.AppendString($"<h3 id=\"section-table-of-contents\">{EscapeHtml(group.Title)}</h3>"); state.AppendString($"<section class=\"table-of-contents-section\">"); foreach (var item in group.Items) { var result = await _tocRenderer.RenderAsync(item, state.EntityCache, state.Document.Entity.Uri.Href, item.IsAdvanced(state.Document.Entity.Id)); state.MarkdownDeferredIds.AddRange(result.DeferredMarkdownIds); state.AppendString(result.Html); state.Quality.TableOfContentsCommentLines.Add(item.Uri.IdUri, result.NumberOfCommentLines); } state.AppendString($"</section>"); } }
private void WriteType(RenderingState state) { var cssClasses = new List <string> { "table-of-contents-item-type", $"table-of-contents-item-type-{state.Id.Type.ToLowerInvariant()}", "fa", $"fa-{GetTypeIcon(state)}" }; state.AppendString($"<span class=\"{string.Join(" ", cssClasses)}\" title=\"{EscapeHtml(state.Id.Type)}\"></span>"); }
private async Task WriteCommentAsync(RenderingState state) { if (string.IsNullOrWhiteSpace(state.Comment?.Brief)) { return; } state.NumberOfCommentLines = CountNumberOfLines(string.IsNullOrWhiteSpace(state.Comment?.Full) ? "" : state.Comment.Full); var brief = state.Comment.Brief; if (state.Comment.Brief != state.Comment.Full) { brief += $" <a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.CurrentPath, state.Uri.Href, "html"))}\" class=\"table-of-contents-item-has-more\" title=\"There is more information available for this entry\"><i class=\"fa fa-ellipsis-h\"></i></a>"; } state.AppendString($"<div class=\"table-of-contents-item-brief\">"); var parsed = await _markdown.DeferredRenderAsync(brief); state.DeferredMarkdownIds.Add(parsed.Id); state.AppendString(parsed.Html); state.AppendString($"</div>"); }
private async Task WriteCommentAsync(RenderingState state) { if (string.IsNullOrWhiteSpace(state.Document.Entity.Comment?.Full)) { return; } state.Quality.NumberOfCommentLines = CountNumberOfLines(state.Document.Entity.Comment.Full); var comment = await _preformatter.ApplyPreformattingAsync(state.Document.Entity.Comment.Full, state.Document.Entity.Uri.Href); state.MarkdownDeferredIds.AddRange(comment.DeferredMarkdownIds); var parserResult = await RenderMarkdownAsync(comment.Markdown); state.MarkdownDeferredIds.Add(parserResult.Id); state.AppendString(parserResult.Html); }
private async Task WriteInterfacesAsync(RenderingState state) { if (state.Document.Entity.ImplementedInterfaces.Count == 0) { return; } state.AppendString($"<section class=\"interfaces\">"); state.AppendString($"<section class=\"table-of-contents\">"); state.AppendString($"<section class=\"table-of-contents-section has-advanced-items only-advanced-items\">"); state.AppendString($"<h4 id=\"section-table-of-contents-implemented-interfaces\">Implemented Interfaces</h4>"); foreach (var iface in state.Document.Entity.ImplementedInterfaces) { var result = await _tocRenderer.RenderAsync(iface.Id, iface.Uri, iface.Titles, iface.Comment, state.EntityCache, state.Document.Entity.Uri.Href, true); state.MarkdownDeferredIds.AddRange(result.DeferredMarkdownIds); state.AppendString(result.Html); } state.AppendString($"</section>"); state.AppendString($"</section>"); state.AppendString($"</section>"); }
private void WriteSubsectionLinks(RenderingState state) { var sections = new List <KeyValuePair <string, string> >(); if (!string.IsNullOrWhiteSpace(state.Document.Entity.Comment?.Ux)) { sections.Add(new KeyValuePair <string, string>("ux", "UX")); } if (state.Document.TableOfContents.Count > 0) { sections.Add(new KeyValuePair <string, string>("table-of-contents", "Table of Contents")); } if (!string.IsNullOrWhiteSpace(state.Document.Entity.Comment?.Remarks)) { sections.Add(new KeyValuePair <string, string>("remarks", "Remarks")); } if (!string.IsNullOrWhiteSpace(state.Document.Entity.Comment?.Examples)) { sections.Add(new KeyValuePair <string, string>("examples", "Examples")); } if ((state.Document.Entity.Comment?.Attributes?.SeeAlso?.Count ?? 0) > 0) { sections.Add(new KeyValuePair <string, string>("see-also", "See Also")); } if (sections.Count == 0) { return; } state.AppendString($"<section class=\"section-jump\">"); state.AppendString($"<ul class=\"nav nav-pills\">"); state.AppendString($"<li class=\"nav-item\"><a href=\"#\" class=\"nav-link disabled\">Jump to:</a></li>"); foreach (var section in sections) { state.AppendString($"<li class=\"nav-item\"><a href=\"#section-{EscapeHtml(section.Key)}\" class=\"nav-link\">{EscapeHtml(section.Value)}</a></li>"); } state.AppendString($"</ul>"); state.AppendString($"</section>"); }
private async Task WriteTocByDeclaredInAsync(List <TocDeclaredInGroup> groups, RenderingState state) { state.AppendString($"<h3 id=\"section-table-of-contents\">"); state.AppendString($"Interface of {EscapeHtml(state.Document.Entity.Titles.IndexTitle)}"); state.AppendString($"</h3>"); foreach (var group in groups) { var hasAdvanced = group.Items.Any(e => e.IsAdvanced(state.Document.Entity.Id)); var onlyAdvanced = group.Items.All(e => e.IsAdvanced(state.Document.Entity.Id)); var cssClasses = new List <string> { "table-of-contents-section" }; if (hasAdvanced) { cssClasses.Add("has-advanced-items"); } if (onlyAdvanced) { cssClasses.Add("only-advanced-items"); } if (group.DeclaredIn != null && group.DeclaredIn.Uri != null && group.DeclaredIn.Uri.IdUri != state.Document.Entity.Uri.IdUri) { cssClasses.Add("inherited"); } if (group.Attached) { cssClasses.Add("attached"); } state.AppendString($"<section class=\"{string.Join(" ", cssClasses)}\">"); if (group.DeclaredIn != null && group.DeclaredIn.Id != null && group.DeclaredIn.Id.Id != state.Document.Entity.Id.Id) { state.AppendString($"<h4 id=\"section-table-of-contents-inherited-from-{group.DeclaredIn.Uri.Href.Replace("/", "-")}\">"); state.AppendString($"Inherited from"); state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, group.DeclaredIn.Uri.Href, "html"))}\">"); state.AppendString(EscapeHtml(group.DeclaredIn.Titles.IndexTitle)); state.AppendString($"</a>"); state.AppendString($"</h4>"); } else if (group.Attached) { state.AppendString($"<h4 id=\"section-table-of-contents-attached-ux-attributes\">Attached UX Attributes</h4>"); } foreach (var item in group.Items) { var result = await _tocRenderer.RenderAsync(item, state.EntityCache, state.Document.Entity.Uri.Href, item.IsAdvanced(state.Document.Entity.Id)); state.MarkdownDeferredIds.AddRange(result.DeferredMarkdownIds); state.AppendString(result.Html); state.Quality.TableOfContentsCommentLines.Add(item.Uri.IdUri, result.NumberOfCommentLines); } state.AppendString($"</section>"); } }
private void WriteTitle(RenderingState state) { var title = GetPageTitle(state.Document, state.EntityCache); var subTitle = ""; var hasToc = (state.Document.TableOfContents?.Count ?? 0) > 0; var onlyAdvanced = state.Document.TableOfContents.HasOnlyAdvancedItems(state.Document.Entity.Id); // Allow topic comment to override the title, and use the existing title as a subtitle instead if (!string.IsNullOrWhiteSpace(state.Document.Entity.Comment?.Attributes?.Topic)) { subTitle = title; title = state.Document.Entity.Comment.Attributes.Topic; } state.AppendString($"<header class=\"page-header\">"); state.AppendString($"<h2 id=\"section-introduction\">"); state.AppendString(EscapeHtml(title)); if (!string.IsNullOrWhiteSpace(subTitle)) { state.AppendString($"<span class=\"sub-title\">{EscapeHtml(subTitle)}</span>"); } state.AppendString($"</h2>"); if (hasToc) { state.AppendString($"<form class=\"advanced-toggle\">"); state.AppendString($"<div class=\"form-check\">"); if (onlyAdvanced) { state.AppendString($"<input class=\"form-check-input\" type=\"checkbox\" id=\"showAdvancedCheckbox\" checked />"); } else { state.AppendString($"<input class=\"form-check-input\" type=\"checkbox\" id=\"showAdvancedCheckbox\" />"); } state.AppendString($"<label class=\"form-check-label\" for=\"showAdvancedCheckbox\">"); state.AppendString($"Show advanced things"); state.AppendString($"</label>"); state.AppendString($"</div>"); state.AppendString($"</form>"); } state.AppendString($"</header>"); if (hasToc && onlyAdvanced) { state.AppendString($"<div class=\"alert alert-info alert-api-advanced-only\">"); state.AppendString($"This page contains documentation for advanced Fuse features, so we have "); state.AppendString($"taken the liberty to tick the \"Show advanced things\" checkbox above for "); state.AppendString($"you in advance to be able to provide you with some additional information."); state.AppendString($"</div>"); } }
private void WriteLocation(RenderingState state) { if (string.IsNullOrWhiteSpace(state.Document.Entity.Location?.NamespaceUri) && string.IsNullOrWhiteSpace(state.Document.Entity.Location?.PackageName)) { return; } var cssClasses = new List <string> { "type-location" }; if (state.Document.TableOfContents.Count == 0) { cssClasses.Add("type-location-leaf"); } state.AppendString($"<section class=\"{string.Join(" ", cssClasses)}\">"); state.AppendString($"<h3 id=\"section-location\">Location</h3>"); state.AppendString($"<dl>"); if (!string.IsNullOrWhiteSpace(state.Document.Entity.Location.NamespaceUri)) { state.AppendString($"<dt>Namespace</dt>"); state.AppendString($"<dd>"); state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, state.Document.Entity.Location.NamespaceUri, "html"))}\">"); state.AppendString(EscapeHtml(state.Document.Entity.Location.NamespaceTitle)); state.AppendString($"</a>"); state.AppendString($"</dd>"); } if (!string.IsNullOrWhiteSpace(state.Document.Entity.Location.PackageName)) { state.AppendString($"<dt>Package</dt>"); state.AppendString($"<dd>{EscapeHtml(state.Document.Entity.Location.PackageName + " " + state.Document.Entity.Location.PackageVersion)}</dd>"); } state.AppendString($"</dl>"); state.AppendString($"</section>"); }
private async Task WriteValuesAsync(RenderingState state) { if ((state.Document.Entity.Values?.Count ?? 0) == 0) { return; } state.AppendString($"<section class=\"values\">"); state.AppendString($"<h3 id=\"section-values\">Possible Values</h3>"); state.AppendString($"<dl>"); foreach (var value in state.Document.Entity.Values) { state.AppendString($"<dt>"); state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, value.Uri, "html"))}\">"); state.AppendString(EscapeHtml(value.Title)); state.AppendString($"</a>"); state.AppendString($"</dt>"); state.AppendString($"<dd>"); if (!string.IsNullOrWhiteSpace(value.Comment?.Brief)) { var result = await RenderMarkdownAsync(value.Comment.Brief); state.MarkdownDeferredIds.Add(result.Id); state.AppendString(result.Html); } state.AppendString($"</dd>"); } state.AppendString($"</dl>"); state.AppendString($"</section>"); }
private async Task WriteParametersAsync(RenderingState state) { var parameters = BuildParameterCollection(state.Document.Entity); if (parameters.Count == 0) { return; } state.AppendString($"<section class=\"parameters\">"); state.AppendString($"<h3 id=\"section-parameters\">Parameters</h3>"); state.AppendString($"<dl>"); foreach (var param in parameters) { state.AppendString($"<dt>{EscapeHtml(param.Name)}</dt>"); state.AppendString($"<dd>"); if (!string.IsNullOrWhiteSpace(param.ReturnsTitle)) { state.AppendString($"<p>"); if (!string.IsNullOrWhiteSpace(param.ReturnsHref)) { state.AppendString($"<a href=\"{EscapeHtml(RelativePathHelper.GetRelativePath(state.Document.Entity.Uri.Href, param.ReturnsHref, "html"))}\">"); } state.AppendString(EscapeHtml(param.ReturnsTitle)); if (!string.IsNullOrWhiteSpace(param.ReturnsHref)) { state.AppendString($"</a>"); } state.AppendString("</p>"); } else if (!string.IsNullOrWhiteSpace(param.TypeHint)) { state.AppendString($"<p>{EscapeHtml(param.TypeHint)}</p>"); } if (!string.IsNullOrWhiteSpace(param.Comment)) { var result = await RenderMarkdownAsync(param.Comment); state.MarkdownDeferredIds.Add(result.Id); state.AppendString(result.Html); } state.AppendString($"</dd>"); } state.AppendString($"</dl>"); state.AppendString($"</section>"); }