mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-05-22 10:15:43 +02:00
196e0a3c17
I copied all files except dot files (.github and .gitignore), the _examples folder, and go.mod/go.sum. At some point we may want to copy the files back to the gocui repo when other clients (e.g. lazydocker) want to use the newer versions of them.
274 lines
7.8 KiB
Go
274 lines
7.8 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/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.CanOnlyDiscardFromLocalCommits, ShowErrorInPanel: true}
|
|
}
|
|
if self.c.Git().Status.WorkingTreeState().Any() {
|
|
return &types.DisabledReason{Text: self.c.Tr.CantPatchWhileRebasingError, ShowErrorInPanel: true}
|
|
}
|
|
if self.c.UserConfig().Git.DiffContextSize == 0 {
|
|
text := fmt.Sprintf(self.c.Tr.Actions.NotEnoughContextToRemoveLines,
|
|
keybindings.Label(self.c.UserConfig().Keybinding.Universal.IncreaseContextInDiffView))
|
|
return &types.DisabledReason{Text: text, ShowErrorInPanel: true}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) discardSelection() error {
|
|
prompt := lo.Ternary(self.c.Git().Patch.PatchBuilder.IsEmpty(),
|
|
self.c.Tr.DiscardLinesFromCommitPrompt,
|
|
self.c.Tr.DiscardLinesFromCommitPromptWithReset)
|
|
|
|
self.c.Confirm(types.ConfirmOpts{
|
|
Title: self.c.Tr.DiscardLinesFromCommitTitle,
|
|
Prompt: prompt,
|
|
HandleConfirm: func() error {
|
|
return self.discardSelectionFromCommit()
|
|
},
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *PatchBuildingController) discardSelectionFromCommit() error {
|
|
// Reset the current patch if there is one.
|
|
if !self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
|
self.c.Git().Patch.PatchBuilder.Reset()
|
|
}
|
|
|
|
if err := self.toggleSelection(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
|
return nil
|
|
}
|
|
|
|
return self.c.WithWaitingStatusSync(self.c.Tr.RebasingStatus, func() error {
|
|
commitIndex := self.getPatchCommitIndex()
|
|
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
|
|
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
|
|
self.c.Helpers().PatchBuilding.Escape()
|
|
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
|
err, types.RefreshOptions{Mode: types.SYNC})
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|