Skip to content

prompt: swallow escape sequences, both backspaces, exit 130 on Ctrl+C - #14214

Open
ndeloof wants to merge 1 commit into
docker:mainfrom
ndeloof:prompt-followups
Open

prompt: swallow escape sequences, both backspaces, exit 130 on Ctrl+C#14214
ndeloof wants to merge 1 commit into
docker:mainfrom
ndeloof:prompt-followups

Conversation

@ndeloof

@ndeloof ndeloof commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What this PR does, in one sentence: three edges the survey-removal review (#14161) surfaced in the new interactive confirmation reader — escape sequences, the second backspace encoding, and Ctrl+C's exit status — plus the lost pipe-mode hint.

Context

#14161 replaced the archived survey/v2 with a small raw-terminal reader, faithfully preserving the existing behavior. Raw mode changes what the reader receives, though: arrow keys arrive as byte sequences (ESC [ A) whose printable tail was echoed and taken as answer characters; terminals emitting ^H for backspace could no longer erase; and Ctrl+C — which raw mode hides from the signal handler — surfaced as a generic error with exit 1, where a real SIGINT exits 130.

What the PR brings

  • Escape sequences are swallowed whole (CSI and SS3 forms): pressing an arrow or function key at the prompt no longer prints [A nor corrupts the answer.
  • Both backspace encodings erase: DEL and ^H (some terminals, legacy Windows console).
  • Ctrl+C at a prompt exits 130, like a real SIGINT: the interrupt only exists as an error in raw mode, so prompt.ErrInterrupt (now exported) is mapped by AdaptCmd alongside the existing cancellation cases. This is the one deliberate behavior change — before and after cmd/prompt: remove uses of github.com/AlecAivazis/survey/v2 #14161 alike, a prompt interrupt reported a generic failure.
  • The [y/N] hint shows in pipe mode too: the message reaching a log or a piped consumer documents what was asked and what the default was, as it did before cmd/prompt: remove uses of github.com/AlecAivazis/survey/v2 #14161 moved hints into the prompt.

Each point is locked by a test (pty-driven arrow-key round-trip, both-encodings erase, 130 mapping through AdaptCmd, updated pipe expectations).

🤖 Generated with Claude Code

Follow-ups to the survey removal (docker#14161), all on the interactive
confirmation reader:

- Arrow and function keys arrive as raw escape sequences now that the
  terminal is raw: the printable tail ("[A"...) used to be echoed and
  taken as answer characters. The whole sequence (CSI and SS3 forms) is
  swallowed instead.
- Backspace erases under both encodings: DEL (most terminals) and ^H
  (some terminals, legacy Windows console).
- Ctrl+C at a prompt now exits with the conventional 130 status: raw
  mode swallows the SIGINT, so the interrupt only exists as an error —
  prompt.ErrInterrupt, now exported — and AdaptCmd maps it exactly like
  a real SIGINT instead of reporting a generic failure.
- The [y/N] hint shows in pipe mode too: the message reaching a log or
  a piped consumer documents what was asked and what the default was,
  like it did before the survey removal moved hints into the prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
@ndeloof
ndeloof requested review from a team as code owners September 11, 2026 13:36

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

The three fixes are clean and correct. Escape-sequence swallowing, dual backspace handling, and ErrInterrupt→130 mapping all look good. Each is covered by a test.

Lower-confidence findings (not posted inline)

  • [low] cmd/prompt/prompt.go:148 — discardEscapeSequence does not handle ANSI intermediate bytes (0x20–0x2F): the CSI parameter loop correctly handles numeric params and `;" (all ≥0x30), but true ECMA-48 intermediate bytes (0x20–0x2F, rare in practice) would not be recognized as non-final, causing the loop to stop early and treat the next byte as a new sequence. This is an extremely minor edge case for a y/n prompt and no practical terminal emits such sequences for cursor/function keys.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.41379% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/prompt/prompt.go 71.42% 5 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

Comment thread cmd/prompt/prompt.go
Comment on lines -68 to +67
_, _ = fmt.Fprint(u.stdout, message+prompt)
_, _ = fmt.Fprint(u.stdout, message+confirmHint(defaultValue))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that it added a helper (thought from your Slack thread that it generally preferred just raw inline)?

Comment thread cmd/prompt/prompt.go
Comment on lines -34 to +38
var errInterrupt = errors.New("interrupt")
// ErrInterrupt is returned by an interactive prompt when the user presses
// Ctrl+C. The terminal being in raw mode, no SIGINT is ever delivered: this
// error is the only interrupt signal callers get, and the command runner
// maps it to the conventional 130 exit status like a real SIGINT.
var ErrInterrupt = errors.New("interrupt")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When looking at the original PR, I was actually considering if we wanted to have context-cancellation somewhere.

(I kept the interrupt error because the old library printed that, but I think it's not that common to print something normally, e.g.

docker system prun
...
Are you sure you want to continue? [y/N] ^C

" - git://github.com/user/repo.git\n" +
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
"Do you want to continue?",
"Do you want to continue? [y/N]: ",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait; did I mess this up? (if so; why didn't CI fail?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants