Show the progress rate over the last 5 seconds, and what a phase is doing after 10 seconds with no item done (closes #125)
check / check (push) Canceled after 0s

A display's rate was the phase's average since it started, so after a
long wait a burst of files read as a steady rate of over 1,000 files a
second. It now covers the items completed over the last 5 seconds, or
since the line before off a terminal, and falls to 0 while nothing
completes; the ETA still comes from the average. Once no item has
completed for 10 seconds, the line says so and what the phase is doing,
a phrase each phase gives where it is defined. A narrow terminal leaves
that out after the rate, keeping the phase, progress, elapsed time and
ETA whole. The tests set the display's clock by hand.

Judgement call: the line reads "no files finished", in the phase's unit.
Judgement call: trees' group phase says "grouping directories".

Model: opus-5-5
This commit is contained in:
2026-10-16 21:00:38 +00:00
parent e9e09effc6
commit 98bca296c5
9 changed files with 403 additions and 99 deletions
+14 -3
View File
@@ -88,6 +88,15 @@ type fileMeta struct {
// names it.
const scanUnit = "files"
// readingFiles is what the display of a phase that reads files says it
// is doing once no file has been read for stallTime.
const readingFiles = "reading files"
// searchingDatabase is what the display of the content phase's checks
// says it waits for until the search returns its first record, and what
// it says it is doing once no record has been checked for stallTime.
const searchingDatabase = "searching the database"
// scanPhase names one phase of a scan with its place among all of them,
// as "[2/5] walk", for the phase's progress display. The list here is
// the one place the phases are counted.
@@ -446,7 +455,7 @@ func (s *scanState) loadIndex(ctx context.Context, roots []string) error {
// first gives the display its total.
prog := newProgress(progressParams{
label: scanPhase("load"), unit: scanUnit, total: -1,
waitingFor: countingRecords,
waitingFor: countingRecords, doing: readingDatabase,
})
defer prog.finish()
@@ -497,7 +506,7 @@ func (s *scanState) walkPhase(
prog := newProgress(progressParams{
label: scanPhase("walk"), unit: scanUnit, total: estimate,
estimate: true,
estimate: true, doing: "listing directories",
})
var (
@@ -689,6 +698,7 @@ func (s *scanState) readRuns(ctx context.Context, workers int,
prog := newProgress(progressParams{
label: scanPhase(label), unit: scanUnit, total: int64(len(runs)),
doing: readingFiles,
})
defer prog.finish()
@@ -778,6 +788,7 @@ func (s *scanState) updatePhase(ctx context.Context) error {
total := len(s.batch) + len(s.sentinels) + len(deletes)
prog := newProgress(progressParams{
label: scanPhase("update"), unit: scanUnit, total: int64(total),
doing: "writing to the database",
})
defer prog.finish()
@@ -866,7 +877,7 @@ func (s *scanState) contentCandidates(
// the search returns, and so do their rate and ETA.
prog := newProgress(progressParams{
label: scanPhase("content"), unit: scanUnit, total: -1,
waitingFor: "searching the database",
waitingFor: searchingDatabase, doing: searchingDatabase,
})
defer prog.finish()