summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2020-10-02 07:56:14 +1000
committerJesse Duffield <jessedduffield@gmail.com>2020-10-10 00:23:01 +1100
commit2e05ac0c90848c71ab23728859a7bd4b67c39114 (patch)
tree936571f345de7b87ebbcc2025d29c04fc1e9d129
parent40c5cd4b4b3436da2616e848d40e7760ec99167f (diff)
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
-rw-r--r--pkg/gui/commit_files_panel.go2
-rw-r--r--pkg/gui/commits_panel.go10
-rw-r--r--pkg/gui/files_panel.go8
-rw-r--r--pkg/gui/global_handlers.go4
-rw-r--r--pkg/gui/gui.go53
-rw-r--r--pkg/gui/keybindings.go84
-rw-r--r--pkg/gui/layout.go2
-rw-r--r--pkg/gui/line_by_line_panel.go249
-rw-r--r--pkg/gui/list_context.go15
-rw-r--r--pkg/gui/patch_building_panel.go2
-rw-r--r--pkg/gui/remotes_panel.go4
-rw-r--r--pkg/gui/staging_panel.go2
-rw-r--r--pkg/gui/status_panel.go4
-rw-r--r--pkg/gui/view_helpers.go12
14 files changed, 289 insertions, 162 deletions
diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go
index 830d600d6..6a5109f08 100644
--- a/pkg/gui/commit_files_panel.go
+++ b/pkg/gui/commit_files_panel.go
@@ -15,7 +15,7 @@ func (gui *Gui) getSelectedCommitFile() *models.CommitFile {
}
func (gui *Gui) handleCommitFileSelect() error {
- gui.handleEscapeLineByLinePanel()
+ gui.escapeLineByLinePanel()
commitFile := gui.getSelectedCommitFile()
if commitFile == nil {
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 231ee4a98..cc969c0c1 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -31,7 +31,7 @@ func (gui *Gui) handleCommitSelect() error {
}()
}
- gui.handleEscapeLineByLinePanel()
+ gui.escapeLineByLinePanel()
var task updateTask
commit := gui.getSelectedLocalCommit()
@@ -110,8 +110,8 @@ func (gui *Gui) refreshCommits() error {
}
func (gui *Gui) refreshCommitsWithLimit() error {
- gui.State.BranchCommitsMutex.Lock()
- defer gui.State.BranchCommitsMutex.Unlock()
+ gui.State.Mutexes.BranchCommitsMutex.Lock()
+ defer gui.State.Mutexes.BranchCommitsMutex.Unlock()
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 {
- gui.State.BranchCommitsMutex.Lock()
- defer gui.State.BranchCommitsMutex.Unlock()
+ gui.State.Mutexes.BranchCommitsMutex.Lock()
+ defer gui.State.Mutexes.BranchCommitsMutex.Unlock()
builder := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr)
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index b8f79a51d..a413beab5 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -81,11 +81,11 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
}
func (gui *Gui) refreshFilesAndSubmodules() error {
- gui.State.RefreshingFilesMutex.Lock()
+ gui.State.Mutexes.RefreshingFilesMutex.Lock()
gui.State.IsRefreshingFiles = true
defer func() {
gui.State.IsRefreshingFiles = false
- gui.State.RefreshingFilesMutex.Unlock()
+ gui.State.Mutexes.RefreshingFilesMutex.Unlock()
}()
selectedFile := gui.getSelectedFile()
@@ -517,8 +517,8 @@ func (gui *Gui) pullFiles(opts PullFilesOptions) error {
}
func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error {
- gui.State.FetchMutex.Lock()
- defer gui.State.FetchMutex.Unlock()
+ gui.State.Mutexes.FetchMutex.Lock()
+ defer gui.State.Mutexes.FetchMutex.Unlock()
err := gui.GitCommand.Fetch(
commands.FetchOptions{
diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go
index e7c0eb888..6c344cdbd 100644
--- a/pkg/gui/global_handlers.go
+++ b/pkg/gui/global_handlers.go
@@ -164,8 +164,8 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
- gui.State.FetchMutex.Lock()
- defer gui.State.FetchMutex.Unlock()
+ gui.State.Mutexes.FetchMutex.Lock()
+ defer gui.State.Mutexes.FetchMutex.Unlock()
fetchOpts := commands.FetchOptions{}
if canPromptForCredentials {
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index c1df0ee93..ce3546c74 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -285,6 +285,14 @@ type Modes struct {
Diffing Diffing
}
+type guiStateMutexes struct {
+ RefreshingFilesMutex sync.Mutex
+ RefreshingStatusMutex sync.Mutex
+ FetchMutex sync.Mutex
+ BranchCommitsMutex sync.Mutex
+ LineByLinePanelMutex sync.Mutex
+}
+
type guiState struct {
Files []*models.File
Submodules []*models.SubmoduleConfig
@@ -298,30 +306,27 @@ type guiState struct {
// ReflogCommits are the ones used by the branches panel to obtain recency values
// if we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
// one and the same
- ReflogCommits []*models.Commit
- SubCommits []*models.Commit
- Remotes []*models.Remote
- RemoteBranches []*models.RemoteBranch
- Tags []*models.Tag
- MenuItems []*menuItem
- Updating bool
- Panels *panelStates
- MainContext string // used to keep the main and secondary views' contexts in sync
- SplitMainPanel bool
- RetainOriginalDir bool
- IsRefreshingFiles bool
- RefreshingFilesMutex sync.Mutex
- RefreshingStatusMutex sync.Mutex
- FetchMutex sync.Mutex
- BranchCommitsMutex sync.Mutex
- Searching searchingState
- ScreenMode int
- SideView *gocui.View
- Ptmx *os.File
- PrevMainWidth int
- PrevMainHeight int
- OldInformation string
- StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once
+ ReflogCommits []*models.Commit
+ SubCommits []*models.Commit
+ Remotes []*models.Remote
+ RemoteBranches []*models.RemoteBranch
+ Tags []*models.Tag
+ MenuItems []*menuItem
+ Updating bool
+ Panels *panelStates
+ MainContext string // used to keep the main and secondary views' contexts in sync
+ SplitMainPanel bool
+ RetainOriginalDir bool
+ IsRefreshingFiles bool
+ Mutexes guiStateMutexes
+ Searching searchingState
+ ScreenMode int
+ SideView *gocui.View
+ Ptmx *os.File
+ PrevMainWidth int
+ PrevMainHeight int
+ OldInformation string
+ StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once
Modes Modes
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index a92434561..ac87b6dd8 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -1147,14 +1147,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevItem),
- Handler: gui.handleSelectPrevLine,
+ Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
Description: gui.Tr.PrevLine,
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextItem),
- Handler: gui.handleSelectNextLine,
+ Handler: gui.wrappedHandler(gui.handleSelectNextLine),
Description: gui.Tr.NextLine,
},
{
@@ -1162,56 +1162,56 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevItemAlt),
Modifier: gocui.ModNone,
- Handler: gui.handleSelectPrevLine,
+ Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextItemAlt),
Modifier: gocui.ModNone,
- Handler: gui.handleSelectNextLine,
+ Handler: gui.wrappedHandler(gui.handleSelectNextLine),
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone,
- Handler: gui.handleSelectPrevLine,
+ Handler: gui.wrappedHandler(gui.handleSelectPrevLine),
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone,
- Handler: gui.handleSelectNextLine,
+ Handler: gui.wrappedHandler(gui.handleSelectNextLine),
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevBlock),
- Handler: gui.handleSelectPrevHunk,
+ Handler: gui.wrappedHandler(gui.handleSelectPrevHunk),
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",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.PrevBlockAlt),
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",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.NextBlockAlt),
Modifier: gocui.ModNone,
- Handler: gui.handleSelectNextHunk,
+ Handler: gui.wrappedHandler(gui.handleSelectNextHunk),
},
{
ViewName: "main",
@@ -1229,6 +1229,50 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
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",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.Select),
Handler: gui.handleToggleSelectionForPatch,
@@ -1238,7 +1282,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleDragSelect),
- Handler: gui.handleToggleSelectRange,
+ Handler: gui.wrappedHandler(gui.handleToggleSelectRange),
Description: gui.Tr.ToggleDragSelect,
},
// Alias 'V' -> 'v'
@@ -1246,14 +1290,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleDragSelectAlt),
- Handler: gui.handleToggleSelectRange,
+ Handler: gui.wrappedHandler(gui.handleToggleSelectRange),
Description: gui.Tr.ToggleDragSelect,
},
{
ViewName: "main",
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Main.ToggleSelectHunk),
- Handler: gui.handleToggleSelectHunk,
+ Handler: gui.wrappedHandler(gui.handleToggleSelectHunk),
Description: gui.Tr.ToggleSelectHunk,
},
{
@@ -1261,7 +1305,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY, MAIN_STAGING_CONTEXT_KEY},
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
- Handler: gui.handleMouseDown,
+ Handler: gui.handleLBLMouseDown,
},
{
ViewName: "main",
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index 5b3d0da4d..3f02bfe85 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -317,6 +317,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
listContextState.view.SetOnSelectItem(gui.onSelectItemWrapper(listContextState.listContext.onSearchSelect))
}
+ gui.getMainView().SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
+
mainViewWidth, mainViewHeight := gui.getMainView().Size()
if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight {
gui.State.PrevMainWidth = mainViewWidth
diff --git a/pkg/gui/line_by_line_panel.go b/pkg/gui/line_by_line_panel.go
index e06a685a6..bd74d0fc6 100644
--- a/pkg/gui/line_by_line_panel.go
+++ b/pkg/gui/line_by_line_panel.go
@@ -25,6 +25,9 @@ const (
// 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
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
patchParser, err := patch.NewPatchParser(gui.Log, diff)
@@ -98,26 +101,32 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
return false, nil
}
-func (gui *Gui) handleSelectPrevLine(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(-1)
+func (gui *Gui) handleSelectPrevLine() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ return gui.LBLCycleLine(-1)
+ })
}
-func (gui *Gui) handleSelectNextLine(g *gocui.Gui, v *gocui.View) error {
- return gui.handleCycleLine(+1)
+func (gui *Gui) handleSelectNextLine() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ return gui.LBLCycleLine(+1)
+ })
}
-func (gui *Gui) handleSelectPrevHunk(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.LineByLine
- newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1)
+func (gui *Gui) handleSelectPrevHunk() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ 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 {
- state := gui.State.Panels.LineByLine
- newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1)
+func (gui *Gui) handleSelectNextHunk() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1)
- return gui.selectNewHunk(newHunk)
+ return gui.selectNewHunk(newHunk)
+ })
}
func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
@@ -136,7 +145,7 @@ func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
return gui.focusSelection(true)
}
-func (gui *Gui) handleCycleLine(change int) error {
+func (gui *Gui) LBLCycleLine(change int) error {
state := gui.State.Panels.LineByLine
if state.SelectMode == HUNK {
@@ -144,10 +153,10 @@ func (gui *Gui) handleCycleLine(change int) error {
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
if newSelectedLineIdx < 0 {
@@ -176,52 +185,54 @@ func (gui *Gui) handleSelectNewLine(newSelectedLineIdx int) error {
return gui.focusSelection(false)
}
-func (gui *Gui) handleMouseDown(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.LineByLine
-
- if gui.popupPanelFocused() {
- return nil
- }
+func (gui *Gui) handleLBLMouseDown(g *gocui.Gui, v *gocui.View) error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ if gui.popupPanelFocused() {
+ return nil
+ }
- newSelectedLineIdx := v.SelectedLineIdx()
- state.FirstLineIdx = newSelectedLineIdx
- state.LastLineIdx = newSelectedLineIdx
+ newSelectedLineIdx := v.SelectedLineIdx()
+ state.FirstLineIdx = newSelectedLineIdx
+ state.LastLineIdx = newSelectedLineIdx
- 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 {
- if gui.popupPanelFocused() {
- return nil
- }
+ return gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
+ if gui.popupPanelFocused() {
+ return nil
+ }
- return gui.handleSelectNewLine(v.SelectedLineIdx())
+ return gui.LBLSelectLine(v.SelectedLineIdx())
+ })
}
func (gui *Gui) handleMouseScrollUp(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.LineByLine
-
- if gui.popupPanelFocused() {
- return nil
- }
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ if gui.popupPanelFocused() {
+ 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 {
- state := gui.State.Panels.LineByLine
-
- if gui.popupPanelFocused() {
- return nil
- }
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ if gui.popupPanelFocused() {
+ return nil
+ }
- state.SelectMode = LINE
+ state.SelectMode = LINE
- return gui.handleCycleLine(1)
+ return gui.LBLCycleLine(1)
+ })
}
func (gui *Gui) getSelectedCommitFileName() string {
@@ -297,65 +308,123 @@ func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
return nil
}
-func (gui *Gui) handleToggleSelectRange(g *gocui.Gui, v *gocui.View) error {
- state := gui.State.Panels.LineByLine
- if state.SelectMode == RANGE {
- state.SelectMode = LINE
- } else {
- state.SelectMode = RANGE
- }
- state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
+func (gui *Gui) handleToggleSelectRange() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ if state.SelectMode == RANGE {
+ state.SelectMode = LINE
+ } else {
+ state.SelectMode = RANGE
+ }
+ 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 {
- state := gui.State.Panels.LineByLine
-
- if state.SelectMode == HUNK {
- state.SelectMode = LINE
- state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
- } else {
- state.SelectMode = HUNK
- selectedHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
- state.FirstLineIdx, state.LastLineIdx = selectedHunk.FirstLineIdx, selectedHunk.LastLineIdx()
- }
+func (gui *Gui) handleToggleSelectHunk() error {
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ if state.SelectMode == HUNK {
+ state.SelectMode = LINE
+ state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
+ } else {
+ state.SelectMode = HUNK
+ selectedHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
+ state.FirstLineIdx, state.LastLineIdx = selectedHunk.FirstLineIdx, selectedHunk.LastLineIdx()
+ }
- if err := gui.refreshMainViewForLineByLine(); err != nil {
- return err
- }
+ if err := gui.refreshMainViewForLineByLine(); err != nil {
+ return err
+ }
- return gui.focusSelection(state.SelectMode == HUNK)
+ return gui.focusSelection(state.SelectMode == HUNK)
+ })
}
-func (gui *Gui) handleEscapeLineByLinePanel() {
- gui.State.Panels.LineByLine = nil
+func (gui *Gui) escapeLineByLinePanel() {
+ gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
+ gui.State.Panels.LineByLine = nil
+ return nil
+ })
}
func (gui *Gui) handleOpenFileAtLine() error {
- // again, would be good to use inheritance here (or maybe even composition)
- var filename string
- switch gui.State.MainContext {
- case gui.Contexts.PatchBuilding.Context.GetKey():
- filename = gui.getSelectedCommitFileName()
- case gui.Contexts.Staging.Context.GetKey():
- file := gui.getSelectedFile()
- if file == nil {
- return nil
+ return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
+ // again, would be good to use inheritance here (or maybe even composition)
+ var filename string
+ switch gui.State.MainContext {
+ case gui.Contexts.PatchBuilding.Context.GetKey():
+ filename = gui.getSelectedCommitFileName()
+ case gui.Contexts.Staging.Context.GetKey():
+ file := gui.getSelectedFile()
+ if file == nil {
+ return nil
+ }
+ filename = file.Name
+ default:
+ return errors.Errorf("unknown main context: %s", gui.State.MainContext)
}
- filename = file.Name
- default:
- return errors.Errorf("unknown main context: %s", gui.State.MainContext)
- }
+ // 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)
+ lineNumber := selectedHunk.LineNumberOfLine(state.SelectedLineIdx)
+ filenameWithLineNum := fmt.Sprintf("%s:%d", filename, lineNumber)
+ if err := gui.OSCommand.OpenFile(filenameWithLineNum); err != nil {
+ return err
+ }
+
+ 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
- // 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)
- lineNumber := selectedHunk.LineNumberOfLine(state.SelectedLineIdx)
- filenameWithLineNum := fmt.Sprintf("%s:%d", filename, lineNumber)
- if err := gui.OSCommand.OpenFile(filenameWithLineNum); err != nil {
- return err
+ 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 nil
+ return f(state)
}
diff --git a/pkg/gui/list_context.go b/pkg/gui/list_context.go
index 83a4072f1..529a7bd02 100644
--- a/pkg/gui/list_context.go
+++ b/pkg/gui/list_context.go
@@ -186,11 +186,8 @@ func (lc *ListContext) handleNextPage(g *gocui.Gui, v *gocui.View) error {
if err != nil {
return nil
}
- _, height := view.Size()
- delta := height - 1
- if delta == 0 {
- delta = 1
- }
+ delta := lc.Gui.pageDelta(view)
+
return lc.handleLineChange(delta)
}
@@ -207,11 +204,9 @@ func (lc *ListContext) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
if err != nil {
return nil
}
- _, height := view.Size()
- delta := height - 1
- if delta == 0 {
- delta = 1
- }
+
+ delta := lc.Gui.pageDelta(view)
+
return lc.handleLineChange(-delta)
}
diff --git a/pkg/gui/patch_building_panel.go b/pkg/gui/patch_building_panel.go
index c69b22476..f246ce92d 100644
--- a/pkg/gui/patch_building_panel.go
+++ b/pkg/gui/patch_building_panel.go
@@ -92,7 +92,7 @@ func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error
}
func (gui *Gui) handleEscapePatchBuildingPanel() error {
- gui.handleEscapeLineByLinePanel()
+ gui.escapeLineByLinePanel()
if gui.GitCommand.PatchManager.IsEmpty() {
gui.GitCommand.PatchManager.Reset()
diff --git a/pkg/gui/remotes_panel.go b/pkg/gui/remotes_panel.go
index c0258b890..7e321d81f 100644
--- a/pkg/gui/remotes_panel.go
+++ b/pkg/gui/remotes_panel.go
@@ -158,8 +158,8 @@ func (gui *Gui) handleFetchRemote(g *gocui.Gui, v *gocui.View) error {
}
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
- gui.State.FetchMutex.Lock()
- defer gui.State.FetchMutex.Unlock()
+ gui.State.Mutexes.FetchMutex.Lock()
+ defer gui.State.Mutexes.FetchMutex.Unlock()
// TODO: test this
err := gui.GitCommand.FetchRemote(remote.Name, gui.promptUserForCredential)
diff --git a/pkg/gui/staging_panel.go b/pkg/gui/staging_panel.go
index d07ba463c..e2010b980 100644
--- a/pkg/gui/staging_panel.go
+++ b/pkg/gui/staging_panel.go
@@ -84,7 +84,7 @@ func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleStagingEscape() error {
- gui.handleEscapeLineByLinePanel()
+ gui.escapeLineByLinePanel()
return gui.switchContext(gui.Contexts.Files.Context)
}
diff --git a/pkg/gui/status_panel.go b/pkg/gui/status_panel.go
index 26cad012d..3abf0829d 100644
--- a/pkg/gui/status_panel.go
+++ b/pkg/gui/status_panel.go
@@ -12,8 +12,8 @@ import (
// never call this on its own, it should only be called from within refreshCommits()
func (gui *Gui) refreshStatus() {
- gui.State.RefreshingStatusMutex.Lock()
- defer gui.State.RefreshingStatusMutex.Unlock()
+ gui.State.Mutexes.RefreshingStatusMutex.Lock()
+ defer gui.State.Mutexes.RefreshingStatusMutex.Unlock()
currentBranch := gui.currentBranch()
if currentBranch == nil {
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index 67abfbc02..06de4179b 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -433,3 +433,15 @@ func (gui *Gui) handlePrevTab(g *gocui.Gui, v *gocui.View) error {
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
+}