diff --git a/CodeBlocker.Test/CodeBlockerExtensionsTests.cs b/CodeBlocker.Test/CodeBlockerExtensionsTests.cs
index 0a14c3d..b6bfae1 100644
--- a/CodeBlocker.Test/CodeBlockerExtensionsTests.cs
+++ b/CodeBlocker.Test/CodeBlockerExtensionsTests.cs
@@ -65,6 +65,32 @@ public void AutoGeneratedHeaderWritesABlankCopyrightLineAsABareComment()
codeBlocker.ToString());
}
+ [TestMethod]
+ [DataRow("\n", DisplayName = "LF")]
+ [DataRow("\r\n", DisplayName = "CRLF")]
+ [DataRow("\r", DisplayName = "CR")]
+ [DataRow("\u0085", DisplayName = "NEL")]
+ [DataRow("\u2028", DisplayName = "LINE SEPARATOR")]
+ [DataRow("\u2029", DisplayName = "PARAGRAPH SEPARATOR")]
+ public void AutoGeneratedHeaderIgnoresATrailingLineTerminatorOnTheCopyright(string terminator)
+ {
+ using CodeBlocker codeBlocker = Create();
+
+ codeBlocker.WriteAutoGeneratedHeader($"Copyright (c) X{terminator}");
+
+ Assert.AreEqual("// Copyright (c) X\n// \n\n", codeBlocker.ToString());
+ }
+
+ [TestMethod]
+ public void AutoGeneratedHeaderKeepsABlankLineBeforeATrailingLineTerminator()
+ {
+ using CodeBlocker codeBlocker = Create();
+
+ codeBlocker.WriteAutoGeneratedHeader("A\n\nB\n");
+
+ Assert.AreEqual("// A\n//\n// B\n// \n\n", codeBlocker.ToString());
+ }
+
[TestMethod]
public void NullableDirectivesAreWrittenVerbatim()
{
diff --git a/CodeBlocker.Test/DocCommentTests.cs b/CodeBlocker.Test/DocCommentTests.cs
index f802433..67d85a2 100644
--- a/CodeBlocker.Test/DocCommentTests.cs
+++ b/CodeBlocker.Test/DocCommentTests.cs
@@ -56,6 +56,30 @@ public void AMultiLineSummaryPrefixesEveryLine()
Render(documentation));
}
+ [TestMethod]
+ [DataRow("\n", DisplayName = "LF")]
+ [DataRow("\r\n", DisplayName = "CRLF")]
+ [DataRow("\u2028", DisplayName = "LINE SEPARATOR")]
+ public void ASingleLineSummaryWithATrailingLineTerminatorStaysOnOneLine(string terminator) =>
+ Assert.AreEqual(
+ "/// Hello.\n",
+ Render(new DocComment { Summary = $"Hello.{terminator}" }));
+
+ [TestMethod]
+ public void AMultiLineSummaryWithATrailingLineTerminatorHasNoEmptyLine() =>
+ Assert.AreEqual(
+ "/// \n/// A\n/// B\n/// \n",
+ Render(new DocComment { Summary = "A\nB\n" }));
+
+ [TestMethod]
+ public void ANamedTagWithATrailingLineTerminatorStaysOnOneLine()
+ {
+ DocComment documentation = new();
+ documentation.Params.Add(new DocTag { Name = "a", Text = "The value.\n" });
+
+ Assert.AreEqual("/// The value.\n", Render(documentation));
+ }
+
[TestMethod]
public void ABlankLineInsideATagIsWrittenWithoutTrailingWhitespace()
{
diff --git a/CodeBlocker/LineTerminators.cs b/CodeBlocker/LineTerminators.cs
index d7b8fb3..68a376d 100644
--- a/CodeBlocker/LineTerminators.cs
+++ b/CodeBlocker/LineTerminators.cs
@@ -23,9 +23,21 @@ internal static class LineTerminators
/// the comment and into the code.
///
/// The text to split.
- /// The lines, without their terminators. Text with no terminator is a single line.
- internal static string[] Split(string text) =>
- text.Replace("\r\n", "\n").Split(Characters);
+ ///
+ /// The lines, without their terminators. Text with no terminator is a single line. A trailing
+ /// terminator leaves an empty tail, which is not a line of its own, so "A\n" is the one
+ /// line A; a blank line inside the text, as in "A\n\nB", is kept.
+ ///
+ internal static string[] Split(string text)
+ {
+ string[] lines = text.Replace("\r\n", "\n").Split(Characters);
+ if (lines.Length > 1 && lines[^1].Length == 0)
+ {
+ Array.Resize(ref lines, lines.Length - 1);
+ }
+
+ return lines;
+ }
///
/// Reports whether text contains any line terminator the C# compiler recognises.