mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 14:17:51 +08:00
231 lines
7.5 KiB
C#
231 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityExplorer.Core.Runtime;
|
|
|
|
namespace UnityExplorer.UI.Utility
|
|
{
|
|
/// <summary>
|
|
/// Syntax-highlights a member's signature, by either the Type name or a Type and Member together.
|
|
/// </summary>
|
|
public class SignatureHighlighter
|
|
{
|
|
public const string FIELD_STATIC = "#8d8dc6";
|
|
public const string FIELD_INSTANCE = "#c266ff";
|
|
|
|
public const string METHOD_STATIC = "#b55b02";
|
|
public const string METHOD_INSTANCE = "#ff8000";
|
|
|
|
public const string PROP_STATIC = "#588075";
|
|
public const string PROP_INSTANCE = "#55a38e";
|
|
|
|
public const string CLASS_STATIC = "#3a8d71";
|
|
public const string CLASS_INSTANCE = "#2df7b2";
|
|
|
|
public const string CLASS_STRUCT = "#0fba3a";
|
|
|
|
public const string LOCAL_ARG = "#a6e9e9";
|
|
|
|
public static string CONST_VAR = "#92c470";
|
|
|
|
public static string NAMESPACE = "#a8a8a8";
|
|
|
|
internal static string GetClassColor(Type type)
|
|
{
|
|
if (type.IsAbstract && type.IsSealed)
|
|
return CLASS_STATIC;
|
|
else if (type.IsEnum || type.IsGenericParameter)
|
|
return CONST_VAR;
|
|
else if (type.IsValueType)
|
|
return CLASS_STRUCT;
|
|
else
|
|
return CLASS_INSTANCE;
|
|
}
|
|
|
|
private static readonly StringBuilder syntaxBuilder = new StringBuilder(8192);
|
|
|
|
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
|
|
{
|
|
if (type == null)
|
|
throw new ArgumentNullException("type");
|
|
|
|
syntaxBuilder.Clear();
|
|
|
|
if (includeNamespace && !string.IsNullOrEmpty(type.Namespace))
|
|
syntaxBuilder.Append($"<color={NAMESPACE}>{type.Namespace}</color>.");
|
|
|
|
var declaring = type.DeclaringType;
|
|
while (declaring != null)
|
|
{
|
|
syntaxBuilder.Append(HighlightTypeName(declaring) + ".");
|
|
declaring = declaring.DeclaringType;
|
|
}
|
|
|
|
syntaxBuilder.Append(HighlightTypeName(type));
|
|
|
|
if (memberInfo != null)
|
|
{
|
|
syntaxBuilder.Append('.');
|
|
|
|
string memColor = GetMemberInfoColor(memberInfo, out bool isStatic);
|
|
|
|
if (isStatic)
|
|
syntaxBuilder.Append("<i>");
|
|
|
|
syntaxBuilder.Append($"<color={memColor}>{memberInfo.Name}</color>");
|
|
|
|
if (isStatic)
|
|
syntaxBuilder.Append("</i>");
|
|
|
|
if (memberInfo is MethodInfo method)
|
|
syntaxBuilder.Append(ParseGenericArgs(method.GetGenericArguments(), true));
|
|
}
|
|
|
|
return syntaxBuilder.ToString();
|
|
}
|
|
|
|
private static readonly Dictionary<string, string> typeToRichType = new Dictionary<string, string>();
|
|
|
|
public static string HighlightTypeName(Type type, bool includeNamespace = false, bool includeDllName = false)
|
|
{
|
|
string ret = HighlightType(type);
|
|
|
|
if (includeNamespace && !string.IsNullOrEmpty(type.Namespace))
|
|
ret = $"<color={NAMESPACE}>{type.Namespace}</color>.{ret}";
|
|
|
|
if (includeDllName)
|
|
{
|
|
if (!string.IsNullOrEmpty(type.Assembly.Location))
|
|
ret = $"{ret} ({Path.GetFileName(type.Assembly.Location)})";
|
|
else
|
|
ret = $"{ret} ({type.Assembly.GetName().Name})";
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static string HighlightType(Type type)
|
|
{
|
|
string key = type.ToString();
|
|
if (typeToRichType.ContainsKey(key))
|
|
return typeToRichType[key];
|
|
|
|
var typeName = type.Name;
|
|
|
|
bool isArray = false;
|
|
if (typeName.EndsWith("[]"))
|
|
{
|
|
isArray = true;
|
|
typeName = typeName.Substring(0, typeName.Length - 2);
|
|
}
|
|
|
|
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
|
|
{
|
|
typeName = $"<color={CONST_VAR}>{typeName}</color>";
|
|
}
|
|
else
|
|
{
|
|
var args = type.GetGenericArguments();
|
|
|
|
if (args.Length > 0)
|
|
{
|
|
// remove the `N from the end of the type name
|
|
// this could actually be >9 in some cases, so get the length of the length string and use that.
|
|
// eg, if it was "List`15", we would remove the ending 3 chars
|
|
|
|
int suffixLen = 1 + args.Length.ToString().Length;
|
|
|
|
// make sure the typename actually has expected "`N" format.
|
|
if (typeName[typeName.Length - suffixLen] == '`')
|
|
typeName = typeName.Substring(0, typeName.Length - suffixLen);
|
|
}
|
|
|
|
// highlight the base name itself
|
|
// do this after removing the `N suffix, so only the name itself is in the color tags.
|
|
typeName = $"<color={GetClassColor(type)}>{typeName}</color>";
|
|
|
|
// parse the generic args, if any
|
|
if (args.Length > 0)
|
|
typeName += ParseGenericArgs(args);
|
|
}
|
|
|
|
if (isArray)
|
|
typeName += "[]";
|
|
|
|
typeToRichType.Add(key, typeName);
|
|
|
|
return typeName;
|
|
}
|
|
|
|
private static readonly StringBuilder genericBuilder = new StringBuilder(4096);
|
|
|
|
public static string ParseGenericArgs(Type[] args, bool isGenericParams = false)
|
|
{
|
|
if (args.Length < 1)
|
|
return string.Empty;
|
|
|
|
genericBuilder.Clear();
|
|
genericBuilder.Append('<');
|
|
|
|
for (int i = 0; i < args.Length; i++)
|
|
{
|
|
if (i > 0)
|
|
genericBuilder.Append(',');
|
|
|
|
if (isGenericParams)
|
|
{
|
|
genericBuilder.Append($"<color={CONST_VAR}>{args[i].Name}</color>");
|
|
continue;
|
|
}
|
|
|
|
// using HighlightTypeName makes it recursive, so we can parse nested generic args.
|
|
genericBuilder.Append(HighlightTypeName(args[i]));
|
|
}
|
|
|
|
genericBuilder.Append('>');
|
|
return genericBuilder.ToString();
|
|
}
|
|
|
|
public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic)
|
|
{
|
|
isStatic = false;
|
|
if (memberInfo is FieldInfo fi)
|
|
{
|
|
if (fi.IsStatic)
|
|
{
|
|
isStatic = true;
|
|
return FIELD_STATIC;
|
|
}
|
|
else
|
|
return FIELD_INSTANCE;
|
|
}
|
|
else if (memberInfo is MethodInfo mi)
|
|
{
|
|
if (mi.IsStatic)
|
|
{
|
|
isStatic = true;
|
|
return METHOD_STATIC;
|
|
}
|
|
else
|
|
return METHOD_INSTANCE;
|
|
}
|
|
else if (memberInfo is PropertyInfo pi)
|
|
{
|
|
if (pi.GetAccessors(true)[0].IsStatic)
|
|
{
|
|
isStatic = true;
|
|
return PROP_STATIC;
|
|
}
|
|
else
|
|
return PROP_INSTANCE;
|
|
}
|
|
|
|
throw new NotImplementedException(memberInfo.GetType().Name + " is not supported");
|
|
}
|
|
}
|
|
}
|