From c06b9b7304a53ed5526dc51fc4fcafc45d92be54 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 19 Nov 2023 13:43:21 -0500 Subject: [PATCH] update progress rendering to be closer to `v0.1.10` --- cmd/cmd.go | 37 ++++++++++++++++++------------------ progress/bar.go | 3 ++- progress/progress.go | 31 +++++++++++++++++------------- progress/spinner.go | 45 ++++++++------------------------------------ 4 files changed, 46 insertions(+), 70 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index d4c5f92c..68636dcf 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -53,10 +53,6 @@ func CreateHandler(cmd *cobra.Command, args []string) error { bars := make(map[string]*progress.Bar) - status := fmt.Sprintf("creating %s", args[0]) - spinner := progress.NewSpinner(status) - p.Add(status, spinner) - modelfile, err := os.ReadFile(filename) if err != nil { return err @@ -72,10 +68,8 @@ func CreateHandler(cmd *cobra.Command, args []string) error { return err } - spinner.Stop() - - status = "transferring model data" - spinner = progress.NewSpinner(status) + status := "transferring model data" + spinner := progress.NewSpinner(status) p.Add(status, spinner) for _, c := range commands { @@ -179,14 +173,14 @@ func PushHandler(cmd *cobra.Command, args []string) error { defer p.Stop() bars := make(map[string]*progress.Bar) - - status := fmt.Sprintf("pushing %s", args[0]) - spinner := progress.NewSpinner(status) - p.Add(status, spinner) + var status string + var spinner *progress.Spinner fn := func(resp api.ProgressResponse) error { if resp.Digest != "" { - spinner.Stop() + if spinner != nil { + spinner.Stop() + } bar, ok := bars[resp.Digest] if !ok { @@ -197,7 +191,9 @@ func PushHandler(cmd *cobra.Command, args []string) error { bar.Set(resp.Completed) } else if status != resp.Status { - spinner.Stop() + if spinner != nil { + spinner.Stop() + } status = resp.Status spinner = progress.NewSpinner(status) @@ -373,13 +369,14 @@ func PullHandler(cmd *cobra.Command, args []string) error { bars := make(map[string]*progress.Bar) - status := fmt.Sprintf("pulling %s", args[0]) - spinner := progress.NewSpinner(status) - p.Add(status, spinner) + var status string + var spinner *progress.Spinner fn := func(resp api.ProgressResponse) error { if resp.Digest != "" { - spinner.Stop() + if spinner != nil { + spinner.Stop() + } bar, ok := bars[resp.Digest] if !ok { @@ -390,7 +387,9 @@ func PullHandler(cmd *cobra.Command, args []string) error { bar.Set(resp.Completed) } else if status != resp.Status { - spinner.Stop() + if spinner != nil { + spinner.Stop() + } status = resp.Status spinner = progress.NewSpinner(status) diff --git a/progress/bar.go b/progress/bar.go index 6be4ab2b..a8383f5c 100644 --- a/progress/bar.go +++ b/progress/bar.go @@ -66,7 +66,8 @@ func (b *Bar) String() string { mid.WriteString("▕") - f := termWidth - pre.Len() - suf.Len() - 2 + // add 3 extra spaces: 2 boundary characters and 1 space at the end + f := termWidth - pre.Len() - suf.Len() - 3 n := int(float64(f) * b.percent() / 100) if n > 0 { diff --git a/progress/progress.go b/progress/progress.go index 8c195be9..3c488307 100644 --- a/progress/progress.go +++ b/progress/progress.go @@ -12,9 +12,10 @@ type State interface { } type Progress struct { - mu sync.Mutex + mu sync.Mutex + w io.Writer + pos int - w io.Writer ticker *time.Ticker states []State @@ -37,6 +38,7 @@ func (p *Progress) Stop() bool { p.ticker.Stop() p.ticker = nil p.render() + fmt.Fprint(p.w, "\n") return true } @@ -50,11 +52,8 @@ func (p *Progress) StopAndClear() bool { stopped := p.Stop() if stopped { // clear the progress bar by: - // 1. for each line in the progress: - // a. move the cursor up one line - // b. clear the line for i := 0; i < p.pos; i++ { - fmt.Fprint(p.w, "\033[A\033[2K") + fmt.Fprint(p.w, "\033[A\033[2K\033[1G") } } @@ -75,17 +74,23 @@ func (p *Progress) render() error { fmt.Fprint(p.w, "\033[?25l") defer fmt.Fprint(p.w, "\033[?25h") - if p.pos > 0 { - fmt.Fprintf(p.w, "\033[%dA", p.pos) + // clear already rendered progress lines + for i := 0; i < p.pos; i++ { + if i > 0 { + fmt.Fprint(p.w, "\033[A") + } + fmt.Fprint(p.w, "\033[2K\033[1G") } - for _, state := range p.states { - fmt.Fprintln(p.w, state.String()) + // render progress lines + for i, state := range p.states { + fmt.Fprint(p.w, state.String()) + if i < len(p.states)-1 { + fmt.Fprint(p.w, "\n") + } } - if len(p.states) > 0 { - p.pos = len(p.states) - } + p.pos = len(p.states) return nil } diff --git a/progress/spinner.go b/progress/spinner.go index bc46bc02..62ed4f09 100644 --- a/progress/spinner.go +++ b/progress/spinner.go @@ -2,11 +2,8 @@ package progress import ( "fmt" - "os" "strings" "time" - - "golang.org/x/term" ) type Spinner struct { @@ -35,45 +32,28 @@ func NewSpinner(message string) *Spinner { } func (s *Spinner) String() string { - termWidth, _, err := term.GetSize(int(os.Stderr.Fd())) - if err != nil { - panic(err) - } - - var pre strings.Builder + var sb strings.Builder if len(s.message) > 0 { message := strings.TrimSpace(s.message) if s.messageWidth > 0 && len(message) > s.messageWidth { message = message[:s.messageWidth] } - fmt.Fprintf(&pre, "%s", message) - if s.messageWidth-pre.Len() >= 0 { - pre.WriteString(strings.Repeat(" ", s.messageWidth-pre.Len())) + fmt.Fprintf(&sb, "%s", message) + if s.messageWidth-sb.Len() >= 0 { + sb.WriteString(strings.Repeat(" ", s.messageWidth-sb.Len())) } - pre.WriteString(" ") + sb.WriteString(" ") } - var pad int if s.stopped.IsZero() { - // spinner has a string length of 3 but a rune length of 1 - // in order to align correctly, we need to pad with (3 - 1) = 2 spaces spinner := s.parts[s.value] - pre.WriteString(spinner) - pad = len(spinner) - len([]rune(spinner)) + sb.WriteString(spinner) + sb.WriteString(" ") } - var suf strings.Builder - fmt.Fprintf(&suf, "(%s)", s.elapsed()) - - var mid strings.Builder - f := termWidth - pre.Len() - mid.Len() - suf.Len() + pad - if f > 0 { - mid.WriteString(strings.Repeat(" ", f)) - } - - return pre.String() + mid.String() + suf.String() + return sb.String() } func (s *Spinner) start() { @@ -91,12 +71,3 @@ func (s *Spinner) Stop() { s.stopped = time.Now() } } - -func (s *Spinner) elapsed() time.Duration { - stopped := s.stopped - if stopped.IsZero() { - stopped = time.Now() - } - - return stopped.Sub(s.started).Round(time.Second) -}