mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-17 17:32:50 +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));
|
throw new ArgumentNullException(nameof(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = ExceptionConverter.Convert(exception);
|
return GetException(exception, settings);
|
||||||
|
|
||||||
return GetException(info, 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[]
|
return new Rows(new IRenderable[]
|
||||||
{
|
{
|
||||||
GetMessage(info, settings),
|
GetMessage(exception, settings),
|
||||||
GetStackFrames(info, settings),
|
GetStackFrames(exception, settings),
|
||||||
}).Expand();
|
}).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 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()}[/]";
|
var message = $"[{settings.Style.Message.ToMarkup()}]{ex.Message.EscapeMarkup()}[/]";
|
||||||
return new Markup(string.Concat(type, ": ", message));
|
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;
|
var styles = settings.Style;
|
||||||
|
|
||||||
@ -46,39 +46,44 @@ internal static class ExceptionFormatter
|
|||||||
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
|
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
|
||||||
|
|
||||||
// Inner
|
// Inner
|
||||||
if (ex.Inner != null)
|
if (ex.InnerException != null)
|
||||||
{
|
{
|
||||||
grid.AddRow(
|
grid.AddRow(
|
||||||
Text.Empty,
|
Text.Empty,
|
||||||
GetException(ex.Inner, settings));
|
GetException(ex.InnerException, settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stack frames
|
// 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();
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
// Method
|
// Method
|
||||||
var shortenMethods = (settings.Format & ExceptionFormats.ShortenMethods) != 0;
|
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, "(");
|
builder.AppendWithStyle(styles.Parenthesis, "(");
|
||||||
AppendParameters(builder, frame, settings);
|
AppendParameters(builder, method, settings);
|
||||||
builder.AppendWithStyle(styles.Parenthesis, ")");
|
builder.AppendWithStyle(styles.Parenthesis, ")");
|
||||||
|
|
||||||
if (frame.Path != null)
|
var path = frame.GetFileName();
|
||||||
|
if (path != null)
|
||||||
{
|
{
|
||||||
builder.Append(' ');
|
builder.Append(' ');
|
||||||
builder.AppendWithStyle(styles.Dimmed, "in");
|
builder.AppendWithStyle(styles.Dimmed, "in");
|
||||||
builder.Append(' ');
|
builder.Append(' ');
|
||||||
|
|
||||||
// Path
|
// Path
|
||||||
AppendPath(builder, frame, settings);
|
AppendPath(builder, path, settings);
|
||||||
|
|
||||||
// Line number
|
// Line number
|
||||||
if (frame.LineNumber != null)
|
var lineNumber = frame.GetFileLineNumber();
|
||||||
|
if (lineNumber != 0)
|
||||||
{
|
{
|
||||||
builder.AppendWithStyle(styles.Dimmed, ":");
|
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;
|
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 typeColor = settings.Style.ParameterType.ToMarkup();
|
||||||
var nameColor = settings.Style.ParameterName.ToMarkup();
|
var nameColor = settings.Style.ParameterName.ToMarkup();
|
||||||
var parameters = frame.Parameters.Select(x => $"[{typeColor}]{x.Type.EscapeMarkup()}[/] [{nameColor}]{x.Name.EscapeMarkup()}[/]");
|
var parameters = method?.GetParameters().Select(x => $"[{typeColor}]{x.ParameterType.Name.EscapeMarkup()}[/] [{nameColor}]{x.Name?.EscapeMarkup()}[/]");
|
||||||
builder.Append(string.Join(", ", parameters));
|
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()
|
void AppendPath()
|
||||||
{
|
{
|
||||||
var shortenPaths = (settings.Format & ExceptionFormats.ShortenPaths) != 0;
|
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)
|
if ((settings.Format & ExceptionFormats.ShowLinks) != 0)
|
||||||
{
|
{
|
||||||
var hasLink = frame.TryGetUri(out var uri);
|
var hasLink = path.TryGetUri(out var uri);
|
||||||
if (hasLink && uri != null)
|
if (hasLink && uri != null)
|
||||||
{
|
{
|
||||||
builder.Append("[link=").Append(uri.AbsoluteUri).Append(']');
|
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