Fix #167 r2: gutter rows align with wrapped visual rows #193

Closed
fen wants to merge 1 commits from fix-167-r2 into dev
Showing only changes of commit 71c53b65f3 - Show all commits
+29 -12
View File
@@ -1,7 +1,13 @@
// #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.
// lines; the gutter must show one number per logical line, aligned with its
// FIRST visual row, and blank rows below it so later numbers don't drift.
//
// Row accounting must be based on actual rendered geometry, not per-span
// height: in a pre-wrap container the newline text nodes BETWEEN the
// .codeline blocks each render as their own anonymous-block row, so a
// logical line's visual footprint is (next line's top - this line's top),
// not this line's own box height. Measuring the gap between consecutive
// line tops captures both the wrapped rows and the newline row.
(function () {
var body = document.getElementById('codebody');
var gutter = document.getElementById('gutter');
@@ -28,21 +34,32 @@
var lines = body.querySelectorAll('.codeline');
var s = '';
if (wrapOn() && lines.length) {
// 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.
// Rows per logical line = rendered vertical gap to the next line's
// first row (wrapped rows + the pre-wrap newline row between blocks),
// divided by line-height. The last line has no following newline, so
// its own box height is correct. 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: only width changes re-trigger it.
var lh = parseFloat(getComputedStyle(body).lineHeight) || 1;
var tops = [];
for (var i = 0; i < lines.length; i++) {
tops.push(lines[i].getBoundingClientRect().top);
}
gutter.textContent = '';
var frag = document.createDocumentFragment();
for (var i = 0; i < lines.length; i++) {
for (var j = 0; j < lines.length; j++) {
var span;
if (j < lines.length - 1) {
span = tops[j + 1] - tops[j];
} else {
span = lines[j].getBoundingClientRect().height;
}
var rows = Math.max(1, Math.round(span / lh));
var num = document.createElement('span');
num.className = 'gutline';
num.textContent = String(i + 1);
num.textContent = String(j + 1);
frag.appendChild(num);
var rows = Math.max(1, Math.round(lines[i].getBoundingClientRect().height / lh));
for (var r = 1; r < rows; r++) {
var blank = document.createElement('span');
blank.className = 'gutline';