Read line-by-line without the line editor when stdin or stdout is not a tty - #924
Open
tompng wants to merge 1 commit into
Open
Read line-by-line without the line editor when stdin or stdout is not a tty#924tompng wants to merge 1 commit into
tompng wants to merge 1 commit into
Conversation
… a tty Key-by-key line editing is a closed feedback loop of input and display; when either side is not a terminal the loop is broken, and an editor the user cannot see is worse than useless (invisible history recall or completion submits unseen content, and raw mode disables even the terminal driver's own echo). Instead, read with gets and echo the prompt and the input back to the output as a plain, escape-sequence-free transcript. This makes piped usage behave like GNU Readline for printable input: `echo input | ruby -rreadline -e '...'` now produces "> input" with no escape sequences. Behavior details: - The line editor is used only when both input and output are ttys (libedit does the same). For interactive editing with stdout redirected, point the render target at a tty with `Reline.output = $stderr`, as bash does with rl_outstream (ANSI only; the Windows gate is bound to the console's stdout). - Editing keys in piped input are no longer interpreted (libedit-style raw reads). GNU Readline interprets them, but GNU and libedit already disagree here, so exact readline-ext compatibility is not a well-defined target; control characters in input are the uncommon case. They are echoed in caret notation (^A) so they cannot corrupt the output. - The prompt and the input are echoed even though frontends with local echo will then show the input twice; GNU Readline echoes in exactly the same situations, so this introduces no new doubling, and it keeps redirected output a self-contained transcript. The prompt line is closed with a newline even on EOF. - Multiline reads honor confirm_multiline_termination and prompt_proc, so irb transcripts keep their per-line prompts and indentation. - Reline::Dumb is unchanged: TERM=dumb and test-mode usage keep the line editor path (IO#both_tty? defaults to true). Fixes ruby#886 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #886
When stdin or stdout is not a tty, read line-by-line with
getsinstead of running the key-by-key line editor, and echo the prompt and input back as a plain, escape-sequence-free transcript (like GNU Readline).Line editing is a feedback loop of input and display; with either side redirected the loop is broken: escape sequences leak into the output, invisible history recall or completion can submit unseen content, and raw mode disables even the terminal driver's own echo.
Reline.output = $stderr, as bash does with rl_outstream (ANSI gate only).^A) so they cannot corrupt the output.confirm_multiline_terminationandprompt_proc, so irb transcripts keep their per-line dynamic prompts.Reline::Dumb(TERM=dumb) is unchanged and keeps the line editor path.