How to build a Table of Contents at compile time?

Compiler: Tweego v2.1.1
Story Format: SugarCube v2.30.0

Is there any way to programmatically append (at compile time, not at runtime) a link to the current passage to the end of another passage?

I’d like to generate a “Table of Contents” passage without having to manually type in the names of the specific passages that I want shown there. That gets to be tedious and error prone, especially when passage names change and get re-ordered or deleted. Having them show up in the same order that they’re encountered in the .tw file would be appropriate for my needs.

A construct of the form

:: FirstChapterTitle [toc]

:: SecondChapterTitle [toc]

or perhaps

:: FirstChapterTitle
<<toc "FirstChapterTitle">>

would be fine.

I’m not at all proficient in writing CSS so there’s doubtless something I haven’t thought of or have overlooked in the tweego and SugarCube documentation.

Thanks,
Selden

This is completely untested, but I think this should work the way you want. I can’t guarantee it’ll be ordered in order of appearance though.

<<set _toc = Story.lookup("tags", "toc")>>
	
<<for _i = 0; _i < _toc.length; ++_i>>
	<<link _toc[_i].title>><</link>>
<</for>>

Thanks for the suggestion! I’ll give it a try.

While waiting for a response, though, I decided I should try thinking “outside the box”. I.e. use something other than tweego/sugarcube to create the ToC.

The solution I came up with was to use a script (bash) to invoke a programmable text editor. It searches (grep) through all .tw files for lines which include the tag string “toc]”, writing those lines to another file. Then it edits (mung) that file so each line is in proper passage link format. I used mung (tecoc) just because it’s burned into my fingers after many years of using it when I was young. I’m sure there are many other scripting languages and text editors which would be just as effective.

My scripts are available upon request, although they’re very simplistic and designed to work in my very specific environment.

1 Like

The function works!

Except, as you mentioned it doesn’t list the passages in order of appearance.

By default Story.lookup seems to be returning the passage titles in alphabetic order. That doesn’t work so well for me. The SugarCube docs say one can specify the optional 3rd argument “sortProperty” to sort them, and by default it uses the “title” property. Unfortunately, the list of properties in the “Passage API” and “Story API” sections don’t include anything that looks to me like “order of appearance”.

Okay… so this isn’t the prettiest thing I’ve ever written, but it seems to work. (This time it’s actually tested. :stuck_out_tongue:)

Be aware that the last tag is the ordering number. If you always have the number as the last tag, this script shouldn’t break.

:: Start
<<nobr>>

<<script>>
	var toc = Story.lookup("tags", "toc");
	var sorted = [];
	
	var i, x;
	for (i = 0; i < toc.length; ++i) {
		for (x = 0; x < sorted.length; ++x) {
			if (sorted[x].tags[0] > toc[i].tags[0]) {
				sorted.splice(x, 0, toc[i]);
				break;
			}
		}
		if (x === sorted.length) sorted.push(toc[i]);
	}
	
	State.temporary.toc = sorted;
<</script>>
	
<<for _i = 0; _i < _toc.length; ++_i>>
	<<link _toc[_i].title _toc[_i].title>><</link>><br>
<</for>>

<</nobr>>

:: Uwu [asdf toc 1]

:: Test [toc 2]

:: Alpha [toc asdf 3]

Edit: I also noticed that this here seems to hold the index of a passage:

Story.get("passage_name").element.attributes.pid.value

However, I’m not sure if it’s a good idea to use that or not.

For one, I don’t see it documented anywhere. I only figured it out using the developer console, which means that it may be subject to change in the future.

And two, if you use that, a problem you may run into in the future is that I believe tweego processes files in an alphabetical order. So if you were to split your stuff up into a clearly named files, it will throw your TOC out of order, unless you start the file names with a number or something.

With that warning having been given… if you wanted to use it anyway, just replace the line:

if (sorted[x].tags[0] > toc[i].tags[0]) {

with this one

if (sorted[x].element.attributes.pid.value > toc[i].element.attributes.pid.value) {

I actually have a bunch of Twine/SugarCube sample code online, and it includes a “Table of Links” example which does what you want, except it puts them in alphabetical order.

You could try taking out the “<<set $inv = $inv.sort()>>” line to see what order they end up in.

It will need a few tweaks to only have passages with “toc” tags, instead of ignoring passages with “Unlisted” or “non-story-passage” tags, but hopefully that helps. :slight_smile:

Thanks, guys!

I’ll give them a try and figure out which works best in my environment.