Line numbering is actually surprisingly hard

« Back to blog :: September 29, 2012

Line numbering is actually surprisingly hard

Let me share with you a little tale involving line numbers.

One of the required features for Agora Octave (which I’ve been working on for the past couple of months, as part of the European Space Agency’s Summer of Code 2012) is the ability to display syntax-highlighted code. Ideally, the code would come adorned with correctly-aligned line numbers as well (which can be linked to directly), and the code would be wrapped around so that you don’t have to scroll horizontally to view the end of long lines.

Here’s an example of what it should look like:

Yay line numbers

My first attempt towards this noble goal was a fairly lackluster one that resulted in a horizontal scrollbar whenever there was one line that was too wide for the box:

Horizontal scrollbar

It was pointed out in the mailing list that enabling line-wrapping by default might be better, which I was able to accomplish by simply setting the white-space CSS attribute to pre-wrap. Unfortunately, when you add line numbers to the mix, this is what happens:

Uhoh, line numbers

One way to fix that is to output the line number within the same element as the contents of the line. Using a table, this involves showing each line within a <tr>, with one <td> for the line number and one <td> for the contents of the line. This is the result:

Everything is perfect! Or so I thought

Which, as I posted about several weeks ago, seemed to resolve the problem.

A wild complication appears

The day after I pushed the line-number alignment fix, I was informed via IRC that what I thought to be a bulletproof solution actually broke a very important feature: the ability to highlight a code snippet and then copy and paste it elsewhere. See, the fact that the line numbers were now integrated with the code itself meant that when you try to highlight a piece of code, you end up getting the line numbers as well:

This is not what was meant by syntax highlighting

At first, I panicked, thinking that all my efforts were for nought and that I would never get to live in a world in which Agora had a decent way of displaying code.

Then, I recalled the training I had received at boot camp, from my short stint in the Web Developer Paramilitary Forces: “Don’t panic. There is probably some CSS attribute for that. Also, it probably doesn’t work in Internet Explorer, but who cares. Does anyone even use that anymore?”

So I stopped panicking and did a search for css disable selection. The top result was a StackOverflow post that happened to be exactly what I wanted. Essentially, the trick is to make use of the user-select CSS attribute (and all of the vendor-prefixed variations, for the different browsers out there) on the table cells containing the line numbers:

-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

Two minutes later, I had pushed a fix, and the world was beautiful once more. (In my haste to get out a fix, I made a typo, which was corrected twenty minutes later. Not sure why I didn’t just copy and paste; StackOverflow doesn’t even have line numbers.)

Here’s what it looked like after the fix had been applied:

Proudest moment of my life

If that were the end of it, I would probably have forgotten about the topic entirely and would certainly never have written a blog post about it. By which I mean, that was not the end of it.

The user-select property is a lie

About three days ago, my SOC mentor Jordi informed me that although line numbers no longer appeared to be selected, they would still show up when you tried to copy and paste a snippet. Once again, my world was shattered. Looking into this more, I discovered that this issue has been reported as a bug for Chromium and Firefox (and probably other browsers). In some reports, the issue is said to have been fixed. However, the Mozilla Developer Network specifications for the user-select property seem to indicate that this is actually intended behaviour:

Controls the appearance (only) of selection. This does not have any affect on actual selection operation.

After shedding some tears, I decided I had several options. One was to go back to the original system, with the horizontal scrollbar, which seems to be the most popular way of aligning line numbers (see: Github, Bitbucket). Another was to simply ignore the fact that users couldn’t select text without catching the line number as well, which is what Trac and the Mercurial web server seem to be doing. And then there’s SourceForge, which, I believe, is a strong proponent of the innovative “If the line is too long, then we’ll just cut it off, lol” method. Pastebin.com comes the closest I’ve ever seen to solving this problem, with the line numbers properly aligned and not selected when you highlight the code (even if they appear to be), but they introduce weird whitespace to the beginning of lines and to be honest I have no idea what they’re doing.

I ended up going with the other option: making it work. It took a while, and it’s not the most elegant one, but it does work. You can view a demo here: http://agora.dellsystem.me/snippet/8DVW/ (although if it doesn’t work for you, I’m starting to think that I’d rather not know).

The final solution

Although I hate the idea of using Javascript to accomplish what really should be possible without it, it seemed like I had no choice. Line numbers are now inserted via Javascript. The container for the snippet is relatively positioned, and the line numbers are absolutely positioned, with their top offset set to the top offset of the line they are meant to indicate. To see how this is done, check out the commit.

Other solutions

Closing remarks

Was this worth it? Well, maybe. I learned some things about CSS (and, in the process, myself!!11). In any case, it’s not like I had a better way to spend my Friday night. Unfortunately, this does mean that I wasn’t able to finish the features for Agora that I promised last week. Expect an update on that in the next few days.

My hope is that someone, someday, who happens to be looking at an Octave snippet on Agora, will notice my painstakingly crafted solution to the hardest problem in computer science. And, instead of thinking, “Wow, I can’t believe someone went to the trouble of making this combination of features work together, that’s such a pointless thing to do”, this person will instead think, “Wow, what a brilliant solution to an otherwise intractable problem. This web developer is clearly a genius, and we should give her a job.”

At the very least, I should be able to get a paper out of this. I’m thinking Nature.