diff --git a/pkg/gocui/view.go b/pkg/gocui/view.go index 6fbbecdb6..7bb0b7ca5 100644 --- a/pkg/gocui/view.go +++ b/pkg/gocui/view.go @@ -1985,11 +1985,12 @@ func (v *View) SelectedLine() string { v.writeMutex.Lock() defer v.writeMutex.Unlock() - if len(v.buf.lines) == 0 { + idx, ok := v.bufferLineForViewLine(v.SelectedLineIdx()) + if !ok { return "" } - return v.lineContentAtIdx(v.SelectedLineIdx()) + return v.lineContentAtIdx(idx) } // expected to only be used in tests @@ -2004,8 +2005,17 @@ func (v *View) SelectedLines() []string { startIdx, endIdx := v.SelectedLineRange() lines := make([]string, 0, endIdx-startIdx+1) + previous := -1 for i := startIdx; i <= endIdx; i++ { - lines = append(lines, v.lineContentAtIdx(i)) + // The selection is in view lines, which count the segments a wrapped line + // is drawn as; a line the selection covers several segments of is still + // the one line it is. + idx, ok := v.bufferLineForViewLine(i) + if !ok || idx == previous { + continue + } + previous = idx + lines = append(lines, v.lineContentAtIdx(idx)) } return lines @@ -2015,6 +2025,19 @@ func (v *View) lineContentAtIdx(idx int) string { return v.buf.lines[idx].cells.String() } +// bufferLineForViewLine maps a view line index, which counts the wrapped +// segments of the lines it draws, to the index of the line of content it is a +// segment of. Only call this with a lock on writeMutex. +func (v *View) bufferLineForViewLine(y int) (int, bool) { + v.refreshViewLinesIfNeeded() + + if y < 0 || y >= len(v.viewLines) { + return 0, false + } + + return v.viewLines[y].linesY, true +} + func (v *View) SelectedPoint() (int, int) { cx, cy := v.Cursor() ox, oy := v.Origin() diff --git a/pkg/gocui/view_test.go b/pkg/gocui/view_test.go index adbfb952b..2b2d50fd2 100644 --- a/pkg/gocui/view_test.go +++ b/pkg/gocui/view_test.go @@ -837,15 +837,9 @@ func TestSelectedLinesOfWrappedContent(t *testing.T) { // The cursor on the wrapped line's second half is on that line. v.FocusPoint(0, 3, false) - /* EXPECTED: assert.Equal(t, "a line that wraps", v.SelectedLine()) - ACTUAL: */ - assert.Equal(t, "four", v.SelectedLine()) // A range over both halves of the wrapped line covers one line of content. v.SetRangeSelectStart(2) - /* EXPECTED: assert.Equal(t, []string{"a line that wraps"}, v.SelectedLines()) - ACTUAL: */ - assert.Equal(t, []string{"a line that wraps", "four"}, v.SelectedLines()) }