using System.Text; using System.Xml; namespace RobotNet10.ScriptEditor.Helpers.Monaco.Documentation; public class DocumentationComment( string summaryText = "", DocumentationItem[]? typeParamElements = null, DocumentationItem[]? paramElements = null, string returnsText = "", string remarksText = "", string exampleText = "", string valueText = "", DocumentationItem[]? exception = null) { public string SummaryText { get; } = summaryText; public DocumentationItem[] TypeParamElements { get; } = typeParamElements ?? []; public DocumentationItem[] ParamElements { get; } = paramElements ?? []; public string ReturnsText { get; } = returnsText; public string RemarksText { get; } = remarksText; public string ExampleText { get; } = exampleText; public string ValueText { get; } = valueText; public DocumentationItem[] Exception { get; } = exception ?? []; public static DocumentationComment? From(string xmlDocumentation, string lineEnding) { if (string.IsNullOrEmpty(xmlDocumentation)) return Empty; var reader = new StringReader("" + xmlDocumentation + ""); var summaryText = new StringBuilder(); var typeParamElements = new List(); var paramElements = new List(); var returnsText = new StringBuilder(); var remarksText = new StringBuilder(); var exampleText = new StringBuilder(); var valueText = new StringBuilder(); var exception = new List(); using (var xml = XmlReader.Create(reader)) { try { xml.Read(); string? elementName = null; StringBuilder? currentSectionBuilder = null; do { if (xml.NodeType == XmlNodeType.Element) { elementName = xml.Name.ToLowerInvariant(); switch (elementName) { case "filterpriority": xml.Skip(); break; case "remarks": currentSectionBuilder = remarksText; break; case "example": currentSectionBuilder = exampleText; break; case "exception": DocumentationItemBuilder exceptionInstance = new(GetCref(xml["cref"]).TrimEnd()); currentSectionBuilder = exceptionInstance.Documentation; exception.Add(exceptionInstance); break; case "returns": currentSectionBuilder = returnsText; break; case "summary": currentSectionBuilder = summaryText; break; case "see": if (currentSectionBuilder is null) continue; currentSectionBuilder.Append(GetCref(xml["cref"])); currentSectionBuilder.Append(xml["langword"]); break; case "seealso": if (currentSectionBuilder is null) continue; currentSectionBuilder.Append("See also: "); currentSectionBuilder.Append(GetCref(xml["cref"])); break; case "paramref": if (currentSectionBuilder is null) continue; currentSectionBuilder.Append(xml["name"]); currentSectionBuilder.Append(' '); break; case "param": DocumentationItemBuilder paramInstance = new(TrimMultiLineString(xml["name"] ?? "", lineEnding)); currentSectionBuilder = paramInstance.Documentation; paramElements.Add(paramInstance); break; case "typeparamref": if (currentSectionBuilder is null) continue; currentSectionBuilder.Append(xml["name"]); currentSectionBuilder.Append(' '); break; case "typeparam": DocumentationItemBuilder typeParamInstance = new(TrimMultiLineString(xml["name"] ?? "", lineEnding)); currentSectionBuilder = typeParamInstance.Documentation; typeParamElements.Add(typeParamInstance); break; case "value": currentSectionBuilder = valueText; break; case "br": case "para": if (currentSectionBuilder is null) continue; currentSectionBuilder.Append(lineEnding); break; } } else if (xml.NodeType == XmlNodeType.Text && currentSectionBuilder != null) { if (elementName == "code") { currentSectionBuilder.Append(xml.Value); } else { currentSectionBuilder.Append(TrimMultiLineString(xml.Value, lineEnding)); } } } while (xml.Read()); } catch (Exception) { return null; } } return new DocumentationComment( summaryText.ToString(), [.. typeParamElements.Select(s => s.ConvertToDocumentedObject())], [.. paramElements.Select(s => s.ConvertToDocumentedObject())], returnsText.ToString(), remarksText.ToString(), exampleText.ToString(), valueText.ToString(), [.. exception.Select(s => s.ConvertToDocumentedObject())]); } private static string TrimMultiLineString(string input, string lineEnding) { var lines = input.Split(separator, StringSplitOptions.RemoveEmptyEntries); return string.Join(lineEnding, lines.Select(l => TrimStartRetainingSingleLeadingSpace(l))); } private static string GetCref(string? cref) { if (cref == null || cref.Trim().Length == 0) { return ""; } if (cref.Length < 2) { return cref; } if (cref.Substring(1, 1) == ":") { return string.Concat(cref.AsSpan(2, cref.Length - 2), " "); } return cref + " "; } private static string TrimStartRetainingSingleLeadingSpace(string input) { if (string.IsNullOrWhiteSpace(input)) return string.Empty; if (!char.IsWhiteSpace(input[0])) return input; return $" {input.TrimStart()}"; } public string GetParameterText(string name) => Array.Find(ParamElements, parameter => parameter.Name == name)?.Documentation ?? string.Empty; public string GetTypeParameterText(string name) => Array.Find(TypeParamElements, typeParam => typeParam.Name == name)?.Documentation ?? string.Empty; public static readonly DocumentationComment Empty = new(); private static readonly string[] separator = ["\n", "\r\n"]; } class DocumentationItemBuilder(string name) { public string Name { get; set; } = name; public StringBuilder Documentation { get; set; } = new StringBuilder(); public DocumentationItem ConvertToDocumentedObject() { return new DocumentationItem(Name, Documentation.ToString()); } }