using System;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditorInternal;
using UnityEngine;
namespace Phantom.XRMOD.Runtime.Editor
{
///
/// Provides functionality to redirect Unity Console log clicks to custom stack trace locations.
/// Also includes a menu item to toggle this feature.
///
public static class LogRedirection
{
private static readonly Regex _CONST_REGEX = new(@"\(at (.+)\:(\d+)\)\r?\n");
private static readonly Regex _CONST_VIRT_REGEX =
new(@"IL_[0-9a-f]+: .*\r?\n.* \(at .+:\d+\)", RegexOptions.Multiline);
private const string _CONST_ENABLE_LOG_REDIRECTION_KEY = "UnityFusionLogRedirection.Enabled";
private const string _CONST_MENU_ITEM_PATH = "Tools/XR-MOD/Enable UnityFusion Trace";
///
/// Gets or sets a value indicating whether log redirection is enabled.
/// Stored in EditorPrefs and defaults to true.
///
private static bool IsLogRedirectionEnabled
{
get => EditorPrefs.GetBool(_CONST_ENABLE_LOG_REDIRECTION_KEY, true);
set => EditorPrefs.SetBool(_CONST_ENABLE_LOG_REDIRECTION_KEY, value);
}
///
/// Initializes the menu item state when the editor loads or scripts are reloaded.
///
[InitializeOnLoadMethod]
private static void InitLogRedirectionMenuItem()
{
Menu.SetChecked(_CONST_MENU_ITEM_PATH, IsLogRedirectionEnabled);
}
///
/// Toggles the log redirection feature on or off.
///
[MenuItem(_CONST_MENU_ITEM_PATH, false, 10000)]
private static void ToggleLogRedirection()
{
IsLogRedirectionEnabled = !IsLogRedirectionEnabled;
Menu.SetChecked(_CONST_MENU_ITEM_PATH, IsLogRedirectionEnabled);
}
///
/// Callback for opening assets from the Unity Console.
/// Redirects log clicks to custom stack trace locations if the feature is enabled.
///
/// The instance ID of the object.
/// The line number.
/// True if the log was handled by this method, false otherwise.
[OnOpenAsset(0)]
public static bool OpenLog(int _instanceID, int _line)
{
if (!IsLogRedirectionEnabled)
{
return false; // Do not handle if disabled, let Unity's default behavior take over.
}
string tmp_SelectedStackTrace = GetSelectedStackTrace();
if (string.IsNullOrEmpty(tmp_SelectedStackTrace))
{
return false;
}
if (!tmp_SelectedStackTrace.Contains("UnityFusion StackTrace"))
{
Match tmp_VirtMatch = _CONST_VIRT_REGEX.Match(tmp_SelectedStackTrace);
if (!tmp_VirtMatch.Success) return false;
var tmp_LineMatch = new Regex(@" \(at (.+)\:(\d+)\)");
var tmp_LineMatchResult = tmp_LineMatch.Match(tmp_VirtMatch.Groups[0].Value);
if (!tmp_LineMatchResult.Success) return false;
InternalEditorUtility.OpenFileAtLineExternal(tmp_LineMatchResult.Groups[1].Value,
int.Parse(tmp_LineMatchResult.Groups[2].Value));
return true;
}
Match tmp_Match = _CONST_REGEX.Match(tmp_SelectedStackTrace);
if (!tmp_Match.Success) return false;
InternalEditorUtility.OpenFileAtLineExternal(tmp_Match.Groups[1].Value,
int.Parse(tmp_Match.Groups[2].Value));
return true;
}
///
/// Retrieves the currently selected stack trace from the Unity Console window.
///
/// The selected stack trace string, or null if not found.
private static string GetSelectedStackTrace()
{
Assembly tmp_EditorWindowAssembly = typeof(EditorWindow).Assembly;
Type tmp_ConsoleWindowType = tmp_EditorWindowAssembly.GetType("UnityEditor.ConsoleWindow");
if (tmp_ConsoleWindowType == null) return null;
FieldInfo tmp_ConsoleWindowFieldInfo =
tmp_ConsoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
if (tmp_ConsoleWindowFieldInfo == null) return null;
EditorWindow tmp_ConsoleWindow = tmp_ConsoleWindowFieldInfo.GetValue(null) as EditorWindow;
if (tmp_ConsoleWindow == null) return null;
// Only redirect if the Console window is currently focused
if (tmp_ConsoleWindow != EditorWindow.focusedWindow) return null;
FieldInfo tmp_ActiveTextFieldInfo =
tmp_ConsoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
if (tmp_ActiveTextFieldInfo == null) return null;
return (string) tmp_ActiveTextFieldInfo.GetValue(tmp_ConsoleWindow);
}
}
}