From df489b4712dd59b097de0d2fb84cfac7a013f5cd Mon Sep 17 00:00:00 2001 From: bep Date: Wed, 10 Sep 2014 12:21:22 +0200 Subject: [PATCH] Enable soft livereload of CSS and images Prior to this commit a dummy JavaScript filename was sent to LiveReload when changing a static file (CSS, image etc.), forcing a full browser reload of the page. This commit fixes this by sending the relative file path of the changed static resource, enabling partial live reloading for CSS- and image-changes. If more than one static file happens to end up in the same changeevent-batch, it will fall back to do a full refresh. To enable this logic, the change events with names ending with ".goutputstream*" is now filtered out as temporary. Changes in dynamic content behaves like before. Issue #490 --- commands/hugo.go | 20 ++++++++++++++++++-- helpers/helpers_test.go | 23 +++++++++++++++++++++++ helpers/path.go | 18 ++++++++++++++++++ livereload/livereload.go | 7 ++++++- 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/commands/hugo.go b/commands/hugo.go index e95b0c0d8..3684d3e66 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -312,10 +312,11 @@ func NewWatcher(port int) error { static_changed := false dynamic_changed := false + static_files_changed := make(map[string]bool) for _, ev := range evs { ext := filepath.Ext(ev.Name) - istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp") + istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp") || (strings.HasPrefix(ext, ".goutputstream")) if istemp { continue } @@ -328,6 +329,12 @@ func NewWatcher(port int) error { static_changed = static_changed || isstatic dynamic_changed = dynamic_changed || !isstatic + if isstatic { + if staticPath, err := helpers.MakeStaticPathRelative(ev.Name); err == nil { + static_files_changed[staticPath] = true + } + } + // add new directory to watch list if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() { if ev.IsCreate() { @@ -342,7 +349,16 @@ func NewWatcher(port int) error { if !viper.GetBool("DisableLiveReload") { // Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized - livereload.ForceRefresh() + + // force refresh when more than one file + if len(static_files_changed) == 1 { + for path := range static_files_changed { + livereload.RefreshPath(path) + } + + } else { + livereload.ForceRefresh() + } } } diff --git a/helpers/helpers_test.go b/helpers/helpers_test.go index ad041cc0a..b1ce2a93c 100644 --- a/helpers/helpers_test.go +++ b/helpers/helpers_test.go @@ -122,3 +122,26 @@ func TestMakePermalink(t *testing.T) { } } } + +func TestMakePathRelative(t *testing.T) { + type test struct { + inPath, path1, path2, output string + } + + data := []test{ + {"/abc/bcd/ab.css", "/abc/bcd", "/bbc/bcd", "/ab.css"}, + {"/abc/bcd/ab.css", "/abcd/bcd", "/abc/bcd", "/ab.css"}, + } + + for i, d := range data { + output, _ := MakePathRelative(d.inPath, d.path1, d.path2) + if d.output != output { + t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output) + } + } + _, error := MakePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f") + + if error == nil { + t.Errorf("Test #%d failed. Expected error") + } +} diff --git a/helpers/path.go b/helpers/path.go index 7f99bfca0..952c5428f 100644 --- a/helpers/path.go +++ b/helpers/path.go @@ -14,6 +14,7 @@ package helpers import ( + "errors" "fmt" "io" "os" @@ -130,6 +131,23 @@ func AbsPathify(inPath string) string { return filepath.Clean(filepath.Join(viper.GetString("WorkingDir"), inPath)) } +func MakeStaticPathRelative(inPath string) (string, error) { + staticDir := AbsPathify(viper.GetString("StaticDir")) + themeStaticDir := AbsPathify("themes/"+viper.GetString("theme")) + "/static/" + + return MakePathRelative(inPath, staticDir, themeStaticDir) +} + +func MakePathRelative(inPath string, possibleDirectories ...string) (string, error) { + + for _, currentPath := range possibleDirectories { + if strings.HasPrefix(inPath, currentPath) { + return strings.TrimPrefix(inPath, currentPath), nil + } + } + return inPath, errors.New("Can't extract relative path, unknown prefix") +} + func Filename(in string) (name string) { name, _ = FileAndExt(in) return diff --git a/livereload/livereload.go b/livereload/livereload.go index 9fb24696d..29a6168a5 100644 --- a/livereload/livereload.go +++ b/livereload/livereload.go @@ -39,7 +39,12 @@ func Initialize() { func ForceRefresh() { // Tell livereload a js file changed to force a hard refresh - wsHub.broadcast <- []byte(`{"command":"reload","path":"/x.js","originalPath":"","liveCSS":true}`) + RefreshPath("/x.js") +} + +func RefreshPath(s string) { + // Tell livereload a file has changed - will force a hard refresh if not CSS or an image + wsHub.broadcast <- []byte(`{"command":"reload","path":"` + s + "\"" + `,"originalPath":"","liveCSS":true,"liveImg":true}`) } func ServeJS(w http.ResponseWriter, r *http.Request) {