mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-30 03:23:08 +03:00
Add key binding for switching from the commit message panel to an editor
This is useful for when you begin to type the message in lazygit's commit panel, and then realize that you'd rather use your editor's more powerful editing capabilities. Pressing <c-o> will take you right there.
This commit is contained in:
@ -68,6 +68,19 @@ func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj
|
|||||||
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
|
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitCommands) RewordLastCommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
|
||||||
|
return self.cmd.New(NewGitCmd("commit").
|
||||||
|
Arg("--allow-empty", "--amend", "--only", "--edit", "--file="+tmpMessageFile).ToArgv())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitCommands) CommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
|
||||||
|
return self.cmd.New(NewGitCmd("commit").
|
||||||
|
Arg("--edit").
|
||||||
|
Arg("--file="+tmpMessageFile).
|
||||||
|
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||||
|
ToArgv())
|
||||||
|
}
|
||||||
|
|
||||||
// RewordLastCommit rewords the topmost commit with the given message
|
// RewordLastCommit rewords the topmost commit with the given message
|
||||||
func (self *CommitCommands) RewordLastCommit(summary string, description string) error {
|
func (self *CommitCommands) RewordLastCommit(summary string, description string) error {
|
||||||
messageArgs := self.commitMessageArgs(summary, description)
|
messageArgs := self.commitMessageArgs(summary, description)
|
||||||
|
@ -132,16 +132,17 @@ type UpdateConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type KeybindingConfig struct {
|
type KeybindingConfig struct {
|
||||||
Universal KeybindingUniversalConfig `yaml:"universal"`
|
Universal KeybindingUniversalConfig `yaml:"universal"`
|
||||||
Status KeybindingStatusConfig `yaml:"status"`
|
Status KeybindingStatusConfig `yaml:"status"`
|
||||||
Files KeybindingFilesConfig `yaml:"files"`
|
Files KeybindingFilesConfig `yaml:"files"`
|
||||||
Branches KeybindingBranchesConfig `yaml:"branches"`
|
Branches KeybindingBranchesConfig `yaml:"branches"`
|
||||||
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
|
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
|
||||||
Commits KeybindingCommitsConfig `yaml:"commits"`
|
Commits KeybindingCommitsConfig `yaml:"commits"`
|
||||||
Stash KeybindingStashConfig `yaml:"stash"`
|
Stash KeybindingStashConfig `yaml:"stash"`
|
||||||
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
|
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
|
||||||
Main KeybindingMainConfig `yaml:"main"`
|
Main KeybindingMainConfig `yaml:"main"`
|
||||||
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
|
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
|
||||||
|
CommitMessage KeybindingCommitMessageConfig `yaml:"commitMessage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// damn looks like we have some inconsistencies here with -alt and -alt1
|
// damn looks like we have some inconsistencies here with -alt and -alt1
|
||||||
@ -305,6 +306,10 @@ type KeybindingSubmodulesConfig struct {
|
|||||||
BulkMenu string `yaml:"bulkMenu"`
|
BulkMenu string `yaml:"bulkMenu"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KeybindingCommitMessageConfig struct {
|
||||||
|
SwitchToEditor string `yaml:"switchToEditor"`
|
||||||
|
}
|
||||||
|
|
||||||
// OSConfig contains config on the level of the os
|
// OSConfig contains config on the level of the os
|
||||||
type OSConfig struct {
|
type OSConfig struct {
|
||||||
// Command for editing a file. Should contain "{{filename}}".
|
// Command for editing a file. Should contain "{{filename}}".
|
||||||
@ -652,6 +657,9 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
Update: "u",
|
Update: "u",
|
||||||
BulkMenu: "b",
|
BulkMenu: "b",
|
||||||
},
|
},
|
||||||
|
CommitMessage: KeybindingCommitMessageConfig{
|
||||||
|
SwitchToEditor: "<c-o>",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
OS: OSConfig{},
|
OS: OSConfig{},
|
||||||
DisableStartupPopups: false,
|
DisableStartupPopups: false,
|
||||||
|
@ -32,6 +32,8 @@ type CommitMessageViewModel struct {
|
|||||||
preservedMessage string
|
preservedMessage string
|
||||||
// invoked when pressing enter in the commit message panel
|
// invoked when pressing enter in the commit message panel
|
||||||
onConfirm func(string, string) error
|
onConfirm func(string, string) error
|
||||||
|
// invoked when pressing the switch-to-editor key binding
|
||||||
|
onSwitchToEditor func(string) error
|
||||||
|
|
||||||
// The message typed in before cycling through history
|
// The message typed in before cycling through history
|
||||||
// We store this separately to 'preservedMessage' because 'preservedMessage'
|
// We store this separately to 'preservedMessage' because 'preservedMessage'
|
||||||
@ -98,10 +100,12 @@ func (self *CommitMessageContext) SetPanelState(
|
|||||||
descriptionTitle string,
|
descriptionTitle string,
|
||||||
preserveMessage bool,
|
preserveMessage bool,
|
||||||
onConfirm func(string, string) error,
|
onConfirm func(string, string) error,
|
||||||
|
onSwitchToEditor func(string) error,
|
||||||
) {
|
) {
|
||||||
self.viewModel.selectedindex = index
|
self.viewModel.selectedindex = index
|
||||||
self.viewModel.preserveMessage = preserveMessage
|
self.viewModel.preserveMessage = preserveMessage
|
||||||
self.viewModel.onConfirm = onConfirm
|
self.viewModel.onConfirm = onConfirm
|
||||||
|
self.viewModel.onSwitchToEditor = onSwitchToEditor
|
||||||
self.GetView().Title = summaryTitle
|
self.GetView().Title = summaryTitle
|
||||||
self.c.Views().CommitDescription.Title = descriptionTitle
|
self.c.Views().CommitDescription.Title = descriptionTitle
|
||||||
}
|
}
|
||||||
@ -117,3 +121,11 @@ func (self *CommitMessageContext) RenderCommitLength() {
|
|||||||
func getBufferLength(view *gocui.View) string {
|
func getBufferLength(view *gocui.View) string {
|
||||||
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageContext) SwitchToEditor(message string) error {
|
||||||
|
return self.viewModel.onSwitchToEditor(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageContext) CanSwitchToEditor() bool {
|
||||||
|
return self.viewModel.onSwitchToEditor != nil
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,6 +35,10 @@ func (self *CommitDescriptionController) GetKeybindings(opts types.KeybindingsOp
|
|||||||
Key: opts.GetKey(opts.Config.Universal.ConfirmInEditor),
|
Key: opts.GetKey(opts.Config.Universal.ConfirmInEditor),
|
||||||
Handler: self.confirm,
|
Handler: self.confirm,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.CommitMessage.SwitchToEditor),
|
||||||
|
Handler: self.switchToEditor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
@ -43,7 +48,7 @@ func (self *CommitDescriptionController) Context() types.Context {
|
|||||||
return self.context()
|
return self.context()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionController) context() types.Context {
|
func (self *CommitDescriptionController) context() *context.CommitMessageContext {
|
||||||
return self.c.Contexts().CommitMessage
|
return self.c.Contexts().CommitMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,3 +63,7 @@ func (self *CommitDescriptionController) close() error {
|
|||||||
func (self *CommitDescriptionController) confirm() error {
|
func (self *CommitDescriptionController) confirm() error {
|
||||||
return self.c.Helpers().Commits.HandleCommitConfirm()
|
return self.c.Helpers().Commits.HandleCommitConfirm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitDescriptionController) switchToEditor() error {
|
||||||
|
return self.c.Helpers().Commits.SwitchToEditor()
|
||||||
|
}
|
||||||
|
@ -46,6 +46,10 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
|
|||||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||||
Handler: self.switchToCommitDescription,
|
Handler: self.switchToCommitDescription,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.CommitMessage.SwitchToEditor),
|
||||||
|
Handler: self.switchToEditor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
@ -84,6 +88,10 @@ func (self *CommitMessageController) switchToCommitDescription() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitMessageController) switchToEditor() error {
|
||||||
|
return self.c.Helpers().Commits.SwitchToEditor()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
||||||
currentIndex := self.context().GetSelectedIndex()
|
currentIndex := self.context().GetSelectedIndex()
|
||||||
newIndex := currentIndex + value
|
newIndex := currentIndex + value
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ICommitsHelper interface {
|
type ICommitsHelper interface {
|
||||||
@ -62,6 +65,28 @@ func (self *CommitsHelper) JoinCommitMessageAndDescription() string {
|
|||||||
return self.getCommitSummary() + "\n" + self.getCommitDescription()
|
return self.getCommitSummary() + "\n" + self.getCommitDescription()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CommitsHelper) SwitchToEditor() error {
|
||||||
|
if !self.c.Contexts().CommitMessage.CanSwitchToEditor() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
message := lo.Ternary(len(self.getCommitDescription()) == 0,
|
||||||
|
self.getCommitSummary(),
|
||||||
|
self.getCommitSummary()+"\n\n"+self.getCommitDescription())
|
||||||
|
filepath := filepath.Join(self.c.OS().GetTempDir(), self.c.Git().RepoPaths.RepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".msg")
|
||||||
|
err := self.c.OS().CreateFileWithContent(filepath, message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.CloseCommitMessagePanel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Contexts().CommitMessage.SwitchToEditor(filepath)
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CommitsHelper) UpdateCommitPanelView(message string) {
|
func (self *CommitsHelper) UpdateCommitPanelView(message string) {
|
||||||
if message != "" {
|
if message != "" {
|
||||||
self.SetMessageAndDescriptionInView(message)
|
self.SetMessageAndDescriptionInView(message)
|
||||||
@ -83,6 +108,7 @@ type OpenCommitMessagePanelOpts struct {
|
|||||||
DescriptionTitle string
|
DescriptionTitle string
|
||||||
PreserveMessage bool
|
PreserveMessage bool
|
||||||
OnConfirm func(summary string, description string) error
|
OnConfirm func(summary string, description string) error
|
||||||
|
OnSwitchToEditor func(string) error
|
||||||
InitialMessage string
|
InitialMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +127,7 @@ func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOp
|
|||||||
opts.DescriptionTitle,
|
opts.DescriptionTitle,
|
||||||
opts.PreserveMessage,
|
opts.PreserveMessage,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
|
opts.OnSwitchToEditor,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.UpdateCommitPanelView(opts.InitialMessage)
|
self.UpdateCommitPanelView(opts.InitialMessage)
|
||||||
|
@ -104,6 +104,7 @@ func (self *WorkingTreeHelper) HandleCommitPressWithMessage(initialMessage strin
|
|||||||
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
|
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
|
||||||
PreserveMessage: true,
|
PreserveMessage: true,
|
||||||
OnConfirm: self.handleCommit,
|
OnConfirm: self.handleCommit,
|
||||||
|
OnSwitchToEditor: self.switchFromCommitMessagePanelToEditor,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -117,6 +118,21 @@ func (self *WorkingTreeHelper) handleCommit(summary string, description string)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *WorkingTreeHelper) switchFromCommitMessagePanelToEditor(filepath string) error {
|
||||||
|
// We won't be able to tell whether the commit was successful, because
|
||||||
|
// RunSubprocessAndRefresh doesn't return the error (it opens an error alert
|
||||||
|
// itself and returns nil on error). But even if we could, we wouldn't have
|
||||||
|
// access to the last message that the user typed, and it might be very
|
||||||
|
// different from what was last in the commit panel. So the best we can do
|
||||||
|
// here is to always clear the remembered commit message.
|
||||||
|
self.commitsHelper.OnCommitSuccess()
|
||||||
|
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.Commit)
|
||||||
|
return self.c.RunSubprocessAndRefresh(
|
||||||
|
self.c.Git().Commit.CommitInEditorWithMessageFileCmdObj(filepath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleCommitEditorPress - handle when the user wants to commit changes via
|
// HandleCommitEditorPress - handle when the user wants to commit changes via
|
||||||
// their editor rather than via the popup panel
|
// their editor rather than via the popup panel
|
||||||
func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
|
func (self *WorkingTreeHelper) HandleCommitEditorPress() error {
|
||||||
|
@ -279,10 +279,37 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
|||||||
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
|
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
|
||||||
PreserveMessage: false,
|
PreserveMessage: false,
|
||||||
OnConfirm: self.handleReword,
|
OnConfirm: self.handleReword,
|
||||||
|
OnSwitchToEditor: self.switchFromCommitMessagePanelToEditor,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsController) switchFromCommitMessagePanelToEditor(filepath string) error {
|
||||||
|
if self.isHeadCommit() {
|
||||||
|
return self.c.RunSubprocessAndRefresh(
|
||||||
|
self.c.Git().Commit.RewordLastCommitInEditorWithMessageFileCmdObj(filepath))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := self.c.Git().Rebase.BeginInteractiveRebaseForCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx(), false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// now the selected commit should be our head so we'll amend it with the new message
|
||||||
|
err = self.c.RunSubprocessAndRefresh(
|
||||||
|
self.c.Git().Commit.RewordLastCommitInEditorWithMessageFileCmdObj(filepath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.c.Git().Rebase.ContinueRebase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) handleReword(summary string, description string) error {
|
func (self *LocalCommitsController) handleReword(summary string, description string) error {
|
||||||
err := self.c.Git().Rebase.RewordCommit(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), summary, description)
|
err := self.c.Git().Rebase.RewordCommit(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), summary, description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Reference in New Issue
Block a user