* Position the hardware cursor for IME candidate window. * @param cursorPos The cursor position extracted from rendered output, or null * @param totalLines Total number of rendered lines * @param viewportTop Logical row shown on the top screen row after this frame * @param height Terminal he
(
cursorPos: { row: number; col: number } | null,
totalLines: number,
viewportTop: number,
height: number,
)
| 1781 | * @param height Terminal height (visible viewport size) |
| 1782 | */ |
| 1783 | private positionHardwareCursor( |
| 1784 | cursorPos: { row: number; col: number } | null, |
| 1785 | totalLines: number, |
| 1786 | viewportTop: number, |
| 1787 | height: number, |
| 1788 | ): void { |
| 1789 | if (!cursorPos || totalLines <= 0) { |
| 1790 | this.terminal.hideCursor(); |
| 1791 | return; |
| 1792 | } |
| 1793 | |
| 1794 | // Clamp cursor position to valid range |
| 1795 | const targetRow = Math.max(0, Math.min(cursorPos.row, totalLines - 1)); |
| 1796 | const targetCol = Math.max(0, cursorPos.col); |
| 1797 | |
| 1798 | // The hardware cursor can only sit inside the visible window. A |
| 1799 | // marker outside it (e.g. a tall editor poking above a pinned |
| 1800 | // viewport) must not move the cursor: the ANSI moves would clamp at |
| 1801 | // the screen edge while hardwareCursorRow recorded the unreachable |
| 1802 | // row, desyncing every later differential move. Hide the cursor and |
| 1803 | // keep the bookkeeping on the real cursor row instead. |
| 1804 | if (targetRow < viewportTop || targetRow >= viewportTop + height) { |
| 1805 | this.terminal.hideCursor(); |
| 1806 | return; |
| 1807 | } |
| 1808 | |
| 1809 | // Move cursor from current position to target |
| 1810 | const rowDelta = targetRow - this.hardwareCursorRow; |
| 1811 | let buffer = ""; |
| 1812 | if (rowDelta > 0) { |
| 1813 | buffer += `\x1b[${rowDelta}B`; // Move down |
| 1814 | } else if (rowDelta < 0) { |
| 1815 | buffer += `\x1b[${-rowDelta}A`; // Move up |
| 1816 | } |
| 1817 | // Move to absolute column (1-indexed) |
| 1818 | buffer += `\x1b[${targetCol + 1}G`; |
| 1819 | |
| 1820 | if (buffer) { |
| 1821 | this.terminal.write(buffer); |
| 1822 | } |
| 1823 | |
| 1824 | this.hardwareCursorRow = targetRow; |
| 1825 | if (this.showHardwareCursor) { |
| 1826 | this.terminal.showCursor(); |
| 1827 | } else { |
| 1828 | this.terminal.hideCursor(); |
| 1829 | } |
| 1830 | } |
| 1831 | |
| 1832 | /** |
| 1833 | * Query the terminal's default background color with OSC 11 (`ESC ] 11 ; ? BEL`). |
no test coverage detected