mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-17 21:18:31 +02:00
support tcell simulation screen
This commit is contained in:
parent
7782fe92d8
commit
b3696e2005
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
0000000000000000000000000000000000000000 55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa CI <CI@example.com> 1617579407 +1000 commit (initial): myfile1
|
||||
55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa e68c1143545a09cac1db6e6f20db13b82b5dc43e CI <CI@example.com> 1617579407 +1000 commit: myfile2
|
||||
e68c1143545a09cac1db6e6f20db13b82b5dc43e ba001a041f64109b238f03550d6a21209bd5d4fc CI <CI@example.com> 1617579407 +1000 commit: myfile3
|
||||
ba001a041f64109b238f03550d6a21209bd5d4fc 6f31cc35ebabdf3fb71759e3d267677e712f737f CI <CI@example.com> 1617579407 +1000 commit: myfile4
|
||||
6f31cc35ebabdf3fb71759e3d267677e712f737f ecc083de4d216847c184c72a639743bba6520d88 CI <CI@example.com> 1617579409 +1000 commit: commit
|
||||
0000000000000000000000000000000000000000 47912ceb0ffa9b205290e75103d9dd6b1878e87b CI <CI@example.com> 1617580570 +1000 commit (initial): myfile1
|
||||
47912ceb0ffa9b205290e75103d9dd6b1878e87b 6bd1486e023f9853f9e6f611cac5ccbc8960ce57 CI <CI@example.com> 1617580570 +1000 commit: myfile2
|
||||
6bd1486e023f9853f9e6f611cac5ccbc8960ce57 980224a1dd75d91fdcab11edc8d25c3ff8f751ba CI <CI@example.com> 1617580570 +1000 commit: myfile3
|
||||
980224a1dd75d91fdcab11edc8d25c3ff8f751ba f4e779d1bd2ad074259ad763210f5b911337054f CI <CI@example.com> 1617580570 +1000 commit: myfile4
|
||||
f4e779d1bd2ad074259ad763210f5b911337054f d0cab53ed70fc66096575c2ccd7ef150b4b470e8 CI <CI@example.com> 1617580572 +1000 commit: commit
|
||||
|
@ -1,5 +1,5 @@
|
||||
0000000000000000000000000000000000000000 55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa CI <CI@example.com> 1617579407 +1000 commit (initial): myfile1
|
||||
55bf9e2babc63fb5bae4b42c79fb1ddd1f4205fa e68c1143545a09cac1db6e6f20db13b82b5dc43e CI <CI@example.com> 1617579407 +1000 commit: myfile2
|
||||
e68c1143545a09cac1db6e6f20db13b82b5dc43e ba001a041f64109b238f03550d6a21209bd5d4fc CI <CI@example.com> 1617579407 +1000 commit: myfile3
|
||||
ba001a041f64109b238f03550d6a21209bd5d4fc 6f31cc35ebabdf3fb71759e3d267677e712f737f CI <CI@example.com> 1617579407 +1000 commit: myfile4
|
||||
6f31cc35ebabdf3fb71759e3d267677e712f737f ecc083de4d216847c184c72a639743bba6520d88 CI <CI@example.com> 1617579409 +1000 commit: commit
|
||||
0000000000000000000000000000000000000000 47912ceb0ffa9b205290e75103d9dd6b1878e87b CI <CI@example.com> 1617580570 +1000 commit (initial): myfile1
|
||||
47912ceb0ffa9b205290e75103d9dd6b1878e87b 6bd1486e023f9853f9e6f611cac5ccbc8960ce57 CI <CI@example.com> 1617580570 +1000 commit: myfile2
|
||||
6bd1486e023f9853f9e6f611cac5ccbc8960ce57 980224a1dd75d91fdcab11edc8d25c3ff8f751ba CI <CI@example.com> 1617580570 +1000 commit: myfile3
|
||||
980224a1dd75d91fdcab11edc8d25c3ff8f751ba f4e779d1bd2ad074259ad763210f5b911337054f CI <CI@example.com> 1617580570 +1000 commit: myfile4
|
||||
f4e779d1bd2ad074259ad763210f5b911337054f d0cab53ed70fc66096575c2ccd7ef150b4b470e8 CI <CI@example.com> 1617580572 +1000 commit: commit
|
||||
|
@ -0,0 +1,2 @@
|
||||
x�ÍA
|
||||
Â0@Q×9Å왩“I¡«#M&Xè�R"èííÜ~üÜÌ–Ärê»* J®˜d7�…Y)J‰4$ª�g¾áš²\z÷WÛaœà>NOý$ÛV½äf ¡à#ú€p&DtG=&]ÿäξuY•Ü16,Ç
|
@ -1,2 +0,0 @@
|
||||
x�ΝA
|
||||
ƒ0@Ρ®s�ΩΚ�'J\y��L¨ΰ�")΄·Χ#tϋyπS5[Λ¥ν�€*©`”Ε2³R�¨‹T</|ΟΒ%¦ΎsρΣ^u‡i†Η4�ϊ�φήτ–�=�„|οFWBDwΦs�τOξμWΦMΙ3‰,Υ
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +0,0 @@
|
||||
x�ÎA
|
||||
1…a×=Eö‚4M§± "ÌjŽ‘¶)
|
||||
Ö†
|
||||
ß.<€ÛÇ÷àÏkk�éÐwU +˜…ükÑLÎOÉ:QÉ5±+¥ªO”Ôl²ë«C¨„9Ó¤IR©4ò•Š˜•ÑU&®FÞý¾î0/p™—›~¤mO=åµ]Ãøpô6µf¬#ªëŸüçÍe);a
|
@ -0,0 +1,2 @@
|
||||
x�ŽK
|
||||
Â0@]çÙ2“dò�"BW=Æ4™`¡±¥DÐÛÛ#¸}<x/o-]cr—~ˆhS=6‚³±¢›Ñ{ŠlÉKðÆYÊL Õ·¼ºNŒqŒ¥* kÉ<#JɱʶÖXáÌŠßý¹zœô0NùpÛW¹åÝ5žIŠ@ô@�ôœêò§®Ú·.«8õ#Ó9}
|
@ -1 +1 @@
|
||||
ecc083de4d216847c184c72a639743bba6520d88
|
||||
d0cab53ed70fc66096575c2ccd7ef150b4b470e8
|
||||
|
@ -1 +1 @@
|
||||
{"KeyEvents":[{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":32},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":99},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":99},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":111},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":109},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":109},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":105},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":116},{"Timestamp":9223372036854,"Mod":0,"Key":13,"Ch":13},{"Timestamp":9223372036854,"Mod":0,"Key":256,"Ch":113}]}
|
||||
{"KeyEvents":[{"Timestamp":527,"Mod":0,"Key":256,"Ch":32},{"Timestamp":830,"Mod":0,"Key":256,"Ch":99},{"Timestamp":1127,"Mod":0,"Key":256,"Ch":99},{"Timestamp":1190,"Mod":0,"Key":256,"Ch":111},{"Timestamp":1335,"Mod":0,"Key":256,"Ch":109},{"Timestamp":1447,"Mod":0,"Key":256,"Ch":109},{"Timestamp":1583,"Mod":0,"Key":256,"Ch":105},{"Timestamp":1606,"Mod":0,"Key":256,"Ch":116},{"Timestamp":1935,"Mod":0,"Key":13,"Ch":13},{"Timestamp":2353,"Mod":0,"Key":27,"Ch":0}],"ResizeEvents":[{"Timestamp":0,"Width":127,"Height":35}]}
|
||||
|
@ -1 +1 @@
|
||||
{ "description": "stage a file and commit the change", "speed": 20 }
|
||||
{ "description": "stage a file and commit the change", "speed": 15 }
|
||||
|
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@ -16,15 +15,12 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
var errSubProcess = errors.New("subprocess")
|
||||
|
||||
type App struct {
|
||||
tests []*IntegrationTest
|
||||
itemIdx int
|
||||
subProcess *exec.Cmd
|
||||
testDir string
|
||||
editing bool
|
||||
g *gocui.Gui
|
||||
tests []*IntegrationTest
|
||||
itemIdx int
|
||||
testDir string
|
||||
editing bool
|
||||
g *gocui.Gui
|
||||
}
|
||||
|
||||
func (app *App) getCurrentTest() *IntegrationTest {
|
||||
@ -70,217 +66,217 @@ func main() {
|
||||
app := &App{testDir: testDir}
|
||||
app.loadTests()
|
||||
|
||||
Loop:
|
||||
for {
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL)
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
g.Cursor = false
|
||||
|
||||
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--
|
||||
}
|
||||
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
|
||||
|
127
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
127
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -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)
|
||||
|
39
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
39
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user