Skip to content

Normalize line endings in one pass, so Mac keeps every break - #141

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/normalize-line-endings-single-pass
Sep 28, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/normalize-line-endings-single-pass

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #137

What changed

NormalizeLineEndings(Mac) ran two regex passes. The first turned each CRLF into CR. That left any LF that came right after a CRLF sitting directly after the new CR. The second pass's (?<!\r)\n lookbehind then skipped that LF. So "a\r\n\nb" became "a\r\nb": one line break was lost, and the Mac output contained a Windows ending.

Every style now does a single \r\n|\r|\n replacement (AnyLineEndingRegex), so no intermediate string can form a new sequence. DetermineLineEndings still uses the three existing regexes. Output only changes for inputs that hit the bug. The existing NormalizeLineEndingsTo* tests pass unchanged.

Tests

  • NormalizeLineEndingsKeepsEveryBreakOfAdjacentMixedEndings covers CRLF followed by LF, and CR followed by CRLF, for every LineEndingStyle.
  • NormalizeLineEndingsOfAdjacentMixedEndingsIsDetectedAsTheTargetStyle checks that Unix, Windows and Mac output is detected as that style.
  • With the library change reverted, the Mac cases of both tests fail. With the change, dotnet test Extensions.Test passes 155/155.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LasidbyRZfyxgW8Z9wQu9M


Generated by Claude Code

NormalizeLineEndings(Mac) first turned each CRLF into CR, which left an
LF that followed a CRLF directly after the new CR. The LF pass skips an
LF preceded by CR, so "a\r\n\nb" came out as "a\r\nb": a line break was
lost and the Mac output held a Windows ending.

Every style now replaces \r\n, \r or \n in a single regex pass, so no
intermediate string can form a new sequence. The results for inputs
that did not hit the bug are unchanged.

Fixes #137

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LasidbyRZfyxgW8Z9wQu9M
SonarCloud flags a Regex built without a timeout (S6444), which failed
the quality gate on its security rating. The pattern is a plain
alternation that cannot backtrack, so one second is far more than any
match needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LasidbyRZfyxgW8Z9wQu9M
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NormalizeLineEndings(Mac) leaves a CRLF in the output and drops a line break when a CRLF is followed by an LF

2 participants