Skip to content

Commit

Permalink
Line highlight: Fixed top offset in combination with Line numbers (#2237
Browse files Browse the repository at this point in the history
)
  • Loading branch information
RunDevelopment authored Dec 23, 2020
1 parent 2af3e2c commit b40f8f4
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
8 changes: 6 additions & 2 deletions plugins/line-highlight/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@ <h2>Line 43, starting from line 41</h2>

<p><a href="plugins/line-highlight/#play.50-55,60">Linking example</a></p>

<h2>With line numbers</h2>
<pre class="line-numbers" data-line="43" data-start="41" data-src="plugins/line-highlight/prism-line-highlight.js"></pre>
<h2>Compatible with <a href="plugins/line-numbers">Line numbers</a></h2>
<pre class="line-numbers" data-src="plugins/line-numbers/index.html" data-line="1" data-start="-5" style="white-space:pre-wrap;"></pre>

<p>Even with some extra content before the <code>code</code> element.</p>
<pre class="line-numbers" data-line="7"><div style="padding: .25em">Some content</div><hr/><code class="language-markup" id="foo"></code></pre>
<script>document.querySelector('#foo').textContent = document.documentElement.innerHTML;</script>

<h2>With linkable line numbers</h2>
<pre id="foo" class="line-numbers linkable-line-numbers" data-start="20" data-src="plugins/line-highlight/prism-line-highlight.js"></pre>
Expand Down
54 changes: 46 additions & 8 deletions plugins/line-highlight/prism-line-highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,39 @@
}
}());

/**
* Returns the top offset of the content box of the given parent and the content box of one of its children.
*
* @param {HTMLElement} parent
* @param {HTMLElement} child
*/
function getContentBoxTopOffset(parent, child) {
var parentStyle = getComputedStyle(parent);
var childStyle = getComputedStyle(child);

/**
* Returns the numeric value of the given pixel value.
*
* @param {string} px
*/
function pxToNumber(px) {
return +px.substr(0, px.length - 2);
}

return child.offsetTop
+ pxToNumber(childStyle.borderTopWidth)
+ pxToNumber(childStyle.paddingTop)
- pxToNumber(parentStyle.paddingTop);
}

/**
* Highlights the lines of the given pre.
*
* This function is split into a DOM measuring and mutate phase to improve performance.
* The returned function mutates the DOM when called.
*
* @param {HTMLElement} pre
* @param {string} [lines]
* @param {string | null} [lines]
* @param {string} [classes='']
* @returns {() => void}
*/
Expand All @@ -77,9 +102,22 @@
var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
var hasLineNumbers = hasClass(pre, 'line-numbers');
var parentElement = hasLineNumbers ? pre : pre.querySelector('code') || pre;
var codeElement = pre.querySelector('code');
var parentElement = hasLineNumbers ? pre : codeElement || pre;
var mutateActions = /** @type {(() => void)[]} */ ([]);

/**
* The top offset between the content box of the <code> element and the content box of the parent element of
* the line highlight element (either `<pre>` or `<code>`).
*
* This offset might not be zero for some themes where the <code> element has a top margin. Some plugins
* (or users) might also add element above the <code> element. Because the line highlight is aligned relative
* to the <pre> element, we have to take this into account.
*
* This offset will be 0 if the parent element of the line highlight element is the `<code>` element.
*/
var codePreOffset = !codeElement || parentElement == codeElement ? 0 : getContentBoxTopOffset(pre, codeElement);

ranges.forEach(function (currentRange) {
var range = currentRange.split('-');

Expand All @@ -101,7 +139,7 @@
var endNode = Prism.plugins.lineNumbers.getLine(pre, end);

if (startNode) {
var top = startNode.offsetTop + 'px';
var top = startNode.offsetTop + codePreOffset + 'px';
mutateActions.push(function () {
line.style.top = top;
});
Expand All @@ -115,13 +153,13 @@
}
} else {
mutateActions.push(function () {
line.setAttribute('data-start', start);
line.setAttribute('data-start', String(start));

if (end > start) {
line.setAttribute('data-end', end);
line.setAttribute('data-end', String(end));
}

line.style.top = (start - offset - 1) * lineHeight + 'px';
line.style.top = (start - offset - 1) * lineHeight + codePreOffset + 'px';

line.textContent = new Array(end - start + 2).join(' \n');
});
Expand Down Expand Up @@ -222,7 +260,7 @@
var fakeTimer = 0; // Hack to limit the number of times applyHash() runs

Prism.hooks.add('before-sanity-check', function (env) {
var pre = env.element.parentNode;
var pre = env.element.parentElement;
var lines = pre && pre.getAttribute('data-line');

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
Expand All @@ -248,7 +286,7 @@
});

Prism.hooks.add('complete', function completeHook(env) {
var pre = env.element.parentNode;
var pre = env.element.parentElement;
var lines = pre && pre.getAttribute('data-line');

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
Expand Down
2 changes: 1 addition & 1 deletion plugins/line-highlight/prism-line-highlight.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b40f8f4

Please sign in to comment.