diff --git a/commands/hugo.go b/commands/hugo.go index 57f2796b0..ab847c01e 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -44,6 +44,8 @@ import ( "gopkg.in/fsnotify.v1" ) +var mainSite *hugolib.Site + // userError is an error used to signal different error situations in command handling. type commandError struct { s string @@ -528,15 +530,29 @@ func getDirList() []string { func buildSite(watching ...bool) (err error) { startTime := time.Now() - site := &hugolib.Site{} - if len(watching) > 0 && watching[0] { - site.RunMode.Watching = true + if mainSite == nil { + mainSite = new(hugolib.Site) } - err = site.Build() + if len(watching) > 0 && watching[0] { + mainSite.RunMode.Watching = true + } + err = mainSite.Build() if err != nil { return err } - site.Stats() + mainSite.Stats() + jww.FEEDBACK.Printf("in %v ms\n", int(1000*time.Since(startTime).Seconds())) + + return nil +} + +func rebuildSite(changes map[string]bool) error { + startTime := time.Now() + err := mainSite.ReBuild(changes) + if err != nil { + return err + } + mainSite.Stats() jww.FEEDBACK.Printf("in %v ms\n", int(1000*time.Since(startTime).Seconds())) return nil @@ -574,6 +590,7 @@ func NewWatcher(port int) error { staticChanged := false dynamicChanged := false staticFilesChanged := make(map[string]bool) + dynamicFilesChanged := make(map[string]bool) for _, ev := range evs { ext := filepath.Ext(ev.Name) @@ -603,7 +620,11 @@ func NewWatcher(port int) error { dynamicChanged = dynamicChanged || !isstatic if isstatic { - staticFilesChanged[ev.Name] = true + if staticPath, err := helpers.MakeStaticPathRelative(ev.Name); err == nil { + staticFilesChanged[staticPath] = true + } + } else { + dynamicFilesChanged[ev.Name] = true } // add new directory to watch list @@ -680,7 +701,10 @@ func NewWatcher(port int) error { fmt.Print("\nChange detected, rebuilding site\n") const layout = "2006-01-02 15:04 -0700" fmt.Println(time.Now().Format(layout)) - utils.CheckErr(buildSite(true)) + //TODO here + + // utils.CheckErr(buildSite(true)) + rebuildSite(dynamicFilesChanged) if !BuildWatch && !viper.GetBool("DisableLiveReload") { // Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized diff --git a/hugolib/site.go b/hugolib/site.go index c5a0422ef..a1e5a7868 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -426,6 +426,78 @@ func (s *Site) Build() (err error) { return nil } +func (s *Site) ReBuild(changed map[string]bool) error { + s.timerStep("initialize rebuild") + // First we need to determine what changed + + sourceChanged := []string{} + tmplChanged := []string{} + dataChanged := []string{} + var err error + + for f := range changed { + // Need to re-read source + if strings.HasPrefix(f, s.absContentDir()) { + fmt.Println("Source changed", f) + sourceChanged = append(sourceChanged, f) + } + if strings.HasPrefix(f, s.absLayoutDir()) || strings.HasPrefix(f, s.absThemeDir()) { + fmt.Println("Template changed", f) + tmplChanged = append(tmplChanged, f) + } + if strings.HasPrefix(f, s.absDataDir()) { + fmt.Println("Data changed", f) + dataChanged = append(dataChanged, f) + } + } + + + if len(tmplChanged) > 0 { + s.prepTemplates() + s.Tmpl.PrintErrors() + s.timerStep("template prep") + } + + if len(dataChanged) > 0 { + s.ReadDataFromSourceFS() + } + + if len (sourceChanged) > 0 { + if err = s.CreatePages(); err != nil { + return err + } + s.setupPrevNext() + if err = s.BuildSiteMeta(); err != nil { + return err + } + s.timerStep("build taxonomies") + } + + if err = s.Render(); err != nil { + // Better reporting when the template is missing (commit 2bbecc7b) + jww.ERROR.Printf("Error rendering site: %s", err) + + jww.ERROR.Printf("Available templates:") + var keys []string + for _, template := range s.Tmpl.Templates() { + if name := template.Name(); name != "" { + keys = append(keys, name) + } + } + sort.Strings(keys) + for _, k := range keys { + jww.ERROR.Printf("\t%s\n", k) + } + + return nil + } else { + return err + } + + return nil +} + + func (s *Site) Analyze() error { if err := s.Process(); err != nil { return err @@ -508,6 +580,21 @@ func readData(f *source.File) (interface{}, error) { } } +func (s *Site) ReadDataFromSourceFS() error { + dataSources := make([]source.Input, 0, 2) + dataSources = append(dataSources, &source.Filesystem{Base: s.absDataDir()}) + + // have to be last - duplicate keys in earlier entries will win + themeStaticDir, err := helpers.GetThemeDataDirPath() + if err == nil { + dataSources = append(dataSources, &source.Filesystem{Base: themeStaticDir}) + } + + err = s.loadData(dataSources) + s.timerStep("load data") + return err +} + func (s *Site) Process() (err error) { s.timerStep("Go initialization") if err = s.initialize(); err != nil { @@ -517,20 +604,9 @@ func (s *Site) Process() (err error) { s.Tmpl.PrintErrors() s.timerStep("initialize & template prep") - dataSources := make([]source.Input, 0, 2) - - dataSources = append(dataSources, &source.Filesystem{Base: s.absDataDir()}) - - // have to be last - duplicate keys in earlier entries will win - themeStaticDir, err := helpers.GetThemeDataDirPath() - if err == nil { - dataSources = append(dataSources, &source.Filesystem{Base: themeStaticDir}) - } - - if err = s.loadData(dataSources); err != nil { + if err = s.ReadDataFromSourceFS(); err != nil { return } - s.timerStep("load data") if err = s.CreatePages(); err != nil { return @@ -864,7 +940,6 @@ func readCollator(s *Site, results <-chan HandledResult, errs chan<- error) { } func (s *Site) BuildSiteMeta() (err error) { - s.assembleMenus() if len(s.Pages) == 0 { @@ -929,6 +1004,7 @@ func (s *SiteInfo) createNodeMenuEntryURL(in string) string { } func (s *Site) assembleMenus() { + s.Menus = Menus{} type twoD struct { MenuName, EntryName string