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
28 changes: 28 additions & 0 deletions Extensions.Test/StringExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,34 @@ public void NormalizeLineEndingsToNone()
Assert.AreEqual(expected, result);
}

// A CRLF followed by an LF, and a CR followed by a CRLF, are two line breaks each. Normalizing in
// several passes used to merge the first into a new CRLF under Mac, losing a break and leaving a
// Windows ending in Mac output.

[TestMethod]
[DataRow("a\r\n\nb", LineEndingStyle.Unix, "a\n\nb")]
[DataRow("a\r\n\nb", LineEndingStyle.Windows, "a\r\n\r\nb")]
[DataRow("a\r\n\nb", LineEndingStyle.Mac, "a\r\rb")]
[DataRow("a\r\n\nb", LineEndingStyle.None, "ab")]
[DataRow("a\r\n\nb", LineEndingStyle.Mixed, "a\n\nb")]
[DataRow("a\r\r\nb", LineEndingStyle.Unix, "a\n\nb")]
[DataRow("a\r\r\nb", LineEndingStyle.Windows, "a\r\n\r\nb")]
[DataRow("a\r\r\nb", LineEndingStyle.Mac, "a\r\rb")]
[DataRow("a\r\r\nb", LineEndingStyle.None, "ab")]
[DataRow("a\r\r\nb", LineEndingStyle.Mixed, "a\n\nb")]
public void NormalizeLineEndingsKeepsEveryBreakOfAdjacentMixedEndings(string input, LineEndingStyle style, string expected) =>
Assert.AreEqual(expected, input.NormalizeLineEndings(style));

[TestMethod]
[DataRow(LineEndingStyle.Unix)]
[DataRow(LineEndingStyle.Windows)]
[DataRow(LineEndingStyle.Mac)]
public void NormalizeLineEndingsOfAdjacentMixedEndingsIsDetectedAsTheTargetStyle(LineEndingStyle style)
{
Assert.AreEqual(style, "a\r\n\nb".NormalizeLineEndings(style).DetermineLineEndings());
Assert.AreEqual(style, "a\r\r\nb".NormalizeLineEndings(style).DetermineLineEndings());
}

[TestMethod]
public void NormalizeLineEndingsToMixed()
{
Expand Down
14 changes: 9 additions & 5 deletions Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,14 @@
#endif
}

private static Regex LineEndingRegexUnix { get; } = new(@"(?<!\r)\n", RegexOptions.Compiled);

Check warning on line 125 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.

Check warning on line 125 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.
private static Regex LineEndingRegexWindows { get; } = new(@"\r\n", RegexOptions.Compiled);

Check warning on line 126 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.

Check warning on line 126 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.
private static Regex LineEndingRegexMac { get; } = new(@"\r(?!\n)", RegexOptions.Compiled);

Check warning on line 127 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.

Check warning on line 127 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Pass a timeout to limit the execution time.

// One pass over every kind of line ending. Normalizing in several passes lets an earlier pass
// build a sequence the next one reads differently: under Mac, "\r\n\n" became "\r" + "\n", a new CRLF.
private static Regex AnyLineEndingRegex { get; } = new(@"\r\n|\r|\n", RegexOptions.Compiled, TimeSpan.FromSeconds(1));

/// <summary>
/// Determines the line ending style of the specified string.
/// </summary>
Expand Down Expand Up @@ -202,11 +206,11 @@

return style switch
{
LineEndingStyle.None => LineEndingRegexUnix.Replace(LineEndingRegexWindows.Replace(LineEndingRegexMac.Replace(s, ""), ""), ""),
LineEndingStyle.Unix => LineEndingRegexWindows.Replace(LineEndingRegexMac.Replace(s, "\n"), "\n"),
LineEndingStyle.Windows => LineEndingRegexUnix.Replace(LineEndingRegexMac.Replace(s, "\r\n"), "\r\n"),
LineEndingStyle.Mac => LineEndingRegexUnix.Replace(LineEndingRegexWindows.Replace(s, "\r"), "\r"),
LineEndingStyle.Mixed => LineEndingRegexWindows.Replace(LineEndingRegexMac.Replace(s, "\n"), "\n"),
LineEndingStyle.None => AnyLineEndingRegex.Replace(s, ""),
LineEndingStyle.Unix => AnyLineEndingRegex.Replace(s, "\n"),
LineEndingStyle.Windows => AnyLineEndingRegex.Replace(s, "\r\n"),
LineEndingStyle.Mac => AnyLineEndingRegex.Replace(s, "\r"),
LineEndingStyle.Mixed => AnyLineEndingRegex.Replace(s, "\n"),
_ => throw new NotImplementedException("Unknown line ending style."),
};
}
Expand Down Expand Up @@ -255,7 +259,7 @@
/// </summary>
private const char SoftHyphen = '颅';

private static IEnumerable<string> NominalWordWrapIterator(string text, int maxCharsPerLine)

Check warning on line 262 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

Check warning on line 262 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.
{
string normalized = text.NormalizeLineEndings(LineEndingStyle.Unix);
string[] segments = normalized.Split('\n');
Expand Down Expand Up @@ -288,7 +292,7 @@

yield return line.ToString();
line.Clear();
atLineStart = true;

Check warning on line 295 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Remove this useless assignment to local variable 'atLineStart'.

Check warning on line 295 in Extensions/StringExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Remove this useless assignment to local variable 'atLineStart'.
}
else if (!atLineStart)
{
Expand Down
Loading