This is a summary of the findings in Can we run a regex replace at render time?
My goal was to make it easy in a wiki for the user to type fractions as normal ("2/3
") but have them display in a prettier format (" 2⁄3
".) I knew how to write a procedure to do this, but I didn’t want the user to have to invoke a proc (macro, function, whatever), but rather to just enter plain text. I understood how to use regular expression replacement to make the change. I didn’t understand how to apply that without user intervention.
There was a great discussion and two very useful techniques emerged.
First, @btheado pointed out that the wikitext parser/formatter is built extensibly, and we could write our own rule for it:
I’ve posted a version of this that involves the following code:
/*\
title: wikiparser/rules/fraction.js
type: application/javascript
module-type: wikirule
Wiki text inline rule for prettifying simple fractions.
Turns, say, "3/4" into <sup>3</sup>⁄<sub>4</sub>
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "fraction";
exports.types = {inline: true};
exports.init = function(parser) {
this.parser = parser;
var regexText = "\\b(\\d+)\\/(\\d+)\\b"
// Regexp for parser to match
this.matchRegExp = new RegExp(regexText, "mg");
// Regexp to collect numerator and denominator
this.execRegExp = new RegExp(regexText);
};
exports.parse = function() {
// Extract the numerator and denominator
var matches = this.parser.source.slice(this.parser.pos).match(this.execRegExp);
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;
// Return the sup-slash-sub elements
return [{
type: "element",
tag: "sup",
children: [{type: 'text', text: matches[1]}]
}, {
type: "text",
text: "⁄"
}, {
type: "element",
tag: "sub",
children: [{type: 'text', text: matches[2]}]
}];
};
})();
This was my first time writing such a rule, and it’s possible I’ve broken conventions of some sort here. But it seems to work well.
Second, @hoelzro pointed out that we can use the View Template Body Cascade to overwrite the default body template, and several people offered improvements.
My version of this uses
title: ViewTemplateBodyFilters/recipes
tags: $:/tags/ViewTemplateBodyFilter
[tag[Recipe]then[ViewTemplate/recipe]]
and
title: ViewTemplate/recipe
<$let filteredText={{{ [{!!text}search-replace:g:regexp[\b(\d+)/(\d+)\b],[^^$1^^⁄,,$2,,]] }}} >
<$transclude $variable="filteredText" $mode="block" />
</$let>
Both techniques do the job well. The wikirule technique has the advantage of running across all wikitext. Anywhere that //text//
would generate italic text, this should also work, including in calls to <$wikify>
The implementation, though, is in JS, and will be more obscure to many readers.
The body cascade technique is more familiar, implemented in wikitext, and has the advantage of allowing us to target specific filters to choose where to run it. On the other hand, there’s no way, I see to run it across all wikitext.
For my usage, I’ve chosen the wikirule technique, but can easily see changing that.
I also have to mention a third option from @TW_Tones:
Just using that Unicode character as the division sign, automatically generated usable fractions. By replacing " / " with " ⁄ " in
1/4 + 1/2 = 3/4
we get this:
1⁄4 + 1⁄2 = 3⁄4
Notice that it looks different from
1⁄4 + 1⁄2 = 3⁄4
I find it too small to be useful for this use-case, and for now the technique is not suitable for my purposes, as it still requires the user to apply the function. But it could be combined with either of the other two techniques if we wanted fractions at that font size, and I did post a version of it online as well.
There was also a suggestions from @buggyj that I never found time to investigate:
as well as a variant of the body template cascade from @Charlie_Veniot that instead of cascading, simply used CSS to hide the default body.
All in all, a fantastic thread!
Thanks to @btheado, @hoelzro, @TW_Tones, @buggyj, @Mohammad, @EricShulman, @CodaCoder, @Charlie_Veniot, and anyone else who participated!