mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-15 00:15:32 +02:00
... and when recalling a commit message from an old commit by pressing up-arrow. This is necessary because committing turns our soft line breaks into real ones, but when rewording we want to turn them back into soft ones again, so that it's possible to insert words at the beginning of a paragraph and have everything rewrap nicely. This is only a best effort; the algorithm only removes those hard line breaks that can be removed without changing the way the message looks. This works well when the previous commit message was wrapped at the same width, which for most users should be the most common case; but if it wasn't, the result is not great. Specifically, if the old wrap width was smaller, some hard line breaks just won't be removed; if it was wider though, you'll get an unpleasant comb effect with alternating long and short lines. In such a case it's best to switch to the editor and use whatever wrapping features you have there (e.g. alt-Q).
218 lines
6.0 KiB
Go
218 lines
6.0 KiB
Go
package helpers
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jesseduffield/gocui"
|
|
"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 {
|
|
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) {
|
|
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.pushCommitMessageContexts()
|
|
}
|
|
|
|
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 self.c.ErrorMsg(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("")
|
|
|
|
return self.PopCommitMessageContexts()
|
|
}
|
|
|
|
func (self *CommitsHelper) PopCommitMessageContexts() error {
|
|
return self.c.RemoveContexts(self.commitMessageContexts())
|
|
}
|
|
|
|
func (self *CommitsHelper) pushCommitMessageContexts() error {
|
|
for _, context := range self.commitMessageContexts() {
|
|
if err := self.c.PushContext(context); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *CommitsHelper) commitMessageContexts() []types.Context {
|
|
return []types.Context{
|
|
self.c.Contexts().CommitDescription,
|
|
self.c.Contexts().CommitMessage,
|
|
}
|
|
}
|