fix(cli): render glyphs in prompts and handle EOF
All checks were successful
Security Scan / security (push) Successful in 19s
All checks were successful
Security Scan / security (push) Successful in 19s
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
37310c7cbd
commit
a5142dea78
3 changed files with 36 additions and 8 deletions
|
|
@ -32,6 +32,8 @@ func newReader() *bufio.Reader {
|
||||||
|
|
||||||
// Prompt asks for text input with a default value.
|
// Prompt asks for text input with a default value.
|
||||||
func Prompt(label, defaultVal string) (string, error) {
|
func Prompt(label, defaultVal string) (string, error) {
|
||||||
|
label = compileGlyphs(label)
|
||||||
|
defaultVal = compileGlyphs(defaultVal)
|
||||||
if defaultVal != "" {
|
if defaultVal != "" {
|
||||||
fmt.Printf("%s [%s]: ", label, defaultVal)
|
fmt.Printf("%s [%s]: ", label, defaultVal)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -40,7 +42,7 @@ func Prompt(label, defaultVal string) (string, error) {
|
||||||
|
|
||||||
r := newReader()
|
r := newReader()
|
||||||
input, err := r.ReadString('\n')
|
input, err := r.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,15 +55,15 @@ func Prompt(label, defaultVal string) (string, error) {
|
||||||
|
|
||||||
// Select presents numbered options and returns the selected value.
|
// Select presents numbered options and returns the selected value.
|
||||||
func Select(label string, options []string) (string, error) {
|
func Select(label string, options []string) (string, error) {
|
||||||
fmt.Println(label)
|
fmt.Println(compileGlyphs(label))
|
||||||
for i, opt := range options {
|
for i, opt := range options {
|
||||||
fmt.Printf(" %d. %s\n", i+1, opt)
|
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
||||||
}
|
}
|
||||||
fmt.Printf("Choose [1-%d]: ", len(options))
|
fmt.Printf("Choose [1-%d]: ", len(options))
|
||||||
|
|
||||||
r := newReader()
|
r := newReader()
|
||||||
input, err := r.ReadString('\n')
|
input, err := r.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil && strings.TrimSpace(input) == "" {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,9 +76,9 @@ func Select(label string, options []string) (string, error) {
|
||||||
|
|
||||||
// MultiSelect presents checkboxes (space-separated numbers).
|
// MultiSelect presents checkboxes (space-separated numbers).
|
||||||
func MultiSelect(label string, options []string) ([]string, error) {
|
func MultiSelect(label string, options []string) ([]string, error) {
|
||||||
fmt.Println(label)
|
fmt.Println(compileGlyphs(label))
|
||||||
for i, opt := range options {
|
for i, opt := range options {
|
||||||
fmt.Printf(" %d. %s\n", i+1, opt)
|
fmt.Printf(" %d. %s\n", i+1, compileGlyphs(opt))
|
||||||
}
|
}
|
||||||
fmt.Printf("Choose (space-separated) [1-%d]: ", len(options))
|
fmt.Printf("Choose (space-separated) [1-%d]: ", len(options))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -26,6 +27,15 @@ func TestPrompt_Good_Default(t *testing.T) {
|
||||||
assert.Equal(t, "world", val)
|
assert.Equal(t, "world", val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrompt_Bad_EOFUsesDefault(t *testing.T) {
|
||||||
|
SetStdin(strings.NewReader(""))
|
||||||
|
defer SetStdin(nil)
|
||||||
|
|
||||||
|
val, err := Prompt("Name", "world")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "world", val)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSelect_Good(t *testing.T) {
|
func TestSelect_Good(t *testing.T) {
|
||||||
SetStdin(strings.NewReader("2\n"))
|
SetStdin(strings.NewReader("2\n"))
|
||||||
defer SetStdin(nil)
|
defer SetStdin(nil)
|
||||||
|
|
@ -43,6 +53,14 @@ func TestSelect_Bad_Invalid(t *testing.T) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelect_Bad_EOF(t *testing.T) {
|
||||||
|
SetStdin(strings.NewReader(""))
|
||||||
|
defer SetStdin(nil)
|
||||||
|
|
||||||
|
_, err := Select("Pick", []string{"a", "b"})
|
||||||
|
assert.ErrorIs(t, err, io.EOF)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultiSelect_Good(t *testing.T) {
|
func TestMultiSelect_Good(t *testing.T) {
|
||||||
SetStdin(strings.NewReader("1 3\n"))
|
SetStdin(strings.NewReader("1 3\n"))
|
||||||
defer SetStdin(nil)
|
defer SetStdin(nil)
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,8 @@ func Confirm(prompt string, opts ...ConfirmOption) bool {
|
||||||
opt(cfg)
|
opt(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt = compileGlyphs(prompt)
|
||||||
|
|
||||||
// Build the prompt suffix
|
// Build the prompt suffix
|
||||||
var suffix string
|
var suffix string
|
||||||
if cfg.required {
|
if cfg.required {
|
||||||
|
|
@ -218,12 +220,14 @@ func Question(prompt string, opts ...QuestionOption) string {
|
||||||
opt(cfg)
|
opt(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt = compileGlyphs(prompt)
|
||||||
|
|
||||||
reader := newReader()
|
reader := newReader()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Build prompt with default
|
// Build prompt with default
|
||||||
if cfg.defaultValue != "" {
|
if cfg.defaultValue != "" {
|
||||||
fmt.Printf("%s [%s] ", prompt, cfg.defaultValue)
|
fmt.Printf("%s [%s] ", prompt, compileGlyphs(cfg.defaultValue))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s ", prompt)
|
fmt.Printf("%s ", prompt)
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +332,8 @@ func Choose[T any](prompt string, items []T, opts ...ChooseOption[T]) T {
|
||||||
opt(cfg)
|
opt(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt = compileGlyphs(prompt)
|
||||||
|
|
||||||
reader := newReader()
|
reader := newReader()
|
||||||
visible := make([]int, len(items))
|
visible := make([]int, len(items))
|
||||||
for i := range items {
|
for i := range items {
|
||||||
|
|
@ -404,6 +410,8 @@ func ChooseMulti[T any](prompt string, items []T, opts ...ChooseOption[T]) []T {
|
||||||
opt(cfg)
|
opt(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt = compileGlyphs(prompt)
|
||||||
|
|
||||||
reader := newReader()
|
reader := newReader()
|
||||||
visible := make([]int, len(items))
|
visible := make([]int, len(items))
|
||||||
for i := range items {
|
for i := range items {
|
||||||
|
|
@ -458,7 +466,7 @@ func renderChoices[T any](prompt string, items []T, visible []int, displayFn fun
|
||||||
if defaultN >= 0 && idx == defaultN {
|
if defaultN >= 0 && idx == defaultN {
|
||||||
marker = "*"
|
marker = "*"
|
||||||
}
|
}
|
||||||
fmt.Printf(" %s%d. %s\n", marker, i+1, displayFn(items[idx]))
|
fmt.Printf(" %s%d. %s\n", marker, i+1, compileGlyphs(displayFn(items[idx])))
|
||||||
}
|
}
|
||||||
if filter {
|
if filter {
|
||||||
fmt.Println(" (type to filter the list)")
|
fmt.Println(" (type to filter the list)")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue