Fix #167 r2: measure wrapped rows from rendered line geometry
Round-1 accounting rounded each .codeline span's own height to rows, but in pre-wrap the newline text nodes between blocks each render as their own anonymous row, so per-span height undercounts and gutter numbers drift below their line's first visual row. Renumber now derives rows per line from the rendered gap between consecutive line tops (wrapped rows plus the newline row); the last line falls back to its own box height. Wrap-off path unchanged.
This commit is contained in:
@@ -1,7 +1,13 @@
|
|||||||
// #167: with line wrapping on, a logical line can occupy several visual
|
// #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
|
// lines; the gutter must show one number per logical line, aligned with its
|
||||||
// give each logical line its own box so offsetTop order stays correct even
|
// FIRST visual row, and blank rows below it so later numbers don't drift.
|
||||||
// when highlighting spans cross no line boundaries.
|
//
|
||||||
|
// 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 () {
|
(function () {
|
||||||
var body = document.getElementById('codebody');
|
var body = document.getElementById('codebody');
|
||||||
var gutter = document.getElementById('gutter');
|
var gutter = document.getElementById('gutter');
|
||||||
@@ -28,21 +34,32 @@
|
|||||||
var lines = body.querySelectorAll('.codeline');
|
var lines = body.querySelectorAll('.codeline');
|
||||||
var s = '';
|
var s = '';
|
||||||
if (wrapOn() && lines.length) {
|
if (wrapOn() && lines.length) {
|
||||||
// Each logical line block occupies rows = height / line-height when
|
// Rows per logical line = rendered vertical gap to the next line's
|
||||||
// wrapped; its number sits on the first row and the remaining rows get
|
// first row (wrapped rows + the pre-wrap newline row between blocks),
|
||||||
// blank gutter lines so numbers stay aligned with line starts.
|
// divided by line-height. The last line has no following newline, so
|
||||||
// The code column width must not change while measuring (the gutter is
|
// its own box height is correct. The code column width must not
|
||||||
// flex-shrink:0 so its own row count never affects it), and renumber
|
// change while measuring (the gutter is flex-shrink:0 so its own row
|
||||||
// must be idempotent to avoid a ResizeObserver feedback loop.
|
// 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 lh = parseFloat(getComputedStyle(body).lineHeight) || 1;
|
||||||
|
var tops = [];
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
tops.push(lines[i].getBoundingClientRect().top);
|
||||||
|
}
|
||||||
gutter.textContent = '';
|
gutter.textContent = '';
|
||||||
var frag = document.createDocumentFragment();
|
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');
|
var num = document.createElement('span');
|
||||||
num.className = 'gutline';
|
num.className = 'gutline';
|
||||||
num.textContent = String(i + 1);
|
num.textContent = String(j + 1);
|
||||||
frag.appendChild(num);
|
frag.appendChild(num);
|
||||||
var rows = Math.max(1, Math.round(lines[i].getBoundingClientRect().height / lh));
|
|
||||||
for (var r = 1; r < rows; r++) {
|
for (var r = 1; r < rows; r++) {
|
||||||
var blank = document.createElement('span');
|
var blank = document.createElement('span');
|
||||||
blank.className = 'gutline';
|
blank.className = 'gutline';
|
||||||
|
|||||||
Reference in New Issue
Block a user