Skip to content

Commit 0eb72d1

Browse files
pieternclaude
andauthored
Fix arrow key navigation in prompts on Windows (#4501)
## Summary - Fixes #4481 — arrow keys stopped working for yes/no and enum selection during `databricks bundle init` on Windows - Root cause: PR #4301 started passing `io.NopCloser(os.Stdin)` as explicit `Stdin` to `promptui`, which bypasses `chzyer/readline`'s Windows-specific `RawReader`. That reader uses `ReadConsoleInputW` to translate arrow key virtual key codes (`VK_UP`/`VK_DOWN`) into readline navigation — without it, arrow keys are silently ignored on Windows. - Fix: introduce `promptStdin()` helper that returns `nil` when `c.in` is `os.Stdin` (letting readline use its platform default) and wraps custom readers for test scenarios. ## Test plan - [x] `go test ./libs/cmdio/...` passes - [x] `go test ./libs/template/...` passes - [x] Verify on Windows that arrow keys work in `databricks bundle init` selection prompts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e15b8f1 commit 0eb72d1

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

libs/cmdio/compat.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func AskSelect(ctx context.Context, question string, choices []string) (string,
123123
Label: "{{.}}: ",
124124
Selected: last + ": {{.}}",
125125
},
126-
Stdin: io.NopCloser(c.in),
126+
Stdin: c.promptStdin(),
127127
Stdout: nopWriteCloser{c.err},
128128
}
129129

libs/cmdio/io.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"io"
7+
"os"
78
"slices"
89
"strings"
910
"sync"
@@ -89,7 +90,7 @@ func (c *cmdIO) Select(items []Tuple, label string) (id string, err error) {
8990
Active: `{{.Name | bold}} ({{.Id|faint}})`,
9091
Inactive: `{{.Name}}`,
9192
},
92-
Stdin: io.NopCloser(c.in),
93+
Stdin: c.promptStdin(),
9394
Stdout: nopWriteCloser{c.err},
9495
}).Run()
9596
if err != nil {
@@ -125,7 +126,7 @@ func (c *cmdIO) Secret(label string) (value string, err error) {
125126
Label: label,
126127
Mask: '*',
127128
HideEntered: true,
128-
Stdin: io.NopCloser(c.in),
129+
Stdin: c.promptStdin(),
129130
Stdout: nopWriteCloser{c.err},
130131
})
131132

@@ -137,6 +138,19 @@ func Secret(ctx context.Context, label string) (value string, err error) {
137138
return c.Secret(label)
138139
}
139140

141+
// promptStdin returns the stdin reader for use with promptui.
142+
// If the reader is os.Stdin, it returns nil to let the underlying readline
143+
// library use its platform-specific default. On Windows, this is critical
144+
// because readline's default uses ReadConsoleInputW to read arrow keys
145+
// as virtual key events. Passing a wrapped os.Stdin would bypass this
146+
// and break arrow key navigation in selection prompts.
147+
func (c *cmdIO) promptStdin() io.ReadCloser {
148+
if c.in == os.Stdin {
149+
return nil
150+
}
151+
return io.NopCloser(c.in)
152+
}
153+
140154
type nopWriteCloser struct {
141155
io.Writer
142156
}
@@ -148,14 +162,14 @@ func (nopWriteCloser) Close() error {
148162
func Prompt(ctx context.Context) *promptui.Prompt {
149163
c := fromContext(ctx)
150164
return &promptui.Prompt{
151-
Stdin: io.NopCloser(c.in),
165+
Stdin: c.promptStdin(),
152166
Stdout: nopWriteCloser{c.err},
153167
}
154168
}
155169

156170
func RunSelect(ctx context.Context, prompt *promptui.Select) (int, string, error) {
157171
c := fromContext(ctx)
158-
prompt.Stdin = io.NopCloser(c.in)
172+
prompt.Stdin = c.promptStdin()
159173
prompt.Stdout = nopWriteCloser{c.err}
160174
return prompt.Run()
161175
}

0 commit comments

Comments
 (0)