Fix up/down arrow regression and add test.

This commit is contained in:
jacob314
2026-01-30 13:42:17 -08:00
parent e31cf4bce6
commit cd0d9eba62
2 changed files with 60 additions and 3 deletions

View File

@@ -1359,7 +1359,7 @@ describe('useTextBuffer', () => {
expect(state.cursor).toEqual([0, 0]); expect(state.cursor).toEqual([0, 0]);
}); });
it('move: left/right should treat multi-byte chars as single units for visual cursor', () => { it('move: up/down should treat multi-byte chars as single units for visual cursor', () => {
const { result } = renderHook(() => const { result } = renderHook(() =>
useTextBuffer({ useTextBuffer({
initialText: '🐶🐱', initialText: '🐶🐱',
@@ -1384,6 +1384,61 @@ describe('useTextBuffer', () => {
expect(state.visualCursor).toEqual([0, 1]); expect(state.visualCursor).toEqual([0, 1]);
}); });
it('move: up/down should work on wrapped lines (regression test)', () => {
// Line that wraps into two visual lines
// Viewport width 10. "0123456789ABCDE" (15 chars)
// Visual Line 0: "0123456789"
// Visual Line 1: "ABCDE"
const { result } = renderHook(() =>
useTextBuffer({
viewport: { width: 10, height: 5 },
isValidPath: () => false,
}),
);
act(() => {
result.current.setText('0123456789ABCDE');
});
// Cursor should be at the end: logical [0, 15], visual [1, 5]
expect(getBufferState(result).cursor).toEqual([0, 15]);
expect(getBufferState(result).visualCursor).toEqual([1, 5]);
// Press Up arrow - should move to first visual line
// This currently fails because handleInput returns false if cursorRow === 0
let handledUp = false;
act(() => {
handledUp = result.current.handleInput({
name: 'up',
shift: false,
alt: false,
ctrl: false,
cmd: false,
insertable: false,
sequence: '\x1b[A',
});
});
expect(handledUp).toBe(true);
expect(getBufferState(result).visualCursor[0]).toBe(0);
// Press Down arrow - should move back to second visual line
// This would also fail if cursorRow is the last logical row
let handledDown = false;
act(() => {
handledDown = result.current.handleInput({
name: 'down',
shift: false,
alt: false,
ctrl: false,
cmd: false,
insertable: false,
sequence: '\x1b[B',
});
});
expect(handledDown).toBe(true);
expect(getBufferState(result).visualCursor[0]).toBe(1);
});
it('moveToVisualPosition: should correctly handle wide characters (Chinese)', () => { it('moveToVisualPosition: should correctly handle wide characters (Chinese)', () => {
const { result } = renderHook(() => const { result } = renderHook(() =>
useTextBuffer({ useTextBuffer({

View File

@@ -2905,12 +2905,12 @@ export function useTextBuffer({
return true; return true;
} }
if (keyMatchers[Command.MOVE_UP](key)) { if (keyMatchers[Command.MOVE_UP](key)) {
if (cursorRow === 0) return false; if (visualCursor[0] === 0) return false;
move('up'); move('up');
return true; return true;
} }
if (keyMatchers[Command.MOVE_DOWN](key)) { if (keyMatchers[Command.MOVE_DOWN](key)) {
if (cursorRow === lines.length - 1) return false; if (visualCursor[0] === visualLines.length - 1) return false;
move('down'); move('down');
return true; return true;
} }
@@ -2990,6 +2990,8 @@ export function useTextBuffer({
singleLine, singleLine,
setText, setText,
text, text,
visualCursor,
visualLines,
], ],
); );