summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-11-12 22:19:20 +1100
committerJesse Duffield <jessedduffield@gmail.com>2019-11-14 22:22:47 +1100
commitf15e47bb67f39904c624846cc8392f3ad14358a9 (patch)
tree2203a588fbd990e53a6eab534e39a78b8d127941
parent7995d56a858fdeb399ed884a6983008aa2089cfb (diff)
add file watching for modified files
log createErrorPanel error swallow error when adding file to watcher
-rw-r--r--pkg/gui/file_watching.go64
-rw-r--r--pkg/gui/files_panel.go14
-rw-r--r--pkg/gui/gui.go40
3 files changed, 101 insertions, 17 deletions
diff --git a/pkg/gui/file_watching.go b/pkg/gui/file_watching.go
new file mode 100644
index 000000000..90fb32639
--- /dev/null
+++ b/pkg/gui/file_watching.go
@@ -0,0 +1,64 @@
+package gui
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/jesseduffield/lazygit/pkg/commands"
+)
+
+// 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() {
+ var err error
+ gui.fileWatcher, err = fsnotify.NewWatcher()
+ if err != nil {
+ gui.Log.Error(err)
+ return
+ }
+ go func() {
+ for {
+ select {
+ // watch for events
+ case event := <-gui.fileWatcher.Events:
+ 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 {
+ if err := gui.refreshFiles(); err != nil {
+ err = gui.createErrorPanel(gui.g, err.Error())
+ if err != nil {
+ gui.Log.Error(err)
+ }
+ }
+ }
+
+ // watch for errors
+ case err := <-gui.fileWatcher.Errors:
+ if err != nil {
+ gui.Log.Warn(err)
+ }
+ }
+ }
+ }()
+}
+
+func (gui *Gui) addFilesToFileWatcher(files []*commands.File) error {
+ // watch the files for changes
+ dirName, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+
+ for _, file := range files {
+ if err := gui.fileWatcher.Add(filepath.Join(dirName, file.Name)); err != nil {
+ // swallowing errors here because it doesn't really matter if we can't watch a file
+ gui.Log.Warn(err)
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index af021c132..ae3328eed 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -105,6 +105,13 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bo
}
func (gui *Gui) refreshFiles() error {
+ gui.State.RefreshingFilesMutex.Lock()
+ gui.State.IsRefreshingFiles = true
+ defer func() {
+ gui.State.IsRefreshingFiles = false
+ gui.State.RefreshingFilesMutex.Unlock()
+ }()
+
selectedFile, _ := gui.getSelectedFile(gui.g)
filesView := gui.getFilesView()
@@ -126,7 +133,7 @@ func (gui *Gui) refreshFiles() error {
}
fmt.Fprint(filesView, list)
- if filesView == g.CurrentView() {
+ if g.CurrentView() == filesView || (g.CurrentView() == gui.getMainView() && gui.State.Context == "merging") {
newSelectedFile, _ := gui.getSelectedFile(gui.g)
alreadySelected := newSelectedFile.Name == selectedFile.Name
return gui.handleFileSelect(g, filesView, alreadySelected)
@@ -387,6 +394,11 @@ func (gui *Gui) refreshStateFiles() error {
// get files to stage
files := gui.GitCommand.GetStatusFiles()
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
+
+ if err := gui.addFilesToFileWatcher(files); err != nil {
+ return err
+ }
+
gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
return gui.updateWorkTreeState()
}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 982e1879c..a3bcb1666 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -14,6 +14,7 @@ import (
"strings"
"time"
+ "github.com/fsnotify/fsnotify"
"github.com/go-errors/errors"
// "strings"
@@ -79,6 +80,7 @@ type Gui struct {
statusManager *statusManager
credentials credentials
waitForIntro sync.WaitGroup
+ fileWatcher *fsnotify.Watcher
}
// for now the staging panel state, unlike the other panel states, is going to be
@@ -145,22 +147,24 @@ type panelStates struct {
}
type guiState struct {
- Files []*commands.File
- Branches []*commands.Branch
- Commits []*commands.Commit
- StashEntries []*commands.StashEntry
- CommitFiles []*commands.CommitFile
- DiffEntries []*commands.Commit
- MenuItemCount int // can't store the actual list because it's of interface{} type
- PreviousView string
- Platform commands.Platform
- Updating bool
- Panels *panelStates
- WorkingTreeState string // one of "merging", "rebasing", "normal"
- Context string // important not to set this value directly but to use gui.changeContext("new context")
- CherryPickedCommits []*commands.Commit
- SplitMainPanel bool
- RetainOriginalDir bool
+ Files []*commands.File
+ Branches []*commands.Branch
+ Commits []*commands.Commit
+ StashEntries []*commands.StashEntry
+ CommitFiles []*commands.CommitFile
+ DiffEntries []*commands.Commit
+ MenuItemCount int // can't store the actual list because it's of interface{} type
+ PreviousView string
+ Platform commands.Platform
+ Updating bool
+ Panels *panelStates
+ WorkingTreeState string // one of "merging", "rebasing", "normal"
+ Context string // important not to set this value directly but to use gui.changeContext("new context")
+ CherryPickedCommits []*commands.Commit
+ SplitMainPanel bool
+ RetainOriginalDir bool
+ IsRefreshingFiles bool
+ RefreshingFilesMutex sync.Mutex
}
// for now the split view will always be on
@@ -204,6 +208,8 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
statusManager: &statusManager{},
}
+ gui.watchFilesForChanges()
+
gui.GenerateSentinelErrors()
return gui, nil
@@ -786,6 +792,8 @@ func (gui *Gui) RunWithSubprocesses() error {
}
}
+ gui.fileWatcher.Close()
+
break
} else if err == gui.Errors.ErrSwitchRepo {
continue