Optimize line metrics computation in App.tsx

This commit is contained in:
2026-02-01 03:31:57 +01:00
parent b5a6b556c9
commit 997c8767e6

View File

@@ -470,55 +470,53 @@ export default function App() {
} }
}, [defaultLineHeight, measureTick, showLineNumbersActive, sidebarCollapsed, historyOpen, textSize]); }, [defaultLineHeight, measureTick, showLineNumbersActive, sidebarCollapsed, historyOpen, textSize]);
useEffect(() => { const computeLineMetrics = useCallback(() => {
if (!showLineNumbersActive) return;
fenwickRef.current.reset(lineCount);
lineHeightsRef.current = new Array(lineCount).fill(0);
setLineNumbersVersion((version) => version + 1);
updateVisibleRange();
}, [defaultLineHeight, lineCount, measureTick, showLineNumbersActive, textSize, updateVisibleRange]);
useLayoutEffect(() => {
if (!showLineNumbersActive || !defaultLineHeight) return; if (!showLineNumbersActive || !defaultLineHeight) return;
const textarea = textareaRef.current; const textarea = textareaRef.current;
const measureContainer = measureRef.current; if (!textarea) return;
const measureLine = measureLineRef.current; const styles = window.getComputedStyle(textarea);
if (!textarea || !measureContainer || !measureLine) return; const paddingLeft = parseFloat(styles.paddingLeft) || 0;
measureContainer.style.width = `${textarea.clientWidth}px`; const paddingRight = parseFloat(styles.paddingRight) || 0;
if (visibleRange.end < visibleRange.start) return; const contentWidth = Math.max(1, textarea.clientWidth - paddingLeft - paddingRight);
let changed = false; let canvas = measureCanvasRef.current;
for (let i = visibleRange.start; i <= visibleRange.end; i += 1) { if (!canvas) {
const lineText = lines[i] ?? ""; canvas = document.createElement("canvas");
measureLine.textContent = lineText.length > 0 ? lineText : " "; measureCanvasRef.current = canvas;
const height = Math.ceil(measureLine.getBoundingClientRect().height);
const prev = lineHeightsRef.current[i] || 0;
const base = prev || defaultLineHeight;
if (height > 0 && height !== prev) {
lineHeightsRef.current[i] = height;
fenwickRef.current.add(i, height - base);
changed = true;
} }
const context = canvas.getContext("2d");
if (!context) return;
context.font = styles.font;
const charWidth = context.measureText("M").width || 1;
const columns = Math.max(1, Math.floor(contentWidth / charWidth));
const heights = new Array(lineCount);
const tops = new Array(lineCount + 1);
tops[0] = 0;
for (let i = 0; i < lineCount; i += 1) {
const length = lines[i]?.length ?? 0;
const wraps = Math.max(1, Math.ceil(length / columns));
const height = wraps * defaultLineHeight;
heights[i] = height;
tops[i + 1] = tops[i] + height;
} }
if (changed) { lineHeightsRef.current = heights;
lineTopsRef.current = tops;
setLineNumbersVersion((version) => version + 1); setLineNumbersVersion((version) => version + 1);
scheduleUpdateVisibleRange(); updateVisibleRange();
}
if (lineNumbersRef.current) { if (lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textarea.scrollTop; lineNumbersRef.current.scrollTop = textarea.scrollTop;
} }
}, [ }, [
defaultLineHeight, defaultLineHeight,
lineCount,
lines, lines,
measureTick,
scheduleUpdateVisibleRange,
showLineNumbersActive, showLineNumbersActive,
sidebarCollapsed, updateVisibleRange
historyOpen,
textSize,
visibleRange.end,
visibleRange.start
]); ]);
useEffect(() => {
computeLineMetrics();
}, [computeLineMetrics, measureTick, sidebarCollapsed, historyOpen, textSize]);
useEffect(() => { useEffect(() => {
if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) { if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop; lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;