refactor(ui): Optimize rendering performance (#8239)

This commit is contained in:
Gal Zahavi
2025-09-17 15:37:13 -07:00
committed by GitHub
parent d54cdd8802
commit 6756a8b8a9
13 changed files with 499 additions and 85 deletions

View File

@@ -1495,6 +1495,53 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
expect(stripAnsi('')).toBe('');
});
});
describe('Memoization', () => {
it('should keep action references stable across re-renders', () => {
// We pass a stable `isValidPath` so that callbacks that depend on it
// are not recreated on every render.
const isValidPath = () => false;
const { result, rerender } = renderHook(() =>
useTextBuffer({ viewport, isValidPath }),
);
const initialInsert = result.current.insert;
const initialBackspace = result.current.backspace;
const initialMove = result.current.move;
const initialHandleInput = result.current.handleInput;
rerender();
expect(result.current.insert).toBe(initialInsert);
expect(result.current.backspace).toBe(initialBackspace);
expect(result.current.move).toBe(initialMove);
expect(result.current.handleInput).toBe(initialHandleInput);
});
it('should have memoized actions that operate on the latest state', () => {
const isValidPath = () => false;
const { result } = renderHook(() =>
useTextBuffer({ viewport, isValidPath }),
);
// Store a reference to the memoized insert function.
const memoizedInsert = result.current.insert;
// Update the buffer state.
act(() => {
result.current.insert('hello');
});
expect(getBufferState(result).text).toBe('hello');
// Now, call the original memoized function reference.
act(() => {
memoizedInsert(' world');
});
// It should have operated on the updated state.
expect(getBufferState(result).text).toBe('hello world');
});
});
});
describe('offsetToLogicalPos', () => {

View File

@@ -1984,71 +1984,135 @@ export function useTextBuffer({
dispatch({ type: 'move_to_offset', payload: { offset } });
}, []);
const returnValue: TextBuffer = {
lines,
text,
cursor: [cursorRow, cursorCol],
preferredCol,
selectionAnchor,
const returnValue: TextBuffer = useMemo(
() => ({
lines,
text,
cursor: [cursorRow, cursorCol],
preferredCol,
selectionAnchor,
allVisualLines: visualLines,
viewportVisualLines: renderedVisualLines,
visualCursor,
visualScrollRow,
visualToLogicalMap,
allVisualLines: visualLines,
viewportVisualLines: renderedVisualLines,
visualCursor,
visualScrollRow,
visualToLogicalMap,
setText,
insert,
newline,
backspace,
del,
move,
undo,
redo,
replaceRange,
replaceRangeByOffset,
moveToOffset,
deleteWordLeft,
deleteWordRight,
setText,
insert,
newline,
backspace,
del,
move,
undo,
redo,
replaceRange,
replaceRangeByOffset,
moveToOffset,
deleteWordLeft,
deleteWordRight,
killLineRight,
killLineLeft,
handleInput,
openInExternalEditor,
// Vim-specific operations
vimDeleteWordForward,
vimDeleteWordBackward,
vimDeleteWordEnd,
vimChangeWordForward,
vimChangeWordBackward,
vimChangeWordEnd,
vimDeleteLine,
vimChangeLine,
vimDeleteToEndOfLine,
vimChangeToEndOfLine,
vimChangeMovement,
vimMoveLeft,
vimMoveRight,
vimMoveUp,
vimMoveDown,
vimMoveWordForward,
vimMoveWordBackward,
vimMoveWordEnd,
vimDeleteChar,
vimInsertAtCursor,
vimAppendAtCursor,
vimOpenLineBelow,
vimOpenLineAbove,
vimAppendAtLineEnd,
vimInsertAtLineStart,
vimMoveToLineStart,
vimMoveToLineEnd,
vimMoveToFirstNonWhitespace,
vimMoveToFirstLine,
vimMoveToLastLine,
vimMoveToLine,
vimEscapeInsertMode,
};
killLineRight,
killLineLeft,
handleInput,
openInExternalEditor,
// Vim-specific operations
vimDeleteWordForward,
vimDeleteWordBackward,
vimDeleteWordEnd,
vimChangeWordForward,
vimChangeWordBackward,
vimChangeWordEnd,
vimDeleteLine,
vimChangeLine,
vimDeleteToEndOfLine,
vimChangeToEndOfLine,
vimChangeMovement,
vimMoveLeft,
vimMoveRight,
vimMoveUp,
vimMoveDown,
vimMoveWordForward,
vimMoveWordBackward,
vimMoveWordEnd,
vimDeleteChar,
vimInsertAtCursor,
vimAppendAtCursor,
vimOpenLineBelow,
vimOpenLineAbove,
vimAppendAtLineEnd,
vimInsertAtLineStart,
vimMoveToLineStart,
vimMoveToLineEnd,
vimMoveToFirstNonWhitespace,
vimMoveToFirstLine,
vimMoveToLastLine,
vimMoveToLine,
vimEscapeInsertMode,
}),
[
lines,
text,
cursorRow,
cursorCol,
preferredCol,
selectionAnchor,
visualLines,
renderedVisualLines,
visualCursor,
visualScrollRow,
setText,
insert,
newline,
backspace,
del,
move,
undo,
redo,
replaceRange,
replaceRangeByOffset,
moveToOffset,
deleteWordLeft,
deleteWordRight,
killLineRight,
killLineLeft,
handleInput,
openInExternalEditor,
vimDeleteWordForward,
vimDeleteWordBackward,
vimDeleteWordEnd,
vimChangeWordForward,
vimChangeWordBackward,
vimChangeWordEnd,
vimDeleteLine,
vimChangeLine,
vimDeleteToEndOfLine,
vimChangeToEndOfLine,
vimChangeMovement,
vimMoveLeft,
vimMoveRight,
vimMoveUp,
vimMoveDown,
vimMoveWordForward,
vimMoveWordBackward,
vimMoveWordEnd,
vimDeleteChar,
vimInsertAtCursor,
vimAppendAtCursor,
vimOpenLineBelow,
vimOpenLineAbove,
vimAppendAtLineEnd,
vimInsertAtLineStart,
vimMoveToLineStart,
vimMoveToLineEnd,
vimMoveToFirstNonWhitespace,
vimMoveToFirstLine,
vimMoveToLastLine,
vimMoveToLine,
vimEscapeInsertMode,
visualToLogicalMap,
],
);
return returnValue;
}