From 71d4c285dd6a18181cf88a119e59b26ce6374a9e Mon Sep 17 00:00:00 2001 From: Chris Suszynski Date: Fri, 6 Dec 2024 16:00:25 +0100 Subject: [PATCH] [release-1.16] Backport of knative#1973 (#504) ## release-1.16 Backport of knative#1973 Required for https://github.com/openshift-knative/kn-plugin-event/pull/556 --- pkg/output/tui/progress_test.go | 1 + pkg/output/tui/spinner.go | 35 ++++++++++++++++++++++++++++++--- pkg/output/tui/spinner_test.go | 14 ++++++++++--- pkg/output/tui/widgets_test.go | 2 ++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/pkg/output/tui/progress_test.go b/pkg/output/tui/progress_test.go index d1540fc610..af75869963 100644 --- a/pkg/output/tui/progress_test.go +++ b/pkg/output/tui/progress_test.go @@ -33,6 +33,7 @@ import ( ) func TestProgress(t *testing.T) { + t.Parallel() ctx := context.TestContext(t) prt := output.NewTestPrinter() ctx = output.WithContext(ctx, prt) diff --git a/pkg/output/tui/spinner.go b/pkg/output/tui/spinner.go index c55bbb51d7..76c8fae819 100644 --- a/pkg/output/tui/spinner.go +++ b/pkg/output/tui/spinner.go @@ -30,7 +30,7 @@ import ( const spinnerColor = lipgloss.Color("205") type Spinner interface { - Runnable[Spinner] + Runnable[SpinnerControl] } func (w *widgets) NewSpinner(message string) Spinner { @@ -44,17 +44,27 @@ type BubbleSpinner struct { output.InputOutput Message + *updater spin spinner.Model tea *tea.Program quitChan chan struct{} teaErr error } -func (b *BubbleSpinner) With(fn func(Spinner) error) error { +// SpinnerControl allows one to control the spinner, for example, to change the +// message. +type SpinnerControl interface { + UpdateMessage(message string) +} + +// With will start the spinner and perform long operation within the +// provided fn. The spinner will be automatically shutdown when the provided +// function exits. +func (b *BubbleSpinner) With(fn func(SpinnerControl) error) error { b.start() err := func() error { defer b.stop() - return fn(b) + return fn(b.updater) }() return multierr.Combine(err, b.teaErr) } @@ -70,10 +80,20 @@ func (b *BubbleSpinner) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (b *BubbleSpinner) View() string { + select { + case m := <-b.updater.messages: + // nil on channel close + if m != nil { + b.Message.Text = *m + } + default: + // nothing + } return fmt.Sprintf("%s %s", b.Message.Text, b.spin.View()) } func (b *BubbleSpinner) start() { + b.updater = &updater{make(chan *string)} b.spin = spinner.New( spinner.WithSpinner(spinner.Meter), spinner.WithStyle(spinnerStyle()), @@ -94,6 +114,7 @@ func (b *BubbleSpinner) stop() { return } + close(b.updater.messages) b.tea.Quit() <-b.quitChan @@ -111,3 +132,11 @@ func (b *BubbleSpinner) stop() { func spinnerStyle() lipgloss.Style { return lipgloss.NewStyle().Foreground(spinnerColor) } + +type updater struct { + messages chan *string +} + +func (u updater) UpdateMessage(message string) { + u.messages <- &message +} diff --git a/pkg/output/tui/spinner_test.go b/pkg/output/tui/spinner_test.go index a0f6a1a4fe..1d743234aa 100644 --- a/pkg/output/tui/spinner_test.go +++ b/pkg/output/tui/spinner_test.go @@ -26,6 +26,7 @@ import ( ) func TestSpinner(t *testing.T) { + t.Parallel() ctx := context.TestContext(t) prt := output.NewTestPrinter() ctx = output.WithContext(ctx, prt) @@ -35,14 +36,21 @@ func TestSpinner(t *testing.T) { if s == nil { t.Errorf("want spinner, got nil") } - if err := s.With(func(spinner tui.Spinner) error { - time.Sleep(20 * time.Millisecond) + if err := s.With(func(sc tui.SpinnerControl) error { + time.Sleep(3 * time.Millisecond) + sc.UpdateMessage("msg-1") + time.Sleep(3 * time.Millisecond) + sc.UpdateMessage("msg-2") + time.Sleep(3 * time.Millisecond) return nil }); err != nil { t.Errorf("want nil, got %v", err) } got := prt.Outputs().Out.String() - want := "\x1b[?25lmessage ▰▱▱\x1b[0D\x1b[2K\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006lmessage Done\n" + want := "\x1b[?25lmessage ▰▱▱\x1b[0D" + + "\x1b[0D\x1b[2Kmsg-1 ▰▰▱\x1b[0D" + + "\x1b[0D\x1b[2Kmsg-2 ▰▰▰\x1b[0D" + + "\x1b[2K\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006lmsg-2 Done\n" if got != want { t.Errorf("text missmatch\nwant %q,\n got %q", want, got) } diff --git a/pkg/output/tui/widgets_test.go b/pkg/output/tui/widgets_test.go index 52911b25c3..ed3c3baf33 100644 --- a/pkg/output/tui/widgets_test.go +++ b/pkg/output/tui/widgets_test.go @@ -24,6 +24,7 @@ import ( ) func TestNewWidgets(t *testing.T) { + t.Parallel() ctx := context.TestContext(t) w := tui.NewWidgets(ctx) @@ -33,6 +34,7 @@ func TestNewWidgets(t *testing.T) { } func TestNewInteractiveWidgets(t *testing.T) { + t.Parallel() ctx := context.TestContext(t) w, err := tui.NewInteractiveWidgets(ctx)