#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