Can we run a regex replace at render time?

It’s not really important here. I simply want to do this at an early enough point in the rendering process.

Thank you. There are many useful options here. In fact, I have considered a number of them, but I’m hoping to make this work in the manner I asked. This is of course for the recipe edition discussed elsewhere. I think it would be useful to allow people to type such fractions naturally but display them more attractively, But that attractive display is not important enough to ask the users to learn anything to achieve the formatting.

It’s quite easy to write a macro for this:

title: $:/community/recipes/proc/frac
tags: $:/tags/Global

\procedure frac(n:"1", d:"2") <sup><<n>></sup>&frasl;<sub><<d>></sub>

or, as you point out, even simpler would be

\procedure frac(n:"1", d:"2") ^^<<n>>^^&frasl;,,<<d>>,,

In either case,

<<frac 1 8>> tsp cream of tartar

would then render as

18 tsp cream of tartar

But the users I’m imagining would prefer to type “1/8 tsp…” and not “<<frac 1 8>> tsp”. In fact, I would rather do so myself.

I’m examining @btheado’s suggestion. It’s a new part of TW for me, so it will take some time. But it looks promising.

1 Like

maybe this would do the trick

http://tiddlystuff.tiddlyspot.com/#ReplacePragma

@Scott_Sauyet I like your use of &frasl;

I am wondering how to pass something like “1 1/2”

Thanks! I’ll check it out soon!

Thanks. I don’t know if I’ve ever had need for it before, but it is pretty… and I should have stopped to realize that of course there’s a Unicode character as well.

I’m on my phone and can’t test to double-check. But it should be just,

1 <<frac 1 2>>

That would work but if a field has the value “1 1/2” I tried to parse it but perhaps its not needed. We will see as the recipes wiki unfolds.

I have a trick I use for my own wiki that leverages the “View Template Body” cascade to highlight the search term I used to open a given tiddler; I think you could make use of it here as well! Here’s a proof-of-concept that defines ViewTemplate/recipe to do the custom rendering, and ViewTemplateBodyFilters/recipes to make sure that template gets used for any tiddlers tagged with “Recipes”: My TiddlyWiki — a non-linear personal web notebook

The way it works is ViewTemplateBodyFilters/recipes uses list-before to get priority over the default view template filter, and defines a filter to select tiddlers with the “Recipes” tag and instructs TiddlyWiki to use ViewTemplate/recipe to render the tiddler. Then, ViewTemplate/recipe grabs the text field, does a search and replace for 3/4 → ¾, and uses the wikify widget to render the result.

3 Likes

Hi hoelzro

Clever solution! I am not sure if the wikify widget is costly here or not!
I expected TW to wikify the result itself.

Oh, that is perfect! I have been under the weather and a bit slow to get to this. I was just getting online to try an earlier suggestion and I find the perfect solution waiting.

Thank you very much!

I don’t know for certain, but I would guess it’s not too bad, since there system has to do something similar anyway.

Perhaps this can also be implemented in a cascade?

Thanks! Sure enough - if I remove the wikify widget and replace <<wikitext>> with <<filteredText>>, that still works!

You mean the list of searches and their replacements, or something else?

Sorry, I don’t recall now, but believe I was hinting at what you said here;

  • I think I mistook this for using $:/tags/ViewTemplate

Of course once fully developed we could use the “Story Tiddler” cascade instead.

1 Like

Thank you Scott, this solution (by hoelzro) is worth documenting. I put a note to myself.
It behaves like a customized tiny parser! I am thinking powerful small tools can be created based on this;-)

1 Like

Oh, interesting - I’ve only ever used the view template body cascade!

ViewTemplate/recipe can be more compactly written like this:

<$let filteredText={{{ [{!!text}search-replace[3/4],[¾]] }}}>
<<filteredText>>
</$let>
  • Eliminate $wikify (as noted by @Mohammad)
  • Use $let instead of $set
  • Use {!!text} instead of is[current]get[text]

-e

1 Like

I’m finally getting a chance to try this, and I’ve run into a problem. Whether I use just <<filteredText>> or wrap it in <<wikify>>, I’m still getting out plain text. But I want to wikify the results.

Thus if my tiddler looks like this:

!! Ingredients

* 2 egg whites
* 3/4 cup sugar
* 5 drops oil of wintergreen
* 1/8 tsp cream of tartar
* Red food coloring
* 6 oz mini chocolate chips


!! Instructions

(more here)

I would like to have rendered:

Ingredients

  • 2 egg whites
  • 34 cup sugar
  • 5 drops oil of wintergreen
  • 18 tsp cream of tartar
  • Red food coloring
  • 6 oz mini chocolate chips

Instructions

(more here)

But instead, I get

Ingredients2 egg whites3⁄4 cup sugar5 drops oil of wintergreen1⁄8 tsp cream of tartarRed food coloring6 oz mini chocolate chipsInstructions(more here)

And if I try

<$let filteredText={{{ [{!!text}search-replace:g:regexp[\b(\d+)\/(\d+)\b],[^^$1^^&frasl;,,$2,,]] }}}>
<$transclude $variable="filteredText" mode="block"/>
</$let>

I get

!! Ingredients * 2 egg whites * 3⁄4 cup sugar * 5 drops oil of wintergreen * 1⁄8 tsp cream of tartar * Red food coloring * 6 oz mini chocolate chips !! Instructions (more here)


This looked so promising, but I really seem stuck. Any suggestions?

Obviously, it’s your call, Scott…

My philosophy is, if the rendering is a regular tiddler (yours is) then I’ll consider the <$wikify> widget. If, however, the rendering is inside a loop ($list) I’ll avoid the <$wikify> widget.

Like I said, your call.

I have a similar need in my wikis, rendering search-term matches throughout a tiddler. I use <$wikify>.

If there is a way I can pull it out, I’m all ears.

I don’t think you are getting plain text. I think the parsing is only recognizing inline syntax (add bold or some other inline markup and you’ll see). The list syntax and heading syntax is only recognized in block mode.

Why it is not parsing in block mode is a mystery to me (explained in the edit at the end).

I was able to get it to parse as desired using these two approaches:

<$let filteredText={{{ [{!!text}search-replace:g:regexp[\b(\d+)\/(\d+)\b],[^^$1^^&frasl;,,$2,,]] }}}>
<$transclude $variable="filteredText">

</$transclude>
</$let>

and (the blank line after $let puts the parser in block mode)

<$let filteredText={{{ [{recipe!!text}search-replace:g:regexp[\b(\d+)\/(\d+)\b],[^^$1^^&frasl;,,$2,,]] }}}>

<<filteredText>>
</$let>

But this did not work:

<$let filteredText={{{ [{recipe!!text}search-replace:g:regexp[\b(\d+)\/(\d+)\b],[^^$1^^&frasl;,,$2,,]] }}}>

<$transclude $variable="filteredText" mode="block"/>
</$let>

Edit: I see the issue now. The attribute on transclude should be $mode, not mode

Well, the problem is that none of this is working. They’re all giving results very different from what I’d expect.

But I did get the technique mentioned by @btheado to work just now, and I’ll write that up in a minute.

D’oh! But I did try playing with spacing for this, and kept failing. I’m really not sure why.

I may go back to this soon, but for the moment, I just got your earlier suggestion working, adding a wikirule. It needs some cleanup, but for the moment, I got it working with this:

$__community_recipes_core_modules_parsers_wikiparser_rules_fraction.js.json (1.3 KB)

(This requires save/refresh to see it working, but if you do so, just create a tiddler with some fractions in it, such as 1/4 + 1/2 = 3/4 and see the prettier formatting.)

This is the content:

/*\
title: $:/community/recipes/core/modules/parsers/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;
	// Regexp to match
	this.matchRegExp = /\b(\d+)\/(\d+)\b/mg;
};

exports.parse = function() {
	// Extract the numerator and denominator
    var matches = this.parser.source.slice(this.parser.pos).match(/\b(\d+)\/(\d+)\b/);
	// Move past the match
	this.parser.pos = this.matchRegExp.lastIndex;


	// Return the sub-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]}]
	}];
};

})();

It needs clean-up because of the duplication between two regexes that differ only by their flags; there should be a common source of truth there. My first attempt broke it, and I reset back to this. I will get back to it soon, but now it’s time to start New Years celebration. I will also go back to try the other solution as well, because it might be nice to target only Recipe tiddlers or some such and not all wikitext. (Or not, I’m not sure which I’d prefer yet.)


Thank you everyone for your help!