diff --git a/moonshark.go b/moonshark.go index ad53b51..8ee6b6c 100644 --- a/moonshark.go +++ b/moonshark.go @@ -90,7 +90,26 @@ func runWithWatcher(scriptPath string) { fmt.Printf("Starting %s in watch mode...\n", scriptPath) + var hadError bool + firstRun := true + for { + // If we had an error on the last run and this isn't the first run, + // wait for file changes before retrying + if hadError && !firstRun { + fmt.Println("Waiting for file changes before retrying...") + select { + case <-restartCh: + fmt.Println("Files changed, retrying...") + case <-sigChan: + fmt.Println("\nExiting...") + return + } + } + + firstRun = false + hadError = false + // Clear cache before each run state.ClearCache() @@ -98,14 +117,14 @@ func runWithWatcher(scriptPath string) { luaState, err := state.NewFromScript(scriptPath) if err != nil { log.Printf("Error creating state: %v", err) - time.Sleep(1 * time.Second) + hadError = true continue } if err := luaState.ExecuteFile(scriptPath); err != nil { log.Printf("Execution error: %v", err) luaState.Close() - time.Sleep(1 * time.Second) + hadError = true continue } diff --git a/watcher.go b/watcher.go index 40f3234..28194a6 100644 --- a/watcher.go +++ b/watcher.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "log" "maps" "os" "path/filepath" @@ -13,6 +12,7 @@ import ( type FileWatcher struct { files map[string]time.Time + dirs map[string]bool // Track watched directories mu sync.RWMutex restartCh chan bool stopCh chan bool @@ -24,6 +24,7 @@ type FileWatcher struct { func NewFileWatcher(debounceMs int) (*FileWatcher, error) { return &FileWatcher{ files: make(map[string]time.Time), + dirs: make(map[string]bool), restartCh: make(chan bool, 1), stopCh: make(chan bool, 1), debounceMs: debounceMs, @@ -51,7 +52,16 @@ func (fw *FileWatcher) AddFile(path string) error { } func (fw *FileWatcher) AddDirectory(dir string) error { - return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + + fw.mu.Lock() + fw.dirs[absDir] = true + fw.mu.Unlock() + + return filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -78,6 +88,7 @@ func (fw *FileWatcher) pollLoop() { return case <-ticker.C: fw.checkFiles() + fw.scanForNewFiles() } } } @@ -103,7 +114,7 @@ func (fw *FileWatcher) checkFiles() { now := time.Now() if now.Sub(fw.lastEvent) > time.Duration(fw.debounceMs)*time.Millisecond { fw.lastEvent = now - log.Printf("File changed: %s", path) + // log.Printf("File changed: %s", path) changed = true } } @@ -117,6 +128,57 @@ func (fw *FileWatcher) checkFiles() { } } +func (fw *FileWatcher) scanForNewFiles() { + fw.mu.RLock() + dirs := make(map[string]bool, len(fw.dirs)) + maps.Copy(dirs, fw.dirs) + existingFiles := make(map[string]bool, len(fw.files)) + for path := range fw.files { + existingFiles[path] = true + } + fw.mu.RUnlock() + + newFilesFound := false + for dir := range dirs { + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && strings.HasSuffix(path, ".lua") { + absPath, err := filepath.Abs(path) + if err != nil { + return err + } + + if !existingFiles[absPath] { + fw.mu.Lock() + fw.files[absPath] = info.ModTime() + fw.mu.Unlock() + + fmt.Printf("New file detected: %s\n", absPath) + newFilesFound = true + } + } + return nil + }) + if err != nil { + continue + } + } + + if newFilesFound { + now := time.Now() + if now.Sub(fw.lastEvent) > time.Duration(fw.debounceMs)*time.Millisecond { + fw.lastEvent = now + select { + case fw.restartCh <- true: + default: + } + } + } +} + func (fw *FileWatcher) Close() error { select { case fw.stopCh <- true: