ITADN

Cursor movement from the pending-wrap state: CUU carries x==cols (CUD normalizes), CUB computes from the phantom column

#565Openhendo12 创建于 2026-06-06
H
hendo12commented
**Description** After a character prints in the last column, `buffer.x == cols` represents the pending-wrap state. Some handlers normalize it back into the grid and some don't, producing divergence from xterm — and internal inconsistency between siblings: - `cmdCursorDown` and `cmdLineFeedBasic` normalize (`if buffer.x >= cols { buffer.x -= 1 }`, with the comment "If the end of the line is hit, prevent this action from wrapping around to the next line"). - `cmdCursorUp` does not — a vertical-only CUU carries the phantom upward, so the **next printed character autowraps onto the row below** the one the cursor visibly occupies. xterm clears the pending flag on cursor movement and prints at the last column of the CUU target row. - `cmdBackspace` calls `restrictCursor()` first; `cursorBackward` (CUB) does not — CUB from the phantom lands one column right of xterm. - `AppleTerminalView.updateCursorPosition` renders the caret at `cellWidth * buffer.x` unclamped — at `x == cols` the caret view draws one cell past the grid's right edge. This looks like the class the `Terminal.swift` header TODO already tracks ("audit every location to use restrictCursor"). **Reproduction (headless engine, no app code)** ```swift final class Sink: TerminalDelegate { func send(source: Terminal, data: ArraySlice<UInt8>) {} } let sink = Sink() let term = Terminal(delegate: sink, options: TerminalOptions(cols: 10, rows: 5)) term.feed(text: "\r\n") // row 1 term.feed(text: String(repeating: "a", count: 10)) // fill row 1 → x == 10 (pending) term.feed(text: "\u{1b}[A") // CUU 1 term.getCursorLocation() // actual (10, 0) — expected (9, 0): CUD/LF normalize, CUU doesn't term.feed(text: "Z") term.getCharacter(col: 9, row: 0) // actual "\0" — expected "Z" term.getCharacter(col: 0, row: 1) // actual "Z" — spuriously wrapped a row low ``` CUB variant: from the same pending state, `ESC[D` lands at x=9; xterm (clear pending → move from col 9) lands at x=8. **Expected:** vertical moves clear pending-wrap like CUD/LF already do; CUB restricts before subtracting like cmdBackspace already does; the caret view clamps to the last column. Found while running zsh-autosuggestions under heavy repaints; zsh's own xenl handling masks most of it in practice, but the caret-view displacement is user-visible. Happy to open a PR with the two-line normalizations + regression tests if that's welcome.
0 条评论