mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-25 12:24:47 +02:00
Sometimes we populate the commit message panel with a pre-created commit message. The two cases where this happens is: - you type `w` to commit, in which case we put the skipHookPrefix in the subject - you have a commitPrefix pattern, in which case we match it against the branch name and populate the subject with the replacement string if it matches In either case, if you have a preserved commit message, we use that. Now, when you use either of these and then cancel, we preserve that initial, unchanged message and reuse it the next time you commit. This has two problems: it strips spaces, which is a problem for the commitPrefix patterns, which often end with a space. And also, when you change your config to experiment with commitPrefix patterns, the change seemingly doesn't take effect, which can be very confusing. To fix both of these problems, only preserve the commit message when it is not identical to the initial message.
275 lines
7.6 KiB
Go
275 lines
7.6 KiB
Go
package helpers
|
|
|
|
import (
|
|
"errors"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jesseduffield/gocui"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type ICommitsHelper interface {
|
|
UpdateCommitPanelView(message string)
|
|
}
|
|
|
|
type CommitsHelper struct {
|
|
c *HelperCommon
|
|
|
|
getCommitSummary func() string
|
|
setCommitSummary func(string)
|
|
getCommitDescription func() string
|
|
getUnwrappedCommitDescription func() string
|
|
setCommitDescription func(string)
|
|
}
|
|
|
|
var _ ICommitsHelper = &CommitsHelper{}
|
|
|
|
func NewCommitsHelper(
|
|
c *HelperCommon,
|
|
getCommitSummary func() string,
|
|
setCommitSummary func(string),
|
|
getCommitDescription func() string,
|
|
getUnwrappedCommitDescription func() string,
|
|
setCommitDescription func(string),
|
|
) *CommitsHelper {
|
|
return &CommitsHelper{
|
|
c: c,
|
|
getCommitSummary: getCommitSummary,
|
|
setCommitSummary: setCommitSummary,
|
|
getCommitDescription: getCommitDescription,
|
|
getUnwrappedCommitDescription: getUnwrappedCommitDescription,
|
|
setCommitDescription: setCommitDescription,
|
|
}
|
|
}
|
|
|
|
func (self *CommitsHelper) SplitCommitMessageAndDescription(message string) (string, string) {
|
|
msg, description, _ := strings.Cut(message, "\n")
|
|
return msg, strings.TrimSpace(description)
|
|
}
|
|
|
|
func (self *CommitsHelper) SetMessageAndDescriptionInView(message string) {
|
|
summary, description := self.SplitCommitMessageAndDescription(message)
|
|
|
|
self.setCommitSummary(summary)
|
|
self.setCommitDescription(description)
|
|
self.c.Contexts().CommitMessage.RenderCommitLength()
|
|
}
|
|
|
|
func (self *CommitsHelper) JoinCommitMessageAndUnwrappedDescription() string {
|
|
if len(self.getUnwrappedCommitDescription()) == 0 {
|
|
return self.getCommitSummary()
|
|
}
|
|
return self.getCommitSummary() + "\n" + self.getUnwrappedCommitDescription()
|
|
}
|
|
|
|
func TryRemoveHardLineBreaks(message string, autoWrapWidth int) string {
|
|
messageRunes := []rune(message)
|
|
lastHardLineStart := 0
|
|
for i, r := range messageRunes {
|
|
if r == '\n' {
|
|
// Try to make this a soft linebreak by turning it into a space, and
|
|
// checking whether it still wraps to the same result then.
|
|
messageRunes[i] = ' '
|
|
|
|
_, cursorMapping := gocui.AutoWrapContent(messageRunes[lastHardLineStart:], autoWrapWidth)
|
|
|
|
// Look at the cursorMapping to check whether auto-wrapping inserted
|
|
// a line break. If it did, there will be a cursorMapping entry with
|
|
// Orig pointing to the position after the inserted line break.
|
|
if len(cursorMapping) == 0 || cursorMapping[0].Orig != i-lastHardLineStart+1 {
|
|
// It didn't, so change it back to a newline
|
|
messageRunes[i] = '\n'
|
|
}
|
|
lastHardLineStart = i + 1
|
|
}
|
|
}
|
|
|
|
return string(messageRunes)
|
|
}
|
|
|
|
func (self *CommitsHelper) SwitchToEditor() error {
|
|
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
|
|
}
|
|
|
|
self.CloseCommitMessagePanel()
|
|
|
|
return self.c.Contexts().CommitMessage.SwitchToEditor(filepath)
|
|
}
|
|
|
|
func (self *CommitsHelper) UpdateCommitPanelView(message string) {
|
|
if message != "" {
|
|
self.SetMessageAndDescriptionInView(message)
|
|
return
|
|
}
|
|
|
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
|
preservedMessage := self.c.Contexts().CommitMessage.GetPreservedMessage()
|
|
self.SetMessageAndDescriptionInView(preservedMessage)
|
|
return
|
|
}
|
|
|
|
self.SetMessageAndDescriptionInView("")
|
|
}
|
|
|
|
type OpenCommitMessagePanelOpts struct {
|
|
CommitIndex int
|
|
SummaryTitle string
|
|
DescriptionTitle string
|
|
PreserveMessage bool
|
|
OnConfirm func(summary string, description string) error
|
|
OnSwitchToEditor func(string) error
|
|
InitialMessage string
|
|
}
|
|
|
|
func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOpts) {
|
|
onConfirm := func(summary string, description string) error {
|
|
self.CloseCommitMessagePanel()
|
|
|
|
return opts.OnConfirm(summary, description)
|
|
}
|
|
|
|
self.c.Contexts().CommitMessage.SetPanelState(
|
|
opts.CommitIndex,
|
|
opts.SummaryTitle,
|
|
opts.DescriptionTitle,
|
|
opts.PreserveMessage,
|
|
opts.InitialMessage,
|
|
onConfirm,
|
|
opts.OnSwitchToEditor,
|
|
)
|
|
|
|
self.UpdateCommitPanelView(opts.InitialMessage)
|
|
|
|
self.c.Context().Push(self.c.Contexts().CommitMessage)
|
|
}
|
|
|
|
func (self *CommitsHelper) OnCommitSuccess() {
|
|
// if we have a preserved message we want to clear it on success
|
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
|
self.c.Contexts().CommitMessage.SetPreservedMessage("")
|
|
}
|
|
}
|
|
|
|
func (self *CommitsHelper) HandleCommitConfirm() error {
|
|
summary, description := self.getCommitSummary(), self.getCommitDescription()
|
|
|
|
if summary == "" {
|
|
return errors.New(self.c.Tr.CommitWithoutMessageErr)
|
|
}
|
|
|
|
err := self.c.Contexts().CommitMessage.OnConfirm(summary, description)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *CommitsHelper) CloseCommitMessagePanel() {
|
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
|
message := self.JoinCommitMessageAndUnwrappedDescription()
|
|
if message != self.c.Contexts().CommitMessage.GetInitialMessage() {
|
|
self.c.Contexts().CommitMessage.SetPreservedMessage(message)
|
|
}
|
|
} else {
|
|
self.SetMessageAndDescriptionInView("")
|
|
}
|
|
|
|
self.c.Contexts().CommitMessage.SetHistoryMessage("")
|
|
|
|
self.c.Views().CommitMessage.Visible = false
|
|
self.c.Views().CommitDescription.Visible = false
|
|
|
|
self.c.Context().Pop()
|
|
}
|
|
|
|
func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.Suggestion) error {
|
|
var disabledReasonForOpenInEditor *types.DisabledReason
|
|
if !self.c.Contexts().CommitMessage.CanSwitchToEditor() {
|
|
disabledReasonForOpenInEditor = &types.DisabledReason{
|
|
Text: self.c.Tr.CommandDoesNotSupportOpeningInEditor,
|
|
}
|
|
}
|
|
|
|
menuItems := []*types.MenuItem{
|
|
{
|
|
Label: self.c.Tr.OpenInEditor,
|
|
OnPress: func() error {
|
|
return self.SwitchToEditor()
|
|
},
|
|
Key: 'e',
|
|
DisabledReason: disabledReasonForOpenInEditor,
|
|
},
|
|
{
|
|
Label: self.c.Tr.AddCoAuthor,
|
|
OnPress: func() error {
|
|
return self.addCoAuthor(suggestionFunc)
|
|
},
|
|
Key: 'c',
|
|
},
|
|
{
|
|
Label: self.c.Tr.PasteCommitMessageFromClipboard,
|
|
OnPress: func() error {
|
|
return self.pasteCommitMessageFromClipboard()
|
|
},
|
|
Key: 'p',
|
|
},
|
|
}
|
|
return self.c.Menu(types.CreateMenuOptions{
|
|
Title: self.c.Tr.CommitMenuTitle,
|
|
Items: menuItems,
|
|
})
|
|
}
|
|
|
|
func (self *CommitsHelper) addCoAuthor(suggestionFunc func(string) []*types.Suggestion) error {
|
|
self.c.Prompt(types.PromptOpts{
|
|
Title: self.c.Tr.AddCoAuthorPromptTitle,
|
|
FindSuggestionsFunc: suggestionFunc,
|
|
HandleConfirm: func(value string) error {
|
|
commitDescription := self.getCommitDescription()
|
|
commitDescription = git_commands.AddCoAuthorToDescription(commitDescription, value)
|
|
self.setCommitDescription(commitDescription)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *CommitsHelper) pasteCommitMessageFromClipboard() error {
|
|
message, err := self.c.OS().PasteFromClipboard()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if message == "" {
|
|
return nil
|
|
}
|
|
|
|
if currentMessage := self.JoinCommitMessageAndUnwrappedDescription(); currentMessage == "" {
|
|
self.SetMessageAndDescriptionInView(message)
|
|
return nil
|
|
}
|
|
|
|
// Confirm before overwriting the commit message
|
|
self.c.Confirm(types.ConfirmOpts{
|
|
Title: self.c.Tr.PasteCommitMessageFromClipboard,
|
|
Prompt: self.c.Tr.SurePasteCommitMessage,
|
|
HandleConfirm: func() error {
|
|
self.SetMessageAndDescriptionInView(message)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
return nil
|
|
}
|