diff --git a/commands/hugo.go b/commands/hugo.go index 8cfbc734..ee1d6f27 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -15,11 +15,11 @@ package commands 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 buildSite(watching ...bool) (err error) { } 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 @@ func NewWatcher(port int) error { 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) + } + + 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)) } - watchChange(ev) - // TODO add newly created directories to the watch list case err := <-watcher.Error: if err != nil { fmt.Println("error:", err) @@ -183,12 +224,6 @@ func NewWatcher(port int) error { } }() - for _, d := range getDirList() { - if d != "" { - _ = watcher.Watch(d) - } - } - if port > 0 { go serve(port) } @@ -196,22 +231,3 @@ func NewWatcher(port int) error { 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)) - } - } -} diff --git a/watcher/batcher.go b/watcher/batcher.go new file mode 100644 index 00000000..a22ad8b4 --- /dev/null +++ b/watcher/batcher.go @@ -0,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() +}