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", @@ -1227,6 +1227,50 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleFileOpen, 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", Contexts: []string{MAIN_PATCH_BUILDING_CONTEXT_KEY}, @@ -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 +func (gui *Gui) handleLBLMouseDown(g *gocui.Gui, v *gocui.View) error { + return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error { + if gui.popupPanelFocused() { + return nil + } - 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 + return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error { + if gui.popupPanelFocused() { + return nil + } - 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 + return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error { + if gui.popupPanelFocused() { + return nil + } - 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 - - 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 +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 - } 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 - } - - return gui.focusSelection(state.SelectMode == HUNK) + return gui.refreshMainViewForLineByLine() + }) } -func (gui *Gui) handleEscapeLineByLinePanel() { - gui.State.Panels.LineByLine = nil +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 + } + + return gui.focusSelection(state.SelectMode == HUNK) + }) +} + +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 + 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 - // 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 + 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 +}