diff --git a/ZHgalGrWSF b/ZHgalGrWSF new file mode 100644 index 000000000..f1f0d2371 --- /dev/null +++ b/ZHgalGrWSF @@ -0,0 +1 @@ +GaUMygWjJa \ No newline at end of file diff --git a/commit_message_panel.go b/commit_message_panel.go new file mode 100644 index 000000000..49551c1eb --- /dev/null +++ b/commit_message_panel.go @@ -0,0 +1,43 @@ +package main + +import "github.com/jesseduffield/gocui" + +func handleCommitConfirm(g *gocui.Gui, v *gocui.View) error { + message := trimmedContent(v) + if message == "" { + return createErrorPanel(g, "You cannot commit without a commit message") + } + if output, err := gitCommit(g, message); err != nil { + if err == errNoUsername { + return createErrorPanel(g, err.Error()) + } + return createErrorPanel(g, output) + } + refreshFiles(g) + g.SetViewOnBottom("commitMessage") + switchFocus(g, v, getFilesView(g)) + return refreshCommits(g) +} + +func handleCommitClose(g *gocui.Gui, v *gocui.View) error { + g.SetViewOnBottom("commitMessage") + return switchFocus(g, v, getFilesView(g)) +} + +func handleNewlineCommitMessage(g *gocui.Gui, v *gocui.View) error { + // resising ahead of time so that the top line doesn't get hidden to make + // room for the cursor on the second line + x0, y0, x1, y1 := getConfirmationPanelDimensions(g, v.Buffer()) + if _, err := g.SetView("commitMessage", x0, y0, x1, y1+1, 0); err != nil { + if err != gocui.ErrUnknownView { + return err + } + } + + v.EditNewLine() + return nil +} + +func handleCommitFocused(g *gocui.Gui, v *gocui.View) error { + return renderString(g, "options", "esc: close, enter: confirm") +} diff --git a/confirmation_panel.go b/confirmation_panel.go index 7ae55eaf9..03e078155 100644 --- a/confirmation_panel.go +++ b/confirmation_panel.go @@ -56,6 +56,10 @@ func getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int, } func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, initialValue *[]byte, handleConfirm func(*gocui.Gui, *gocui.View) error) error { + g.SetViewOnBottom("commitMessage") + if initialValue == nil { + initialValue = &[]byte{} + } // only need to fit one line x0, y0, x1, y1 := getConfirmationPanelDimensions(g, "") if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil { @@ -63,9 +67,7 @@ func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, init return err } - g.Cursor = true - - handleConfirmAndClear := func(gui *gocui.Gui, view *gocui.View) error { + handleConfirm := func(gui *gocui.Gui, view *gocui.View) error { *initialValue = nil return handleConfirm(g, view) } @@ -79,15 +81,31 @@ func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, init confirmationView.Editable = true confirmationView.Title = title - confirmationView.Write(*initialValue) - confirmationView.SetCursor(len(*initialValue), 0) + restorePreviousBuffer(confirmationView, initialValue) switchFocus(g, currentView, confirmationView) - return setKeyBindings(g, handleConfirmAndClear, handleClose) + return setKeyBindings(g, handleConfirm, handleClose) } return nil } +func restorePreviousBuffer(confirmationView *gocui.View, initialValue *[]byte) { + confirmationView.Write(*initialValue) + x, y := getCursorPositionFromBuffer(initialValue) + devLog("New cursor position:", x, y) + confirmationView.SetCursor(0, 0) + confirmationView.MoveCursor(x, y, false) +} + +func getCursorPositionFromBuffer(initialValue *[]byte) (int, int) { + split := strings.Split(string(*initialValue), "\n") + lastLine := split[len(split)-1] + x := len(lastLine) + y := len(split) + return x, y +} + func createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { + g.SetViewOnBottom("commitMessage") g.Update(func(g *gocui.Gui) error { // delete the existing confirmation panel if it exists if view, _ := g.View("confirmation"); view != nil { @@ -154,15 +172,20 @@ func trimTrailingNewline(str string) string { return str } -func resizeConfirmationPanel(g *gocui.Gui) error { +func resizeConfirmationPanel(g *gocui.Gui, viewName string) error { // If the confirmation panel is already displayed, just resize the width, // otherwise continue - if v, err := g.View("confirmation"); err == nil { + g.Update(func(g *gocui.Gui) error { + v, err := g.View(viewName) + if err != nil { + return nil + } content := trimTrailingNewline(v.Buffer()) x0, y0, x1, y1 := getConfirmationPanelDimensions(g, content) - if _, err = g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil { + if _, err := g.SetView(viewName, x0, y0, x1, y1, 0); err != nil { return err } - } + return nil + }) return nil } diff --git a/files_panel.go b/files_panel.go index b2de2c226..75e20885f 100644 --- a/files_panel.go +++ b/files_panel.go @@ -15,9 +15,8 @@ import ( ) var ( - savedCommitMessage = &[]byte{} - errNoFiles = errors.New("No changed files") - errNoUsername = errors.New(`No username set. Please do: git config --global user.name "Your Name"`) + errNoFiles = errors.New("No changed files") + errNoUsername = errors.New(`No username set. Please do: git config --global user.name "Your Name"`) ) func stagedFiles(files []GitFile) []GitFile { @@ -178,19 +177,11 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error { if len(stagedFiles(state.GitFiles)) == 0 && !state.HasMergeConflicts { return createErrorPanel(g, "There are no staged files to commit") } - createPromptPanel(g, filesView, "Commit message", savedCommitMessage, func(g *gocui.Gui, v *gocui.View) error { - message := trimmedContent(v) - if message == "" { - return createErrorPanel(g, "You cannot commit without a commit message") - } - if output, err := gitCommit(g, message); err != nil { - if err == errNoUsername { - return createErrorPanel(g, err.Error()) - } - return createErrorPanel(g, output) - } - refreshFiles(g) - return refreshCommits(g) + commitMessageView := getCommitMessageView(g) + g.Update(func(g *gocui.Gui) error { + g.SetViewOnTop("commitMessage") + switchFocus(g, filesView, commitMessageView) + return nil }) return nil } diff --git a/gitcommands.go b/gitcommands.go index 2d073aec9..798627c24 100644 --- a/gitcommands.go +++ b/gitcommands.go @@ -107,13 +107,11 @@ func mergeGitStatusFiles(oldGitFiles, newGitFiles []GitFile) []GitFile { } func runDirectCommand(command string) (string, error) { - timeStart := time.Now() commandLog(command) cmdOut, err := exec. Command(state.Platform.shell, state.Platform.shellArg, command). CombinedOutput() - devLog("run direct command time for command: ", command, time.Now().Sub(timeStart)) return sanitisedCommandOutput(cmdOut, err) } @@ -218,12 +216,9 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) { } func runCommand(command string) (string, error) { - commandStartTime := time.Now() commandLog(command) splitCmd := strings.Split(command, " ") - devLog(splitCmd) cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).CombinedOutput() - devLog("run command time: ", time.Now().Sub(commandStartTime)) return sanitisedCommandOutput(cmdOut, err) } diff --git a/gui.go b/gui.go index 4cf7d7df5..b6258ec95 100644 --- a/gui.go +++ b/gui.go @@ -199,15 +199,26 @@ func layout(g *gocui.Gui) error { if err != gocui.ErrUnknownView { return err } - v.BgColor = gocui.ColorDefault v.FgColor = gocui.ColorBlue v.Frame = false } - if err = resizeConfirmationPanel(g); err != nil { - return err + if getCommitMessageView(g) == nil { + // doesn't matter where this view starts because it will be hidden + if commitMessageView, err := g.SetView("commitMessage", 0, 0, width, height, 0); err != nil { + if err != gocui.ErrUnknownView { + return err + } + g.SetViewOnBottom("commitMessage") + commitMessageView.Title = "Commit message" + commitMessageView.FgColor = gocui.ColorWhite + commitMessageView.Editable = true + } } + resizeConfirmationPanel(g, "commitMessage") + resizeConfirmationPanel(g, "confirmation") + if v, err := g.SetView("version", width-len(version)-1, optionsTop, width, optionsTop+2, 0); err != nil { if err != gocui.ErrUnknownView { return err diff --git a/keybindings.go b/keybindings.go index 07309790f..bc4a67015 100644 --- a/keybindings.go +++ b/keybindings.go @@ -58,6 +58,9 @@ func keybindings(g *gocui.Gui) error { {ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply}, {ViewName: "stash", Key: 'g', Modifier: gocui.ModNone, Handler: handleStashPop}, {ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop}, + {ViewName: "commitMessage", Key: gocui.KeyEnter, Modifier: gocui.ModNone, Handler: handleCommitConfirm}, + {ViewName: "commitMessage", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleCommitClose}, + {ViewName: "commitMessage", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: handleNewlineCommitMessage}, } // Would make these keybindings global but that interferes with editing diff --git a/view_helpers.go b/view_helpers.go index 2f5f2caf0..fdf3a802f 100644 --- a/view_helpers.go +++ b/view_helpers.go @@ -75,6 +75,8 @@ func newLineFocused(g *gocui.Gui, v *gocui.View) error { return handleBranchSelect(g, v) case "confirmation": return nil + case "commitMessage": + return handleCommitFocused(g, v) case "main": // TODO: pull this out into a 'view focused' function refreshMergePanel(g) @@ -215,3 +217,19 @@ func loader() string { index := nanos / 50000000 % int64(len(characters)) return characters[index : index+1] } + +// TODO: refactor properly +func getFilesView(g *gocui.Gui) *gocui.View { + v, _ := g.View("files") + return v +} + +func getCommitsView(g *gocui.Gui) *gocui.View { + v, _ := g.View("commits") + return v +} + +func getCommitMessageView(g *gocui.Gui) *gocui.View { + v, _ := g.View("commitMessage") + return v +}