From 7a01d294e28e87b1af013c4f2d21a8c671e1cd49 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Fri, 8 May 2026 12:10:05 +0200 Subject: [PATCH] cmdio: drop AskSelect, migrate caller to RunSelect AskSelect was the only function in libs/cmdio/compat.go that mixed template-specific concerns (multi-line label handling) with cmdio. Its single caller in libs/template/config.go now calls cmdio.RunSelect directly and handles the prefix lines locally, shrinking cmdio's public surface by one. Added HideHelp to cmdio.SelectOptions so the template prompt keeps the hidden-help behavior that AskSelect previously hardcoded. Co-authored-by: Isaac --- libs/cmdio/compat.go | 42 ---------------------------------- libs/cmdio/compat_test.go | 48 --------------------------------------- libs/cmdio/select.go | 4 ++++ libs/template/config.go | 17 +++++++++++++- 4 files changed, 20 insertions(+), 91 deletions(-) diff --git a/libs/cmdio/compat.go b/libs/cmdio/compat.go index 95c1ca2d00..ed2ed2498a 100644 --- a/libs/cmdio/compat.go +++ b/libs/cmdio/compat.go @@ -5,8 +5,6 @@ import ( "fmt" "io" "strings" - - "github.com/manifoldco/promptui" ) /* @@ -94,43 +92,3 @@ func AskYesOrNo(ctx context.Context, question string) (bool, error) { ans = strings.ToLower(strings.TrimSpace(ans)) return ans == "y" || ans == "yes", nil } - -func splitAtLastNewLine(s string) (string, string) { - // Split at the newline character - if i := strings.LastIndex(s, "\n"); i != -1 { - return s[:i+1], s[i+1:] - } - // Return the original string if no newline found - return "", s -} - -// AskSelect is a compatibility layer for the progress logger interfaces. -// It prompts the user with a question and returns the answer. -func AskSelect(ctx context.Context, question string, choices []string) (string, error) { - c := fromContext(ctx) - - // Promptui does not support multiline prompts. So we split the question. - first, last := splitAtLastNewLine(question) - _, err := io.WriteString(c.err, first) - if err != nil { - return "", err - } - - prompt := promptui.Select{ - Label: last, - Items: choices, - HideHelp: true, - Templates: &promptui.SelectTemplates{ - Label: "{{.}}: ", - Selected: last + ": {{.}}", - }, - Stdin: c.promptStdin(), - Stdout: nopWriteCloser{c.err}, - } - - _, ans, err := prompt.Run() - if err != nil { - return "", err - } - return ans, nil -} diff --git a/libs/cmdio/compat_test.go b/libs/cmdio/compat_test.go index 3323556e8b..702f25748b 100644 --- a/libs/cmdio/compat_test.go +++ b/libs/cmdio/compat_test.go @@ -147,54 +147,6 @@ func (e *errorAfterNReader) Read(p []byte) (n int, err error) { return 0, e.err } -func TestCompat_splitAtLastNewLine(t *testing.T) { - tests := []struct { - name string - input string - wantFirst string - wantLast string - }{ - { - name: "LF newline in middle", - input: "hello\nworld", - wantFirst: "hello\n", - wantLast: "world", - }, - { - name: "CRLF newline in middle", - input: "hello\r\nworld", - wantFirst: "hello\r\n", - wantLast: "world", - }, - { - name: "no newline", - input: "hello world", - wantFirst: "", - wantLast: "hello world", - }, - { - name: "newline at end", - input: "hello\nworld\n", - wantFirst: "hello\nworld\n", - wantLast: "", - }, - { - name: "newline at start", - input: "\nhello world", - wantFirst: "\n", - wantLast: "hello world", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - first, last := splitAtLastNewLine(tt.input) - assert.Equal(t, tt.wantFirst, first) - assert.Equal(t, tt.wantLast, last) - }) - } -} - func TestCompat_AskYesOrNo(t *testing.T) { tests := []struct { name string diff --git a/libs/cmdio/select.go b/libs/cmdio/select.go index 186e7a0e76..f541f97eb7 100644 --- a/libs/cmdio/select.go +++ b/libs/cmdio/select.go @@ -23,6 +23,9 @@ type SelectOptions struct { // StartInSearchMode opens the prompt with the search input focused. StartInSearchMode bool + // HideHelp hides the navigation help line shown by promptui by default. + HideHelp bool + // LabelTemplate renders Label. Empty uses the default. LabelTemplate string @@ -44,6 +47,7 @@ func RunSelect(ctx context.Context, opts SelectOptions) (int, error) { Items: opts.Items, Searcher: opts.Searcher, StartInSearchMode: opts.StartInSearchMode, + HideHelp: opts.HideHelp, Templates: &promptui.SelectTemplates{ Label: opts.LabelTemplate, Active: opts.Active, diff --git a/libs/template/config.go b/libs/template/config.go index 95fd7c5caa..5f6ea3c915 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -7,6 +7,7 @@ import ( "io/fs" "maps" "slices" + "strings" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" @@ -213,10 +214,24 @@ func (c *config) promptOnce(property *jsonschema.Schema, name, defaultVal, descr if err != nil { return err } - userInput, err = cmdio.AskSelect(c.ctx, description, options) + // promptui only supports a single-line label, so render any preceding + // lines of the description separately. + label := description + if i := strings.LastIndex(description, "\n"); i != -1 { + cmdio.LogString(c.ctx, description[:i]) + label = description[i+1:] + } + idx, err := cmdio.RunSelect(c.ctx, cmdio.SelectOptions{ + Label: label, + Items: options, + HideHelp: true, + LabelTemplate: "{{.}}: ", + Selected: label + ": {{.}}", + }) if err != nil { return err } + userInput = options[idx] } else { var err error userInput, err = cmdio.Ask(c.ctx, description, defaultVal)