summaryrefslogtreecommitdiffstats
path: root/pkg/gui/patch_exploring/state.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/gui/patch_exploring/state.go')
-rw-r--r--pkg/gui/patch_exploring/state.go212
1 files changed, 212 insertions, 0 deletions
diff --git a/pkg/gui/patch_exploring/state.go b/pkg/gui/patch_exploring/state.go
new file mode 100644
index 000000000..008338326
--- /dev/null
+++ b/pkg/gui/patch_exploring/state.go
@@ -0,0 +1,212 @@
+package patch_exploring
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/commands/patch"
+ "github.com/sirupsen/logrus"
+)
+
+// State represents the current state of the patch explorer context i.e. when
+// you're staging a file or you're building a patch from an existing commit
+// this struct holds the info about the diff you're interacting with and what's currently selected.
+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 {
+ if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 {
+ // if we're here then we can return the old state. If selectedLineIdx was not -1
+ // then that would mean we were trying to click and potentiall drag a range, which
+ // is why in that case we continue below
+ return oldState
+ }
+
+ 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(isFocused bool, includedLineIndices []int) string {
+ firstLineIdx, lastLineIdx := s.SelectedRange()
+ return s.patchParser.Render(isFocused, firstLineIdx, lastLineIdx, includedLineIndices)
+}
+
+func (s *State) PlainRenderSelected() string {
+ firstLineIdx, lastLineIdx := s.SelectedRange()
+ return s.patchParser.RenderLinesPlain(firstLineIdx, lastLineIdx)
+}
+
+func (s *State) SelectBottom() {
+ s.SetLineSelectMode()
+ s.SelectLine(len(s.patchParser.PatchLines) - 1)
+}
+
+func (s *State) SelectTop() {
+ s.SetLineSelectMode()
+ s.SelectLine(0)
+}
+
+func (s *State) CalculateOrigin(currentOrigin int, bufferHeight int) int {
+ firstLineIdx, lastLineIdx := s.SelectedRange()
+
+ return calculateOrigin(currentOrigin, bufferHeight, firstLineIdx, lastLineIdx, s.GetSelectedLineIdx(), s.selectMode)
+}