Improve .Content vs shortcodes
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 19 Apr 2018 10:04:34 +0000 (12:04 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 19 Apr 2018 12:46:50 +0000 (14:46 +0200)
For the content from other pages in shortcodes there are some chicken and
egg dependencies that is hard to get around. But we can improve on this  by preparing the pages in a certain order:

 1. The headless bundles goes first. These are page typically page and image collections..
 2. Leaf bundles
 3. Regular single pages
 4. Branch bundles

Fixes #4632

hugolib/hugo_sites.go
hugolib/hugo_sites_build.go
hugolib/page.go
hugolib/page_bundler_handlers.go
hugolib/site.go

index 7eec98329515a22abd9b915d93a640e7d75f9f7b..6a71fd97b1cdf55a3ba136d262cf978681197173 100644 (file)
@@ -559,37 +559,82 @@ func (h *HugoSites) setupTranslations() {
        }
 }
 
-func (s *Site) preparePagesForRender(cfg *BuildCfg) {
-
-       pageChan := make(chan *Page)
-       wg := &sync.WaitGroup{}
+type pagesRenderPreparer struct {
+       numWorkers int
+       s          *Site
+       cfg        *BuildCfg
+       wg         *sync.WaitGroup
+       pages      chan *Page
+}
 
+func newStartedRenderPreparator(s *Site, cfg *BuildCfg) *pagesRenderPreparer {
        numWorkers := getGoMaxProcs() * 4
+       pp := &pagesRenderPreparer{
+               s:          s,
+               cfg:        cfg,
+               numWorkers: numWorkers,
+               wg:         &sync.WaitGroup{},
+               pages:      make(chan *Page),
+       }
 
-       for i := 0; i < numWorkers; i++ {
-               wg.Add(1)
-               go func(pages <-chan *Page, wg *sync.WaitGroup) {
-                       defer wg.Done()
-                       for p := range pages {
-                               if err := p.prepareForRender(cfg); err != nil {
-                                       s.Log.ERROR.Printf("Failed to prepare page %q for render: %s", p.BaseFileName(), err)
+       pp.start()
+       return pp
+}
+
+func (pp *pagesRenderPreparer) start() {
+       for i := 0; i < pp.numWorkers; i++ {
+               pp.wg.Add(1)
+               go func() {
+                       defer pp.wg.Done()
+                       for p := range pp.pages {
+                               if err := p.prepareForRender(pp.cfg); err != nil {
+                                       pp.s.Log.ERROR.Printf("Failed to prepare page %q for render: %s", p.BaseFileName(), err)
 
                                }
                        }
-               }(pageChan, wg)
+               }()
        }
+}
 
-       for _, p := range s.Pages {
-               pageChan <- p
-       }
+func (pp *pagesRenderPreparer) add(p *Page) {
+       pp.pages <- p
+}
 
+func (pp *pagesRenderPreparer) done() {
+       close(pp.pages)
+       pp.wg.Wait()
+}
+
+func (s *Site) preparePagesForRender(cfg *BuildCfg) {
+
+       // For the content from other pages in shortcodes there are some chicken and
+       // egg dependencies that is hard to get around. But we can improve on this
+       // by preparing the pages in a certain order.
+       // So the headless pages goes first. These are typically collection of
+       // pages and images etc. used by others.
+       batch := newStartedRenderPreparator(s, cfg)
        for _, p := range s.headlessPages {
-               pageChan <- p
+               batch.add(p)
        }
 
-       close(pageChan)
+       batch.done()
+
+       // Then the rest in the following order:
+       order := []bundleDirType{bundleLeaf, bundleNot, bundleBranch}
 
-       wg.Wait()
+       for _, tp := range order {
+               batch = newStartedRenderPreparator(s, cfg)
+               for _, p := range s.Pages {
+                       // sanity check
+                       if p.bundleType < 0 || p.bundleType > bundleBranch {
+                               panic("unknown bundle type")
+                       }
+                       if p.bundleType == tp {
+                               batch.add(p)
+                       }
+               }
+               batch.done()
+       }
 
 }
 
index 1c4ee7b632ce2f469a870fae0ca8d07cca8932a2..dcff4b3b25c1d1efaf4cc2945df2b0e47d962f7a 100644 (file)
@@ -224,6 +224,7 @@ func (h *HugoSites) render(config *BuildCfg) error {
                s.initRenderFormats()
                for i, rf := range s.renderFormats {
                        s.rc = &siteRenderingContext{Format: rf}
+
                        s.preparePagesForRender(config)
 
                        if !config.SkipRender {
index ebd7a3a2a9b2200c5aa4ae9c728c7e8a80db6c17..d0951bff76c09fc3c542c9c8fe034d892e0a4b8a 100644 (file)
@@ -243,6 +243,8 @@ type Page struct {
        // 3. But you can get it via .Site.GetPage
        headless bool
 
+       bundleType bundleDirType
+
        layoutDescriptor output.LayoutDescriptor
 
        scratch *Scratch
index c22b719d193a181d9ee73bdb9b77d951158033f1..b38a3c35adf3f2a57c8d9f61deaa24f300258eda 100644 (file)
@@ -218,6 +218,8 @@ func (c *contentHandlers) parsePage(h contentHandler) contentHandler {
                ctx.currentPage = p
 
                if ctx.bundle != nil {
+                       p.bundleType = ctx.bundle.tp
+
                        // Add the bundled files
                        for _, fi := range ctx.bundle.resources {
                                childCtx := ctx.childCtx(fi)
index 262bf2d4dd313bbe0909523becc2d552ead7074c..820a2c829d1ffce935615c3c7164b8a4d2b654a5 100644 (file)
@@ -1865,14 +1865,15 @@ func getGoMaxProcs() int {
 
 func (s *Site) newNodePage(typ string, sections ...string) *Page {
        p := &Page{
-               language: s.Language,
-               pageInit: &pageInit{},
-               Kind:     typ,
-               Source:   Source{File: &source.FileInfo{}},
-               Data:     make(map[string]interface{}),
-               Site:     &s.Info,
-               sections: sections,
-               s:        s}
+               bundleType: bundleBranch,
+               language:   s.Language,
+               pageInit:   &pageInit{},
+               Kind:       typ,
+               Source:     Source{File: &source.FileInfo{}},
+               Data:       make(map[string]interface{}),
+               Site:       &s.Info,
+               sections:   sections,
+               s:          s}
 
        p.outputFormats = p.s.outputFormats[p.Kind]