( text: string, offset: number, )
| 596 | } |
| 597 | |
| 598 | export function offsetToLogicalPos( |
| 599 | text: string, |
| 600 | offset: number, |
| 601 | ): [number, number] { |
| 602 | let row = 0; |
| 603 | let col = 0; |
| 604 | let currentOffset = 0; |
| 605 | |
| 606 | if (offset === 0) return [0, 0]; |
| 607 | |
| 608 | const lines = text.split('\n'); |
| 609 | for (let i = 0; i < lines.length; i++) { |
| 610 | const line = lines[i]; |
| 611 | const lineLength = cpLen(line); |
| 612 | const lineLengthWithNewline = lineLength + (i < lines.length - 1 ? 1 : 0); |
| 613 | |
| 614 | if (offset <= currentOffset + lineLength) { |
| 615 | // Check against lineLength first |
| 616 | row = i; |
| 617 | col = offset - currentOffset; |
| 618 | return [row, col]; |
| 619 | } else if (offset <= currentOffset + lineLengthWithNewline) { |
| 620 | // Check if offset is the newline itself |
| 621 | row = i; |
| 622 | col = lineLength; // Position cursor at the end of the current line content |
| 623 | // If the offset IS the newline, and it's not the last line, advance to next line, col 0 |
| 624 | if ( |
| 625 | offset === currentOffset + lineLengthWithNewline && |
| 626 | i < lines.length - 1 |
| 627 | ) { |
| 628 | return [i + 1, 0]; |
| 629 | } |
| 630 | return [row, col]; // Otherwise, it's at the end of the current line content |
| 631 | } |
| 632 | currentOffset += lineLengthWithNewline; |
| 633 | } |
| 634 | |
| 635 | // If offset is beyond the text length, place cursor at the end of the last line |
| 636 | // or [0,0] if text is empty |
| 637 | if (lines.length > 0) { |
| 638 | row = lines.length - 1; |
| 639 | col = cpLen(lines[row]); |
| 640 | } else { |
| 641 | row = 0; |
| 642 | col = 0; |
| 643 | } |
| 644 | return [row, col]; |
| 645 | } |
| 646 | |
| 647 | /** |
| 648 | * Converts logical row/col position to absolute text offset |
no test coverage detected