diff --git a/pkg/gocui/gui.go b/pkg/gocui/gui.go index ad8ba1e41..558b9d619 100644 --- a/pkg/gocui/gui.go +++ b/pkg/gocui/gui.go @@ -103,6 +103,7 @@ type replayedEvents struct { Keys chan *TcellKeyEventWrapper Resizes chan *TcellResizeEventWrapper MouseEvents chan *TcellMouseEventWrapper + FocusEvents chan *TcellFocusEventWrapper } type RecordingConfig struct { @@ -245,6 +246,7 @@ func NewGui(opts NewGuiOpts) (*Gui, error) { Keys: make(chan *TcellKeyEventWrapper), Resizes: make(chan *TcellResizeEventWrapper), MouseEvents: make(chan *TcellMouseEventWrapper), + FocusEvents: make(chan *TcellFocusEventWrapper), } } diff --git a/pkg/gocui/tcell_driver.go b/pkg/gocui/tcell_driver.go index b2fd40c19..226ee0580 100644 --- a/pkg/gocui/tcell_driver.go +++ b/pkg/gocui/tcell_driver.go @@ -266,6 +266,22 @@ func (wrapper TcellResizeEventWrapper) toTcellEvent() tcell.Event { return tcell.NewEventResize(wrapper.Width, wrapper.Height) } +type TcellFocusEventWrapper struct { + Timestamp int64 + Focused bool +} + +func NewTcellFocusEventWrapper(event *tcell.EventFocus, timestamp int64) *TcellFocusEventWrapper { + return &TcellFocusEventWrapper{ + Timestamp: timestamp, + Focused: event.Focused, + } +} + +func (wrapper TcellFocusEventWrapper) toTcellEvent() tcell.Event { + return tcell.NewEventFocus(wrapper.Focused) +} + // pollEvent get tcell.Event and transform it into gocuiEvent func (g *Gui) pollEvent() GocuiEvent { var tev tcell.Event @@ -277,6 +293,8 @@ func (g *Gui) pollEvent() GocuiEvent { tev = (ev).toTcellEvent() case ev := <-g.ReplayedEvents.MouseEvents: tev = (ev).toTcellEvent() + case ev := <-g.ReplayedEvents.FocusEvents: + tev = (ev).toTcellEvent() } } else { tev = <-Screen.EventQ() diff --git a/pkg/gui/gui_driver.go b/pkg/gui/gui_driver.go index 08f3ecf62..632e271c3 100644 --- a/pkg/gui/gui_driver.go +++ b/pkg/gui/gui_driver.go @@ -56,6 +56,18 @@ func (self *GuiDriver) Click(x, y int) { self.waitTillIdle() } +// FocusIn simulates the terminal window regaining focus, which is how lazygit +// learns to reload changed config files. Tests use it to exercise the live +// config-reload path. +func (self *GuiDriver) FocusIn() { + self.gui.g.ReplayedEvents.FocusEvents <- gocui.NewTcellFocusEventWrapper( + tcell.NewEventFocus(true), + 0, + ) + + self.waitTillIdle() +} + // wait until lazygit is idle (i.e. all processing is done) before continuing func (self *GuiDriver) waitTillIdle() { <-self.isIdleChan diff --git a/pkg/integration/components/test_driver.go b/pkg/integration/components/test_driver.go index 8294f3b46..301ab3862 100644 --- a/pkg/integration/components/test_driver.go +++ b/pkg/integration/components/test_driver.go @@ -56,6 +56,14 @@ func (self *TestDriver) GlobalPress(key config.Keybinding) { self.press(key[0]) } +// FocusIn simulates the terminal window regaining focus, which causes lazygit +// to reload any config files that changed while it was in the background. +func (self *TestDriver) FocusIn() { + self.SetCaption("Focusing window") + self.gui.FocusIn() + self.Wait(self.inputDelay) +} + func (self *TestDriver) typeContent(content string) { for _, char := range content { self.pressFast(string(char)) diff --git a/pkg/integration/components/test_test.go b/pkg/integration/components/test_test.go index ab32f9f89..b00a2a672 100644 --- a/pkg/integration/components/test_test.go +++ b/pkg/integration/components/test_test.go @@ -34,6 +34,9 @@ func (self *fakeGuiDriver) Click(x, y int) { self.clickedCoordinates = append(self.clickedCoordinates, coordinate{x: x, y: y}) } +func (self *fakeGuiDriver) FocusIn() { +} + func (self *fakeGuiDriver) Keys() config.KeybindingConfig { return config.KeybindingConfig{} } diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 752639058..3d87e7d6e 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -24,6 +24,9 @@ type IntegrationTest interface { type GuiDriver interface { PressKey(string) Click(int, int) + // Simulate the terminal window regaining focus (which triggers a reload of + // changed config files) + FocusIn() Keys() config.KeybindingConfig CurrentContext() types.Context ContextForView(viewName string) types.Context