/* Function: AppendToolTip * * Builds the HTML for the topic's tooltip and appends it to the passed StringBuilder. If the topic shoudn't have a tooltip it will * return false. * * Parameters: * * topic - The topic to build the tooltip for. * context - The context of the page the tooltip is being built for. The topic will automatically replace the context's topic * so you can just pass the context of the page, if any. * links - A list of <Links> that must contain any links found in the topic. * imageLinks - A list of <ImageLinks> that must contain any image links found in this topic. */ public bool AppendToolTip(Topics.Topic topic, Context context, IList <Link> links, IList <ImageLink> imageLinks, StringBuilder output) { if (topic.Prototype == null && topic.Summary == null) { return(false); } this.context = context; this.context.Topic = topic; this.links = links; this.imageLinks = imageLinks; string simpleCommentTypeName = EngineInstance.CommentTypes.FromID(topic.CommentTypeID).SimpleIdentifier; string simpleLanguageName = EngineInstance.Languages.FromID(topic.LanguageID).SimpleIdentifier; // No line breaks and indentation because this will be embedded in JavaScript strings. output.Append("<div class=\"NDToolTip T" + simpleCommentTypeName + " L" + simpleLanguageName + "\">"); if (topic.Prototype != null) { AppendPrototype(output); } if (topic.Summary != null) { AppendSummary(output); } output.Append("</div>"); return(true); }
/* Function: AppendOpeningLinkTag * * Constructs an <a> tag from the <Context's> <HTMLTopicPage> to the passed topic and appends it to the passed StringBuilder. * * Requirements: * * - The <Context>'s topic page must be set. */ public void AppendOpeningLinkTag(Topics.Topic targetTopic, StringBuilder output, string extraCSSClass = null) { #if DEBUG if (Context.TopicPage == null) { throw new Exception("Tried to call AppendOpeningLinkTag without setting the context's topic page."); } #endif // The URL can't be only the hash path because it would use the iframe's location. We need a relative path back to index.html // to append it to. Path currentOutputFolder = Context.TopicPage.OutputFile.ParentFolder; Path indexFile = Context.Builder.OutputFolder + "/index.html"; Path pathToIndex = indexFile.MakeRelativeTo(currentOutputFolder); Output.Components.HTMLTopicPage targetTopicPage = Context.TopicPage.GetLinkTarget(targetTopic); output.Append("<a "); if (extraCSSClass != null) { output.Append("class=\"" + extraCSSClass + "\" "); } string topicHashPath = Context.Builder.Source_TopicHashPath(targetTopic, targetTopicPage.IncludeClassInTopicHashPaths); output.Append("href=\"" + pathToIndex.ToURL() + '#' + targetTopicPage.OutputFileHashPath.EntityEncode() + (topicHashPath != null ? ':' + topicHashPath.EntityEncode() : "") + "\" " + "target=\"_top\" " + "onmouseover=\"NDContentPage.OnLinkMouseOver(event," + targetTopic.TopicID + ");\" " + "onmouseout=\"NDContentPage.OnLinkMouseOut(event);\" " + ">"); }
/* Function: BuildToolTip * * Builds the HTML for the topic's tooltip and returns it as a string. If the topic shoudn't have a tooltip it will return null. * * Parameters: * * topic - The topic to build the tooltip for. * context - The context of the page the tooltip is being built for. The topic will automatically replace the context's topic * so you can just pass the context of the page, if any. * links - A list of <Links> that must contain any links found in the topic. * imageLinks - A list of <ImageLinks> that must contain any image links found in this topic. */ public string BuildToolTip(Topics.Topic topic, Context context, IList <Link> links, IList <ImageLink> imageLinks) { if (topic.Prototype == null && topic.Summary == null) { return(null); } StringBuilder output = new StringBuilder(); AppendToolTip(topic, context, links, imageLinks, output); return(output.ToString()); }
/* Function: AppendChildClassPrototype */ protected void AppendChildClassPrototype(Topics.Topic childTopic, StringBuilder output) { CommentType childCommentType = EngineInstance.CommentTypes.FromID(childTopic.CommentTypeID); string memberOperator = language.MemberOperator; AppendOpeningLinkTag(childTopic, output, "CPEntry Child T" + childCommentType.SimpleIdentifier); output.Append("<div class=\"CPName\">"); AppendWrappedTitle(childTopic.Symbol.FormatWithSeparator(memberOperator), WrappedTitleMode.Code, output); output.Append("</div>"); output.Append("</a>"); }
/* Function: AppendOpeningLinkTag * * Constructs an <a> tag from the <Context's> <PageLocation> to the passed topic and appends it to the passed StringBuilder. * * Requirements: * * - The <Context>'s page must be set. */ public void AppendOpeningLinkTag(Topics.Topic targetTopic, StringBuilder output, string extraCSSClass = null) { #if DEBUG if (Context.Page.IsNull) { throw new Exception("Tried to call AppendOpeningLinkTag without setting the context's page."); } #endif // Build a context for the link target. If we're already in a hierarchy page, make the link target go to a hierarchy page as well. // However, it may have to fall back to a source file page if the target isn't part of a class. Context targetContext; if (context.Page.InHierarchy && targetTopic.ClassString != null) { targetContext = new Context(context.Target, targetTopic.ClassID, targetTopic.ClassString, targetTopic); } else { targetContext = new Context(context.Target, targetTopic.FileID, targetTopic); } // Find the path from the current output file back to index.html. The target URL needs to include this because relative paths are // based on the the iframe's location. Path currentOutputFolder = Context.OutputFile.ParentFolder; Path indexFile = Context.Target.OutputFolder + "/index.html"; Path pathToIndex = indexFile.MakeRelativeTo(currentOutputFolder); // Build the link output.Append("<a "); if (extraCSSClass != null) { output.Append("class=\"" + extraCSSClass + "\" "); } output.Append("href=\"" + pathToIndex.ToURL() + '#' + targetContext.HashPath.EntityEncode() + "\" " + "target=\"_top\" " + "onmouseover=\"NDContentPage.OnLinkMouseOver(event," + targetTopic.TopicID + ");\" " + "onmouseout=\"NDContentPage.OnLinkMouseOut(event);\" " + ">"); }
/* Function: AppendSyntaxHighlightedTextWithTypeLinks * * Formats the text between the iterators with syntax highlighting and links for any tokens marked with * <PrototypeParsingType.Type> and <PrototypeParsingType.TypeQualifier>. Appends the result to the passed StringBuilder. * * Parameters: * * start - The first token of the text to convert. * end - The end of the text to convert, which is one token past the last one included. * output - The StringBuilder to append the output to. * * links - A list of <Links> that should contain any appearing in the code. * linkTargets - A list of topics that should contain any used as targets in the list of links. * * extendTypeSearch - If true, it will search beyond the bounds of the iterators to get the complete type. This allows you to * format only a portion of the link with this function yet still have the link go to the complete destination. * * Requirements: * * - The <Context>'s topic and page must be set. */ public void AppendSyntaxHighlightedTextWithTypeLinks(TokenIterator start, TokenIterator end, StringBuilder output, IList <Link> links, IList <Topics.Topic> linkTargets, bool extendTypeSearch = false) { #if DEBUG if (Context.Topic == null) { throw new Exception("Tried to call AppendSyntaxtHighlightedTextWithTypeLinks without setting the context's topic."); } if (Context.Page.IsNull) { throw new Exception("Tried to call AppendSyntaxtHighlightedTextWithTypeLinks without setting the context's page."); } if (links == null) { throw new Exception("Tried to call AppendSyntaxtHighlightedTextWithTypeLinks without setting the links variable."); } if (linkTargets == null) { throw new Exception("Tried to call AppendSyntaxtHighlightedTextWithTypeLinks without setting the linkTargets variable."); } #endif Language language = EngineInstance.Languages.FromID(Context.Topic.LanguageID); // Find each Type/TypeQualifier stretch in the text TokenIterator iterator = start; while (iterator < end) { if (iterator.PrototypeParsingType == PrototypeParsingType.Type || iterator.PrototypeParsingType == PrototypeParsingType.TypeQualifier) { TokenIterator textStart = iterator; TokenIterator textEnd = iterator; do { textEnd.Next(); }while (textEnd < end && (textEnd.PrototypeParsingType == PrototypeParsingType.Type || textEnd.PrototypeParsingType == PrototypeParsingType.TypeQualifier)); TokenIterator symbolStart = textStart; TokenIterator symbolEnd = textEnd; // Extend past start and end if the flag is set if (extendTypeSearch && symbolStart == start) { TokenIterator temp = symbolStart; temp.Previous(); while (temp.IsInBounds && (temp.PrototypeParsingType == PrototypeParsingType.Type || temp.PrototypeParsingType == PrototypeParsingType.TypeQualifier)) { symbolStart = temp; temp.Previous(); } } if (extendTypeSearch && symbolEnd == end) { while (symbolEnd.IsInBounds && (symbolEnd.PrototypeParsingType == PrototypeParsingType.Type || symbolEnd.PrototypeParsingType == PrototypeParsingType.TypeQualifier)) { symbolEnd.Next(); } } // Built in types don't get links if (language.IsBuiltInType(symbolStart, symbolEnd)) { AppendSyntaxHighlightedText(textStart, textEnd, output); } else { // Create a link object with the identifying properties needed to look it up in the list of links. Link linkStub = new Link(); linkStub.Type = LinkType.Type; linkStub.Symbol = SymbolString.FromPlainText_NoParameters(symbolStart.TextBetween(symbolEnd)); linkStub.Context = Context.Topic.PrototypeContext; linkStub.ContextID = Context.Topic.PrototypeContextID; linkStub.FileID = Context.Topic.FileID; linkStub.ClassString = Context.Topic.ClassString; linkStub.ClassID = Context.Topic.ClassID; linkStub.LanguageID = Context.Topic.LanguageID; // Find the actual link so we know if it resolved to anything. Link fullLink = null; foreach (Link link in links) { if (link.SameIdentifyingPropertiesAs(linkStub)) { fullLink = link; break; } } #if DEBUG if (fullLink == null) { throw new Exception("All links in a topic must be in the list passed to AppendSyntaxtHighlightedTextWithTypeLinks."); } #endif // If it didn't resolve, we just output the original text. if (!fullLink.IsResolved) { AppendSyntaxHighlightedText(textStart, textEnd, output); } else { // If it did resolve, find Topic it resolved to. Topics.Topic targetTopic = null; foreach (var linkTarget in linkTargets) { if (linkTarget.TopicID == fullLink.TargetTopicID) { targetTopic = linkTarget; break; } } #if DEBUG if (targetTopic == null) { throw new Exception("All links targets for a topic must be in the list passed to AppendSyntaxtHighlightedTextWithTypeLinks."); } #endif AppendOpeningLinkTag(targetTopic, output); AppendSyntaxHighlightedText(textStart, textEnd, output); output.Append("</a>"); } } iterator = textEnd; } else // not on a type { TokenIterator startText = iterator; do { iterator.Next(); }while (iterator < end && iterator.PrototypeParsingType != PrototypeParsingType.Type && iterator.PrototypeParsingType != PrototypeParsingType.TypeQualifier); AppendSyntaxHighlightedText(startText, iterator, output); } } }
/* Function: AppendNaturalDocsLink */ protected void AppendNaturalDocsLink(NDMarkup.Iterator iterator, StringBuilder output) { // Create a link object with the identifying properties needed to look it up in the list of links. Link linkStub = new Link(); linkStub.Type = LinkType.NaturalDocs; linkStub.Text = iterator.Property("originaltext"); linkStub.Context = context.Topic.BodyContext; linkStub.ContextID = context.Topic.BodyContextID; linkStub.FileID = context.Topic.FileID; linkStub.ClassString = context.Topic.ClassString; linkStub.ClassID = context.Topic.ClassID; linkStub.LanguageID = context.Topic.LanguageID; // Find the actual link so we know if it resolved to anything. Link fullLink = null; foreach (Link link in links) { if (link.SameIdentifyingPropertiesAs(linkStub)) { fullLink = link; break; } } #if DEBUG if (fullLink == null) { throw new Exception("All links in a topic must be in the list passed to HTMLTopic."); } #endif // If it didn't resolve, we just output the original text and we're done. if (!fullLink.IsResolved) { output.EntityEncodeAndAppend(iterator.Property("originaltext")); return; } // If it did resolve, find the interpretation that was used. If it was a named link it would affect the link text. LinkInterpretation linkInterpretation = null; string ignore; List <LinkInterpretation> linkInterpretations = EngineInstance.Comments.NaturalDocsParser.LinkInterpretations(fullLink.Text, Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowNamedLinks | Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowPluralsAndPossessives | Comments.Parsers.NaturalDocs.LinkInterpretationFlags.FromOriginalText, out ignore); linkInterpretation = linkInterpretations[fullLink.TargetInterpretationIndex]; // Find the Topic it resolved to. Topics.Topic targetTopic = null; foreach (var linkTarget in linkTargets) { if (linkTarget.TopicID == fullLink.TargetTopicID) { targetTopic = linkTarget; break; } } #if DEBUG if (targetTopic == null) { throw new Exception("All links targets for a topic must be in the list passed to HTMLTopic."); } #endif AppendOpeningLinkTag(targetTopic, output); output.EntityEncodeAndAppend(linkInterpretation.Text); output.Append("</a>"); }
/* Function: AppendTopic * * Builds the HTML for the topic and appends it to the passed StringBuilder. * * Parameters: * * topic - The topic to build. * context - The <Context> the topic appears in. The topic will automatically replace the context's topic, so you can just * pass the context of the page. * links - A list of <Links> that must contain any links found in the topic. * linkTargets - A list of topics that must contain any topics used as targets in the links. * imageLinks - A list of <ImageLinks> that must contain any image links found in this topic. * output - The StringBuilder that the output will be appended to. * embeddedTopics - A list of topics that contains any embedded topics contained in this one. * embeddedTopicIndex - The index into embeddedTopics to start at. * extraClass - If specified, this string will be added to the CTopic div as an extra CSS class. */ public void AppendTopic(Topics.Topic topic, Context context, IList <Link> links, IList <Topics.Topic> linkTargets, IList <ImageLink> imageLinks, StringBuilder output, IList <Topics.Topic> embeddedTopics = null, int embeddedTopicIndex = 0, string extraClass = null) { try { // Setup context.Topic = topic; this.context = context; this.links = links; this.linkTargets = linkTargets; this.imageLinks = imageLinks; this.embeddedTopics = embeddedTopics; this.embeddedTopicIndex = embeddedTopicIndex; // Core string simpleCommentTypeName = EngineInstance.CommentTypes.FromID(topic.CommentTypeID).SimpleIdentifier; string simpleLanguageName = EngineInstance.Languages.FromID(topic.LanguageID).SimpleIdentifier; string topicHashPath = context.TopicOnlyHashPath; if (topicHashPath != null) { output.Append("<a name=\"" + topicHashPath.EntityEncode() + "\"></a>"); } output.Append( "<a name=\"Topic" + topic.TopicID + "\"></a>" + "<div class=\"CTopic T" + simpleCommentTypeName + " L" + simpleLanguageName + (extraClass == null ? "" : ' ' + extraClass) + "\">" + "\r\n "); AppendTitle(output); #if SHOW_NDMARKUP if (topic.Body != null) { htmlOutput.Append( "\r\n " + "<div class=\"CBodyNDMarkup\">" + topic.Body.ToHTML() + "</div>"); } #endif if (topic.Prototype != null) { output.Append("\r\n "); AppendPrototype(output); } if (topic.Body != null) { output.Append("\r\n "); AppendBody(output); } output.Append( "\r\n" + "</div>" ); } catch (Exception e) { // Build a message to show the topic we crashed on if (topic != null) { StringBuilder task = new StringBuilder("Building HTML for topic"); // Topic name if (topic.Title == null) { task.Append(" ID " + topic.TopicID + " (null title)"); } else if (topic.Title == "") { task.Append(" ID " + topic.TopicID + " (empty string title)"); } else { task.Append(" \"" + topic.Title + "\""); } // File name var file = (topic.FileID > 0 ? EngineInstance.Files.FromID(topic.FileID) : null); if (file != null) { task.Append(" from file " + file.FileName); } else { task.Append(" from file ID " + topic.FileID); } // Line number if (topic.CommentLineNumber > 0) { if (topic.CodeLineNumber > 0 && topic.CodeLineNumber != topic.CommentLineNumber) { task.Append(" lines " + topic.CommentLineNumber + " and " + topic.CodeLineNumber); } else { task.Append(" line " + topic.CommentLineNumber); } } else { task.Append(" line " + topic.CodeLineNumber); } e.AddNaturalDocsTask(task.ToString()); } throw; } }