diff --git a/textinput/textinput.go b/textinput/textinput.go index 363089b2d..8b2af9371 100644 --- a/textinput/textinput.go +++ b/textinput/textinput.go @@ -921,8 +921,9 @@ func (m Model) Cursor() *tea.Cursor { w := lipgloss.Width promptWidth := w(m.promptView()) - xOffset := m.Position() + - promptWidth + // Like View, use the scroll-adjusted column (pos-offset) so the cursor + // tracks the visible position when the value overflows the width. + xOffset := max(0, m.pos-m.offset) + promptWidth if m.width > 0 { xOffset = min(xOffset, m.width+promptWidth) } diff --git a/textinput/textinput_test.go b/textinput/textinput_test.go index b5e344b99..252c6b458 100644 --- a/textinput/textinput_test.go +++ b/textinput/textinput_test.go @@ -7,6 +7,7 @@ import ( "testing" tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" ) func Test_CurrentSuggestion(t *testing.T) { @@ -118,3 +119,38 @@ func sendString(m Model, str string) Model { return m } + +// Cursor() must report the scroll-adjusted column (pos-offset) like View() when +// the value overflows the width. +func TestCursorXAccountsForScrollOffset(t *testing.T) { + m := New() + m.Focus() + m.SetVirtualCursor(false) + m.CharLimit = 200 + m.SetWidth(20) + m.SetValue(strings.Repeat("a", 30)) + + // Scroll to the end, then move into the middle of the viewport, where the + // width clamp doesn't mask the wrong column. + m.CursorEnd() + m.SetCursor(25) + + if m.offset == 0 { + t.Fatalf("test setup: expected a non-zero scroll offset for an overflowing input, got 0") + } + + cur := m.Cursor() + if cur == nil { + t.Fatal("Cursor() returned nil") + } + + // Oracle: the cursor must land on the column where View renders the glyph + // for value[pos] — the prompt width plus the rendered width of the visible + // text before the cursor (value[offset:pos]). Measured from the rendered + // text, so it's independent of Cursor()'s own arithmetic. + want := lipgloss.Width(m.promptView()) + lipgloss.Width(string(m.value[m.offset:m.pos])) + if cur.X != want { + t.Errorf("Cursor().X = %d, want %d (column where View renders the cursor glyph; pos=%d, offset=%d)", + cur.X, want, m.pos, m.offset) + } +}