1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-03-21 21:47:32 +02:00

paging keybindings for line by line panel

support searching in line by line panel

move mutexes into their own struct

add line by line panel mutex

apply LBL panel mutex

bump gocui to prevent crashing when search item count decreases
This commit is contained in:
Jesse Duffield 2020-10-02 07:56:14 +10:00
parent 40c5cd4b4b
commit 2e05ac0c90
14 changed files with 292 additions and 165 deletions

View File

@ -15,7 +15,7 @@ func (gui *Gui) getSelectedCommitFile() *models.CommitFile {
} }
func (gui *Gui) handleCommitFileSelect() error { func (gui *Gui) handleCommitFileSelect() error {
gui.handleEscapeLineByLinePanel() gui.escapeLineByLinePanel()
commitFile := gui.getSelectedCommitFile() commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {

View File

@ -31,7 +31,7 @@ func (gui *Gui) handleCommitSelect() error {
}() }()
} }
gui.handleEscapeLineByLinePanel() gui.escapeLineByLinePanel()
var task updateTask var task updateTask
commit := gui.getSelectedLocalCommit() commit := gui.getSelectedLocalCommit()
@ -110,8 +110,8 @@ func (gui *Gui) refreshCommits() error {
} }
func (gui *Gui) refreshCommitsWithLimit() error { func (gui *Gui) refreshCommitsWithLimit() error {
gui.State.BranchCommitsMutex.Lock() gui.State.Mutexes.BranchCommitsMutex.Lock()
defer gui.State.BranchCommitsMutex.Unlock() defer gui.State.Mutexes.BranchCommitsMutex.Unlock()
builder := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr) builder := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr)
@ -132,8 +132,8 @@ func (gui *Gui) refreshCommitsWithLimit() error {
} }
func (gui *Gui) refreshRebaseCommits() error { func (gui *Gui) refreshRebaseCommits() error {
gui.State.BranchCommitsMutex.Lock() gui.State.Mutexes.BranchCommitsMutex.Lock()
defer gui.State.BranchCommitsMutex.Unlock() defer gui.State.Mutexes.BranchCommitsMutex.Unlock()
builder := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr) builder := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr)

View File

@ -81,11 +81,11 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
} }
func (gui *Gui) refreshFilesAndSubmodules() error { func (gui *Gui) refreshFilesAndSubmodules() error {
gui.State.RefreshingFilesMutex.Lock() gui.State.Mutexes.RefreshingFilesMutex.Lock()
gui.State.IsRefreshingFiles = true gui.State.IsRefreshingFiles = true
defer func() { defer func() {
gui.State.IsRefreshingFiles = false gui.State.IsRefreshingFiles = false
gui.State.RefreshingFilesMutex.Unlock() gui.State.Mutexes.RefreshingFilesMutex.Unlock()
}() }()
selectedFile := gui.getSelectedFile() selectedFile := gui.getSelectedFile()
@ -517,8 +517,8 @@ func (gui *Gui) pullFiles(opts PullFilesOptions) error {
} }
func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error { func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error {
gui.State.FetchMutex.Lock() gui.State.Mutexes.FetchMutex.Lock()
defer gui.State.FetchMutex.Unlock() defer gui.State.Mutexes.FetchMutex.Unlock()
err := gui.GitCommand.Fetch( err := gui.GitCommand.Fetch(
commands.FetchOptions{ commands.FetchOptions{

View File

@ -164,8 +164,8 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) fetch(canPromptForCredentials bool) (err error) { func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
gui.State.FetchMutex.Lock() gui.State.Mutexes.FetchMutex.Lock()
defer gui.State.FetchMutex.Unlock() defer gui.State.Mutexes.FetchMutex.Unlock()
fetchOpts := commands.FetchOptions{} fetchOpts := commands.FetchOptions{}
if canPromptForCredentials { if canPromptForCredentials {

View File

@ -285,6 +285,14 @@ type Modes struct {
Diffing Diffing Diffing Diffing
} }
type guiStateMutexes struct {
RefreshingFilesMutex sync.Mutex
RefreshingStatusMutex sync.Mutex
FetchMutex sync.Mutex
BranchCommitsMutex sync.Mutex
LineByLinePanelMutex sync.Mutex
}
type guiState struct { type guiState struct {
Files []*models.File Files []*models.File
Submodules []*models.SubmoduleConfig Submodules []*models.SubmoduleConfig
@ -310,10 +318,7 @@ type guiState struct {
SplitMainPanel bool SplitMainPanel bool
RetainOriginalDir bool RetainOriginalDir bool
IsRefreshingFiles bool IsRefreshingFiles bool
RefreshingFilesMutex sync.Mutex Mutexes guiStateMutexes
RefreshingStatusMutex sync.Mutex
FetchMutex sync.Mutex
BranchCommitsMutex sync.Mutex
Searching searchingState Searching searchingState
ScreenMode int ScreenMode int
SideView *gocui.View SideView *gocui.View

View File

@ -1147,14 +1147,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevItem), Key: gui.getKey(config.Universal.PrevItem),
Handler: gui.handleSelectPrevLine, Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
Description: gui.Tr.PrevLine, Description: gui.Tr.PrevLine,
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextItem), Key: gui.getKey(config.Universal.NextItem),
Handler: gui.handleSelectNextLine, Handler: gui.wrappedHandler(gui.handleSelectNextLine),
Description: gui.Tr.NextLine, Description: gui.Tr.NextLine,
}, },
{ {
@ -1162,56 +1162,56 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevItemAlt), Key: gui.getKey(config.Universal.PrevItemAlt),
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectPrevLine, Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextItemAlt), Key: gui.getKey(config.Universal.NextItemAlt),
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectNextLine, Handler: gui.wrappedHandler(gui.handleSelectNextLine),
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseWheelUp, Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectPrevLine, Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseWheelDown, Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectNextLine, Handler: gui.wrappedHandler(gui.handleSelectNextLine),
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevBlock), Key: gui.getKey(config.Universal.PrevBlock),
Handler: gui.handleSelectPrevHunk, Handler: gui.wrappedHandler(gui.handleSelectPrevHunk),
Description: gui.Tr.PrevHunk, Description: gui.Tr.PrevHunk,
}, },
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextBlock),
Handler: gui.handleSelectNextHunk,
Description: gui.Tr.NextHunk,
},
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevBlockAlt), Key: gui.getKey(config.Universal.PrevBlockAlt),
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectPrevHunk, Handler: gui.wrappedHandler(gui.handleSelectPrevHunk),
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextBlock),
Handler: gui.wrappedHandler(gui.handleSelectNextHunk),
Description: gui.Tr.NextHunk,
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextBlockAlt), Key: gui.getKey(config.Universal.NextBlockAlt),
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectNextHunk, Handler: gui.wrappedHandler(gui.handleSelectNextHunk),
}, },
{ {
ViewName: "main", ViewName: "main",
@ -1227,6 +1227,50 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleFileOpen, Handler: gui.handleFileOpen,
Description: gui.Tr.LcOpenFile, Description: gui.Tr.LcOpenFile,
}, },
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextPage),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.handleLineByLineNextPage),
Description: gui.Tr.LcNextPage,
Tag: "navigation",
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevPage),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.handleLineByLinePrevPage),
Description: gui.Tr.LcPrevPage,
Tag: "navigation",
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.GotoTop),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.handleLineByLineGotoTop),
Description: gui.Tr.LcGotoTop,
Tag: "navigation",
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.GotoBottom),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.handleLineByLineGotoBottom),
Description: gui.Tr.LcGotoBottom,
Tag: "navigation",
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.StartSearch),
Handler: gui.handleOpenSearch,
Description: gui.Tr.LcStartSearch,
Tag: "navigation",
},
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY},
@ -1238,7 +1282,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleDragSelect), Key: gui.getKey(config.Main.ToggleDragSelect),
Handler: gui.handleToggleSelectRange, Handler: gui.wrappedHandler(gui.handleToggleSelectRange),
Description: gui.Tr.ToggleDragSelect, Description: gui.Tr.ToggleDragSelect,
}, },
// Alias 'V' -> 'v' // Alias 'V' -> 'v'
@ -1246,14 +1290,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleDragSelectAlt), Key: gui.getKey(config.Main.ToggleDragSelectAlt),
Handler: gui.handleToggleSelectRange, Handler: gui.wrappedHandler(gui.handleToggleSelectRange),
Description: gui.Tr.ToggleDragSelect, Description: gui.Tr.ToggleDragSelect,
}, },
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleSelectHunk), Key: gui.getKey(config.Main.ToggleSelectHunk),
Handler: gui.handleToggleSelectHunk, Handler: gui.wrappedHandler(gui.handleToggleSelectHunk),
Description: gui.Tr.ToggleSelectHunk, Description: gui.Tr.ToggleSelectHunk,
}, },
{ {
@ -1261,7 +1305,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY}, Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseLeft, Key: gocui.MouseLeft,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMouseDown, Handler: gui.handleLBLMouseDown,
}, },
{ {
ViewName: "main", ViewName: "main",

View File

@ -317,6 +317,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
listContextState.view.SetOnSelectItem(gui.onSelectItemWrapper(listContextState.listContext.onSearchSelect)) listContextState.view.SetOnSelectItem(gui.onSelectItemWrapper(listContextState.listContext.onSearchSelect))
} }
gui.getMainView().SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
mainViewWidth, mainViewHeight := gui.getMainView().Size() mainViewWidth, mainViewHeight := gui.getMainView().Size()
if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight { if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight {
gui.State.PrevMainWidth = mainViewWidth gui.State.PrevMainWidth = mainViewWidth

View File

@ -25,6 +25,9 @@ const (
// returns whether the patch is empty so caller can escape if necessary // returns whether the patch is empty so caller can escape if necessary
// both diffs should be non-coloured because we'll parse them and colour them here // both diffs should be non-coloured because we'll parse them and colour them here
func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int) (bool, error) { func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int) (bool, error) {
gui.State.Mutexes.LineByLinePanelMutex.Lock()
defer gui.State.Mutexes.LineByLinePanelMutex.Unlock()
state := gui.State.Panels.LineByLine state := gui.State.Panels.LineByLine
patchParser, err := patch.NewPatchParser(gui.Log, diff) patchParser, err := patch.NewPatchParser(gui.Log, diff)
@ -98,26 +101,32 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
return false, nil return false, nil
} }
func (gui *Gui) handleSelectPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSelectPrevLine() error {
return gui.handleCycleLine(-1) return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.LBLCycleLine(-1)
})
} }
func (gui *Gui) handleSelectNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSelectNextLine() error {
return gui.handleCycleLine(+1) return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.LBLCycleLine(+1)
})
} }
func (gui *Gui) handleSelectPrevHunk(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSelectPrevHunk() error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1) newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1)
return gui.selectNewHunk(newHunk) return gui.selectNewHunk(newHunk)
})
} }
func (gui *Gui) handleSelectNextHunk(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSelectNextHunk() error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1) newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1)
return gui.selectNewHunk(newHunk) return gui.selectNewHunk(newHunk)
})
} }
func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error { func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
@ -136,7 +145,7 @@ func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
return gui.focusSelection(true) return gui.focusSelection(true)
} }
func (gui *Gui) handleCycleLine(change int) error { func (gui *Gui) LBLCycleLine(change int) error {
state := gui.State.Panels.LineByLine state := gui.State.Panels.LineByLine
if state.SelectMode == HUNK { if state.SelectMode == HUNK {
@ -144,10 +153,10 @@ func (gui *Gui) handleCycleLine(change int) error {
return gui.selectNewHunk(newHunk) return gui.selectNewHunk(newHunk)
} }
return gui.handleSelectNewLine(state.SelectedLineIdx + change) return gui.LBLSelectLine(state.SelectedLineIdx + change)
} }
func (gui *Gui) handleSelectNewLine(newSelectedLineIdx int) error { func (gui *Gui) LBLSelectLine(newSelectedLineIdx int) error {
state := gui.State.Panels.LineByLine state := gui.State.Panels.LineByLine
if newSelectedLineIdx < 0 { if newSelectedLineIdx < 0 {
@ -176,9 +185,8 @@ func (gui *Gui) handleSelectNewLine(newSelectedLineIdx int) error {
return gui.focusSelection(false) return gui.focusSelection(false)
} }
func (gui *Gui) handleMouseDown(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleLBLMouseDown(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
@ -189,39 +197,42 @@ func (gui *Gui) handleMouseDown(g *gocui.Gui, v *gocui.View) error {
state.SelectMode = RANGE state.SelectMode = RANGE
return gui.handleSelectNewLine(newSelectedLineIdx) return gui.LBLSelectLine(newSelectedLineIdx)
})
} }
func (gui *Gui) handleMouseDrag(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMouseDrag(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
return gui.handleSelectNewLine(v.SelectedLineIdx()) return gui.LBLSelectLine(v.SelectedLineIdx())
})
} }
func (gui *Gui) handleMouseScrollUp(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMouseScrollUp(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
state.SelectMode = LINE state.SelectMode = LINE
return gui.handleCycleLine(-1) return gui.LBLCycleLine(-1)
})
} }
func (gui *Gui) handleMouseScrollDown(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMouseScrollDown(g *gocui.Gui, v *gocui.View) error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
state.SelectMode = LINE state.SelectMode = LINE
return gui.handleCycleLine(1) return gui.LBLCycleLine(1)
})
} }
func (gui *Gui) getSelectedCommitFileName() string { func (gui *Gui) getSelectedCommitFileName() string {
@ -297,8 +308,8 @@ func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
return nil return nil
} }
func (gui *Gui) handleToggleSelectRange(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleToggleSelectRange() error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
if state.SelectMode == RANGE { if state.SelectMode == RANGE {
state.SelectMode = LINE state.SelectMode = LINE
} else { } else {
@ -307,11 +318,11 @@ func (gui *Gui) handleToggleSelectRange(g *gocui.Gui, v *gocui.View) error {
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
return gui.refreshMainViewForLineByLine() return gui.refreshMainViewForLineByLine()
})
} }
func (gui *Gui) handleToggleSelectHunk(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleToggleSelectHunk() error {
state := gui.State.Panels.LineByLine return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
if state.SelectMode == HUNK { if state.SelectMode == HUNK {
state.SelectMode = LINE state.SelectMode = LINE
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
@ -326,13 +337,18 @@ func (gui *Gui) handleToggleSelectHunk(g *gocui.Gui, v *gocui.View) error {
} }
return gui.focusSelection(state.SelectMode == HUNK) return gui.focusSelection(state.SelectMode == HUNK)
})
} }
func (gui *Gui) handleEscapeLineByLinePanel() { func (gui *Gui) escapeLineByLinePanel() {
gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
gui.State.Panels.LineByLine = nil gui.State.Panels.LineByLine = nil
return nil
})
} }
func (gui *Gui) handleOpenFileAtLine() error { func (gui *Gui) handleOpenFileAtLine() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
// again, would be good to use inheritance here (or maybe even composition) // again, would be good to use inheritance here (or maybe even composition)
var filename string var filename string
switch gui.State.MainContext { switch gui.State.MainContext {
@ -348,7 +364,6 @@ func (gui *Gui) handleOpenFileAtLine() error {
return errors.Errorf("unknown main context: %s", gui.State.MainContext) return errors.Errorf("unknown main context: %s", gui.State.MainContext)
} }
state := gui.State.Panels.LineByLine
// need to look at current index, then work out what my hunk's header information is, and see how far my line is away from the hunk header // need to look at current index, then work out what my hunk's header information is, and see how far my line is away from the hunk header
selectedHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0) selectedHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
lineNumber := selectedHunk.LineNumberOfLine(state.SelectedLineIdx) lineNumber := selectedHunk.LineNumberOfLine(state.SelectedLineIdx)
@ -358,4 +373,58 @@ func (gui *Gui) handleOpenFileAtLine() error {
} }
return nil return nil
})
}
func (gui *Gui) handleLineByLineNextPage() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
newSelectedLineIdx := state.SelectedLineIdx + gui.pageDelta(gui.getMainView())
return gui.lineByLineNavigateTo(newSelectedLineIdx)
})
}
func (gui *Gui) handleLineByLinePrevPage() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
newSelectedLineIdx := state.SelectedLineIdx - gui.pageDelta(gui.getMainView())
return gui.lineByLineNavigateTo(newSelectedLineIdx)
})
}
func (gui *Gui) handleLineByLineGotoBottom() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
newSelectedLineIdx := len(state.PatchParser.PatchLines) - 1
return gui.lineByLineNavigateTo(newSelectedLineIdx)
})
}
func (gui *Gui) handleLineByLineGotoTop() error {
return gui.lineByLineNavigateTo(0)
}
func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.lineByLineNavigateTo(selectedLineIdx)
})
}
func (gui *Gui) lineByLineNavigateTo(selectedLineIdx int) error {
state := gui.State.Panels.LineByLine
state.SelectMode = LINE
return gui.LBLSelectLine(selectedLineIdx)
}
func (gui *Gui) withLBLActiveCheck(f func(*lineByLinePanelState) error) error {
gui.State.Mutexes.LineByLinePanelMutex.Lock()
defer gui.State.Mutexes.LineByLinePanelMutex.Unlock()
state := gui.State.Panels.LineByLine
if state == nil {
return nil
}
return f(state)
} }

View File

@ -186,11 +186,8 @@ func (lc *ListContext) handleNextPage(g *gocui.Gui, v *gocui.View) error {
if err != nil { if err != nil {
return nil return nil
} }
_, height := view.Size() delta := lc.Gui.pageDelta(view)
delta := height - 1
if delta == 0 {
delta = 1
}
return lc.handleLineChange(delta) return lc.handleLineChange(delta)
} }
@ -207,11 +204,9 @@ func (lc *ListContext) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
if err != nil { if err != nil {
return nil return nil
} }
_, height := view.Size()
delta := height - 1 delta := lc.Gui.pageDelta(view)
if delta == 0 {
delta = 1
}
return lc.handleLineChange(-delta) return lc.handleLineChange(-delta)
} }

View File

@ -92,7 +92,7 @@ func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error
} }
func (gui *Gui) handleEscapePatchBuildingPanel() error { func (gui *Gui) handleEscapePatchBuildingPanel() error {
gui.handleEscapeLineByLinePanel() gui.escapeLineByLinePanel()
if gui.GitCommand.PatchManager.IsEmpty() { if gui.GitCommand.PatchManager.IsEmpty() {
gui.GitCommand.PatchManager.Reset() gui.GitCommand.PatchManager.Reset()

View File

@ -158,8 +158,8 @@ func (gui *Gui) handleFetchRemote(g *gocui.Gui, v *gocui.View) error {
} }
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error { return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
gui.State.FetchMutex.Lock() gui.State.Mutexes.FetchMutex.Lock()
defer gui.State.FetchMutex.Unlock() defer gui.State.Mutexes.FetchMutex.Unlock()
// TODO: test this // TODO: test this
err := gui.GitCommand.FetchRemote(remote.Name, gui.promptUserForCredential) err := gui.GitCommand.FetchRemote(remote.Name, gui.promptUserForCredential)

View File

@ -84,7 +84,7 @@ func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleStagingEscape() error { func (gui *Gui) handleStagingEscape() error {
gui.handleEscapeLineByLinePanel() gui.escapeLineByLinePanel()
return gui.switchContext(gui.Contexts.Files.Context) return gui.switchContext(gui.Contexts.Files.Context)
} }

View File

@ -12,8 +12,8 @@ import (
// never call this on its own, it should only be called from within refreshCommits() // never call this on its own, it should only be called from within refreshCommits()
func (gui *Gui) refreshStatus() { func (gui *Gui) refreshStatus() {
gui.State.RefreshingStatusMutex.Lock() gui.State.Mutexes.RefreshingStatusMutex.Lock()
defer gui.State.RefreshingStatusMutex.Unlock() defer gui.State.Mutexes.RefreshingStatusMutex.Unlock()
currentBranch := gui.currentBranch() currentBranch := gui.currentBranch()
if currentBranch == nil { if currentBranch == nil {

View File

@ -433,3 +433,15 @@ func (gui *Gui) handlePrevTab(g *gocui.Gui, v *gocui.View) error {
utils.ModuloWithWrap(v.TabIndex-1, len(v.Tabs)), utils.ModuloWithWrap(v.TabIndex-1, len(v.Tabs)),
) )
} }
// this is the distance we will move the cursor when paging up or down in a view
func (gui *Gui) pageDelta(view *gocui.View) int {
_, height := view.Size()
delta := height - 1
if delta == 0 {
return 1
}
return delta
}