From 5ff01ce86b374403ab28c456a23a1c33fe0f7e3c Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 17 Aug 2026 23:03:27 +0200 Subject: [PATCH] perf(text): size the line numbers in one layout, not one per line `updateLineNumberHeight` set a gutter cell's height and then read the next line's `getBoundingClientRect()`, so every read forced the layout the write before it had invalidated - one full layout of the whole document per line. Read every height first, then write them. Measured in a WebView on a Pixel 9 Pro emulator, a 1 MB text file (7.8k lines): | | ready | in this function | |---|---|---| | before | 38.2s | 37.6s | | after | 0.26s | 6ms | The cost is linear in the number of lines from here: 10 MB takes 3.0s and 25 MB 6.7s, both of which are the browser parsing and laying out two elements per line rather than anything this function does. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TouAQNfktsp9THcceennEX --- CHANGELOG.md | 2 ++ src/odr/internal/html/frontend.cpp | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acf81d48..c054a086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ The release run heads these entries with the version and opens a fresh - Prose is no longer read as a csv. A separator that every field follows with a space, in fields long enough to be sentences, is punctuation; `a, b, c` with short values is still a csv. One record is a line, not a table. +- A large text file appears at once: sizing the line numbers laid the page out + once per line. A megabyte took 38s and now takes under a second. ## v6.7.1 - 2026-08-16 diff --git a/src/odr/internal/html/frontend.cpp b/src/odr/internal/html/frontend.cpp index 67ed2771..0f3063b0 100644 --- a/src/odr/internal/html/frontend.cpp +++ b/src/odr/internal/html/frontend.cpp @@ -800,12 +800,19 @@ constexpr std::string_view text_js = R"js( // The measured height is fractional; `offsetHeight` would round it per line // and the numbers would walk away from the lines they belong to. + // + // Every height is read before any is written: interleaving them makes each + // read force the layout the write before it invalidated, one per line. TextEditor.prototype.updateLineNumberHeight = function () { var nrCells = this.textNr.querySelectorAll("div"); var textCells = this.textBody.querySelectorAll("div"); - for (var i = 0; i < textCells.length && i < nrCells.length; ++i) { - nrCells[i].style.height = - textCells[i].getBoundingClientRect().height + "px"; + var count = Math.min(textCells.length, nrCells.length); + var heights = new Array(count); + for (var i = 0; i < count; ++i) { + heights[i] = textCells[i].getBoundingClientRect().height; + } + for (var j = 0; j < count; ++j) { + nrCells[j].style.height = heights[j] + "px"; } };