Tuesday 6 March 2012

Adding language choice to FogBugz's code snippets

Surprisingly, the "code snippet" widget used by the FogBugz wiki feature doesn't support telling the pretty-printer (they're using Google's google-code-prettify script) what language the text is in. Since the script can't always tell what language the code is in, this is a problem. And apparently, I'm the first one to ask about this. Wow.

The answer is to write a BugMonkey script and install it in your FogBugz installation. Here's my first take at it, dashed off fairly quickly but apparently functional:

name:          Add language support to code snippet pretty-printing in FogBugz
description: Fixes code snipeet pretty-printing in FogBugz by adding the ability to specify a language.
author: T.J. Crowder [tj at crowder software dot com]
version: 1.0.0.0

js:

// Written by T.J. Crowder [tj at crowder software dot com]
// Licensed under the Creative Commons Attribution License 2.0 (UK)
// http://creativecommons.org/licenses/by/2.0/uk/
//
// At the beginning of each code snippet, you can optionally include a line
// defining the language, in the form:
//
// lang_xyz:
//
// This must be the first line.
//
// The script below will find these, extract the "xyz" from it, remove it and
// any line break following it, and add "lang-xyz" to the `pre` element.
// If any matches were found, when done `prettyPrint` is called to reformat
// the elements.
//
// Example:
//
// lang_sql:
// -- A comment
// CREATE TABLE [Foo] (
// [Bar] NVARCHAR(MAX)
// )
//
// ...renders without the first line, with the class "lang-sql" on the element
// so the pretty-printer knows what the language is.
//
// This code may be fairly fragile, depending on the precise workings of the pretty
// printer. It would be better BY FAR if FogBugz updated the code snippet widget to
// support specifying the language.
//
// Many thanks to Ben McCormack and Michel de Ruiter for pointing me in the right
// direction here: http://fogbugz.stackexchange.com/questions/10065
(function($) {
// Our handler
function handlePrettyLanguages() {
var changes = false;
$("pre.prettyprint").each(function() {
var pre, firstElement, span, match, lang, nextpun, nextbr;

firstElement = this.firstChild;
while (firstElement && firstElement.nodeType !== 1) {
firstElement = firstElement.nextSibling;
}
if (firstElement && firstElement.tagName === 'SPAN') {
pre = $(this);
span = $(firstElement);
match = /^\s*lang_([A-Za-z0-9_]+)\s*$/.exec(span.text());
if (match && match[1]) {
lang = match[1];
nextpun = span.next("span.pun");
if (!nextpun[0]) {
nextpun = span;
}
nextbr = nextpun.next("br");
if (!nextbr[0]) {
nextbr = nextpun.next().children().first();
if (nextbr[0] && nextbr[0].tagName !== "BR") {
nextbr = $();
}
}
pre.addClass("lang-" + lang);
span.remove();
if (nextpun !== span) {
nextpun.remove();
}
nextbr.remove();
changes = true;
}
}
});
if (changes) {
prettyPrint();
}
}

// Hook it up on page ready and when BugViewChange events occur
$(handlePrettyLanguages);
$(window).on('BugViewChange', handlePrettyLanguages);
})(jQuery);

You install that via My Settings | Customizations.

Happy pretty printing!

Thursday 1 March 2012

Match everything...except!

Micro-post:

I was truly shocked to find today that in JavaScript regular expressions, . (the decimal point) doesn't do what I thought it did. I thought . meant "match any character." You too? Yeah. But it doesn't. Specifically, . doesn't match line terminators (so, \r, \n, \u2028, and \u2029). From Section 15.10.2.8:

The production Atom :: . evaluates as follows:

  1. Let A be the set of all characters except LineTerminator.
  2. Call CharacterSetMatcher(A, false) and return its Matcher result.

...which if you spend really quite a long time looking tells you that . matches anything but line terminators.

Maybe I'm just parading my ignorance here, but I would have thought that absent the "multiline" flag or something, . matched everything. Nope. If you want to do that, use [\s\S] (e.g., everything that either is or isn't whitespace).

Happy coding!