Prism Line Number Plugin

By  on  

The Mozilla Developer Network (MDN) is in the midst of a remodel (sorry for the spoiler) and we've been implementing features incrementally.  One larger front-end change we'll be implementing is using the PrismJS for document code syntax highlighting.  One requirement of MDN's syntax highlighter is that line numbers are displayed, a functionality not provided by Prism.  Prism does provide a line-highlighting plugin but not a line-numbering plugin, so I used that plugin as my base and simply simplified it to provide numbers for a lines.

The CSS

The CSS here is copied from the line-highlighting plugin.  I've changed the attribute to data-number, which we'll use instead of data-line, and I've changed the colors used for the line background:

pre[data-number] {
	position: relative;
	padding: 1em 0 1em 3em;
}

.line-number {
	position: absolute;
	left: 0;
	right: 0;
	padding: inherit 0;
	margin-top: 1em; /* Same as .prism's padding-top */

	background: transparent;
	
	pointer-events: none;
	
	line-height: inherit;
	white-space: pre;
}

	.line-number:before,
	.line-number[data-end]:after {
		content: attr(data-start);
		position: absolute;
		top: .4em;
		left: .6em;
		min-width: 1em;
		padding: 0 .5em;
		color: #999;
		font: bold 65%/1.5 sans-serif;
		text-align: center;
		vertical-align: .3em;
		border-radius: 999px;
		text-shadow: none;
		border: 0;
	}
	
	.line-number[data-end]:after {
		content: attr(data-end);
		top: auto;
		bottom: .4em;
	}

Of course you can style the line number element to however you'd like, but this CSS keeps styles fairly consistent with the line-highlighting plugin.

The JavaScript

Here's the JavaScript portion for the plugin in all its glory:

/*
	This plugins was created based on the Prism line-numbering plugin.
	This plugin aims to number all lines and is independent of highlighting.
*/
(function(){

if(!window.Prism || !document.querySelectorAll) {
	return;
}

function $$(expr, con) {
	return Array.prototype.slice.call((con || document).querySelectorAll(expr));
}
    
function numberLines(pre) {
	var offset = +pre.getAttribute('data-line-offset') || 0;
	var lineHeight = parseFloat(getComputedStyle(pre).lineHeight);
	var code = pre.querySelector('code');
	var numLines = code.innerHTML.split('\n').length;
	pre.setAttribute('data-number', '');

	for (var i=1; i <= numLines; i++) {
		var line = document.createElement('div');
		line.className = 'line-number';
		line.setAttribute('data-start', i);
		line.style.top = (i - offset - 1) * lineHeight + 'px';
		
		(code || pre).appendChild(line);
	}
}

Prism.hooks.add('after-highlight', function(env) {
	var pre = env.element.parentNode;
	
	if (!pre || !/pre/i.test(pre.nodeName)) {
		return;
	}

	$$('.line-number', pre).forEach(function (line) {
		line.parentNode.removeChild(line);
	});
	
	numberLines(pre);
});

})();

This code is also based on the syntax-highlighting plugin's code, but much more simplified because there's less line-numbering logic.

If there's enough interest in what I have, I can make a GitHub repo for the plugin.  I haven't to this point because I believe this plugin could/should be merged with the line-highlighting plugin, since in many cases, developers reference line numbers.  In any event, this plugin may be what you're looking for.  Let me know if you have updates or ideas!

Recent Features

  • By
    Write Better JavaScript with Promises

    You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll...

  • By
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

Incredible Demos

  • By
    Editable Content Using MooTools 1.2, PHP, and MySQL

    Everybody and their aerobics instructor wants to be able to edit their own website these days. And why wouldn't they? I mean, they have a $500 budget, no HTML/CSS experience, and extraordinary expectations. Enough ranting though. Having a website that allows for...

  • By
    jQuery UI DatePicker:  Disable Specified Days

    One project I'm currently working on requires jQuery. The project also features a datepicker for requesting a visit to their location. jQuery UI's DatePicker plugin was the natural choice and it does a really nice job. One challenge I encountered was the...

Discussion

  1. Jonas

    This fails in WebKit, as getComputedStyle(pre).lineHeight returns ‘normal’.
    See also: https://github.com/LeaVerou/dabblet/pull/175

    • Ahh yes Jonas; I encountered that issue and explicitly set a line-height within a different stylesheet so as to avoid this issue.

  2. Maybe you missed it, but PrismJS does have a line numbering plugin. More info can be found on http://prismjs.com/plugins/line-numbers/

    • Hmm, that may be new. Thanks Mark!

    • So I just took a look at this and the line-numbers plugin isn’t compatible with the line-highlight plugin; mine is.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!