|
7 | 7 | "testing" |
8 | 8 |
|
9 | 9 | tea "charm.land/bubbletea/v2" |
| 10 | + "charm.land/lipgloss/v2" |
10 | 11 | ) |
11 | 12 |
|
12 | 13 | func Test_CurrentSuggestion(t *testing.T) { |
@@ -118,3 +119,38 @@ func sendString(m Model, str string) Model { |
118 | 119 |
|
119 | 120 | return m |
120 | 121 | } |
| 122 | + |
| 123 | +// Cursor() must report the scroll-adjusted column (pos-offset) like View() when |
| 124 | +// the value overflows the width. |
| 125 | +func TestCursorXAccountsForScrollOffset(t *testing.T) { |
| 126 | + m := New() |
| 127 | + m.Focus() |
| 128 | + m.SetVirtualCursor(false) |
| 129 | + m.CharLimit = 200 |
| 130 | + m.SetWidth(20) |
| 131 | + m.SetValue(strings.Repeat("a", 30)) |
| 132 | + |
| 133 | + // Scroll to the end, then move into the middle of the viewport, where the |
| 134 | + // width clamp doesn't mask the wrong column. |
| 135 | + m.CursorEnd() |
| 136 | + m.SetCursor(25) |
| 137 | + |
| 138 | + if m.offset == 0 { |
| 139 | + t.Fatalf("test setup: expected a non-zero scroll offset for an overflowing input, got 0") |
| 140 | + } |
| 141 | + |
| 142 | + cur := m.Cursor() |
| 143 | + if cur == nil { |
| 144 | + t.Fatal("Cursor() returned nil") |
| 145 | + } |
| 146 | + |
| 147 | + // Oracle: the cursor must land on the column where View renders the glyph |
| 148 | + // for value[pos] — the prompt width plus the rendered width of the visible |
| 149 | + // text before the cursor (value[offset:pos]). Measured from the rendered |
| 150 | + // text, so it's independent of Cursor()'s own arithmetic. |
| 151 | + want := lipgloss.Width(m.promptView()) + lipgloss.Width(string(m.value[m.offset:m.pos])) |
| 152 | + if cur.X != want { |
| 153 | + t.Errorf("Cursor().X = %d, want %d (column where View renders the cursor glyph; pos=%d, offset=%d)", |
| 154 | + cur.X, want, m.pos, m.offset) |
| 155 | + } |
| 156 | +} |
0 commit comments