From af147d49f752199934f8b43f4ec5a31e0c40bd52 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 10 Jun 2026 11:11:21 +0200 Subject: [PATCH] Lock the view while reading viewLines on mouse move onMouseMove (and findHyperlinkAt, which it calls) read v.viewLines without holding writeMutex, unlike every other reader. They run on the event-handling goroutine, so a re-render on the task goroutine can shrink or rebuild viewLines between onMouseMove's bounds check and findHyperlinkAt's indexing, causing an out-of-range panic (observed: "index out of range [60] with length 0" while hovering during a diff re-render). Take writeMutex for the duration, like the other viewLines readers do, so the check and the access see the same slice. Pre-existing, but the off-screen re-render rebuilds viewLines on the task goroutine more often, widening the window. --- pkg/gocui/view.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/gocui/view.go b/pkg/gocui/view.go index 42e54b34a..98c5a048d 100644 --- a/pkg/gocui/view.go +++ b/pkg/gocui/view.go @@ -2414,6 +2414,14 @@ func (v *View) onMouseMove(x int, y int) { return } + // Reading v.viewLines (here and in findHyperlinkAt) must hold writeMutex like + // every other reader: this runs on the event-handling goroutine, and a + // concurrent re-render on the task goroutine can rebuild or shrink viewLines + // between the bounds check below and the indexing in findHyperlinkAt — which + // panicked with an out-of-range index. + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + // newCx and newCy are relative to the view port, i.e. to the visible area of the view newCx := x - v.x0 - 1 newCy := y - v.y0 - 1