Skip to content

Commit

Permalink
Reworked formatting and support multi-dimensional arrays.
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisdoomen committed Mar 2, 2025
1 parent 669f817 commit e9357fb
Show file tree
Hide file tree
Showing 20 changed files with 699 additions and 307 deletions.
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ dotnet_diagnostic.IDE0055.severity = error
dotnet_diagnostic.CS1574.severity = error

# StyleCop

# Purpose: An opening square bracket within a C# statement is not spaced correctly.
# Reason: Doesn't understand the new collection initializers
dotnet_diagnostic.SA1010.severity = none

# SA1028: Code should not contain trailing whitespace
dotnet_diagnostic.SA1028.severity = suggestion
# SA1101: Prefix local calls with this
Expand Down
5 changes: 5 additions & 0 deletions Build/_build.v3.ncrunchproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>
8 changes: 8 additions & 0 deletions FluentAssertions.v3.ncrunchsolution
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<EnableRDI>False</EnableRDI>
<RdiConfigured>True</RdiConfigured>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>
88 changes: 88 additions & 0 deletions Src/FluentAssertions/Formatting/Anchor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace FluentAssertions.Formatting;

/// <summary>
/// Write fragments that may be on a single line or span multiple lines,
/// and this is not known until later parts of the fragment are written.
/// </summary>
internal class Anchor
{
private readonly FormattedObjectGraph parent;
private readonly int indentation;
private readonly int characterIndex;
private readonly Line line;
private readonly bool lineWasEmptyAtCreation;

public Anchor(FormattedObjectGraph parent, Line line)
{
indentation = parent.Indentation;
this.parent = parent;
this.line = line;
lineWasEmptyAtCreation = line is null || line.Length == 0;

// Track the point in the graph where this instance was created.
characterIndex = line?.LengthWithoutOffset ?? 0;
}

public bool UseLineBreaks { get; set; }

public void InsertFragment(string fragment)
{
// 3. If there are no lines following the current, insert at the character index
// 4. If there are more lines following the current, insert at the character index and add a new line
line.Insert(characterIndex, fragment);

if (!RenderOnSingleLine)
{
// Split the line at the character index
// REFACTOR: Don't even split if there's nothing beyond the character index (other than whitespace)
parent.SplitLine(line, characterIndex + fragment.Length);
}
}

public void InsertLineOrFragment(string fragment)
{
if (RenderOnSingleLine)
{
if (line is null)
{
parent.InsertAtTop(fragment);
}
else
{
line.Insert(characterIndex, fragment);
}
}
else
{
// FIX: We sometimes need to insert the new line before the current line.

// Insert a new line in-between the current line and the next line and put the fragment there.
if (lineWasEmptyAtCreation)
{
parent.InsertLineBefore(line, FormattedObjectGraph.MakeWhitespace(indentation) + fragment);
}
else
{
parent.AddLineAfter(line, FormattedObjectGraph.MakeWhitespace(indentation) + fragment);
}
}
}

internal void AddLineOrFragment(string fragment)
{
if (line is null)
{
parent.AddLineOrFragment(fragment);
}
else if (RenderOnSingleLine)
{
line.Append(fragment);
}
else
{
parent.AddLine(fragment);
}
}

private bool RenderOnSingleLine => !UseLineBreaks && !parent.HasLinesBeyond(line);
}
7 changes: 4 additions & 3 deletions Src/FluentAssertions/Formatting/DefaultValueFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ private void WriteTypeAndMemberValues(object obj, FormattedObjectGraph formatted

private void WriteTypeName(FormattedObjectGraph formattedGraph, Type type)
{
var typeName = type.HasFriendlyName() ? TypeDisplayName(type) : string.Empty;
formattedGraph.AddFragment(typeName);
if (type.HasFriendlyName())
{
formattedGraph.AddFragment(TypeDisplayName(type));
}
}

private void WriteTypeValue(object obj, FormattedObjectGraph formattedGraph, FormatChild formatChild, Type type)
Expand All @@ -89,7 +91,6 @@ private void WriteTypeValue(object obj, FormattedObjectGraph formattedGraph, For
}
else
{
formattedGraph.EnsureInitialNewLine();
formattedGraph.AddLine("{");
WriteMemberValues(obj, members, formattedGraph, formatChild);
formattedGraph.AddFragmentOnNewLine("}");
Expand Down
21 changes: 11 additions & 10 deletions Src/FluentAssertions/Formatting/EnumerableValueFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public void Format(object value, FormattedObjectGraph formattedGraph, Formatting

using var iterator = new Iterator<object>(collection, MaxItems);

var iteratorGraph = formattedGraph.KeepOnSingleLineAsLongAsPossible();
FormattedObjectGraph.PossibleMultilineFragment separatingCommaGraph = null;
var startingAnchor = formattedGraph.GetAnchor();
startingAnchor.UseLineBreaks = context.UseLineBreaks;
Anchor commaSeparatorAnchor = null;

while (iterator.MoveNext())
{
Expand All @@ -46,26 +47,26 @@ public void Format(object value, FormattedObjectGraph formattedGraph, Formatting
{
using IDisposable _ = formattedGraph.WithIndentation();
string moreItemsMessage = value is ICollection c ? $"…{c.Count - MaxItems} more…" : "…more…";
iteratorGraph.AddLineOrFragment(moreItemsMessage);
formattedGraph.AddLineOrFragment(moreItemsMessage);
}

separatingCommaGraph?.InsertLineOrFragment(", ");
separatingCommaGraph = formattedGraph.KeepOnSingleLineAsLongAsPossible();
commaSeparatorAnchor?.InsertFragment(", ");
commaSeparatorAnchor = formattedGraph.GetAnchor();

// We cannot know whether or not the enumerable will take up more than one line of
// output until we have formatted the first item. So we format the first item, then
// We cannot know whether the enumerable will take up more than one line of
// output until we have formatted all items. So we format items, then
// go back and insert the enumerable's opening brace in the correct place depending
// on whether that first item was all on one line or not.
if (iterator.IsLast)
{
iteratorGraph.AddStartingLineOrFragment("{");
iteratorGraph.AddLineOrFragment("}");
startingAnchor.InsertLineOrFragment("{");
startingAnchor.AddLineOrFragment("}");
}
}

if (iterator.IsEmpty)
{
iteratorGraph.AddFragment("{empty}");
formattedGraph.AddFragment("{empty}");
}
}
}
Loading

0 comments on commit e9357fb

Please sign in to comment.