mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-15 00:12:50 +08:00
Making tuples pretty too
This commit is contained in:
parent
e8eb5b85b9
commit
74a2e10ff0
@ -276,16 +276,85 @@ internal static class ExceptionFormatter
|
||||
var prefix = GetPrefix(parameter);
|
||||
var parameterType = parameter.ParameterType;
|
||||
|
||||
if (parameterType.IsByRef && parameterType.GetElementType() is { } elementType)
|
||||
string typeName;
|
||||
if (parameterType.IsGenericType && TryGetTupleName(parameter, parameterType, out var s))
|
||||
{
|
||||
parameterType = elementType;
|
||||
typeName = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parameterType.IsByRef && parameterType.GetElementType() is { } elementType)
|
||||
{
|
||||
parameterType = elementType;
|
||||
}
|
||||
|
||||
typeName = TypeNameHelper.GetTypeDisplayName(parameterType);
|
||||
}
|
||||
|
||||
var typeName = TypeNameHelper.GetTypeDisplayName(parameterType, false, true);
|
||||
|
||||
return string.IsNullOrWhiteSpace(prefix) ? typeName : $"{prefix} {typeName}";
|
||||
}
|
||||
|
||||
private static bool TryGetTupleName(ParameterInfo parameter, Type parameterType, [NotNullWhen(true)] out string? tupleName)
|
||||
{
|
||||
var customAttribs = parameter.GetCustomAttributes(inherit: false);
|
||||
|
||||
var tupleNameAttribute = customAttribs
|
||||
.OfType<Attribute>()
|
||||
.FirstOrDefault(a =>
|
||||
{
|
||||
var attributeType = a.GetType();
|
||||
return attributeType.Namespace == "System.Runtime.CompilerServices" &&
|
||||
attributeType.Name == "TupleElementNamesAttribute";
|
||||
});
|
||||
|
||||
if (tupleNameAttribute != null)
|
||||
{
|
||||
var propertyInfo = tupleNameAttribute.GetType()
|
||||
.GetProperty("TransformNames", BindingFlags.Instance | BindingFlags.Public)!;
|
||||
var tupleNames = propertyInfo.GetValue(tupleNameAttribute) as IList<string>;
|
||||
if (tupleNames?.Count > 0)
|
||||
{
|
||||
var args = parameterType.GetGenericArguments();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append('(');
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Append(TypeNameHelper.GetTypeDisplayName(args[i]));
|
||||
|
||||
if (i >= tupleNames.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var argName = tupleNames[i];
|
||||
|
||||
sb.Append(' ');
|
||||
sb.Append(argName);
|
||||
}
|
||||
|
||||
sb.Append(')');
|
||||
|
||||
tupleName = sb.ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (parameterType.Namespace == "System" && parameterType.Name.Contains("ValueTuple`"))
|
||||
{
|
||||
var args = parameterType.GetGenericArguments().Select(i => TypeNameHelper.GetTypeDisplayName(i));
|
||||
tupleName = $"({string.Join(", ", args)})";
|
||||
return true;
|
||||
}
|
||||
|
||||
tupleName = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetMethodName(ref MethodBase method, out bool isAsync)
|
||||
{
|
||||
var declaringType = method.DeclaringType;
|
||||
|
@ -40,35 +40,13 @@ internal static class TypeNameHelper
|
||||
/// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
|
||||
/// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
|
||||
/// <returns>The pretty printed type name.</returns>
|
||||
public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false)
|
||||
public static string GetTypeDisplayName(Type type, bool fullName = false, bool includeGenericParameterNames = true)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true,
|
||||
bool includeGenericParameterNames = false)
|
||||
{
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a name of given generic type without '`'.
|
||||
/// </summary>
|
||||
public static string GetTypeNameForGenericType(Type type)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
throw new ArgumentException("The given type should be generic", nameof(type));
|
||||
}
|
||||
|
||||
var genericPartIndex = type.Name.IndexOf('`');
|
||||
|
||||
return (genericPartIndex >= 0) ? type.Name.Substring(0, genericPartIndex) : type.Name;
|
||||
}
|
||||
|
||||
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
|
@ -35,4 +35,10 @@ public static class TestExceptions
|
||||
firstFewItems = new List<T>();
|
||||
throw new InvalidOperationException("Throwing!");
|
||||
}
|
||||
|
||||
public static (string Key, List<T> Values) GetTuplesWithInnerException<T>((int First, string Second) myValue)
|
||||
{
|
||||
MethodThatThrows(0);
|
||||
return ("key", new List<T>());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
InvalidOperationException: Throwing!
|
||||
at bool Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(int? number) in /xyz/Exceptions.cs:nn
|
||||
at (string Key, List<T> Values) Spectre.Console.Tests.Data.TestExceptions.GetTuplesWithInnerException<T>((int First, string Second) myValue) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Tuple_Return>b__6_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
@ -94,6 +94,21 @@ public sealed class ExceptionTests
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Tuple")]
|
||||
public Task Should_Write_Exception_With_Tuple_Return()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole().Width(1024);
|
||||
var dex = GetException(() => TestExceptions.GetTuplesWithInnerException<int>((0, "value")));
|
||||
|
||||
// When
|
||||
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenTypes);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
|
||||
public static Exception GetException(Action action)
|
||||
{
|
||||
try
|
||||
|
Loading…
x
Reference in New Issue
Block a user