Skip to content

Commit

Permalink
fix: console prompt conflict with bubble tea (#2425)
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge authored Jun 13, 2024
1 parent d8141c7 commit e5644de
Showing 1 changed file with 33 additions and 28 deletions.
61 changes: 33 additions & 28 deletions internal/utils/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"os"
"strings"
"sync"
"time"

"github.com/go-errors/errors"
Expand All @@ -18,30 +19,50 @@ type Console struct {
stdin *bufio.Scanner
logger io.Writer
token chan string
mu sync.Mutex
}

func NewConsole() Console {
c := Console{
func NewConsole() *Console {
return &Console{
IsTTY: term.IsTerminal(int(os.Stdin.Fd())),
stdin: bufio.NewScanner(os.Stdin),
logger: GetDebugLogger(),
token: make(chan string),
mu: sync.Mutex{},
}
}

// Prevent interactive terminals from hanging more than 10 minutes
const ttyTimeout = time.Minute * 10

func (c *Console) ReadLine(ctx context.Context) string {
// Wait a few ms for input
timeout := time.Millisecond
if c.IsTTY {
timeout = ttyTimeout
}
timer := time.NewTimer(timeout)
defer timer.Stop()
// Read from stdin in background
go func() {
// Scan line by line from input or file
for c.stdin.Scan() {
c.mu.Lock()
defer c.mu.Unlock()
// Scan one line from input or file
if c.stdin.Scan() {
c.token <- strings.TrimSpace(c.stdin.Text())
}
if err := c.stdin.Err(); err != nil {
fmt.Fprintln(c.logger, err)
}
close(c.token)
}()
return c
var input string
select {
case input = <-c.token:
case <-ctx.Done():
case <-timer.C:
}
return input
}

// PromptYesNo asks yes/no questions using the label.
func (c Console) PromptYesNo(ctx context.Context, label string, def bool) (bool, error) {
func (c *Console) PromptYesNo(ctx context.Context, label string, def bool) (bool, error) {
choices := "Y/n"
if !def {
choices = "y/N"
Expand All @@ -68,26 +89,10 @@ func parseYesNo(s string) *bool {
return nil
}

// Prevent interactive terminals from hanging more than 10 minutes
const ttyTimeout = time.Minute * 10

// PromptText asks for input using the label.
func (c Console) PromptText(ctx context.Context, label string) (string, error) {
func (c *Console) PromptText(ctx context.Context, label string) (string, error) {
fmt.Fprint(os.Stderr, label)
// Wait a few ms for input
timeout := time.Millisecond
if c.IsTTY {
timeout = ttyTimeout
}
timer := time.NewTimer(timeout)
defer timer.Stop()
// Read from stdin
var input string
select {
case input = <-c.token:
case <-ctx.Done():
case <-timer.C:
}
input := c.ReadLine(ctx)
// Echo to stderr for non-interactive terminals
if !c.IsTTY {
fmt.Fprintln(os.Stderr, input)
Expand Down

0 comments on commit e5644de

Please sign in to comment.