Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions CodeBlocker.Test/TemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,67 @@ public void ABodyThatWritesNothingRendersAsAnEmptyBlock()
Assert.AreEqual("public virtual void Run() { }\n", Render(method));
}

[TestMethod]
[DataRow(1)]
[DataRow(2)]
public void ABodyThatWritesOnlyBlankLinesRendersAsAnEmptyBlock(int newLines)
{
// A NewLine after a loop over an empty collection used to leave "public void M() " with no
// body, which does not compile (CS0501).
MethodTemplate method = new()
{
Type = "void",
Name = "M",
Keywords = { "public" },
BodyFactory = codeBlocker =>
{
for (int i = 0; i < newLines; i++)
{
codeBlocker.NewLine();
}
},
};

Assert.AreEqual("public void M() { }\n", Render(method));
}

[TestMethod]
public void AnExpressionBodyBetweenBlankLinesStaysOnTheDeclarationLine() =>
Assert.AreEqual(
"public void Reset() => count = 0;\n",
Render(new MethodTemplate
{
Type = "void",
Name = "Reset",
Keywords = { "public" },
BodyFactory = codeBlocker =>
{
codeBlocker.NewLine();
codeBlocker.WriteLine("=> count = 0;");
codeBlocker.NewLine();
},
}));

[TestMethod]
public void ABlankLineInsideABracedBodyIsKept()
{
MethodTemplate method = new()
{
Type = "void",
Name = "Run",
Keywords = { "public" },
BodyFactory = codeBlocker =>
{
using Scope scope = new(codeBlocker);
codeBlocker.WriteLine("First();");
codeBlocker.NewLine();
codeBlocker.WriteLine("Second();");
},
};

Assert.AreEqual("public void Run()\n{\n\tFirst();\n\n\tSecond();\n}\n", Render(method));
}

[TestMethod]
public void AMultiLineBodyIsIndentedToWhereItIsSpliced()
{
Expand Down Expand Up @@ -568,6 +629,25 @@ public void AConstructorWithNoBaseCallIsAnEmptyBlock() =>
"public Widget() { }\n",
Render(new ConstructorTemplate { Name = "Widget", Keywords = { "public" } }));

[TestMethod]
[DataRow(1)]
[DataRow(2)]
public void AConstructorBodyThatWritesOnlyBlankLinesIsAnEmptyBlock(int newLines) =>
Assert.AreEqual(
"public Widget() { }\n",
Render(new ConstructorTemplate
{
Name = "Widget",
Keywords = { "public" },
BodyFactory = codeBlocker =>
{
for (int i = 0; i < newLines; i++)
{
codeBlocker.NewLine();
}
},
}));

[TestMethod]
public void AConstructorInitialiserIsIndentedOnItsOwnLine()
{
Expand Down
11 changes: 8 additions & 3 deletions CodeBlocker/Templates/TemplateRendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,19 +223,24 @@ internal static void WriteBody(CodeBlocker codeBlocker, Action<CodeBlocker>? bod
string body = RenderFragment(codeBlocker, bodyFactory);
string[] lines = SplitLines(codeBlocker, body);

// Blank lines carry no body of their own, so they are left out when deciding its shape. A
// factory that only wrote spacing, such as a NewLine after a loop over an empty collection,
// would otherwise leave the declaration with no body at all.
string[] contentLines = [.. lines.Where(line => line.Length != 0)];

// A factory that wrote nothing means "declared, but empty" — a virtual base method, or a
// constructor that only forwards to its base.
if (lines.Length == 0)
if (contentLines.Length == 0)
{
codeBlocker.WriteLine(" { }");
return;
}

if (lines.Length == 1)
if (contentLines.Length == 1)
{
// An expression body stays on the declaration line.
codeBlocker.Write(" ");
codeBlocker.WriteLine(lines[0]);
codeBlocker.WriteLine(contentLines[0]);
return;
}

Expand Down
Loading