diff --git a/package/Editor/Modules/SyntaxHighlighting.cs b/package/Editor/Modules/SyntaxHighlighting.cs index 0dfb19f..64bb9ba 100644 --- a/package/Editor/Modules/SyntaxHighlighting.cs +++ b/package/Editor/Modules/SyntaxHighlighting.cs @@ -204,7 +204,28 @@ string Eval(Match m) if (colorDict.TryGetValue("link", out var col)) { var displayUrl = link.Value.Substring(linkPrefix.Length).TrimEnd(')'); - line += $" in {displayUrl}"; + + // Trim the preceding path from the filename. + if (NeedleConsoleSettings.instance.StacktraceFilenameMode == NeedleConsoleSettings.StacktraceFilename.Compact) + { + var lastSlashIndex = displayUrl.LastIndexOf("/", StringComparison.Ordinal); + if (lastSlashIndex != -1) + { + displayUrl = displayUrl[(lastSlashIndex + 1)..]; + } + } + + var lineIndex = link.Groups["line"].Value; + line = NeedleConsoleSettings.instance.StacktraceFilenameMode switch + { + NeedleConsoleSettings.StacktraceFilename.Full + => $"{line} in {displayUrl}", + NeedleConsoleSettings.StacktraceFilename.Compact + => $"{line} {displayUrl}", + NeedleConsoleSettings.StacktraceFilename.Inline + => $"{line}:{lineIndex}", + _ => throw new ArgumentOutOfRangeException(), + }; } else line += link.Value; diff --git a/package/Editor/NeedleConsole.cs b/package/Editor/NeedleConsole.cs index 5a36442..b9fcc58 100644 --- a/package/Editor/NeedleConsole.cs +++ b/package/Editor/NeedleConsole.cs @@ -1,5 +1,6 @@ -using System.IO; +using System; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Unity.Profiling; using UnityEditor; @@ -10,6 +11,21 @@ namespace Needle.Console { public static class NeedleConsole { + /// + /// Matches namespaces - "XX.YY.ZZ" is split into the groups "XX.YY" and "ZZ".
+ /// Also handles generics inside "<" and ">". + ///
+ private static readonly Regex namespaceCompactRegex = new Regex(@"([^<>.\s]+(?:\.[^<>.\s]+)*)(\.)([^<>.(]+)", RegexOptions.Compiled); + private static readonly Regex paramsRegex = new Regex(@"\((?!at)(.*?)\)", RegexOptions.Compiled); + private static readonly Regex paramsArgumentRegex = new Regex(@"([ (])([^),]+?) (.+?)([\),])", RegexOptions.Compiled); + private static readonly Regex paramsRefRegex = new Regex(@"\b ?ref ?\b", RegexOptions.Compiled); + private static readonly MatchEvaluator namespaceReplacer = NamespaceReplacer; + private static readonly MatchEvaluator paramReplacer = ParamReplacer; + + private static int namespaceLinkStartIndex; + private static int namespaceMatchIndex; + private static string namespaceStored; + private static int namespaceStartIndex; [HyperlinkCallback(Href = "OpenNeedleConsoleSettings")] private static void OpenNeedleConsoleUserPreferences() @@ -120,6 +136,49 @@ public static void Apply(ref string stacktrace) } } + if (foundPrefix) + { + if ( + settings.StacktraceNamespaceMode == NeedleConsoleSettings.StacktraceNamespace.Compact || + settings.StacktraceNamespaceMode == NeedleConsoleSettings.StacktraceNamespace.CompactNoReturnType) + { + namespaceLinkStartIndex = line.IndexOf(" (at ", StringComparison.Ordinal); + namespaceMatchIndex = 0; + namespaceStartIndex = -1; + namespaceStored = ""; + + line = namespaceCompactRegex.Replace(line, namespaceReplacer); + + // If the class name was stripped out along with the namespaces, add + // it back in here. + int methodIndexEndIndex = line.IndexOf('('); + if (methodIndexEndIndex != -1 && line.IndexOf('.', 0, methodIndexEndIndex) == -1) + { + int classIndex = namespaceStored.LastIndexOf('.'); + if (classIndex != -1) + { + namespaceStored = namespaceStored[(classIndex + 1)..]; + } + line = line.Insert(namespaceStartIndex, $"{namespaceStored}."); + } + + // Remove the return type. + if (settings.StacktraceNamespaceMode == NeedleConsoleSettings.StacktraceNamespace.CompactNoReturnType) + { + if (namespaceStartIndex != -1) + { + line = line[namespaceStartIndex..]; + } + } + } + + if (settings.StacktraceParamsMode != NeedleConsoleSettings.StacktraceParams.Full) + { + line = paramsRefRegex.Replace(line, ""); + line = paramsRegex.Replace(line, paramReplacer); + } + } + if (foundPrefix && settings.UseSyntaxHighlighting) SyntaxHighlighting.AddSyntaxHighlighting(ref line); @@ -152,5 +211,36 @@ public static void Apply(ref string stacktrace) // ignore } } + + private static string NamespaceReplacer(Match match) + { + namespaceMatchIndex++; + + // Prevent replacing stuff in the filename link. + if (namespaceLinkStartIndex != -1 && match.Index >= namespaceLinkStartIndex) + return match.Value; + + // At this point there's no wa to differentiate between a generic type parameter and "Class.Method", + // causing the class name to be stripped which we don't want. + // Store this so that the class name can be added back at the start after all replacements have happened. + if (namespaceMatchIndex == 1) + { + namespaceStartIndex = match.Index; + namespaceStored = match.Groups[1].Value; + } + + return match.Groups[3].Value; + } + + private static string ParamReplacer(Match match) + { + return NeedleConsoleSettings.instance.StacktraceParamsMode switch + { + NeedleConsoleSettings.StacktraceParams.TypesOnly => paramsArgumentRegex.Replace(match.Value, "$1$2$4"), + NeedleConsoleSettings.StacktraceParams.NamesOnly => paramsArgumentRegex.Replace(match.Value, "$1$3$4"), + NeedleConsoleSettings.StacktraceParams.Compact => "()", + _ => throw new ArgumentOutOfRangeException(), + }; + } } } diff --git a/package/Editor/Settings/NeedleConsoleSettings.cs b/package/Editor/Settings/NeedleConsoleSettings.cs index c9976d0..67cca34 100644 --- a/package/Editor/Settings/NeedleConsoleSettings.cs +++ b/package/Editor/Settings/NeedleConsoleSettings.cs @@ -18,6 +18,28 @@ internal enum StacktraceOrientations Horizontal, Auto, } + [Serializable, Flags] + internal enum StacktraceNamespace + { + Full, + Compact, + CompactNoReturnType, + } + [Serializable, Flags] + internal enum StacktraceParams + { + Full, + TypesOnly, + NamesOnly, + Compact, + } + [Serializable] + internal enum StacktraceFilename + { + Full, + Compact, + Inline, + } [SerializeField] internal bool Enabled = true; @@ -129,6 +151,9 @@ public void UpdateCurrentTheme() public Font CustomLogEntryFont; public StacktraceOrientations StacktraceOrientation = StacktraceOrientations.Vertical; + public StacktraceNamespace StacktraceNamespaceMode = StacktraceNamespace.Full; + public StacktraceParams StacktraceParamsMode = StacktraceParams.Full; + public StacktraceFilename StacktraceFilenameMode = StacktraceFilename.Full; public float StacktraceOrientationAutoHeight = 300; public bool UseStacktraceIgnoreFilters = true; public string[] StacktraceIgnoreFilters = new string[] { }; diff --git a/package/Editor/Settings/NeedleConsoleSettingsProvider.cs b/package/Editor/Settings/NeedleConsoleSettingsProvider.cs index 0b22794..a30ad3f 100644 --- a/package/Editor/Settings/NeedleConsoleSettingsProvider.cs +++ b/package/Editor/Settings/NeedleConsoleSettingsProvider.cs @@ -137,13 +137,23 @@ public override void OnGUI(string searchContext) if (_scope.changed) NeedleConsoleProjectSettings.RaiseColorsChangedEvent(); } - settings.StacktraceOrientation = (NeedleConsoleSettings.StacktraceOrientations)EditorGUILayout.EnumPopup("Stacktrace Orientation", settings.StacktraceOrientation); - using (new EditorGUI.DisabledScope(settings.StacktraceOrientation != NeedleConsoleSettings.StacktraceOrientations.Auto)) - { - var windowHeight = Math.Max(300, Screen.height); - EditorGUI.indentLevel++; - settings.StacktraceOrientationAutoHeight = EditorGUILayout.IntSlider(new GUIContent("Auto Height (in pixel)", "The height at which the stacktrace orientation will switch from Vertical to Horizontal when Stacktrace Orientation is set to Auto."), (int)settings.StacktraceOrientationAutoHeight, 100, windowHeight); - EditorGUI.indentLevel--; + GUILayout.Space(10); + EditorGUILayout.LabelField("Experimental > Stacktrace", EditorStyles.boldLabel); + using (var _scope = new EditorGUI.ChangeCheckScope()) { + settings.StacktraceOrientation = (NeedleConsoleSettings.StacktraceOrientations)EditorGUILayout.EnumPopup("Orientation", settings.StacktraceOrientation); + using (new EditorGUI.DisabledScope(settings.StacktraceOrientation != NeedleConsoleSettings.StacktraceOrientations.Auto)) + { + var windowHeight = Math.Max(300, Screen.height); + EditorGUI.indentLevel++; + settings.StacktraceOrientationAutoHeight = EditorGUILayout.IntSlider(new GUIContent("Auto Height (in pixel)", "The height at which the stacktrace orientation will switch from Vertical to Horizontal when Stacktrace Orientation is set to Auto."), (int)settings.StacktraceOrientationAutoHeight, 100, windowHeight); + EditorGUI.indentLevel--; + } + + settings.StacktraceNamespaceMode = (NeedleConsoleSettings.StacktraceNamespace)EditorGUILayout.EnumPopup("Namespace", settings.StacktraceNamespaceMode); + settings.StacktraceParamsMode = (NeedleConsoleSettings.StacktraceParams)EditorGUILayout.EnumPopup("Parameters", settings.StacktraceParamsMode); + settings.StacktraceFilenameMode = (NeedleConsoleSettings.StacktraceFilename)EditorGUILayout.EnumPopup("Filename", settings.StacktraceFilenameMode); + + if (_scope.changed) ThemeEditedOrChanged?.Invoke(); } // using (new GUILayout.HorizontalScope())