Skip to content

Commit

Permalink
Line Highlight: Added linkable line numbers (#2328)
Browse files Browse the repository at this point in the history
This makes individual line numbers clickable creating a link to that specific line for that specific code block.
  • Loading branch information
RunDevelopment committed May 7, 2020
1 parent 2c10ef8 commit eb82e80
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 11 deletions.
11 changes: 11 additions & 0 deletions plugins/line-highlight/index.html
Expand Up @@ -9,6 +9,7 @@
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
<link rel="stylesheet" href="plugins/line-highlight/prism-line-highlight.css" data-noprefix />
<link rel="stylesheet" href="plugins/line-numbers/prism-line-numbers.css" data-noprefix />
<script src="scripts/prefixfree.min.js"></script>

<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
Expand Down Expand Up @@ -51,6 +52,8 @@ <h1>How to use</h1>
<p>You can also link to specific lines on any code snippet, by using the following as a url hash: <code>#{element-id}.{lines}</code> where
<code>{element-id}</code> is the id of the <code>&lt;pre></code> element and <code>{lines}</code> is one or more lines or line ranges that follow the format
outlined above. For example, if there is an element with <code>id="play"</code> on the page, you can link to lines 5-6 by linking to <a href="plugins/line-highlight/#play.5-6">#play.5-6</a></p>

<p>If line numbers are also enabled for a code block and the <code>&lt;pre></code> element has an id, you can add the <code>linkable-line-numbers</code> class to the <code>&lt;pre></code> element. This will make all line numbers clickable and when clicking any line number, it will change the hash of the current page to link to that specific line.</p>
</section>

<section>
Expand All @@ -69,12 +72,20 @@ <h2>Line 43, starting from line 41</h2>
<pre data-line="43" data-line-offset="40" data-src="plugins/line-highlight/prism-line-highlight.js"></pre>

<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>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>

</section>

<footer data-src="templates/footer.html" data-type="text/html"></footer>

<script src="prism.js"></script>
<script src="plugins/line-highlight/prism-line-highlight.js"></script>
<script src="plugins/line-numbers/prism-line-numbers.js"></script>
<script src="scripts/utopia.js"></script>
<script src="components.js"></script>
<script src="scripts/code.js"></script>
Expand Down
10 changes: 10 additions & 0 deletions plugins/line-highlight/prism-line-highlight.css
Expand Up @@ -47,3 +47,13 @@ pre[data-line] {
.line-numbers .line-highlight:after {
content: none;
}

pre[id].linkable-line-numbers span.line-numbers-rows {
pointer-events: all;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:before {
cursor: pointer;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before {
background-color: rgba(128, 128, 128, .2);
}
85 changes: 76 additions & 9 deletions plugins/line-highlight/prism-line-highlight.js
Expand Up @@ -4,15 +4,33 @@
return;
}

function $$(expr, con) {
return Array.prototype.slice.call((con || document).querySelectorAll(expr));
/**
* @param {string} selector
* @param {ParentNode} [container]
* @returns {HTMLElement[]}
*/
function $$(selector, container) {
return Array.prototype.slice.call((container || document).querySelectorAll(selector));
}

/**
* Returns whether the given element has the given class.
*
* @param {Element} element
* @param {string} className
* @returns {boolean}
*/
function hasClass(element, className) {
className = " " + className + " ";
return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
}

/**
* Calls the given function.
*
* @param {() => any} func
* @returns {void}
*/
function callFunction(func) {
func();
}
Expand All @@ -26,8 +44,8 @@
var d = document.createElement('div');
d.style.fontSize = '13px';
d.style.lineHeight = '1.5';
d.style.padding = 0;
d.style.border = 0;
d.style.padding = '0';
d.style.border = '0';
d.innerHTML = '&nbsp;<br />&nbsp;';
document.body.appendChild(d);
// Browsers that round the line-height should have offsetHeight === 38
Expand All @@ -53,7 +71,7 @@
function highlightLines(pre, lines, classes) {
lines = typeof lines === 'string' ? lines : pre.getAttribute('data-line');

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

var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
Expand All @@ -68,6 +86,7 @@
var start = +range[0];
var end = +range[1] || start;

/** @type {HTMLElement} */
var line = pre.querySelector('.line-highlight[data-range="' + currentRange + '"]') || document.createElement('div');

mutateActions.push(function () {
Expand Down Expand Up @@ -115,11 +134,58 @@
});
});

var id = pre.id;
if (hasLineNumbers && 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);
};
});
}
}

return function () {
mutateActions.forEach(callFunction);
};
}

var scrollIntoView = true;
function applyHash() {
var hash = location.hash.slice(1);

Expand Down Expand Up @@ -148,7 +214,9 @@
var mutateDom = highlightLines(pre, range, 'temporary ');
mutateDom();

document.querySelector('.temporary.line-highlight').scrollIntoView();
if (scrollIntoView) {
document.querySelector('.temporary.line-highlight').scrollIntoView();
}
}

var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
Expand Down Expand Up @@ -203,9 +271,8 @@

window.addEventListener('hashchange', applyHash);
window.addEventListener('resize', function () {
var actions = [];
$$('pre[data-line]').forEach(function (pre) {
actions.push(highlightLines(pre));
var actions = $$('pre[data-line]').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.

1 change: 0 additions & 1 deletion plugins/line-numbers/prism-line-numbers.css
Expand Up @@ -27,7 +27,6 @@ pre[class*="language-"].line-numbers > code {
}

.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
Expand Down

0 comments on commit eb82e80

Please sign in to comment.