shithub: hugo

Download patch

ref: 8d80f9b39e42dd5b61af052979a7c04dc8edf851
parent: 1979f7d9c7d047340c9205f0d3e6d8393d498f9c
author: Egon Elbre <[email protected]>
date: Sun Dec 15 12:19:22 EST 2013

Added batching behavior for page building.

Quite often file watcher gets many changes and each change triggered a
build. One build per second should be sufficient. Also added tracking for
new folders.

--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -15,11 +15,11 @@
 
 import (
 	"fmt"
-	"github.com/howeyc/fsnotify"
 	"github.com/mostafah/fsync"
 	"github.com/spf13/cobra"
 	"github.com/spf13/hugo/hugolib"
 	"github.com/spf13/hugo/utils"
+	"github.com/spf13/hugo/watcher"
 	"github.com/spf13/nitro"
 	"os"
 	"path/filepath"
@@ -155,7 +155,7 @@
 }
 
 func NewWatcher(port int) error {
-	watcher, err := fsnotify.NewWatcher()
+	watcher, err := watcher.New(1 * time.Second)
 	var wg sync.WaitGroup
 
 	if err != nil {
@@ -166,15 +166,56 @@
 	defer watcher.Close()
 
 	wg.Add(1)
+
+	for _, d := range getDirList() {
+		if d != "" {
+			_ = watcher.Watch(d)
+		}
+	}
+
 	go func() {
 		for {
 			select {
-			case ev := <-watcher.Event:
+			case evs := <-watcher.Event:
 				if Verbose {
-					fmt.Println(ev)
+					fmt.Println(evs)
 				}
-				watchChange(ev)
-				// TODO add newly created directories to the watch list
+
+				static_changed := false
+				dynamic_changed := false
+
+				for _, ev := range evs {
+					ext := filepath.Ext(ev.Name)
+					istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp")
+					if istemp {
+						continue
+					}
+					// renames are always followed with Create/Modify
+					if ev.IsRename() {
+						continue
+					}
+
+					isstatic := strings.HasPrefix(ev.Name, Config.GetAbsPath(Config.StaticDir))
+					static_changed = static_changed || isstatic
+					dynamic_changed = dynamic_changed || !isstatic
+
+					// add new directory to watch list
+					if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() {
+						if ev.IsCreate() {
+							watcher.Watch(ev.Name)
+						}
+					}
+				}
+
+				if static_changed {
+					fmt.Println("Static file changed, syncing\n")
+					utils.CheckErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", Config.GetAbsPath(Config.PublishDir)))
+				}
+
+				if dynamic_changed {
+					fmt.Println("Change detected, rebuilding site\n")
+					utils.StopOnErr(buildSite(true))
+				}
 			case err := <-watcher.Error:
 				if err != nil {
 					fmt.Println("error:", err)
@@ -183,12 +224,6 @@
 		}
 	}()
 
-	for _, d := range getDirList() {
-		if d != "" {
-			_ = watcher.Watch(d)
-		}
-	}
-
 	if port > 0 {
 		go serve(port)
 	}
@@ -195,23 +230,4 @@
 
 	wg.Wait()
 	return nil
-}
-
-func watchChange(ev *fsnotify.FileEvent) {
-	ext := filepath.Ext(ev.Name)
-	// ignore temp files
-	istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp")
-	if istemp {
-		return
-	}
-
-	if strings.HasPrefix(ev.Name, Config.GetAbsPath(Config.StaticDir)) {
-		fmt.Println("Static file changed, syncing\n")
-		utils.CheckErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", Config.GetAbsPath(Config.PublishDir)))
-	} else {
-		if !ev.IsRename() { // Rename is always accompanied by a create or modify
-			fmt.Println("Change detected, rebuilding site\n")
-			utils.StopOnErr(buildSite(true))
-		}
-	}
 }
--- /dev/null
+++ b/watcher/batcher.go
@@ -1,0 +1,56 @@
+package watcher
+
+import (
+	"github.com/howeyc/fsnotify"
+	"time"
+)
+
+type Batcher struct {
+	*fsnotify.Watcher
+	interval time.Duration
+	done     chan struct{}
+
+	Event chan []*fsnotify.FileEvent // Events are returned on this channel
+}
+
+func New(interval time.Duration) (*Batcher, error) {
+	watcher, err := fsnotify.NewWatcher()
+
+	batcher := &Batcher{}
+	batcher.Watcher = watcher
+	batcher.interval = interval
+	batcher.done = make(chan struct{}, 1)
+	batcher.Event = make(chan []*fsnotify.FileEvent, 1)
+
+	if err == nil {
+		go batcher.run()
+	}
+
+	return batcher, err
+}
+
+func (b *Batcher) run() {
+	tick := time.Tick(b.interval)
+	evs := make([]*fsnotify.FileEvent, 0)
+OuterLoop:
+	for {
+		select {
+		case ev := <-b.Watcher.Event:
+			evs = append(evs, ev)
+		case <-tick:
+			if len(evs) == 0 {
+				continue
+			}
+			b.Event <- evs
+			evs = make([]*fsnotify.FileEvent, 0)
+		case <-b.done:
+			break OuterLoop
+		}
+	}
+	close(b.done)
+}
+
+func (b *Batcher) Close() {
+	b.done <- struct{}{}
+	b.Watcher.Close()
+}