Repository: jesseduffield/lazygit (language: go)

Issue:
crash when drag selecting and then staging ranges
**Sometimes** when drag selecting a range using mouse, and then staging it, there is a crash

**To Reproduce**
Steps to reproduce the behavior:

1. Drag select a range
2. Spacebar to stage

**Expected behavior**
It always works, does not panic

**Version info:**
```
commit=v0.48.0, build date=2025-03-01T07:57:57Z, build source=binaryRelease, version=0.48.0, os=linux, arch=amd64, git version=2.49.0
```

**Additional context**
stack trace:
```
panic: runtime error: index out of range [65] with length 64

goroutine 1 [running]:
github.com/jesseduffield/lazygit/pkg/gui/patch_exploring.(*State).SelectedPatchRange(0xc000218620)
        github.com/jesseduffield/lazygit/pkg/gui/patch_exploring/state.go:284 +0x48
github.com/jesseduffield/lazygit/pkg/gui/controllers.(*StagingController).applySelection(0xc000279740, 0x0)
        github.com/jesseduffield/lazygit/pkg/gui/controllers/staging_controller.go:237 +0x125
github.com/jesseduffield/lazygit/pkg/gui/controllers.(*StagingController).applySelectionAndRefresh(0xc000279740, 0x30?)
        github.com/jesseduffield/lazygit/pkg/gui/controllers/staging_controller.go:220 +0x1c
github.com/jesseduffield/lazygit/pkg/gui/controllers.(*StagingController).ToggleStaged(0x1?)
        github.com/jesseduffield/lazygit/pkg/gui/controllers/staging_controller.go:195 +0xa5
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).callKeybindingHandler(0xc000294008, 0xc000002380?)
        github.com/jesseduffield/lazygit/pkg/gui/keybindings.go:468 +0x10a
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).SetKeybinding.func1()
        github.com/jesseduffield/lazygit/pkg/gui/keybindings.go:413 +0x1b
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).SetKeybinding.(*Gui).wrappedHandler.func3(0x0?, 0xc0043c1520?)
        github.com/jesseduffield/lazygit/pkg/gui/keybindings.go:407 +0x13
github.com/jesseduffield/gocui.(*Gui).execKeybinding(0xc0043c1460?, 0x5970b067d414?, 0xc0043c156c?)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:1553 +0x4a
github.com/jesseduffield/gocui.(*Gui).execKeybindings(0xc000200a80, 0xc0002d18c8, 0xc0043c1580)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:1521 +0x418
github.com/jesseduffield/gocui.(*Gui).onKey(0xc000278088?, 0xc0043c1508?)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:1328 +0x4d
github.com/jesseduffield/gocui.(*Gui).handleEvent(0xc000278060?, 0xc0043c1568?)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:845 +0x2f
github.com/jesseduffield/gocui.(*Gui).processEvent(0xc000200a80)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:799 +0x226
github.com/jesseduffield/gocui.(*Gui).MainLoop(0xc000200a80)
        github.com/jesseduffield/gocui@v0.3.1-0.20250220081214-b376cb0857ac/gui.go:778 +0xea
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).Run(0xc000294008, {{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}})
        github.com/jesseduffield/lazygit/pkg/gui/gui.go:855 +0x525
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).RunAndHandleError.func1()
        github.com/jesseduffield/lazygit/pkg/gui/gui.go:861 +0x48
github.com/jesseduffield/lazygit/pkg/utils.SafeWithError(0xc00004d918?)
        github.com/jesseduffield/lazygit/pkg/utils/utils.go:99 +0x56
github.com/jesseduffield/lazygit/pkg/gui.(*Gui).RunAndHandleError(0xc000294008, {{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}})
        github.com/jesseduffield/lazygit/pkg/gui/gui.go:860 +0xff
github.com/jesseduffield/lazygit/pkg/app.(*App).Run(...)
        github.com/jesseduffield/lazygit/pkg/app/app.go:270
github.com/jesseduffield/lazygit/pkg/app.Run({0x5970b12927f8?, 0xc00019c370?}, 0xc000244700, {{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, ...}})
        github.com/jesseduffield/lazygit/pkg/app/app.go:48 +0xda
github.com/jesseduffield/lazygit/pkg/app.Start(0xc00004df00, {0x0, 0x0})
        github.com/jesseduffield/lazygit/pkg/app/entry_point.go:168 +0xd45
main.main()
        github.com/jesseduffield/lazygit/main.go:23 +0x98
```

I have not tried the latest master commit


Discussion / hints:
I did a quick test and couldn't immediately reproduce it. Would be good to have a concrete, reproducible scenario if possible, or at least more information about when it happens and when it doesn't.

My suspicion is that it is related to the wrapLinesInStagingView config (which is on by default); also, I guess your range selection included the last line of the diff, is that true?
yes it did in fact include the last line, and i've had this crash happen like once every two weeks-ish for a couple months now, i just never got around to reporting it though, until today
I can reproduce the crash when I start dragging below the end of the diff, and then drag upwards. I can't reproduce it when dragging downwards. Does that sound like it could be what you experienced?

Fix patch:
diff --git a/pkg/gui/patch_exploring/state.go b/pkg/gui/patch_exploring/state.go
index 074793f8e48..73ce0a756b6 100644
--- a/pkg/gui/patch_exploring/state.go
+++ b/pkg/gui/patch_exploring/state.go
@@ -7,6 +7,7 @@ import (
 	"github.com/jesseduffield/gocui"
 	"github.com/jesseduffield/lazygit/pkg/commands/patch"
 	"github.com/jesseduffield/lazygit/pkg/utils"
+	"github.com/samber/lo"
 )
 
 // State represents the current state of the patch explorer context i.e. when
@@ -177,19 +178,17 @@ func (s *State) SelectLine(newSelectedLineIdx int) {
 	s.selectLineWithoutRangeCheck(newSelectedLineIdx)
 }
 
+func (s *State) clampLineIdx(lineIdx int) int {
+	return lo.Clamp(lineIdx, 0, len(s.patchLineIndices)-1)
+}
+
 // This just moves the cursor without caring about range select
 func (s *State) selectLineWithoutRangeCheck(newSelectedLineIdx int) {
-	if newSelectedLineIdx < 0 {
-		newSelectedLineIdx = 0
-	} else if newSelectedLineIdx > len(s.patchLineIndices)-1 {
-		newSelectedLineIdx = len(s.patchLineIndices) - 1
-	}
-
-	s.selectedLineIdx = newSelectedLineIdx
+	s.selectedLineIdx = s.clampLineIdx(newSelectedLineIdx)
 }
 
 func (s *State) SelectNewLineForRange(newSelectedLineIdx int) {
-	s.rangeStartLineIdx = newSelectedLineIdx
+	s.rangeStartLineIdx = s.clampLineIdx(newSelectedLineIdx)
 
 	s.selectMode = RANGE
 


Test patch: