Repository navigation
Deliver a line ending in a bare CR at once instead of on the next write [patch] - #129
Conversation
…te [patch] Since #85, a read that ended in CR kept its whole line buffered until the next read showed whether an LF followed. A progress or status line ending in a bare CR therefore arrived only when the command next wrote, or at EOF, which could be seconds late. A CR now ends its line immediately, even as the last character of a read. Each stream remembers that its last read ended on a CR, and an LF at the start of the next read completes the CRLF and is skipped instead of ending an empty line, which is what Process.OutputDataReceived does. Reset() and Complete() clear that state, and Complete() no longer strips a trailing CR. The test that pinned the held-back CR is replaced by tests that the line is delivered at once on both streams, that Complete() does not deliver it again, and that Reset() forgets the pending LF skip. The #85 CRLF-split tests are unchanged and still pass. Fixes #128 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW24pNCmCLDhz6z3ZdY
|
Blocked: I'm treating this as caused by this PR until shown otherwise. macOS passed on Why I couldn't root-cause it here:
What would unblock it: run Generated by Claude Code |
Resolves the conflict in LineOutputHandler.cs: keeps this branch's immediate CR delivery with its per-stream skip-leading-LF state, and takes main's CreateRunHandler (#132), its class remarks, and its narrowing of IsLineBreak to LF only (#136). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vfJW24pNCmCLDhz6z3ZdY
|
Following up on the macOS hang: it was the runner, not this change. The re-run on 2026-11-04 passed in 58 s on the same commit. The PR had since picked up a merge conflict with Generated by Claude Code |
|



Fixes #128
Summary
Since the #85 fix,
LineOutputHandlerkept a line buffered whenever a read ended in\r, until the next read showed whether a\nfollowed. A progress line likeDownloading 100%\rfollowed by a quiet period was only delivered when the command next wrote, or at EOF. In the issue's repro that was about 3 s late, whereProcess.OutputDataReceiveddelivers it at once.This PR follows .NET's
AsyncStreamReader:SkipLeadingLineFeedflag. When the next read starts with\n, that LF completes the CRLF and is skipped instead of producing an empty line.Reset()replaces the per-stream state, flag included.Complete()clears the flag and no longer strips a trailing CR.CompletePendingCarriageReturnis removed.The per-stream state is now a small internal
LineBuffer(text plus flag).outputBufferanderrorBufferkeep their names, andToString()still returns the partial line.Reset()still swaps in fresh objects, so a delivery from a cancelled run finishes against its own state.Tests
HandleStandardOutputDataShouldHoldBackTrailingCarriageReturn, which pinned the delayed behaviour, with:HandleStandardOutputDataShouldDeliverALineEndingInATrailingCarriageReturnAtOnceHandleStandardErrorDataShouldDeliverALineEndingInATrailingCarriageReturnAtOnceCompleteShouldNotDeliverALineAlreadyEndedByATrailingCarriageReturnResetShouldForgetAnLfStillDueToBeSkipped"x\r"+"\ny\n"→x,y) and the multi-read case ("a\r","\nb\r","\r","c\n"→a,b,"",c).LineOutputHandlerTestspass.RunCommand.Testsuite: 67 passed and 2 skipped out of 69, locally on Linux with the .NET 10 SDK.Generated by Claude Code