You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Template bodies whose line endings differ from the writer's come out with mixed line endings, or as one run-on line with no indentation (CRLF writer + LF body becomes an expression body) #123
A BodyFactory, ExpressionBodyFactory or accessor body can write text that uses the other terminator. The usual source is a verbatim or raw string literal, whose line endings follow the generator's own source file rather than the writer's setting. Two cases go wrong:
LF writer (the default), CRLF text: the lines split on \n and each keeps a stray \r, so the generated file has mixed line endings.
CRLF writer, LF text: nothing splits. A braced method body is taken for a one-line expression body and glued to the declaration line, and every following line loses its indentation.
DocComment.SplitLines already normalises \r\n and \r to \n before splitting. Its remarks explain that this is "so that a caller's verbatim or raw string literal lays out the same way whichever platform the source file was written on". The body path has no such step.
Repro
// Case A: default (LF) writer, body written with CRLFusingvarcb=CodeBlocker.Create();varc=newClassTemplate{Name="C"};c.Members.Add(newMethodTemplate{Type="int",Name="M",BodyFactory= b =>b.WriteLine("{\r\n\treturn 1;\r\n}")});cb.AddClass(c);// Actual: class C\n{\n\tint M()\n\t{\r\n\t\treturn 1;\r\n\t}\n}\n// Expected: LF only
// Case B: CRLF writer, body written with LFusingvarcb=CodeBlocker.Create("\t",NewLines.CrLf);// same ClassTemplate, BodyFactory = b => b.WriteLine("{\n\treturn 1;\n}")// Actual: class C\r\n{\r\n\tint M() {\n\treturn 1;\n}\r\n}\r\n// Expected: class C\r\n{\r\n\tint M()\r\n\t{\r\n\t\treturn 1;\r\n\t}\r\n}\r\n
PropertyTemplate.ExpressionBodyFactory has the same problem. With a CRLF writer, b.Write("[\n\t1,\n\t2,\n]") produces \tint[] P => [\n\t1,\n\t2,\n];\r\n: the continuation lines get no enclosing indent and use LF inside a CRLF file.
Why it matters
Most generators embed multi-line code as a raw string literal, and git's core.autocrlf or .gitattributes decides whether that literal holds CRLF or LF. The same generator therefore produces different bytes, or different layout, depending on how its source was checked out. That brings back the platform-dependent output #81 set out to remove, and it breaks the README's promise of identical output on every platform.
Suggested fix
Normalise before splitting, the same way DocComment.SplitLines does:
Each line already goes back out through WriteLine/NewLine, so the configured terminator is then applied uniformly. ATerminatorThatIsNeitherLfNorCrLfIsHonoured must keep passing: after normalising, also split on parent.NewLineString when it is neither LF nor CRLF.
Tests: render a MethodTemplate and an expression-bodied PropertyTemplate with an LF writer fed CRLF text and with a CRLF writer fed LF text, and assert the exact bytes.
This is separate from #121, which asks whether lines inside a verbatim string literal should be re-indented at all.
Priority: Medium. Generator output depends on how the generator's source was checked out (core.autocrlf), which breaks the README's identical-output promise. With a CRLF writer, a braced body is mangled into an expression body. The fix is small and has a working precedent in DocComment.SplitLines.
Activity
matt-edmondson commentedon Sep 29, 2026
Triage
core.autocrlf), which breaks the README's identical-output promise. With a CRLF writer, a braced body is mangled into an expression body. The fix is small and has a working precedent inDocComment.SplitLines.TemplateRendering.SplitLines(CodeBlocker/Templates/TemplateRendering.cs:50)Generated by Claude Code