mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-02-20 01:02:29 +08:00
I always press 'd' in the patch building view, expecting that I can do exactly what I can do in the staging view, to find out I need to go space -> ctrl+p -> d and I think it's time to honour the muscle memory and support this convenience keybinding.
287 lines
8.0 KiB
Go
287 lines
8.0 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jesseduffield/gocui"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type PatchBuildingController struct {
|
|
baseController
|
|
c *ControllerCommon
|
|
}
|
|
|
|
var _ types.IController = &PatchBuildingController{}
|
|
|
|
func NewPatchBuildingController(
|
|
c *ControllerCommon,
|
|
) *PatchBuildingController {
|
|
return &PatchBuildingController{
|
|
baseController: baseController{},
|
|
c: c,
|
|
}
|
|
}
|
|
|
|
func (self *PatchBuildingController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
|
return []*types.Binding{
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.OpenFile),
|
|
Handler: self.OpenFile,
|
|
Description: self.c.Tr.OpenFile,
|
|
Tooltip: self.c.Tr.OpenFileTooltip,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Edit),
|
|
Handler: self.EditFile,
|
|
Description: self.c.Tr.EditFile,
|
|
Tooltip: self.c.Tr.EditFileTooltip,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Select),
|
|
Handler: self.ToggleSelectionAndRefresh,
|
|
Description: self.c.Tr.ToggleSelectionForPatch,
|
|
DisplayOnScreen: true,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Remove),
|
|
Handler: self.DiscardSelection,
|
|
GetDisabledReason: self.getDisabledReasonForDiscard,
|
|
Description: self.c.Tr.RemoveSelectionFromPatch,
|
|
Tooltip: self.c.Tr.RemoveSelectionFromPatchTooltip,
|
|
DisplayOnScreen: true,
|
|
},
|
|
{
|
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
|
Handler: self.Escape,
|
|
Description: self.c.Tr.ExitCustomPatchBuilder,
|
|
DescriptionFunc: self.EscapeDescription,
|
|
DisplayOnScreen: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (self *PatchBuildingController) Context() types.Context {
|
|
return self.c.Contexts().CustomPatchBuilder
|
|
}
|
|
|
|
func (self *PatchBuildingController) context() types.IPatchExplorerContext {
|
|
return self.c.Contexts().CustomPatchBuilder
|
|
}
|
|
|
|
func (self *PatchBuildingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
|
return []*gocui.ViewMouseBinding{}
|
|
}
|
|
|
|
func (self *PatchBuildingController) GetOnFocus() func(types.OnFocusOpts) {
|
|
return func(opts types.OnFocusOpts) {
|
|
// no need to change wrap on the secondary view because it can't be interacted with
|
|
self.c.Views().PatchBuilding.Wrap = self.c.UserConfig().Gui.WrapLinesInStagingView
|
|
|
|
self.c.Helpers().PatchBuilding.RefreshPatchBuildingPanel(opts)
|
|
}
|
|
}
|
|
|
|
func (self *PatchBuildingController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
|
return func(opts types.OnFocusLostOpts) {
|
|
self.context().SetState(nil)
|
|
|
|
self.c.Views().PatchBuilding.Wrap = true
|
|
|
|
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
|
self.c.Git().Patch.PatchBuilder.Reset()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (self *PatchBuildingController) OpenFile() error {
|
|
self.context().GetMutex().Lock()
|
|
defer self.context().GetMutex().Unlock()
|
|
|
|
path := self.c.Contexts().CommitFiles.GetSelectedPath()
|
|
|
|
if path == "" {
|
|
return nil
|
|
}
|
|
|
|
return self.c.Helpers().Files.OpenFile(path)
|
|
}
|
|
|
|
func (self *PatchBuildingController) EditFile() error {
|
|
self.context().GetMutex().Lock()
|
|
defer self.context().GetMutex().Unlock()
|
|
|
|
path := self.c.Contexts().CommitFiles.GetSelectedPath()
|
|
|
|
if path == "" {
|
|
return nil
|
|
}
|
|
|
|
lineNumber := self.context().GetState().CurrentLineNumber()
|
|
lineNumber = self.c.Helpers().Diff.AdjustLineNumber(path, lineNumber, self.context().GetViewName())
|
|
return self.c.Helpers().Files.EditFileAtLine(path, lineNumber)
|
|
}
|
|
|
|
func (self *PatchBuildingController) ToggleSelectionAndRefresh() error {
|
|
if err := self.toggleSelection(); err != nil {
|
|
return err
|
|
}
|
|
|
|
self.c.Refresh(types.RefreshOptions{
|
|
Scope: []types.RefreshableView{types.PATCH_BUILDING, types.COMMIT_FILES},
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) toggleSelection() error {
|
|
self.context().GetMutex().Lock()
|
|
defer self.context().GetMutex().Unlock()
|
|
|
|
filename := self.c.Contexts().CommitFiles.GetSelectedPath()
|
|
if filename == "" {
|
|
return nil
|
|
}
|
|
|
|
state := self.context().GetState()
|
|
|
|
// Get added/deleted lines in the selected patch range
|
|
lineIndicesToToggle := state.LineIndicesOfAddedOrDeletedLinesInSelectedPatchRange()
|
|
if len(lineIndicesToToggle) == 0 {
|
|
// Only context lines or header lines selected, so nothing to do
|
|
return nil
|
|
}
|
|
|
|
includedLineIndices, err := self.c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
toggleFunc := self.c.Git().Patch.PatchBuilder.AddFileLineRange
|
|
firstSelectedChangeLineIsStaged := lo.Contains(includedLineIndices, lineIndicesToToggle[0])
|
|
if firstSelectedChangeLineIsStaged {
|
|
toggleFunc = self.c.Git().Patch.PatchBuilder.RemoveFileLineRange
|
|
}
|
|
|
|
// add range of lines to those set for the file
|
|
if err := toggleFunc(filename, lineIndicesToToggle); err != nil {
|
|
// might actually want to return an error here
|
|
self.c.Log.Error(err)
|
|
}
|
|
|
|
if state.SelectingRange() {
|
|
state.SetLineSelectMode()
|
|
}
|
|
|
|
state.SelectNextStageableLineOfSameIncludedState(self.context().GetIncludedLineIndices(), firstSelectedChangeLineIsStaged)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) getDisabledReasonForDiscard() *types.DisabledReason {
|
|
if !self.c.Git().Patch.PatchBuilder.CanRebase {
|
|
return &types.DisabledReason{Text: self.c.Tr.CanOnlyRemoveLinesFromLocalCommits}
|
|
}
|
|
if !self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
|
return &types.DisabledReason{Text: self.c.Tr.MustClearPatchBeforeRemovingLines}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) DiscardSelection() error {
|
|
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
|
return fmt.Errorf(self.c.Tr.Actions.NotEnoughContextToRemoveLines,
|
|
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
|
}
|
|
|
|
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
|
return err
|
|
}
|
|
|
|
self.c.Confirm(types.ConfirmOpts{
|
|
Title: self.c.Tr.RemoveLinesFromCommitTitle,
|
|
Prompt: self.c.Tr.RemoveLinesFromCommitPrompt,
|
|
HandleConfirm: func() error {
|
|
return self.removeSelectionFromCommit()
|
|
},
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) addSelectionToPatch() error {
|
|
self.context().GetMutex().Lock()
|
|
defer self.context().GetMutex().Unlock()
|
|
|
|
filename := self.c.Contexts().CommitFiles.GetSelectedPath()
|
|
if filename == "" {
|
|
return nil
|
|
}
|
|
|
|
state := self.context().GetState()
|
|
lineIndicesToToggle := state.LineIndicesOfAddedOrDeletedLinesInSelectedPatchRange()
|
|
if len(lineIndicesToToggle) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return self.c.Git().Patch.PatchBuilder.AddFileLineRange(filename, lineIndicesToToggle)
|
|
}
|
|
|
|
func (self *PatchBuildingController) removeSelectionFromCommit() error {
|
|
if err := self.addSelectionToPatch(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
|
return nil
|
|
}
|
|
|
|
self.c.Helpers().PatchBuilding.Escape()
|
|
|
|
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 *PatchBuildingController) 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 *PatchBuildingController) Escape() error {
|
|
context := self.c.Contexts().CustomPatchBuilder
|
|
state := context.GetState()
|
|
|
|
if state.SelectingRange() || state.SelectingHunkEnabledByUser() {
|
|
state.SetLineSelectMode()
|
|
self.c.PostRefreshUpdate(context)
|
|
return nil
|
|
}
|
|
|
|
self.c.Helpers().PatchBuilding.Escape()
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) EscapeDescription() string {
|
|
context := self.c.Contexts().CustomPatchBuilder
|
|
if state := context.GetState(); state != nil {
|
|
if state.SelectingRange() {
|
|
return self.c.Tr.DismissRangeSelect
|
|
}
|
|
|
|
if state.SelectingHunkEnabledByUser() {
|
|
return self.c.Tr.SelectLineByLine
|
|
}
|
|
}
|
|
|
|
return self.c.Tr.ExitCustomPatchBuilder
|
|
}
|