Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Line highlight: Fixed top offset in combination with Line numbers #2237

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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.