private void ParseGenericParameters(AssemblyComponent com, ref string nameString) { Match genericParams = Regex.Match(nameString, "(`+[0-9])"); if (genericParams.Success) { int genericCount = 0; string generic = ""; if (int.TryParse(genericParams.Value.Replace("`", ""), out genericCount)) { if (genericCount > 1) { for (int i = 0; i < genericCount; i++) { com.GenericParameters.Add(new ComponentParameter() { Name = $"T{i + 1}", }); } } else { com.GenericParameters.Add(new ComponentParameter() { Name = "T" }); } } nameString = nameString.Replace(genericParams.Value, generic); } }
private string ParseSummaryText(AssemblyComponent assembly, string innerXml) { string result = innerXml; Match xmlMatch = Regex.Match(result, @"(<.*?/>)|(<.*?>\S*</.*?>)"); while (xmlMatch.Success) { XmlDocument inlineDoc = new XmlDocument(); inlineDoc.LoadXml(xmlMatch.Value); foreach (XmlNode child in inlineDoc.ChildNodes) { foreach (XmlAttribute att in child.Attributes) { string attName = att.Name.ToLower(); switch (attName) { case "cref": AssemblyComponent com = _nameParser.Parse(assembly, att.Value); result = result.Replace(xmlMatch.Value, com.QualifiedName); break; } } } xmlMatch = xmlMatch.NextMatch(); } return(result); }
private AssemblyComponent ParseAssembly(ref string xml) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); AssemblyComponent assembly = new AssemblyComponent(ComponentType.Assembly); XmlNode root = xmlDoc["doc"]; foreach (XmlNode node in root.ChildNodes) { switch (node.Name.ToLower()) { case "assembly": ParseAssemblyNode(assembly, node); break; case "members": ParseMembers(assembly, node); break; } } return(assembly); }
private void ParseIndexerParameters(AssemblyComponent com, ref string nameString) { Match indexerParams = Regex.Match(nameString, @"\[(.*?)\]"); if (indexerParams.Success) { nameString = nameString.Replace(indexerParams.Value, ""); } }
private void ParseAssemblyNode(AssemblyComponent assembly, XmlNode assemblyNode) { foreach (XmlNode node in assemblyNode.ChildNodes) { switch (node.Name.ToLower()) { case "name": assembly.ShortName = node.InnerText; break; } } }
private void ParseSummary(AssemblyComponent assembly, AssemblyComponent component, XmlNode node) { foreach (XmlNode child in node.ChildNodes) { string nName = child.Name.ToLower(); switch (nName) { case "summary": component.Summary = ParseSummaryText(assembly, child.InnerXml); break; } } }
private void GenerateIndexPage(AssemblyComponent component, StreamWriter writer, int depth, string path) { if (component.Parent != null && component.ComponentType == ComponentType.Namespace) { string ns = component.ShortName; // Get full namespace by travelling backup the tree. AssemblyComponent p = component.Parent; while (p != null && p.ComponentType == ComponentType.Namespace) { ns = $"{p.ShortName}.{ns}"; p = p.Parent; } depth = 1; writer.Write(" " + Environment.NewLine); writer.Write($"* {ns}"); } else { if (!string.IsNullOrEmpty(path) && component.ComponentType == ComponentType.Type) { string indent = ""; for (int i = 0; i < depth - 1; i++) { indent += " "; } indent += "* "; writer.Write(" " + Environment.NewLine); string fn = SanitizePath($"{path}/{component.GenericName}"); writer.Write($"{indent} [{component.Definition}]({fn}.md)"); GenerateTypePage(path, component); } depth++; } List <AssemblyComponent> children = component.Children.Values.ToList(); children.Sort(_namespaceComparer); foreach (AssemblyComponent child in children) { GenerateIndexPage(child, writer, depth + 1, $"{path}/{component.GenericName}"); } }
private void ParseMethodParameters(AssemblyComponent com, ref string nameString) { Match methodParams = Regex.Match(nameString, @"\((.*?)\)"); if (methodParams.Success) { nameString = nameString.Replace(methodParams.Value, ""); string[] parameters = methodParams.Value.Replace("(", "").Replace(")", "").Split(","); for (int i = 0; i < parameters.Length; i++) { com.InputParameters.Add(new ComponentParameter() { Name = parameters[i], }); } } }
/// <summary> /// Gets a child component with the specified name. If the child does not exist, it will be created. /// </summary> /// <param name="childName">The name of the child component to be retrieved.</param> /// <returns>A <see cref="AssemblyComponent"/>.</returns> public AssemblyComponent this[string childName] { get { if (Children.TryGetValue(childName, out AssemblyComponent child)) { return(child); } else { child = new AssemblyComponent(ComponentType.Namespace); child.ShortName = childName; child.Parent = this; Children.Add(childName, child); return(child); } } }
/// <summary> /// Generates Markdown file from Visual Studio XML documentation. /// </summary> /// <param name="path">The path to store the markdown files.</param> /// <param name="xml">The xml documentation string.</param> /// <returns></returns> public void ToMarkdown(string path, string xml) { AssemblyComponent assembly = ParseAssembly(ref xml); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } using (FileStream stream = new FileStream($"{path}/{assembly.ShortName}.md", FileMode.Create, FileAccess.Write)) { using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)) { writer.Write($"# {assembly.ShortName}"); GenerateIndexPage(assembly, writer, 0, ""); } } }
private void GenerateTypePage(string path, AssemblyComponent typeComponent) { path = SanitizePath(path); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fn = SanitizePath(typeComponent.GenericName); using (FileStream stream = new FileStream($"{path}/{fn}.md", FileMode.Create, FileAccess.Write)) { using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8)) { GenerateTypeIndex(typeComponent, writer, 0, path); } } }
private void ParseMembers(AssemblyComponent assembly, XmlNode membersNode) { foreach (XmlNode node in membersNode.ChildNodes) { if (node.Name == "#comment") { Console.WriteLine("Documentation warning: " + node.InnerText); continue; } // Parts: 0 = member type, 1 = namespace and name. AssemblyComponent com = _nameParser.Parse(assembly, node.Attributes["name"].Value); if (com == null) { continue; } ParseSummary(assembly, com, node); } }
private void GenerateTypeIndex(AssemblyComponent component, StreamWriter writer, int depth, string path) { if (!string.IsNullOrEmpty(path)) { string indent = ""; if (depth > 0) { for (int i = 0; i < depth - 1; i++) { indent += " "; } indent += "* "; } writer.Write(" " + Environment.NewLine); string fn = SanitizePath($"{path}/{component.ShortName}"); if (depth > 0) { writer.Write($"{indent} [{component.Definition}]({fn}.md)"); } else { writer.WriteLine($"# {component.QualifiedName}"); writer.WriteLine($"{component.Summary}"); } } List <AssemblyComponent> children = component.Children.Values.ToList(); children.Sort(_namespaceComparer); foreach (AssemblyComponent child in children) { GenerateTypeIndex(child, writer, depth + 1, $"{path}/{component.ShortName}"); } }
internal AssemblyComponent Parse(AssemblyComponent assembly, string nameString) { AssemblyComponent com = new AssemblyComponent(ComponentType.Namespace); ParseGenericParameters(com, ref nameString); ParseMethodParameters(com, ref nameString); ParseIndexerParameters(com, ref nameString); string[] nameParts = nameString.Split(":"); string[] tnParts = nameParts[1].Split("~"); string[] nsParts = tnParts[0].Split("."); int typeNameID = nsParts.Length - 1; string returnType = tnParts.Length > 1 ? tnParts[1] : ""; int i = 0; if (nsParts[0] == assembly.ShortName) { i++; } // Add or locate namespace components AssemblyComponent parent = assembly; for (; i < typeNameID; i++) { AssemblyComponent newParent = parent[nsParts[i]]; if (parent != assembly) { newParent.ParentNamespace = parent.ParentNamespace != null ? $"{parent.ParentNamespace}.{parent.ShortName}" : parent.ShortName; } parent = newParent; } if (parent.ComponentType == ComponentType.OperatorMethod) { return(null); } com.ShortName = nsParts[typeNameID]; com.Parent = parent; com.ReturnType = returnType; com.ParentNamespace = $"{parent.ParentNamespace}.{parent.ShortName}"; switch (nameParts[0]) { case "F": // Field parent.ComponentType = ComponentType.Type; com.ComponentType = ComponentType.Field; break; case "T": // Type: com.ComponentType = ComponentType.Type; break; case "M": // Method parent.ComponentType = ComponentType.Type; com.ComponentType = ComponentType.Method; break; case "P": // Property parent.ComponentType = ComponentType.Type; if (com.InputParameters.Count == 0) { com.ComponentType = ComponentType.Property; } else { com.ComponentType = ComponentType.IndexerProperty; } break; case "E": // Event parent.ComponentType = ComponentType.Type; com.ComponentType = ComponentType.Event; break; } // TODO improve this. Do not create a new component if (!parent.Children.ContainsKey(com.Definition)) { parent.Children.Add(com.Definition, com); } return(com); }