@@ -19,6 +19,7 @@ import (
19
19
"errors"
20
20
"fmt"
21
21
"io"
22
+ "math"
22
23
"os"
23
24
"runtime"
24
25
"strconv"
@@ -29,6 +30,7 @@ import (
29
30
30
31
"github.com/fatih/color"
31
32
"github.com/mattn/go-isatty"
33
+ "golang.org/x/term"
32
34
)
33
35
34
36
// errInvalidColor is returned when attempting to set an invalid color
@@ -439,13 +441,23 @@ func (s *Spinner) erase() {
439
441
return
440
442
}
441
443
444
+ numberOfLinesToErase := computeNumberOfLinesNeededToPrintString (s .lastOutputPlain )
445
+
442
446
// Taken from https://en.wikipedia.org/wiki/ANSI_escape_code:
443
447
// \r - Carriage return - Moves the cursor to column zero
444
448
// \033[K - Erases part of the line. If n is 0 (or missing), clear from
445
449
// cursor to the end of the line. If n is 1, clear from cursor to beginning
446
450
// of the line. If n is 2, clear entire line. Cursor position does not
447
451
// change.
448
- fmt .Fprintf (s .Writer , "\r \033 [K" )
452
+ // \033[F - Go to the beginning of previous line
453
+ eraseCodeString := strings.Builder {}
454
+ // current position is at the end of the last printed line. Start by erasing current line
455
+ eraseCodeString .WriteString ("\r \033 [K" ) // start by erasing current line
456
+ for i := 1 ; i < numberOfLinesToErase ; i ++ {
457
+ // For each additional lines, go up one line and erase it.
458
+ eraseCodeString .WriteString ("\033 [F\033 [K" )
459
+ }
460
+ fmt .Fprintf (s .Writer , eraseCodeString .String ())
449
461
s .lastOutputPlain = ""
450
462
}
451
463
@@ -473,3 +485,29 @@ func GenerateNumberSequence(length int) []string {
473
485
func isRunningInTerminal () bool {
474
486
return isatty .IsTerminal (os .Stdout .Fd ())
475
487
}
488
+
489
+ func computeNumberOfLinesNeededToPrintString (linePrinted string ) int {
490
+ terminalWidth := math .MaxInt // assume infinity by default to keep behaviour consistent with what we had before
491
+ if term .IsTerminal (0 ) {
492
+ if width , _ , err := term .GetSize (0 ); err == nil {
493
+ terminalWidth = width
494
+ }
495
+ }
496
+ return computeNumberOfLinesNeededToPrintStringInternal (linePrinted , terminalWidth )
497
+ }
498
+
499
+ func computeNumberOfLinesNeededToPrintStringInternal (linePrinted string , maxLineWidth int ) int {
500
+ if linePrinted == "" {
501
+ // empty string will necessarily take one line
502
+ return 1
503
+ }
504
+ idxOfNewline := strings .Index (linePrinted , "\n " )
505
+ if idxOfNewline < 0 {
506
+ // we use utf8.RunCountInString() in place of len() because the string contains "complex" unicode chars that
507
+ // might be represented by multiple individual bytes (typically spinner char)
508
+ return int (math .Ceil (float64 (utf8 .RuneCountInString (linePrinted )) / float64 (maxLineWidth )))
509
+ } else {
510
+ return computeNumberOfLinesNeededToPrintStringInternal (linePrinted [:idxOfNewline ], maxLineWidth ) +
511
+ computeNumberOfLinesNeededToPrintStringInternal (linePrinted [idxOfNewline + 1 :], maxLineWidth )
512
+ }
513
+ }
0 commit comments