mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	refactor contexts
This commit is contained in:
		| @@ -13,22 +13,9 @@ import ( | ||||
|  | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedBranch() *models.Branch { | ||||
| 	if len(gui.State.Model.Branches) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	selectedLine := gui.State.Panels.Branches.SelectedLineIdx | ||||
| 	if selectedLine == -1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.Branches[selectedLine] | ||||
| } | ||||
|  | ||||
| func (gui *Gui) branchesRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		task = NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo) | ||||
| 	} else { | ||||
| @@ -48,24 +35,26 @@ func (gui *Gui) branchesRenderToMain() error { | ||||
| // specific functions | ||||
|  | ||||
| func (gui *Gui) handleBranchPress() error { | ||||
| 	if gui.State.Panels.Branches.SelectedLineIdx == -1 { | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if gui.State.Panels.Branches.SelectedLineIdx == 0 { | ||||
|  | ||||
| 	if branch == gui.getCheckedOutBranch() { | ||||
| 		return gui.c.ErrorMsg(gui.c.Tr.AlreadyCheckedOutBranch) | ||||
| 	} | ||||
| 	branch := gui.getSelectedBranch() | ||||
|  | ||||
| 	gui.c.LogAction(gui.c.Tr.Actions.CheckoutBranch) | ||||
| 	return gui.helpers.Refs.CheckoutRef(branch.Name, types.CheckoutRefOptions{}) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreatePullRequestPress() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	return gui.createPullRequest(branch.Name, "") | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreatePullRequestMenu() error { | ||||
| 	selectedBranch := gui.getSelectedBranch() | ||||
| 	selectedBranch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -77,7 +66,7 @@ func (gui *Gui) handleCreatePullRequestMenu() error { | ||||
| func (gui *Gui) handleCopyPullRequestURLPress() error { | ||||
| 	hostingServiceMgr := gui.getHostingServiceMgr() | ||||
|  | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
|  | ||||
| 	branchExistsOnRemote := gui.git.Remote.CheckRemoteBranchExists(branch.Name) | ||||
|  | ||||
| @@ -109,7 +98,7 @@ func (gui *Gui) handleGitFetch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleForceCheckout() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	message := gui.c.Tr.SureForceCheckout | ||||
| 	title := gui.c.Tr.ForceCheckoutBranch | ||||
|  | ||||
| @@ -156,7 +145,7 @@ func (gui *Gui) getCheckedOutBranch() *models.Branch { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) createNewBranchWithName(newBranchName string) error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -165,7 +154,7 @@ func (gui *Gui) createNewBranchWithName(newBranchName string) error { | ||||
| 		return gui.c.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	gui.State.Panels.Branches.SelectedLineIdx = 0 | ||||
| 	gui.State.Contexts.Branches.SetSelectedLineIdx(0) | ||||
| 	return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}) | ||||
| } | ||||
|  | ||||
| @@ -174,7 +163,7 @@ func (gui *Gui) handleDeleteBranch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) deleteBranch(force bool) error { | ||||
| 	selectedBranch := gui.getSelectedBranch() | ||||
| 	selectedBranch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -245,12 +234,12 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleMerge() error { | ||||
| 	selectedBranchName := gui.getSelectedBranch().Name | ||||
| 	selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name | ||||
| 	return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleRebaseOntoLocalBranch() error { | ||||
| 	selectedBranchName := gui.getSelectedBranch().Name | ||||
| 	selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name | ||||
| 	return gui.handleRebaseOntoBranch(selectedBranchName) | ||||
| } | ||||
|  | ||||
| @@ -279,7 +268,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleFastForward() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil || !branch.IsRealBranch() { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -305,7 +294,7 @@ func (gui *Gui) handleFastForward() error { | ||||
| 	) | ||||
|  | ||||
| 	return gui.c.WithLoaderPanel(message, func() error { | ||||
| 		if gui.State.Panels.Branches.SelectedLineIdx == 0 { | ||||
| 		if branch == gui.getCheckedOutBranch() { | ||||
| 			gui.c.LogAction(action) | ||||
|  | ||||
| 			err := gui.git.Sync.Pull( | ||||
| @@ -334,7 +323,7 @@ func (gui *Gui) handleFastForward() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreateResetToBranchMenu() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -343,7 +332,7 @@ func (gui *Gui) handleCreateResetToBranchMenu() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleRenameBranch() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil || !branch.IsRealBranch() { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -364,7 +353,7 @@ func (gui *Gui) handleRenameBranch() error { | ||||
| 				// now that we've got our stuff again we need to find that branch and reselect it. | ||||
| 				for i, newBranch := range gui.State.Model.Branches { | ||||
| 					if newBranch.Name == newBranchName { | ||||
| 						gui.State.Panels.Branches.SetSelectedLineIdx(i) | ||||
| 						gui.State.Contexts.Branches.SetSelectedLineIdx(i) | ||||
| 						if err := gui.State.Contexts.Branches.HandleRender(); err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| @@ -391,7 +380,7 @@ func (gui *Gui) handleRenameBranch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleEnterBranch() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -400,7 +389,7 @@ func (gui *Gui) handleEnterBranch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleNewBranchOffBranch() error { | ||||
| 	selectedBranch := gui.getSelectedBranch() | ||||
| 	selectedBranch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -5,16 +5,11 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/patch" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/controllers" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/filetree" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| func (gui *Gui) getSelectedCommitFileNode() *filetree.CommitFileNode { | ||||
| 	return gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getSelectedCommitFile() *models.CommitFile { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -22,20 +17,21 @@ func (gui *Gui) getSelectedCommitFile() *models.CommitFile { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getSelectedCommitFilePath() string { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return node.GetPath() | ||||
| } | ||||
|  | ||||
| // TODO: do we need this? | ||||
| func (gui *Gui) onCommitFileFocus() error { | ||||
| 	gui.escapeLineByLinePanel() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gui *Gui) commitFilesRenderToMain() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -62,7 +58,7 @@ func (gui *Gui) commitFilesRenderToMain() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCheckoutCommitFile() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -88,7 +84,7 @@ func (gui *Gui) handleDiscardOldFileChange() error { | ||||
| 		HandleConfirm: func() error { | ||||
| 			return gui.c.WithWaitingStatus(gui.c.Tr.RebasingStatus, func() error { | ||||
| 				gui.c.LogAction(gui.c.Tr.Actions.DiscardOldFileChange) | ||||
| 				if err := gui.git.Rebase.DiscardOldFileChanges(gui.State.Model.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil { | ||||
| 				if err := gui.git.Rebase.DiscardOldFileChanges(gui.State.Model.Commits, gui.State.Contexts.BranchCommits.GetSelectedLineIdx(), fileName); err != nil { | ||||
| 					if err := gui.helpers.Rebase.CheckMergeOrRebase(err); err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| @@ -122,7 +118,7 @@ func (gui *Gui) refreshCommitFilesView() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleOpenOldCommitFile() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -131,7 +127,7 @@ func (gui *Gui) handleOpenOldCommitFile() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleEditCommitFile() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -144,7 +140,7 @@ func (gui *Gui) handleEditCommitFile() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleToggleFileForPatch() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -212,7 +208,7 @@ func (gui *Gui) handleEnterCommitFile() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) enterCommitFile(opts types.OnFocusOpts) error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -246,7 +242,7 @@ func (gui *Gui) enterCommitFile(opts types.OnFocusOpts) error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleToggleCommitFileDirCollapsed() error { | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -11,18 +11,12 @@ const COMMIT_THRESHOLD = 200 | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedLocalCommit() *models.Commit { | ||||
| 	selectedLine := gui.State.Panels.Commits.SelectedLineIdx | ||||
| 	if selectedLine == -1 || selectedLine > len(gui.State.Model.Commits)-1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.Commits[selectedLine] | ||||
| 	return gui.State.Contexts.BranchCommits.GetSelected() | ||||
| } | ||||
|  | ||||
| func (gui *Gui) onCommitFocus() error { | ||||
| 	state := gui.State.Panels.Commits | ||||
| 	if state.SelectedLineIdx > COMMIT_THRESHOLD && state.LimitCommits { | ||||
| 		state.LimitCommits = false | ||||
| 	if gui.State.Contexts.BranchCommits.GetSelectedLineIdx() > COMMIT_THRESHOLD && gui.State.LimitCommits { | ||||
| 		gui.State.LimitCommits = false | ||||
| 		go utils.Safe(func() { | ||||
| 			if err := gui.refreshCommitsWithLimit(); err != nil { | ||||
| 				_ = gui.c.Error(err) | ||||
| @@ -37,7 +31,7 @@ func (gui *Gui) onCommitFocus() error { | ||||
|  | ||||
| func (gui *Gui) branchCommitsRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	commit := gui.getSelectedLocalCommit() | ||||
| 	commit := gui.State.Contexts.BranchCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		task = NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch) | ||||
| 	} else { | ||||
|   | ||||
| @@ -77,7 +77,29 @@ func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, int, int, int) { | ||||
| 	panelWidth := gui.getConfirmationPanelWidth() | ||||
| 	panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth) | ||||
| 	return gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(contentHeight int) (int, int, int, int) { | ||||
| 	panelWidth := gui.getConfirmationPanelWidth() | ||||
| 	return gui.getConfirmationPanelDimensionsAux(panelWidth, contentHeight) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getConfirmationPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) { | ||||
| 	width, height := gui.g.Size() | ||||
| 	if panelHeight > height*3/4 { | ||||
| 		panelHeight = height * 3 / 4 | ||||
| 	} | ||||
| 	return width/2 - panelWidth/2, | ||||
| 		height/2 - panelHeight/2 - panelHeight%2 - 1, | ||||
| 		width/2 + panelWidth/2, | ||||
| 		height/2 + panelHeight/2 | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getConfirmationPanelWidth() int { | ||||
| 	width, _ := gui.g.Size() | ||||
| 	// we want a minimum width up to a point, then we do it based on ratio. | ||||
| 	panelWidth := 4 * width / 7 | ||||
| 	minWidth := 80 | ||||
| @@ -88,14 +110,8 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i | ||||
| 			panelWidth = minWidth | ||||
| 		} | ||||
| 	} | ||||
| 	panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth) | ||||
| 	if panelHeight > height*3/4 { | ||||
| 		panelHeight = height * 3 / 4 | ||||
| 	} | ||||
| 	return width/2 - panelWidth/2, | ||||
| 		height/2 - panelHeight/2 - panelHeight%2 - 1, | ||||
| 		width/2 + panelWidth/2, | ||||
| 		height/2 + panelHeight/2 | ||||
|  | ||||
| 	return panelWidth | ||||
| } | ||||
|  | ||||
| func (gui *Gui) prepareConfirmationPanel( | ||||
|   | ||||
| @@ -65,7 +65,6 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error { | ||||
| 	} | ||||
|  | ||||
| 	if !c.IsFocusable() { | ||||
| 		panic(c.GetKey()) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/branches_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/branches_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type BranchesContext struct { | ||||
| 	*BranchesViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*BranchesContext)(nil) | ||||
|  | ||||
| func NewBranchesContext( | ||||
| 	getModel func() []*models.Branch, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *BranchesContext { | ||||
| 	viewModel := NewBranchesViewModel(getModel) | ||||
|  | ||||
| 	return &BranchesContext{ | ||||
| 		BranchesViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "branches", | ||||
| 				WindowName: "branches", | ||||
| 				Key:        LOCAL_BRANCHES_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *BranchesContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type BranchesViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.Branch | ||||
| } | ||||
|  | ||||
| func NewBranchesViewModel(getModel func() []*models.Branch) *BranchesViewModel { | ||||
| 	self := &BranchesViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *BranchesViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *BranchesViewModel) GetSelected() *models.Branch { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
|  | ||||
| type CommitFilesContext struct { | ||||
| 	*filetree.CommitFileTreeViewModel | ||||
| 	*BaseContext | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| @@ -17,7 +16,7 @@ var _ types.IListContext = (*CommitFilesContext)(nil) | ||||
|  | ||||
| func NewCommitFilesContext( | ||||
| 	getModel func() []*models.CommitFile, | ||||
| 	getView func() *gocui.View, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| @@ -26,43 +25,30 @@ func NewCommitFilesContext( | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *CommitFilesContext { | ||||
| 	baseContext := NewBaseContext(NewBaseContextOpts{ | ||||
| 		ViewName:   "commitFiles", | ||||
| 		WindowName: "commits", | ||||
| 		Key:        COMMIT_FILES_CONTEXT_KEY, | ||||
| 		Kind:       types.SIDE_CONTEXT, | ||||
| 		Focusable:  true, | ||||
| 	}) | ||||
|  | ||||
| 	self := &CommitFilesContext{} | ||||
| 	takeFocus := func() error { return c.PushContext(self) } | ||||
|  | ||||
| 	viewModel := filetree.NewCommitFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree) | ||||
| 	viewTrait := NewViewTrait(getView) | ||||
| 	listContextTrait := &ListContextTrait{ | ||||
| 		base:      baseContext, | ||||
| 		list:      viewModel, | ||||
| 		viewTrait: viewTrait, | ||||
|  | ||||
| 		GetDisplayStrings: getDisplayStrings, | ||||
| 		OnFocus:           onFocus, | ||||
| 		OnRenderToMain:    onRenderToMain, | ||||
| 		OnFocusLost:       onFocusLost, | ||||
| 		takeFocus:         takeFocus, | ||||
|  | ||||
| 		// TODO: handle this in a trait | ||||
| 		RenderSelection: false, | ||||
|  | ||||
| 		c: c, | ||||
| 	return &CommitFilesContext{ | ||||
| 		CommitFileTreeViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext( | ||||
| 				NewBaseContext(NewBaseContextOpts{ | ||||
| 					ViewName:   "commitFiles", | ||||
| 					WindowName: "commits", | ||||
| 					Key:        COMMIT_FILES_CONTEXT_KEY, | ||||
| 					Kind:       types.SIDE_CONTEXT, | ||||
| 					Focusable:  true, | ||||
| 				}), | ||||
| 				ContextCallbackOpts{ | ||||
| 					OnFocus:        onFocus, | ||||
| 					OnFocusLost:    onFocusLost, | ||||
| 					OnRenderToMain: onRenderToMain, | ||||
| 				}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	baseContext.AddKeybindingsFn(listContextTrait.keybindings) | ||||
|  | ||||
| 	self.BaseContext = baseContext | ||||
| 	self.ListContextTrait = listContextTrait | ||||
| 	self.CommitFileTreeViewModel = viewModel | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *CommitFilesContext) GetSelectedItemId() string { | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| package context | ||||
|  | ||||
| import "github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	GLOBAL_CONTEXT_KEY              types.ContextKey = "global" | ||||
| @@ -60,18 +64,18 @@ type ContextTree struct { | ||||
| 	Global         types.Context | ||||
| 	Status         types.Context | ||||
| 	Files          *WorkingTreeContext | ||||
| 	Submodules     types.IListContext | ||||
| 	Menu           types.IListContext | ||||
| 	Branches       types.IListContext | ||||
| 	Remotes        types.IListContext | ||||
| 	RemoteBranches types.IListContext | ||||
| 	Menu           *MenuContext | ||||
| 	Branches       *BranchesContext | ||||
| 	Tags           *TagsContext | ||||
| 	BranchCommits  types.IListContext | ||||
| 	BranchCommits  *LocalCommitsContext | ||||
| 	CommitFiles    *CommitFilesContext | ||||
| 	ReflogCommits  types.IListContext | ||||
| 	SubCommits     types.IListContext | ||||
| 	Stash          types.IListContext | ||||
| 	Suggestions    types.IListContext | ||||
| 	Remotes        *RemotesContext | ||||
| 	Submodules     *SubmodulesContext | ||||
| 	RemoteBranches *RemoteBranchesContext | ||||
| 	ReflogCommits  *ReflogCommitsContext | ||||
| 	SubCommits     *SubCommitsContext | ||||
| 	Stash          *StashContext | ||||
| 	Suggestions    *SuggestionsContext | ||||
| 	Normal         types.Context | ||||
| 	Staging        types.Context | ||||
| 	PatchBuilding  types.Context | ||||
| @@ -113,6 +117,7 @@ func (self *ContextTree) Flatten() []types.Context { | ||||
|  | ||||
| type ViewContextMap struct { | ||||
| 	content map[string]types.Context | ||||
| 	sync.RWMutex | ||||
| } | ||||
|  | ||||
| func NewViewContextMap() *ViewContextMap { | ||||
| @@ -120,10 +125,15 @@ func NewViewContextMap() *ViewContextMap { | ||||
| } | ||||
|  | ||||
| func (self *ViewContextMap) Get(viewName string) types.Context { | ||||
| 	self.RLock() | ||||
| 	defer self.RUnlock() | ||||
|  | ||||
| 	return self.content[viewName] | ||||
| } | ||||
|  | ||||
| func (self *ViewContextMap) Set(viewName string, context types.Context) { | ||||
| 	self.Lock() | ||||
| 	defer self.Unlock() | ||||
| 	self.content[viewName] = context | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -3,44 +3,35 @@ package context | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| type ListContextTrait struct { | ||||
| 	base      types.IBaseContext | ||||
| 	list      types.IList | ||||
| 	viewTrait *ViewTrait | ||||
| 	types.Context | ||||
|  | ||||
| 	takeFocus func() error | ||||
|  | ||||
| 	GetDisplayStrings func(startIdx int, length int) [][]string | ||||
| 	OnFocus           func(...types.OnFocusOpts) error | ||||
| 	OnRenderToMain    func(...types.OnFocusOpts) error | ||||
| 	OnFocusLost       func() error | ||||
|  | ||||
| 	// if this is true, we'll call GetDisplayStrings for just the visible part of the | ||||
| 	// view and re-render that. This is useful when you need to render different | ||||
| 	// content based on the selection (e.g. for showing the selected commit) | ||||
| 	RenderSelection bool | ||||
|  | ||||
| 	c *types.ControllerCommon | ||||
| 	c                 *types.ControllerCommon | ||||
| 	list              types.IList | ||||
| 	viewTrait         *ViewTrait | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) GetList() types.IList { | ||||
| 	return self.list | ||||
| } | ||||
|  | ||||
| // TODO: remove | ||||
| func (self *ListContextTrait) GetPanelState() types.IListPanelState { | ||||
| 	return self.list | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) GetViewTrait() types.IViewTrait { | ||||
| 	return self.viewTrait | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) FocusLine() { | ||||
| 	// we need a way of knowing whether we've rendered to the view yet. | ||||
| 	self.viewTrait.FocusPoint(self.list.GetSelectedLineIdx()) | ||||
| 	if self.RenderSelection { | ||||
| 		min, max := self.viewTrait.ViewPortYBounds() | ||||
| 		displayStrings := self.GetDisplayStrings(min, max) | ||||
| 		content := utils.RenderDisplayStrings(displayStrings) | ||||
| 		self.viewTrait.SetViewPortContent(content) | ||||
| 	} | ||||
| 	self.viewTrait.SetFooter(formatListFooter(self.list.GetSelectedLineIdx(), self.list.GetItemsLength())) | ||||
| } | ||||
|  | ||||
| @@ -48,164 +39,29 @@ func formatListFooter(selectedLineIdx int, length int) string { | ||||
| 	return fmt.Sprintf("%d of %d", selectedLineIdx+1, length) | ||||
| } | ||||
|  | ||||
| // OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view | ||||
| func (self *ListContextTrait) HandleRender() error { | ||||
| 	if self.GetDisplayStrings != nil { | ||||
| 		self.list.RefreshSelectedIdx() | ||||
| 		content := utils.RenderDisplayStrings(self.GetDisplayStrings(0, self.list.GetItemsLength())) | ||||
| 		self.viewTrait.SetContent(content) | ||||
| 		self.c.Render() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleFocusLost() error { | ||||
| 	if self.OnFocusLost != nil { | ||||
| 		return self.OnFocusLost() | ||||
| 	} | ||||
|  | ||||
| 	self.viewTrait.SetOriginX(0) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleFocus(opts ...types.OnFocusOpts) error { | ||||
| 	self.FocusLine() | ||||
|  | ||||
| 	if self.OnFocus != nil { | ||||
| 		if err := self.OnFocus(opts...); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return self.Context.HandleFocus(opts...) | ||||
| } | ||||
|  | ||||
| 	if self.OnRenderToMain != nil { | ||||
| 		if err := self.OnRenderToMain(opts...); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| func (self *ListContextTrait) HandleFocusLost() error { | ||||
| 	self.viewTrait.SetOriginX(0) | ||||
|  | ||||
| 	return self.Context.HandleFocus() | ||||
| } | ||||
|  | ||||
| // OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view | ||||
| func (self *ListContextTrait) HandleRender() error { | ||||
| 	self.list.RefreshSelectedIdx() | ||||
| 	content := utils.RenderDisplayStrings(self.getDisplayStrings(0, self.list.GetItemsLength())) | ||||
| 	self.viewTrait.SetContent(content) | ||||
| 	self.c.Render() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandlePrevLine() error { | ||||
| 	return self.handleLineChange(-1) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleNextLine() error { | ||||
| 	return self.handleLineChange(1) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleScrollLeft() error { | ||||
| 	return self.scroll(self.viewTrait.ScrollLeft) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleScrollRight() error { | ||||
| 	return self.scroll(self.viewTrait.ScrollRight) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) scroll(scrollFunc func()) error { | ||||
| 	scrollFunc() | ||||
|  | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) handleLineChange(change int) error { | ||||
| 	before := self.list.GetSelectedLineIdx() | ||||
| 	self.list.MoveSelectedLine(change) | ||||
| 	after := self.list.GetSelectedLineIdx() | ||||
|  | ||||
| 	// 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.HandleFocus() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandlePrevPage() error { | ||||
| 	return self.handleLineChange(-self.viewTrait.PageDelta()) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleNextPage() error { | ||||
| 	return self.handleLineChange(self.viewTrait.PageDelta()) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleGotoTop() error { | ||||
| 	return self.handleLineChange(-self.list.GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleGotoBottom() error { | ||||
| 	return self.handleLineChange(self.list.GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleClick(onClick func() error) error { | ||||
| 	prevSelectedLineIdx := self.list.GetSelectedLineIdx() | ||||
| 	// because we're handling a click, we need to determine the new line idx based | ||||
| 	// on the view itself. | ||||
| 	newSelectedLineIdx := self.viewTrait.SelectedLineIdx() | ||||
|  | ||||
| 	currentContextKey := self.c.CurrentContext().GetKey() | ||||
| 	alreadyFocused := currentContextKey == self.base.GetKey() | ||||
|  | ||||
| 	// we need to focus the view | ||||
| 	if !alreadyFocused { | ||||
| 		if err := self.takeFocus(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if newSelectedLineIdx > self.list.GetItemsLength()-1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	self.list.SetSelectedLineIdx(newSelectedLineIdx) | ||||
|  | ||||
| 	if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && onClick != nil { | ||||
| 		return onClick() | ||||
| 	} | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error { | ||||
| 	self.list.SetSelectedLineIdx(selectedLineIdx) | ||||
| 	self.GetList().SetSelectedLineIdx(selectedLineIdx) | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) HandleRenderToMain() error { | ||||
| 	if self.OnRenderToMain != nil { | ||||
| 		return self.OnRenderToMain() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContextTrait) keybindings(opts types.KeybindingsOpts) []*types.Binding { | ||||
| 	return []*types.Binding{ | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.c.Tr.LcPrevPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.c.Tr.LcNextPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.c.Tr.LcGotoTop}, | ||||
| 		{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.StartSearch), | ||||
| 			Handler:     func() error { self.c.OpenSearch(); return nil }, | ||||
| 			Description: self.c.Tr.LcStartSearch, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.GotoBottom), | ||||
| 			Description: self.c.Tr.LcGotoBottom, | ||||
| 			Handler:     self.HandleGotoBottom, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										87
									
								
								pkg/gui/context/local_commits_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								pkg/gui/context/local_commits_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type LocalCommitsContext struct { | ||||
| 	*LocalCommitsViewModel | ||||
| 	*ViewportListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*LocalCommitsContext)(nil) | ||||
|  | ||||
| func NewLocalCommitsContext( | ||||
| 	getModel func() []*models.Commit, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *LocalCommitsContext { | ||||
| 	viewModel := NewLocalCommitsViewModel(getModel) | ||||
|  | ||||
| 	return &LocalCommitsContext{ | ||||
| 		LocalCommitsViewModel: viewModel, | ||||
| 		ViewportListContextTrait: &ViewportListContextTrait{ | ||||
| 			ListContextTrait: &ListContextTrait{ | ||||
| 				Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 					ViewName:   "commits", | ||||
| 					WindowName: "commits", | ||||
| 					Key:        BRANCH_COMMITS_CONTEXT_KEY, | ||||
| 					Kind:       types.SIDE_CONTEXT, | ||||
| 					Focusable:  true, | ||||
| 				}), ContextCallbackOpts{ | ||||
| 					OnFocus:        onFocus, | ||||
| 					OnFocusLost:    onFocusLost, | ||||
| 					OnRenderToMain: onRenderToMain, | ||||
| 				}), | ||||
| 				list:              viewModel, | ||||
| 				viewTrait:         NewViewTrait(view), | ||||
| 				getDisplayStrings: getDisplayStrings, | ||||
| 				c:                 c, | ||||
| 			}}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type LocalCommitsViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.Commit | ||||
| } | ||||
|  | ||||
| func NewLocalCommitsViewModel(getModel func() []*models.Commit) *LocalCommitsViewModel { | ||||
| 	self := &LocalCommitsViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsViewModel) GetSelected() *models.Commit { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										108
									
								
								pkg/gui/context/menu_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								pkg/gui/context/menu_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/presentation" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type MenuContext struct { | ||||
| 	*MenuViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*MenuContext)(nil) | ||||
|  | ||||
| func NewMenuContext( | ||||
| 	view *gocui.View, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| 	getOptionsMap func() map[string]string, | ||||
| ) *MenuContext { | ||||
| 	viewModel := NewMenuViewModel() | ||||
|  | ||||
| 	return &MenuContext{ | ||||
| 		MenuViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:        "menu", | ||||
| 				Key:             "menu", | ||||
| 				Kind:            types.PERSISTENT_POPUP, | ||||
| 				OnGetOptionsMap: getOptionsMap, | ||||
| 				Focusable:       true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			getDisplayStrings: viewModel.GetDisplayStrings, | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO: remove this thing. | ||||
| func (self *MenuContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.DisplayString | ||||
| } | ||||
|  | ||||
| type MenuViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	menuItems []*types.MenuItem | ||||
| } | ||||
|  | ||||
| func NewMenuViewModel() *MenuViewModel { | ||||
| 	self := &MenuViewModel{ | ||||
| 		menuItems: nil, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *MenuViewModel) GetItemsLength() int { | ||||
| 	return len(self.menuItems) | ||||
| } | ||||
|  | ||||
| func (self *MenuViewModel) GetSelected() *types.MenuItem { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.menuItems[self.GetSelectedLineIdx()] | ||||
| } | ||||
|  | ||||
| func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem) { | ||||
| 	self.menuItems = items | ||||
| } | ||||
|  | ||||
| // TODO: move into presentation package | ||||
| func (self *MenuViewModel) GetDisplayStrings(startIdx int, length int) [][]string { | ||||
| 	stringArrays := make([][]string, len(self.menuItems)) | ||||
| 	for i, item := range self.menuItems { | ||||
| 		if item.DisplayStrings == nil { | ||||
| 			styledStr := item.DisplayString | ||||
| 			if item.OpensMenu { | ||||
| 				styledStr = presentation.OpensMenuStyle(styledStr) | ||||
| 			} | ||||
| 			stringArrays[i] = []string{styledStr} | ||||
| 		} else { | ||||
| 			stringArrays[i] = item.DisplayStrings | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return stringArrays | ||||
| } | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/reflog_commits_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/reflog_commits_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type ReflogCommitsContext struct { | ||||
| 	*ReflogCommitsViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*ReflogCommitsContext)(nil) | ||||
|  | ||||
| func NewReflogCommitsContext( | ||||
| 	getModel func() []*models.Commit, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *ReflogCommitsContext { | ||||
| 	viewModel := NewReflogCommitsViewModel(getModel) | ||||
|  | ||||
| 	return &ReflogCommitsContext{ | ||||
| 		ReflogCommitsViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "commits", | ||||
| 				WindowName: "commits", | ||||
| 				Key:        REFLOG_COMMITS_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *ReflogCommitsContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type ReflogCommitsViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.Commit | ||||
| } | ||||
|  | ||||
| func NewReflogCommitsViewModel(getModel func() []*models.Commit) *ReflogCommitsViewModel { | ||||
| 	self := &ReflogCommitsViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *ReflogCommitsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *ReflogCommitsViewModel) GetSelected() *models.Commit { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/remote_branches_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/remote_branches_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type RemoteBranchesContext struct { | ||||
| 	*RemoteBranchesViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*RemoteBranchesContext)(nil) | ||||
|  | ||||
| func NewRemoteBranchesContext( | ||||
| 	getModel func() []*models.RemoteBranch, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *RemoteBranchesContext { | ||||
| 	viewModel := NewRemoteBranchesViewModel(getModel) | ||||
|  | ||||
| 	return &RemoteBranchesContext{ | ||||
| 		RemoteBranchesViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "branches", | ||||
| 				WindowName: "branches", | ||||
| 				Key:        REMOTE_BRANCHES_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *RemoteBranchesContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type RemoteBranchesViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.RemoteBranch | ||||
| } | ||||
|  | ||||
| func NewRemoteBranchesViewModel(getModel func() []*models.RemoteBranch) *RemoteBranchesViewModel { | ||||
| 	self := &RemoteBranchesViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *RemoteBranchesViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *RemoteBranchesViewModel) GetSelected() *models.RemoteBranch { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/remotes_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/remotes_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type RemotesContext struct { | ||||
| 	*RemotesViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*RemotesContext)(nil) | ||||
|  | ||||
| func NewRemotesContext( | ||||
| 	getModel func() []*models.Remote, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *RemotesContext { | ||||
| 	viewModel := NewRemotesViewModel(getModel) | ||||
|  | ||||
| 	return &RemotesContext{ | ||||
| 		RemotesViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "branches", | ||||
| 				WindowName: "branches", | ||||
| 				Key:        REMOTES_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *RemotesContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type RemotesViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.Remote | ||||
| } | ||||
|  | ||||
| func NewRemotesViewModel(getModel func() []*models.Remote) *RemotesViewModel { | ||||
| 	self := &RemotesViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *RemotesViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *RemotesViewModel) GetSelected() *models.Remote { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
| @@ -1,7 +1,6 @@ | ||||
| package gui | ||||
| package context | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
| 
 | ||||
| @@ -12,10 +11,10 @@ type SimpleContext struct { | ||||
| 	// this is for pushing some content to the main view | ||||
| 	OnRenderToMain func(opts ...types.OnFocusOpts) error | ||||
| 
 | ||||
| 	*context.BaseContext | ||||
| 	*BaseContext | ||||
| } | ||||
| 
 | ||||
| type NewSimpleContextOpts struct { | ||||
| type ContextCallbackOpts struct { | ||||
| 	OnFocus     func(opts ...types.OnFocusOpts) error | ||||
| 	OnFocusLost func() error | ||||
| 	OnRender    func() error | ||||
| @@ -23,7 +22,7 @@ type NewSimpleContextOpts struct { | ||||
| 	OnRenderToMain func(opts ...types.OnFocusOpts) error | ||||
| } | ||||
| 
 | ||||
| func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpts) *SimpleContext { | ||||
| func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *SimpleContext { | ||||
| 	return &SimpleContext{ | ||||
| 		OnFocus:        opts.OnFocus, | ||||
| 		OnFocusLost:    opts.OnFocusLost, | ||||
| @@ -35,13 +34,6 @@ func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpt | ||||
| 
 | ||||
| var _ types.Context = &SimpleContext{} | ||||
| 
 | ||||
| func (self *SimpleContext) HandleRender() error { | ||||
| 	if self.OnRender != nil { | ||||
| 		return self.OnRender() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (self *SimpleContext) HandleFocus(opts ...types.OnFocusOpts) error { | ||||
| 	if self.OnFocus != nil { | ||||
| 		if err := self.OnFocus(opts...); err != nil { | ||||
| @@ -65,6 +57,13 @@ func (self *SimpleContext) HandleFocusLost() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (self *SimpleContext) HandleRender() error { | ||||
| 	if self.OnRender != nil { | ||||
| 		return self.OnRender() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (self *SimpleContext) HandleRenderToMain() error { | ||||
| 	if self.OnRenderToMain != nil { | ||||
| 		return self.OnRenderToMain() | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/stash_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/stash_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type StashContext struct { | ||||
| 	*StashViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*StashContext)(nil) | ||||
|  | ||||
| func NewStashContext( | ||||
| 	getModel func() []*models.StashEntry, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *StashContext { | ||||
| 	viewModel := NewStashViewModel(getModel) | ||||
|  | ||||
| 	return &StashContext{ | ||||
| 		StashViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "stash", | ||||
| 				WindowName: "stash", | ||||
| 				Key:        STASH_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *StashContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type StashViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.StashEntry | ||||
| } | ||||
|  | ||||
| func NewStashViewModel(getModel func() []*models.StashEntry) *StashViewModel { | ||||
| 	self := &StashViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *StashViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *StashViewModel) GetSelected() *models.StashEntry { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										87
									
								
								pkg/gui/context/sub_commits_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								pkg/gui/context/sub_commits_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type SubCommitsContext struct { | ||||
| 	*SubCommitsViewModel | ||||
| 	*ViewportListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*SubCommitsContext)(nil) | ||||
|  | ||||
| func NewSubCommitsContext( | ||||
| 	getModel func() []*models.Commit, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *SubCommitsContext { | ||||
| 	viewModel := NewSubCommitsViewModel(getModel) | ||||
|  | ||||
| 	return &SubCommitsContext{ | ||||
| 		SubCommitsViewModel: viewModel, | ||||
| 		ViewportListContextTrait: &ViewportListContextTrait{ | ||||
| 			ListContextTrait: &ListContextTrait{ | ||||
| 				Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 					ViewName:   "branches", | ||||
| 					WindowName: "branches", | ||||
| 					Key:        SUB_COMMITS_CONTEXT_KEY, | ||||
| 					Kind:       types.SIDE_CONTEXT, | ||||
| 					Focusable:  true, | ||||
| 				}), ContextCallbackOpts{ | ||||
| 					OnFocus:        onFocus, | ||||
| 					OnFocusLost:    onFocusLost, | ||||
| 					OnRenderToMain: onRenderToMain, | ||||
| 				}), | ||||
| 				list:              viewModel, | ||||
| 				viewTrait:         NewViewTrait(view), | ||||
| 				getDisplayStrings: getDisplayStrings, | ||||
| 				c:                 c, | ||||
| 			}}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *SubCommitsContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type SubCommitsViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.Commit | ||||
| } | ||||
|  | ||||
| func NewSubCommitsViewModel(getModel func() []*models.Commit) *SubCommitsViewModel { | ||||
| 	self := &SubCommitsViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *SubCommitsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *SubCommitsViewModel) GetSelected() *models.Commit { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										86
									
								
								pkg/gui/context/submodules_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/gui/context/submodules_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type SubmodulesContext struct { | ||||
| 	*SubmodulesViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*SubmodulesContext)(nil) | ||||
|  | ||||
| func NewSubmodulesContext( | ||||
| 	getModel func() []*models.SubmoduleConfig, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *SubmodulesContext { | ||||
| 	viewModel := NewSubmodulesViewModel(getModel) | ||||
|  | ||||
| 	return &SubmodulesContext{ | ||||
| 		SubmodulesViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "files", | ||||
| 				WindowName: "files", | ||||
| 				Key:        SUBMODULES_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *SubmodulesContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| type SubmodulesViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*models.SubmoduleConfig | ||||
| } | ||||
|  | ||||
| func NewSubmodulesViewModel(getModel func() []*models.SubmoduleConfig) *SubmodulesViewModel { | ||||
| 	self := &SubmodulesViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *SubmodulesViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *SubmodulesViewModel) GetSelected() *models.SubmoduleConfig { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
							
								
								
									
										85
									
								
								pkg/gui/context/suggestions_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								pkg/gui/context/suggestions_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context/traits" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type SuggestionsContext struct { | ||||
| 	*SuggestionsViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*SuggestionsContext)(nil) | ||||
|  | ||||
| func NewSuggestionsContext( | ||||
| 	getModel func() []*types.Suggestion, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| 	onRenderToMain func(...types.OnFocusOpts) error, | ||||
| 	onFocusLost func() error, | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *SuggestionsContext { | ||||
| 	viewModel := NewSuggestionsViewModel(getModel) | ||||
|  | ||||
| 	return &SuggestionsContext{ | ||||
| 		SuggestionsViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "suggestions", | ||||
| 				WindowName: "suggestions", | ||||
| 				Key:        SUGGESTIONS_CONTEXT_KEY, | ||||
| 				Kind:       types.PERSISTENT_POPUP, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *SuggestionsContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return item.Value | ||||
| } | ||||
|  | ||||
| type SuggestionsViewModel struct { | ||||
| 	*traits.ListCursor | ||||
| 	getModel func() []*types.Suggestion | ||||
| } | ||||
|  | ||||
| func NewSuggestionsViewModel(getModel func() []*types.Suggestion) *SuggestionsViewModel { | ||||
| 	self := &SuggestionsViewModel{ | ||||
| 		getModel: getModel, | ||||
| 	} | ||||
|  | ||||
| 	self.ListCursor = traits.NewListCursor(self) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *SuggestionsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *SuggestionsViewModel) GetSelected() *types.Suggestion { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
|  | ||||
| type TagsContext struct { | ||||
| 	*TagsViewModel | ||||
| 	*BaseContext | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| @@ -17,7 +16,7 @@ var _ types.IListContext = (*TagsContext)(nil) | ||||
|  | ||||
| func NewTagsContext( | ||||
| 	getModel func() []*models.Tag, | ||||
| 	getView func() *gocui.View, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| @@ -26,47 +25,32 @@ func NewTagsContext( | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *TagsContext { | ||||
| 	baseContext := NewBaseContext(NewBaseContextOpts{ | ||||
| 		ViewName:   "branches", | ||||
| 		WindowName: "branches", | ||||
| 		Key:        TAGS_CONTEXT_KEY, | ||||
| 		Kind:       types.SIDE_CONTEXT, | ||||
| 		Focusable:  true, | ||||
| 	}) | ||||
| 	viewModel := NewTagsViewModel(getModel) | ||||
|  | ||||
| 	self := &TagsContext{} | ||||
| 	takeFocus := func() error { return c.PushContext(self) } | ||||
|  | ||||
| 	list := NewTagsViewModel(getModel) | ||||
| 	viewTrait := NewViewTrait(getView) | ||||
| 	listContextTrait := &ListContextTrait{ | ||||
| 		base:      baseContext, | ||||
| 		list:      list, | ||||
| 		viewTrait: viewTrait, | ||||
|  | ||||
| 		GetDisplayStrings: getDisplayStrings, | ||||
| 		OnFocus:           onFocus, | ||||
| 		OnRenderToMain:    onRenderToMain, | ||||
| 		OnFocusLost:       onFocusLost, | ||||
| 		takeFocus:         takeFocus, | ||||
|  | ||||
| 		// TODO: handle this in a trait | ||||
| 		RenderSelection: false, | ||||
|  | ||||
| 		c: c, | ||||
| 	return &TagsContext{ | ||||
| 		TagsViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "branches", | ||||
| 				WindowName: "branches", | ||||
| 				Key:        TAGS_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	baseContext.AddKeybindingsFn(listContextTrait.keybindings) | ||||
|  | ||||
| 	self.BaseContext = baseContext | ||||
| 	self.ListContextTrait = listContextTrait | ||||
| 	self.TagsViewModel = list | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *TagsContext) GetSelectedItemId() string { | ||||
| 	item := self.GetSelectedTag() | ||||
| 	item := self.GetSelected() | ||||
| 	if item == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| @@ -79,18 +63,6 @@ type TagsViewModel struct { | ||||
| 	getModel func() []*models.Tag | ||||
| } | ||||
|  | ||||
| func (self *TagsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *TagsViewModel) GetSelectedTag() *models.Tag { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
|  | ||||
| func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel { | ||||
| 	self := &TagsViewModel{ | ||||
| 		getModel: getModel, | ||||
| @@ -100,3 +72,15 @@ func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel { | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *TagsViewModel) GetItemsLength() int { | ||||
| 	return len(self.getModel()) | ||||
| } | ||||
|  | ||||
| func (self *TagsViewModel) GetSelected() *models.Tag { | ||||
| 	if self.GetItemsLength() == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return self.getModel()[self.GetSelectedLineIdx()] | ||||
| } | ||||
|   | ||||
| @@ -2,70 +2,62 @@ package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| const HORIZONTAL_SCROLL_FACTOR = 3 | ||||
|  | ||||
| type ViewTrait struct { | ||||
| 	getView func() *gocui.View | ||||
| 	view *gocui.View | ||||
| } | ||||
|  | ||||
| func NewViewTrait(getView func() *gocui.View) *ViewTrait { | ||||
| 	return &ViewTrait{getView: getView} | ||||
| var _ types.IViewTrait = &ViewTrait{} | ||||
|  | ||||
| func NewViewTrait(view *gocui.View) *ViewTrait { | ||||
| 	return &ViewTrait{view: view} | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) FocusPoint(yIdx int) { | ||||
| 	view := self.getView() | ||||
| 	view.FocusPoint(view.OriginX(), yIdx) | ||||
| 	self.view.FocusPoint(self.view.OriginX(), yIdx) | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) SetViewPortContent(content string) { | ||||
| 	view := self.getView() | ||||
|  | ||||
| 	_, y := view.Origin() | ||||
| 	view.OverwriteLines(y, content) | ||||
| 	_, y := self.view.Origin() | ||||
| 	self.view.OverwriteLines(y, content) | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) SetContent(content string) { | ||||
| 	self.getView().SetContent(content) | ||||
| 	self.view.SetContent(content) | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) SetFooter(value string) { | ||||
| 	self.getView().Footer = value | ||||
| 	self.view.Footer = value | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) SetOriginX(value int) { | ||||
| 	_ = self.getView().SetOriginX(value) | ||||
| 	_ = self.view.SetOriginX(value) | ||||
| } | ||||
|  | ||||
| // tells us the bounds of line indexes shown in the view currently | ||||
| func (self *ViewTrait) ViewPortYBounds() (int, int) { | ||||
| 	view := self.getView() | ||||
|  | ||||
| 	_, min := view.Origin() | ||||
| 	max := view.InnerHeight() + 1 | ||||
| 	_, min := self.view.Origin() | ||||
| 	max := self.view.InnerHeight() + 1 | ||||
| 	return min, max | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) ScrollLeft() { | ||||
| 	view := self.getView() | ||||
|  | ||||
| 	newOriginX := utils.Max(view.OriginX()-view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0) | ||||
| 	_ = view.SetOriginX(newOriginX) | ||||
| 	newOriginX := utils.Max(self.view.OriginX()-self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0) | ||||
| 	_ = self.view.SetOriginX(newOriginX) | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) ScrollRight() { | ||||
| 	view := self.getView() | ||||
|  | ||||
| 	_ = view.SetOriginX(view.OriginX() + view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR) | ||||
| 	_ = self.view.SetOriginX(self.view.OriginX() + self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR) | ||||
| } | ||||
|  | ||||
| // this returns the amount we'll scroll if we want to scroll by a page. | ||||
| func (self *ViewTrait) PageDelta() int { | ||||
| 	view := self.getView() | ||||
|  | ||||
| 	_, height := view.Size() | ||||
| 	_, height := self.view.Size() | ||||
|  | ||||
| 	delta := height - 1 | ||||
| 	if delta == 0 { | ||||
| @@ -76,5 +68,5 @@ func (self *ViewTrait) PageDelta() int { | ||||
| } | ||||
|  | ||||
| func (self *ViewTrait) SelectedLineIdx() int { | ||||
| 	return self.getView().SelectedLineIdx() | ||||
| 	return self.view.SelectedLineIdx() | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								pkg/gui/context/viewport_list_context_trait.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								pkg/gui/context/viewport_list_context_trait.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| package context | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| // This embeds a list context trait and adds logic to re-render the viewport | ||||
| // whenever a line is focused. We use this in the commits panel because different | ||||
| // sections of the log graph need to be highlighted depending on the currently selected line | ||||
|  | ||||
| type ViewportListContextTrait struct { | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| func (self *ViewportListContextTrait) FocusLine() { | ||||
| 	self.ListContextTrait.FocusLine() | ||||
|  | ||||
| 	min, max := self.GetViewTrait().ViewPortYBounds() | ||||
| 	displayStrings := self.ListContextTrait.getDisplayStrings(min, max) | ||||
| 	content := utils.RenderDisplayStrings(displayStrings) | ||||
| 	self.GetViewTrait().SetViewPortContent(content) | ||||
| } | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
|  | ||||
| type WorkingTreeContext struct { | ||||
| 	*filetree.FileTreeViewModel | ||||
| 	*BaseContext | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| @@ -17,7 +16,7 @@ var _ types.IListContext = (*WorkingTreeContext)(nil) | ||||
|  | ||||
| func NewWorkingTreeContext( | ||||
| 	getModel func() []*models.File, | ||||
| 	getView func() *gocui.View, | ||||
| 	view *gocui.View, | ||||
| 	getDisplayStrings func(startIdx int, length int) [][]string, | ||||
|  | ||||
| 	onFocus func(...types.OnFocusOpts) error, | ||||
| @@ -26,43 +25,28 @@ func NewWorkingTreeContext( | ||||
|  | ||||
| 	c *types.ControllerCommon, | ||||
| ) *WorkingTreeContext { | ||||
| 	baseContext := NewBaseContext(NewBaseContextOpts{ | ||||
| 		ViewName:   "files", | ||||
| 		WindowName: "files", | ||||
| 		Key:        FILES_CONTEXT_KEY, | ||||
| 		Kind:       types.SIDE_CONTEXT, | ||||
| 		Focusable:  true, | ||||
| 	}) | ||||
|  | ||||
| 	self := &WorkingTreeContext{} | ||||
| 	takeFocus := func() error { return c.PushContext(self) } | ||||
|  | ||||
| 	viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree) | ||||
| 	viewTrait := NewViewTrait(getView) | ||||
| 	listContextTrait := &ListContextTrait{ | ||||
| 		base:      baseContext, | ||||
| 		list:      viewModel, | ||||
| 		viewTrait: viewTrait, | ||||
|  | ||||
| 		GetDisplayStrings: getDisplayStrings, | ||||
| 		OnFocus:           onFocus, | ||||
| 		OnRenderToMain:    onRenderToMain, | ||||
| 		OnFocusLost:       onFocusLost, | ||||
| 		takeFocus:         takeFocus, | ||||
|  | ||||
| 		// TODO: handle this in a trait | ||||
| 		RenderSelection: false, | ||||
|  | ||||
| 		c: c, | ||||
| 	return &WorkingTreeContext{ | ||||
| 		FileTreeViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| 				ViewName:   "files", | ||||
| 				WindowName: "files", | ||||
| 				Key:        FILES_CONTEXT_KEY, | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				Focusable:  true, | ||||
| 			}), ContextCallbackOpts{ | ||||
| 				OnFocus:        onFocus, | ||||
| 				OnFocusLost:    onFocusLost, | ||||
| 				OnRenderToMain: onRenderToMain, | ||||
| 			}), | ||||
| 			list:              viewModel, | ||||
| 			viewTrait:         NewViewTrait(view), | ||||
| 			getDisplayStrings: getDisplayStrings, | ||||
| 			c:                 c, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	baseContext.AddKeybindingsFn(listContextTrait.keybindings) | ||||
|  | ||||
| 	self.BaseContext = baseContext | ||||
| 	self.ListContextTrait = listContextTrait | ||||
| 	self.FileTreeViewModel = viewModel | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeContext) GetSelectedItemId() string { | ||||
|   | ||||
| @@ -35,7 +35,7 @@ func (gui *Gui) allContexts2() []types.Context { | ||||
|  | ||||
| func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 	return &context.ContextTree{ | ||||
| 		Global: NewSimpleContext( | ||||
| 		Global: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.GLOBAL_CONTEXT, | ||||
| 				ViewName:   "", | ||||
| @@ -43,11 +43,11 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.GLOBAL_CONTEXT_KEY, | ||||
| 				Focusable:  false, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain), | ||||
| 			}, | ||||
| 		), | ||||
| 		Status: NewSimpleContext( | ||||
| 		Status: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.SIDE_CONTEXT, | ||||
| 				ViewName:   "status", | ||||
| @@ -55,7 +55,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.STATUS_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain), | ||||
| 			}, | ||||
| 		), | ||||
| @@ -72,7 +72,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 		Tags:           gui.tagsListContext(), | ||||
| 		Stash:          gui.stashListContext(), | ||||
| 		Suggestions:    gui.suggestionsListContext(), | ||||
| 		Normal: NewSimpleContext( | ||||
| 		Normal: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.MAIN_CONTEXT, | ||||
| 				ViewName:   "main", | ||||
| @@ -80,13 +80,13 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.MAIN_NORMAL_CONTEXT_KEY, | ||||
| 				Focusable:  false, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: func(opts ...types.OnFocusOpts) error { | ||||
| 					return nil // TODO: should we do something here? We should allow for scrolling the panel | ||||
| 				}, | ||||
| 			}, | ||||
| 		), | ||||
| 		Staging: NewSimpleContext( | ||||
| 		Staging: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.MAIN_CONTEXT, | ||||
| 				ViewName:   "main", | ||||
| @@ -94,7 +94,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.MAIN_STAGING_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: func(opts ...types.OnFocusOpts) error { | ||||
| 					forceSecondaryFocused := false | ||||
| 					selectedLineIdx := -1 | ||||
| @@ -110,7 +110,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				}, | ||||
| 			}, | ||||
| 		), | ||||
| 		PatchBuilding: NewSimpleContext( | ||||
| 		PatchBuilding: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.MAIN_CONTEXT, | ||||
| 				ViewName:   "main", | ||||
| @@ -118,7 +118,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.MAIN_PATCH_BUILDING_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: func(opts ...types.OnFocusOpts) error { | ||||
| 					selectedLineIdx := -1 | ||||
| 					if len(opts) > 0 && (opts[0].ClickedViewName == "main" || opts[0].ClickedViewName == "secondary") { | ||||
| @@ -129,7 +129,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				}, | ||||
| 			}, | ||||
| 		), | ||||
| 		Merging: NewSimpleContext( | ||||
| 		Merging: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:            types.MAIN_CONTEXT, | ||||
| 				ViewName:        "main", | ||||
| @@ -138,11 +138,11 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				OnGetOptionsMap: gui.getMergingOptions, | ||||
| 				Focusable:       true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: OnFocusWrapper(func() error { return gui.renderConflictsWithLock(true) }), | ||||
| 			}, | ||||
| 		), | ||||
| 		Credentials: NewSimpleContext( | ||||
| 		Credentials: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.PERSISTENT_POPUP, | ||||
| 				ViewName:   "credentials", | ||||
| @@ -150,11 +150,11 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.CREDENTIALS_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: OnFocusWrapper(gui.handleAskFocused), | ||||
| 			}, | ||||
| 		), | ||||
| 		Confirmation: NewSimpleContext( | ||||
| 		Confirmation: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.TEMPORARY_POPUP, | ||||
| 				ViewName:   "confirmation", | ||||
| @@ -162,11 +162,11 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.CONFIRMATION_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: OnFocusWrapper(gui.handleAskFocused), | ||||
| 			}, | ||||
| 		), | ||||
| 		CommitMessage: NewSimpleContext( | ||||
| 		CommitMessage: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.PERSISTENT_POPUP, | ||||
| 				ViewName:   "commitMessage", | ||||
| @@ -174,11 +174,11 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.COMMIT_MESSAGE_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocus: OnFocusWrapper(gui.handleCommitMessageFocused), | ||||
| 			}, | ||||
| 		), | ||||
| 		Search: NewSimpleContext( | ||||
| 		Search: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:       types.PERSISTENT_POPUP, | ||||
| 				ViewName:   "search", | ||||
| @@ -186,9 +186,9 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				Key:        context.SEARCH_CONTEXT_KEY, | ||||
| 				Focusable:  true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{}, | ||||
| 			context.ContextCallbackOpts{}, | ||||
| 		), | ||||
| 		CommandLog: NewSimpleContext( | ||||
| 		CommandLog: context.NewSimpleContext( | ||||
| 			context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 				Kind:            types.EXTRAS_CONTEXT, | ||||
| 				ViewName:        "extras", | ||||
| @@ -197,7 +197,7 @@ func (gui *Gui) contextTree() *context.ContextTree { | ||||
| 				OnGetOptionsMap: gui.getMergingOptions, | ||||
| 				Focusable:       true, | ||||
| 			}), | ||||
| 			NewSimpleContextOpts{ | ||||
| 			context.ContextCallbackOpts{ | ||||
| 				OnFocusLost: func() error { | ||||
| 					gui.Views.Extras.Autoscroll = true | ||||
| 					return nil | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| @@ -14,23 +15,21 @@ type BisectController struct { | ||||
| 	baseController | ||||
|  | ||||
| 	c            *types.ControllerCommon | ||||
| 	context      types.IListContext | ||||
| 	context      *context.LocalCommitsContext | ||||
| 	git          *commands.GitCommand | ||||
| 	bisectHelper *BisectHelper | ||||
|  | ||||
| 	getSelectedLocalCommit func() *models.Commit | ||||
| 	getCommits             func() []*models.Commit | ||||
| 	getCommits func() []*models.Commit | ||||
| } | ||||
|  | ||||
| var _ types.IController = &BisectController{} | ||||
|  | ||||
| func NewBisectController( | ||||
| 	c *types.ControllerCommon, | ||||
| 	context types.IListContext, | ||||
| 	context *context.LocalCommitsContext, | ||||
| 	git *commands.GitCommand, | ||||
| 	bisectHelper *BisectHelper, | ||||
|  | ||||
| 	getSelectedLocalCommit func() *models.Commit, | ||||
| 	getCommits func() []*models.Commit, | ||||
| ) *BisectController { | ||||
| 	return &BisectController{ | ||||
| @@ -40,8 +39,7 @@ func NewBisectController( | ||||
| 		git:            git, | ||||
| 		bisectHelper:   bisectHelper, | ||||
|  | ||||
| 		getSelectedLocalCommit: getSelectedLocalCommit, | ||||
| 		getCommits:             getCommits, | ||||
| 		getCommits: getCommits, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -234,7 +232,7 @@ func (self *BisectController) selectCurrentBisectCommit() { | ||||
| 		// find index of commit with that sha, move cursor to that. | ||||
| 		for i, commit := range self.getCommits() { | ||||
| 			if commit.Sha == info.GetCurrentSha() { | ||||
| 				self.context.GetPanelState().SetSelectedLineIdx(i) | ||||
| 				self.context.SetSelectedLineIdx(i) | ||||
| 				_ = self.context.HandleFocus() | ||||
| 				break | ||||
| 			} | ||||
| @@ -244,7 +242,7 @@ func (self *BisectController) selectCurrentBisectCommit() { | ||||
|  | ||||
| func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error { | ||||
| 	return func() error { | ||||
| 		commit := self.getSelectedLocalCommit() | ||||
| 		commit := self.context.GetSelected() | ||||
| 		if commit == nil { | ||||
| 			return nil | ||||
| 		} | ||||
|   | ||||
| @@ -94,10 +94,10 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types | ||||
| 			Handler:     self.checkSelectedFileNode(self.press), | ||||
| 			Description: self.c.Tr.LcToggleStaged, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:     gocui.MouseLeft, | ||||
| 			Handler: func() error { return self.context.HandleClick(self.checkSelectedFileNode(self.press)) }, | ||||
| 		}, | ||||
| 		// { | ||||
| 		// 	Key:     gocui.MouseLeft, | ||||
| 		// 	Handler: func() error { return self.context.HandleClick(self.checkSelectedFileNode(self.press)) }, | ||||
| 		// }, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey("<c-b>"), // TODO: softcode | ||||
| 			Handler:     self.handleStatusFilterPressed, | ||||
|   | ||||
							
								
								
									
										144
									
								
								pkg/gui/controllers/list_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								pkg/gui/controllers/list_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type ListControllerFactory struct { | ||||
| 	c *types.ControllerCommon | ||||
| } | ||||
|  | ||||
| func NewListControllerFactory(c *types.ControllerCommon) *ListControllerFactory { | ||||
| 	return &ListControllerFactory{ | ||||
| 		c: c, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *ListControllerFactory) Create(context types.IListContext) *ListController { | ||||
| 	return &ListController{ | ||||
| 		baseController: baseController{}, | ||||
| 		c:              self.c, | ||||
| 		context:        context, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type ListController struct { | ||||
| 	baseController | ||||
| 	c *types.ControllerCommon | ||||
|  | ||||
| 	context types.IListContext | ||||
| } | ||||
|  | ||||
| func (self *ListController) Context() types.Context { | ||||
| 	return self.context | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandlePrevLine() error { | ||||
| 	return self.handleLineChange(-1) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleNextLine() error { | ||||
| 	return self.handleLineChange(1) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleScrollLeft() error { | ||||
| 	return self.scroll(self.context.GetViewTrait().ScrollLeft) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleScrollRight() error { | ||||
| 	return self.scroll(self.context.GetViewTrait().ScrollRight) | ||||
| } | ||||
|  | ||||
| func (self *ListController) scroll(scrollFunc func()) error { | ||||
| 	scrollFunc() | ||||
|  | ||||
| 	return self.context.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListController) handleLineChange(change int) error { | ||||
| 	before := self.context.GetList().GetSelectedLineIdx() | ||||
| 	self.context.GetList().MoveSelectedLine(change) | ||||
| 	after := self.context.GetList().GetSelectedLineIdx() | ||||
|  | ||||
| 	// 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 nil | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandlePrevPage() error { | ||||
| 	return self.handleLineChange(-self.context.GetViewTrait().PageDelta()) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleNextPage() error { | ||||
| 	return self.handleLineChange(self.context.GetViewTrait().PageDelta()) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleGotoTop() error { | ||||
| 	return self.handleLineChange(-self.context.GetList().GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleGotoBottom() error { | ||||
| 	return self.handleLineChange(self.context.GetList().GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListController) HandleClick(onClick func() error) error { | ||||
| 	prevSelectedLineIdx := self.context.GetList().GetSelectedLineIdx() | ||||
| 	// because we're handling a click, we need to determine the new line idx based | ||||
| 	// on the view itself. | ||||
| 	newSelectedLineIdx := self.context.GetViewTrait().SelectedLineIdx() | ||||
|  | ||||
| 	currentContextKey := self.c.CurrentContext().GetKey() | ||||
| 	alreadyFocused := currentContextKey == self.context.GetKey() | ||||
|  | ||||
| 	// we need to focus the view | ||||
| 	if !alreadyFocused { | ||||
| 		if err := self.c.PushContext(self.context); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if newSelectedLineIdx > self.context.GetList().GetItemsLength()-1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	self.context.GetList().SetSelectedLineIdx(newSelectedLineIdx) | ||||
|  | ||||
| 	if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && onClick != nil { | ||||
| 		return onClick() | ||||
| 	} | ||||
| 	return self.context.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { | ||||
| 	return []*types.Binding{ | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.c.Tr.LcPrevPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.c.Tr.LcNextPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.c.Tr.LcGotoTop}, | ||||
| 		{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.StartSearch), | ||||
| 			Handler:     func() error { self.c.OpenSearch(); return nil }, | ||||
| 			Description: self.c.Tr.LcStartSearch, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.GotoBottom), | ||||
| 			Description: self.c.Tr.LcGotoBottom, | ||||
| 			Handler:     self.HandleGotoBottom, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/hosting_service" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
| @@ -24,7 +25,7 @@ type ( | ||||
| type LocalCommitsController struct { | ||||
| 	baseController | ||||
| 	c                *types.ControllerCommon | ||||
| 	context          types.IListContext | ||||
| 	context          *context.LocalCommitsContext | ||||
| 	os               *oscommands.OSCommand | ||||
| 	git              *commands.GitCommand | ||||
| 	tagsHelper       *TagsHelper | ||||
| @@ -32,9 +33,7 @@ type LocalCommitsController struct { | ||||
| 	cherryPickHelper *CherryPickHelper | ||||
| 	rebaseHelper     *RebaseHelper | ||||
|  | ||||
| 	getSelectedLocalCommit     func() *models.Commit | ||||
| 	model                      *types.Model | ||||
| 	getSelectedLocalCommitIdx  func() int | ||||
| 	CheckMergeOrRebase         CheckMergeOrRebase | ||||
| 	pullFiles                  PullFilesFn | ||||
| 	getHostingServiceMgr       GetHostingServiceMgrFn | ||||
| @@ -49,16 +48,14 @@ var _ types.IController = &LocalCommitsController{} | ||||
|  | ||||
| func NewLocalCommitsController( | ||||
| 	c *types.ControllerCommon, | ||||
| 	context types.IListContext, | ||||
| 	context *context.LocalCommitsContext, | ||||
| 	os *oscommands.OSCommand, | ||||
| 	git *commands.GitCommand, | ||||
| 	tagsHelper *TagsHelper, | ||||
| 	refsHelper IRefsHelper, | ||||
| 	cherryPickHelper *CherryPickHelper, | ||||
| 	rebaseHelper *RebaseHelper, | ||||
| 	getSelectedLocalCommit func() *models.Commit, | ||||
| 	model *types.Model, | ||||
| 	getSelectedLocalCommitIdx func() int, | ||||
| 	CheckMergeOrRebase CheckMergeOrRebase, | ||||
| 	pullFiles PullFilesFn, | ||||
| 	getHostingServiceMgr GetHostingServiceMgrFn, | ||||
| @@ -78,9 +75,7 @@ func NewLocalCommitsController( | ||||
| 		refsHelper:                 refsHelper, | ||||
| 		cherryPickHelper:           cherryPickHelper, | ||||
| 		rebaseHelper:               rebaseHelper, | ||||
| 		getSelectedLocalCommit:     getSelectedLocalCommit, | ||||
| 		model:                      model, | ||||
| 		getSelectedLocalCommitIdx:  getSelectedLocalCommitIdx, | ||||
| 		CheckMergeOrRebase:         CheckMergeOrRebase, | ||||
| 		pullFiles:                  pullFiles, | ||||
| 		getHostingServiceMgr:       getHostingServiceMgr, | ||||
| @@ -194,10 +189,10 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ | ||||
| 			Description: self.c.Tr.LcGotoBottom, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:     gocui.MouseLeft, | ||||
| 			Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		}, | ||||
| 		// { | ||||
| 		// 	Key:     gocui.MouseLeft, | ||||
| 		// 	Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		// }, | ||||
| 	} | ||||
|  | ||||
| 	for _, binding := range outsideFilterModeBindings { | ||||
| @@ -316,7 +311,7 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error { | ||||
| 		InitialContent: message, | ||||
| 		HandleConfirm: func(response string) error { | ||||
| 			self.c.LogAction(self.c.Tr.Actions.RewordCommit) | ||||
| 			if err := self.git.Rebase.RewordCommit(self.model.Commits, self.getSelectedLocalCommitIdx(), response); err != nil { | ||||
| 			if err := self.git.Rebase.RewordCommit(self.model.Commits, self.context.GetSelectedLineIdx(), response); err != nil { | ||||
| 				return self.c.Error(err) | ||||
| 			} | ||||
|  | ||||
| @@ -336,7 +331,7 @@ func (self *LocalCommitsController) rewordEditor() error { | ||||
|  | ||||
| 	self.c.LogAction(self.c.Tr.Actions.RewordCommit) | ||||
| 	subProcess, err := self.git.Rebase.RewordCommitInEditor( | ||||
| 		self.model.Commits, self.getSelectedLocalCommitIdx(), | ||||
| 		self.model.Commits, self.context.GetSelectedLineIdx(), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return self.c.Error(err) | ||||
| @@ -399,7 +394,7 @@ func (self *LocalCommitsController) pick() error { | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsController) interactiveRebase(action string) error { | ||||
| 	err := self.git.Rebase.InteractiveRebase(self.model.Commits, self.getSelectedLocalCommitIdx(), action) | ||||
| 	err := self.git.Rebase.InteractiveRebase(self.model.Commits, self.context.GetSelectedLineIdx(), action) | ||||
| 	return self.CheckMergeOrRebase(err) | ||||
| } | ||||
|  | ||||
| @@ -407,7 +402,7 @@ func (self *LocalCommitsController) interactiveRebase(action string) error { | ||||
| // commit meaning you are trying to edit the todo file rather than actually | ||||
| // begin a rebase. It then updates the todo file with that action | ||||
| func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool, error) { | ||||
| 	selectedCommit := self.getSelectedLocalCommit() | ||||
| 	selectedCommit := self.context.GetSelected() | ||||
| 	if selectedCommit.Status != "rebasing" { | ||||
| 		return false, nil | ||||
| 	} | ||||
| @@ -427,7 +422,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool, | ||||
| 	) | ||||
|  | ||||
| 	if err := self.git.Rebase.EditRebaseTodo( | ||||
| 		self.getSelectedLocalCommitIdx(), action, | ||||
| 		self.context.GetSelectedLineIdx(), action, | ||||
| 	); err != nil { | ||||
| 		return false, self.c.Error(err) | ||||
| 	} | ||||
| @@ -438,7 +433,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool, | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsController) handleCommitMoveDown() error { | ||||
| 	index := self.context.GetPanelState().GetSelectedLineIdx() | ||||
| 	index := self.context.GetSelectedLineIdx() | ||||
| 	commits := self.model.Commits | ||||
| 	selectedCommit := self.model.Commits[index] | ||||
| 	if selectedCommit.Status == "rebasing" { | ||||
| @@ -454,8 +449,7 @@ func (self *LocalCommitsController) handleCommitMoveDown() error { | ||||
| 		if err := self.git.Rebase.MoveTodoDown(index); err != nil { | ||||
| 			return self.c.Error(err) | ||||
| 		} | ||||
| 		// TODO: use MoveSelectedLine | ||||
| 		_ = self.context.HandleNextLine() | ||||
| 		self.context.MoveSelectedLine(1) | ||||
| 		return self.c.Refresh(types.RefreshOptions{ | ||||
| 			Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS}, | ||||
| 		}) | ||||
| @@ -465,8 +459,7 @@ func (self *LocalCommitsController) handleCommitMoveDown() error { | ||||
| 		self.c.LogAction(self.c.Tr.Actions.MoveCommitDown) | ||||
| 		err := self.git.Rebase.MoveCommitDown(self.model.Commits, index) | ||||
| 		if err == nil { | ||||
| 			// TODO: use MoveSelectedLine | ||||
| 			_ = self.context.HandleNextLine() | ||||
| 			self.context.MoveSelectedLine(1) | ||||
| 		} | ||||
| 		return self.CheckMergeOrRebase(err) | ||||
| 	}) | ||||
| @@ -491,7 +484,7 @@ func (self *LocalCommitsController) handleCommitMoveUp() error { | ||||
| 		if err := self.git.Rebase.MoveTodoDown(index - 1); err != nil { | ||||
| 			return self.c.Error(err) | ||||
| 		} | ||||
| 		_ = self.context.HandlePrevLine() | ||||
| 		self.context.MoveSelectedLine(-1) | ||||
| 		return self.c.Refresh(types.RefreshOptions{ | ||||
| 			Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS}, | ||||
| 		}) | ||||
| @@ -501,7 +494,7 @@ func (self *LocalCommitsController) handleCommitMoveUp() error { | ||||
| 		self.c.LogAction(self.c.Tr.Actions.MoveCommitUp) | ||||
| 		err := self.git.Rebase.MoveCommitDown(self.model.Commits, index-1) | ||||
| 		if err == nil { | ||||
| 			_ = self.context.HandlePrevLine() | ||||
| 			self.context.MoveSelectedLine(-1) | ||||
| 		} | ||||
| 		return self.CheckMergeOrRebase(err) | ||||
| 	}) | ||||
| @@ -514,7 +507,7 @@ func (self *LocalCommitsController) handleCommitAmendTo() error { | ||||
| 		HandleConfirm: func() error { | ||||
| 			return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error { | ||||
| 				self.c.LogAction(self.c.Tr.Actions.AmendCommit) | ||||
| 				err := self.git.Rebase.AmendTo(self.getSelectedLocalCommit().Sha) | ||||
| 				err := self.git.Rebase.AmendTo(self.context.GetSelected().Sha) | ||||
| 				return self.CheckMergeOrRebase(err) | ||||
| 			}) | ||||
| 		}, | ||||
| @@ -569,7 +562,7 @@ func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.C | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsController) afterRevertCommit() error { | ||||
| 	_ = self.context.HandleNextLine() | ||||
| 	self.context.MoveSelectedLine(1) | ||||
| 	return self.c.Refresh(types.RefreshOptions{ | ||||
| 		Mode: types.BLOCK_UI, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES}, | ||||
| 	}) | ||||
| @@ -669,7 +662,7 @@ func (self *LocalCommitsController) gotoBottom() error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	_ = self.context.HandleGotoBottom() | ||||
| 	self.context.SetSelectedLineIdx(self.context.GetItemsLength() - 1) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -791,7 +784,7 @@ func (self *LocalCommitsController) handleOpenCommitInBrowser(commit *models.Com | ||||
|  | ||||
| func (self *LocalCommitsController) checkSelected(callback func(*models.Commit) error) func() error { | ||||
| 	return func() error { | ||||
| 		commit := self.getSelectedLocalCommit() | ||||
| 		commit := self.context.GetSelected() | ||||
| 		if commit == nil { | ||||
| 			return nil | ||||
| 		} | ||||
| @@ -813,7 +806,7 @@ func (self *LocalCommitsController) copy(commit *models.Commit) error { | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsController) copyRange(*models.Commit) error { | ||||
| 	return self.cherryPickHelper.CopyRange(self.context.GetPanelState().GetSelectedLineIdx(), self.model.Commits, self.context) | ||||
| 	return self.cherryPickHelper.CopyRange(self.context.GetSelectedLineIdx(), self.model.Commits, self.context) | ||||
| } | ||||
|  | ||||
| func (self *LocalCommitsController) paste() error { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| @@ -9,24 +9,20 @@ type MenuController struct { | ||||
| 	baseController | ||||
|  | ||||
| 	c       *types.ControllerCommon | ||||
| 	context types.IListContext | ||||
|  | ||||
| 	getSelectedMenuItem func() *types.MenuItem | ||||
| 	context *context.MenuContext | ||||
| } | ||||
|  | ||||
| var _ types.IController = &MenuController{} | ||||
|  | ||||
| func NewMenuController( | ||||
| 	c *types.ControllerCommon, | ||||
| 	context types.IListContext, | ||||
| 	getSelectedMenuItem func() *types.MenuItem, | ||||
| 	context *context.MenuContext, | ||||
| ) *MenuController { | ||||
| 	return &MenuController{ | ||||
| 		baseController: baseController{}, | ||||
|  | ||||
| 		c:                   c, | ||||
| 		context:             context, | ||||
| 		getSelectedMenuItem: getSelectedMenuItem, | ||||
| 		c:       c, | ||||
| 		context: context, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -44,17 +40,17 @@ func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types. | ||||
| 			Key:     opts.GetKey(opts.Config.Universal.ConfirmAlt1), | ||||
| 			Handler: self.press, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:     gocui.MouseLeft, | ||||
| 			Handler: func() error { return self.context.HandleClick(self.press) }, | ||||
| 		}, | ||||
| 		// { | ||||
| 		// 	Key:     gocui.MouseLeft, | ||||
| 		// 	Handler: func() error { return self.context.HandleClick(self.press) }, | ||||
| 		// }, | ||||
| 	} | ||||
|  | ||||
| 	return bindings | ||||
| } | ||||
|  | ||||
| func (self *MenuController) press() error { | ||||
| 	selectedItem := self.getSelectedMenuItem() | ||||
| 	selectedItem := self.context.GetSelected() | ||||
|  | ||||
| 	if err := self.c.PopContext(); err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| @@ -13,10 +12,9 @@ type RemotesController struct { | ||||
| 	baseController | ||||
|  | ||||
| 	c       *types.ControllerCommon | ||||
| 	context types.IListContext | ||||
| 	context *context.RemotesContext | ||||
| 	git     *commands.GitCommand | ||||
|  | ||||
| 	getSelectedRemote func() *models.Remote | ||||
| 	setRemoteBranches func([]*models.RemoteBranch) | ||||
| 	contexts          *context.ContextTree | ||||
| } | ||||
| @@ -25,10 +23,9 @@ var _ types.IController = &RemotesController{} | ||||
|  | ||||
| func NewRemotesController( | ||||
| 	c *types.ControllerCommon, | ||||
| 	context types.IListContext, | ||||
| 	context *context.RemotesContext, | ||||
| 	git *commands.GitCommand, | ||||
| 	contexts *context.ContextTree, | ||||
| 	getSelectedRemote func() *models.Remote, | ||||
| 	setRemoteBranches func([]*models.RemoteBranch), | ||||
| ) *RemotesController { | ||||
| 	return &RemotesController{ | ||||
| @@ -37,7 +34,6 @@ func NewRemotesController( | ||||
| 		git:               git, | ||||
| 		contexts:          contexts, | ||||
| 		context:           context, | ||||
| 		getSelectedRemote: getSelectedRemote, | ||||
| 		setRemoteBranches: setRemoteBranches, | ||||
| 	} | ||||
| } | ||||
| @@ -48,10 +44,10 @@ func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*typ | ||||
| 			Key:     opts.GetKey(opts.Config.Universal.GoInto), | ||||
| 			Handler: self.checkSelected(self.enter), | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:     gocui.MouseLeft, | ||||
| 			Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		}, | ||||
| 		// { | ||||
| 		// 	Key:     gocui.MouseLeft, | ||||
| 		// 	Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		// }, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Branches.FetchRemote), | ||||
| 			Handler:     self.checkSelected(self.fetch), | ||||
| @@ -183,7 +179,7 @@ func (self *RemotesController) fetch(remote *models.Remote) error { | ||||
|  | ||||
| func (self *RemotesController) checkSelected(callback func(*models.Remote) error) func() error { | ||||
| 	return func() error { | ||||
| 		file := self.getSelectedRemote() | ||||
| 		file := self.context.GetSelected() | ||||
| 		if file == nil { | ||||
| 			return nil | ||||
| 		} | ||||
|   | ||||
| @@ -5,9 +5,9 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
| @@ -16,29 +16,26 @@ type SubmodulesController struct { | ||||
| 	baseController | ||||
|  | ||||
| 	c       *types.ControllerCommon | ||||
| 	context types.IListContext | ||||
| 	context *context.SubmodulesContext | ||||
| 	git     *commands.GitCommand | ||||
|  | ||||
| 	enterSubmodule       func(submodule *models.SubmoduleConfig) error | ||||
| 	getSelectedSubmodule func() *models.SubmoduleConfig | ||||
| 	enterSubmodule func(submodule *models.SubmoduleConfig) error | ||||
| } | ||||
|  | ||||
| var _ types.IController = &SubmodulesController{} | ||||
|  | ||||
| func NewSubmodulesController( | ||||
| 	c *types.ControllerCommon, | ||||
| 	context types.IListContext, | ||||
| 	context *context.SubmodulesContext, | ||||
| 	git *commands.GitCommand, | ||||
| 	enterSubmodule func(submodule *models.SubmoduleConfig) error, | ||||
| 	getSelectedSubmodule func() *models.SubmoduleConfig, | ||||
| ) *SubmodulesController { | ||||
| 	return &SubmodulesController{ | ||||
| 		baseController:       baseController{}, | ||||
| 		c:                    c, | ||||
| 		context:              context, | ||||
| 		git:                  git, | ||||
| 		enterSubmodule:       enterSubmodule, | ||||
| 		getSelectedSubmodule: getSelectedSubmodule, | ||||
| 		baseController: baseController{}, | ||||
| 		c:              c, | ||||
| 		context:        context, | ||||
| 		git:            git, | ||||
| 		enterSubmodule: enterSubmodule, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -80,10 +77,10 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []* | ||||
| 			Description: self.c.Tr.LcViewBulkSubmoduleOptions, | ||||
| 			OpensMenu:   true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:     gocui.MouseLeft, | ||||
| 			Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		}, | ||||
| 		// { | ||||
| 		// 	Key:     gocui.MouseLeft, | ||||
| 		// 	Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, | ||||
| 		// }, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -230,7 +227,7 @@ func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) erro | ||||
|  | ||||
| func (self *SubmodulesController) checkSelected(callback func(*models.SubmoduleConfig) error) func() error { | ||||
| 	return func() error { | ||||
| 		submodule := self.getSelectedSubmodule() | ||||
| 		submodule := self.context.GetSelected() | ||||
| 		if submodule == nil { | ||||
| 			return nil | ||||
| 		} | ||||
|   | ||||
| @@ -158,7 +158,7 @@ func (self *TagsController) create() error { | ||||
|  | ||||
| func (self *TagsController) withSelectedTag(f func(tag *models.Tag) error) func() error { | ||||
| 	return func() error { | ||||
| 		tag := self.context.GetSelectedTag() | ||||
| 		tag := self.context.GetSelected() | ||||
| 		if tag == nil { | ||||
| 			return nil | ||||
| 		} | ||||
|   | ||||
| @@ -44,16 +44,16 @@ func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (s | ||||
| 	objects := CustomCommandObjects{ | ||||
| 		SelectedFile:           gui.getSelectedFile(), | ||||
| 		SelectedPath:           gui.getSelectedPath(), | ||||
| 		SelectedLocalCommit:    gui.getSelectedLocalCommit(), | ||||
| 		SelectedReflogCommit:   gui.getSelectedReflogCommit(), | ||||
| 		SelectedLocalBranch:    gui.getSelectedBranch(), | ||||
| 		SelectedRemoteBranch:   gui.getSelectedRemoteBranch(), | ||||
| 		SelectedRemote:         gui.getSelectedRemote(), | ||||
| 		SelectedTag:            gui.State.Contexts.Tags.GetSelectedTag(), | ||||
| 		SelectedStashEntry:     gui.getSelectedStashEntry(), | ||||
| 		SelectedLocalCommit:    gui.State.Contexts.BranchCommits.GetSelected(), | ||||
| 		SelectedReflogCommit:   gui.State.Contexts.ReflogCommits.GetSelected(), | ||||
| 		SelectedLocalBranch:    gui.State.Contexts.Branches.GetSelected(), | ||||
| 		SelectedRemoteBranch:   gui.State.Contexts.RemoteBranches.GetSelected(), | ||||
| 		SelectedRemote:         gui.State.Contexts.Remotes.GetSelected(), | ||||
| 		SelectedTag:            gui.State.Contexts.Tags.GetSelected(), | ||||
| 		SelectedStashEntry:     gui.State.Contexts.Stash.GetSelected(), | ||||
| 		SelectedCommitFile:     gui.getSelectedCommitFile(), | ||||
| 		SelectedCommitFilePath: gui.getSelectedCommitFilePath(), | ||||
| 		SelectedSubCommit:      gui.getSelectedSubCommit(), | ||||
| 		SelectedSubCommit:      gui.State.Contexts.SubCommits.GetSelected(), | ||||
| 		CheckedOutBranch:       gui.getCheckedOutBranch(), | ||||
| 		PromptResponses:        promptResponses, | ||||
| 	} | ||||
|   | ||||
| @@ -43,7 +43,7 @@ func (gui *Gui) currentDiffTerminals() []string { | ||||
| 		return []string{gui.State.Contexts.CommitFiles.GetRefName()} | ||||
| 	case context.LOCAL_BRANCHES_CONTEXT_KEY: | ||||
| 		// for our local branches we want to include both the branch and its upstream | ||||
| 		branch := gui.getSelectedBranch() | ||||
| 		branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 		if branch != nil { | ||||
| 			names := []string{branch.ID()} | ||||
| 			if branch.IsTrackingRemote() { | ||||
|   | ||||
| @@ -16,7 +16,7 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error { | ||||
| 			fileName = node.GetPath() | ||||
| 		} | ||||
| 	case gui.State.Contexts.CommitFiles: | ||||
| 		node := gui.getSelectedCommitFileNode() | ||||
| 		node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 		if node != nil { | ||||
| 			fileName = node.GetPath() | ||||
| 		} | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| func (gui *Gui) handleCreateGitFlowMenu() error { | ||||
| 	branch := gui.getSelectedBranch() | ||||
| 	branch := gui.State.Contexts.Branches.GetSelected() | ||||
| 	if branch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -179,11 +179,11 @@ type GuiRepoState struct { | ||||
|  | ||||
| 	// Suggestions will sometimes appear when typing into a prompt | ||||
| 	Suggestions []*types.Suggestion | ||||
| 	MenuItems   []*types.MenuItem | ||||
|  | ||||
| 	Updating       bool | ||||
| 	Panels         *panelStates | ||||
| 	SplitMainPanel bool | ||||
| 	LimitCommits   bool | ||||
|  | ||||
| 	IsRefreshingFiles bool | ||||
| 	Searching         searchingState | ||||
| @@ -253,68 +253,11 @@ type MergingPanelState struct { | ||||
| 	UserVerticalScrolling bool | ||||
| } | ||||
|  | ||||
| // TODO: consider splitting this out into the window and the branches view | ||||
| type branchPanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type remotePanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type remoteBranchesState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type commitPanelState struct { | ||||
| 	listPanelState | ||||
|  | ||||
| 	LimitCommits bool | ||||
| } | ||||
|  | ||||
| type reflogCommitPanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type subCommitPanelState struct { | ||||
| 	listPanelState | ||||
|  | ||||
| 	// e.g. name of branch whose commits we're looking at | ||||
| 	refName string | ||||
| } | ||||
|  | ||||
| type stashPanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type menuPanelState struct { | ||||
| 	listPanelState | ||||
| 	OnPress func() error | ||||
| } | ||||
|  | ||||
| type submodulePanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| type suggestionsPanelState struct { | ||||
| 	listPanelState | ||||
| } | ||||
|  | ||||
| // as we move things to the new context approach we're going to eventually | ||||
| // remove this struct altogether and store this state on the contexts. | ||||
| type panelStates struct { | ||||
| 	Branches       *branchPanelState | ||||
| 	Remotes        *remotePanelState | ||||
| 	RemoteBranches *remoteBranchesState | ||||
| 	Commits        *commitPanelState | ||||
| 	ReflogCommits  *reflogCommitPanelState | ||||
| 	SubCommits     *subCommitPanelState | ||||
| 	Stash          *stashPanelState | ||||
| 	Menu           *menuPanelState | ||||
| 	LineByLine     *LblPanelState | ||||
| 	Merging        *MergingPanelState | ||||
| 	Submodules     *submodulePanelState | ||||
| 	Suggestions    *suggestionsPanelState | ||||
| 	LineByLine *LblPanelState | ||||
| 	Merging    *MergingPanelState | ||||
| } | ||||
|  | ||||
| type Views struct { | ||||
| @@ -449,23 +392,13 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) { | ||||
| 		}, | ||||
|  | ||||
| 		Panels: &panelStates{ | ||||
| 			// TODO: work out why some of these are -1 and some are 0. Last time I checked there was a good reason but I'm less certain now | ||||
| 			Submodules:     &submodulePanelState{listPanelState{SelectedLineIdx: -1}}, | ||||
| 			Branches:       &branchPanelState{listPanelState{SelectedLineIdx: 0}}, | ||||
| 			Remotes:        &remotePanelState{listPanelState{SelectedLineIdx: 0}}, | ||||
| 			RemoteBranches: &remoteBranchesState{listPanelState{SelectedLineIdx: -1}}, | ||||
| 			Commits:        &commitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, LimitCommits: true}, | ||||
| 			ReflogCommits:  &reflogCommitPanelState{listPanelState{SelectedLineIdx: 0}}, | ||||
| 			SubCommits:     &subCommitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, refName: ""}, | ||||
| 			Stash:          &stashPanelState{listPanelState{SelectedLineIdx: -1}}, | ||||
| 			Menu:           &menuPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, OnPress: nil}, | ||||
| 			Suggestions:    &suggestionsPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}}, | ||||
| 			Merging: &MergingPanelState{ | ||||
| 				State:                 mergeconflicts.NewState(), | ||||
| 				UserVerticalScrolling: false, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Ptmx: nil, | ||||
| 		LimitCommits: true, | ||||
| 		Ptmx:         nil, | ||||
| 		Modes: Modes{ | ||||
| 			Filtering:     filtering.New(filterPath), | ||||
| 			CherryPicking: cherrypicking.New(), | ||||
| @@ -584,7 +517,7 @@ func (gui *Gui) resetControllers() { | ||||
| 			controllerCommon, | ||||
| 			gui.git, | ||||
| 			gui.State.Contexts, | ||||
| 			func() { gui.State.Panels.Commits.LimitCommits = true }, | ||||
| 			func() { gui.State.LimitCommits = true }, | ||||
| 		), | ||||
| 		Bisect:      controllers.NewBisectHelper(controllerCommon, gui.git), | ||||
| 		Suggestions: controllers.NewSuggestionsHelper(controllerCommon, model, gui.refreshSuggestions), | ||||
| @@ -615,7 +548,6 @@ func (gui *Gui) resetControllers() { | ||||
| 		gui.State.Contexts.Submodules, | ||||
| 		gui.git, | ||||
| 		gui.enterSubmodule, | ||||
| 		gui.getSelectedSubmodule, | ||||
| 	) | ||||
|  | ||||
| 	bisectController := controllers.NewBisectController( | ||||
| @@ -623,7 +555,6 @@ func (gui *Gui) resetControllers() { | ||||
| 		gui.State.Contexts.BranchCommits, | ||||
| 		gui.git, | ||||
| 		gui.helpers.Bisect, | ||||
| 		gui.getSelectedLocalCommit, | ||||
| 		func() []*models.Commit { return gui.State.Model.Commits }, | ||||
| 	) | ||||
|  | ||||
| @@ -672,15 +603,13 @@ func (gui *Gui) resetControllers() { | ||||
| 			gui.helpers.Refs, | ||||
| 			gui.helpers.CherryPick, | ||||
| 			gui.helpers.Rebase, | ||||
| 			gui.getSelectedLocalCommit, | ||||
| 			model, | ||||
| 			func() int { return gui.State.Panels.Commits.SelectedLineIdx }, | ||||
| 			gui.helpers.Rebase.CheckMergeOrRebase, | ||||
| 			syncController.HandlePull, | ||||
| 			gui.getHostingServiceMgr, | ||||
| 			gui.SwitchToCommitFilesContext, | ||||
| 			func() bool { return gui.State.Panels.Commits.LimitCommits }, | ||||
| 			func(value bool) { gui.State.Panels.Commits.LimitCommits = value }, | ||||
| 			func() bool { return gui.State.LimitCommits }, | ||||
| 			func(value bool) { gui.State.LimitCommits = value }, | ||||
| 			func() bool { return gui.ShowWholeGitGraph }, | ||||
| 			func(value bool) { gui.ShowWholeGitGraph = value }, | ||||
| 		), | ||||
| @@ -689,13 +618,11 @@ func (gui *Gui) resetControllers() { | ||||
| 			gui.State.Contexts.Remotes, | ||||
| 			gui.git, | ||||
| 			gui.State.Contexts, | ||||
| 			gui.getSelectedRemote, | ||||
| 			func(branches []*models.RemoteBranch) { gui.State.Model.RemoteBranches = branches }, | ||||
| 		), | ||||
| 		Menu: controllers.NewMenuController( | ||||
| 			controllerCommon, | ||||
| 			gui.State.Contexts.Menu, | ||||
| 			gui.getSelectedMenuItem, | ||||
| 		), | ||||
| 		Undo: controllers.NewUndoController( | ||||
| 			controllerCommon, | ||||
| @@ -714,6 +641,11 @@ func (gui *Gui) resetControllers() { | ||||
| 	controllers.AttachControllers(gui.State.Contexts.Remotes, gui.Controllers.Remotes) | ||||
| 	controllers.AttachControllers(gui.State.Contexts.Menu, gui.Controllers.Menu) | ||||
| 	controllers.AttachControllers(gui.State.Contexts.Global, gui.Controllers.Sync, gui.Controllers.Undo, gui.Controllers.Global) | ||||
|  | ||||
| 	listControllerFactory := controllers.NewListControllerFactory(gui.c) | ||||
| 	for _, context := range gui.getListContexts() { | ||||
| 		controllers.AttachControllers(context, listControllerFactory.Create(context)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var RuneReplacements = map[rune]string{ | ||||
|   | ||||
| @@ -1,267 +0,0 @@ | ||||
| package gui | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| type ListContext struct { | ||||
| 	GetItemsLength    func() int | ||||
| 	GetDisplayStrings func(startIdx int, length int) [][]string | ||||
| 	OnFocus           func(...types.OnFocusOpts) error | ||||
| 	OnRenderToMain    func(...types.OnFocusOpts) error | ||||
| 	OnFocusLost       func() error | ||||
|  | ||||
| 	OnGetSelectedItemId func() string | ||||
| 	OnGetPanelState     func() types.IListPanelState | ||||
| 	// if this is true, we'll call GetDisplayStrings for just the visible part of the | ||||
| 	// view and re-render that. This is useful when you need to render different | ||||
| 	// content based on the selection (e.g. for showing the selected commit) | ||||
| 	RenderSelection bool | ||||
|  | ||||
| 	Gui *Gui | ||||
|  | ||||
| 	*context.BaseContext | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = &ListContext{} | ||||
|  | ||||
| func (self *ListContext) GetPanelState() types.IListPanelState { | ||||
| 	return self.OnGetPanelState() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) FocusLine() { | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		// ignoring error for now | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// we need a way of knowing whether we've rendered to the view yet. | ||||
| 	view.FocusPoint(view.OriginX(), self.GetPanelState().GetSelectedLineIdx()) | ||||
| 	if self.RenderSelection { | ||||
| 		_, originY := view.Origin() | ||||
| 		displayStrings := self.GetDisplayStrings(originY, view.InnerHeight()+1) | ||||
| 		self.Gui.renderDisplayStringsInViewPort(view, displayStrings) | ||||
| 	} | ||||
| 	view.Footer = formatListFooter(self.GetPanelState().GetSelectedLineIdx(), self.GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func formatListFooter(selectedLineIdx int, length int) string { | ||||
| 	return fmt.Sprintf("%d of %d", selectedLineIdx+1, length) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) GetSelectedItemId() string { | ||||
| 	return self.OnGetSelectedItemId() | ||||
| } | ||||
|  | ||||
| // OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view | ||||
| func (self *ListContext) HandleRender() error { | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if self.GetDisplayStrings != nil { | ||||
| 		self.Gui.refreshSelectedLine(self.GetPanelState(), self.GetItemsLength()) | ||||
| 		self.Gui.renderDisplayStrings(view, self.GetDisplayStrings(0, self.GetItemsLength())) | ||||
| 		self.Gui.render() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleFocusLost() error { | ||||
| 	if self.OnFocusLost != nil { | ||||
| 		return self.OnFocusLost() | ||||
| 	} | ||||
|  | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	_ = view.SetOriginX(0) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleFocus(opts ...types.OnFocusOpts) error { | ||||
| 	self.FocusLine() | ||||
|  | ||||
| 	if self.OnFocus != nil { | ||||
| 		if err := self.OnFocus(opts...); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if self.OnRenderToMain != nil { | ||||
| 		if err := self.OnRenderToMain(opts...); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandlePrevLine() error { | ||||
| 	return self.handleLineChange(-1) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleNextLine() error { | ||||
| 	return self.handleLineChange(1) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleScrollLeft() error { | ||||
| 	return self.scroll(self.Gui.scrollLeft) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleScrollRight() error { | ||||
| 	return self.scroll(self.Gui.scrollRight) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) scroll(scrollFunc func(*gocui.View)) error { | ||||
| 	if self.ignoreKeybinding() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// get the view, move the origin | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	scrollFunc(view) | ||||
|  | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) ignoreKeybinding() bool { | ||||
| 	return !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) handleLineChange(change int) error { | ||||
| 	if self.ignoreKeybinding() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	selectedLineIdx := self.GetPanelState().GetSelectedLineIdx() | ||||
| 	if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == self.GetItemsLength()-1) { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	self.Gui.changeSelectedLine(self.GetPanelState(), self.GetItemsLength(), change) | ||||
|  | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleNextPage() error { | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	delta := self.Gui.pageDelta(view) | ||||
|  | ||||
| 	return self.handleLineChange(delta) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleGotoTop() error { | ||||
| 	return self.handleLineChange(-self.GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleGotoBottom() error { | ||||
| 	return self.handleLineChange(self.GetItemsLength()) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandlePrevPage() error { | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	delta := self.Gui.pageDelta(view) | ||||
|  | ||||
| 	return self.handleLineChange(-delta) | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleClick(onClick func() error) error { | ||||
| 	if self.ignoreKeybinding() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	view, err := self.Gui.g.View(self.ViewName) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	prevSelectedLineIdx := self.GetPanelState().GetSelectedLineIdx() | ||||
| 	newSelectedLineIdx := view.SelectedLineIdx() | ||||
|  | ||||
| 	// we need to focus the view | ||||
| 	if err := self.Gui.c.PushContext(self); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if newSelectedLineIdx > self.GetItemsLength()-1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	self.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx) | ||||
|  | ||||
| 	prevViewName := self.Gui.currentViewName() | ||||
| 	if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == self.ViewName && onClick != nil { | ||||
| 		return onClick() | ||||
| 	} | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) OnSearchSelect(selectedLineIdx int) error { | ||||
| 	self.GetPanelState().SetSelectedLineIdx(selectedLineIdx) | ||||
| 	return self.HandleFocus() | ||||
| } | ||||
|  | ||||
| func (self *ListContext) HandleRenderToMain() error { | ||||
| 	if self.OnRenderToMain != nil { | ||||
| 		return self.OnRenderToMain() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *ListContext) attachKeybindings() *ListContext { | ||||
| 	self.BaseContext.AddKeybindingsFn(self.keybindings) | ||||
|  | ||||
| 	return self | ||||
| } | ||||
|  | ||||
| func (self *ListContext) keybindings(opts types.KeybindingsOpts) []*types.Binding { | ||||
| 	return []*types.Binding{ | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.Gui.c.Tr.LcPrevPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.Gui.c.Tr.LcNextPage}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.Gui.c.Tr.LcGotoTop}, | ||||
| 		{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }}, | ||||
| 		{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft}, | ||||
| 		{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.StartSearch), | ||||
| 			Handler:     func() error { return self.Gui.handleOpenSearch(self.GetViewName()) }, | ||||
| 			Description: self.Gui.c.Tr.LcStartSearch, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Universal.GotoBottom), | ||||
| 			Description: self.Gui.c.Tr.LcGotoBottom, | ||||
| 			Handler:     self.HandleGotoBottom, | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @@ -3,7 +3,6 @@ package gui | ||||
| import ( | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| @@ -12,27 +11,21 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| func (gui *Gui) menuListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:        "menu", | ||||
| 			Key:             "menu", | ||||
| 			Kind:            types.PERSISTENT_POPUP, | ||||
| 			OnGetOptionsMap: gui.getMenuOptions, | ||||
| 			Focusable:       true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return gui.Views.Menu.LinesHeight() }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Menu }, | ||||
| 		Gui:             gui, | ||||
|  | ||||
| 		// no GetDisplayStrings field because we do a custom render on menu creation | ||||
| 	}).attachKeybindings() | ||||
| func (gui *Gui) menuListContext() *context.MenuContext { | ||||
| 	return context.NewMenuContext( | ||||
| 		gui.Views.Menu, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 		gui.getMenuOptions, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) filesListContext() *context.WorkingTreeContext { | ||||
| 	return context.NewWorkingTreeContext( | ||||
| 		func() []*models.File { return gui.State.Model.Files }, | ||||
| 		func() *gocui.View { return gui.Views.Files }, | ||||
| 		gui.Views.Files, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			lines := presentation.RenderFileTree(gui.State.Contexts.Files.FileTreeViewModel, gui.State.Modes.Diffing.Ref, gui.State.Model.Submodules) | ||||
| 			mappedLines := make([][]string, len(lines)) | ||||
| @@ -49,82 +42,46 @@ func (gui *Gui) filesListContext() *context.WorkingTreeContext { | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) branchesListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "branches", | ||||
| 			WindowName: "branches", | ||||
| 			Key:        context.LOCAL_BRANCHES_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.Branches) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Branches }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) branchesListContext() *context.BranchesContext { | ||||
| 	return context.NewBranchesContext( | ||||
| 		func() []*models.Branch { return gui.State.Model.Branches }, | ||||
| 		gui.Views.Branches, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedBranch() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) remotesListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "branches", | ||||
| 			WindowName: "branches", | ||||
| 			Key:        context.REMOTES_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.Remotes) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Remotes }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) remotesListContext() *context.RemotesContext { | ||||
| 	return context.NewRemotesContext( | ||||
| 		func() []*models.Remote { return gui.State.Model.Remotes }, | ||||
| 		gui.Views.Branches, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetRemoteListDisplayStrings(gui.State.Model.Remotes, gui.State.Modes.Diffing.Ref) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedRemote() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) remoteBranchesListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "branches", | ||||
| 			WindowName: "branches", | ||||
| 			Key:        context.REMOTE_BRANCHES_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.RemoteBranches) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.RemoteBranches }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) remoteBranchesListContext() *context.RemoteBranchesContext { | ||||
| 	return context.NewRemoteBranchesContext( | ||||
| 		func() []*models.RemoteBranch { return gui.State.Model.RemoteBranches }, | ||||
| 		gui.Views.Branches, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetRemoteBranchListDisplayStrings(gui.State.Model.RemoteBranches, gui.State.Modes.Diffing.Ref) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedRemoteBranch() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) withDiffModeCheck(f func() error) func() error { | ||||
| @@ -140,7 +97,7 @@ func (gui *Gui) withDiffModeCheck(f func() error) func() error { | ||||
| func (gui *Gui) tagsListContext() *context.TagsContext { | ||||
| 	return context.NewTagsContext( | ||||
| 		func() []*models.Tag { return gui.State.Model.Tags }, | ||||
| 		func() *gocui.View { return gui.Views.Branches }, | ||||
| 		gui.Views.Branches, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetTagListDisplayStrings(gui.State.Model.Tags, gui.State.Modes.Diffing.Ref) | ||||
| 		}, | ||||
| @@ -151,25 +108,14 @@ func (gui *Gui) tagsListContext() *context.TagsContext { | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) branchCommitsListContext() types.IListContext { | ||||
| 	parseEmoji := gui.c.UserConfig.Git.ParseEmoji | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "commits", | ||||
| 			WindowName: "commits", | ||||
| 			Key:        context.BRANCH_COMMITS_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.Commits) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Commits }, | ||||
| 		OnFocus:         OnFocusWrapper(gui.onCommitFocus), | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) branchCommitsListContext() *context.LocalCommitsContext { | ||||
| 	return context.NewLocalCommitsContext( | ||||
| 		func() []*models.Commit { return gui.State.Model.Commits }, | ||||
| 		gui.Views.Commits, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			selectedCommitSha := "" | ||||
| 			if gui.currentContext().GetKey() == context.BRANCH_COMMITS_CONTEXT_KEY { | ||||
| 				selectedCommit := gui.getSelectedLocalCommit() | ||||
| 				selectedCommit := gui.State.Contexts.BranchCommits.GetSelected() | ||||
| 				if selectedCommit != nil { | ||||
| 					selectedCommitSha = selectedCommit.Sha | ||||
| 				} | ||||
| @@ -179,7 +125,7 @@ func (gui *Gui) branchCommitsListContext() types.IListContext { | ||||
| 				gui.State.ScreenMode != SCREEN_NORMAL, | ||||
| 				gui.helpers.CherryPick.CherryPickedCommitShaMap(), | ||||
| 				gui.State.Modes.Diffing.Ref, | ||||
| 				parseEmoji, | ||||
| 				gui.c.UserConfig.Git.ParseEmoji, | ||||
| 				selectedCommitSha, | ||||
| 				startIdx, | ||||
| 				length, | ||||
| @@ -187,35 +133,21 @@ func (gui *Gui) branchCommitsListContext() types.IListContext { | ||||
| 				gui.State.Model.BisectInfo, | ||||
| 			) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedLocalCommit() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 		RenderSelection: true, | ||||
| 	}).attachKeybindings() | ||||
| 		OnFocusWrapper(gui.onCommitFocus), | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) subCommitsListContext() types.IListContext { | ||||
| 	parseEmoji := gui.c.UserConfig.Git.ParseEmoji | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "branches", | ||||
| 			WindowName: "branches", | ||||
| 			Key:        context.SUB_COMMITS_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.SubCommits) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.SubCommits }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) subCommitsListContext() *context.SubCommitsContext { | ||||
| 	return context.NewSubCommitsContext( | ||||
| 		func() []*models.Commit { return gui.State.Model.SubCommits }, | ||||
| 		gui.Views.Branches, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			selectedCommitSha := "" | ||||
| 			if gui.currentContext().GetKey() == context.SUB_COMMITS_CONTEXT_KEY { | ||||
| 				selectedCommit := gui.getSelectedSubCommit() | ||||
| 				selectedCommit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 				if selectedCommit != nil { | ||||
| 					selectedCommitSha = selectedCommit.Sha | ||||
| 				} | ||||
| @@ -225,7 +157,7 @@ func (gui *Gui) subCommitsListContext() types.IListContext { | ||||
| 				gui.State.ScreenMode != SCREEN_NORMAL, | ||||
| 				gui.helpers.CherryPick.CherryPickedCommitShaMap(), | ||||
| 				gui.State.Modes.Diffing.Ref, | ||||
| 				parseEmoji, | ||||
| 				gui.c.UserConfig.Git.ParseEmoji, | ||||
| 				selectedCommitSha, | ||||
| 				startIdx, | ||||
| 				length, | ||||
| @@ -233,15 +165,11 @@ func (gui *Gui) subCommitsListContext() types.IListContext { | ||||
| 				git_commands.NewNullBisectInfo(), | ||||
| 			) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedSubCommit() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 		RenderSelection: true, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) shouldShowGraph() bool { | ||||
| @@ -263,69 +191,44 @@ func (gui *Gui) shouldShowGraph() bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (gui *Gui) reflogCommitsListContext() types.IListContext { | ||||
| 	parseEmoji := gui.c.UserConfig.Git.ParseEmoji | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "commits", | ||||
| 			WindowName: "commits", | ||||
| 			Key:        context.REFLOG_COMMITS_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.FilteredReflogCommits) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.ReflogCommits }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) reflogCommitsListContext() *context.ReflogCommitsContext { | ||||
| 	return context.NewReflogCommitsContext( | ||||
| 		func() []*models.Commit { return gui.State.Model.FilteredReflogCommits }, | ||||
| 		gui.Views.Commits, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetReflogCommitListDisplayStrings( | ||||
| 				gui.State.Model.FilteredReflogCommits, | ||||
| 				gui.State.ScreenMode != SCREEN_NORMAL, | ||||
| 				gui.helpers.CherryPick.CherryPickedCommitShaMap(), | ||||
| 				gui.State.Modes.Diffing.Ref, | ||||
| 				parseEmoji, | ||||
| 				gui.c.UserConfig.Git.ParseEmoji, | ||||
| 			) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedReflogCommit() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) stashListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "stash", | ||||
| 			WindowName: "stash", | ||||
| 			Key:        context.STASH_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.StashEntries) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Stash }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) stashListContext() *context.StashContext { | ||||
| 	return context.NewStashContext( | ||||
| 		func() []*models.StashEntry { return gui.State.Model.StashEntries }, | ||||
| 		gui.Views.Stash, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetStashEntryListDisplayStrings(gui.State.Model.StashEntries, gui.State.Modes.Diffing.Ref) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedStashEntry() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) commitFilesListContext() *context.CommitFilesContext { | ||||
| 	return context.NewCommitFilesContext( | ||||
| 		func() []*models.CommitFile { return gui.State.Model.CommitFiles }, | ||||
| 		func() *gocui.View { return gui.Views.CommitFiles }, | ||||
| 		gui.Views.CommitFiles, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			if gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetItemsLength() == 0 { | ||||
| 				return [][]string{{style.FgRed.Sprint("(none)")}} | ||||
| @@ -346,48 +249,32 @@ func (gui *Gui) commitFilesListContext() *context.CommitFilesContext { | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) submodulesListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "files", | ||||
| 			WindowName: "files", | ||||
| 			Key:        context.SUBMODULES_CONTEXT_KEY, | ||||
| 			Kind:       types.SIDE_CONTEXT, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Model.Submodules) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Submodules }, | ||||
| 		OnRenderToMain:  OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)), | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) submodulesListContext() *context.SubmodulesContext { | ||||
| 	return context.NewSubmodulesContext( | ||||
| 		func() []*models.SubmoduleConfig { return gui.State.Model.Submodules }, | ||||
| 		gui.Views.Files, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetSubmoduleListDisplayStrings(gui.State.Model.Submodules) | ||||
| 		}, | ||||
| 		OnGetSelectedItemId: func() string { | ||||
| 			item := gui.getSelectedSubmodule() | ||||
| 			if item == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return item.ID() | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)), | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) suggestionsListContext() types.IListContext { | ||||
| 	return (&ListContext{ | ||||
| 		BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ | ||||
| 			ViewName:   "suggestions", | ||||
| 			WindowName: "suggestions", | ||||
| 			Key:        context.SUGGESTIONS_CONTEXT_KEY, | ||||
| 			Kind:       types.PERSISTENT_POPUP, | ||||
| 			Focusable:  true, | ||||
| 		}), | ||||
| 		GetItemsLength:  func() int { return len(gui.State.Suggestions) }, | ||||
| 		OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Suggestions }, | ||||
| 		Gui:             gui, | ||||
| 		GetDisplayStrings: func(startIdx int, length int) [][]string { | ||||
| func (gui *Gui) suggestionsListContext() *context.SuggestionsContext { | ||||
| 	return context.NewSuggestionsContext( | ||||
| 		func() []*types.Suggestion { return gui.State.Suggestions }, | ||||
| 		gui.Views.Files, | ||||
| 		func(startIdx int, length int) [][]string { | ||||
| 			return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions) | ||||
| 		}, | ||||
| 	}).attachKeybindings() | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		gui.c, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getListContexts() []types.IListContext { | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import ( | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/theme" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| func (gui *Gui) getMenuOptions() map[string]string { | ||||
| @@ -35,44 +34,24 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	gui.State.MenuItems = opts.Items | ||||
|  | ||||
| 	stringArrays := make([][]string, len(opts.Items)) | ||||
| 	for i, item := range opts.Items { | ||||
| 	for _, item := range opts.Items { | ||||
| 		if item.OpensMenu && item.DisplayStrings != nil { | ||||
| 			return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user") | ||||
| 		} | ||||
|  | ||||
| 		if item.DisplayStrings == nil { | ||||
| 			styledStr := item.DisplayString | ||||
| 			if item.OpensMenu { | ||||
| 				styledStr = opensMenuStyle(styledStr) | ||||
| 			} | ||||
| 			stringArrays[i] = []string{styledStr} | ||||
| 		} else { | ||||
| 			stringArrays[i] = item.DisplayStrings | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	list := utils.RenderDisplayStrings(stringArrays) | ||||
|  | ||||
| 	x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(false, list) | ||||
| 	x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsForContentHeight(len(opts.Items)) | ||||
| 	menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0) | ||||
| 	menuView.Title = opts.Title | ||||
| 	menuView.FgColor = theme.GocuiDefaultTextColor | ||||
| 	menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error { | ||||
| 		return nil | ||||
| 	})) | ||||
| 	menuView.SetContent(list) | ||||
| 	gui.State.Panels.Menu.SelectedLineIdx = 0 | ||||
|  | ||||
| 	gui.State.Contexts.Menu.SetMenuItems(opts.Items) | ||||
| 	gui.State.Contexts.Menu.GetPanelState().SetSelectedLineIdx(0) | ||||
| 	_ = gui.c.PostRefreshUpdate(gui.State.Contexts.Menu) | ||||
|  | ||||
| 	// TODO: ensure that if we're opened a menu from within a menu that it renders correctly | ||||
| 	return gui.c.PushContext(gui.State.Contexts.Menu) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getSelectedMenuItem() *types.MenuItem { | ||||
| 	if len(gui.State.MenuItems) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.MenuItems[gui.State.Panels.Menu.SelectedLineIdx] | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package gui | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/presentation" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| @@ -34,16 +35,12 @@ func (gui *Gui) getBindings(context types.Context) []*types.Binding { | ||||
|  | ||||
| func (gui *Gui) displayDescription(binding *types.Binding) string { | ||||
| 	if binding.OpensMenu { | ||||
| 		return opensMenuStyle(binding.Description) | ||||
| 		return presentation.OpensMenuStyle(binding.Description) | ||||
| 	} | ||||
|  | ||||
| 	return style.FgCyan.Sprint(binding.Description) | ||||
| } | ||||
|  | ||||
| func opensMenuStyle(str string) string { | ||||
| 	return style.FgMagenta.Sprintf("%s...", str) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreateOptionsMenu() error { | ||||
| 	context := gui.currentContext() | ||||
| 	bindings := gui.getBindings(context) | ||||
|   | ||||
| @@ -26,7 +26,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error { | ||||
| 	gui.Views.Secondary.Title = "Custom Patch" | ||||
|  | ||||
| 	// get diff from commit file that's currently selected | ||||
| 	node := gui.getSelectedCommitFileNode() | ||||
| 	node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -87,7 +87,7 @@ func (gui *Gui) handleToggleSelectionForPatch() error { | ||||
| 		} | ||||
|  | ||||
| 		// add range of lines to those set for the file | ||||
| 		node := gui.getSelectedCommitFileNode() | ||||
| 		node := gui.State.Contexts.CommitFiles.GetSelectedFileNode() | ||||
| 		if node == nil { | ||||
| 			return nil | ||||
| 		} | ||||
|   | ||||
| @@ -118,7 +118,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error { | ||||
| 	return gui.c.WithWaitingStatus(gui.c.Tr.RebasingStatus, func() error { | ||||
| 		commitIndex := gui.getPatchCommitIndex() | ||||
| 		gui.c.LogAction(gui.c.Tr.Actions.MovePatchToSelectedCommit) | ||||
| 		err := gui.git.Patch.MovePatchToSelectedCommit(gui.State.Model.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx) | ||||
| 		err := gui.git.Patch.MovePatchToSelectedCommit(gui.State.Model.Commits, commitIndex, gui.State.Contexts.BranchCommits.GetSelectedLineIdx()) | ||||
| 		return gui.helpers.Rebase.CheckMergeOrRebase(err) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
							
								
								
									
										7
									
								
								pkg/gui/presentation/menu.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								pkg/gui/presentation/menu.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| package presentation | ||||
|  | ||||
| import "github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
|  | ||||
| func OpensMenuStyle(str string) string { | ||||
| 	return style.FgMagenta.Sprintf("%s...", str) | ||||
| } | ||||
| @@ -9,17 +9,11 @@ import ( | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedReflogCommit() *models.Commit { | ||||
| 	selectedLine := gui.State.Panels.ReflogCommits.SelectedLineIdx | ||||
| 	reflogComits := gui.State.Model.FilteredReflogCommits | ||||
| 	if selectedLine == -1 || len(reflogComits) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return reflogComits[selectedLine] | ||||
| 	return gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| } | ||||
|  | ||||
| func (gui *Gui) reflogCommitsRenderToMain() error { | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| 	var task updateTask | ||||
| 	if commit == nil { | ||||
| 		task = NewRenderStringTask("No reflog history") | ||||
| @@ -38,7 +32,7 @@ func (gui *Gui) reflogCommitsRenderToMain() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) CheckoutReflogCommit() error { | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -55,19 +49,17 @@ func (gui *Gui) CheckoutReflogCommit() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	gui.State.Panels.ReflogCommits.SelectedLineIdx = 0 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreateReflogResetMenu() error { | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
|  | ||||
| 	return gui.helpers.Refs.CreateGitResetMenu(commit.Sha) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleViewReflogCommitFiles() error { | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -81,7 +73,7 @@ func (gui *Gui) handleViewReflogCommitFiles() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCopyReflogCommit() error { | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -91,7 +83,7 @@ func (gui *Gui) handleCopyReflogCommit() error { | ||||
|  | ||||
| func (gui *Gui) handleCopyReflogCommitRange() error { | ||||
| 	// just doing this to ensure something is selected | ||||
| 	commit := gui.getSelectedReflogCommit() | ||||
| 	commit := gui.State.Contexts.ReflogCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -211,7 +211,7 @@ func (gui *Gui) refreshCommitsWithLimit() error { | ||||
|  | ||||
| 	commits, err := gui.git.Loaders.Commits.GetCommits( | ||||
| 		loaders.GetCommitsOptions{ | ||||
| 			Limit:                gui.State.Panels.Commits.LimitCommits, | ||||
| 			Limit:                gui.State.LimitCommits, | ||||
| 			FilterPath:           gui.State.Modes.Filtering.GetPath(), | ||||
| 			IncludeRebaseCommits: true, | ||||
| 			RefName:              gui.refForLog(), | ||||
| @@ -484,7 +484,7 @@ func (gui *Gui) refreshReflogCommits() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) refreshRemotes() error { | ||||
| 	prevSelectedRemote := gui.getSelectedRemote() | ||||
| 	prevSelectedRemote := gui.State.Contexts.Remotes.GetSelected() | ||||
|  | ||||
| 	remotes, err := gui.git.Loaders.Remotes.GetRemotes() | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -4,25 +4,15 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedRemoteBranch() *models.RemoteBranch { | ||||
| 	selectedLine := gui.State.Panels.RemoteBranches.SelectedLineIdx | ||||
| 	if selectedLine == -1 || len(gui.State.Model.RemoteBranches) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.RemoteBranches[selectedLine] | ||||
| } | ||||
|  | ||||
| func (gui *Gui) remoteBranchesRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	remoteBranch := gui.getSelectedRemoteBranch() | ||||
| 	remoteBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	if remoteBranch == nil { | ||||
| 		task = NewRenderStringTask("No branches for this remote") | ||||
| 	} else { | ||||
| @@ -43,12 +33,12 @@ func (gui *Gui) handleRemoteBranchesEscape() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleMergeRemoteBranch() error { | ||||
| 	selectedBranchName := gui.getSelectedRemoteBranch().FullName() | ||||
| 	selectedBranchName := gui.State.Contexts.RemoteBranches.GetSelected().FullName() | ||||
| 	return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleDeleteRemoteBranch() error { | ||||
| 	remoteBranch := gui.getSelectedRemoteBranch() | ||||
| 	remoteBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	if remoteBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -72,12 +62,12 @@ func (gui *Gui) handleDeleteRemoteBranch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleRebaseOntoRemoteBranch() error { | ||||
| 	selectedBranchName := gui.getSelectedRemoteBranch().FullName() | ||||
| 	selectedBranchName := gui.State.Contexts.RemoteBranches.GetSelected().FullName() | ||||
| 	return gui.handleRebaseOntoBranch(selectedBranchName) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleSetBranchUpstream() error { | ||||
| 	selectedBranch := gui.getSelectedRemoteBranch() | ||||
| 	selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	checkedOutBranch := gui.getCheckedOutBranch() | ||||
|  | ||||
| 	message := utils.ResolvePlaceholderString( | ||||
| @@ -103,7 +93,7 @@ func (gui *Gui) handleSetBranchUpstream() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreateResetToRemoteBranchMenu() error { | ||||
| 	selectedBranch := gui.getSelectedRemoteBranch() | ||||
| 	selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -112,7 +102,7 @@ func (gui *Gui) handleCreateResetToRemoteBranchMenu() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleEnterRemoteBranch() error { | ||||
| 	selectedBranch := gui.getSelectedRemoteBranch() | ||||
| 	selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -121,7 +111,7 @@ func (gui *Gui) handleEnterRemoteBranch() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleNewBranchOffRemoteBranch() error { | ||||
| 	selectedBranch := gui.getSelectedRemoteBranch() | ||||
| 	selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected() | ||||
| 	if selectedBranch == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -4,24 +4,14 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
| ) | ||||
|  | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedRemote() *models.Remote { | ||||
| 	selectedLine := gui.State.Panels.Remotes.SelectedLineIdx | ||||
| 	if selectedLine == -1 || len(gui.State.Model.Remotes) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.Remotes[selectedLine] | ||||
| } | ||||
|  | ||||
| func (gui *Gui) remotesRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	remote := gui.getSelectedRemote() | ||||
| 	remote := gui.State.Contexts.Remotes.GetSelected() | ||||
| 	if remote == nil { | ||||
| 		task = NewRenderStringTask("No remotes") | ||||
| 	} else { | ||||
|   | ||||
| @@ -9,12 +9,7 @@ import ( | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedStashEntry() *models.StashEntry { | ||||
| 	selectedLine := gui.State.Panels.Stash.SelectedLineIdx | ||||
| 	if selectedLine == -1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.StashEntries[selectedLine] | ||||
| 	return gui.State.Contexts.Stash.GetSelected() | ||||
| } | ||||
|  | ||||
| func (gui *Gui) stashRenderToMain() error { | ||||
|   | ||||
| @@ -2,25 +2,14 @@ package gui | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/loaders" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/controllers" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| ) | ||||
|  | ||||
| // list panel functions | ||||
|  | ||||
| func (gui *Gui) getSelectedSubCommit() *models.Commit { | ||||
| 	selectedLine := gui.State.Panels.SubCommits.SelectedLineIdx | ||||
| 	commits := gui.State.Model.SubCommits | ||||
| 	if selectedLine == -1 || len(commits) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return commits[selectedLine] | ||||
| } | ||||
|  | ||||
| func (gui *Gui) subCommitsRenderToMain() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	var task updateTask | ||||
| 	if commit == nil { | ||||
| 		task = NewRenderStringTask("No commits") | ||||
| @@ -39,7 +28,7 @@ func (gui *Gui) subCommitsRenderToMain() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCheckoutSubCommit() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -62,13 +51,13 @@ func (gui *Gui) handleCheckoutSubCommit() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCreateSubCommitResetMenu() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
|  | ||||
| 	return gui.helpers.Refs.CreateGitResetMenu(commit.Sha) | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleViewSubCommitFiles() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -85,7 +74,7 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error { | ||||
| 	// need to populate my sub commits | ||||
| 	commits, err := gui.git.Loaders.Commits.GetCommits( | ||||
| 		loaders.GetCommitsOptions{ | ||||
| 			Limit:                gui.State.Panels.Commits.LimitCommits, | ||||
| 			Limit:                gui.State.LimitCommits, | ||||
| 			FilterPath:           gui.State.Modes.Filtering.GetPath(), | ||||
| 			IncludeRebaseCommits: false, | ||||
| 			RefName:              refName, | ||||
| @@ -96,7 +85,6 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error { | ||||
| 	} | ||||
|  | ||||
| 	gui.State.Model.SubCommits = commits | ||||
| 	gui.State.Panels.SubCommits.refName = refName | ||||
| 	gui.State.Contexts.SubCommits.GetPanelState().SetSelectedLineIdx(0) | ||||
| 	gui.State.Contexts.SubCommits.SetParentContext(gui.currentSideListContext()) | ||||
|  | ||||
| @@ -104,7 +92,7 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleNewBranchOffSubCommit() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -113,7 +101,7 @@ func (gui *Gui) handleNewBranchOffSubCommit() error { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) handleCopySubCommit() error { | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -123,7 +111,7 @@ func (gui *Gui) handleCopySubCommit() error { | ||||
|  | ||||
| func (gui *Gui) handleCopySubCommitRange() error { | ||||
| 	// just doing this to ensure something is selected | ||||
| 	commit := gui.getSelectedSubCommit() | ||||
| 	commit := gui.State.Contexts.SubCommits.GetSelected() | ||||
| 	if commit == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|   | ||||
| @@ -8,18 +8,9 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
| ) | ||||
|  | ||||
| func (gui *Gui) getSelectedSubmodule() *models.SubmoduleConfig { | ||||
| 	selectedLine := gui.State.Panels.Submodules.SelectedLineIdx | ||||
| 	if selectedLine == -1 || len(gui.State.Model.Submodules) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Model.Submodules[selectedLine] | ||||
| } | ||||
|  | ||||
| func (gui *Gui) submodulesRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	submodule := gui.getSelectedSubmodule() | ||||
| 	submodule := gui.State.Contexts.Submodules.GetSelected() | ||||
| 	if submodule == nil { | ||||
| 		task = NewRenderStringTask("No submodules") | ||||
| 	} else { | ||||
|   | ||||
| @@ -15,17 +15,12 @@ func (gui *Gui) getSelectedSuggestionValue() string { | ||||
| } | ||||
|  | ||||
| func (gui *Gui) getSelectedSuggestion() *types.Suggestion { | ||||
| 	selectedLine := gui.State.Panels.Suggestions.SelectedLineIdx | ||||
| 	if selectedLine == -1 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return gui.State.Suggestions[selectedLine] | ||||
| 	return gui.State.Contexts.Suggestions.GetSelected() | ||||
| } | ||||
|  | ||||
| func (gui *Gui) setSuggestions(suggestions []*types.Suggestion) { | ||||
| 	gui.State.Suggestions = suggestions | ||||
| 	gui.State.Panels.Suggestions.SelectedLineIdx = 0 | ||||
| 	gui.State.Contexts.Suggestions.SetSelectedLineIdx(0) | ||||
| 	_ = gui.resetOrigin(gui.Views.Suggestions) | ||||
| 	_ = gui.State.Contexts.Suggestions.HandleRender() | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package gui | ||||
|  | ||||
| func (self *Gui) tagsRenderToMain() error { | ||||
| 	var task updateTask | ||||
| 	tag := self.State.Contexts.Tags.GetSelectedTag() | ||||
| 	tag := self.State.Contexts.Tags.GetSelected() | ||||
| 	if tag == nil { | ||||
| 		task = NewRenderStringTask("No tags") | ||||
| 	} else { | ||||
|   | ||||
| @@ -24,6 +24,7 @@ type ParentContexter interface { | ||||
| } | ||||
|  | ||||
| type IBaseContext interface { | ||||
| 	HasKeybindings | ||||
| 	ParentContexter | ||||
|  | ||||
| 	GetKind() ContextKind | ||||
| @@ -35,9 +36,7 @@ type IBaseContext interface { | ||||
|  | ||||
| 	GetOptionsMap() map[string]string | ||||
|  | ||||
| 	GetKeybindings(opts KeybindingsOpts) []*Binding | ||||
| 	AddKeybindingsFn(KeybindingsFn) | ||||
| 	GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding | ||||
| 	AddMouseKeybindingsFn(MouseKeybindingsFn) | ||||
| } | ||||
|  | ||||
| @@ -50,6 +49,33 @@ type Context interface { | ||||
| 	HandleRenderToMain() error | ||||
| } | ||||
|  | ||||
| type IListContext interface { | ||||
| 	Context | ||||
|  | ||||
| 	GetSelectedItemId() string | ||||
|  | ||||
| 	GetList() IList | ||||
|  | ||||
| 	OnSearchSelect(selectedLineIdx int) error | ||||
| 	FocusLine() | ||||
|  | ||||
| 	GetPanelState() IListPanelState | ||||
| 	GetViewTrait() IViewTrait | ||||
| } | ||||
|  | ||||
| type IViewTrait interface { | ||||
| 	FocusPoint(yIdx int) | ||||
| 	SetViewPortContent(content string) | ||||
| 	SetContent(content string) | ||||
| 	SetFooter(value string) | ||||
| 	SetOriginX(value int) | ||||
| 	ViewPortYBounds() (int, int) | ||||
| 	ScrollLeft() | ||||
| 	ScrollRight() | ||||
| 	PageDelta() int | ||||
| 	SelectedLineIdx() int | ||||
| } | ||||
|  | ||||
| type OnFocusOpts struct { | ||||
| 	ClickedViewName    string | ||||
| 	ClickedViewLineIdx int | ||||
| @@ -76,28 +102,6 @@ type IController interface { | ||||
| 	Context() Context | ||||
| } | ||||
|  | ||||
| type IListContext interface { | ||||
| 	HasKeybindings | ||||
|  | ||||
| 	GetSelectedItemId() string | ||||
| 	HandlePrevLine() error | ||||
| 	HandleNextLine() error | ||||
| 	HandleScrollLeft() error | ||||
| 	HandleScrollRight() error | ||||
| 	HandlePrevPage() error | ||||
| 	HandleNextPage() error | ||||
| 	HandleGotoTop() error | ||||
| 	HandleGotoBottom() error | ||||
| 	HandleClick(onClick func() error) error | ||||
|  | ||||
| 	OnSearchSelect(selectedLineIdx int) error | ||||
| 	FocusLine() | ||||
|  | ||||
| 	GetPanelState() IListPanelState | ||||
|  | ||||
| 	Context | ||||
| } | ||||
|  | ||||
| type IList interface { | ||||
| 	IListCursor | ||||
| 	GetItemsLength() int | ||||
|   | ||||
		Reference in New Issue
	
	Block a user