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
Write("") followed by NewLine() leaves a tab on the blank line and drops the next line to column 0 (the #115 fix misses empty writes) #126
Writing an empty string with Write("") (or Write(null)) and then calling NewLine() puts the indentation on the blank line instead of on the next line. Every line after that starts at column 0.
varcb=CodeBlocker.Create();cb.WriteLine("class C");using(newScope(cb)){cb.WriteLine("int a;");cb.Write("");// e.g. an optional modifier that happens to be emptycb.NewLine();cb.WriteLine("int b;");}
Observed output, with → standing for a tab:
class C
{
→int a;
→
int b;
}
Expected output:
class C
{
→int a;
→int b;
}
I reproduced this against the current main project, and the existing test suite passes.
Cause
There are two pieces involved (CodeBlocker/CodeBlocker.cs):
Write, around lines 254–261: it forwards to IndentedTextWriter.Write(string). That method always writes the pending tabs and clears its tabs-pending flag, even when the string is empty or null. Write only sets isMidLine when the text is non-empty.
NewLine(), around lines 219–232: because isMidLine is false, it takes the WriteLineNoTabs branch. That branch does not re-arm the pending tabs.
The tabs are already spent on the blank line, so the next WriteLine gets none. This is the same failure #115 fixed for non-empty partial writes. The empty-string path was left out of that fix.
Why it matters
Generators often build a line from optional fragments (modifiers, attributes, a separator) that can be empty. When that happens, the output silently gets a trailing-whitespace line and everything after it in the scope loses its indentation. Nothing fails. The generated code is just mis-formatted, and snapshot tests show unrelated diffs.
Suggested fix / acceptance criteria
In Write, return early when string.IsNullOrEmpty(text), so the underlying writer never consumes the pending tabs. Alternatively, set isMidLine = true after every call that reaches the inner writer.
Priority: Medium. Generators often emit optional fragments that turn out to be empty, and when that happens the generated code is silently mis-indented. The output still compiles, though, so the damage is cosmetic and shows up as snapshot noise.
Area / suggested assignment:CodeBlocker/CodeBlocker.cs, specifically Write (~lines 254–261) and NewLine() (~lines 219–232).
Notes: Of the two options, the early return on string.IsNullOrEmpty is the smaller and safer change. Also check the other Write overloads, such as Write(char[]) and format-string writes, for the same empty-input path. Add the regression test next to the #115 test, and cover both "" and null.
Activity
matt-edmondson commentedon Oct 5, 2026
Triage
CodeBlocker/CodeBlocker.cs, specificallyWrite(~lines 254–261) andNewLine()(~lines 219–232).public void M()(CS0501) #119 (a body containing only blank lines), which is a different path.Notes: Of the two options, the early return on
string.IsNullOrEmptyis the smaller and safer change. Also check the otherWriteoverloads, such asWrite(char[])and format-string writes, for the same empty-input path. Add the regression test next to the #115 test, and cover both""andnull.Generated by Claude Code