diff --git a/integration/main.go b/integration/main.go index 25abdf56a..b447fdb58 100644 --- a/integration/main.go +++ b/integration/main.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "os" "path/filepath" "strconv" @@ -13,6 +14,7 @@ import ( "github.com/creack/pty" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/stretchr/testify/assert" ) // To run an integration test, e.g. for test 'commit', go: @@ -209,19 +211,19 @@ func Test() error { speeds := getTestSpeeds(test.Speed, updateSnapshots) for i, speed := range speeds { - // t.Logf("%s: attempting test at speed %d\n", test.Name, speed) + log.Printf("%s: attempting test at speed %d\n", test.Name, speed) testPath := filepath.Join(testDir, test.Name) actualDir := filepath.Join(testPath, "actual") expectedDir := filepath.Join(testPath, "expected") - // t.Logf("testPath: %s, actualDir: %s, expectedDir: %s", testPath, actualDir, expectedDir) + log.Printf("testPath: %s, actualDir: %s, expectedDir: %s", testPath, actualDir, expectedDir) findOrCreateDir(testPath) prepareIntegrationTestDir(actualDir) err := createFixture(testPath, actualDir) if err != nil { - return err + // return err } runLazygit(testPath, rootDir, record, speed) @@ -268,13 +270,14 @@ func Test() error { }() if expected == actual { - // t.Logf("%s: success at speed %d\n", test.Name, speed) + log.Printf("%s: success at speed %d\n", test.Name, speed) break } // if the snapshots and we haven't tried all playback speeds different we'll retry at a slower speed if i == len(speeds)-1 { - // assert.Equal(t, expected, actual, fmt.Sprintf("expected:\n%s\nactual:\n%s\n", expected, actual)) + assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("expected:\n%s\nactual:\n%s\n", expected, actual)) + os.Exit(1) } } } @@ -282,6 +285,12 @@ func Test() error { return nil } +type MockTestingT struct{} + +func (t MockTestingT) Errorf(format string, args ...interface{}) { + fmt.Printf(format, args...) +} + func createFixture(testPath, actualDir string) error { osCommand := oscommands.NewDummyOSCommand() bashScriptPath := filepath.Join(testPath, "setup.sh") diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 154a68b99..f334c59b0 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -458,7 +458,7 @@ func (gui *Gui) Run() error { playMode = gocui.REPLAYING } - g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode) + g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode, headless()) if err != nil { return err } @@ -468,7 +468,7 @@ func (gui *Gui) Run() error { if replaying() { g.RecordingConfig = gocui.RecordingConfig{ Speed: getRecordingSpeed(), - Leeway: 0, + Leeway: 100, } g.Recording, err = gui.loadRecording() diff --git a/pkg/gui/recording.go b/pkg/gui/recording.go index 4b26f73c4..51235b96b 100644 --- a/pkg/gui/recording.go +++ b/pkg/gui/recording.go @@ -22,6 +22,10 @@ func replaying() bool { return os.Getenv("REPLAY_EVENTS_FROM") != "" } +func headless() bool { + return os.Getenv("HEADLESS") != "" +} + func getRecordingSpeed() int { // humans are slow so this speeds things up. speed := 1 diff --git a/test/integration/commit/expected/.git_keep/index b/test/integration/commit/expected/.git_keep/index index e0b0f6831..b712be28a 100644 Binary files a/test/integration/commit/expected/.git_keep/index and b/test/integration/commit/expected/.git_keep/index differ diff --git a/test/integration/commit/expected/.git_keep/logs/HEAD b/test/integration/commit/expected/.git_keep/logs/HEAD index 01374ee2b..0583e2f0b 100644 --- a/test/integration/commit/expected/.git_keep/logs/HEAD +++ b/test/integration/commit/expected/.git_keep/logs/HEAD @@ -1,5 +1,5 @@ -0000000000000000000000000000000000000000 55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa CI 1617579407 +1000 commit (initial): myfile1 -55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa e68c1143545a09cac1db6e6f20db13b82b5dc43e CI 1617579407 +1000 commit: myfile2 -e68c1143545a09cac1db6e6f20db13b82b5dc43e ba001a041f64109b238f03550d6a21209bd5d4fc CI 1617579407 +1000 commit: myfile3 -ba001a041f64109b238f03550d6a21209bd5d4fc 6f31cc35ebabdf3fb71759e3d267677e712f737f CI 1617579407 +1000 commit: myfile4 -6f31cc35ebabdf3fb71759e3d267677e712f737f ecc083de4d216847c184c72a639743bba6520d88 CI 1617579409 +1000 commit: commit +0000000000000000000000000000000000000000 47912ceb0ffa9b205290e75103d9dd6b1878e87b CI 1617580570 +1000 commit (initial): myfile1 +47912ceb0ffa9b205290e75103d9dd6b1878e87b 6bd1486e023f9853f9e6f611cac5ccbc8960ce57 CI 1617580570 +1000 commit: myfile2 +6bd1486e023f9853f9e6f611cac5ccbc8960ce57 980224a1dd75d91fdcab11edc8d25c3ff8f751ba CI 1617580570 +1000 commit: myfile3 +980224a1dd75d91fdcab11edc8d25c3ff8f751ba f4e779d1bd2ad074259ad763210f5b911337054f CI 1617580570 +1000 commit: myfile4 +f4e779d1bd2ad074259ad763210f5b911337054f d0cab53ed70fc66096575c2ccd7ef150b4b470e8 CI 1617580572 +1000 commit: commit diff --git a/test/integration/commit/expected/.git_keep/logs/refs/heads/master b/test/integration/commit/expected/.git_keep/logs/refs/heads/master index 01374ee2b..0583e2f0b 100644 --- a/test/integration/commit/expected/.git_keep/logs/refs/heads/master +++ b/test/integration/commit/expected/.git_keep/logs/refs/heads/master @@ -1,5 +1,5 @@ -0000000000000000000000000000000000000000 55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa CI 1617579407 +1000 commit (initial): myfile1 -55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa e68c1143545a09cac1db6e6f20db13b82b5dc43e CI 1617579407 +1000 commit: myfile2 -e68c1143545a09cac1db6e6f20db13b82b5dc43e ba001a041f64109b238f03550d6a21209bd5d4fc CI 1617579407 +1000 commit: myfile3 -ba001a041f64109b238f03550d6a21209bd5d4fc 6f31cc35ebabdf3fb71759e3d267677e712f737f CI 1617579407 +1000 commit: myfile4 -6f31cc35ebabdf3fb71759e3d267677e712f737f ecc083de4d216847c184c72a639743bba6520d88 CI 1617579409 +1000 commit: commit +0000000000000000000000000000000000000000 47912ceb0ffa9b205290e75103d9dd6b1878e87b CI 1617580570 +1000 commit (initial): myfile1 +47912ceb0ffa9b205290e75103d9dd6b1878e87b 6bd1486e023f9853f9e6f611cac5ccbc8960ce57 CI 1617580570 +1000 commit: myfile2 +6bd1486e023f9853f9e6f611cac5ccbc8960ce57 980224a1dd75d91fdcab11edc8d25c3ff8f751ba CI 1617580570 +1000 commit: myfile3 +980224a1dd75d91fdcab11edc8d25c3ff8f751ba f4e779d1bd2ad074259ad763210f5b911337054f CI 1617580570 +1000 commit: myfile4 +f4e779d1bd2ad074259ad763210f5b911337054f d0cab53ed70fc66096575c2ccd7ef150b4b470e8 CI 1617580572 +1000 commit: commit diff --git a/test/integration/commit/expected/.git_keep/objects/47/912ceb0ffa9b205290e75103d9dd6b1878e87b b/test/integration/commit/expected/.git_keep/objects/47/912ceb0ffa9b205290e75103d9dd6b1878e87b new file mode 100644 index 000000000..a92c77d4a --- /dev/null +++ b/test/integration/commit/expected/.git_keep/objects/47/912ceb0ffa9b205290e75103d9dd6b1878e87b @@ -0,0 +1,2 @@ +xA +0@Q9I#M&XR"~̖r*Jd7Y)J4$gᚲ\zWa>NO$Vf #p&DtG=&]ξuY16, \ No newline at end of file diff --git a/test/integration/commit/expected/.git_keep/objects/55/bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa b/test/integration/commit/expected/.git_keep/objects/55/bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa deleted file mode 100644 index e169a11e3..000000000 --- a/test/integration/commit/expected/.git_keep/objects/55/bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa +++ /dev/null @@ -1,2 +0,0 @@ -xA -0@Ѯsʌ'J\yL")#tyS5[˥*`2RT 0 { + app.itemIdx-- + } + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, 'q', gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, 'r', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("RECORD_EVENTS=true go run integration/main.go %s", currentTest.Name)) + app.runSubprocess(cmd) + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("go run integration/main.go %s", currentTest.Name)) + app.runSubprocess(cmd) + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, 'o', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r %s/%s/test.json", app.testDir, currentTest.Name)) + if err := cmd.Run(); err != nil { + return err + } + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, 'n', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + // need to duplicate that folder and then re-fetch our tests. + dir := app.testDir + "/" + app.getCurrentTest().Name + newDir := dir + "_Copy" + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("cp -r %s %s", dir, newDir)) + if err := cmd.Run(); err != nil { + return err + } + + app.loadTests() + + app.refreshTests() + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", nil, 'm', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + app.editing = true + if _, err := g.SetCurrentView("editor"); err != nil { + return err + } + editorView, err := g.View("editor") if err != nil { - log.Panicln(err) + return err } + editorView.Clear() + fmt.Fprint(editorView, currentTest.Name) - g.Cursor = false + return nil + }); err != nil { + log.Panicln(err) + } - app.g = g - - g.SetManagerFunc(app.layout) - - if err := g.SetKeybinding("list", nil, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - if app.itemIdx > 0 { - app.itemIdx-- - } + if err := g.SetKeybinding("list", nil, 'd', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { return nil - }); err != nil { - log.Panicln(err) } - if err := g.SetKeybinding("list", nil, gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { - log.Panicln(err) + dir := app.testDir + "/" + app.getCurrentTest().Name + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("rm -rf %s", dir)) + if err := cmd.Run(); err != nil { + return err } - if err := g.SetKeybinding("list", nil, 'q', gocui.ModNone, quit); err != nil { - log.Panicln(err) - } + app.refreshTests() - if err := g.SetKeybinding("list", nil, 'r', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("RECORD_EVENTS=true go test pkg/gui/gui_test.go -run /%s", currentTest.Name)) - app.subProcess = cmd - - return errSubProcess - }); err != nil { - log.Panicln(err) - } - - if err := g.SetKeybinding("list", nil, gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("go test pkg/gui/gui_test.go -run /%s", currentTest.Name)) - app.subProcess = cmd - - return errSubProcess - }); err != nil { - log.Panicln(err) - } - - if err := g.SetKeybinding("list", nil, 'o', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r %s/%s/test.json", app.testDir, currentTest.Name)) - if err := cmd.Run(); err != nil { - return err - } + return nil + }); err != nil { + log.Panicln(err) + } + if err := g.SetKeybinding("editor", nil, gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { return nil - }); err != nil { - log.Panicln(err) } - if err := g.SetKeybinding("list", nil, 'n', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - // need to duplicate that folder and then re-fetch our tests. - dir := app.testDir + "/" + app.getCurrentTest().Name - newDir := dir + "_Copy" - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("cp -r %s %s", dir, newDir)) - if err := cmd.Run(); err != nil { - return err - } - - app.loadTests() - - app.refreshTests() - return nil - }); err != nil { - log.Panicln(err) + app.editing = false + if _, err := g.SetCurrentView("list"); err != nil { + return err } - if err := g.SetKeybinding("list", nil, 'm', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - app.editing = true - if _, err := g.SetCurrentView("editor"); err != nil { - return err - } - editorView, err := g.View("editor") - if err != nil { - return err - } - editorView.Clear() - fmt.Fprint(editorView, currentTest.Name) - - return nil - }); err != nil { - log.Panicln(err) - } - - if err := g.SetKeybinding("list", nil, 'd', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - dir := app.testDir + "/" + app.getCurrentTest().Name - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("rm -rf %s", dir)) - if err := cmd.Run(); err != nil { - return err - } - - app.refreshTests() - - return nil - }); err != nil { - log.Panicln(err) - } - - if err := g.SetKeybinding("editor", nil, gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - currentTest := app.getCurrentTest() - if currentTest == nil { - return nil - } - - app.editing = false - if _, err := g.SetCurrentView("list"); err != nil { - return err - } - - editorView, err := g.View("editor") - if err != nil { - return err - } - - dir := app.testDir + "/" + app.getCurrentTest().Name - newDir := app.testDir + "/" + editorView.Buffer() - - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("mv %s %s", dir, newDir)) - if err := cmd.Run(); err != nil { - return err - } - - editorView.Clear() - - app.refreshTests() - return nil - }); err != nil { - log.Panicln(err) - } - - if err := g.SetKeybinding("editor", nil, gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - app.editing = false - if _, err := g.SetCurrentView("list"); err != nil { - return err - } - - return nil - }); err != nil { - log.Panicln(err) - } - - err = g.MainLoop() - g.Close() + editorView, err := g.View("editor") if err != nil { - switch err { - case gocui.ErrQuit: - break Loop + return err + } - case errSubProcess: - cmd := app.subProcess - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - if err := cmd.Run(); err != nil { - log.Println(err.Error()) - } - cmd.Stdin = nil - cmd.Stderr = nil - cmd.Stdout = nil + dir := app.testDir + "/" + app.getCurrentTest().Name + newDir := app.testDir + "/" + editorView.Buffer() - fmt.Fprintf(os.Stdout, "\n%s", coloredString("press enter to return", color.FgGreen)) - fmt.Scanln() // wait for enter press + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("mv %s %s", dir, newDir)) + if err := cmd.Run(); err != nil { + return err + } - default: - log.Panicln(err) - } + editorView.Clear() + + app.refreshTests() + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("editor", nil, gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + app.editing = false + if _, err := g.SetCurrentView("list"); err != nil { + return err + } + + return nil + }); err != nil { + log.Panicln(err) + } + + err = g.MainLoop() + g.Close() + if err != nil { + switch err { + case gocui.ErrQuit: + return + default: + log.Panicln(err) } } } +func (app *App) runSubprocess(cmd *exec.Cmd) { + gocui.Screen.Suspend() + + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + log.Println(err.Error()) + } + cmd.Stdin = nil + cmd.Stderr = nil + cmd.Stdout = nil + + fmt.Fprintf(os.Stdout, "\n%s", coloredString("press enter to return", color.FgGreen)) + fmt.Scanln() // wait for enter press + + gocui.Screen.Resume() +} + func (app *App) layout(g *gocui.Gui) error { maxX, maxY := g.Size() descriptionViewHeight := 7 diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index 0fd89fbb5..deeb4371a 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -85,11 +85,13 @@ const ( ) type Recording struct { - KeyEvents []*TcellKeyEventWrapper + KeyEvents []*TcellKeyEventWrapper + ResizeEvents []*TcellResizeEventWrapper } type replayedEvents struct { - keys chan *TcellKeyEventWrapper + keys chan *TcellKeyEventWrapper + resizes chan *TcellResizeEventWrapper } type RecordingConfig struct { @@ -159,14 +161,19 @@ type Gui struct { } // NewGui returns a new Gui object with a given output mode. -func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode) (*Gui, error) { - err := tcellInit() +func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode, headless bool) (*Gui, error) { + g := &Gui{} + + var err error + if headless { + err = tcellInitSimulation() + } else { + err = tcellInit() + } if err != nil { return nil, err } - g := &Gui{} - g.outputMode = mode g.stop = make(chan struct{}) @@ -176,11 +183,13 @@ func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode) (*Gui, err if playMode == RECORDING { g.Recording = &Recording{ - KeyEvents: []*TcellKeyEventWrapper{}, + KeyEvents: []*TcellKeyEventWrapper{}, + ResizeEvents: []*TcellResizeEventWrapper{}, } } else if playMode == REPLAYING { g.ReplayedEvents = replayedEvents{ - keys: make(chan *TcellKeyEventWrapper), + keys: make(chan *TcellKeyEventWrapper), + resizes: make(chan *TcellResizeEventWrapper), } } @@ -562,8 +571,10 @@ func (g *Gui) SetManagerFunc(manager func(*Gui) error) { // MainLoop runs the main loop until an error is returned. A successful // finish should return ErrQuit. func (g *Gui) MainLoop() error { + + g.StartTime = time.Now() if g.PlayMode == REPLAYING { - g.replayRecording() + go g.replayRecording() } go func() { @@ -1182,38 +1193,80 @@ func IsQuit(err error) bool { } func (g *Gui) replayRecording() { - ticker := time.NewTicker(time.Millisecond) - defer ticker.Stop() + waitGroup := sync.WaitGroup{} - // The playback could be paused at any time because integration tests run concurrently. - // Therefore we can't just check for a given event whether we've passed its timestamp, - // or else we'll have an explosion of keypresses after the test is resumed. - // We need to check if we've waited long enough since the last event was replayed. - // Only handling key events for now. - for i, event := range g.Recording.KeyEvents { - var prevEventTimestamp int64 = 0 - if i > 0 { - prevEventTimestamp = g.Recording.KeyEvents[i-1].Timestamp - } - timeToWait := (event.Timestamp - prevEventTimestamp) / int64(g.RecordingConfig.Speed) - if i == 0 { - timeToWait += int64(g.RecordingConfig.Leeway) - } - var timeWaited int64 = 0 - middle: - for { - select { - case <-ticker.C: - timeWaited += 1 - if g != nil && timeWaited >= timeToWait { - g.ReplayedEvents.keys <- event - break middle + waitGroup.Add(2) + + go func() { + ticker := time.NewTicker(time.Millisecond) + defer ticker.Stop() + + // The playback could be paused at any time because integration tests run concurrently. + // Therefore we can't just check for a given event whether we've passed its timestamp, + // or else we'll have an explosion of keypresses after the test is resumed. + // We need to check if we've waited long enough since the last event was replayed. + for i, event := range g.Recording.KeyEvents { + var prevEventTimestamp int64 = 0 + if i > 0 { + prevEventTimestamp = g.Recording.KeyEvents[i-1].Timestamp + } + timeToWait := (event.Timestamp - prevEventTimestamp) / int64(g.RecordingConfig.Speed) + if i == 0 { + timeToWait += int64(g.RecordingConfig.Leeway) + } + var timeWaited int64 = 0 + middle: + for { + select { + case <-ticker.C: + timeWaited += 1 + if timeWaited >= timeToWait { + g.ReplayedEvents.keys <- event + break middle + } + case <-g.stop: + return } - case <-g.stop: - return } } - } + + waitGroup.Done() + }() + + go func() { + ticker := time.NewTicker(time.Millisecond) + defer ticker.Stop() + + // duplicating until Go gets generics + for i, event := range g.Recording.ResizeEvents { + var prevEventTimestamp int64 = 0 + if i > 0 { + prevEventTimestamp = g.Recording.ResizeEvents[i-1].Timestamp + } + timeToWait := (event.Timestamp - prevEventTimestamp) / int64(g.RecordingConfig.Speed) + if i == 0 { + timeToWait += int64(g.RecordingConfig.Leeway) + } + var timeWaited int64 = 0 + middle2: + for { + select { + case <-ticker.C: + timeWaited += 1 + if timeWaited >= timeToWait { + g.ReplayedEvents.resizes <- event + break middle2 + } + case <-g.stop: + return + } + } + } + + waitGroup.Done() + }() + + waitGroup.Wait() // leaving some time for any handlers to execute before quitting time.Sleep(time.Second * 1) diff --git a/vendor/github.com/jesseduffield/gocui/tcell_driver.go b/vendor/github.com/jesseduffield/gocui/tcell_driver.go index 12f269f04..e0378ac13 100644 --- a/vendor/github.com/jesseduffield/gocui/tcell_driver.go +++ b/vendor/github.com/jesseduffield/gocui/tcell_driver.go @@ -38,6 +38,17 @@ func tcellInit() error { } } +// tcellInitSimulation initializes tcell screen for use. +func tcellInitSimulation() error { + s := tcell.NewSimulationScreen("") + if e := s.Init(); e != nil { + return e + } else { + Screen = s + return nil + } +} + // tcellSetCell sets the character cell at a given location to the given // content (rune) and attributes using provided OutputMode func tcellSetCell(x, y int, ch rune, fg, bg Attribute, outputMode OutputMode) { @@ -166,6 +177,26 @@ func (wrapper TcellKeyEventWrapper) toTcellEvent() tcell.Event { return tcell.NewEventKey(wrapper.Key, wrapper.Ch, wrapper.Mod) } +type TcellResizeEventWrapper struct { + Timestamp int64 + Width int + Height int +} + +func NewTcellResizeEventWrapper(event *tcell.EventResize, timestamp int64) *TcellResizeEventWrapper { + w, h := event.Size() + + return &TcellResizeEventWrapper{ + Timestamp: timestamp, + Width: w, + Height: h, + } +} + +func (wrapper TcellResizeEventWrapper) toTcellEvent() tcell.Event { + return tcell.NewEventResize(wrapper.Width, wrapper.Height) +} + func (g *Gui) timeSinceStart() int64 { return time.Since(g.StartTime).Nanoseconds() / 1e6 } @@ -177,6 +208,8 @@ func (g *Gui) pollEvent() GocuiEvent { select { case ev := <-g.ReplayedEvents.keys: tev = (ev).toTcellEvent() + case ev := <-g.ReplayedEvents.resizes: + tev = (ev).toTcellEvent() } } else { tev = Screen.PollEvent() @@ -186,6 +219,12 @@ func (g *Gui) pollEvent() GocuiEvent { case *tcell.EventInterrupt: return GocuiEvent{Type: eventInterrupt} case *tcell.EventResize: + if g.PlayMode == RECORDING { + g.Recording.ResizeEvents = append( + g.Recording.ResizeEvents, NewTcellResizeEventWrapper(tev, g.timeSinceStart()), + ) + } + w, h := tev.Size() return GocuiEvent{Type: eventResize, Width: w, Height: h} case *tcell.EventKey: