Skip to content

Commit 26f9aed

Browse files
author
Nitai Sasson
committed
Prevent Ferris from hiding code
1 parent 8695fc2 commit 26f9aed

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

ferris.css

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ body.ayu .not_desired_behavior {
1919
background: #501f21;
2020
}
2121

22+
:root {
23+
--ferris-large-width: 4.5em;
24+
--ferris-small-width: 2.3em;
25+
}
26+
2227
.ferris-container {
2328
position: absolute;
2429
z-index: 99;
@@ -33,11 +38,19 @@ body.ayu .not_desired_behavior {
3338
}
3439

3540
.ferris-large {
36-
width: 4.5em;
41+
width: var(--ferris-large-width);
3742
}
3843

3944
.ferris-small {
40-
width: 2.3em;
45+
width: var(--ferris-small-width);
46+
}
47+
48+
.ferris-buffer-large {
49+
margin-right: calc(var(--ferris-large-width) + 1.0em);
50+
}
51+
52+
.ferris-buffer-small {
53+
margin-right: calc(var(--ferris-small-width) + 0.5em);
4154
}
4255

4356
.ferris-explain {

ferris.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ function attachFerrises(type) {
5252
continue;
5353
}
5454

55-
container.appendChild(createFerris(type, size));
55+
let ferris = createFerris(type, size)
56+
container.appendChild(ferris);
57+
giveFerrisSpace(codeBlock, ferris, size);
5658
}
5759
}
5860

@@ -100,3 +102,72 @@ function createFerris(type, size) {
100102

101103
return a;
102104
}
105+
106+
/**
107+
* Put each line ending in a span. For each of those spans,
108+
* if Ferris might hide it, give it a safety buffer.
109+
* @param {HTMLElement} codeBlock
110+
* @param {HTMLAnchorElement} ferris
111+
* @param {'small' | 'large'} size
112+
*/
113+
function giveFerrisSpace(codeBlock, ferris, size) {
114+
// sanity checking + lint awareness
115+
const ferrisImage = ferris.firstChild;
116+
if (!(ferrisImage instanceof HTMLImageElement)) {
117+
console.error("ferris should be <a> containing <img>", ferris);
118+
return;
119+
}
120+
121+
/** @type {HTMLSpanElement[]} */
122+
const lineEndings = []; // line endings which might be hidden by Ferris
123+
124+
const walker = document.createTreeWalker(codeBlock, NodeFilter.SHOW_TEXT);
125+
const re = /^(.*?)\n(.*)$/s
126+
127+
while (walker.nextNode()) {
128+
const current = walker.currentNode;
129+
const parent = current.parentNode;
130+
131+
// sanity checking + lint awareness
132+
if (!(current instanceof Text) || !parent) {
133+
continue;
134+
}
135+
136+
let re_results;
137+
while (re_results = current.textContent.match(re)) {
138+
// text node contains newline
139+
const [_, beforeNewline, afterNewline] = re_results;
140+
141+
// line ending gets a span
142+
const lineEnd = document.createElement("span");
143+
lineEnd.textContent = beforeNewline;
144+
lineEndings.push(lineEnd);
145+
parent.insertBefore(lineEnd, current);
146+
147+
// newline now stands alone
148+
parent.insertBefore(document.createTextNode("\n"), current);
149+
150+
// rest of the text
151+
current.textContent = afterNewline;
152+
// current might still contain newlines, so we go again until it doesn't
153+
}
154+
}
155+
156+
codeBlock.normalize(); // not strictly necessary, but good practice to leave the DOM normalized
157+
158+
// setTimeout so getBoundingClientRect returns valid results
159+
setTimeout(() => {
160+
const f = ferrisImage.getBoundingClientRect();
161+
lineEndings.forEach((s) => {
162+
const {bottom, top} = s.getBoundingClientRect();
163+
if ( // vertical overlap between ferris and span
164+
(bottom >= f.top && bottom <= f.bottom)
165+
|| (top >= f.top && top <= f.bottom)
166+
|| (f.top >= top && f.top <= bottom)
167+
) {
168+
// buffer needed!
169+
s.classList.add("ferris-buffer-" + size);
170+
}
171+
});
172+
});
173+
}

0 commit comments

Comments
 (0)