Skip to content

Commit

Permalink
remove unused ability to include internal members in formatter output
Browse files Browse the repository at this point in the history
  • Loading branch information
jonsequitur committed Jan 14, 2023
1 parent 1ae32dc commit 178288f
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 242 deletions.
2 changes: 1 addition & 1 deletion src/Microsoft.DotNet.Interactive.FSharp/FSharpKernel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ type FSharpKernel () as this =
let valueInfos =
script.Value.Fsi.GetBoundValues()
|> List.filter (fun x -> x.Name <> "it") // don't report special variable `it`
|> List.map (fun x -> new KernelValueInfo(x.Name, FormattedValue.FromObject(x.Value, requestValueInfos.MimeType)[0], this.getValueType(x.Name)))
|> List.map (fun x -> new KernelValueInfo(x.Name, FormattedValue.FromObject(x.Value.ReflectionValue, requestValueInfos.MimeType)[0], this.getValueType(x.Name)))
:> IReadOnlyCollection<KernelValueInfo>
context.Publish(new ValueInfosProduced(valueInfos, requestValueInfos))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void It_emits_a_configurable_maximum_number_of_properties()
PlainTextFormatter.MaxProperties = 1;

var writer = new StringWriter();
formatter.Format(new Dummy.ClassWithManyProperties(), writer);
formatter.Format(new ClassWithManyProperties(), writer);

var s = writer.ToString();
s.Should().Be($"ClassWithManyProperties{NewLine} X1: 1{NewLine} ...");
Expand All @@ -119,42 +119,12 @@ public void When_Zero_properties_available_to_choose_just_ToString_is_used()
var formatter = PlainTextFormatter.GetPreferredFormatterFor<ClassWithNoPropertiesAndCustomToString>();

var writer = new StringWriter();
formatter.Format(new Dummy.ClassWithNoPropertiesAndCustomToString(), writer);
formatter.Format(new ClassWithNoPropertiesAndCustomToString(), writer);

var s = writer.ToString();
s.Should().Be($"{typeof(ClassWithNoPropertiesAndCustomToString)} custom ToString value");
}

[Fact]
public void CreateForMembers_emits_the_specified_property_names_and_values_for_a_specific_type()
{
var formatter = PlainTextFormatter<SomethingWithLotsOfProperties>.CreateForMembers(
o => o.DateProperty,
o => o.StringProperty);

var s = new SomethingWithLotsOfProperties
{
DateProperty = DateTime.MinValue,
StringProperty = "howdy"
}.ToDisplayString(formatter);

s.Should().Contain("DateProperty: 0001-01-01 00:00:00Z");
s.Should().Contain("StringProperty: howdy");
s.Should().NotContain("IntProperty");
s.Should().NotContain("BoolProperty");
s.Should().NotContain("UriProperty");
}

[Fact]
public void CreateForMembers_throws_when_an_expression_is_not_a_MemberExpression()
{
var ex = Assert.Throws<ArgumentException>(() => PlainTextFormatter<SomethingWithLotsOfProperties>.CreateForMembers(
o => o.DateProperty.ToShortDateString(),
o => o.StringProperty));

ex.Message.Should().Contain("o => o.DateProperty.ToShortDateString()");
}


[Theory]
[InlineData(typeof(Boolean), "False")]
[InlineData(typeof(Byte), "0")]
Expand Down Expand Up @@ -311,40 +281,7 @@ public void It_expands_fields_of_objects()
output.Should().Contain("DateField: ");
output.Should().Contain("DateProperty: ");
}

[Fact]
public void Output_can_include_internal_fields()
{
var formatter = PlainTextFormatter<Node>.CreateForAnyObject(true);

var node = new Node { Id = "5" };

var output = node.ToDisplayString(formatter);

output.Should().Contain("_id: 5");
}

[Fact]
public void Output_does_not_include_autoproperty_backing_fields()
{
var formatter = PlainTextFormatter<Node>.CreateForAnyObject(true);

var output = new Node().ToDisplayString(formatter);

output.Should().NotContain("<Nodes>k__BackingField");
output.Should().NotContain("<NodesArray>k__BackingField");
}

[Fact]
public void Output_can_include_internal_properties()
{
var formatter = PlainTextFormatter<Node>.CreateForAnyObject(true);

var output = new Node { Id = "6" }.ToDisplayString(formatter);

output.Should().Contain("InternalId: 6");
}


[Fact]
public void Tuple_values_are_formatted_on_one_line_when_all_scalar()
{
Expand Down
125 changes: 2 additions & 123 deletions src/Microsoft.DotNet.Interactive.Formatting/Csv/CsvFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.DotNet.Interactive.Formatting.Csv;

Expand All @@ -16,7 +14,7 @@ public static class CsvFormatter
{
new CsvFormatter<IEnumerable>((seq, context) =>
{
ITypeFormatter formatter = FormattersForAnyEnumerable.GetOrCreateFormatterForType(seq.GetType(), false);
ITypeFormatter formatter = FormattersForAnyEnumerable.GetOrCreateFormatterForType(seq.GetType());
formatter.Format(seq, context);
Expand All @@ -25,7 +23,7 @@ public static class CsvFormatter

new CsvFormatter<object>((obj, context) =>
{
var formatter = FormattersForAnyEnumerable.GetOrCreateFormatterForType(obj.GetType(), false);
var formatter = FormattersForAnyEnumerable.GetOrCreateFormatterForType(obj.GetType());
formatter.Format(obj, context);
Expand Down Expand Up @@ -70,123 +68,4 @@ bool ShouldBeWrappedInQuotes()
return false;
}
}
}

public class CsvFormatter<T> : TypeFormatter<T>
{
private readonly FormatDelegate<T> _format;

public CsvFormatter(FormatDelegate<T> format)
{
_format = format;
}

public override bool Format(T value, FormatContext context) => _format(value, context);

public override string MimeType => CsvFormatter.MimeType;

internal static CsvFormatter<T> Create(bool includeInternals)
{
Func<T, IEnumerable> getHeaderValues = null;

Func<T, IEnumerable> getRowValues;

IDestructurer destructurer;
if (typeof(T).IsEnumerable())
{
destructurer = Destructurer.GetOrCreate(typeof(T).GetElementTypeIfEnumerable());
getRowValues = instance => (IEnumerable)instance;
}
else
{
destructurer = Destructurer<T>.GetOrCreate();
getRowValues = instance => new[] { instance };
}

var isDictionary =
typeof(T).GetAllInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)) is not null
||
typeof(T).GetAllInterfaces()
.FirstOrDefault(i => i == typeof(IDictionary)) is not null;

if (isDictionary)
{
getHeaderValues = instance => ((IDictionary)instance).Keys;
getRowValues = instance => new[] { instance };
}
else
{
getHeaderValues = _ => destructurer.Keys;
}

return new CsvFormatter<T>( (v,c) => BuildTable(v, getHeaderValues, getRowValues, c));
}

internal static bool BuildTable(T value, Func<T, IEnumerable> getHeaderValues, Func<T, IEnumerable> getRowValues, FormatContext context)
{
var keys = getHeaderValues(value);

if (keys is not ICollection headers)
{
return false;
}

var headerIndex = 0;

foreach (var header in headers)
{
context.Writer.Write(header);

if (++headerIndex < headers.Count)
{
context.Writer.Write(",");
}
}

context.Writer.WriteLine();

IDestructurer rowDestructurer = null;

var rows = getRowValues(value);

foreach (var row in rows)
{
var rowIndex = 0;

if (row is not IEnumerable cells)
{
rowDestructurer ??= Destructurer.GetOrCreate(row.GetType());

cells = rowDestructurer.Destructure(row).Values;
}
else if (row is IDictionary d)
{
cells = d.Values;
}

foreach (var cell in cells)
{
var formatted = cell switch
{
DateTime d => d.ToString("o"),
null => "",
_ => cell.ToString()
};

context.Writer.Write(formatted.EscapeCsvValue());

if (++rowIndex < headers.Count)
{
context.Writer.Write(",");
}
}

context.Writer.WriteLine();
}

return true;
}


}
128 changes: 128 additions & 0 deletions src/Microsoft.DotNet.Interactive.Formatting/Csv/CsvFormatter{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.DotNet.Interactive.Formatting.Csv;

public class CsvFormatter<T> : TypeFormatter<T>
{
private readonly FormatDelegate<T> _format;

public CsvFormatter(FormatDelegate<T> format)
{
_format = format;
}

public override bool Format(T value, FormatContext context) => _format(value, context);

public override string MimeType => CsvFormatter.MimeType;

internal static CsvFormatter<T> Create()
{
Func<T, IEnumerable> getHeaderValues = null;

Func<T, IEnumerable> getRowValues;

IDestructurer destructurer;
if (typeof(T).IsEnumerable())
{
destructurer = Destructurer.GetOrCreate(typeof(T).GetElementTypeIfEnumerable());
getRowValues = instance => (IEnumerable)instance;
}
else
{
destructurer = Destructurer<T>.GetOrCreate();
getRowValues = instance => new[] { instance };
}

var isDictionary =
typeof(T).GetAllInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)) is not null
||
typeof(T).GetAllInterfaces()
.FirstOrDefault(i => i == typeof(IDictionary)) is not null;

if (isDictionary)
{
getHeaderValues = instance => ((IDictionary)instance).Keys;
getRowValues = instance => new[] { instance };
}
else
{
getHeaderValues = _ => destructurer.Keys;
}

return new CsvFormatter<T>( (v,c) => BuildTable(v, getHeaderValues, getRowValues, c));
}

internal static bool BuildTable(T value, Func<T, IEnumerable> getHeaderValues, Func<T, IEnumerable> getRowValues, FormatContext context)
{
var keys = getHeaderValues(value);

if (keys is not ICollection headers)
{
return false;
}

var headerIndex = 0;

foreach (var header in headers)
{
context.Writer.Write(header);

if (++headerIndex < headers.Count)
{
context.Writer.Write(",");
}
}

context.Writer.WriteLine();

IDestructurer rowDestructurer = null;

var rows = getRowValues(value);

foreach (var row in rows)
{
var rowIndex = 0;

if (row is not IEnumerable cells)
{
rowDestructurer ??= Destructurer.GetOrCreate(row.GetType());

cells = rowDestructurer.Destructure(row).Values;
}
else if (row is IDictionary d)
{
cells = d.Values;
}

foreach (var cell in cells)
{
var formatted = cell switch
{
DateTime d => d.ToString("o"),
null => "",
_ => cell.ToString()
};

context.Writer.Write(formatted.EscapeCsvValue());

if (++rowIndex < headers.Count)
{
context.Writer.Write(",");
}
}

context.Writer.WriteLine();
}

return true;
}


}
Loading

0 comments on commit 178288f

Please sign in to comment.