@@ -20,6 +20,7 @@ import (
2020 "strings"
2121 "sync"
2222 "sync/atomic"
23+ "time"
2324
2425 "github.com/bep/logg"
2526 "github.com/gohugoio/hugo/cache/dynacache"
@@ -33,9 +34,11 @@ import (
3334 "github.com/gohugoio/hugo/output"
3435 "github.com/gohugoio/hugo/parser/metadecoders"
3536
37+ "github.com/gohugoio/hugo/common/htime"
3638 "github.com/gohugoio/hugo/common/hugo"
3739 "github.com/gohugoio/hugo/common/maps"
3840 "github.com/gohugoio/hugo/common/para"
41+ "github.com/gohugoio/hugo/common/terminal"
3942 "github.com/gohugoio/hugo/common/types"
4043 "github.com/gohugoio/hugo/hugofs"
4144
@@ -98,12 +101,29 @@ type HugoSites struct {
98101 numWorkersSites int
99102 numWorkers int
100103
104+ buildProgress progressReporter
101105 * fatalErrorHandler
102106 * buildCounters
103107 // Tracks invocations of the Build method.
104108 buildCounter atomic.Uint64
105109}
106110
111+ type progressReporter struct {
112+ mu sync.Mutex
113+ t time.Time
114+ progress float64
115+ queue []func (* progressReporter ) (state terminal.ProgressState , progress float64 )
116+ state terminal.ProgressState
117+ renderProgressStart float64
118+ numPagesToRender atomic.Uint64
119+ }
120+
121+ func (p * progressReporter ) Start () {
122+ p .mu .Lock ()
123+ defer p .mu .Unlock ()
124+ p .t = htime .Now ()
125+ }
126+
107127// ShouldSkipFileChangeEvent allows skipping filesystem event early before
108128// the build is started.
109129func (h * HugoSites ) ShouldSkipFileChangeEvent (ev fsnotify.Event ) bool {
@@ -254,6 +274,52 @@ func (h *HugoSites) codeownersForPage(p page.Page) ([]string, error) {
254274 return h .codeownerInfo .forPage (p ), nil
255275}
256276
277+ func (h * HugoSites ) reportProgress (f func (* progressReporter ) (state terminal.ProgressState , progress float64 )) {
278+ h .buildProgress .mu .Lock ()
279+ defer h .buildProgress .mu .Unlock ()
280+
281+ if h .buildProgress .t .IsZero () {
282+ // Not started yet, queue it up and return.
283+ h .buildProgress .queue = append (h .buildProgress .queue , f )
284+ return
285+ }
286+
287+ handleOne := func (ff func (* progressReporter ) (state terminal.ProgressState , progress float64 )) {
288+ state , progress := ff (& h .buildProgress )
289+
290+ if h .buildProgress .progress > 0 && h .buildProgress .state == state && progress <= h .buildProgress .progress {
291+ // Only report progress forward.
292+ return
293+ }
294+
295+ h .buildProgress .state = state
296+ h .buildProgress .progress = progress
297+ terminal .ReportProgress (h .Log .StdOut (), state , h .buildProgress .progress )
298+ }
299+
300+ // Drain queue first.
301+ for _ , ff := range h .buildProgress .queue {
302+ handleOne (ff )
303+ }
304+ h .buildProgress .queue = nil
305+
306+ handleOne (f )
307+ }
308+
309+ func (h * HugoSites ) onPageRender () {
310+ pagesRendered := h .buildCounters .pageRenderCounter .Add (1 )
311+ if pagesRendered <= 100 || pagesRendered % 10 == 0 {
312+ h .reportProgress (func (pr * progressReporter ) (terminal.ProgressState , float64 ) {
313+ if pr .renderProgressStart == 0.0 && pr .state == terminal .ProgressNormal {
314+ pr .renderProgressStart = h .buildProgress .progress
315+ }
316+ numPagesToRender := pr .numPagesToRender .Load ()
317+ pagesProgress := pr .renderProgressStart + float64 (pagesRendered )/ float64 (numPagesToRender )* (1.0 - pr .renderProgressStart )
318+ return terminal .ProgressNormal , pagesProgress
319+ })
320+ }
321+ }
322+
257323func (h * HugoSites ) pickOneAndLogTheRest (errors []error ) error {
258324 if len (errors ) == 0 {
259325 return nil
0 commit comments