Automatically link to preexisting passage

Hi,
Is there a way to automatically create links to passages without manually inserting [[ ]]? For example, if I have a [[London]] passage and ‘London’ appears in multiple passages is there an easy way for these to be linked? I’m asking because I have a large dataset with lots of recurring words that I would like to link. Sorry if this is actually very easy and I’m missing something basic.
Thanks!

Twine Version: 2.3.9
Story Format: SugarCube 2.31.1

1 Like

I don’t know of any feature of Twine/Sugarcube that lets you automate that process, and a brief skim on Google doesn’t yield anything for me either. I’ve also only been using Twine/Sugarcube for a couple of months, so maybe such a feature exists. Out of curiosity, I’ll watch your post and see if you get an answer.

I did want to comment this idea in case it turns out that Twine doesn’t support such a thing. I don’t know how many passages you’re talking here, but would it be feasible for you to copy your large dataset from your many passages into a single Word Document (or something of the like) and Find/Replace all “London” words to “[[London]]”? I apologize if you’ve already considered this. I figure it’s better than manually going through and inserting [[ ]] for all of your Twine passages.

Good luck!

1 Like

Yeah, Harlowe lets you say (click-goto: "London" "London passage") and it will find all occurrences of London in the current passage and make them link to London passage, but SugarCube doesn’t have that built in. Of course, it’s possible to do anything with JavaScript, but I don’t know the code for that off the top of my head…

1 Like

EDITED TO INCLUDE @tayruh’s REGEX IMPROVEMENT

Hey there,

As long as you’re using a newer version of SugarCube, you can plop this into your Story’s Javascript (click the story title in the bottom left of the editor and click “Edit Story Javascript”) and it’ll automatically work on every single passage.

// List of keywords to turn into links
const keywords = ["London", "England"]

// This creates a regular expression which searches for any of the given keywords
// Thanks to @tayruh for the updated regex
const keywordRegex = new RegExp("(?<!\\[\\[(?:\\s*[^|\\]]+\\|)?\\s*)\\b(" + keywords.join("|") + ")\\b", "g");

// This is called before each passage's text is processed
Config.passages.onProcess = function (p) {
        // We use the regular expression made above to replace any instances of those keywords with a link
	return p.text.replace(keywordRegex, "[[$1]]");
};

Just tested it in the online editor and it works. Right now it’s case sensitive (meaning it’ll replace London but not london). If you want it to match both, change the “g” in the regex line with a “gi”.

It’ll also only match whole words. So if you have “Londonish” in a passage, it won’t replace that. Thanks to @tayruh, it also won’t break existing links that include any of the words.

If you want to have the keyword list created automatically, change the first line of code to

// Find all codex passages and get their names
const keywords = Story.lookup("tags", "codex").map(p => p.title);

This will create the list of keywords from any passages that have the tag codex. So instead of needing to maintain the list, just tag your London passage with codex and it’ll automatically get picked up.

3 Likes

The only issue I see with this is that perhaps the RegExp should do a check to see if it’s already inside a [[ ]] section? Otherwise it’ll be impossible to link to a location without using the full name. IOW, “It’s time to go [[home|England]]” would render as “It’s time to go [[home|[[England]]]]” and break.

2 Likes

That’s a really good point. Unless I’m missing a galaxy brain Regex solution, I’m not sure what a good way is to detect that with pure regex? There are plenty of solutions that involve a lot more complex javascript to do some parsing but maybe it’ll be good enough for the OP for now? But yeah, the caveat is that manually linking to these pages will get messed up by the replace.

Is that okay @beigepursuit?

So, I’m not the greatest at RegExp, but I came up with this that seems to work:

(?<!\[\[(?:\s*[^|\]]+\|)?\s*)\b(England)\b

Using that with this test data results in only the two standalone "England"s being matched.

Test [[England]] Test England [[home|England]] Test xEngland England Englandx

Edit:
To be clear for the OP, this line in the original solution:

const keywordRegex = new RegExp("\\b(" + keywords.join("|") + ")\\b", "g");

Should be changed to:

const keywordRegex = new RegExp("(?<!\\[\\[(?:\\s*[^|\\]]+\\|)?\\s*)\\b(" + keywords.join("|") + ")\\b", "g");
3 Likes

Updated my answer post to include your suggestion!

Clearly better than me :stuck_out_tongue:. Nice job. I had never heard of negative look-behind before. Looks like it’s supported in basically every browser though? Might have some issues with Safari according to this chart: Lookbehind in JS regular expressions | Can I use... Support tables for HTML5, CSS3, etc

1 Like

Oh right. I knew using the look behind was ringing warning bells for some reason. I’ve been mostly coding in node.js lately so I’ve been spoiled when using RegExp. I had to avoid look behind when coding up Sadako to support Safari and IE.

I’ll see if I can maybe come up with a better solution.

1 Like

I think I got it.

// List of keywords to turn into links
const keywords = ["London", "England"]

// This creates a regular expression which searches for any of the given keywords
const keywordRegex = new RegExp("(\\[\\[(?:\\s*[^|\\]]+\\|)?\\s*)?\\b(" + keywords.join("|") + ")\\b", "g");

// This is called before each passage's text is processed
Config.passages.onProcess = function (p) {
	// We use the regular expression made above to replace any instances of those keywords with a link
	
	return p.text.replace(keywordRegex, function(m, p1, p2) {
		// returns original text if inside brackets
		if (p1 !== undefined) return m;

		return "[[" + p2 + "]]";
	});
	
}; 
2 Likes

Thank you all so much. It works perfectly.
As you can tell I’m a complete newbie to this and really appreciate the help.
Can I ask a general question - am i right in thinking twine isn’t very well suited to stories with lots of passages and links? Like 1000s.

2 Likes

I gather the visual editor starts getting slow when you get up toward 1000 passages, but if you don’t mind working in a purely text format, things like Tweego should still be fine. And I think some people do write bigger stories and just live with the slowness? Not sure.

2 Likes