2019-11-16 14:00:27 +11:00
package gui
2020-08-19 21:51:50 +10:00
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
)
2019-11-16 14:00:27 +11:00
2020-08-17 21:58:30 +10:00
type ListContext struct {
2020-08-16 10:05:45 +10:00
ViewName string
2020-08-16 14:16:28 +10:00
ContextKey string
2020-08-16 10:05:45 +10:00
GetItemsLength func ( ) int
GetSelectedLineIdxPtr func ( ) * int
2020-08-19 21:51:50 +10:00
GetDisplayStrings func ( ) [ ] [ ] string
2020-08-16 10:05:45 +10:00
OnFocus func ( ) error
2020-08-16 13:58:29 +10:00
OnFocusLost func ( ) error
2020-08-16 10:05:45 +10:00
OnClickSelectedItem func ( ) error
2020-08-19 18:06:51 +10:00
Gui * Gui
RendersToMainView bool
Kind int
2019-11-16 14:00:27 +11:00
}
2020-08-19 21:51:50 +10:00
// 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 ( lc * ListContext ) OnRender ( ) error {
view , err := lc . Gui . g . View ( lc . ViewName )
if err != nil {
return nil
}
if lc . GetDisplayStrings != nil {
lc . Gui . refreshSelectedLine ( lc . GetSelectedLineIdxPtr ( ) , lc . GetItemsLength ( ) )
lc . Gui . renderDisplayStrings ( view , lc . GetDisplayStrings ( ) )
}
return nil
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) GetKey ( ) string {
return lc . ContextKey
2020-08-16 13:58:29 +10:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) GetKind ( ) int {
return lc . Kind
2020-08-16 13:58:29 +10:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) GetViewName ( ) string {
return lc . ViewName
2020-08-16 13:58:29 +10:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) HandleFocusLost ( ) error {
if lc . OnFocusLost != nil {
return lc . OnFocusLost ( )
2020-08-16 13:58:29 +10:00
}
return nil
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) HandleFocus ( ) error {
2020-08-19 22:27:31 +10:00
if lc . Gui . popupPanelFocused ( ) {
return nil
}
if lc . Gui . inDiffMode ( ) {
return lc . Gui . renderDiff ( )
}
if lc . OnFocus != nil {
return lc . OnFocus ( )
}
return nil
2020-08-16 13:58:29 +10:00
}
2020-08-19 18:06:51 +10:00
func ( lc * ListContext ) HandleRender ( ) error {
return lc . OnRender ( )
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handlePrevLine ( g * gocui . Gui , v * gocui . View ) error {
return lc . handleLineChange ( - 1 )
2019-11-16 14:00:27 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleNextLine ( g * gocui . Gui , v * gocui . View ) error {
return lc . handleLineChange ( 1 )
2019-11-16 14:00:27 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleLineChange ( change int ) error {
if ! lc . Gui . isPopupPanel ( lc . ViewName ) && lc . Gui . popupPanelFocused ( ) {
2019-11-16 14:00:27 +11:00
return nil
}
2020-08-17 21:58:30 +10:00
view , err := lc . Gui . g . View ( lc . ViewName )
2020-08-16 09:18:57 +10:00
if err != nil {
return err
}
2020-08-17 21:58:30 +10:00
lc . Gui . changeSelectedLine ( lc . GetSelectedLineIdxPtr ( ) , lc . GetItemsLength ( ) , change )
view . FocusPoint ( 0 , * lc . GetSelectedLineIdxPtr ( ) )
2019-11-16 14:00:27 +11:00
2020-08-17 21:58:30 +10:00
if lc . RendersToMainView {
if err := lc . Gui . resetOrigin ( lc . Gui . getMainView ( ) ) ; err != nil {
2019-11-16 14:00:27 +11:00
return err
}
2020-08-17 21:58:30 +10:00
if err := lc . Gui . resetOrigin ( lc . Gui . getSecondaryView ( ) ) ; err != nil {
2020-08-16 09:18:57 +10:00
return err
}
2019-11-16 14:00:27 +11:00
}
2020-01-12 13:39:48 +11:00
2020-08-19 22:27:31 +10:00
return lc . HandleFocus ( )
2019-11-16 14:00:27 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleNextPage ( g * gocui . Gui , v * gocui . View ) error {
view , err := lc . Gui . g . View ( lc . ViewName )
2020-03-28 13:44:20 +11:00
if err != nil {
return nil
}
_ , height := view . Size ( )
delta := height - 1
if delta == 0 {
delta = 1
}
2020-08-17 21:58:30 +10:00
return lc . handleLineChange ( delta )
2020-03-28 13:44:20 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleGotoTop ( g * gocui . Gui , v * gocui . View ) error {
return lc . handleLineChange ( - lc . GetItemsLength ( ) )
2020-03-28 13:44:20 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleGotoBottom ( g * gocui . Gui , v * gocui . View ) error {
return lc . handleLineChange ( lc . GetItemsLength ( ) )
2020-03-28 13:44:20 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handlePrevPage ( g * gocui . Gui , v * gocui . View ) error {
view , err := lc . Gui . g . View ( lc . ViewName )
2020-03-28 13:44:20 +11:00
if err != nil {
return nil
}
_ , height := view . Size ( )
delta := height - 1
if delta == 0 {
delta = 1
}
2020-08-17 21:58:30 +10:00
return lc . handleLineChange ( - delta )
2020-03-28 13:44:20 +11:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) handleClick ( g * gocui . Gui , v * gocui . View ) error {
if ! lc . Gui . isPopupPanel ( lc . ViewName ) && lc . Gui . popupPanelFocused ( ) {
2019-11-17 17:23:06 +11:00
return nil
}
2020-08-17 21:58:30 +10:00
selectedLineIdxPtr := lc . GetSelectedLineIdxPtr ( )
2019-11-17 17:23:06 +11:00
prevSelectedLineIdx := * selectedLineIdxPtr
newSelectedLineIdx := v . SelectedLineIdx ( )
2020-08-16 09:18:57 +10:00
// we need to focus the view
2020-08-17 21:58:30 +10:00
if err := lc . Gui . switchContext ( lc ) ; err != nil {
2020-08-16 09:18:57 +10:00
return err
}
2020-08-17 21:58:30 +10:00
if newSelectedLineIdx > lc . GetItemsLength ( ) - 1 {
2020-08-16 13:58:29 +10:00
return nil
2019-11-17 17:23:06 +11:00
}
* selectedLineIdxPtr = newSelectedLineIdx
2020-08-17 21:58:30 +10:00
prevViewName := lc . Gui . currentViewName ( )
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lc . ViewName && lc . OnClickSelectedItem != nil {
return lc . OnClickSelectedItem ( )
2019-11-17 17:23:06 +11:00
}
2020-08-19 22:27:31 +10:00
return lc . HandleFocus ( )
2020-08-16 09:18:57 +10:00
}
2020-08-17 21:58:30 +10:00
func ( lc * ListContext ) onSearchSelect ( selectedLineIdx int ) error {
* lc . GetSelectedLineIdxPtr ( ) = selectedLineIdx
2020-08-19 22:27:31 +10:00
return lc . HandleFocus ( )
2020-08-16 09:18:57 +10:00
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) menuListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "menu" ,
2020-08-16 14:16:28 +10:00
ContextKey : "menu" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return gui . getMenuView ( ) . LinesHeight ( ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Menu . SelectedLine } ,
OnFocus : gui . handleMenuSelect ,
2020-08-16 09:18:57 +10:00
// need to add a layer of indirection here because the callback changes during runtime
2020-08-16 10:05:45 +10:00
OnClickSelectedItem : func ( ) error { return gui . State . Panels . Menu . OnPress ( gui . g , nil ) } ,
Gui : gui ,
RendersToMainView : false ,
2020-08-16 13:58:29 +10:00
Kind : PERSISTENT_POPUP ,
2020-08-19 21:51:50 +10:00
// no GetDisplayStrings field because we do a custom render on menu creation
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) filesListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "files" ,
2020-08-16 14:16:28 +10:00
ContextKey : "files" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . Files ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Files . SelectedLine } ,
OnFocus : gui . focusAndSelectFile ,
OnClickSelectedItem : gui . handleFilePress ,
Gui : gui ,
RendersToMainView : false ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetFileListDisplayStrings ( gui . State . Files , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) branchesListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "branches" ,
2020-08-16 14:16:28 +10:00
ContextKey : "local-branches" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . Branches ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Branches . SelectedLine } ,
OnFocus : gui . handleBranchSelect ,
2020-08-19 18:41:57 +10:00
Gui : gui ,
RendersToMainView : true ,
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetBranchListDisplayStrings ( gui . State . Branches , gui . State . ScreenMode != SCREEN_NORMAL , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) remotesListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "branches" ,
2020-08-16 14:16:28 +10:00
ContextKey : "remotes" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . Remotes ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Remotes . SelectedLine } ,
2020-08-19 19:31:58 +10:00
OnFocus : gui . handleRemoteSelect ,
2020-08-16 10:05:45 +10:00
OnClickSelectedItem : gui . handleRemoteEnter ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetRemoteListDisplayStrings ( gui . State . Remotes , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) remoteBranchesListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "branches" ,
2020-08-16 14:16:28 +10:00
ContextKey : "remote-branches" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . RemoteBranches ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . RemoteBranches . SelectedLine } ,
OnFocus : gui . handleRemoteBranchSelect ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetRemoteBranchListDisplayStrings ( gui . State . RemoteBranches , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) tagsListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "branches" ,
2020-08-16 14:16:28 +10:00
ContextKey : "tags" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . Tags ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Tags . SelectedLine } ,
OnFocus : gui . handleTagSelect ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetTagListDisplayStrings ( gui . State . Tags , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) branchCommitsListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "commits" ,
2020-08-16 14:16:28 +10:00
ContextKey : "branch-commits" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . Commits ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Commits . SelectedLine } ,
OnFocus : gui . handleCommitSelect ,
OnClickSelectedItem : gui . handleSwitchToCommitFilesPanel ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetCommitListDisplayStrings ( gui . State . Commits , gui . State . ScreenMode != SCREEN_NORMAL , gui . cherryPickedCommitShaMap ( ) , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) reflogCommitsListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "commits" ,
2020-08-16 14:16:28 +10:00
ContextKey : "reflog-commits" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . FilteredReflogCommits ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . ReflogCommits . SelectedLine } ,
OnFocus : gui . handleReflogCommitSelect ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetReflogCommitListDisplayStrings ( gui . State . FilteredReflogCommits , gui . State . ScreenMode != SCREEN_NORMAL , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) stashListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "stash" ,
2020-08-16 14:16:28 +10:00
ContextKey : "stash" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . StashEntries ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . Stash . SelectedLine } ,
OnFocus : gui . handleStashEntrySelect ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
// TODO :see if we still need to reset the origin here
return presentation . GetStashEntryListDisplayStrings ( gui . State . StashEntries , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) commitFilesListContext ( ) * ListContext {
return & ListContext {
2020-08-16 10:05:45 +10:00
ViewName : "commitFiles" ,
2020-08-19 22:57:22 +10:00
ContextKey : "commit-files" ,
2020-08-16 10:05:45 +10:00
GetItemsLength : func ( ) int { return len ( gui . State . CommitFiles ) } ,
GetSelectedLineIdxPtr : func ( ) * int { return & gui . State . Panels . CommitFiles . SelectedLine } ,
OnFocus : gui . handleCommitFileSelect ,
Gui : gui ,
RendersToMainView : true ,
2020-08-16 13:58:29 +10:00
Kind : SIDE_CONTEXT ,
2020-08-19 21:51:50 +10:00
GetDisplayStrings : func ( ) [ ] [ ] string {
return presentation . GetCommitFileListDisplayStrings ( gui . State . CommitFiles , gui . State . Diff . Ref )
} ,
2020-08-16 09:18:57 +10:00
}
2019-11-17 17:23:06 +11:00
}
2020-08-17 21:58:30 +10:00
func ( gui * Gui ) getListContexts ( ) [ ] * ListContext {
return [ ] * ListContext {
gui . menuListContext ( ) ,
gui . filesListContext ( ) ,
gui . branchesListContext ( ) ,
gui . remotesListContext ( ) ,
gui . remoteBranchesListContext ( ) ,
gui . tagsListContext ( ) ,
gui . branchCommitsListContext ( ) ,
gui . reflogCommitsListContext ( ) ,
gui . stashListContext ( ) ,
gui . commitFilesListContext ( ) ,
2019-11-16 14:00:27 +11:00
}
}
2020-08-17 22:05:15 +10:00
func ( gui * Gui ) getListContextKeyBindings ( ) [ ] * Binding {
bindings := make ( [ ] * Binding , 0 )
for _ , listContext := range gui . getListContexts ( ) {
bindings = append ( bindings , [ ] * Binding {
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.prevItem-alt" ) , Modifier : gocui . ModNone , Handler : listContext . handlePrevLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.prevItem" ) , Modifier : gocui . ModNone , Handler : listContext . handlePrevLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gocui . MouseWheelUp , Modifier : gocui . ModNone , Handler : listContext . handlePrevLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.nextItem-alt" ) , Modifier : gocui . ModNone , Handler : listContext . handleNextLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.nextItem" ) , Modifier : gocui . ModNone , Handler : listContext . handleNextLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.prevPage" ) , Modifier : gocui . ModNone , Handler : listContext . handlePrevPage , Description : gui . Tr . SLocalize ( "prevPage" ) } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.nextPage" ) , Modifier : gocui . ModNone , Handler : listContext . handleNextPage , Description : gui . Tr . SLocalize ( "nextPage" ) } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gui . getKey ( "universal.gotoTop" ) , Modifier : gocui . ModNone , Handler : listContext . handleGotoTop , Description : gui . Tr . SLocalize ( "gotoTop" ) } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gocui . MouseWheelDown , Modifier : gocui . ModNone , Handler : listContext . handleNextLine } ,
{ ViewName : listContext . ViewName , Contexts : [ ] string { listContext . ContextKey } , Key : gocui . MouseLeft , Modifier : gocui . ModNone , Handler : listContext . handleClick } ,
} ... )
// the commits panel needs to lazyload things so it has a couple of its own handlers
openSearchHandler := gui . handleOpenSearch
gotoBottomHandler := listContext . handleGotoBottom
if listContext . ViewName == "commits" {
openSearchHandler = gui . handleOpenSearchForCommitsPanel
gotoBottomHandler = gui . handleGotoBottomForCommitsPanel
}
bindings = append ( bindings , [ ] * Binding {
{
ViewName : listContext . ViewName ,
Contexts : [ ] string { listContext . ContextKey } ,
Key : gui . getKey ( "universal.startSearch" ) ,
Handler : openSearchHandler ,
Description : gui . Tr . SLocalize ( "startSearch" ) ,
} ,
{
ViewName : listContext . ViewName ,
Contexts : [ ] string { listContext . ContextKey } ,
Key : gui . getKey ( "universal.gotoBottom" ) ,
Handler : gotoBottomHandler ,
Description : gui . Tr . SLocalize ( "gotoBottom" ) ,
} ,
} ... )
}
return bindings
}