Fix #167: renumber gutter to visual rows when line wrap is on
CI / test (pull_request) Successful in 39s
CI / docker (pull_request) Skipped

Split the paste codebody into per-line block spans (paste-lines.js) and
rebuild the gutter from each line's wrapped height so numbers count
visual rows, not logical lines. Pinned the gutter width (flex 0 0 auto,
3ch) so its row count no longer feeds back into the code column width.
ResizeObserver renumbers on wrap toggle / resize; wrap-off behavior is
unchanged (one number per newline).
This commit is contained in:
fen
2026-09-10 12:08:49 -05:00
parent b603b29359
commit e430eb57b7
2 changed files with 20 additions and 15 deletions
+7 -15
View File
@@ -1,7 +1,7 @@
// #167: with line wrapping on, a logical line can occupy several visual
// lines; the gutter must show one number per VISUAL line. Per-line spans
// give each logical line its own box so offsetTop order stays correct even
// when highlighting spans cross no line boundaries.
// give each logical line its own box so its height identifies the wrapped
// row count; blank gutter rows keep numbers aligned with line starts.
(function () {
var body = document.getElementById('codebody');
var gutter = document.getElementById('gutter');
@@ -31,9 +31,9 @@
// Each logical line block occupies rows = height / line-height when
// wrapped; its number sits on the first row and the remaining rows get
// blank gutter lines so numbers stay aligned with line starts.
// The code column width must not change while measuring (the gutter is
// flex-shrink:0 so its own row count never affects it), and renumber
// must be idempotent to avoid a ResizeObserver feedback loop.
// The gutter has a fixed CSS width now (.code .gutter), so its row
// count never changes the code column width: measurement is stable
// with no measurement/layout feedback.
var lh = parseFloat(getComputedStyle(body).lineHeight) || 1;
gutter.textContent = '';
var frag = document.createDocumentFragment();
@@ -61,19 +61,11 @@
renumber();
// Re-renumber on toggle (theme.js toggles data-wrap on <html>) and on any
// size change (paste of lots of text, window resize, zoom). Only width
// changes of the code column affect wrapping, so observe width only —
// height changes caused by our own renumbering must not re-trigger.
// size change (paste of lots of text, window resize, zoom).
var mo = new MutationObserver(renumber);
mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-wrap'] });
var lastW = body.getBoundingClientRect().width;
if (window.ResizeObserver) {
var ro = new ResizeObserver(function () {
var w = body.getBoundingClientRect().width;
if (Math.abs(w - lastW) < 0.5) return;
lastW = w;
renumber();
});
var ro = new ResizeObserver(function () { renumber(); });
ro.observe(body);
} else {
window.addEventListener('resize', renumber);