2019-11-12 13:19:20 +02:00
|
|
|
package gui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/fsnotify/fsnotify"
|
2020-09-29 12:28:39 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
2020-10-07 12:19:38 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
2020-01-08 12:02:01 +02:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-11-12 13:19:20 +02:00
|
|
|
)
|
|
|
|
|
2020-01-08 12:02:01 +02:00
|
|
|
// macs for some bizarre reason cap the number of watchable files to 256.
|
|
|
|
// there's no obvious platform agonstic way to check the situation of the user's
|
|
|
|
// computer so we're just arbitrarily capping at 200. This isn't so bad because
|
|
|
|
// file watching is only really an added bonus for faster refreshing.
|
2020-01-08 12:41:39 +02:00
|
|
|
const MAX_WATCHED_FILES = 50
|
2020-01-08 12:02:01 +02:00
|
|
|
|
|
|
|
type fileWatcher struct {
|
|
|
|
Watcher *fsnotify.Watcher
|
|
|
|
WatchedFilenames []string
|
|
|
|
Log *logrus.Entry
|
2020-01-12 05:43:43 +02:00
|
|
|
Disabled bool
|
2020-01-08 12:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewFileWatcher(log *logrus.Entry) *fileWatcher {
|
2020-03-09 02:34:10 +02:00
|
|
|
// TODO: get this going again, and ensure we don't see any crashes from it
|
2020-01-12 05:43:43 +02:00
|
|
|
return &fileWatcher{
|
|
|
|
Disabled: true,
|
|
|
|
}
|
2020-01-08 12:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *fileWatcher) watchingFilename(filename string) bool {
|
|
|
|
for _, watchedFilename := range w.WatchedFilenames {
|
|
|
|
if watchedFilename == filename {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *fileWatcher) popOldestFilename() {
|
|
|
|
// shift the last off the array to make way for this one
|
|
|
|
oldestFilename := w.WatchedFilenames[0]
|
|
|
|
w.WatchedFilenames = w.WatchedFilenames[1:]
|
|
|
|
if err := w.Watcher.Remove(oldestFilename); err != nil {
|
|
|
|
// swallowing errors here because it doesn't really matter if we can't unwatch a file
|
2020-09-26 02:23:10 +02:00
|
|
|
w.Log.Error(err)
|
2020-01-08 12:02:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *fileWatcher) watchFilename(filename string) {
|
|
|
|
if err := w.Watcher.Add(filename); err != nil {
|
|
|
|
// swallowing errors here because it doesn't really matter if we can't watch a file
|
2020-09-26 02:23:10 +02:00
|
|
|
w.Log.Error(err)
|
2020-01-08 12:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// assume we're watching it now to be safe
|
|
|
|
w.WatchedFilenames = append(w.WatchedFilenames, filename)
|
|
|
|
}
|
|
|
|
|
2020-09-29 10:45:00 +02:00
|
|
|
func (w *fileWatcher) addFilesToFileWatcher(files []*models.File) error {
|
2020-01-12 05:43:43 +02:00
|
|
|
if w.Disabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:48:40 +02:00
|
|
|
if len(files) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:02:01 +02:00
|
|
|
// watch the files for changes
|
|
|
|
dirName, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:48:40 +02:00
|
|
|
for _, file := range files[0:min(MAX_WATCHED_FILES, len(files))] {
|
2020-01-08 12:57:39 +02:00
|
|
|
if file.Deleted {
|
|
|
|
continue
|
|
|
|
}
|
2020-01-08 12:02:01 +02:00
|
|
|
filename := filepath.Join(dirName, file.Name)
|
|
|
|
if w.watchingFilename(filename) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(w.WatchedFilenames) > MAX_WATCHED_FILES {
|
|
|
|
w.popOldestFilename()
|
|
|
|
}
|
|
|
|
|
|
|
|
w.watchFilename(filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-08 12:48:40 +02:00
|
|
|
func min(a int, b int) int {
|
|
|
|
if a < b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2019-11-12 13:19:20 +02:00
|
|
|
// NOTE: given that we often edit files ourselves, this may make us end up refreshing files too often
|
|
|
|
// TODO: consider watching the whole directory recursively (could be more expensive)
|
|
|
|
func (gui *Gui) watchFilesForChanges() {
|
2020-01-08 12:02:01 +02:00
|
|
|
gui.fileWatcher = NewFileWatcher(gui.Log)
|
2020-01-12 05:43:43 +02:00
|
|
|
if gui.fileWatcher.Disabled {
|
2019-11-12 13:19:20 +02:00
|
|
|
return
|
|
|
|
}
|
2020-10-07 12:19:38 +02:00
|
|
|
go utils.Safe(func() {
|
2019-11-12 13:19:20 +02:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// watch for events
|
2020-01-08 12:02:01 +02:00
|
|
|
case event := <-gui.fileWatcher.Watcher.Events:
|
2019-11-12 13:19:20 +02:00
|
|
|
if event.Op == fsnotify.Chmod {
|
|
|
|
// for some reason we pick up chmod events when they don't actually happen
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// only refresh if we're not already
|
|
|
|
if !gui.State.IsRefreshingFiles {
|
2021-03-31 14:55:06 +02:00
|
|
|
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
2019-11-12 13:19:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// watch for errors
|
2020-01-08 12:02:01 +02:00
|
|
|
case err := <-gui.fileWatcher.Watcher.Errors:
|
2019-11-12 13:19:20 +02:00
|
|
|
if err != nil {
|
2020-09-26 02:23:10 +02:00
|
|
|
gui.Log.Error(err)
|
2019-11-12 13:19:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-07 12:19:38 +02:00
|
|
|
})
|
2019-11-12 13:19:20 +02:00
|
|
|
}
|