I revamped the UI for advanced MathJax configuration for the upcoming Marked 2.5 update I mentioned recently. The configuration is simply a field where you can input additional inline configuration options using the JSON syntax you’d find in a script tag in an HTML document.
When writing JSON in a text field without the benefit of syntax highlighting and linters, though, it’s easy to make a mistake, and Marked would only let you know that when refreshing a document and attempting to implement the configuration during load. I wanted it to be able to provide immediate feedback.
Cocoa provides some decent JSON parsing libraries, but all I needed to know was whether it was valid syntax for a JavaScript engine to parse. I ended up using JavaScriptCore to quickly allocate and validate the string. It works quite well, though I haven’t benchmarked it very thoroughly.
This below code is from an NSTextView subclass. It takes the field’s current string, trims it and removes newlines, then attempts to run a JSON.stringify() in a JavaScriptCore context.
- (void)didChangeText
{
[super didChangeText];
NSColor *errColor = [NSColor colorWithCalibratedRed:0.445
green:0.009
blue:0.027
alpha:1.000];
NSColor *okColor = [NSColor blackColor];
NSString *input = [[[self string] straightenQuotes] trim];
input = [input replace:RX(@"[\\n\\r]+") with:@""];
if (![input isMatch:RX(@"^\\{.*?\\}$")])
{
input = [NSString stringWithFormat:@"{%@}",
input];
}
NSString *scriptString = [NSString stringWithFormat:@"JSON.stringify(%@)",
input];
JSGlobalContextRef jsx = JSGlobalContextCreate(NULL);
JSStringRef scriptJS = JSStringCreateWithCFString(
(CFStringRef)CFStringCreateWithCString(
kCFAllocatorDefault,
scriptString.UTF8String,
kCFStringEncodingUTF8
)
);
BOOL valid = JSCheckScriptSyntax(jsx, scriptJS, NULL, 0, NULL);
JSStringRelease(scriptJS);
if (!valid)
[self setTextColor:errColor];
else
[self setTextColor:okColor];
}
For reference, the RX()
methods come from Objective-C Regex Categories by Josh Wright. They are very handy shortcuts for the NSRegularExpression syntax1. Also note that it calls a method called “straightenQuotes”, which is from an NSString category and simply dumbs down smart quotes:
- (NSString *)straightenQuotes
{
NSString *input = [self replace:RX(@"[“”]") with:@"\""];
input = [input replace:RX(@"[‘’]") with:@"'"];
return input;
}
This is because even though the text field disables smart quotes, system settings can override it, and users can paste them into the field, which can be a hard bug to track down in customer support.
If this is useful to you, great. If you see any major issues with the choice or in the implementation, I welcome the input.
Here’s a good (though aging) intro to JavaScriptCore.
-
Yes, I should be working toward Swift, but this codebase is quite well grounded in Obj-C, and I’m easily intimidated by large undertakings that will break things that are working. ↩