#if UNITY_EDITOR using JetBrains.Annotations; using System.Text; using UnityEditor; using UnityEngine; namespace Phantom.XRMOD.UnityFusion.Editor { internal static class MonoScriptSummaryParser { private static readonly StringBuilder stringBuilder = new(); public static bool TryParseSummary([NotNull] MonoBehaviour monoBehaviour, out string summary) { if(!(MonoScript.FromMonoBehaviour(monoBehaviour) is MonoScript monoScript) || monoScript == null) { summary = ""; return false; } string text = monoScript.text; int summaryStart = text.IndexOf(""); if(summaryStart == -1) { summary = ""; return false; } int baseClassBeforeSummary = text.LastIndexOf(':', summaryStart); if(baseClassBeforeSummary != -1) { summary = ""; return false; } int summaryEnd = text.IndexOf("", summaryStart); if(summaryEnd == -1) { summary = ""; return false; } summaryStart += "".Length; ParseSummary(text, summaryStart, summaryEnd); summary = stringBuilder.ToString().TrimEnd(); stringBuilder.Clear(); return true; } private static void ParseSummary(string code, int summaryStart, int summaryEnd) { int tagLength = -1; for(int i = summaryStart; i < summaryEnd; i++) { switch(code[i]) { case '<': if(TryParseXMLTag(code, i, ref tagLength)) { i += tagLength - 1; break; } stringBuilder.Append(code[i]); break; case '/': if(SubstringEquals(code, i, "///")) { i += 2; break; } stringBuilder.Append(code[i]); break; case '\n': case '\r': case '\t': break; case ' ': if(stringBuilder.Length > 0) { char prev = stringBuilder[stringBuilder.Length - 1]; if(prev != ' ' && prev != '\n') { stringBuilder.Append(' '); } } break; case '{': stringBuilder.Append('<'); break; case '}': stringBuilder.Append('>'); break; default: stringBuilder.Append(code[i]); break; } } } private static bool TryParseXMLTag(string line, int tagStartIndex, ref int tagLength) { int firstLetterIndex = tagStartIndex + 1; if(line.Length <= firstLetterIndex) { return false; } switch(line[firstLetterIndex]) { case 's': // Examples: // // Awake function if(SubstringEquals(line, firstLetterIndex, "see cref=\"")) { int refStartIndex = firstLetterIndex + "see cref=\"".Length; int tagEndIndex = line.IndexOf('>', refStartIndex + 1); if(tagEndIndex == -1) { return false; } // Example: if(line[tagEndIndex - 1] == '/') { int refEndIndex = line.IndexOf('"', refStartIndex + 1); if(refEndIndex == -1) { return false; } Append(line.Substring(refStartIndex, refEndIndex - refStartIndex)); tagLength = tagEndIndex - tagStartIndex + 1; return true; } // Example: Awake function else { int contentStartIndex = tagEndIndex + 1; int contentEndIndex = line.IndexOf('<', contentStartIndex + 1); if(contentEndIndex == -1) { return false; } int closingTagEndIndex = line.IndexOf('>', contentEndIndex + 1); Append(line.Substring(contentStartIndex, contentEndIndex - contentStartIndex)); tagLength = closingTagEndIndex - tagStartIndex + 1; return true; } } return false; case 't': // Examples: // // client if(SubstringEquals(line, firstLetterIndex, "typeparamref name=\"")) { int refStartIndex = firstLetterIndex + "typeparamref name=\"".Length; int tagEndIndex = line.IndexOf('>', refStartIndex + 1); if(tagEndIndex == -1) { return false; } // Example: if(line[tagEndIndex - 1] == '/') { int refEndIndex = line.IndexOf('"', refStartIndex + 1); if(refEndIndex == -1) { return false; } Append(line.Substring(refStartIndex, refEndIndex - refStartIndex)); tagLength = tagEndIndex - tagStartIndex + 1; return true; } // Example: client else { int contentStartIndex = tagEndIndex + 1; int contentEndIndex = line.IndexOf('<', contentStartIndex + 1); if(contentEndIndex == -1) { return false; } int closingTagEndIndex = line.IndexOf('>', contentEndIndex + 1); Append(line.Substring(contentStartIndex, contentEndIndex - contentStartIndex)); tagLength = closingTagEndIndex - tagStartIndex + 1; return true; } } return false; case 'e': if(SubstringEquals(line, firstLetterIndex, "example>")) { tagLength = "".Length; return true; } return false; case 'c': if(SubstringEquals(line, firstLetterIndex, "code>")) { tagLength = "".Length; return true; } return false; case 'p': if(SubstringEquals(line, firstLetterIndex, "para>")) { stringBuilder.Append("\n\n"); tagLength = "".Length; return true; } return false; case '/': if(SubstringEquals(line, firstLetterIndex, "/para>")) { tagLength = "".Length; return true; } if(SubstringEquals(line, firstLetterIndex, "/typeparamref>")) { tagLength = "".Length; return true; } if(SubstringEquals(line, firstLetterIndex, "/code>")) { tagLength = "".Length; return true; } if(SubstringEquals(line, firstLetterIndex, "/example>")) { tagLength = "".Length; return true; } return false; } int endIndex = line.IndexOf('>', tagStartIndex + 1); if(endIndex == -1) { return false; } tagLength = endIndex - tagStartIndex + 1; return true; } private static void Append(string substring) => stringBuilder.Append(substring.Replace('{', '<').Replace('}', '>')); private static bool SubstringEquals(string line, int startIndex, string substring) { int charCount = substring.Length; int lastIndex = startIndex + charCount - 1; if(line.Length <= lastIndex) { return false; } for(int i = 0; i < charCount; i++) { if(line[startIndex + i] != substring[i]) { return false; } } return true; } } } #endif