Skip to content

Commit

Permalink
Line Highlight: Fixed linkable line numbers not being initialized (#2732
Browse files Browse the repository at this point in the history
)
  • Loading branch information
RunDevelopment authored Feb 7, 2021
1 parent ec9767d commit ccc73ab
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 52 deletions.
2 changes: 1 addition & 1 deletion plugins/line-highlight/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ <h2>Compatible with <a href="plugins/line-numbers">Line numbers</a></h2>
<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>
<pre id="linkable" class="line-numbers linkable-line-numbers" data-start="20" data-src="plugins/line-highlight/prism-line-highlight.js"></pre>

</section>

Expand Down
113 changes: 63 additions & 50 deletions plugins/line-highlight/prism-line-highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
return;
}

var LINE_NUMBERS_CLASS = 'line-numbers';
var LINKABLE_LINE_NUMBERS_CLASS = 'linkable-line-numbers';

/**
* @param {string} selector
* @param {ParentNode} [container]
Expand All @@ -21,8 +24,7 @@
* @returns {boolean}
*/
function hasClass(element, className) {
className = " " + className + " ";
return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
return element.classList.contains(className);
}

/**
Expand Down Expand Up @@ -82,6 +84,32 @@
- pxToNumber(parentStyle.paddingTop);
}

/**
* Returns whether the Line Highlight plugin is active for the given element.
*
* If this function returns `false`, do not call `highlightLines` for the given element.
*
* @param {HTMLElement | null | undefined} pre
* @returns {boolean}
*/
function isActiveFor(pre) {
if (!pre || !/pre/i.test(pre.nodeName)) {
return false;
}

if (pre.hasAttribute('data-line')) {
return true;
}

if (pre.id && Prism.util.isActive(pre, LINKABLE_LINE_NUMBERS_CLASS)) {
// Technically, the line numbers plugin is also necessary but this plugin doesn't control the classes of
// the line numbers plugin, so we can't assume that they are present.
return true;
}

return false;
}

/**
* Highlights the lines of the given pre.
*
Expand All @@ -94,14 +122,14 @@
* @returns {() => void}
*/
function highlightLines(pre, lines, classes) {
lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');
lines = typeof lines === 'string' ? lines : (pre.getAttribute('data-line') || '');

var ranges = lines.replace(/\s+/g, '').split(',').filter(Boolean);
var offset = +pre.getAttribute('data-line-offset') || 0;

var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
var hasLineNumbers = hasClass(pre, 'line-numbers');
var hasLineNumbers = Prism.util.isActive(pre, LINE_NUMBERS_CLASS);
var codeElement = pre.querySelector('code');
var parentElement = hasLineNumbers ? pre : codeElement || pre;
var mutateActions = /** @type {(() => void)[]} */ ([]);
Expand Down Expand Up @@ -173,49 +201,36 @@
});

var id = pre.id;
if (hasLineNumbers && id) {
if (hasLineNumbers && Prism.util.isActive(pre, LINKABLE_LINE_NUMBERS_CLASS) && id) {
// This implements linkable line numbers. Linkable line numbers use Line Highlight to create a link to a
// specific line. For this to work, the pre element has to:
// 1) have line numbers,
// 2) have the `linkable-line-numbers` class or an ascendant that has that class, and
// 3) have an id.

var linkableLineNumbersClass = 'linkable-line-numbers';
var linkableLineNumbers = false;
var node = pre;
while (node) {
if (hasClass(node, linkableLineNumbersClass)) {
linkableLineNumbers = true;
break;
}
node = node.parentElement;
}

if (linkableLineNumbers) {
if (!hasClass(pre, linkableLineNumbersClass)) {
// add class to pre
mutateActions.push(function () {
pre.className = (pre.className + ' ' + linkableLineNumbersClass).trim();
});
}

var start = parseInt(pre.getAttribute('data-start') || '1');

// iterate all line number spans
$$('.line-numbers-rows > span', pre).forEach(function (lineSpan, i) {
var lineNumber = i + start;
lineSpan.onclick = function () {
var hash = id + '.' + lineNumber;

// this will prevent scrolling since the span is obviously in view
scrollIntoView = false;
location.hash = hash;
setTimeout(function () {
scrollIntoView = true;
}, 1);
};
if (!hasClass(pre, LINKABLE_LINE_NUMBERS_CLASS)) {
// add class to pre
mutateActions.push(function () {
pre.classList.add(LINKABLE_LINE_NUMBERS_CLASS);
});
}

var start = parseInt(pre.getAttribute('data-start') || '1');

// iterate all line number spans
$$('.line-numbers-rows > span', pre).forEach(function (lineSpan, i) {
var lineNumber = i + start;
lineSpan.onclick = function () {
var hash = id + '.' + lineNumber;

// this will prevent scrolling since the span is obviously in view
scrollIntoView = false;
location.hash = hash;
setTimeout(function () {
scrollIntoView = true;
}, 1);
};
});
}

return function () {
Expand Down Expand Up @@ -261,9 +276,7 @@

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

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
if (!isActiveFor(pre)) {
return;
}

Expand All @@ -287,9 +300,7 @@

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

if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
if (!isActiveFor(pre)) {
return;
}

Expand All @@ -298,20 +309,22 @@
var hasLineNumbers = Prism.plugins.lineNumbers;
var isLineNumbersLoaded = env.plugins && env.plugins.lineNumbers;

if (hasClass(pre, 'line-numbers') && hasLineNumbers && !isLineNumbersLoaded) {
if (hasClass(pre, LINE_NUMBERS_CLASS) && hasLineNumbers && !isLineNumbersLoaded) {
Prism.hooks.add('line-numbers', completeHook);
} else {
var mutateDom = highlightLines(pre, lines);
var mutateDom = highlightLines(pre);
mutateDom();
fakeTimer = setTimeout(applyHash, 1);
}
});

window.addEventListener('hashchange', applyHash);
window.addEventListener('resize', function () {
var actions = $$('pre[data-line]').map(function (pre) {
return highlightLines(pre);
});
var actions = $$('pre')
.filter(isActiveFor)
.map(function (pre) {
return highlightLines(pre);
});
actions.forEach(callFunction);
});

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 ccc73ab

Please sign in to comment.