diff --git a/pkg/commands/patch/patch_manager.go b/pkg/commands/patch/patch_manager.go index 3d6a076e8..6615d831f 100644 --- a/pkg/commands/patch/patch_manager.go +++ b/pkg/commands/patch/patch_manager.go @@ -182,11 +182,8 @@ func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse b if plain { return patch } - parser, err := NewPatchParser(p.Log, patch) - if err != nil { - // swallowing for now - return "" - } + parser := NewPatchParser(p.Log, patch) + // not passing included lines because we don't want to see them in the secondary panel return parser.Render(-1, -1, nil) } diff --git a/pkg/commands/patch/patch_parser.go b/pkg/commands/patch/patch_parser.go index 00eafd995..a5a94964f 100644 --- a/pkg/commands/patch/patch_parser.go +++ b/pkg/commands/patch/patch_parser.go @@ -39,11 +39,8 @@ type PatchParser struct { } // NewPatchParser builds a new branch list builder -func NewPatchParser(log *logrus.Entry, patch string) (*PatchParser, error) { - hunkStarts, stageableLines, patchLines, err := parsePatch(patch) - if err != nil { - return nil, err - } +func NewPatchParser(log *logrus.Entry, patch string) *PatchParser { + hunkStarts, stageableLines, patchLines := parsePatch(patch) patchHunks := GetHunksFromDiff(patch) @@ -53,7 +50,7 @@ func NewPatchParser(log *logrus.Entry, patch string) (*PatchParser, error) { StageableLines: stageableLines, PatchLines: patchLines, PatchHunks: patchHunks, - }, nil + } } // GetHunkContainingLine takes a line index and an offset and finds the hunk @@ -139,7 +136,7 @@ func coloredString(colorAttr color.Attribute, str string, selected bool, include return utils.ColoredStringDirect(str[:1], clIncluded) + utils.ColoredStringDirect(str[1:], cl) } -func parsePatch(patch string) ([]int, []int, []*PatchLine, error) { +func parsePatch(patch string) ([]int, []int, []*PatchLine) { lines := strings.Split(patch, "\n") hunkStarts := []int{} stageableLines := []int{} @@ -185,7 +182,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine, error) { } patchLines[index] = &PatchLine{Kind: lineKind, Content: line} } - return hunkStarts, stageableLines, patchLines, nil + return hunkStarts, stageableLines, patchLines } // Render returns the coloured string of the diff with any selected lines highlighted diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index c29fcb99c..de3b319c0 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -18,9 +18,9 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/commands/patch" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/filetree" + "github.com/jesseduffield/lazygit/pkg/gui/lbl" "github.com/jesseduffield/lazygit/pkg/gui/modes/filtering" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" @@ -133,13 +133,8 @@ func (h *listPanelState) GetSelectedLineIdx() int { // 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 lBlPanelState struct { - SelectedLineIdx int - FirstLineIdx int - LastLineIdx int - Diff string - PatchParser *patch.PatchParser - SelectMode SelectMode +type LblPanelState struct { + *lbl.State SecondaryFocused bool // this is for if we show the left or right panel } @@ -230,7 +225,7 @@ type panelStates struct { SubCommits *subCommitPanelState Stash *stashPanelState Menu *menuPanelState - LineByLine *lBlPanelState + LineByLine *LblPanelState Merging *mergingPanelState CommitFiles *commitFilesPanelState Submodules *submodulePanelState diff --git a/pkg/gui/lbl/line_by_line.go b/pkg/gui/lbl/line_by_line.go new file mode 100644 index 000000000..87a70136e --- /dev/null +++ b/pkg/gui/lbl/line_by_line.go @@ -0,0 +1,191 @@ +package lbl + +import ( + "github.com/jesseduffield/lazygit/pkg/commands/patch" + "github.com/sirupsen/logrus" +) + +type State struct { + selectedLineIdx int + rangeStartLineIdx int + diff string + patchParser *patch.PatchParser + selectMode selectMode +} + +// these represent what select mode we're in +type selectMode int + +const ( + LINE selectMode = iota + RANGE + HUNK +) + +func NewState(diff string, selectedLineIdx int, oldState *State, log *logrus.Entry) *State { + patchParser := patch.NewPatchParser(log, diff) + + if len(patchParser.StageableLines) == 0 { + return nil + } + + rangeStartLineIdx := 0 + if oldState != nil { + rangeStartLineIdx = oldState.rangeStartLineIdx + } + + selectMode := LINE + // if we have clicked from the outside to focus the main view we'll pass in a non-negative line index so that we can instantly select that line + if selectedLineIdx >= 0 { + selectMode = RANGE + rangeStartLineIdx = selectedLineIdx + } else if oldState != nil { + // if we previously had a selectMode of RANGE, we want that to now be line again + if oldState.selectMode == HUNK { + selectMode = HUNK + } + selectedLineIdx = patchParser.GetNextStageableLineIndex(oldState.selectedLineIdx) + } else { + selectedLineIdx = patchParser.StageableLines[0] + } + + return &State{ + patchParser: patchParser, + selectedLineIdx: selectedLineIdx, + selectMode: selectMode, + rangeStartLineIdx: rangeStartLineIdx, + diff: diff, + } +} + +func (s *State) GetSelectedLineIdx() int { + return s.selectedLineIdx +} + +func (s *State) GetDiff() string { + return s.diff +} + +func (s *State) ToggleSelectHunk() { + if s.selectMode == HUNK { + s.selectMode = LINE + } else { + s.selectMode = HUNK + } +} + +func (s *State) ToggleSelectRange() { + if s.selectMode == RANGE { + s.selectMode = LINE + } else { + s.selectMode = RANGE + s.rangeStartLineIdx = s.selectedLineIdx + } +} + +func (s *State) SelectingHunk() bool { + return s.selectMode == HUNK +} + +func (s *State) SelectingRange() bool { + return s.selectMode == RANGE +} + +func (s *State) SelectingLine() bool { + return s.selectMode == LINE +} + +func (s *State) SetLineSelectMode() { + s.selectMode = LINE +} + +func (s *State) SelectLine(newSelectedLineIdx int) { + if newSelectedLineIdx < 0 { + newSelectedLineIdx = 0 + } else if newSelectedLineIdx > len(s.patchParser.PatchLines)-1 { + newSelectedLineIdx = len(s.patchParser.PatchLines) - 1 + } + + s.selectedLineIdx = newSelectedLineIdx +} + +func (s *State) SelectNewLineForRange(newSelectedLineIdx int) { + s.rangeStartLineIdx = newSelectedLineIdx + + s.selectMode = RANGE + + s.SelectLine(newSelectedLineIdx) +} + +func (s *State) CycleSelection(forward bool) { + if s.SelectingHunk() { + s.CycleHunk(forward) + } else { + s.CycleLine(forward) + } +} + +func (s *State) CycleHunk(forward bool) { + change := 1 + if !forward { + change = -1 + } + + newHunk := s.patchParser.GetHunkContainingLine(s.selectedLineIdx, change) + s.selectedLineIdx = s.patchParser.GetNextStageableLineIndex(newHunk.FirstLineIdx) +} + +func (s *State) CycleLine(forward bool) { + change := 1 + if !forward { + change = -1 + } + + s.SelectLine(s.selectedLineIdx + change) +} + +func (s *State) CurrentHunk() *patch.PatchHunk { + return s.patchParser.GetHunkContainingLine(s.selectedLineIdx, 0) +} + +func (s *State) SelectedRange() (int, int) { + switch s.selectMode { + case HUNK: + hunk := s.CurrentHunk() + return hunk.FirstLineIdx, hunk.LastLineIdx() + case RANGE: + if s.rangeStartLineIdx > s.selectedLineIdx { + return s.selectedLineIdx, s.rangeStartLineIdx + } else { + return s.rangeStartLineIdx, s.selectedLineIdx + } + case LINE: + return s.selectedLineIdx, s.selectedLineIdx + default: + // should never happen + return 0, 0 + } +} + +func (s *State) CurrentLineNumber() int { + return s.CurrentHunk().LineNumberOfLine(s.selectedLineIdx) +} + +func (s *State) AdjustSelectedLineIdx(change int) { + s.SelectLine(s.selectedLineIdx + change) +} + +func (s *State) RenderForLineIndices(includedLineIndices []int) string { + firstLineIdx, lastLineIdx := s.SelectedRange() + return s.patchParser.Render(firstLineIdx, lastLineIdx, includedLineIndices) +} + +func (s *State) SelectBottom() { + s.SetLineSelectMode() + s.SelectLine(len(s.patchParser.PatchLines) - 1) +} + +func (s *State) SelectTop() { + s.SetLineSelectMode() + s.SelectLine(0) +} diff --git a/pkg/gui/line_by_line_panel.go b/pkg/gui/line_by_line_panel.go index 0267a091d..ff2d6d5f8 100644 --- a/pkg/gui/line_by_line_panel.go +++ b/pkg/gui/line_by_line_panel.go @@ -6,6 +6,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/patch" + "github.com/jesseduffield/lazygit/pkg/gui/lbl" ) // Currently there are two 'pseudo-panels' that make use of this 'pseudo-panel'. @@ -15,82 +16,38 @@ import ( // staging_panel.go and patch_building_panel.go have functions specific to their // use cases -// these represent what select mode we're in -type SelectMode int - -const ( - LINE SelectMode = iota - RANGE - HUNK -) - // 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, state *lBlPanelState) (bool, error) { +func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int) (bool, error) { gui.splitMainPanel(true) - patchParser, err := patch.NewPatchParser(gui.Log, diff) - if err != nil { - return false, nil + var oldState *lbl.State + if gui.State.Panels.LineByLine != nil { + oldState = gui.State.Panels.LineByLine.State } - if len(patchParser.StageableLines) == 0 { + state := lbl.NewState(diff, selectedLineIdx, oldState, gui.Log) + if state == nil { return true, nil } - var firstLineIdx int - var lastLineIdx int - selectMode := LINE - // if we have clicked from the outside to focus the main view we'll pass in a non-negative line index so that we can instantly select that line - if selectedLineIdx >= 0 { - selectMode = RANGE - firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx - } else if state != nil { - if state.SelectMode == HUNK { - // this is tricky: we need to find out which hunk we just staged based on our old `state.PatchParser` (as opposed to the new `patchParser`) - // we do this by getting the first line index of the original hunk, then - // finding the next stageable line, then getting its containing hunk - // in the new diff - selectMode = HUNK - prevNewHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0) - selectedLineIdx = patchParser.GetNextStageableLineIndex(prevNewHunk.FirstLineIdx) - newHunk := patchParser.GetHunkContainingLine(selectedLineIdx, 0) - firstLineIdx, lastLineIdx = newHunk.FirstLineIdx, newHunk.LastLineIdx() - } else { - selectedLineIdx = patchParser.GetNextStageableLineIndex(state.SelectedLineIdx) - firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx - } - } else { - selectedLineIdx = patchParser.StageableLines[0] - firstLineIdx, lastLineIdx = selectedLineIdx, selectedLineIdx - } - - state = &lBlPanelState{ - PatchParser: patchParser, - SelectedLineIdx: selectedLineIdx, - SelectMode: selectMode, - FirstLineIdx: firstLineIdx, - LastLineIdx: lastLineIdx, - Diff: diff, + gui.State.Panels.LineByLine = &LblPanelState{ + State: state, SecondaryFocused: secondaryFocused, } - gui.State.Panels.LineByLine = state - if err := gui.refreshMainViewForLineByLine(state); err != nil { + if err := gui.refreshMainViewForLineByLine(gui.State.Panels.LineByLine); err != nil { return false, err } - if err := gui.focusSelection(selectMode == HUNK, state); err != nil { + if err := gui.focusSelection(gui.State.Panels.LineByLine); err != nil { return false, err } gui.Views.Secondary.Highlight = true gui.Views.Secondary.Wrap = false - secondaryPatchParser, err := patch.NewPatchParser(gui.Log, secondaryDiff) - if err != nil { - return false, nil - } + secondaryPatchParser := patch.NewPatchParser(gui.Log, secondaryDiff) gui.setViewContent(gui.Views.Secondary, secondaryPatchParser.Render(-1, -1, nil)) @@ -98,107 +55,66 @@ func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, second } func (gui *Gui) handleSelectPrevLine() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - return gui.LBLCycleLine(-1, state) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.CycleSelection(false) + + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleSelectNextLine() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - return gui.LBLCycleLine(+1, state) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.CycleSelection(true) + + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleSelectPrevHunk() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.CycleHunk(false) - return gui.selectNewHunk(newHunk, state) + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleSelectNextHunk() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.CycleHunk(true) - return gui.selectNewHunk(newHunk, state) + return gui.refreshAndFocusLblPanel(state) }) } -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() - } else { - state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx - } - +func (gui *Gui) refreshAndFocusLblPanel(state *LblPanelState) error { if err := gui.refreshMainViewForLineByLine(state); err != nil { return err } - return gui.focusSelection(true, state) -} - -func (gui *Gui) LBLCycleLine(change int, state *lBlPanelState) error { - if state.SelectMode == HUNK { - newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, change) - return gui.selectNewHunk(newHunk, state) - } - - return gui.LBLSelectLine(state.SelectedLineIdx+change, state) -} - -func (gui *Gui) LBLSelectLine(newSelectedLineIdx int, state *lBlPanelState) error { - if newSelectedLineIdx < 0 { - newSelectedLineIdx = 0 - } else if newSelectedLineIdx > len(state.PatchParser.PatchLines)-1 { - newSelectedLineIdx = len(state.PatchParser.PatchLines) - 1 - } - - state.SelectedLineIdx = newSelectedLineIdx - - if state.SelectMode == RANGE { - if state.SelectedLineIdx < state.FirstLineIdx { - state.FirstLineIdx = state.SelectedLineIdx - } else { - state.LastLineIdx = state.SelectedLineIdx - } - } else { - state.LastLineIdx = state.SelectedLineIdx - state.FirstLineIdx = state.SelectedLineIdx - } - - if err := gui.refreshMainViewForLineByLine(state); err != nil { - return err - } - - return gui.focusSelection(false, state) + return gui.focusSelection(state) } func (gui *Gui) handleLBLMouseDown() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { if gui.popupPanelFocused() { return nil } - newSelectedLineIdx := gui.Views.Main.SelectedLineIdx() - state.FirstLineIdx = newSelectedLineIdx - state.LastLineIdx = newSelectedLineIdx + state.SelectNewLineForRange(gui.Views.Main.SelectedLineIdx()) - state.SelectMode = RANGE - - return gui.LBLSelectLine(newSelectedLineIdx, state) + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleMouseDrag() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { if gui.popupPanelFocused() { return nil } - return gui.LBLSelectLine(gui.Views.Main.SelectedLineIdx(), state) + state.SelectLine(gui.Views.Main.SelectedLineIdx()) + + return gui.refreshAndFocusLblPanel(state) }) } @@ -208,7 +124,7 @@ func (gui *Gui) getSelectedCommitFileName() string { return gui.State.CommitFileManager.GetItemAtIndex(idx).GetPath() } -func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) error { +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 @@ -220,7 +136,7 @@ func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) error { return err } } - colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices) + colorDiff := state.RenderForLineIndices(includedLineIndices) gui.Views.Main.Highlight = true gui.Views.Main.Wrap = false @@ -232,21 +148,14 @@ func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) 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, state *lBlPanelState) error { +func (gui *Gui) focusSelection(state *LblPanelState) error { stagingView := gui.Views.Main _, viewHeight := stagingView.Size() bufferHeight := viewHeight - 1 _, origin := stagingView.Origin() - firstLineIdx := state.SelectedLineIdx - lastLineIdx := state.SelectedLineIdx - - if includeCurrentHunk { - hunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0) - firstLineIdx = hunk.FirstLineIdx - lastLineIdx = hunk.LastLineIdx() - } + firstLineIdx, lastLineIdx := state.SelectedRange() margin := 0 // we may want to have a margin in place to show context but right now I'm thinking we keep this at zero @@ -264,41 +173,25 @@ func (gui *Gui) focusSelection(includeCurrentHunk bool, state *lBlPanelState) er return err } - return stagingView.SetCursor(0, state.SelectedLineIdx-newOrigin) + return stagingView.SetCursor(0, state.GetSelectedLineIdx()-newOrigin) }) return nil } func (gui *Gui) handleToggleSelectRange() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - if state.SelectMode == RANGE { - state.SelectMode = LINE - } else { - state.SelectMode = RANGE - } - state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.ToggleSelectRange() return gui.refreshMainViewForLineByLine(state) }) } func (gui *Gui) handleToggleSelectHunk() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) 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() - } + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.ToggleSelectHunk() - if err := gui.refreshMainViewForLineByLine(state); err != nil { - return err - } - - return gui.focusSelection(state.SelectMode == HUNK, state) + return gui.refreshAndFocusLblPanel(state) }) } @@ -307,7 +200,7 @@ func (gui *Gui) escapeLineByLinePanel() { } func (gui *Gui) handleOpenFileAtLine() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) 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 { @@ -324,8 +217,7 @@ func (gui *Gui) handleOpenFileAtLine() error { } // 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) + lineNumber := state.CurrentLineNumber() filenameWithLineNum := fmt.Sprintf("%s:%d", filename, lineNumber) if err := gui.OSCommand.OpenFile(filenameWithLineNum); err != nil { return err @@ -336,48 +228,49 @@ func (gui *Gui) handleOpenFileAtLine() error { } func (gui *Gui) handleLineByLineNextPage() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - newSelectedLineIdx := state.SelectedLineIdx + gui.pageDelta(gui.Views.Main) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.SetLineSelectMode() + state.AdjustSelectedLineIdx(gui.pageDelta(gui.Views.Main)) - return gui.lineByLineNavigateTo(newSelectedLineIdx, state) + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleLineByLinePrevPage() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - newSelectedLineIdx := state.SelectedLineIdx - gui.pageDelta(gui.Views.Main) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.SetLineSelectMode() + state.AdjustSelectedLineIdx(-gui.pageDelta(gui.Views.Main)) - return gui.lineByLineNavigateTo(newSelectedLineIdx, state) + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleLineByLineGotoBottom() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - newSelectedLineIdx := len(state.PatchParser.PatchLines) - 1 + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.SelectBottom() - return gui.lineByLineNavigateTo(newSelectedLineIdx, state) + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handleLineByLineGotoTop() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - return gui.lineByLineNavigateTo(0, state) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.SelectTop() + + return gui.refreshAndFocusLblPanel(state) }) } func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { - return gui.lineByLineNavigateTo(selectedLineIdx, state) + return gui.withLBLActiveCheck(func(state *LblPanelState) error { + state.SetLineSelectMode() + state.SelectLine(selectedLineIdx) + + return gui.refreshAndFocusLblPanel(state) }) } -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 { +func (gui *Gui) withLBLActiveCheck(f func(*LblPanelState) error) error { gui.Mutexes.LineByLinePanelMutex.Lock() defer gui.Mutexes.LineByLinePanelMutex.Unlock() diff --git a/pkg/gui/patch_building_panel.go b/pkg/gui/patch_building_panel.go index b7ff83f3c..5c1d4de5f 100644 --- a/pkg/gui/patch_building_panel.go +++ b/pkg/gui/patch_building_panel.go @@ -17,7 +17,7 @@ func (gui *Gui) getFromAndReverseArgsForDiff(to string) (string, bool) { return from, reverse } -func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int, state *lBlPanelState) error { +func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int, state *LblPanelState) error { if !gui.GitCommand.PatchManager.Active() { return gui.handleEscapePatchBuildingPanel() } @@ -43,7 +43,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int, state *lBlPanelSt return err } - empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx, state) + empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx) if err != nil { return err } @@ -63,14 +63,14 @@ func (gui *Gui) handleRefreshPatchBuildingPanel(selectedLineIdx int) error { } func (gui *Gui) handleToggleSelectionForPatch() error { - err := gui.withLBLActiveCheck(func(state *lBlPanelState) error { + err := gui.withLBLActiveCheck(func(state *LblPanelState) error { toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange filename := gui.getSelectedCommitFileName() includedLineIndices, err := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename) if err != nil { return err } - currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.SelectedLineIdx) + currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.GetSelectedLineIdx()) if currentLineIsStaged { toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange } @@ -81,7 +81,9 @@ func (gui *Gui) handleToggleSelectionForPatch() error { return nil } - if err := toggleFunc(node.GetPath(), state.FirstLineIdx, state.LastLineIdx); err != nil { + firstLineIdx, lastLineIdx := state.SelectedRange() + + if err := toggleFunc(node.GetPath(), firstLineIdx, lastLineIdx); err != nil { // might actually want to return an error here gui.Log.Error(err) } diff --git a/pkg/gui/staging_panel.go b/pkg/gui/staging_panel.go index 0a6ccfa3d..c016586da 100644 --- a/pkg/gui/staging_panel.go +++ b/pkg/gui/staging_panel.go @@ -6,7 +6,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/patch" ) -func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int, state *lBlPanelState) error { +func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error { gui.splitMainPanel(true) file := gui.getSelectedFile() @@ -17,8 +17,8 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx secondaryFocused := false if forceSecondaryFocused { secondaryFocused = true - } else if state != nil { - secondaryFocused = state.SecondaryFocused + } else if gui.State.Panels.LineByLine != nil { + secondaryFocused = gui.State.Panels.LineByLine.SecondaryFocused } if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) { @@ -47,7 +47,7 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx diff, secondaryDiff = secondaryDiff, diff } - empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx, state) + empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx) if err != nil { return err } @@ -60,10 +60,10 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx } func (gui *Gui) handleTogglePanelClick() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { state.SecondaryFocused = !state.SecondaryFocused - return gui.refreshStagingPanel(false, gui.Views.Secondary.SelectedLineIdx(), state) + return gui.refreshStagingPanel(false, gui.Views.Secondary.SelectedLineIdx()) }) } @@ -71,13 +71,13 @@ func (gui *Gui) handleRefreshStagingPanel(forceSecondaryFocused bool, selectedLi gui.Mutexes.LineByLinePanelMutex.Lock() defer gui.Mutexes.LineByLinePanelMutex.Unlock() - return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx, gui.State.Panels.LineByLine) + return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx) } func (gui *Gui) handleTogglePanel() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { state.SecondaryFocused = !state.SecondaryFocused - return gui.refreshStagingPanel(false, -1, state) + return gui.refreshStagingPanel(false, -1) }) } @@ -88,13 +88,13 @@ func (gui *Gui) handleStagingEscape() error { } func (gui *Gui) handleToggleStagedSelection() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { return gui.applySelection(state.SecondaryFocused, state) }) } func (gui *Gui) handleResetSelection() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { if state.SecondaryFocused { // for backwards compatibility return gui.applySelection(true, state) @@ -106,7 +106,7 @@ func (gui *Gui) handleResetSelection() error { prompt: gui.Tr.UnstageLinesPrompt, handlersManageFocus: true, handleConfirm: func() error { - return gui.withLBLActiveCheck(func(state *lBlPanelState) error { + return gui.withLBLActiveCheck(func(state *LblPanelState) error { if err := gui.pushContext(gui.State.Contexts.Staging); err != nil { return err } @@ -124,13 +124,14 @@ func (gui *Gui) handleResetSelection() error { }) } -func (gui *Gui) applySelection(reverse bool, state *lBlPanelState) error { +func (gui *Gui) applySelection(reverse bool, state *LblPanelState) error { file := gui.getSelectedFile() if file == nil { return nil } - patch := patch.ModifiedPatchForRange(gui.Log, file.Name, state.Diff, state.FirstLineIdx, state.LastLineIdx, reverse, false) + firstLineIdx, lastLineIdx := state.SelectedRange() + patch := patch.ModifiedPatchForRange(gui.Log, file.Name, state.GetDiff(), firstLineIdx, lastLineIdx, reverse, false) if patch == "" { return nil @@ -147,14 +148,14 @@ func (gui *Gui) applySelection(reverse bool, state *lBlPanelState) error { return gui.surfaceError(err) } - if state.SelectMode == RANGE { - state.SelectMode = LINE + if state.SelectingRange() { + state.SetLineSelectMode() } if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil { return err } - if err := gui.refreshStagingPanel(false, -1, state); err != nil { + if err := gui.refreshStagingPanel(false, -1); err != nil { return err } return nil