// Group: Functions // __________________________________________________________________________ public TopicEntry(Topic topic, SearchIndex.Manager manager) : base() { this.topic = topic; var commentType = manager.EngineInstance.CommentTypes.FromID(topic.CommentTypeID); var language = manager.EngineInstance.Languages.FromID(topic.LanguageID); // Get the title without any parameters. We don't want to include parameters in the index. Multiple functions that // differ only by parameter will be treated as one entry. string title, ignore; ParameterString.SplitFromParameters(topic.Title, out title, out ignore); title = title.TrimEnd(); // Figure out the extra scope text that should be added to the title to make it a fully resolved symbol. We do this by // comparing the symbol from the topic to one generated from the title. We don't just use the symbol to begin with // because we want to show the title as written; there's some normalization that occurs when generating symbols // that we want to bypass. string extraScope = null; SymbolString titleSymbol = SymbolString.FromPlainText_NoParameters(title); string titleSymbolString = titleSymbol.FormatWithSeparator(language.MemberOperator); string symbolString = topic.Symbol.FormatWithSeparator(language.MemberOperator); if (symbolString.Length > titleSymbolString.Length) { // We have to go by LastIndexOf rather than EndsWith because operator<string> will have <string> cut off as a parameter. // We have to go by LastIndexOf instead of IndexOf so constructors don't get cut off (Package.Class.Class). int titleIndex = symbolString.LastIndexOf(titleSymbolString); #if DEBUG if (titleIndex == -1) { throw new Exception("Title symbol string \"" + titleSymbolString + "\" isn't part of symbol string \"" + symbolString + "\" which " + "was assumed when creating a search index entry."); } #endif extraScope = symbolString.Substring(0, titleIndex); } // Remove the space in "operator <". This prevents them from appearing as two keywords, and also makes sure "operator <" and // "operator<" are always displayed consistently, which will be important for sorting. title = SpaceAfterOperatorKeywordRegex.Replace(title, ""); displayName = (extraScope == null ? title : extraScope + title); searchText = Normalize(displayName); if (commentType.Flags.File) { endOfDisplayNameQualifiers = EndOfQualifiers(displayName, FileSplitSymbolsRegex.Matches(displayName)); endOfSearchTextQualifiers = EndOfQualifiers(searchText, FileSplitSymbolsRegex.Matches(searchText)); } else if (commentType.Flags.Code) { endOfDisplayNameQualifiers = EndOfQualifiers(displayName, CodeSplitSymbolsRegex.Matches(displayName)); endOfSearchTextQualifiers = EndOfQualifiers(searchText, CodeSplitSymbolsRegex.Matches(searchText)); } else // documentation topic { if (extraScope == null) { endOfDisplayNameQualifiers = 0; endOfSearchTextQualifiers = 0; } else { endOfDisplayNameQualifiers = extraScope.Length; // Don't need +1 because only leading separators are removed. The trailing separator will still be there. endOfSearchTextQualifiers = Normalize(extraScope).Length; } } keywords = new List <string>(); if (endOfDisplayNameQualifiers == 0) { AddKeywords(displayName, commentType.Flags.Documentation); } else { AddKeywords(displayName.Substring(endOfDisplayNameQualifiers), commentType.Flags.Documentation); } }
/* Function: AppendWrappedTitle * Appends the title to the passed StringBuilder as HTML with zero-width spaces added so that long identifiers wrap. It will * also add a span surrounding the qualifiers with a "Qualifier" CSS class. */ public void AppendWrappedTitle(string title, WrappedTitleMode mode, StringBuilder output) { MatchCollection splitSymbols = null; int splitCount = 0; if (mode == WrappedTitleMode.Code) { splitSymbols = CodeSplitSymbolsRegex.Matches(title); splitCount = splitSymbols.Count; } else if (mode == WrappedTitleMode.File) { splitSymbols = FileSplitSymbolsRegex.Matches(title); splitCount = splitSymbols.Count; } // Don't count separators on the end of the string. if (splitCount > 0) { int endOfString = title.Length; for (int i = splitCount - 1; i >= 0; i--) { if (splitSymbols[i].Index + splitSymbols[i].Length == endOfString) { splitCount--; endOfString = splitSymbols[i].Index; } else { break; } } } // Build the HTML. if (splitCount == 0) { output.Append(title.ToHTML()); } else { int appendedSoFar = 0; output.Append("<span class=\"Qualifier\">"); for (int i = 0; i < splitCount; i++) { int endOfSection = splitSymbols[i].Index + splitSymbols[i].Length; string titleSection = title.Substring(appendedSoFar, endOfSection - appendedSoFar); output.Append(titleSection.ToHTML()); if (i < splitCount - 1) { // Insert a zero-width space for wrapping. We have to put the final one outside the closing </span> or // Webkit browsers won't wrap on it. output.Append("​"); } appendedSoFar = endOfSection; } output.Append("</span>​"); // zero-width space for wrapping output.Append(title.Substring(appendedSoFar).ToHTML()); } }