mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-08 23:56:15 +02:00
add file watching for modified files
log createErrorPanel error swallow error when adding file to watcher
This commit is contained in:
parent
7995d56a85
commit
f15e47bb67
64
pkg/gui/file_watching.go
Normal file
64
pkg/gui/file_watching.go
Normal file
@ -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
|
||||||
|
}
|
@ -105,6 +105,13 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshFiles() error {
|
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)
|
selectedFile, _ := gui.getSelectedFile(gui.g)
|
||||||
|
|
||||||
filesView := gui.getFilesView()
|
filesView := gui.getFilesView()
|
||||||
@ -126,7 +133,7 @@ func (gui *Gui) refreshFiles() error {
|
|||||||
}
|
}
|
||||||
fmt.Fprint(filesView, list)
|
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)
|
newSelectedFile, _ := gui.getSelectedFile(gui.g)
|
||||||
alreadySelected := newSelectedFile.Name == selectedFile.Name
|
alreadySelected := newSelectedFile.Name == selectedFile.Name
|
||||||
return gui.handleFileSelect(g, filesView, alreadySelected)
|
return gui.handleFileSelect(g, filesView, alreadySelected)
|
||||||
@ -387,6 +394,11 @@ func (gui *Gui) refreshStateFiles() error {
|
|||||||
// get files to stage
|
// get files to stage
|
||||||
files := gui.GitCommand.GetStatusFiles()
|
files := gui.GitCommand.GetStatusFiles()
|
||||||
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
|
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))
|
gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
|
||||||
return gui.updateWorkTreeState()
|
return gui.updateWorkTreeState()
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
@ -79,6 +80,7 @@ type Gui struct {
|
|||||||
statusManager *statusManager
|
statusManager *statusManager
|
||||||
credentials credentials
|
credentials credentials
|
||||||
waitForIntro sync.WaitGroup
|
waitForIntro sync.WaitGroup
|
||||||
|
fileWatcher *fsnotify.Watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now the staging panel state, unlike the other panel states, is going to be
|
// 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 {
|
type guiState struct {
|
||||||
Files []*commands.File
|
Files []*commands.File
|
||||||
Branches []*commands.Branch
|
Branches []*commands.Branch
|
||||||
Commits []*commands.Commit
|
Commits []*commands.Commit
|
||||||
StashEntries []*commands.StashEntry
|
StashEntries []*commands.StashEntry
|
||||||
CommitFiles []*commands.CommitFile
|
CommitFiles []*commands.CommitFile
|
||||||
DiffEntries []*commands.Commit
|
DiffEntries []*commands.Commit
|
||||||
MenuItemCount int // can't store the actual list because it's of interface{} type
|
MenuItemCount int // can't store the actual list because it's of interface{} type
|
||||||
PreviousView string
|
PreviousView string
|
||||||
Platform commands.Platform
|
Platform commands.Platform
|
||||||
Updating bool
|
Updating bool
|
||||||
Panels *panelStates
|
Panels *panelStates
|
||||||
WorkingTreeState string // one of "merging", "rebasing", "normal"
|
WorkingTreeState string // one of "merging", "rebasing", "normal"
|
||||||
Context string // important not to set this value directly but to use gui.changeContext("new context")
|
Context string // important not to set this value directly but to use gui.changeContext("new context")
|
||||||
CherryPickedCommits []*commands.Commit
|
CherryPickedCommits []*commands.Commit
|
||||||
SplitMainPanel bool
|
SplitMainPanel bool
|
||||||
RetainOriginalDir bool
|
RetainOriginalDir bool
|
||||||
|
IsRefreshingFiles bool
|
||||||
|
RefreshingFilesMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now the split view will always be on
|
// 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{},
|
statusManager: &statusManager{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gui.watchFilesForChanges()
|
||||||
|
|
||||||
gui.GenerateSentinelErrors()
|
gui.GenerateSentinelErrors()
|
||||||
|
|
||||||
return gui, nil
|
return gui, nil
|
||||||
@ -786,6 +792,8 @@ func (gui *Gui) RunWithSubprocesses() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gui.fileWatcher.Close()
|
||||||
|
|
||||||
break
|
break
|
||||||
} else if err == gui.Errors.ErrSwitchRepo {
|
} else if err == gui.Errors.ErrSwitchRepo {
|
||||||
continue
|
continue
|
||||||
|
Loading…
x
Reference in New Issue
Block a user