1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-23 12:18:51 +02:00
lazygit/pkg/gui/controllers/custom_patch_options_menu_action.go
Stefan Haller 22dc7fece9 Have only one of commit message and description on the context stack at a time
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
2024-06-27 10:14:00 +02:00

258 lines
7.9 KiB
Go

package controllers
import (
"errors"
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type CustomPatchOptionsMenuAction struct {
c *ControllerCommon
}
func (self *CustomPatchOptionsMenuAction) Call() error {
if !self.c.Git().Patch.PatchBuilder.Active() {
return errors.New(self.c.Tr.NoPatchError)
}
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
return errors.New(self.c.Tr.EmptyPatchError)
}
menuItems := []*types.MenuItem{
{
Label: self.c.Tr.ResetPatch,
Tooltip: self.c.Tr.ResetPatchTooltip,
OnPress: self.c.Helpers().PatchBuilding.Reset,
Key: 'c',
},
{
Label: self.c.Tr.ApplyPatch,
Tooltip: self.c.Tr.ApplyPatchTooltip,
OnPress: func() error { return self.handleApplyPatch(false) },
Key: 'a',
},
{
Label: self.c.Tr.ApplyPatchInReverse,
Tooltip: self.c.Tr.ApplyPatchInReverseTooltip,
OnPress: func() error { return self.handleApplyPatch(true) },
Key: 'r',
},
}
if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
menuItems = append(menuItems, []*types.MenuItem{
{
Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, self.c.Git().Patch.PatchBuilder.To),
Tooltip: self.c.Tr.RemovePatchFromOriginalCommitTooltip,
OnPress: self.handleDeletePatchFromCommit,
Key: 'd',
},
{
Label: self.c.Tr.MovePatchOutIntoIndex,
Tooltip: self.c.Tr.MovePatchOutIntoIndexTooltip,
OnPress: self.handleMovePatchIntoWorkingTree,
Key: 'i',
},
{
Label: self.c.Tr.MovePatchIntoNewCommit,
Tooltip: self.c.Tr.MovePatchIntoNewCommitTooltip,
OnPress: self.handlePullPatchIntoNewCommit,
Key: 'n',
},
}...)
if self.c.CurrentContext().GetKey() == self.c.Contexts().LocalCommits.GetKey() {
selectedCommit := self.c.Contexts().LocalCommits.GetSelected()
if selectedCommit != nil && self.c.Git().Patch.PatchBuilder.To != selectedCommit.Hash {
var disabledReason *types.DisabledReason
if self.c.Contexts().LocalCommits.AreMultipleItemsSelected() {
disabledReason = &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupported}
}
// adding this option to index 1
menuItems = append(
menuItems[:1],
append(
[]*types.MenuItem{
{
Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Hash),
Tooltip: self.c.Tr.MovePatchToSelectedCommitTooltip,
OnPress: self.handleMovePatchToSelectedCommit,
Key: 'm',
DisabledReason: disabledReason,
},
}, menuItems[1:]...,
)...,
)
}
}
}
menuItems = append(menuItems, []*types.MenuItem{
{
Label: self.c.Tr.CopyPatchToClipboard,
OnPress: func() error { return self.copyPatchToClipboard() },
Key: 'y',
},
}...)
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.PatchOptionsTitle, Items: menuItems})
}
func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
for index, commit := range self.c.Model().Commits {
if commit.Hash == self.c.Git().Patch.PatchBuilder.To {
return index
}
}
return -1
}
func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) {
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
return false, errors.New(self.c.Tr.CantPatchWhileRebasingError)
}
return true, nil
}
func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessary() error {
if self.c.CurrentContext().GetKey() == self.c.Contexts().CustomPatchBuilder.GetKey() {
return self.c.Helpers().PatchBuilding.Escape()
}
return nil
}
func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
if ok, err := self.validateNormalWorkingTreeState(); !ok {
return err
}
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
})
}
func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() error {
if ok, err := self.validateNormalWorkingTreeState(); !ok {
return err
}
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.MovePatchToSelectedCommit)
err := self.c.Git().Patch.MovePatchToSelectedCommit(self.c.Model().Commits, commitIndex, self.c.Contexts().LocalCommits.GetSelectedLineIdx())
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
})
}
func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error {
if ok, err := self.validateNormalWorkingTreeState(); !ok {
return err
}
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
return err
}
pull := func(stash bool) error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoIndex)
err := self.c.Git().Patch.MovePatchIntoIndex(self.c.Model().Commits, commitIndex, stash)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
})
}
if self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.MustStashTitle,
Prompt: self.c.Tr.MustStashWarning,
HandleConfirm: func() error {
return pull(true)
},
})
} else {
return pull(false)
}
}
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
if ok, err := self.validateNormalWorkingTreeState(); !ok {
return err
}
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
return err
}
commitIndex := self.getPatchCommitIndex()
return self.c.Helpers().Commits.OpenCommitMessagePanel(
&helpers.OpenCommitMessagePanelOpts{
// Pass a commit index of one less than the moved-from commit, so that
// you can press up arrow once to recall the original commit message:
CommitIndex: commitIndex - 1,
InitialMessage: "",
SummaryTitle: self.c.Tr.CommitSummaryTitle,
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
PreserveMessage: false,
OnConfirm: func(summary string, description string) error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
_ = self.c.Helpers().Commits.CloseCommitMessagePanel()
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit)
err := self.c.Git().Patch.PullPatchIntoNewCommit(self.c.Model().Commits, commitIndex, summary, description)
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
return err
}
return self.c.PushContext(self.c.Contexts().LocalCommits)
})
},
},
)
}
func (self *CustomPatchOptionsMenuAction) handleApplyPatch(reverse bool) error {
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
return err
}
action := self.c.Tr.Actions.ApplyPatch
if reverse {
action = "Apply patch in reverse"
}
self.c.LogAction(action)
if err := self.c.Git().Patch.ApplyCustomPatch(reverse, true); err != nil {
return err
}
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}
func (self *CustomPatchOptionsMenuAction) copyPatchToClipboard() error {
patch := self.c.Git().Patch.PatchBuilder.RenderAggregatedPatch(true)
self.c.LogAction(self.c.Tr.Actions.CopyPatchToClipboard)
if err := self.c.OS().CopyToClipboard(patch); err != nil {
return err
}
self.c.Toast(self.c.Tr.PatchCopiedToClipboard)
return nil
}