HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

In-browser syntax highlighter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
highlighterbrowsersyntax

Problem

I've made a simple in-browser syntax highlighter using JQuery. It's pretty simple, it just wraps keywords, integers, and such in ` elements with color styles.



"use strict";

/**
* Replaces any JS code with
* highlighted JS code.
*/
function colorJS(element) {
$(element).html(
$(element).text()
.replace(/\bfunction\b/g, 'function')
.replace(/\bvar\b/g, 'var')
.replace(/\bwhile\b/g, 'while')
.replace(/\bdo\b/g, 'do')
.replace(/\bfor\b/g, 'for')
.replace(/\bif\b/g, 'if')
.replace(/\belse\b/g, 'else')
.replace(/\btry\b/g, 'try')
.replace(/\bcatch\b/g, 'catch')
.replace(/\bthrow\b/g, 'throw')
.replace(/\bswitch\b/g, 'switch')
.replace(/\bcase\b/g, 'case')
.replace(/\bin\b/g, 'in')
.replace(/\breturn\b/g, 'return')
.replace(/\bnull\b/g, 'null')
.replace(/\bthis\b/g, 'this')
.replace(/0/g, '0')
.replace(/1/g, '1')
.replace(/2/g, '2')
.replace(/3/g, '3')
.replace(/4/g, '4')
.replace(/5/g, '5')
.replace(/6/g, '6')
.replace(/7/g, '7')
.replace(/8/g, '8')
.replace(/9/g, '9')
);
}

/**
* Replaces any Python code with
* highlighted python code.
*/
function colorPy(element) {
$(element).html(
$(element).text()
.replace(/\bprint\b/g, 'print')
.replace(/\bdef\b/g, 'def')
.replace(/\bpass\b/g, 'pass')
.replace(/\bclass\b/g, 'class')
.replace(/\bself\b/g, 'self')
.replace(/\bif\b/g, 'if')
.replace(/\belif\b/g, 'elif')
.replace(/\belse\b/g, 'else')
.replace(/\bglobal\b/g, 'global')
.replace(/\bimport\b/g, 'import')
.replace(/\bfrom\b/g, 'from')
.replace(/\btry\b/g, 'try')
.replace(/\bexcept\b/g, 'except')
.replace(/\bfinally\b/g, 'finally')
.replace(/\breturn\b/g, 'return')
.replace(/\bfor\b/g, 'for')
.replace(/\bin\b/g, 'in')
.replace(/\bis\b/g, 'is')
.replace(/\bbreak\b/g, 'break')
.replace(/\bcontinue\b/g, 'continue')
.replace(/\bassert\b/g, 'assert')
.replace(/\bas\b/g, 'as')
.replace(/\bwith\b/g, 'with')
.replac

Solution

To make it more maintainable and reduce duplication, I'd list the keywords etc. in arrays, and use classes for the styling.

That fits nicely as an object with class names as the keys, and an array of strings as values, e.g.:

var syntax = {
  keyword: ["function", "var", "while", ... ],
  globals: ["null", "this", "undefined"],
  literal: ["\\d+"] // numbers
};


Note the double-escaping. To match digits the simplest regex is just \d+, but it still has to be expressed as a string rather than a regex literal, which means escaping the backslash with another backslash (\\d).

Each array can then be joined into a branching regex like so:

new RegExp("\\b(" + array.join("|") + ")\\b", "g")


which, for the globals array above will equal the following regex literal:

/\b(null|this|undefined)\b/g


... which can then be used for replacing parts of the string.

Last trick is to use replace() with a callback as the 2nd argument. The callback receives each capture group as an argument (i.e. 1st argument is capture 0 - the whole match - while 1, 2, 3, etc. refers to explicit groups, if any) and its return value is used as the replacement string. Thus you avoid hardcoding the replacement string.

By the way, I'd also wrap text in ` elements instead of spans, as it's more semantically fitting.

Here's an example (just for the JS code):



"use strict";

function highlight(text, syntax) {
var className, regex, list;
for(className in syntax) {
if(!syntax.hasOwnProperty(className)) continue;
list = syntax[className].join("|");
regex = new RegExp("\\b(" + list + ")\\b", "g");
text = text.replace(regex, function (_, string) {
return '' + string + '';
});
}
return text;
}

function colorJS(element) {
var text, html;

text = $(element).text();

html = highlight(text, {
keyword: ["function", "var", "while", "do", "for", "if", "else", "try", "catch", "throw", "switch", "case", "in", "return"],
globals: ["null", "this"],
literal: ["\\d+"]
});

$(element).html(html);
}

$(document).ready(function() {
colorJS('.code-js');
});
.code-js code.keyword { color: blue }
.code-js code.globals { color: lightgreen }
.code-js code.literal { color: green }


JavaScript code

var a = 10 * 2;
var b = null;
var c = 'ergo';

function func() {
return null && this;
}

if(a) {

}
else if(!a) {

}
else {

}

while(a) {

}

do {

} while(a);
for(var n = 0; n <= 10; ++n) {

}

switch(a) {
case 1:
console.log(f);
}

for(var n in [1, 2, 3]) {

}

try {

}
catch(error) {

}
`

Code Snippets

var syntax = {
  keyword: ["function", "var", "while", ... ],
  globals: ["null", "this", "undefined"],
  literal: ["\\d+"] // numbers
};
new RegExp("\\b(" + array.join("|") + ")\\b", "g")
/\b(null|this|undefined)\b/g

Context

StackExchange Code Review Q#90351, answer score: 16

Revisions (0)

No revisions yet.