/* Function: HashPath * * Returns the hash path for the topic. When appending this to the hash path of a file or class use a colon to separate * them. * * Examples: * * topic - Member * topic + includeClass - Module.Module.Member */ static public string HashPath(Engine.Topics.Topic topic, bool includeClass = true) { // We want to work from Topic.Title instead of Topic.Symbol so that we can use the separator characters as originally // written, as opposed to having them normalized and condensed in the anchor. int titleParametersIndex = ParameterString.GetParametersIndex(topic.Title); StringBuilder hashPath; if (titleParametersIndex == -1) { hashPath = new StringBuilder(topic.Title); } else { hashPath = new StringBuilder(titleParametersIndex); hashPath.Append(topic.Title, 0, titleParametersIndex); } hashPath.Replace('\t', ' '); // Remove all whitespace unless it separates two text characters. int i = 0; while (i < hashPath.Length) { if (hashPath[i] == ' ') { if (i == 0 || i == hashPath.Length - 1) { hashPath.Remove(i, 1); } else if (Tokenizer.FundamentalTypeOf(hashPath[i - 1]) == FundamentalType.Text && Tokenizer.FundamentalTypeOf(hashPath[i + 1]) == FundamentalType.Text) { i++; } else { hashPath.Remove(i, 1); } } else { i++; } } // Add parentheses to distinguish between multiple symbols in the same file. // xxx this will be a problem when doing class hash paths as symboldefnumber is only unique to a file if (topic.SymbolDefinitionNumber != 1) { hashPath.Append('('); hashPath.Append(topic.SymbolDefinitionNumber); hashPath.Append(')'); } // Add class if present and desired. // xxx when class id is included in topic test for that here, maybe instead of having a flag if (includeClass) { // Find the part of the symbol that isn't generated by the title, if any. string ignore; string titleSymbol = SymbolString.FromPlainText(topic.Title, out ignore).ToString(); string fullSymbol = topic.Symbol.ToString(); if (titleSymbol.Length < fullSymbol.Length && fullSymbol.Substring(fullSymbol.Length - titleSymbol.Length) == titleSymbol) { string classSymbol = fullSymbol.Substring(0, fullSymbol.Length - titleSymbol.Length); classSymbol = classSymbol.Replace(SymbolString.SeparatorChar, '.'); // The class symbol should already have a trailing member operator. hashPath.Insert(0, classSymbol); } } return(Utilities.Sanitize(hashPath.ToString())); }
public override string OutputOf(IList <string> commands) { StringBuilder output = new StringBuilder(); bool lastWasLineBreak = true; Topic topic = new Topic(EngineInstance.CommentTypes); topic.LanguageID = EngineInstance.Languages.FromName("C#").ID; topic.CommentTypeID = EngineInstance.CommentTypes.FromKeyword("Function").ID; Link link = new Engine.Links.Link(); link.LanguageID = EngineInstance.Languages.FromName("C#").ID; link.Type = Engine.Links.LinkType.NaturalDocs; bool linkIsPlainText = true; string show = "all"; for (int i = 0; i < commands.Count; i++) { string command = commands[i]; if (command.EndsWith(";")) { command = command.Substring(0, command.Length - 1).TrimEnd(); } Match match = GetPropertyRegex.Match(command); string target, property, value, valueString; Match addPrefixMatch = null; Match replacePrefixMatch = null; if (match.Success) { target = match.Groups[1].ToString().ToLower(); property = match.Groups[2].ToString().ToLower(); value = match.Groups[3].ToString(); if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"') { valueString = value.Substring(1, value.Length - 2).Trim(); } else { value = value.ToLower(); valueString = null; } } else { addPrefixMatch = AddPrefixRegex.Match(command); replacePrefixMatch = ReplacePrefixRegex.Match(command); if (addPrefixMatch.Success || replacePrefixMatch.Success) { target = "link"; property = "using"; value = null; valueString = null; } else { target = null; property = null; value = null; valueString = null; } } var lcCommand = command.ToLower(); try { if (command == "") { if (!lastWasLineBreak) { output.AppendLine(); lastWasLineBreak = true; } } else if (command.StartsWith("//")) { output.AppendLine(command); lastWasLineBreak = false; } // Topic properties else if (target == "topic") { if (property == "languagename") { if (valueString == null) { throw new Exception("Topic.LanguageName must be set to a string value."); } var language = EngineInstance.Languages.FromName(valueString); if (language == null) { throw new Exception("\"" + valueString + "\" is not recognized as a language."); } topic.LanguageID = language.ID; } else if (property == "keyword") { if (valueString == null) { throw new Exception("Topic.Keyword must be set to a string value."); } var commentType = EngineInstance.CommentTypes.FromKeyword(valueString); if (commentType == null) { throw new Exception("\"" + valueString + "\" is not recognized as a keyword."); } topic.CommentTypeID = commentType.ID; } else if (property == "title") { if (valueString == null) { throw new Exception("Topic.Title must be set to a string value."); } if (valueString == "") { throw new Exception("Topic.Title cannot be set to an empty string."); } topic.Title = valueString; } else if (property == "body") { if (value == "null") { topic.Body = null; } else if (valueString == null) { throw new Exception("Topic.Body must be set to null or a string value."); } else { topic.Body = valueString; } } else if (property == "prototype") { if (value == "null") { topic.Prototype = null; } else if (valueString == null) { throw new Exception("Topic.Prototype must be set to null or a string value."); } else { topic.Prototype = valueString; } } else if (property == "scope") { if (value == "null") { ContextString temp = topic.PrototypeContext; temp.Scope = new SymbolString(); topic.PrototypeContext = temp; } else if (valueString == null) { throw new Exception("Topic.Scope must be set to null or a string value."); } else { ContextString temp = topic.PrototypeContext; temp.Scope = SymbolString.FromPlainText_NoParameters(valueString); topic.PrototypeContext = temp; } } else { throw new Exception("\"" + property + "\" is not a recognized Topic property."); } // Leave lastWasLineBreak alone since we're not generating output. } // Link properties else if (target == "link") { if (property == "languagename") { if (valueString == null) { throw new Exception("Link.LanguageName must be set to a string value."); } var language = EngineInstance.Languages.FromName(valueString); if (language == null) { throw new Exception("\"" + valueString + "\" is not recognized as a language."); } link.LanguageID = language.ID; } else if (property == "type") { if (valueString == null) { throw new Exception("Link.Type must be set to a string value."); } string lcValueString = valueString.ToLower(); if (lcValueString == "naturaldocs" || lcValueString == "natural docs") { link.Type = LinkType.NaturalDocs; } else if (lcValueString == "type") { link.Type = LinkType.Type; } else if (lcValueString == "classparent" || lcValueString == "class parent") { link.Type = LinkType.ClassParent; } else { throw new Exception("\"" + valueString + "\" is not recognized as a link type."); } } else if (property == "text") { if (valueString == null) { throw new Exception("Link.Text must be set to a string value"); } if (valueString == "") { throw new Exception("Link.Text cannot be set to an empty string."); } link.TextOrSymbol = valueString; linkIsPlainText = true; } else if (property == "scope") { if (value == "null") { ContextString temp = link.Context; temp.Scope = new SymbolString(); link.Context = temp; } else if (valueString == null) { throw new Exception("Link.Scope must be set to null or a string value."); } else if (valueString == "") { throw new Exception("Link.Scope cannot be set to an empty string."); } else { ContextString temp = link.Context; temp.Scope = SymbolString.FromPlainText_NoParameters(valueString); link.Context = temp; } } else if (property == "using") { if (value == "null") { ContextString temp = link.Context; temp.ClearUsingStatements(); link.Context = temp; } else if (addPrefixMatch != null && addPrefixMatch.Success) { string op = addPrefixMatch.Groups[1].ToString(); string add = addPrefixMatch.Groups[2].ToString(); ContextString temp = link.Context; if (op == "=") { temp.ClearUsingStatements(); } temp.AddUsingStatement( UsingString.FromParameters( UsingString.UsingType.AddPrefix, SymbolString.FromPlainText_NoParameters(add) ) ); link.Context = temp; } else if (replacePrefixMatch != null && replacePrefixMatch.Success) { string op = replacePrefixMatch.Groups[1].ToString(); string remove = replacePrefixMatch.Groups[2].ToString(); string add = replacePrefixMatch.Groups[3].ToString(); ContextString temp = link.Context; if (op == "=") { temp.ClearUsingStatements(); } temp.AddUsingStatement( UsingString.FromParameters( UsingString.UsingType.ReplacePrefix, SymbolString.FromPlainText_NoParameters(add), SymbolString.FromPlainText_NoParameters(remove) ) ); link.Context = temp; } else { throw new Exception("\"" + command + "\" is not a recognized Link.Using statement."); } } else { throw new Exception("\"" + property + "\" is not recognized as a link property."); } // Leave lastWasLineBreak alone since we're not generating output. } // Show else if (lcCommand.StartsWith("show")) { show = lcCommand.Substring(4); } // Score else if (lcCommand == "score") { // Validate fields if (topic.Title == null) { throw new Exception("You didn't set Topic.Title."); } if (link.TextOrSymbol == null) { throw new Exception("You didn't set Link.Text."); } // Calculate fields string parametersString; SymbolString topicSymbol = SymbolString.FromPlainText(topic.Title, out parametersString); var commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); if (commentType.Scope == Engine.CommentTypes.CommentType.ScopeValue.Normal && topic.PrototypeContext.ScopeIsGlobal == false) { topicSymbol = topic.PrototypeContext.Scope + topicSymbol; } topic.Symbol = topicSymbol; if (link.Type == LinkType.Type || link.Type == LinkType.ClassParent) { if (linkIsPlainText) { SymbolString linkSymbol = SymbolString.FromPlainText_NoParameters(link.TextOrSymbol); link.TextOrSymbol = linkSymbol.ToString(); link.EndingSymbol = linkSymbol.EndingSymbol; linkIsPlainText = false; } } else { string ignore; SymbolString linkSymbol = SymbolString.FromPlainText(link.TextOrSymbol, out ignore); link.EndingSymbol = linkSymbol.EndingSymbol; } // Show topic if (!lastWasLineBreak) { output.AppendLine(); } var topicLanguage = EngineInstance.Languages.FromID(topic.LanguageID); commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); output.AppendLine(topicLanguage.Name + " " + commentType.Name + " Topic: " + topic.Title); output.AppendLine(" Symbol: " + topic.Symbol.FormatWithSeparator('.')); if (topic.TitleParameters != null) { output.AppendLine(" Title Parameters: " + topic.TitleParameters.ToString().Replace(Engine.Symbols.ParameterString.SeparatorChar, ',')); } if (topic.PrototypeParameters != null) { output.AppendLine(" Prototype Parameters: " + topic.PrototypeParameters.ToString().Replace(Engine.Symbols.ParameterString.SeparatorChar, ',')); } if (topic.Prototype != null) { output.AppendLine(" Prototype: " + topic.Prototype); } if (topic.Body != null) { output.AppendLine(" Body: " + topic.Body); } output.AppendLine(); // Show link var linkLanguage = EngineInstance.Languages.FromID(link.LanguageID); output.AppendLine(linkLanguage.Name + " " + link.Type + " Link: " + link.TextOrSymbol.Replace(Engine.Symbols.SymbolString.SeparatorChar, '*')); if (link.Context.ScopeIsGlobal) { output.AppendLine(" Scope: Global"); } else { output.AppendLine(" Scope: " + link.Context.Scope.FormatWithSeparator('.')); } var usingStatements = link.Context.GetUsingStatements(); if (usingStatements != null) { foreach (var usingStatement in usingStatements) { if (usingStatement.Type == UsingString.UsingType.AddPrefix) { output.AppendLine(" Using: Add Prefix " + usingStatement.PrefixToAdd.FormatWithSeparator('.')); } else if (usingStatement.Type == UsingString.UsingType.ReplacePrefix) { output.AppendLine(" Using: Replace Prefix " + usingStatement.PrefixToRemove.FormatWithSeparator('.') + " with " + usingStatement.PrefixToAdd.FormatWithSeparator('.')); } else { throw new NotImplementedException("Unexpected using type " + usingStatement.Type); } } } output.AppendLine(); // Show score List <Engine.Links.LinkInterpretation> interpretations = null; if (link.Type == LinkType.NaturalDocs) { string ignore; interpretations = EngineInstance.Comments.NaturalDocsParser.LinkInterpretations(link.TextOrSymbol, Engine.Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowNamedLinks | Engine.Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowPluralsAndPossessives, out ignore); } long score = EngineInstance.CodeDB.ScoreLink(link, topic, 0, interpretations); if (score <= 0) { output.AppendLine("☓☓☓ No Match ☓☓☓"); } else { output.Append("Match score:"); if (show.Contains("all") || show.Contains("rawscore")) { string scoreString = score.ToString("X16"); output.Append(" " + scoreString.Substring(0, 4) + " " + scoreString.Substring(4, 4) + " " + scoreString.Substring(8, 4) + " " + scoreString.Substring(12, 4)); } output.AppendLine(); // DEPENDENCY: This code depends on the format generated by Engine.CodeDB.ScoreLink(). // Format: // 0LCETPPP PPPPPPPP PPPPPPPP PSSSSSSS SSSIIIII IBFFFFFF Rbbbbbbb brrrrrr1 // L - Whether the topic matches the link's language. // C - Whether the topic and link's capitalization match if it matters to the language. // E - Whether the text is an exact match with no plural or possessive conversions applied. // T - Whether the link parameters exactly match the topic title parameters. // P - How well the parameters match. // S - How high on the scope list the symbol match is. // I - How high on the interpretation list (named/plural/possessive) the match is. // B - Whether the topic has a body // F - How high on the list of topics that define the same symbol in the same file this is. // R - Whether the topic has a prototype. // b - The length of the body divided by 16. // r - The length of the prototype divided by 16. long LValue = (score & 0x4000000000000000) >> 62; long CValue = (score & 0x2000000000000000) >> 61; long EValue = (score & 0x1000000000000000) >> 60; long TValue = (score & 0x0800000000000000) >> 59; long PValue = (score & 0x07FFFF8000000000) >> 39; long SValue = (score & 0x0000007FE0000000) >> 29; long IValue = (score & 0x000000001F800000) >> 23; long BValue = (score & 0x0000000000400000) >> 22; long FValue = (score & 0x00000000003F0000) >> 16; long RValue = (score & 0x0000000000008000) >> 15; long bValue = (score & 0x0000000000007F80) >> 7; long rValue = (score & 0x000000000000007E) >> 1; if (show.Contains("all") || show.Contains("language")) { output.AppendLine(" " + (LValue == 1 ? "☒" : "☐") + " - Language"); } if (show.Contains("all") || show.Contains("capitalization")) { output.AppendLine(" " + (CValue == 1 ? "☒" : "☐") + " - Capitalization"); } if (show.Contains("all") || show.Contains("interpretation")) { output.AppendLine(" " + (EValue == 1 ? "☒" : "☐") + " - Exact text"); } if (show.Contains("all") || show.Contains("parameters")) { output.AppendLine(" " + (TValue == 1 ? "☒" : "☐") + " - Topic title parameters"); output.Append(" "); for (int shift = 18; shift >= 0; shift -= 2) { long individualPValue = PValue >> shift; individualPValue &= 0x0000000000000003; switch (individualPValue) { case 3: output.Append("☒"); break; case 2: output.Append("↑"); break; case 1: output.Append("↓"); break; case 0: output.Append("☐"); break; } } output.AppendLine(" - Parameters"); } if (show.Contains("all") || show.Contains("scope")) { output.AppendLine(" " + (1023 - SValue) + " - Scope index"); output.AppendLine(" (" + SValue + " score)"); } if (show.Contains("all") || show.Contains("interpretation")) { output.AppendLine(" " + (63 - IValue) + " - Interpretation index"); output.AppendLine(" (" + IValue + " score)"); } if (show.Contains("all") || show.Contains("body")) { output.AppendLine(" " + (BValue == 1 ? "☒" : "☐") + " - Body"); if (BValue == 1) { output.Append(" (" + bValue + " score, " + (bValue * 16)); if (bValue == 0xFF) { output.Append('+'); } else { output.Append("-" + ((bValue * 16) + 15)); } output.AppendLine(" characters)"); } } if (show.Contains("all") || show.Contains("prototype")) { output.AppendLine(" " + (RValue == 1 ? "☒" : "☐") + " - Prototype"); if (RValue == 1) { output.Append(" (" + rValue + " score, " + (rValue * 16)); if (rValue == 0x3F) { output.Append('+'); } else { output.Append("-" + ((rValue * 16) + 15)); } output.AppendLine(" characters)"); } } if (show.Contains("all") || show.Contains("samesymbol")) { output.AppendLine(" " + (63 - FValue) + " - Same symbol in same file index"); output.AppendLine(" (" + FValue + " score)"); } } output.AppendLine(); lastWasLineBreak = true; } else { throw new Exception("Unknown command " + command); } } catch (Exception e) { output.AppendLine("Command: " + command); output.AppendLine("Exception: " + e.Message); output.AppendLine("(" + e.GetType().ToString() + ")"); lastWasLineBreak = false; } } return(output.ToString()); }