❯ go-clikit
v0.10.0 pkg.go.dev ↗

packages › progress & output › progress/display

display

import "github.com/GSI-HPC/go-clikit/progress/display"

Show spans as a live tree, a counter or plain lines on standard error, and leave a summary.

since v0.1.0 imports: progress, termtextrequires: github.com/GSI-HPC/go-nodeset decisions 9 · 16 · 18 · 19 · 20 · 46
The place of progress/display in go-clikit: the packages it imports, and the packages that import it.termtextprogressdisplaycliprogressprogresstestmcpprogresseventlogfanoutfanouttestclioutdoctorfsutilcobratreecobratreetestlocalwebsessionsselocalwebtestlaunchassetspagehtmx

display draws the events of a Bus on standard error. Most programs do not call it directly: cliprogress picks a display from --progress. Use display when your program picks by a rule of its own.

reinstall exe[01-12] --progress=tty
✓ fetching the base image  5.0s  2.0 GiB at 410 MiB/s
reinstall ◦ 0:10.5
╎ reinstalling the nodes  ◉◉◉◉◉◌◌◌◌◌ 6/12 ◦ 4 running ◦ 2 queued
  ╎ ◡ exe07  2.8s/30s  POST /redfish/v1/Systems/1
  ╎ ◡ exe08  2.1s/30s  POST /redfish/v1/Systems/1
  ╎ ◡ exe09  1.4s/30s  POST /redfish/v1/Systems/1
  ╎ ◡ exe10  0.7s/30s  POST /redfish/v1/Systems/1
  ╎ ✓ exe[01-06]
The tree, in each theme.

The displays#

DisplayMake it withIt draws
Treedisplay.NewTreea few rows at the bottom of the terminal, redrawn
Counterdisplay.NewCounterone line at the bottom, redrawn
Plaindisplay.NewPlainone line for each change worth one
Summarydisplay.NewSummaryone line once the command ended

Each display shares the terminal with the command through a display.Terminal. The command’s own lines go through Terminal.Writer, and lines of other goroutines through Terminal.Lines.

clock := newExampleClock()
term := display.NewTerminal(os.Stdout, display.TerminalOptions{})
plain := display.NewPlain(term, display.PlainOptions{
	Now: clock.Now,
	Noun: func(n int) string {
		if n == 1 {
			return "1 node"
		}
		return fmt.Sprintf("%d nodes", n)
	},
})
summary := &display.Summary{}
bus := progress.NewBus(progress.BusOptions{Sinks: []progress.Sink{plain, summary}, Now: clock.Now})
ctx, command := progress.Start(progress.WithBus(context.Background(), bus), progress.KindCommand, "create cluster")

ctx, step := progress.Start(ctx, progress.KindStep, "starting the nodes",
	progress.Flagged(progress.FlagFold), progress.Total(3), progress.Limit(3))
targets := startTargets(ctx, "worker0", "worker1", "worker2")
for _, target := range targets {
	target.Run()
}
clock.Add(3 * time.Second)
failed := errors.New("container exited with 1")
targets[1].End(failed)
clock.Add(7 * time.Second)
plain.Draw()
clock.Add(2 * time.Second)
targets[0].End(nil)
targets[2].End(nil)
step.End(failed)
command.End(failed)
bus.Close()
plain.Close()
fmt.Println(summary.Line())
[0:00] create cluster › starting the nodes: start, 3 nodes
[0:03] create cluster › starting the nodes › worker1 failed (target): container exited with 1
[0:10] create cluster › starting the nodes: 1/3 done, 1 failed, 2 running
[0:12] create cluster › starting the nodes: failed in 12s: 2 ok, 1 failed
create cluster: failed in 12s: 2 ok, 1 failed
ExampleNewPlain in progress/display/example_test.go. CI runs it and checks its output.

Themes#

The zero display.Theme draws no colour. Five themes, from display.Themes or display.ParseTheme, add colours and marks. display.Theme.In draws a theme in 256 colours, in 16, or in none.

theme, err := display.ParseTheme("Tide")
fmt.Println(theme, theme.Colors(), err)
_, err = display.ParseTheme("dawn")
fmt.Println(err)
tide 256 <nil>
display: unknown theme "dawn": want none, classic, aurora, ember, neon or tide
ExampleParseTheme in progress/display/example_test.go. CI runs it and checks its output.

Fixed interfaces#

  • The text that the displays draw with the zero Theme. Programs compare it in their tests.
  • The colours and the art of a theme can change in a minor release; its name and its words cannot (decision 20).

Read more#

Displays and themes tells how the region, the writers and the folding of node sets work.