mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
This is how we do it for confirmation with suggestions too, so be consistent. It will make things easier later in this branch if we only have one context per "panel" on the stack, even if the panel consists of two views. Concretely this means: - only push the message context onto the stack when opening the panel (this requires making the description view visible manually; we do the same for suggestions) - when switching between message and description, use ReplaceContext rather than PushContext
274 lines
7.6 KiB
Go
274 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
|
|
}
|
|
|
|
err = self.CloseCommitMessagePanel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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) error {
|
|
onConfirm := func(summary string, description string) error {
|
|
if err := self.CloseCommitMessagePanel(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return opts.OnConfirm(summary, description)
|
|
}
|
|
|
|
self.c.Contexts().CommitMessage.SetPanelState(
|
|
opts.CommitIndex,
|
|
opts.SummaryTitle,
|
|
opts.DescriptionTitle,
|
|
opts.PreserveMessage,
|
|
onConfirm,
|
|
opts.OnSwitchToEditor,
|
|
)
|
|
|
|
self.UpdateCommitPanelView(opts.InitialMessage)
|
|
|
|
return self.c.PushContext(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() error {
|
|
if self.c.Contexts().CommitMessage.GetPreserveMessage() {
|
|
message := self.JoinCommitMessageAndUnwrappedDescription()
|
|
|
|
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
|
|
|
|
return self.c.PopContext()
|
|
}
|
|
|
|
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 {
|
|
return 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
|
|
},
|
|
})
|
|
}
|
|
|
|
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
|
|
return self.c.Confirm(types.ConfirmOpts{
|
|
Title: self.c.Tr.PasteCommitMessageFromClipboard,
|
|
Prompt: self.c.Tr.SurePasteCommitMessage,
|
|
HandleConfirm: func() error {
|
|
self.SetMessageAndDescriptionInView(message)
|
|
return nil
|
|
},
|
|
})
|
|
}
|