mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
Simplify exception formatting
Now that exceptions are not _parsed_ anymore (#513 and #637), keeping the `ExceptionInfo` and `StackFrameInfo` internal classes doesn't make much sense anymore. The `Exception` and `StackFrame` types can be used by the `ExceptionFormatter` class directly, without conversion.
This commit is contained in:
parent
f221c1f25c
commit
ff4215f431
@ -1,57 +0,0 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class ExceptionConverter
|
||||
{
|
||||
public static ExceptionInfo Convert(Exception exception)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var exceptionType = exception.GetType();
|
||||
var stackTrace = new StackTrace(exception, true);
|
||||
var frames = stackTrace.GetFrames().Where(f => f != null).Cast<StackFrame>().Select(Convert).ToList();
|
||||
var inner = exception.InnerException is null ? null : Convert(exception.InnerException);
|
||||
return new ExceptionInfo(exceptionType.FullName ?? exceptionType.Name, exception.Message, frames, inner);
|
||||
}
|
||||
|
||||
private static StackFrameInfo Convert(StackFrame frame)
|
||||
{
|
||||
var method = frame.GetMethod();
|
||||
if (method is null)
|
||||
{
|
||||
return new StackFrameInfo("<unknown method>", new List<(string Type, string Name)>(), null, null);
|
||||
}
|
||||
|
||||
var methodName = GetMethodName(method);
|
||||
var parameters = method.GetParameters().Select(e => (e.ParameterType.Name, e.Name ?? string.Empty)).ToList();
|
||||
var path = frame.GetFileName();
|
||||
var lineNumber = frame.GetFileLineNumber();
|
||||
return new StackFrameInfo(methodName, parameters, path, lineNumber == 0 ? null : lineNumber);
|
||||
}
|
||||
|
||||
private static string GetMethodName(MethodBase method)
|
||||
{
|
||||
var builder = new StringBuilder(256);
|
||||
|
||||
var fullName = method.DeclaringType?.FullName;
|
||||
if (fullName != null)
|
||||
{
|
||||
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L247-L253
|
||||
builder.Append(fullName.Replace('+', '.'));
|
||||
builder.Append('.');
|
||||
}
|
||||
|
||||
builder.Append(method.Name);
|
||||
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
builder.Append('[');
|
||||
builder.Append(string.Join(",", method.GetGenericArguments().Select(t => t.Name)));
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
@ -9,35 +9,35 @@ internal static class ExceptionFormatter
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var info = ExceptionConverter.Convert(exception);
|
||||
|
||||
return GetException(info, settings);
|
||||
return GetException(exception, settings);
|
||||
}
|
||||
|
||||
private static IRenderable GetException(ExceptionInfo info, ExceptionSettings settings)
|
||||
private static IRenderable GetException(Exception exception, ExceptionSettings settings)
|
||||
{
|
||||
if (info is null)
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
return new Rows(new IRenderable[]
|
||||
{
|
||||
GetMessage(info, settings),
|
||||
GetStackFrames(info, settings),
|
||||
GetMessage(exception, settings),
|
||||
GetStackFrames(exception, settings),
|
||||
}).Expand();
|
||||
}
|
||||
|
||||
private static Markup GetMessage(ExceptionInfo ex, ExceptionSettings settings)
|
||||
private static Markup GetMessage(Exception ex, ExceptionSettings settings)
|
||||
{
|
||||
var shortenTypes = (settings.Format & ExceptionFormats.ShortenTypes) != 0;
|
||||
var type = Emphasize(ex.Type, new[] { '.' }, settings.Style.Exception, shortenTypes, settings);
|
||||
var exceptionType = ex.GetType();
|
||||
var exceptionTypeFullName = exceptionType.FullName ?? exceptionType.Name;
|
||||
var type = Emphasize(exceptionTypeFullName, new[] { '.' }, settings.Style.Exception, shortenTypes, settings);
|
||||
|
||||
var message = $"[{settings.Style.Message.ToMarkup()}]{ex.Message.EscapeMarkup()}[/]";
|
||||
return new Markup(string.Concat(type, ": ", message));
|
||||
}
|
||||
|
||||
private static Grid GetStackFrames(ExceptionInfo ex, ExceptionSettings settings)
|
||||
private static Grid GetStackFrames(Exception ex, ExceptionSettings settings)
|
||||
{
|
||||
var styles = settings.Style;
|
||||
|
||||
@ -46,39 +46,44 @@ internal static class ExceptionFormatter
|
||||
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
|
||||
|
||||
// Inner
|
||||
if (ex.Inner != null)
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
grid.AddRow(
|
||||
Text.Empty,
|
||||
GetException(ex.Inner, settings));
|
||||
GetException(ex.InnerException, settings));
|
||||
}
|
||||
|
||||
// Stack frames
|
||||
foreach (var frame in ex.Frames)
|
||||
var stackTrace = new StackTrace(ex, fNeedFileInfo: true);
|
||||
foreach (var frame in stackTrace.GetFrames().Where(f => f != null).Cast<StackFrame>())
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// Method
|
||||
var shortenMethods = (settings.Format & ExceptionFormats.ShortenMethods) != 0;
|
||||
builder.Append(Emphasize(frame.Method, new[] { '.' }, styles.Method, shortenMethods, settings));
|
||||
var method = frame.GetMethod();
|
||||
var methodName = method.GetName();
|
||||
builder.Append(Emphasize(methodName, new[] { '.' }, styles.Method, shortenMethods, settings));
|
||||
builder.AppendWithStyle(styles.Parenthesis, "(");
|
||||
AppendParameters(builder, frame, settings);
|
||||
AppendParameters(builder, method, settings);
|
||||
builder.AppendWithStyle(styles.Parenthesis, ")");
|
||||
|
||||
if (frame.Path != null)
|
||||
var path = frame.GetFileName();
|
||||
if (path != null)
|
||||
{
|
||||
builder.Append(' ');
|
||||
builder.AppendWithStyle(styles.Dimmed, "in");
|
||||
builder.Append(' ');
|
||||
|
||||
// Path
|
||||
AppendPath(builder, frame, settings);
|
||||
AppendPath(builder, path, settings);
|
||||
|
||||
// Line number
|
||||
if (frame.LineNumber != null)
|
||||
var lineNumber = frame.GetFileLineNumber();
|
||||
if (lineNumber != 0)
|
||||
{
|
||||
builder.AppendWithStyle(styles.Dimmed, ":");
|
||||
builder.AppendWithStyle(styles.LineNumber, frame.LineNumber);
|
||||
builder.AppendWithStyle(styles.LineNumber, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,30 +95,28 @@ internal static class ExceptionFormatter
|
||||
return grid;
|
||||
}
|
||||
|
||||
private static void AppendParameters(StringBuilder builder, StackFrameInfo frame, ExceptionSettings settings)
|
||||
private static void AppendParameters(StringBuilder builder, MethodBase? method, ExceptionSettings settings)
|
||||
{
|
||||
var typeColor = settings.Style.ParameterType.ToMarkup();
|
||||
var nameColor = settings.Style.ParameterName.ToMarkup();
|
||||
var parameters = frame.Parameters.Select(x => $"[{typeColor}]{x.Type.EscapeMarkup()}[/] [{nameColor}]{x.Name.EscapeMarkup()}[/]");
|
||||
builder.Append(string.Join(", ", parameters));
|
||||
var parameters = method?.GetParameters().Select(x => $"[{typeColor}]{x.ParameterType.Name.EscapeMarkup()}[/] [{nameColor}]{x.Name?.EscapeMarkup()}[/]");
|
||||
if (parameters != null)
|
||||
{
|
||||
builder.Append(string.Join(", ", parameters));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendPath(StringBuilder builder, StackFrameInfo frame, ExceptionSettings settings)
|
||||
private static void AppendPath(StringBuilder builder, string path, ExceptionSettings settings)
|
||||
{
|
||||
if (frame?.Path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void AppendPath()
|
||||
{
|
||||
var shortenPaths = (settings.Format & ExceptionFormats.ShortenPaths) != 0;
|
||||
builder.Append(Emphasize(frame.Path, new[] { '/', '\\' }, settings.Style.Path, shortenPaths, settings));
|
||||
builder.Append(Emphasize(path, new[] { '/', '\\' }, settings.Style.Path, shortenPaths, settings));
|
||||
}
|
||||
|
||||
if ((settings.Format & ExceptionFormats.ShowLinks) != 0)
|
||||
{
|
||||
var hasLink = frame.TryGetUri(out var uri);
|
||||
var hasLink = path.TryGetUri(out var uri);
|
||||
if (hasLink && uri != null)
|
||||
{
|
||||
builder.Append("[link=").Append(uri.AbsoluteUri).Append(']');
|
||||
|
@ -1,20 +0,0 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal sealed class ExceptionInfo
|
||||
{
|
||||
public string Type { get; }
|
||||
public string Message { get; }
|
||||
public List<StackFrameInfo> Frames { get; }
|
||||
public ExceptionInfo? Inner { get; }
|
||||
|
||||
public ExceptionInfo(
|
||||
string type, string message,
|
||||
List<StackFrameInfo> frames,
|
||||
ExceptionInfo? inner)
|
||||
{
|
||||
Type = type ?? string.Empty;
|
||||
Message = message ?? string.Empty;
|
||||
Frames = frames ?? new List<StackFrameInfo>();
|
||||
Inner = inner;
|
||||
}
|
||||
}
|
33
src/Spectre.Console/Widgets/Exceptions/MethodExtensions.cs
Normal file
33
src/Spectre.Console/Widgets/Exceptions/MethodExtensions.cs
Normal file
@ -0,0 +1,33 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class MethodExtensions
|
||||
{
|
||||
public static string GetName(this MethodBase? method)
|
||||
{
|
||||
if (method is null)
|
||||
{
|
||||
return "<unknown method>";
|
||||
}
|
||||
|
||||
var builder = new StringBuilder(256);
|
||||
|
||||
var fullName = method.DeclaringType?.FullName;
|
||||
if (fullName != null)
|
||||
{
|
||||
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L247-L253
|
||||
builder.Append(fullName.Replace('+', '.'));
|
||||
builder.Append('.');
|
||||
}
|
||||
|
||||
builder.Append(method.Name);
|
||||
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
builder.Append('[');
|
||||
builder.Append(string.Join(",", method.GetGenericArguments().Select(t => t.Name)));
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal sealed class StackFrameInfo
|
||||
{
|
||||
public string Method { get; }
|
||||
public List<(string Type, string Name)> Parameters { get; }
|
||||
public string? Path { get; }
|
||||
public int? LineNumber { get; }
|
||||
|
||||
public StackFrameInfo(
|
||||
string method, List<(string Type, string Name)> parameters,
|
||||
string? path, int? lineNumber)
|
||||
{
|
||||
Method = method ?? throw new ArgumentNullException(nameof(method));
|
||||
Parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
|
||||
Path = path;
|
||||
LineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public bool TryGetUri([NotNullWhen(true)] out Uri? result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Path == null)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(Path, UriKind.Absolute, out var uri))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.Scheme == "file")
|
||||
{
|
||||
// For local files, we need to append
|
||||
// the host name. Otherwise the terminal
|
||||
// will most probably not allow it.
|
||||
var builder = new UriBuilder(uri)
|
||||
{
|
||||
Host = Dns.GetHostName(),
|
||||
};
|
||||
|
||||
uri = builder.Uri;
|
||||
}
|
||||
|
||||
result = uri;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class StringUriExtensions
|
||||
{
|
||||
public static bool TryGetUri(this string path, [NotNullWhen(true)] out Uri? result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Uri.TryCreate(path, UriKind.Absolute, out var uri))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.Scheme == "file")
|
||||
{
|
||||
// For local files, we need to append
|
||||
// the host name. Otherwise the terminal
|
||||
// will most probably not allow it.
|
||||
var builder = new UriBuilder(uri)
|
||||
{
|
||||
Host = Dns.GetHostName(),
|
||||
};
|
||||
|
||||
uri = builder.Uri;
|
||||
}
|
||||
|
||||
result = uri;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user