Skip to content

Instantly share code, notes, and snippets.

@lox
Created January 2, 2022 02:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lox/2e33c4dfbd3bb524c3dca42fa69b615f to your computer and use it in GitHub Desktop.
Save lox/2e33c4dfbd3bb524c3dca42fa69b615f to your computer and use it in GitHub Desktop.
Thinking on an API for spinner/progress bar library
// Spinners and Progress bars are the same thing.
// They are assembled with a go template with some built in functions or templated strings
// for things like messages before and after.
// Render a spinner that looks like:
// ⡀ Loading blah... [3s]
spinner := indicate.New(context.Background(),
`{{ spinner "⠁⠂⠄⡀⢀⠠⠐⠈ " }} {{ template "message" }} [{{ elapsed }}]`).
WithDefaults(indicate.SpinnerDefaults).
Start()
for i := 0 ; i < 100; i++ {
spinner.Update("message", "Loading...")
spinner.Increment()
}
spinner.FinishAndClear()
// Render a progress bar that looks like
// Crawling... 45m [########> ] 3s
progress := indicate.New(context.Background(),
`{{ template "prefix" }} {{ elapsed }} {{ progress "[#> ]" }} {{ eta }}`).
WithTotal(100).
WithTemplateFunc("prefix", func(state indicate.State) string {
return fmt.Sprintf("[%d/%d]", state.Current, state.Total)
}).
Start()
for i := 0 ; i < 100; i++ {
progress.Update("prefix", "Crawling... ")
progress.Increment()
}
progress.FinishAndClear()
// Printing and re-rendering the indicator after the output
progress.Println("blah")
@lox
Copy link
Author

lox commented Jan 2, 2022

@lox
Copy link
Author

lox commented Jan 2, 2022

@lox
Copy link
Author

lox commented Jan 2, 2022

@lox
Copy link
Author

lox commented Jan 2, 2022

@lox
Copy link
Author

lox commented Jan 2, 2022

package main

import (
	"fmt"
	"log"
	"time"

	"golang.org/x/term"
)

// For the record here's what's used (\033 is ESC):
//
// `ESC 7`           is DECSC   (Save Cursor)
// `ESC 8`           is DECRC   (Restore Cursor)
// `ESC [ Pn ; Pn r` is DECSTBM (Set Top and Bottom Margins)
// `ESC [ Pn A`      is CUU     (Cursor Up)
// `ESC [ Pn ; Pn f` is HVP     (Horizontal and Vertical Position)
// `ESC [ Ps K`      is EL      (Erase In Line)

func main() {
	var height int
	var err error

	_, height, err = term.GetSize(0)
	if err != nil {
		log.Fatal(err)
	}

	// go func() {
	// 	// SIGWINCH syscall to react on window size changes.
	// 	c := make(chan os.Signal, 1)
	// 	signal.Notify(c, syscall.SIGWINCH)

	// 	for range c {
	// 		var err error
	// 		_, height, err = term.GetSize(0)
	// 		if err != nil {
	// 			panic(err)
	// 		}
	// 		fmt.Printf("resize detected, new height is %d", height)
	// 	}
	// }()

	fmt.Print("\033[?25l")               // Turn off cursor
	fmt.Println()                        // Ensure the last line is available.
	fmt.Print("\0337")                   // Save cursor position
	fmt.Printf("\033[0;%dr\n", height-1) // Reserve the bottom line
	fmt.Printf("\033[%d;0f", height)     // Move cursor to the bottom margin
	fmt.Print("\033[1A")                 // Move up one line

	var bar Bar
	bar.NewOption(0, 100)

	for i := 0; i <= 100; i++ {
		fmt.Println("Hello!")
		time.Sleep(100 * time.Millisecond)
		fmt.Print("\0337")               // Save cursor position
		fmt.Printf("\033[%d;0f", height) // Move cursor to the bottom margin
		fmt.Print("\033[2K")             // Clear line
		bar.Draw(int64(i))
		fmt.Print("\0338") // Restore cursor position
	}
	bar.Finish()
}

func monitorResize() {

}

type Bar struct {
	Percent int64  // percentage
	Cur     int64  // current progress location
	Total   int64  // total progress
	Rate    string // progress bar
	Graph   string // display symbols
}

func (b *Bar) NewOption(start, total int64) {
	b.Cur = start
	b.Total = total
	if b.Graph == "" {
		b.Graph = "█"
	}
	b.Percent = b.CalculatePercent()
	for i := 0; i < int(b.Percent); i += 2 {
		b.Rate += b.Graph // initialize the progress bar position
	}
}

func (b *Bar) CalculatePercent() int64 {
	return int64(float32(b.Cur) / float32(b.Total) * 100)
}

func (b *Bar) Draw(cur int64) {
	b.Cur = cur
	last := b.Percent
	b.Percent = b.CalculatePercent()
	if b.Percent != last && b.Percent%2 == 0 {
		b.Rate += b.Graph
	}
	fmt.Printf("[%-50s]%3d%% %8d/%d", b.Rate, b.Percent, b.Cur, b.Total)
}

func (b *Bar) Finish() {
	fmt.Println()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment