Skip to content

Commit 3f999ff

Browse files
committed
Safe and dynamically resizable progress bar
Progress bar changes width on terminal resize (it reads termwidth every tick). Progress bar will not print if termwidth is < 30 chars (arbitrary limit, but rather sane). It also guards against the barwidth being < 0, which would cause a panic.
1 parent 4a02ff9 commit 3f999ff

File tree

1 file changed

+47
-16
lines changed

1 file changed

+47
-16
lines changed

gincmd/common.go

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,61 @@ func printProgressWithBar(statuschan <-chan git.RepoFileStatus, nitems int) (fil
138138
return printProgressOutput(statuschan)
139139
}
140140
ndigits := len(fmt.Sprintf("%d", nitems))
141-
dfmt := fmt.Sprintf("%%%dd/%%%dd", ndigits, ndigits)
141+
dfmt := fmt.Sprintf("%%%dd/%%%dd", ndigits, ndigits) // dynamic formatting string adapts to number of digits in item count
142142
filesuccess = make(map[string]bool)
143-
var barratio float64
144-
var ncomplt int
145-
linewidth := termwidth()
146-
if linewidth > 80 {
147-
linewidth = 80
148-
}
149-
barwidth := linewidth - (5 + ndigits*2)
150-
barratio = float64(barwidth) / float64(nitems)
143+
144+
// closure binds ndigits and nitems but keeps bar width dynamic so it can
145+
// adapt to terminal resizing -- introduces a few operations per refresh,
146+
// but makes the bar printing much nicer
147+
printbar := func(completed int) int {
148+
linewidth := termwidth()
149+
if linewidth > 80 {
150+
linewidth = 80
151+
} else if linewidth < 30 {
152+
// Skip bar printing for very small terminals
153+
fmt.Println()
154+
return 0
155+
}
156+
fullbarwidth := linewidth - (5 + ndigits*2)
157+
if fullbarwidth < 0 {
158+
// Again, skip bar printing if ndigits is so large that a bar would
159+
// have negative width. Since we skip printing the bar if the
160+
// termwidth is < 30, this can only happen when processing more
161+
// than 1e13 files, but if we ever change the min width to
162+
// something smaller than 30 (or make it dynamic somehow), this
163+
// guard will be useful.
164+
fmt.Println()
165+
return 0
166+
}
167+
barratio := float64(fullbarwidth) / float64(nitems)
168+
169+
complsigns := int(math.Floor(float64(completed) * barratio))
170+
blocks := strings.Repeat("=", complsigns)
171+
blanks := strings.Repeat(" ", fullbarwidth-complsigns)
172+
dprg := fmt.Sprintf(dfmt, completed, nitems)
173+
fmt.Printf("\n [%s%s] %s\r", blocks, blanks, dprg)
174+
return linewidth
175+
}
176+
151177
outline := new(bytes.Buffer)
152178
outappend := func(part string) {
153179
if len(part) > 0 {
154180
outline.WriteString(part)
155181
outline.WriteString(" ")
156182
}
157183
}
184+
158185
printed := false
186+
prevlinewidth := 0
187+
ncompleted := 0
159188
for stat := range statuschan {
160-
ncomplt++
189+
ncompleted++
190+
if ncompleted > nitems {
191+
// BUG: Not sure when this occurs, but it's been happening in the
192+
// CI environment for some remove-content calls and I haven't been
193+
// able to reproduce - AK, 2019-07-07
194+
nitems = ncompleted
195+
}
161196
outline.Reset()
162197
outline.WriteString(" ")
163198
outappend(stat.State)
@@ -172,13 +207,9 @@ func printProgressWithBar(statuschan <-chan git.RepoFileStatus, nitems int) (fil
172207
filesuccess[stat.FileName] = false
173208
}
174209
newprint := outline.String()
175-
fmt.Printf("\r%s\r", strings.Repeat(" ", linewidth)) // clear the line
210+
fmt.Printf("\r%s\r", strings.Repeat(" ", prevlinewidth)) // clear the line
176211
fmt.Fprint(color.Output, newprint)
177-
complsigns := int(math.Floor(float64(ncomplt) * barratio))
178-
blocks := strings.Repeat("=", complsigns)
179-
blanks := strings.Repeat(" ", barwidth-complsigns)
180-
dprg := fmt.Sprintf(dfmt, ncomplt, nitems)
181-
fmt.Printf("\n [%s%s] %s\r", blocks, blanks, dprg)
212+
prevlinewidth = printbar(ncompleted)
182213
printed = true
183214
}
184215
if !printed {

0 commit comments

Comments
 (0)