Repository navigation
Keep line breaks inside literals when formatting - #9116
Conversation
Changelog🐛 Bug fix
Semantic Diff1 commit · Full page ✏️ Write the formatter's own line breaks with the file's newline9de0dae · cknitt + Claude · Oct 28, 2026, 15:42 UTC · edited
The source is now parsed as is, and the printer writes the line breaks it emits with the newline of the source (CRLF when the first line break is CRLF), while literals keep their bytes. bsc -format, res_parser -print res and the editor's format command do the same. In the examples below, Format modified (let x = 1\r\nlet s = "a\nb"\r\n)
-let x = 1\r\nlet s = "a\r\nb"\r\n
+let x = 1\r\nlet s = "a\nb"\r\nFormat modified (let x = 1\nlet s = "a\r\nb"\n)
-let x = 1\nlet s = "a\nb"\n
+let x = 1\nlet s = "a\r\nb"\nFormat modified (let x = 1\n/* a\r\r\nb */\n)
-let x = 1\n/* a\r\n b */\n
let x = 1\n/* a\n b */\nReview
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9116 +/- ##
=======================================
Coverage 81.46% 81.47%
=======================================
Files 460 460
Lines 64675 64680 +5
=======================================
+ Hits 52689 52696 +7
+ Misses 11986 11984 -2
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9116 |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Why:
Since #9042, formatting converted the whole source to LF and the output
back to CRLF, string literals included, so `bsc -format` changed the
program when a string held a raw line break of the other kind. Each
conversion also dropped only one CR before an LF, so a comment with
`\r\r\n` changed on every run (#9100).
The source is now parsed as is, and the printer writes the line breaks it
emits with the newline of the source (CRLF when the first line break is
CRLF), while literals keep their bytes. bsc -format, res_parser -print res
and the editor's format command do the same. In the examples below, `\r`
and `\n` stand for CR and LF, the heading holds the input, and each version
is what `bsc -format` prints for it.
Format modified (let x = 1\r\nlet s = "a\nb"\r\n):
Before (text): let x = 1\r\nlet s = "a\r\nb"\r\n
After (text): let x = 1\r\nlet s = "a\nb"\r\n
Format modified (let x = 1\nlet s = "a\r\nb"\n):
Before (text): let x = 1\nlet s = "a\nb"\n
After (text): let x = 1\nlet s = "a\r\nb"\n
Format modified (let x = 1\n/* a\r\r\nb */\n):
Before (text): first run, then second run
let x = 1\n/* a\r\n b */\n
let x = 1\n/* a\n b */\n
After (text): first and second run
let x = 1\n/* a\n b */\n
Review: Why does the comment lose its CRs? The printer reindents a
multi-line `/* … */` comment and writes that comment's line breaks
itself, as it already did for the lines after the first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP
Signed-off-by: Christoph Knittel <[email protected]>
b32d878 to
9de0dae
Compare
Why:
Some Windows editors write a byte order mark, and bsc and bsc -format
rejected it byte by byte. A CR without an LF ended a `//` comment but
did not start a new line, so the next statement counted as on the same
line. The scanner now starts after a byte order mark and ends a line
at a CR not followed by LF, as editors and JavaScript do; code frames
show lines the same way, and the formatter keeps the byte order mark.
Diagnostic removed (byte order mark):
Example:
\xEF\xBB\xBFlet x = 1
Before (diagnostic):
Syntax error!
A.res:1:1
Not sure what to do with this character: "\239".
… the same for "\187" and "\191"
After (diagnostic): none
Diagnostic removed (lone CR):
Example:
let x = 1 // c\rlet y = 2
Before (diagnostic):
Syntax error!
B.res:1:10-18
Consecutive statements on a line must be separated by ';' or a newline
After (diagnostic): none
Format modified (lone CR):
Example:
let x = 1 // c\rlet y = 2
Before (text): Syntax error
After (rescript):
let x = 1 // c
let y = 2
Review: Why convert a CR-only file to LF when formatting? The formatter
writes CRLF when the first line break is CRLF and LF otherwise (#9116);
CR-only files are rare enough not to get their own newline.
Fixes #9108
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP
Signed-off-by: Christoph Knittel <[email protected]>
cristianoc
left a comment
There was a problem hiding this comment.
Reviewed the separation of generated line endings from literal contents and the LF/CRLF regression tests. No blocking issues found. CI, including multi-printer and syntax tests, passes; not rerun locally.
Skip a UTF-8 byte order mark and end a line at a lone CR
Why:
Some Windows editors write a byte order mark, and bsc and bsc -format
rejected it byte by byte. A CR without an LF ended a `//` comment but
did not start a new line, so the next statement counted as on the same
line. The scanner now starts after a byte order mark and ends a line
at a CR not followed by LF, as editors and JavaScript do; code frames
show lines the same way, and the formatter keeps the byte order mark.
Diagnostic removed (byte order mark):
Example:
\xEF\xBB\xBFlet x = 1
Before (diagnostic):
Syntax error!
A.res:1:1
Not sure what to do with this character: "\239".
… the same for "\187" and "\191"
After (diagnostic): none
Diagnostic removed (lone CR):
Example:
let x = 1 // c\rlet y = 2
Before (diagnostic):
Syntax error!
B.res:1:10-18
Consecutive statements on a line must be separated by ';' or a newline
After (diagnostic): none
Format modified (lone CR):
Example:
let x = 1 // c\rlet y = 2
Before (text): Syntax error
After (rescript):
let x = 1 // c
let y = 2
Review: Why convert a CR-only file to LF when formatting? The formatter
writes CRLF when the first line break is CRLF and LF otherwise (#9116);
CR-only files are rare enough not to get their own newline.
Fixes #9108
Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP
Signed-off-by: Christoph Knittel <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Since #9042,
bsc -formatconverted the whole source to LF before parsing and the printed result back to CRLF for CRLF files. String literals went through both conversions, so formatting changed the program: a raw LF inside a string in a CRLF file became CRLF, and a raw CRLF inside a string in an LF file became LF. The same happened to template literals, character literals ('<LF>') and polymorphic variant names (#"a<LF>b"). Each conversion also removed only one CR before an LF, so a comment containing\r\r\nchanged again on every run.The source is now parsed as is.
Res_doc.to_stringtakes the newline to write for the line breaks the printer emits itself, andRes_printer.print_implementation/print_interfacepass it through;literal_line, which only string and template contents use, stays LF, and text copied from the source (literals, comments) keeps its bytes.Res_line_endingsshrinks tonewline source: CRLF when the source's first line break is CRLF, LF otherwise.bsc -format,res_parser -print resand the editor's format command all print with it. The one place where the printer rewrote a comment's line break itself, the first line of a reindented/* … * … */comment, now drops that line's CRs, as the following lines' trimming already did.res_parser -print resused to print a CRLF file with LF line endings; it now keeps them, likebsc -format.The multi-printer test in
tests/syntax_tests/res_test.mlnow also checks that formatting leaves these unchanged and is stable on them: a reindented star comment in a CRLF file; raw LF and CRLF inside strings, templates, character literals and polymorphic variants, in both CRLF and LF files; comments holding\r\r\nor mixed line breaks. It also checks that a reindented comment's\r\r\nbecomes the file's newline. Each of the new literal and comment cases fails on master.This issue doesn't cover lone CRs (#9108), and this PR leaves them alone.
Fixes #9100
🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP