Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show properties as well as values when formatting IEnumerable types #3074

Conversation

jonsequitur
Copy link
Contributor

@@ -186,7 +192,17 @@ bool BuildTable(T source, FormatContext context, bool summarize)
}
}

rows.Add(tr(rowValues.Select(r => td(r))));
rows.Add(tr(rowValues.Select(value =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need any change on the TabularDataResource Formatter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of change?

@@ -309,6 +309,7 @@ public static void Format(this ITypeFormatter formatter, object instance, TextWr
public static void FormatTo<T>(
this T obj,
FormatContext context,
// FIX: (FormatTo) make this non-optional
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to remove this before merge?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I suppose I might as well do this now. It's a breaking change that eventually has to happen.

if (@interface.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: Why only IDictionary and IOrderedEnumerable specifically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These interfaces have properties that aren't very interesting, e.g. Count (since we already show it for all sequences.)

return false;
}

if (typeof(ICollection).IsAssignableFrom(type))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why properties are included for types that implement IEnumerable but not ICollection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ICollection is a lot more specific and includes a Count property which is redundant with the existing sequence formatters, which show e.g. (13 more) after hitting the list expansion limit.

return false;
}

if (typeof(ICollection).IsAssignableFrom(type))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a micro-optimization, but considering that we are already looking at implemented interfaces in the loop below, would it be better to add an early out for ICollection there instead? (Taking a quick look at the implementation for IsAssignableFrom it seems to be doing a bunch morethings before checking if an implemented interface IsSubClassOf(typeof(ICollection)) )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving it into the loop means we're paying the cost of GetTypeInfo and then doing an equality check against ICollection for every single interface we find. This isn't to say that I think that's expensive, I just don't have enough information to say whether this would be an optimization or actually be more costly.

@@ -43,13 +42,21 @@ void Initialize()

internal static void FormatAndStyleAsPlainText(
object value,
FormatContext context)
FormatContext context,
string mimeType = PlainTextFormatter.MimeType)
Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit confusing to see the PlainText suffix in the name of the function when the mime type is also specified in this parameter. Could we do something to disambiguate?

(Also applies to TagWithPlainTextStyling() helper below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestions?

"style as plain text" seems reasonable in the context of the HtmlFormatter.

enumerableFormatter.Format(obj, c);
}));
Array.Resize(ref rows, rows.Length + 1);
rows[^1] = enumerableAccessor;
Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: Would it be better to leave rows as an enumerable and use Enumerable.Append here (and call ToArray right before calling BuildTreeView below)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would assume that would be less performant than doing the array operations directly, though it would be far from the most critical piece of code to optimize here.

@@ -121,7 +128,7 @@ void Initialize()

new HtmlFormatter<string>((s, context) =>
{
// If PlainTextPreformat is true, then strings
// If PreformatPlainText is true, then strings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is PreformatPlainText referring to an option that is present inside context?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting this comment. It was outdated and refers to something that no longer exists.

@@ -107,10 +107,14 @@ internal static PlainTextFormatter<T> CreateForAnyEnumerable()

return new((value, context) =>
{
using var a = context.IncrementDepth();
using var b = context.IncrementDepth();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variables (a and b) seem to be unused - I am guessing you added them for testing and intended to remove them before merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are being used because we're using them. 😂 (Sorry, that pun was right there.) In other words, Dispose is being called on them.

The variable names are awful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I often use _ as a variable name for these situations. I suppose _ and __ can work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I missed the using keyword 😄

@@ -8,6 +8,7 @@ namespace Microsoft.DotNet.Interactive.Formatting;

public static class PlainTextSummaryFormatter
{
// FIX: (PlainTextSummaryFormatter) rename this
public const string MimeType = "text/plain+summary";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I am guessing you intend to fix this up before merge. If not, I wonder if it may be better to log an issue to track this...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a bigger change because it affects the VS Code extension. I've opened #3078 to track it.

{
return XElement.Parse(html).ToString();
}
catch (XmlException ex) when (ex.Message.Contains("multiple root elements"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we ever want to run tests on localized / non-ENU setups?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unlikely but if so, it will produce a false negative rather than a false positive, which is good, and it will likely be pretty clear what happened. I did look for a better programmatic way to determine the specific cause of the XmlException but I didn't find anything.

@jonsequitur jonsequitur force-pushed the formatter-fixes-for-IEnumerable-with-properties branch from b8171ac to a42d0dc Compare July 10, 2023 20:52
@jonsequitur jonsequitur enabled auto-merge (rebase) July 10, 2023 21:20
@jonsequitur jonsequitur enabled auto-merge (squash) July 10, 2023 21:21
@jonsequitur jonsequitur merged commit d87e3e1 into dotnet:main Jul 10, 2023
4 checks passed
@colombod colombod added the Area-Formatting Data and object formatting as HTML and plaintext label Aug 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Formatting Data and object formatting as HTML and plaintext
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants