diff --git a/src/DebugUtils/UnityDebugService/UnityDebugService.cs b/src/DebugUtils/UnityDebugService/UnityDebugService.cs index 8c3c9d0..7945c2c 100644 --- a/src/DebugUtils/UnityDebugService/UnityDebugService.cs +++ b/src/DebugUtils/UnityDebugService/UnityDebugService.cs @@ -30,27 +30,6 @@ namespace DCFApixels.DragonECS if (Instance.GetType() == typeof(UnityDebugService)) { return; } Set(); } - protected override void OnEnableBaseService(DebugService oldService) - { - EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; - EditorGUI.hyperLinkClicked += EditorGUI_hyperLinkClicked; - Application.logMessageReceived -= Application_logMessageReceived; - Application.logMessageReceived += Application_logMessageReceived; - } - protected override void OnDisableBaseService(DebugService nextService) - { - Application.logMessageReceived -= Application_logMessageReceived; - EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; - } - private void EditorGUI_hyperLinkClicked(EditorWindow editor, HyperLinkClickedEventArgs args) - { - throw new NotImplementedException(); - } - private void Application_logMessageReceived(string logString, string stackTrace, LogType type) - { - } - - protected override DebugService CreateThreadInstance() { @@ -67,6 +46,7 @@ namespace DCFApixels.DragonECS return; } string msg = AutoConvertObjectToString(v); + string indexedLink = UnityDebugServiceStorage.NewIndexedLink(); bool hasTag = string.IsNullOrEmpty(tag) == false; if (hasTag) { @@ -75,24 +55,24 @@ namespace DCFApixels.DragonECS { case "pass": Debug.Log( - $"[{tag}] {msg}"); + $"[{tag}] {msg}{indexedLink}"); break; case "warning": Debug.LogWarning( - $"[{tag}] {msg}"); + $"[{tag}] {msg}{indexedLink}"); break; case "error": Debug.LogError( - $"[{tag}] {msg}"); + $"[{tag}] {msg}{indexedLink}"); break; default: Debug.Log( - $"[{tag}] {msg}"); + $"[{tag}] {msg}{indexedLink}"); break; } return; } - Debug.Log(msg); + Debug.Log($"{msg}{indexedLink}"); } public override void Break() { diff --git a/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs index 93edbee..7c1ce5d 100644 --- a/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs +++ b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs @@ -1,17 +1,23 @@ #if UNITY_EDITOR using System; +using System.Collections.Generic; using System.Reflection; +using System.Text.RegularExpressions; using System.Threading; using UnityEditor; using UnityEngine; namespace DCFApixels.DragonECS.Unity.Internal { - internal class UnityDebugServiceStorage : ScriptableSingleton + using static UnityDebugServiceStorage; + using static UnityDebugServiceStorageInitializer; + + [InitializeOnLoad] + internal static class UnityDebugServiceStorageInitializer { private static readonly MethodInfo _getLogsCountMethod; public static readonly bool IsSupportAutoLingks; - static UnityDebugServiceStorage() + static UnityDebugServiceStorageInitializer() { var logEntriesType = typeof(EditorWindow).Assembly.GetType("UnityEditor.LogEntries"); if (logEntriesType != null) @@ -23,88 +29,167 @@ namespace DCFApixels.DragonECS.Unity.Internal EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; Application.logMessageReceived -= Application_logMessageReceived; _consoleLogCounter = -1; - if (IsSupportAutoLingks) + //if (IsSupportAutoLingks) { EditorGUI.hyperLinkClicked += EditorGUI_hyperLinkClicked; Application.logMessageReceived += Application_logMessageReceived; _consoleLogCounter = GetConsoleLogCount(); } } + + internal static int GetConsoleLogCount() + { + return (int)_getLogsCountMethod.Invoke(null, null); + } + } + internal class UnityDebugServiceStorage : ScriptableSingleton + { + + public UnityDebugServiceStorage() { } - private const int IntervalChecksTicksThreshold = 100; - private static int _consoleLogCounter; - private static int _intervalChecksTicks = 0; - private static StructList _recycledIndexes; - private static StructList _logEntries; - private static object _lock = new object(); + internal const int IntervalChecksTicksThreshold = 100; + internal static int _consoleLogCounter; + internal static int _intervalChecksTicks = 0; + internal static StructList _logEntries = new StructList(256); + internal static object _lock = new object(); - private static void EditorGUI_hyperLinkClicked(EditorWindow window, HyperLinkClickedEventArgs args) + internal static void EditorGUI_hyperLinkClicked(EditorWindow window, HyperLinkClickedEventArgs args) { - throw new NotImplementedException(); + OnProcessClickData(args.hyperLinkData); + } + internal static void OnProcessClickData(Dictionary infos) + { + if (infos == null) return; + if (!infos.TryGetValue("href", out var path)) return; + infos.TryGetValue("line", out var line); + + for (int i = 0; i < _logEntries.Count; i++) + { + ref var e = ref _logEntries._items[i]; + if (CheckLogWithIndexedLink(e.LogString)) + { + int indexof = e.LogString.LastIndexOf(INDEXED_LINK_PREV) - 1 + INDEXED_LINK_PREV.Length;// откатываю символ ∆ + int stringIndexLength = e.LogString.Length - (indexof + INDEXED_LINK_POST.Length); + + if(stringIndexLength == path.Length) + { + bool isSkip = false; + for (int j = 1; j < stringIndexLength; j++) + { + var pathchar = path[j]; + var logchar = e.LogString[indexof + j]; + if (pathchar != logchar) { isSkip = true; break; } + } + + if (isSkip) { continue; } + + OpenIDE(e); + + break; + } + + } + } } - private static void Application_logMessageReceived(string logString, string stackTrace, LogType type) + private static void OpenIDE(LogEntry entry) + { + var parsed = ParseLastCall(entry.StackTrace); + if (string.IsNullOrEmpty(parsed.path)) { return; } + UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(parsed.path, parsed.line); //TODO + } + public static (string path, int line) ParseLastCall(string stackTrace) + { + var debugTypeFullname = typeof(DCFApixels.DragonECS.EcsDebug).FullName; + stackTrace = stackTrace.Remove(0, stackTrace.IndexOf(debugTypeFullname)); + var lines = stackTrace.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 1; i < lines.Length; i++) + { + var line = lines[i]; + Match match = Regex.Match(line, @"\(at (?.+?):(?\d+)\)"); + if (match.Success) + { + string filePath = match.Groups["path"].Value; + string lineNumber = match.Groups["line"].Value; + return (filePath, int.Parse(lineNumber)); + } + } + return default; + } + + + internal static void Application_logMessageReceived(string logString, string stackTrace, LogType type) { if (_intervalChecksTicks >= IntervalChecksTicksThreshold || _logEntries.Count >= _logEntries.Capacity - 1) { CheckConsoleClean(); - _intervalChecksTicks = 0; } _logEntries.Add(new LogEntry(logString, stackTrace)); - Interlocked.Increment(ref _consoleLogCounter); - Interlocked.Increment(ref _intervalChecksTicks); + _consoleLogCounter++; + _intervalChecksTicks++; } private static bool CheckConsoleClean() { - int currentCount = GetConsoleLogCount(); - if (_consoleLogCounter > currentCount) + lock (_lock) { + if (_intervalChecksTicks < IntervalChecksTicksThreshold) { return false; } + int currentCount = GetConsoleLogCount(); + if (_consoleLogCounter > currentCount) + { + var l = _consoleLogCounter - currentCount; + if(l < _logEntries.Count) + { + _logEntries.FastRemoveSpan(0, l); + } - error - - _consoleLogCounter = currentCount; - return true; + _consoleLogCounter = currentCount; + _intervalChecksTicks = 0; + return true; + } + return false; } - return false; - } - private static int GetConsoleLogCount() - { - return (int)_getLogsCountMethod.Invoke(null, null); } + + private const string INDEXED_LINK_PREV = "\r\n\r\nOpen line"; private static string CreateIndexedLink(int index) { - return $" "; + return $"{INDEXED_LINK_PREV}{index}{INDEXED_LINK_POST}"; } //multi thread access. - public static string GetHyperLink() + public static string NewIndexedLink() { return instance.GetHyperLink_Internal(); } + private static int _hyperLinkIndex = 0; public string GetHyperLink_Internal() { - string hyperLink; - if (_recycledIndexes.Count > 0) - { - hyperLink = _recycledIndexes.Dequeue(); - } - else - { - hyperLink = CreateIndexedLink(_logEntries.Count); - } + var index = Interlocked.Increment(ref _hyperLinkIndex); + string hyperLink = CreateIndexedLink(index); return hyperLink; } + private static bool CheckLogWithIndexedLink(string log) + { + if (log.Length < INDEXED_LINK_POST.Length) { return false; } + for (int i = 0; i < INDEXED_LINK_POST.Length; i++) + { + char constChar = INDEXED_LINK_POST[i]; + char logChar = log[log.Length - INDEXED_LINK_POST.Length + i]; + if (constChar != logChar) { return false; } + } + return true; + } - - private readonly struct LogEntry + internal readonly struct LogEntry { public readonly string LogString; public readonly string StackTrace; diff --git a/src/Internal/Utils/StructList.cs b/src/Internal/Utils/StructList.cs index 88b8100..89eb4cb 100644 --- a/src/Internal/Utils/StructList.cs +++ b/src/Internal/Utils/StructList.cs @@ -154,6 +154,33 @@ namespace DCFApixels.DragonECS.Unity.Internal return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FastRemoveSpan(int startIndex, int length) + { +#if DEBUG + if (startIndex < 0) { Throw.ArgumentOutOfRange(); } + if (length < 0) { Throw.ArgumentOutOfRange(); } + if (startIndex + length > _count) { Throw.Argument("Invalid range specified"); } +#endif + + if (length == 0 || _count == 0) { return; } + + int elementsToMove = _count - (startIndex + length); + + if (elementsToMove > 0) + { + Array.Copy( + sourceArray: _items, + sourceIndex: startIndex + length, + destinationArray: _items, + destinationIndex: startIndex, + length: elementsToMove + ); + } + _count -= length; + + //RuntimeHelpers.IsReferenceOrContainsReferences(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveWithOrder(T item) { int index = IndexOf(item); @@ -222,17 +249,17 @@ namespace DCFApixels.DragonECS.Unity.Internal self._items[index] = item; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryDequeue_MultiAccess(this ref StructList self, T item) - { - var index = Interlocked.Increment(ref self._count); - -#if DEBUG - if (_count <= 0) { Throw.ArgumentOutOfRange(); } -#endif - T result = _items[--_count]; - _items[_count] = default; - return result; - } +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public static bool TryDequeue_MultiAccess(this ref StructList self, T item) +// { +// var index = Interlocked.Increment(ref self._count); +// +//#if DEBUG +// if (_count <= 0) { Throw.ArgumentOutOfRange(); } +//#endif +// T result = _items[--_count]; +// _items[_count] = default; +// return result; +// } } } \ No newline at end of file diff --git a/src/Internal/Utils/Throw.cs b/src/Internal/Utils/Throw.cs index 03aa65d..9fe02ff 100644 --- a/src/Internal/Utils/Throw.cs +++ b/src/Internal/Utils/Throw.cs @@ -8,5 +8,13 @@ namespace DCFApixels.DragonECS.Unity.Internal { throw new ArgumentOutOfRangeException(); } + internal static void Argument(string message) + { + throw new ArgumentException(message); + } + internal static void Exception() + { + throw new Exception(); + } } }