patternjavascriptModerate
In-browser syntax highlighter
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 `
/**
* 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
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.:
Note the double-escaping. To match digits the simplest regex is just
Each array can then be joined into a branching regex like so:
which, for the
... which can then be used for replacing parts of the string.
Last trick is to use
By the way, I'd also wrap text in `
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 + '
.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) {
}
`
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/gContext
StackExchange Code Review Q#90351, answer score: 16
Revisions (0)
No revisions yet.