diff options
Diffstat (limited to 'pkg/gui/controllers')
-rw-r--r-- | pkg/gui/controllers/bisect_controller.go | 2 | ||||
-rw-r--r-- | pkg/gui/controllers/commits_files_controller.go | 10 | ||||
-rw-r--r-- | pkg/gui/controllers/context_lines_controller.go | 116 | ||||
-rw-r--r-- | pkg/gui/controllers/files_controller.go | 32 | ||||
-rw-r--r-- | pkg/gui/controllers/helpers/patch_building_helper.go | 37 | ||||
-rw-r--r-- | pkg/gui/controllers/list_controller.go | 37 | ||||
-rw-r--r-- | pkg/gui/controllers/patch_building_controller.go | 138 | ||||
-rw-r--r-- | pkg/gui/controllers/patch_explorer_controller.go | 289 | ||||
-rw-r--r-- | pkg/gui/controllers/scroll_controller.go | 70 | ||||
-rw-r--r-- | pkg/gui/controllers/staging_controller.go | 242 |
10 files changed, 937 insertions, 36 deletions
diff --git a/pkg/gui/controllers/bisect_controller.go b/pkg/gui/controllers/bisect_controller.go index 805911030..083b6dce8 100644 --- a/pkg/gui/controllers/bisect_controller.go +++ b/pkg/gui/controllers/bisect_controller.go @@ -222,7 +222,7 @@ func (self *BisectController) selectCurrentBisectCommit() { for i, commit := range self.model.Commits { if commit.Sha == info.GetCurrentSha() { self.context().SetSelectedLineIdx(i) - _ = self.context().HandleFocus() + _ = self.context().HandleFocus(types.OnFocusOpts{}) break } } diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 5beecba02..f11005cd4 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -75,10 +75,10 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] func (self *CommitFilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding { return []*gocui.ViewMouseBinding{ { - ViewName: "main", + ViewName: "patchBuilding", Key: gocui.MouseLeft, Handler: self.onClickMain, - FromContext: string(self.context().GetKey()), + FocusedView: self.context().GetViewName(), }, } } @@ -107,7 +107,7 @@ func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts) if node == nil { return nil } - return self.enterCommitFile(node, types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: opts.Y}) + return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y}) } func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error { @@ -220,7 +220,7 @@ func (self *CommitFilesController) startPatchManager() error { } func (self *CommitFilesController) enter(node *filetree.CommitFileNode) error { - return self.enterCommitFile(node, types.OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1}) + return self.enterCommitFile(node, types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1}) } func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode, opts types.OnFocusOpts) error { @@ -235,7 +235,7 @@ func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode } } - return self.c.PushContext(self.contexts.PatchBuilding, opts) + return self.c.PushContext(self.contexts.CustomPatchBuilder, opts) } if self.git.Patch.PatchManager.Active() && self.git.Patch.PatchManager.To != self.context().GetRef().RefName() { diff --git a/pkg/gui/controllers/context_lines_controller.go b/pkg/gui/controllers/context_lines_controller.go new file mode 100644 index 000000000..c90bd9c9f --- /dev/null +++ b/pkg/gui/controllers/context_lines_controller.go @@ -0,0 +1,116 @@ +package controllers + +import ( + "errors" + + "github.com/jesseduffield/lazygit/pkg/gui/context" + "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/samber/lo" +) + +// This controller lets you change the context size for diffs. The 'context' in 'context size' refers to the conventional meaning of the word 'context' in a diff, as opposed to lazygit's own idea of a 'context'. + +var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{ + context.FILES_CONTEXT_KEY, + context.COMMIT_FILES_CONTEXT_KEY, + context.STASH_CONTEXT_KEY, + context.LOCAL_COMMITS_CONTEXT_KEY, + context.SUB_COMMITS_CONTEXT_KEY, + context.STAGING_MAIN_CONTEXT_KEY, + context.STAGING_SECONDARY_CONTEXT_KEY, + context.PATCH_BUILDING_MAIN_CONTEXT_KEY, + context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY, +} + +type ContextLinesController struct { + baseController + *controllerCommon +} + +var _ types.IController = &ContextLinesController{} + +func NewContextLinesController( + common *controllerCommon, +) *ContextLinesController { + return &ContextLinesController{ + baseController: baseController{}, + controllerCommon: common, + } +} + +func (self *ContextLinesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + bindings := []*types.Binding{ + { + Key: opts.GetKey(opts.Config.Universal.IncreaseContextInDiffView), + Handler: self.Increase, + Description: self.c.Tr.IncreaseContextInDiffView, + }, + { + Key: opts.GetKey(opts.Config.Universal.DecreaseContextInDiffView), + Handler: self.Decrease, + Description: self.c.Tr.DecreaseContextInDiffView, + }, + } + + return bindings +} + +func (self *ContextLinesController) Context() types.Context { + return nil +} + +func (self *ContextLinesController) Increase() error { + if self.isShowingDiff() { + if err := self.checkCanChangeContext(); err != nil { + return self.c.Error(err) + } + + self.c.UserConfig.Git.DiffContextSize = self.c.UserConfig.Git.DiffContextSize + 1 + return self.applyChange() + } + + return nil +} + +func (self *ContextLinesController) Decrease() error { + old_size := self.c.UserConfig.Git.DiffContextSize + + if self.isShowingDiff() && old_size > 1 { + if err := self.checkCanChangeContext(); err != nil { + return self.c.Error(err) + } + + self.c.UserConfig.Git.DiffContextSize = old_size - 1 + return self.applyChange() + } + + return nil +} + +func (self *ContextLinesController) applyChange() error { + currentContext := self.c.CurrentStaticContext() + switch currentContext.GetKey() { + // we make an exception for our staging and patch building contexts because they actually need to refresh their state afterwards. + case context.PATCH_BUILDING_MAIN_CONTEXT_KEY: + return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.PATCH_BUILDING}}) + case context.STAGING_MAIN_CONTEXT_KEY, context.STAGING_SECONDARY_CONTEXT_KEY: + return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STAGING}}) + default: + return currentContext.HandleRenderToMain() + } +} + +func (self *ContextLinesController) checkCanChangeContext() error { + if self.git.Patch.PatchManager.Active() { + return errors.New(self.c.Tr.CantChangeContextSizeError) + } + + return nil +} + +func (self *ContextLinesController) isShowingDiff() bool { + return lo.Contains( + CONTEXT_KEYS_SHOWING_DIFFS, + self.c.CurrentStaticContext().GetKey(), + ) +} diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 5304d0d81..0d2a10b35 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -152,13 +152,31 @@ func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []* ViewName: "main", Key: gocui.MouseLeft, Handler: self.onClickMain, - FromContext: string(self.context().GetKey()), + FocusedView: self.context().GetViewName(), + }, + { + ViewName: "patchBuilding", + Key: gocui.MouseLeft, + Handler: self.onClickMain, + FocusedView: self.context().GetViewName(), + }, + { + ViewName: "merging", + Key: gocui.MouseLeft, + Handler: self.onClickMain, + FocusedView: self.context().GetViewName(), }, { ViewName: "secondary", Key: gocui.MouseLeft, Handler: self.onClickSecondary, - FromContext: string(self.context().GetKey()), + FocusedView: self.context().GetViewName(), + }, + { + ViewName: "patchBuildingSecondary", + Key: gocui.MouseLeft, + Handler: self.onClickSecondary, + FocusedView: self.context().GetViewName(), }, } } @@ -318,7 +336,7 @@ func (self *FilesController) press(node *filetree.FileNode) error { return err } - return self.context().HandleFocus() + return self.context().HandleFocus(types.OnFocusOpts{}) } func (self *FilesController) checkSelectedFileNode(callback func(*filetree.FileNode) error) func() error { @@ -349,7 +367,7 @@ func (self *FilesController) getSelectedFile() *models.File { } func (self *FilesController) enter() error { - return self.EnterFile(types.OnFocusOpts{ClickedViewName: "", ClickedViewLineIdx: -1}) + return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1}) } func (self *FilesController) EnterFile(opts types.OnFocusOpts) error { @@ -389,7 +407,7 @@ func (self *FilesController) toggleStagedAll() error { return err } - return self.context().HandleFocus() + return self.context().HandleFocus(types.OnFocusOpts{}) } func (self *FilesController) toggleStagedAllWithLock() error { @@ -828,11 +846,11 @@ func (self *FilesController) handleStashSave(stashFunc func(message string) erro } func (self *FilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error { - return self.EnterFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: opts.Y}) + return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "main", ClickedViewLineIdx: opts.Y}) } func (self *FilesController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error { - return self.EnterFile(types.OnFocusOpts{ClickedViewName: "secondary", ClickedViewLineIdx: opts.Y}) + return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "secondary", ClickedViewLineIdx: opts.Y}) } func (self *FilesController) fetch() error { diff --git a/pkg/gui/controllers/helpers/patch_building_helper.go b/pkg/gui/controllers/helpers/patch_building_helper.go index efb8ec671..25ac63a08 100644 --- a/pkg/gui/controllers/helpers/patch_building_helper.go +++ b/pkg/gui/controllers/helpers/patch_building_helper.go @@ -3,6 +3,7 @@ package helpers import ( "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" + "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -11,17 +12,20 @@ type IPatchBuildingHelper interface { } type PatchBuildingHelper struct { - c *types.HelperCommon - git *commands.GitCommand + c *types.HelperCommon + git *commands.GitCommand + contexts *context.ContextTree } func NewPatchBuildingHelper( c *types.HelperCommon, git *commands.GitCommand, + contexts *context.ContextTree, ) *PatchBuildingHelper { return &PatchBuildingHelper{ - c: c, - git: git, + c: c, + git: git, + contexts: contexts, } } @@ -31,3 +35,28 @@ func (self *PatchBuildingHelper) ValidateNormalWorkingTreeState() (bool, error) } return true, nil } + +// takes us from the patch building panel back to the commit files panel +func (self *PatchBuildingHelper) Escape() error { + return self.c.PushContext(self.contexts.CommitFiles) +} + +// kills the custom patch and returns us back to the commit files panel if needed +func (self *PatchBuildingHelper) Reset() error { + self.git.Patch.PatchManager.Reset() + + if self.c.CurrentStaticContext().GetKind() != types.SIDE_CONTEXT { + if err := self.Escape(); err != nil { + return err + } + } + + if err := self.c.Refresh(types.RefreshOptions{ + Scope: []types.RefreshableView{types.COMMIT_FILES}, + }); err != nil { + return err + } + + // refreshing the current context so that the secondary panel is hidden if necessary. + return self.c.PostRefreshUpdate(self.c.CurrentContext()) +} diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index 7da05710e..c74d87244 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -51,22 +51,24 @@ func (self *ListController) HandleScrollRight() error { } func (self *ListController) HandleScrollUp() error { - self.context.GetViewTrait().ScrollUp() + scrollHeight := self.c.UserConfig.Gui.ScrollHeight + self.context.GetViewTrait().ScrollUp(scrollHeight) // we only need to do a line change if our line has been pushed out of the viewport, because // at the moment much logic depends on the selected line always being visible if !self.isSelectedLineInViewPort() { - return self.handleLineChange(-1) + return self.handleLineChange(-scrollHeight) } return nil } func (self *ListController) HandleScrollDown() error { - self.context.GetViewTrait().ScrollDown() + scrollHeight := self.c.UserConfig.Gui.ScrollHeight + self.context.GetViewTrait().ScrollDown(scrollHeight) if !self.isSelectedLineInViewPort() { - return self.handleLineChange(1) + return self.handleLineChange(scrollHeight) } return nil @@ -81,7 +83,7 @@ func (self *ListController) isSelectedLineInViewPort() bool { func (self *ListController) scrollHorizontal(scrollFunc func()) error { scrollFunc() - return self.context.HandleFocus() + return self.context.HandleFocus(types.OnFocusOpts{}) } func (self *ListController) handleLineChange(change int) error { @@ -96,7 +98,7 @@ func (self *ListController) handleLineChange(change int) error { // doing this check so that if we're holding the up key at the start of the list // we're not constantly re-rendering the main view. if before != after { - return self.context.HandleFocus() + return self.context.HandleFocus(types.OnFocusOpts{}) } return nil @@ -136,7 +138,7 @@ func (self *ListController) HandleClick(opts gocui.ViewMouseBindingOpts) error { if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && self.context.GetOnClick() != nil { return self.context.GetOnClick()() } - return self.context.HandleFocus() + return self.context.HandleFocus(types.OnFocusOpts{}) } func (self *ListController) pushContextIfNotFocused() error { @@ -182,22 +184,19 @@ func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types. func (self *ListController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding { return []*gocui.ViewMouseBinding{ { - ViewName: self.context.GetViewName(), - ToContext: string(self.context.GetKey()), - Key: gocui.MouseWheelUp, - Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollUp() }, + ViewName: self.context.GetViewName(), + Key: gocui.MouseWheelUp, + Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollUp() }, }, { - ViewName: self.context.GetViewName(), - ToContext: string(self.context.GetKey()), - Key: gocui.MouseLeft, - Handler: func(opts gocui.ViewMouseBindingOpts) error { return self.HandleClick(opts) }, + ViewName: self.context.GetViewName(), + Key: gocui.MouseLeft, + Handler: func(opts gocui.ViewMouseBindingOpts) error { return self.HandleClick(opts) }, }, { - ViewName: self.context.GetViewName(), - ToContext: string(self.context.GetKey()), - Key: gocui.MouseWheelDown, - Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollDown() }, + ViewName: self.context.GetViewName(), + Key: gocui.MouseWheelDown, + Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollDown() }, }, } } diff --git a/pkg/gui/controllers/patch_building_controller.go b/pkg/gui/controllers/patch_building_controller.go new file mode 100644 index 000000000..798472f7f --- /dev/null +++ b/pkg/gui/controllers/patch_building_controller.go @@ -0,0 +1,138 @@ +package controllers + +import ( + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/samber/lo" +) + +type PatchBuildingController struct { + baseController + *controllerCommon +} + +var _ types.IController = &PatchBuildingController{} + +func NewPatchBuildingController( + common *controllerCommon, +) *PatchBuildingController { + return &PatchBuildingController{ + baseController: baseController{}, + controllerCommon: common, + } +} + +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.LcOpenFile, + }, + { + Key: opts.GetKey(opts.Config.Universal.Edit), + Handler: self.EditFile, + Description: self.c.Tr.LcEditFile, + }, + { + Key: opts.GetKey(opts.Config.Universal.Select), + Handler: self.ToggleSelectionAndRefresh, + Description: self.c.Tr.ToggleSelectionForPatch, + }, + { + Key: opts.GetKey(opts.Config.Universal.Return), + Handler: self.Escape, + Description: self.c.Tr.ExitCustomPatchBuilder, + }, + } +} + +func (self *PatchBuildingController) Context() types.Context { + return self.contexts.CustomPatchBuilder +} + +func (self *PatchBuildingController) context() types.IPatchExplorerContext { + return self.contexts.CustomPatchBuilder +} + +func (self *PatchBuildingController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding { + return []*gocui.ViewMouseBinding{} +} + +func (self *PatchBuildingController) OpenFile() error { + self.context().GetMutex().Lock() + defer self.context().GetMutex().Unlock() + + path := self.contexts.CommitFiles.GetSelectedPath() + + if path == "" { + return nil + } + + lineNumber := self.context().GetState().CurrentLineNumber() + return self.helpers.Files.OpenFileAtLine(path, lineNumber) +} + +func (self *PatchBuildingController) EditFile() error { + self.context().GetMutex().Lock() + defer self.context().GetMutex().Unlock() + + path := self.contexts.CommitFiles.GetSelectedPath() + + if path == "" { + return nil + } + + lineNumber := self.context().GetState().CurrentLineNumber() + return self.helpers.Files.EditFileAtLine(path, lineNumber) +} + +func (self *PatchBuildingController) ToggleSelectionAndRefresh() error { + if err := self.toggleSelection(); err != nil { + return err + } + + return self.c.Refresh(types.RefreshOptions{ + Scope: []types.RefreshableView{types.PATCH_BUILDING, types.COMMIT_FILES}, + }) +} + +func (self *PatchBuildingController) toggleSelection() error { + self.context().GetMutex().Lock() + defer self.context().GetMutex().Unlock() + + toggleFunc := self.git.Patch.PatchManager.AddFileLineRange + filename := self.contexts.CommitFiles.GetSelectedPath() + if filename == "" { + return nil + } + + state := self.context().GetState() + + includedLineIndices, err := self.git.Patch.PatchManager.GetFileIncLineIndices(filename) + if err != nil { + return err + } + currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedLineIdx()) + if currentLineIsStaged { + toggleFunc = self.git.Patch.PatchManager.RemoveFileLineRange + } + + // add range of lines to those set for the file + firstLineIdx, lastLineIdx := state.SelectedRange() + + if err := toggleFunc(filename, firstLineIdx, lastLineIdx); err != nil { + // might actually want to return an error here + self.c.Log.Error(err) + } + + if state.SelectingRange() { + state.SetLineSelectMode() + } + + return nil +} + +func (self *PatchBuildingController) Escape() error { + return self.helpers.PatchBuilding.Escape() +} diff --git a/pkg/gui/controllers/patch_explorer_controller.go b/pkg/gui/controllers/patch_explorer_controller.go new file mode 100644 index 000000000..dac63a7b1 --- /dev/null +++ b/pkg/gui/controllers/patch_explorer_controller.go @@ -0,0 +1,289 @@ +package controllers + +import ( + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +type PatchExplorerControllerFactory struct { + *controllerCommon +} + +func NewPatchExplorerControllerFactory(c *controllerCommon) *PatchExplorerControllerFactory { + return &PatchExplorerControllerFactory{ + controllerCommon: c, + } +} + +func (self *PatchExplorerControllerFactory) Create(context types.IPatchExplorerContext) *PatchExplorerController { + return &PatchExplorerController{ + baseController: baseController{}, + controllerCommon: self.controllerCommon, + context: context, + } +} + +type PatchExplorerController struct { + baseController + *controllerCommon + + context types.IPatchExplorerContext +} + +func (self *PatchExplorerController) Context() types.Context { + return self.context +} + +func (self *PatchExplorerController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + return []*types.Binding{ + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), + Handler: self.withRenderAndFocus(self.HandlePrevLine), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.PrevItem), + Handler: self.withRenderAndFocus(self.HandlePrevLine), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.NextItemAlt), + Handler: self.withRenderAndFocus(self.HandleNextLine), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.NextItem), + Handler: self.withRenderAndFocus(self.HandleNextLine), + }, + { + Key: opts.GetKey(opts.Config.Universal.PrevBlock), + Handler: self.withRenderAndFocus(self.HandlePrevHunk), + Description: self.c.Tr.PrevHunk, + }, + { + Key: opts.GetKey(opts.Config.Universal.PrevBlockAlt), + Handler: self.withRenderAndFocus(self.HandlePrevHunk), + }, + { + Key: opts.GetKey(opts.Config.Universal.NextBlock), + Handler: self.withRenderAndFocus(self.HandleNextHunk), + Description: self.c.Tr.NextHunk, + }, + { + Key: opts.GetKey(opts.Config.Universal.NextBlockAlt), + Handler: self.withRenderAndFocus(self.HandleNextHunk), + }, + { + Key: opts.GetKey(opts.Config.Main.ToggleDragSelect), + Handler: self.withRenderAndFocus(self.HandleToggleSelectRange), + Description: self.c.Tr.ToggleDragSelect, + }, + { + Key: opts.GetKey(opts.Config.Main.ToggleDragSelectAlt), + Handler: self.withRenderAndFocus(self.HandleToggleSelectRange), + Description: self.c.Tr.ToggleDragSelect, + }, + { + Key: opts.GetKey(opts.Config.Main.ToggleSelectHunk), + Handler: self.withRenderAndFocus(self.HandleToggleSelectHunk), + Description: self.c.Tr.ToggleSelectHunk, + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.PrevPage), + Handler: self.withRenderAndFocus(self.HandlePrevPage), + Description: self.c.Tr.LcPrevPage, + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.NextPage), + Handler: self.withRenderAndFocus(self.HandleNextPage), + Description: self.c.Tr.LcNextPage, + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.GotoTop), + Handler: self.withRenderAndFocus(self.HandleGotoTop), + Description: self.c.Tr.LcGotoTop, + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.GotoBottom), + Description: self.c.Tr.LcGotoBottom, + Handler: self.withRenderAndFocus(self.HandleGotoBottom), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.ScrollLeft), + Handler: self.withRenderAndFocus(self.HandleScrollLeft), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.ScrollRight), + Handler: self.withRenderAndFocus(self.HandleScrollRight), + }, + { + Tag: "navigation", + Key: opts.GetKey(opts.Config.Universal.StartSearch), + Handler: func() error { self.c.OpenSearch(); return nil }, + Description: self.c.Tr.LcStartSearch, + }, + { + Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Handler: self.withLock(self.CopySelectedToClipboard), + Description: self.c.Tr.LcCopySelectedTexToClipboard, + }, + } +} + +func (self *PatchExplorerController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding { + return []*gocui.ViewMouseBinding{ + { + ViewName: self.context.GetViewName(), + Key: gocui.MouseLeft, + Handler: func(opts gocui.ViewMouseBindingOpts) error { + if self.isFocused() { + return self.withRenderAndFocus(self.HandleMouseDown)() + } + + return self.c.PushContext(self.context, types.OnFocusOpts{ + ClickedWindowName: self.context.GetWindowName(), + ClickedViewLineIdx: opts.Y, + }) + }, + }, + { + ViewName: self.context.GetViewName(), + Key: gocui.MouseLeft, + Modifier: gocui.ModMotion, + Handler: func(gocui.ViewMouseBindingOpts) error { + return self.withRenderAndFocus(self.HandleMouseDrag)() + }, + }, + } +} + +func (self *PatchExplorerController) HandlePrevLine() error { + self.context.GetState().CycleSelection(false) + + return nil +} + +func (self *PatchExplorerController) HandleNextLine() error { + self.context.GetState().CycleSelection(true) + + return nil +} + +func (self *PatchExplorerController) HandlePrevHunk() error { + self.context.GetState().CycleHunk(false) + + return nil +} + +func (self *PatchExplorerController) HandleNextHunk() error { + self.context.GetState().CycleHunk(true) + + return nil +} + +func (self *PatchExplorerController) HandleToggleSelectRange() error { + self.context.GetState().ToggleSelectRange() + + return nil +} + +func (self *PatchExplorerController) HandleToggleSelectHunk() error { + self.context.GetState().ToggleSelectHunk() + + return nil +} + +func (self *PatchExplorerController) HandleScrollLeft() error { + self.context.GetViewTrait().ScrollLeft() + + return nil +} + +func (self *PatchExplorerController) HandleScrollRight() error { + self.context.GetViewTrait().ScrollRight() + + return nil +} + +func (self *PatchExplorerController) HandlePrevPage() error { + self.context.GetState().SetLineSelectMode() + self.context.GetState().AdjustSelectedLineIdx(-self.context.GetViewTrait().PageDelta()) + + return nil +} + +func (self *PatchExplorerController) HandleNextPage() error { + self.context.GetState().SetLineSelectMode() + self.context.GetState().AdjustSelectedLineIdx(self.context.GetViewTrait().PageDelta()) + + return nil +} + +func (self *PatchExplorerController) HandleGotoTop() error { + self.context.GetState().SelectTop() + + return nil +} + +func (self *PatchExplorerController) HandleGotoBottom() error { + self.context.GetState().SelectBottom() + + return nil +} + +func (self *PatchExplorerController) HandleMouseDown() error { + self.context.GetState().SelectNewLineForRange(self.context.GetViewTrait().SelectedLineIdx()) + + return nil +} + +func (self *PatchExplorerController) HandleMouseDrag() error { + self.context.GetState().SelectLine(self.context.GetViewTrait().SelectedLineIdx()) + + return nil +} + +func (self *PatchExplorerController) CopySelectedToClipboard() error { + selected := self.context.GetState().PlainRenderSelected() + + self.c.LogAction(self.c.Tr.Actions.CopySelectedTextToClipboard) + if err := self.os.CopyToClipboard(selected); err != nil { + return self.c.Error(err) + } + + return nil +} + +func (self *PatchExplorerController) isFocused() bool { + return self.c.CurrentContext().GetKey() == self.context.GetKey() +} + +func (self *PatchExplorerController) withRenderAndFocus(f func() error) func() error { + return self.withLock(func() error { + if err := f(); err != nil { + return err + } + + return self.context.RenderAndFocus(self.isFocused()) + }) +} + +func (self *PatchExplorerController) withLock(f func() error) func() error { + return func() error { + self.context.GetMutex().Lock() + defer self.context.GetMutex().Unlock() + + if self.context.GetState() == nil { + return nil + } + + return f() + } +} diff --git a/pkg/gui/controllers/scroll_controller.go b/pkg/gui/controllers/scroll_controller.go new file mode 100644 index 000000000..3f3e9d177 --- /dev/null +++ b/pkg/gui/controllers/scroll_controller.go @@ -0,0 +1,70 @@ +package controllers + +import ( + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +// given we have no fields here, arguably we shouldn't even need this factory +// struct, but we're maintaining consistency with the other files. +type VerticalScrollControllerFactory struct { + controllerCommon *controllerCommon +} + +func NewVerticalScrollControllerFactory(c *controllerCommon) *VerticalScrollControllerFactory { + return &VerticalScrollControllerFactory{controllerCommon: c} +} + +func (self *VerticalScrollControllerFactory) Create(context types.Context) types.IController { + return &VerticalScrollController{ + baseController: baseController{}, + controllerCommon: self.controllerCommon, + context: context, + } +} + +type VerticalScrollController struct { + baseController + *controllerCommon + + context types.Context +} + +func (self *VerticalScrollController) Context() types.Context { + return self.context +} + +func (self *VerticalScrollController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + return []*types.Binding{} +} + +func (self *VerticalScrollController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding { + return []*gocui.ViewMouseBinding{ + { + ViewName: se |