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

fix mutex deadlock

This commit is contained in:
Jesse Duffield
2020-10-08 08:01:04 +11:00
parent 9011271a01
commit 4c9ec88be5
8 changed files with 149 additions and 152 deletions

View File

@ -77,7 +77,7 @@ func (gui *Gui) handleDiscardOldFileChange(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) refreshCommitFilesView() error {
if err := gui.refreshPatchBuildingPanel(-1); err != nil {
if err := gui.handleRefreshPatchBuildingPanel(-1); err != nil {
return err
}
@ -179,7 +179,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
if err := gui.switchContext(gui.Contexts.PatchBuilding.Context); err != nil {
return err
}
return gui.refreshPatchBuildingPanel(selectedLineIdx)
return gui.handleRefreshPatchBuildingPanel(selectedLineIdx)
}
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {

View File

@ -185,7 +185,7 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
}
gui.switchContext(gui.Contexts.Staging.Context)
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx) // TODO: check if this is broken, try moving into context code
return gui.handleRefreshStagingPanel(forceSecondaryFocused, selectedLineIdx) // TODO: check if this is broken, try moving into context code
}
func (gui *Gui) handleFilePress() error {

View File

@ -142,7 +142,7 @@ type IListPanelState interface {
// for now the staging panel state, unlike the other panel states, is going to be
// non-mutative, so that we don't accidentally end up
// with mismatches of data. We might change this in the future
type lineByLinePanelState struct {
type lBlPanelState struct {
SelectedLineIdx int
FirstLineIdx int
LastLineIdx int
@ -234,7 +234,7 @@ type panelStates struct {
SubCommits *subCommitPanelState
Stash *stashPanelState
Menu *menuPanelState
LineByLine *lineByLinePanelState
LineByLine *lBlPanelState
Merging *mergingPanelState
CommitFiles *commitFilesPanelState
Submodules *submodulePanelState

View File

@ -1112,21 +1112,21 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "main",
Contexts: []string{MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.Select),
Handler: gui.handleToggleStagedSelection,
Handler: gui.wrappedHandler(gui.handleToggleStagedSelection),
Description: gui.Tr.StageSelection,
},
{
ViewName: "main",
Contexts: []string{MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.Remove),
Handler: gui.handleResetSelection,
Handler: gui.wrappedHandler(gui.handleResetSelection),
Description: gui.Tr.ResetSelection,
},
{
ViewName: "main",
Contexts: []string{MAIN_STAGING_CONTEXT_KEY},
Key: gui.getKey(config.Universal.TogglePanel),
Handler: gui.handleTogglePanel,
Handler: gui.wrappedHandler(gui.handleTogglePanel),
Description: gui.Tr.TogglePanel,
},
{

View File

@ -24,12 +24,7 @@ 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.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
state := gui.State.Panels.LineByLine
func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int, state *lBlPanelState) (bool, error) {
patchParser, err := patch.NewPatchParser(gui.Log, diff)
if err != nil {
return false, nil
@ -66,7 +61,7 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx
}
gui.State.Panels.LineByLine = &lineByLinePanelState{
state = &lBlPanelState{
PatchParser: patchParser,
SelectedLineIdx: selectedLineIdx,
SelectMode: selectMode,
@ -75,12 +70,13 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
Diff: diff,
SecondaryFocused: secondaryFocused,
}
gui.State.Panels.LineByLine = state
if err := gui.refreshMainViewForLineByLine(); err != nil {
if err := gui.refreshMainViewForLineByLine(state); err != nil {
return false, err
}
if err := gui.focusSelection(selectMode == HUNK); err != nil {
if err := gui.focusSelection(selectMode == HUNK, state); err != nil {
return false, err
}
@ -102,35 +98,34 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second
}
func (gui *Gui) handleSelectPrevLine() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.LBLCycleLine(-1)
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
return gui.LBLCycleLine(-1, state)
})
}
func (gui *Gui) handleSelectNextLine() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.LBLCycleLine(+1)
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
return gui.LBLCycleLine(+1, state)
})
}
func (gui *Gui) handleSelectPrevHunk() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1)
return gui.selectNewHunk(newHunk)
return gui.selectNewHunk(newHunk, state)
})
}
func (gui *Gui) handleSelectNextHunk() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1)
return gui.selectNewHunk(newHunk)
return gui.selectNewHunk(newHunk, state)
})
}
func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
state := gui.State.Panels.LineByLine
func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk, state *lBlPanelState) error {
state.SelectedLineIdx = state.PatchParser.GetNextStageableLineIndex(newHunk.FirstLineIdx)
if state.SelectMode == HUNK {
state.FirstLineIdx, state.LastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx()
@ -138,27 +133,23 @@ func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk) error {
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
}
if err := gui.refreshMainViewForLineByLine(); err != nil {
if err := gui.refreshMainViewForLineByLine(state); err != nil {
return err
}
return gui.focusSelection(true)
return gui.focusSelection(true, state)
}
func (gui *Gui) LBLCycleLine(change int) error {
state := gui.State.Panels.LineByLine
func (gui *Gui) LBLCycleLine(change int, state *lBlPanelState) error {
if state.SelectMode == HUNK {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, change)
return gui.selectNewHunk(newHunk)
return gui.selectNewHunk(newHunk, state)
}
return gui.LBLSelectLine(state.SelectedLineIdx + change)
return gui.LBLSelectLine(state.SelectedLineIdx+change, state)
}
func (gui *Gui) LBLSelectLine(newSelectedLineIdx int) error {
state := gui.State.Panels.LineByLine
func (gui *Gui) LBLSelectLine(newSelectedLineIdx int, state *lBlPanelState) error {
if newSelectedLineIdx < 0 {
newSelectedLineIdx = 0
} else if newSelectedLineIdx > len(state.PatchParser.PatchLines)-1 {
@ -178,15 +169,15 @@ func (gui *Gui) LBLSelectLine(newSelectedLineIdx int) error {
state.FirstLineIdx = state.SelectedLineIdx
}
if err := gui.refreshMainViewForLineByLine(); err != nil {
if err := gui.refreshMainViewForLineByLine(state); err != nil {
return err
}
return gui.focusSelection(false)
return gui.focusSelection(false, state)
}
func (gui *Gui) handleLBLMouseDown(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if gui.popupPanelFocused() {
return nil
}
@ -197,41 +188,41 @@ func (gui *Gui) handleLBLMouseDown(g *gocui.Gui, v *gocui.View) error {
state.SelectMode = RANGE
return gui.LBLSelectLine(newSelectedLineIdx)
return gui.LBLSelectLine(newSelectedLineIdx, state)
})
}
func (gui *Gui) handleMouseDrag(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if gui.popupPanelFocused() {
return nil
}
return gui.LBLSelectLine(v.SelectedLineIdx())
return gui.LBLSelectLine(v.SelectedLineIdx(), state)
})
}
func (gui *Gui) handleMouseScrollUp(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if gui.popupPanelFocused() {
return nil
}
state.SelectMode = LINE
return gui.LBLCycleLine(-1)
return gui.LBLCycleLine(-1, state)
})
}
func (gui *Gui) handleMouseScrollDown(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if gui.popupPanelFocused() {
return nil
}
state.SelectMode = LINE
return gui.LBLCycleLine(1)
return gui.LBLCycleLine(1, state)
})
}
@ -239,9 +230,7 @@ func (gui *Gui) getSelectedCommitFileName() string {
return gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLineIdx].Name
}
func (gui *Gui) refreshMainViewForLineByLine() error {
state := gui.State.Panels.LineByLine
func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) error {
var includedLineIndices []int
// I'd prefer not to have knowledge of contexts using this file but I'm not sure
// how to get around this
@ -269,9 +258,8 @@ func (gui *Gui) refreshMainViewForLineByLine() error {
// focusSelection works out the best focus for the staging panel given the
// selected line and size of the hunk
func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
func (gui *Gui) focusSelection(includeCurrentHunk bool, state *lBlPanelState) error {
stagingView := gui.getMainView()
state := gui.State.Panels.LineByLine
_, viewHeight := stagingView.Size()
bufferHeight := viewHeight - 1
@ -309,7 +297,7 @@ func (gui *Gui) focusSelection(includeCurrentHunk bool) error {
}
func (gui *Gui) handleToggleSelectRange() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if state.SelectMode == RANGE {
state.SelectMode = LINE
} else {
@ -317,12 +305,12 @@ func (gui *Gui) handleToggleSelectRange() error {
}
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
return gui.refreshMainViewForLineByLine()
return gui.refreshMainViewForLineByLine(state)
})
}
func (gui *Gui) handleToggleSelectHunk() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if state.SelectMode == HUNK {
state.SelectMode = LINE
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
@ -332,23 +320,20 @@ func (gui *Gui) handleToggleSelectHunk() error {
state.FirstLineIdx, state.LastLineIdx = selectedHunk.FirstLineIdx, selectedHunk.LastLineIdx()
}
if err := gui.refreshMainViewForLineByLine(); err != nil {
if err := gui.refreshMainViewForLineByLine(state); err != nil {
return err
}
return gui.focusSelection(state.SelectMode == HUNK)
return gui.focusSelection(state.SelectMode == HUNK, state)
})
}
func (gui *Gui) escapeLineByLinePanel() {
_ = gui.withLBLActiveCheck(func(*lineByLinePanelState) error {
gui.State.Panels.LineByLine = nil
return nil
})
}
func (gui *Gui) handleOpenFileAtLine() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
// again, would be good to use inheritance here (or maybe even composition)
var filename string
switch gui.State.MainContext {
@ -377,47 +362,48 @@ func (gui *Gui) handleOpenFileAtLine() error {
}
func (gui *Gui) handleLineByLineNextPage() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
newSelectedLineIdx := state.SelectedLineIdx + gui.pageDelta(gui.getMainView())
return gui.lineByLineNavigateTo(newSelectedLineIdx)
return gui.lineByLineNavigateTo(newSelectedLineIdx, state)
})
}
func (gui *Gui) handleLineByLinePrevPage() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
newSelectedLineIdx := state.SelectedLineIdx - gui.pageDelta(gui.getMainView())
return gui.lineByLineNavigateTo(newSelectedLineIdx)
return gui.lineByLineNavigateTo(newSelectedLineIdx, state)
})
}
func (gui *Gui) handleLineByLineGotoBottom() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
newSelectedLineIdx := len(state.PatchParser.PatchLines) - 1
return gui.lineByLineNavigateTo(newSelectedLineIdx)
return gui.lineByLineNavigateTo(newSelectedLineIdx, state)
})
}
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)
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
return gui.lineByLineNavigateTo(0, state)
})
}
func (gui *Gui) lineByLineNavigateTo(selectedLineIdx int) error {
state := gui.State.Panels.LineByLine
state.SelectMode = LINE
return gui.LBLSelectLine(selectedLineIdx)
func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
return gui.lineByLineNavigateTo(selectedLineIdx, state)
})
}
func (gui *Gui) withLBLActiveCheck(f func(*lineByLinePanelState) error) error {
func (gui *Gui) lineByLineNavigateTo(selectedLineIdx int, state *lBlPanelState) error {
state.SelectMode = LINE
return gui.LBLSelectLine(selectedLineIdx, state)
}
func (gui *Gui) withLBLActiveCheck(f func(*lBlPanelState) error) error {
gui.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock()

View File

@ -17,7 +17,7 @@ func (gui *Gui) getFromAndReverseArgsForDiff(to string) (string, bool) {
return from, reverse
}
func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int, state *lBlPanelState) error {
if !gui.GitCommand.PatchManager.Active() {
return gui.handleEscapePatchBuildingPanel()
}
@ -45,7 +45,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
return err
}
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx)
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx, state)
if err != nil {
return err
}
@ -57,8 +57,15 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
return nil
}
func (gui *Gui) handleRefreshPatchBuildingPanel(selectedLineIdx int) error {
gui.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
return gui.refreshPatchBuildingPanel(selectedLineIdx, gui.State.Panels.LineByLine)
}
func (gui *Gui) handleToggleSelectionForPatch() error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
err := gui.withLBLActiveCheck(func(state *lBlPanelState) error {
toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange
filename := gui.getSelectedCommitFileName()
includedLineIndices, err := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
@ -81,16 +88,18 @@ func (gui *Gui) handleToggleSelectionForPatch() error {
gui.Log.Error(err)
}
return nil
})
if err != nil {
return err
}
if err := gui.refreshCommitFilesView(); err != nil {
return err
}
if err := gui.refreshPatchBuildingPanel(-1); err != nil {
return err
}
return nil
})
}
func (gui *Gui) handleEscapePatchBuildingPanel() error {

View File

@ -7,17 +7,11 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/patch"
)
func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int, state *lBlPanelState) error {
gui.splitMainPanel(true)
state := gui.State.Panels.LineByLine
file := gui.getSelectedFile()
if file == nil {
return gui.handleStagingEscape()
}
if !file.HasUnstagedChanges && !file.HasStagedChanges {
if file == nil || (!file.HasUnstagedChanges && !file.HasStagedChanges) {
return gui.handleStagingEscape()
}
@ -56,7 +50,7 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
diff, secondaryDiff = secondaryDiff, diff
}
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx)
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx, state)
if err != nil {
return err
}
@ -69,17 +63,24 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
}
func (gui *Gui) handleTogglePanelClick(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
state.SecondaryFocused = !state.SecondaryFocused
return gui.refreshStagingPanel(false, v.SelectedLineIdx())
return gui.refreshStagingPanel(false, v.SelectedLineIdx(), state)
})
}
func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
func (gui *Gui) handleRefreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
gui.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock()
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx, gui.State.Panels.LineByLine)
}
func (gui *Gui) handleTogglePanel() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
state.SecondaryFocused = !state.SecondaryFocused
return gui.refreshStagingPanel(false, -1)
return gui.refreshStagingPanel(false, -1, state)
})
}
@ -89,17 +90,17 @@ func (gui *Gui) handleStagingEscape() error {
return gui.switchContext(gui.Contexts.Files.Context)
}
func (gui *Gui) handleToggleStagedSelection(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
return gui.applySelection(state.SecondaryFocused)
func (gui *Gui) handleToggleStagedSelection() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
return gui.applySelection(state.SecondaryFocused, state)
})
}
func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
func (gui *Gui) handleResetSelection() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if state.SecondaryFocused {
// for backwards compatibility
return gui.applySelection(true)
return gui.applySelection(true, state)
}
if !gui.Config.GetUserConfig().Gui.SkipUnstageLineWarning {
@ -108,24 +109,25 @@ func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
prompt: gui.Tr.UnstageLinesPrompt,
handlersManageFocus: true,
handleConfirm: func() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error {
if err := gui.switchContext(gui.Contexts.Staging.Context); err != nil {
return err
}
return gui.applySelection(true)
return gui.applySelection(true, state)
})
},
handleClose: func() error {
return gui.switchContext(gui.Contexts.Staging.Context)
},
})
} else {
return gui.applySelection(true)
return gui.applySelection(true, state)
}
})
}
func (gui *Gui) applySelection(reverse bool) error {
return gui.withLBLActiveCheck(func(state *lineByLinePanelState) error {
func (gui *Gui) applySelection(reverse bool, state *lBlPanelState) error {
file := gui.getSelectedFile()
if file == nil {
return nil
@ -155,9 +157,8 @@ func (gui *Gui) applySelection(reverse bool) error {
if err := gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}); err != nil {
return err
}
if err := gui.refreshStagingPanel(false, -1); err != nil {
if err := gui.refreshStagingPanel(false, -1, state); err != nil {
return err
}
return nil
})
}

View File

@ -405,7 +405,8 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
// secondaryViewFocused tells us whether it appears that the secondary view is focused. The view is actually never focused for real: we just swap the main and secondary views and then you're still focused on the main view so that we can give you access to all its keybindings for free. I will probably regret this design decision soon enough.
func (gui *Gui) secondaryViewFocused() bool {
return gui.State.Panels.LineByLine != nil && gui.State.Panels.LineByLine.SecondaryFocused
state := gui.State.Panels.LineByLine
return state != nil && state.SecondaryFocused
}
func (gui *Gui) clearEditorView(v *gocui.View) {