Fix #167 r2: gutter rows align with wrapped visual rows #193
@@ -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