/// <summary> /// Generate an element tree from code. /// </summary> /// <param name="code">Code</param> /// <param name="node">Root node</param> /// <returns>Lines of code parsed.</returns> int ParseBlock(string code, MemberNode node) { int codeLen = code.Length, lastEnding = 0, lastSlash = -2, lastEquals = -2, parenthesis = 0, depth = 0, lastRemovableDepth = 0, loc = 0; bool commentLine = false, inRemovableBlock = false, inString = false, preprocessorLine = false, preprocessorSkip = false, summaryLine = false, inTemplate = false, propertyArray = false; string summary = string.Empty; // Parse defines string[] defineConstants = defines.Split(';'); int defineCount = defineConstants.Length; for (int i = 0; i < defineCount; ++i) { defineConstants[i] = defineConstants[i].Trim(); } for (int i = 0; i < codeLen; ++i) { if (i != 0 && code[i - 1] != '\\') { // Skip strings if (!inString && code[i] == '"') { inString = true; } else if (inString) { if (code[i] == '"') { inString = false; } else { continue; } // Skip characters } else if (code[i] == '\'') { if (code[i + 3] == '\'') { i += 4; } if (code[i + 2] == '\'') { i += 3; } } } // Actual parser switch (code[i]) { case ',': case ';': case '{': case '}': if (!commentLine && !preprocessorSkip && parenthesis == 0) { if (code[i] == ',' && inTemplate) { continue; } inTemplate = false; bool instruction = code[i] == ';', block = code[i] == '{', closing = code[i] == '}'; if (block) { ++depth; } if (closing) { --depth; if (inRemovableBlock && depth < lastRemovableDepth) { inRemovableBlock = false; lastEnding = i; if (propertyArray) { ++lastEnding; propertyArray = false; break; } } } string cutout = code.Substring(lastEnding, i - lastEnding).Trim(); lastEnding = i + 1; if (inRemovableBlock) { bool getter = cutout.EndsWith(_getter), setter = cutout.EndsWith(_setter); if (getter || setter) { Visibility visHere = Visibility.Public; if (cutout.StartsWith(_protected)) { visHere = Visibility.Protected; } else if (cutout.StartsWith(_internal)) { visHere = Visibility.Internal; } else if (cutout.StartsWith(_private)) { visHere = Visibility.Private; } if (getter) { node.Getter = visHere; } else { node.Setter = visHere; } } continue; } // Remove multiline comments int commentBlockStart; while ((commentBlockStart = cutout.IndexOf(_commentBlockStart)) != -1) { int commentEnd = cutout.IndexOf(_commentBlockEnd, commentBlockStart + 2) + 2; if (commentEnd == 1) { break; } cutout = cutout.Substring(0, commentBlockStart) + cutout.Substring(commentEnd); } if (cutout.StartsWith(_using)) { break; } // Property array initialization int nodes = node.Nodes.Count; if (block && cutout[0] == '=' && nodes != 0 && ((MemberNode)node.Nodes[nodes - 1]).Kind == Element.Properties) { lastRemovableDepth = depth; inRemovableBlock = true; propertyArray = true; break; } if (cutout.Length > 0 && cutout[cutout.Length - 1] == '=') { cutout = cutout.Remove(cutout.Length - 2, 1).TrimEnd(); } int lambda = cutout.IndexOf(_lambda); if (lambda >= 0) { cutout = cutout.Substring(0, lambda); } // Attributes string attributes = string.Empty; while (cutout.StartsWith(_indexStart)) { int endPos = cutout.IndexOf(_indexEnd); string between = cutout.Substring(1, endPos - 1); if (attributes.Equals(string.Empty)) { attributes = between; } else { attributes = string.Format("{0}, {1}", attributes, between); } cutout = cutout.Substring(endPos + 1).TrimStart(); } // Default value string defaultValue = string.Empty; int eqPos = -1; while ((eqPos = cutout.IndexOf('=', eqPos + 1)) != -1) { // Not a parameter if (cutout.LastIndexOf('(', eqPos) == -1 || cutout.IndexOf(')', eqPos) == -1) { defaultValue = cutout.Substring(eqPos + 1).TrimStart(); cutout = cutout.Substring(0, eqPos).TrimEnd(); break; } } // Visibility Visibility vis = Visibility.Default; if (Utils.RemoveModifier(ref cutout, _private)) { vis = Visibility.Private; } else if (Utils.RemoveModifier(ref cutout, _protected)) { vis = Visibility.Protected; } else if (Utils.RemoveModifier(ref cutout, _internal)) { vis = Visibility.Internal; } else if (Utils.RemoveModifier(ref cutout, _public)) { vis = Visibility.Public; } // Modifiers string modifiers = string.Empty; Utils.RemoveModifier(ref cutout, _partial_); Utils.MoveModifiers(ref cutout, ref modifiers, Constants.modifiers); // Type string type = string.Empty; int spaceIndex = -1; while ((spaceIndex = cutout.IndexOf(' ', spaceIndex + 1)) != -1) { // Not just the delegate word if ((spaceIndex == -1 || !cutout.Substring(0, spaceIndex).Equals(_delegate)) && // Not in a template type (cutout.LastIndexOf('<', spaceIndex) == -1 || cutout.IndexOf('>', spaceIndex) == -1)) { type = cutout.Substring(0, spaceIndex); if (type.IndexOf('(') != -1) { type = constructor; } else { cutout = cutout.Substring(spaceIndex).TrimStart(); } break; } } Element kind = Element.Variables; if (type.Equals(_class)) { kind = Element.Classes; } else if (type.Equals(_interface)) { kind = Element.Interfaces; } else if (type.Equals(_namespace)) { kind = Element.Namespaces; } else if (type.Equals(_enum)) { kind = Element.Enums; } else if (type.Equals(_struct)) { kind = Element.Structs; } else if (lambda >= 0) { kind = cutout.Contains('(') ? Element.Functions : Element.Properties; } // Extension string extends = string.Empty; int extensionIndex = cutout.IndexOf(':'); if (extensionIndex != -1) { extends = cutout.Substring(extensionIndex + 1).TrimStart(); cutout = cutout.Substring(0, extensionIndex).TrimEnd(); } // Default visibility if (vis == Visibility.Default && kind != Element.Namespaces) { if (node.name != null && (node.Kind == Element.Enums || node.Kind == Element.Interfaces)) { vis = Visibility.Public; } else if (kind == Element.Classes || kind == Element.Interfaces || kind == Element.Structs) { vis = Visibility.Internal; } else { vis = Visibility.Private; } } // Multiple ; support if (instruction && cutout.Equals(string.Empty)) { summary = string.Empty; break; } // Node handling MemberNode newNode = Utils.GetNodeByText(node, cutout); if (!cutout.Equals(string.Empty)) { if (newNode == null) { sourceInfo.Invoke((MethodInvoker) delegate { newNode = new MemberNode(Utils.RemoveParamNames(cutout)); node.Nodes.Add(newNode); newNode.Vis = vis; Utils.MakeNodeName(newNode); }); if (modifiers.Contains(_abstract)) { newNode.NodeFont = italic; } else if (modifiers.Contains(_static)) { newNode.NodeFont = underline; } } if (block) { node = newNode; if (kind == Element.Variables) { kind = cutout.IndexOf('(') != -1 ? Element.Functions : Element.Properties; lastRemovableDepth = depth; inRemovableBlock = true; } } if (newNode.name != null) { newNode.summary += summary; } // "int a, b;" case, copy tags else if (type.Equals(string.Empty) && newNode.Parent != null && newNode.Parent.Nodes.Count > 1 && !cutout.Contains(((MemberNode)newNode.Parent).name)) // Not constructor { MemberNode lastNode = (MemberNode)newNode.Parent.Nodes[newNode.Parent.Nodes.Count - 2]; newNode.NodeFont = lastNode.NodeFont; newNode.CopyFrom(lastNode, false); newNode.defaultValue = defaultValue; newNode.name = cutout; if (!string.IsNullOrEmpty(summary)) { newNode.summary = summary; } newNode.export = new ExportInfo(); } else { newNode.name = cutout; newNode.attributes = attributes; newNode.defaultValue = defaultValue; newNode.extends = extends; newNode.modifiers = modifiers.Trim(); newNode.summary = summary; newNode.type = type; newNode.Vis = vis; newNode.Kind = kind; newNode.export = new ExportInfo(); if (kind == Element.Namespaces) { newNode.NodeFont = bold; } } } if (closing && node.Parent != null) { node = (MemberNode)node.Parent; } summary = string.Empty; } break; case '(': case '[': if (!inRemovableBlock && !commentLine && !preprocessorSkip) { ++parenthesis; } break; case ')': case ']': if (lastEquals != i - 1 && !inRemovableBlock && !commentLine && !preprocessorSkip) { --parenthesis; } if (code[i] != ']') { inTemplate = false; } break; case '<': // could be a simple "smaller than", but cancelled at all possibilities that break this assumption inTemplate = true; break; case '?': case '>': inTemplate = false; break; case '/': if (!preprocessorSkip) { if (!commentLine) { if (lastSlash == i - 1) { commentLine = true; } lastSlash = i; } else if (commentLine && !summaryLine) { if (lastSlash == i - 1) { summaryLine = true; } lastSlash = i; } } break; case '=': lastEquals = i; break; case '#': commentLine = preprocessorLine = true; break; case '\n': if (preprocessorLine) { string line = code.Substring(lastEnding, i - lastEnding).Trim(); if (line.StartsWith(_define)) { string defined = line.Substring(line.IndexOf(' ')).TrimStart(); Array.Resize(ref defineConstants, ++defineCount); defineConstants[defineCount - 1] = defined; } else if (line.StartsWith(_if) || line.StartsWith(_elif)) { int commentPos; if ((commentPos = line.IndexOf("//")) != -1) { line = line.Substring(0, commentPos).TrimEnd(); } preprocessorSkip = !ParseSimpleIf(line.Substring(line.IndexOf(' ')).TrimStart(), defineConstants); } else if (line.StartsWith(_endif)) { preprocessorSkip = false; } preprocessorLine = false; } if (commentLine || preprocessorSkip) { commentLine = false; lastEnding = i + 1; } if (summaryLine) { summary += code.Substring(lastSlash + 1, i - lastSlash - 1).Trim() + '\n'; summaryLine = false; } ++loc; break; default: break; } } return(loc); }
/// <summary> /// Start the documentation generation. /// </summary> /// <param name="node">Program tree</param> /// <param name="path">Path to the documentation</param> /// <param name="exporter">Async export handler</param> public static void GenerateDocumentation(MemberNode node, string path, Exporter exporter = null) { File.WriteAllText(path + '\\' + stylesheet, style); Design.exporter = exporter; GenerateDocumentation(node, path, 0); }
public static string QuickSummary(string source, MemberNode node) => RemoveTag(ref source, "summary", node).Trim();
/// <summary> /// Generate the documentation page's relevant information of the code element. /// </summary> static string Content(MemberNode node) { List <MemberNode>[] types = new List <MemberNode> [(int)Element.Variables + 1]; for (int i = 0; i <= (int)Element.Variables; ++i) { types[i] = new List <MemberNode>(); } IEnumerator enumer = node.Nodes.GetEnumerator(); while (enumer.MoveNext()) { MemberNode current = (MemberNode)enumer.Current; if (current.exportable) { types[(int)current.Kind].Add(current); } } StringBuilder output = new StringBuilder("<h1>"); if (node.name != null) { output.Append(node.type).Append(' ').Append(node.name); } else { output.Append(node.Name); } output.Append("</h1>"); if (node.name != null) { // Summary block string summary = node.summary; BlockStart(); output.Append(node.export.summary).Append("<table>"); if (exportAttributes && !node.attributes.Equals(string.Empty)) { BlockAppend(output, "Attributes", node.attributes); } if (node.Vis != Visibility.Default) { BlockAppend(output, "Visibility", node.Vis.ToString()); } if (node.Kind == Element.Properties) { BlockAppend(output, "Getter", node.Getter != Visibility.Default ? node.Getter.ToString() : "No getter"); BlockAppend(output, "Setter", node.Setter != Visibility.Default ? node.Setter.ToString() : "No setter"); } if (!node.modifiers.Equals(string.Empty)) { BlockAppend(output, "Modifiers", node.modifiers); } if (!node.extends.Equals(string.Empty)) { BlockAppend(output, "Extends", node.extends); } if (!node.defaultValue.Equals(string.Empty)) { BlockAppend(output, "Default value", node.defaultValue); } if (!node.export.returns.Equals(string.Empty)) { BlockAppend(output, "Returns", node.export.returns); } output.Append("</table>"); if (summary.Contains("</param>")) { output.Append("<h1>Parameters</h1>").Append("<table>"); BlockStart(); string[] definedParams = node.name.Substring(node.name.IndexOf('(') + 1).Split(',', ')'); string[] parameters; while ((parameters = Utils.RemoveParam(ref summary)) != null) { string paramType = string.Empty; foreach (string definedParam in definedParams) { if (definedParam.Contains(parameters[0])) { string cut = definedParam; int eqPos; if ((eqPos = cut.IndexOf('=')) != -1) // remove default value { cut = cut.Substring(0, eqPos).Trim(); } if (cut.EndsWith(parameters[0])) { cut = cut.Substring(0, cut.LastIndexOf(parameters[0])); string trim = cut.Trim(); // if it doesn't end with a whitespace, it's another param that ends the same way if (cut.Length != trim.Length) { paramType = cut.Trim(); break; } } } } BlockAppend(output, paramType + " <b>" + parameters[0] + "</b>", parameters[1]); } output.Append("</table>"); } } output.Append(ContentBlock(types[0], "Namespaces")); for (int i = (int)Element.Namespaces + 1; i <= (int)Element.Variables; ++i) { output.Append(VisibilityContentBlock(types[i], ((Element)i).ToString().ToLower())); } if (node.name != null && node.export.referencedBy.Count != 0) { HashSet <MemberNode> referencedBy = node.export.referencedBy; output.Append("<h1>See also</h1>").Append("<table>"); BlockStart(); foreach (MemberNode reference in referencedBy) { if (reference.exportable) { BlockAppend(output, Utils.FullyQualifiedName(reference), Utils.QuickSummary(reference.summary)); } } output.Append("</table>"); } return(output.ToString()); }
public static string LocalLink(MemberNode to) { return(new StringBuilder(to.Name).Append(to.Kind < Element.Functions ? "\\index." : ".") .Append(Design.extension).ToString()); }