setCursorRuneOffset moves the cursor to an absolute rune position. textarea has no (row, col) setter, so we step CursorUp/Down to the target row then SetCursor the column. The step cap bounds the walk so a buggy textarea can't wedge the loop.
(offset int)
| 416 | // SetCursor the column. The step cap bounds the walk so a buggy textarea can't |
| 417 | // wedge the loop. |
| 418 | func (p *promptInput) setCursorRuneOffset(offset int) { |
| 419 | value := p.ta.Value() |
| 420 | targetRow, targetCol := runeOffsetToRowCol(value, offset) |
| 421 | |
| 422 | for range 2048 { |
| 423 | curRow := p.ta.Line() |
| 424 | if curRow == targetRow { |
| 425 | break |
| 426 | } |
| 427 | info := p.ta.LineInfo() |
| 428 | if curRow < targetRow { |
| 429 | p.ta.CursorDown() |
| 430 | } else { |
| 431 | p.ta.CursorUp() |
| 432 | } |
| 433 | // Inside a soft-wrapped logical line a cursor step moves only the |
| 434 | // visual position (LineInfo), not Line(); that's progress toward the |
| 435 | // target row, not a wedged textarea. Bail only when nothing moved, or |
| 436 | // the walk stops one visual row short and SetCursor lands on the |
| 437 | // wrong logical row. |
| 438 | if next := p.ta.LineInfo(); p.ta.Line() == curRow && |
| 439 | next.RowOffset == info.RowOffset && next.ColumnOffset == info.ColumnOffset { |
| 440 | break |
| 441 | } |
| 442 | } |
| 443 | p.ta.SetCursor(targetCol) |
| 444 | } |
| 445 | |
| 446 | // runeOffsetToRowCol converts an absolute rune offset into (row, col). |
| 447 | func runeOffsetToRowCol(value string, offset int) (int, int) { |