diff --git a/cmd/serve.go b/cmd/serve.go index b2bf0c71eb..28f8dafca0 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -1,6 +1,8 @@ package cmd import ( + "fmt" + "github.com/spf13/cobra" "tidbyt.dev/pixlet/server" @@ -33,6 +35,10 @@ containing multiple Starlark files and resources.`, } func serve(cmd *cobra.Command, args []string) error { + if watch && cmd.Flags().Changed("watch") { + fmt.Printf("explicitly setting --watch is unnecessary, since it's the default\n\n") + } + s, err := server.NewServer(host, port, watch, args[0], maxDuration, timeout) if err != nil { return err diff --git a/server/server.go b/server/server.go index 6ff5459674..11f0729840 100644 --- a/server/server.go +++ b/server/server.go @@ -29,7 +29,7 @@ func NewServer(host string, port int, watch bool, path string, maxDuration int, // check if path exists, and whether it is a directory or a file info, err := os.Stat(path) if err != nil { - return nil, fmt.Errorf("failed to stat %s: %w", path, err) + return nil, fmt.Errorf("stat'ing %s: %w", path, err) } var fs fs.FS @@ -43,7 +43,7 @@ func NewServer(host string, port int, watch bool, path string, maxDuration int, } fs = tools.NewSingleFileFS(path) - w = NewWatcher(filepath.Dir(path), fileChanges) + w = NewWatcher(path, fileChanges) } updatesChan := make(chan loader.Update, 100) diff --git a/server/watcher.go b/server/watcher.go index de50bbdb73..12d28c6291 100644 --- a/server/watcher.go +++ b/server/watcher.go @@ -2,9 +2,8 @@ package server import ( "fmt" - "log" + "os" "path/filepath" - "strings" "github.com/fsnotify/fsnotify" ) @@ -29,37 +28,53 @@ func NewWatcher(filename string, fileChanges chan bool) *Watcher { // If there is an error, it's returned. It's up to the caller to respawn the // watcher if it's desireable to keep watching. // -// The reason it watches a directory is becausde some editers like VIM write +// The reason it watches a directory is becausde some editors like VIM write // to a swap file and recreate the original file. So we can't simply watch the // original file, we have to watch the directory. This is also why we check both // the WRITE and CREATE events since VIM will write to a swap and then create // the file on save. VSCode does a WRITE and then a CHMOD, so tracking WRITE // catches the changes for VSCode exactly once. func (w *Watcher) Run() error { + // check if path exists, and whether it is a directory or a file + info, err := os.Stat(w.path) + if err != nil { + return fmt.Errorf("stat'ing %s: %w", w.path, err) + } + isDir := info.IsDir() + watcher, err := fsnotify.NewWatcher() if err != nil { - return fmt.Errorf("error watching for changes: %w", err) + return fmt.Errorf("watching for changes: %w", err) } defer watcher.Close() - watcher.Add(w.path) + if isDir { + watcher.Add(w.path) + } else { + watcher.Add(filepath.Dir(w.path)) + } for { select { case event, ok := <-watcher.Events: if !ok { - return fmt.Errorf("something is weird with the file watcher") + return fmt.Errorf("watcher events channel closed unexpectedly") } - log.Println(event.Name) - if strings.HasPrefix(event.Name, w.path) && shouldNotify(event.Op) { + + if !isDir && event.Name != w.path { + // if we're watching a single file, ignore changes to other files + continue + } + + if shouldNotify(event.Op) { w.fileChanges <- true } case err, ok := <-watcher.Errors: if !ok { - return fmt.Errorf("something is weird with the file watcher around error handling") + return fmt.Errorf("watcher errors channel closed unexpectedly") } - return fmt.Errorf("error in file watcher: %w", err) + return fmt.Errorf("watcher: %w", err) } } }