1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-17 12:06:38 +02:00

refactor line by line panel

This commit is contained in:
Jesse Duffield 2021-04-18 16:30:34 +10:00
parent b28b2d05bd
commit e73de332a1
7 changed files with 295 additions and 219 deletions

View File

@ -182,11 +182,8 @@ func (p *PatchManager) RenderPatchForFile(filename string, plain bool, reverse b
if plain { if plain {
return patch return patch
} }
parser, err := NewPatchParser(p.Log, patch) parser := NewPatchParser(p.Log, patch)
if err != nil {
// swallowing for now
return ""
}
// not passing included lines because we don't want to see them in the secondary panel // not passing included lines because we don't want to see them in the secondary panel
return parser.Render(-1, -1, nil) return parser.Render(-1, -1, nil)
} }

View File

@ -39,11 +39,8 @@ type PatchParser struct {
} }
// NewPatchParser builds a new branch list builder // NewPatchParser builds a new branch list builder
func NewPatchParser(log *logrus.Entry, patch string) (*PatchParser, error) { func NewPatchParser(log *logrus.Entry, patch string) *PatchParser {
hunkStarts, stageableLines, patchLines, err := parsePatch(patch) hunkStarts, stageableLines, patchLines := parsePatch(patch)
if err != nil {
return nil, err
}
patchHunks := GetHunksFromDiff(patch) patchHunks := GetHunksFromDiff(patch)
@ -53,7 +50,7 @@ func NewPatchParser(log *logrus.Entry, patch string) (*PatchParser, error) {
StageableLines: stageableLines, StageableLines: stageableLines,
PatchLines: patchLines, PatchLines: patchLines,
PatchHunks: patchHunks, PatchHunks: patchHunks,
}, nil }
} }
// GetHunkContainingLine takes a line index and an offset and finds the hunk // 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) 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") lines := strings.Split(patch, "\n")
hunkStarts := []int{} hunkStarts := []int{}
stageableLines := []int{} stageableLines := []int{}
@ -185,7 +182,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine, error) {
} }
patchLines[index] = &PatchLine{Kind: lineKind, Content: line} 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 // Render returns the coloured string of the diff with any selected lines highlighted

View File

@ -18,9 +18,9 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands" "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/config"
"github.com/jesseduffield/lazygit/pkg/gui/filetree" "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/modes/filtering"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n" "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 // 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 // non-mutative, so that we don't accidentally end up
// with mismatches of data. We might change this in the future // with mismatches of data. We might change this in the future
type lBlPanelState struct { type LblPanelState struct {
SelectedLineIdx int *lbl.State
FirstLineIdx int
LastLineIdx int
Diff string
PatchParser *patch.PatchParser
SelectMode SelectMode
SecondaryFocused bool // this is for if we show the left or right panel SecondaryFocused bool // this is for if we show the left or right panel
} }
@ -230,7 +225,7 @@ type panelStates struct {
SubCommits *subCommitPanelState SubCommits *subCommitPanelState
Stash *stashPanelState Stash *stashPanelState
Menu *menuPanelState Menu *menuPanelState
LineByLine *lBlPanelState LineByLine *LblPanelState
Merging *mergingPanelState Merging *mergingPanelState
CommitFiles *commitFilesPanelState CommitFiles *commitFilesPanelState
Submodules *submodulePanelState Submodules *submodulePanelState

191
pkg/gui/lbl/line_by_line.go Normal file
View File

@ -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)
}

View File

@ -6,6 +6,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/patch" "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'. // 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 // staging_panel.go and patch_building_panel.go have functions specific to their
// use cases // 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 // returns whether the patch is empty so caller can escape if necessary
// both diffs should be non-coloured because we'll parse them and colour them here // both diffs should be non-coloured because we'll parse them and colour them here
func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int, state *lBlPanelState) (bool, error) { func (gui *Gui) refreshLineByLinePanel(diff string, secondaryDiff string, secondaryFocused bool, selectedLineIdx int) (bool, error) {
gui.splitMainPanel(true) gui.splitMainPanel(true)
patchParser, err := patch.NewPatchParser(gui.Log, diff) var oldState *lbl.State
if err != nil { if gui.State.Panels.LineByLine != nil {
return false, 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 return true, nil
} }
var firstLineIdx int gui.State.Panels.LineByLine = &LblPanelState{
var lastLineIdx int State: state,
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,
SecondaryFocused: secondaryFocused, 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 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 return false, err
} }
gui.Views.Secondary.Highlight = true gui.Views.Secondary.Highlight = true
gui.Views.Secondary.Wrap = false gui.Views.Secondary.Wrap = false
secondaryPatchParser, err := patch.NewPatchParser(gui.Log, secondaryDiff) secondaryPatchParser := patch.NewPatchParser(gui.Log, secondaryDiff)
if err != nil {
return false, nil
}
gui.setViewContent(gui.Views.Secondary, secondaryPatchParser.Render(-1, -1, nil)) 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 { func (gui *Gui) handleSelectPrevLine() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
return gui.LBLCycleLine(-1, state) state.CycleSelection(false)
return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleSelectNextLine() error { func (gui *Gui) handleSelectNextLine() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
return gui.LBLCycleLine(+1, state) state.CycleSelection(true)
return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleSelectPrevHunk() error { func (gui *Gui) handleSelectPrevHunk() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, -1) state.CycleHunk(false)
return gui.selectNewHunk(newHunk, state) return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleSelectNextHunk() error { func (gui *Gui) handleSelectNextHunk() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
newHunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 1) state.CycleHunk(true)
return gui.selectNewHunk(newHunk, state) return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) selectNewHunk(newHunk *patch.PatchHunk, state *lBlPanelState) error { func (gui *Gui) refreshAndFocusLblPanel(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
}
if err := gui.refreshMainViewForLineByLine(state); err != nil { if err := gui.refreshMainViewForLineByLine(state); err != nil {
return err return err
} }
return gui.focusSelection(true, state) return gui.focusSelection(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)
} }
func (gui *Gui) handleLBLMouseDown() error { func (gui *Gui) handleLBLMouseDown() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
newSelectedLineIdx := gui.Views.Main.SelectedLineIdx() state.SelectNewLineForRange(gui.Views.Main.SelectedLineIdx())
state.FirstLineIdx = newSelectedLineIdx
state.LastLineIdx = newSelectedLineIdx
state.SelectMode = RANGE return gui.refreshAndFocusLblPanel(state)
return gui.LBLSelectLine(newSelectedLineIdx, state)
}) })
} }
func (gui *Gui) handleMouseDrag() error { func (gui *Gui) handleMouseDrag() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil 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() return gui.State.CommitFileManager.GetItemAtIndex(idx).GetPath()
} }
func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) error { func (gui *Gui) refreshMainViewForLineByLine(state *LblPanelState) error {
var includedLineIndices []int var includedLineIndices []int
// I'd prefer not to have knowledge of contexts using this file but I'm not sure // I'd prefer not to have knowledge of contexts using this file but I'm not sure
// how to get around this // how to get around this
@ -220,7 +136,7 @@ func (gui *Gui) refreshMainViewForLineByLine(state *lBlPanelState) error {
return err return err
} }
} }
colorDiff := state.PatchParser.Render(state.FirstLineIdx, state.LastLineIdx, includedLineIndices) colorDiff := state.RenderForLineIndices(includedLineIndices)
gui.Views.Main.Highlight = true gui.Views.Main.Highlight = true
gui.Views.Main.Wrap = false 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 // focusSelection works out the best focus for the staging panel given the
// selected line and size of the hunk // 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 stagingView := gui.Views.Main
_, viewHeight := stagingView.Size() _, viewHeight := stagingView.Size()
bufferHeight := viewHeight - 1 bufferHeight := viewHeight - 1
_, origin := stagingView.Origin() _, origin := stagingView.Origin()
firstLineIdx := state.SelectedLineIdx firstLineIdx, lastLineIdx := state.SelectedRange()
lastLineIdx := state.SelectedLineIdx
if includeCurrentHunk {
hunk := state.PatchParser.GetHunkContainingLine(state.SelectedLineIdx, 0)
firstLineIdx = hunk.FirstLineIdx
lastLineIdx = hunk.LastLineIdx()
}
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 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 err
} }
return stagingView.SetCursor(0, state.SelectedLineIdx-newOrigin) return stagingView.SetCursor(0, state.GetSelectedLineIdx()-newOrigin)
}) })
return nil return nil
} }
func (gui *Gui) handleToggleSelectRange() error { func (gui *Gui) handleToggleSelectRange() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
if state.SelectMode == RANGE { state.ToggleSelectRange()
state.SelectMode = LINE
} else {
state.SelectMode = RANGE
}
state.FirstLineIdx, state.LastLineIdx = state.SelectedLineIdx, state.SelectedLineIdx
return gui.refreshMainViewForLineByLine(state) return gui.refreshMainViewForLineByLine(state)
}) })
} }
func (gui *Gui) handleToggleSelectHunk() error { func (gui *Gui) handleToggleSelectHunk() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
if state.SelectMode == HUNK { state.ToggleSelectHunk()
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(state); err != nil { return gui.refreshAndFocusLblPanel(state)
return err
}
return gui.focusSelection(state.SelectMode == HUNK, state)
}) })
} }
@ -307,7 +200,7 @@ func (gui *Gui) escapeLineByLinePanel() {
} }
func (gui *Gui) handleOpenFileAtLine() error { 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) // again, would be good to use inheritance here (or maybe even composition)
var filename string var filename string
switch gui.State.MainContext { switch gui.State.MainContext {
@ -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 // 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 := state.CurrentLineNumber()
lineNumber := selectedHunk.LineNumberOfLine(state.SelectedLineIdx)
filenameWithLineNum := fmt.Sprintf("%s:%d", filename, lineNumber) filenameWithLineNum := fmt.Sprintf("%s:%d", filename, lineNumber)
if err := gui.OSCommand.OpenFile(filenameWithLineNum); err != nil { if err := gui.OSCommand.OpenFile(filenameWithLineNum); err != nil {
return err return err
@ -336,48 +228,49 @@ func (gui *Gui) handleOpenFileAtLine() error {
} }
func (gui *Gui) handleLineByLineNextPage() error { func (gui *Gui) handleLineByLineNextPage() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
newSelectedLineIdx := state.SelectedLineIdx + gui.pageDelta(gui.Views.Main) state.SetLineSelectMode()
state.AdjustSelectedLineIdx(gui.pageDelta(gui.Views.Main))
return gui.lineByLineNavigateTo(newSelectedLineIdx, state) return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleLineByLinePrevPage() error { func (gui *Gui) handleLineByLinePrevPage() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
newSelectedLineIdx := state.SelectedLineIdx - gui.pageDelta(gui.Views.Main) state.SetLineSelectMode()
state.AdjustSelectedLineIdx(-gui.pageDelta(gui.Views.Main))
return gui.lineByLineNavigateTo(newSelectedLineIdx, state) return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleLineByLineGotoBottom() error { func (gui *Gui) handleLineByLineGotoBottom() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
newSelectedLineIdx := len(state.PatchParser.PatchLines) - 1 state.SelectBottom()
return gui.lineByLineNavigateTo(newSelectedLineIdx, state) return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handleLineByLineGotoTop() error { func (gui *Gui) handleLineByLineGotoTop() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
return gui.lineByLineNavigateTo(0, state) state.SelectTop()
return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error { func (gui *Gui) handlelineByLineNavigateTo(selectedLineIdx int) error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
return gui.lineByLineNavigateTo(selectedLineIdx, state) state.SetLineSelectMode()
state.SelectLine(selectedLineIdx)
return gui.refreshAndFocusLblPanel(state)
}) })
} }
func (gui *Gui) lineByLineNavigateTo(selectedLineIdx int, state *lBlPanelState) error { func (gui *Gui) withLBLActiveCheck(f func(*LblPanelState) error) error {
state.SelectMode = LINE
return gui.LBLSelectLine(selectedLineIdx, state)
}
func (gui *Gui) withLBLActiveCheck(f func(*lBlPanelState) error) error {
gui.Mutexes.LineByLinePanelMutex.Lock() gui.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock() defer gui.Mutexes.LineByLinePanelMutex.Unlock()

View File

@ -17,7 +17,7 @@ func (gui *Gui) getFromAndReverseArgsForDiff(to string) (string, bool) {
return from, reverse 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() { if !gui.GitCommand.PatchManager.Active() {
return gui.handleEscapePatchBuildingPanel() return gui.handleEscapePatchBuildingPanel()
} }
@ -43,7 +43,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int, state *lBlPanelSt
return err return err
} }
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx, state) empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, false, selectedLineIdx)
if err != nil { if err != nil {
return err return err
} }
@ -63,14 +63,14 @@ func (gui *Gui) handleRefreshPatchBuildingPanel(selectedLineIdx int) error {
} }
func (gui *Gui) handleToggleSelectionForPatch() 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 toggleFunc := gui.GitCommand.PatchManager.AddFileLineRange
filename := gui.getSelectedCommitFileName() filename := gui.getSelectedCommitFileName()
includedLineIndices, err := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename) includedLineIndices, err := gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
if err != nil { if err != nil {
return err return err
} }
currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.SelectedLineIdx) currentLineIsStaged := utils.IncludesInt(includedLineIndices, state.GetSelectedLineIdx())
if currentLineIsStaged { if currentLineIsStaged {
toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange toggleFunc = gui.GitCommand.PatchManager.RemoveFileLineRange
} }
@ -81,7 +81,9 @@ func (gui *Gui) handleToggleSelectionForPatch() error {
return nil 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 // might actually want to return an error here
gui.Log.Error(err) gui.Log.Error(err)
} }

View File

@ -6,7 +6,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/patch" "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) gui.splitMainPanel(true)
file := gui.getSelectedFile() file := gui.getSelectedFile()
@ -17,8 +17,8 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
secondaryFocused := false secondaryFocused := false
if forceSecondaryFocused { if forceSecondaryFocused {
secondaryFocused = true secondaryFocused = true
} else if state != nil { } else if gui.State.Panels.LineByLine != nil {
secondaryFocused = state.SecondaryFocused secondaryFocused = gui.State.Panels.LineByLine.SecondaryFocused
} }
if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) { if (secondaryFocused && !file.HasStagedChanges) || (!secondaryFocused && !file.HasUnstagedChanges) {
@ -47,7 +47,7 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
diff, secondaryDiff = secondaryDiff, diff diff, secondaryDiff = secondaryDiff, diff
} }
empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx, state) empty, err := gui.refreshLineByLinePanel(diff, secondaryDiff, secondaryFocused, selectedLineIdx)
if err != nil { if err != nil {
return err return err
} }
@ -60,10 +60,10 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
} }
func (gui *Gui) handleTogglePanelClick() error { func (gui *Gui) handleTogglePanelClick() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
state.SecondaryFocused = !state.SecondaryFocused 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() gui.Mutexes.LineByLinePanelMutex.Lock()
defer gui.Mutexes.LineByLinePanelMutex.Unlock() defer gui.Mutexes.LineByLinePanelMutex.Unlock()
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx, gui.State.Panels.LineByLine) return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx)
} }
func (gui *Gui) handleTogglePanel() error { func (gui *Gui) handleTogglePanel() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
state.SecondaryFocused = !state.SecondaryFocused 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 { 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) return gui.applySelection(state.SecondaryFocused, state)
}) })
} }
func (gui *Gui) handleResetSelection() error { func (gui *Gui) handleResetSelection() error {
return gui.withLBLActiveCheck(func(state *lBlPanelState) error { return gui.withLBLActiveCheck(func(state *LblPanelState) error {
if state.SecondaryFocused { if state.SecondaryFocused {
// for backwards compatibility // for backwards compatibility
return gui.applySelection(true, state) return gui.applySelection(true, state)
@ -106,7 +106,7 @@ func (gui *Gui) handleResetSelection() error {
prompt: gui.Tr.UnstageLinesPrompt, prompt: gui.Tr.UnstageLinesPrompt,
handlersManageFocus: true, handlersManageFocus: true,
handleConfirm: func() error { 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 { if err := gui.pushContext(gui.State.Contexts.Staging); err != nil {
return err 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() file := gui.getSelectedFile()
if file == nil { if file == nil {
return 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 == "" { if patch == "" {
return nil return nil
@ -147,14 +148,14 @@ func (gui *Gui) applySelection(reverse bool, state *lBlPanelState) error {
return gui.surfaceError(err) return gui.surfaceError(err)
} }
if state.SelectMode == RANGE { if state.SelectingRange() {
state.SelectMode = LINE state.SetLineSelectMode()
} }
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil { if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
return err return err
} }
if err := gui.refreshStagingPanel(false, -1, state); err != nil { if err := gui.refreshStagingPanel(false, -1); err != nil {
return err return err
} }
return nil return nil