Search code examples
tiddlywikitiddlywiki5

Generating a list of links with prefix "The " removed points to empty Tiddler instead of the real Tiddler


I'm trying to create a tiddler that's an alphabetical list of all the tiddlers in my tiddlywiki. I've gotten it to work to a certain point with this code:

A
<<list-links "[prefix[A]][removeprefix[The ]]+[prefix[A]]">>
B
<<list-links "[prefix[B]][removeprefix[The ]]+[prefix[B]]">>
C
<<list-links "[prefix[C]][removeprefix[The ]]+[prefix[C]]">>

What I've done here is that some of my tiddlers are prefixed with the word "The ", so I'm removing the prefix and just using non-prefixed name of the tiddler to display in the index. This is also working fine, it's finding all the tiddlers, stripping out the leading "The " if there is one and then creating a link to a tiddler.

Here is the problem, instead of linking to the actually tiddler, such as "The Mytiddler", it's creating a link to "Mytiddler" instead, which doesn't exist.

Is there a way to display the non-prefixed tiddler name in the list, but have it link to the actual tiddler?


Solution

  • This is a case for the filter, subfilter, and sortsub operators, which let you run another filter on the contents of a filter without affecting the values in the main filter pipeline.

    Here's a complete solution:

    \define myprefix(letter) [removeprefix[The ]prefix[$letter$]]
    \define mysort() [search-replace::regexp[^The ],[]]
    
    \define render-letter(letter) <<list-links "[filter<myprefix $letter$>] [prefix[$letter$]] +[sortsub<mysort>]">>
    
    <$list filter="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" variable="currentLetter">
      <<currentLetter>>
        
      <$macrocall $name="render-letter" letter=<<currentLetter>>/>
    </$list>
    

    And an explanation (not in order):

    \define render-letter(letter) <<list-links "[filter<myprefix $letter$>] [prefix[$letter$]] +[sortsub<mysort>]">>
    

    First we create a macro to render each letter. Rather than using the removeprefix filter operator, we delegate to the filter we called myprefix:

    \define myprefix(letter) [removeprefix[The ]prefix[$letter$]]
    

    The result is that the first filter run matches everything prefixed with The and the specified letter, but its outputs are the actual tiddler names, rather than the tiddler names with the prefix removed.

    We actually don't need to use filter at all here, because the removeprefix operator is not necessary – just as effective would be [prefix[The $letter$]]. But I've left this in to demonstrate the use of filter, since if you were trying to do something a little more complicated, this solution might not work.

    At the end of the pipeline we use sortsub to combine all of the runs into a single properly sorted list. The filter used for sortsub represents the key the items will be sorted on, and in this case we do a regular expression search and replace, replacing The at the start of any tiddler names in the list with nothing:

    \define mysort() [search-replace::regexp[^The ],[]]
    

    You can easily handle additional cases in the regex here (e.g., by separating them with |), if you added other runs for removed prefixes.)

    This accomplishes everything in your original question, but to produce a complete version of this tiddler, here's an easy way to render this for every letter:

    <$list filter="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" variable="currentLetter">
      <<currentLetter>>
        
      <$macrocall $name="render-letter" letter=<<currentLetter>>/>
    </$list>
    

    Our “filter” here is just a list of “tiddler names”, which presumably don't actually exist in your wiki but will be iterated through in sequence in the body of the $list widget. For each one we call the render-letter macro on that letter, and you have your alphabetical list of tiddlers subdivided by letter.