Subfilter recursion

209 views
Skip to first unread message

Yaisog Bonegnasher

unread,
Apr 18, 2021, 3:01:51 PM4/18/21
to TiddlyWiki
Hi,
I recently tried to create a recursive subfilter to find all tiddlers below a certain tag, no matter how many levels deep (similar to the kin filter, which  is too slow for my large wiki). Unfortunately, I was greeted with a red-message-box-of-death informing me of too much recursion. However, there should only be a couple of levels.
Are recursive filters at all possible or am I doing something wrong?
For a quick example, try this in a new tiddler on tiddlywiki.com:
\define subfilter-test() [tagging[]] [tagging[]subfilter<subfilter-test>]

<$set name="test" filter="[[TableOfContents]subfilter<subfilter-test>]">
  <$list filter="=[enlist<test>]" template="$:/core/ui/ListItemTemplate" />
</$set>
Any enlightment is greatly appreciated.
Best regards
Yaisog

Mark S.

unread,
Apr 18, 2021, 3:31:20 PM4/18/21
to TiddlyWiki
My guess is that it's trying to build the complete filter even before it runs, so that's why you hit the recursive limit. That is, it's not running one filter, running the next, testing, etc. It's trying to build the whole thing, which has no way of exiting.

Since you know about how many levels, maybe you could prevent the over-run like this:

\define subfilter-test5() [tagging[]] [tagging[]]
\define subfilter-test4() [tagging[]] [tagging[]subfilter<subfilter-test5>]
\define subfilter-test3() [tagging[]] [tagging[]subfilter<subfilter-test4>]
\define subfilter-test2() [tagging[]] [tagging[]subfilter<subfilter-test3>]
\define subfilter-test()  [tagging[]] [tagging[]subfilter<subfilter-test2>]

<$set name="test" filter="[[HelloThere]subfilter<subfilter-test>]">
  <$list filter="=[enlist<test>]" template="$:/core/ui/ListItemTemplate" />
</$set>

You can add on as many levels as you think you'll ever need. 

Soren Bjornstad

unread,
Apr 18, 2021, 3:36:33 PM4/18/21
to TiddlyWiki
I've never tried to do a recursive filter, so I don't know what's technically possible (though I've never seen someone try to use multiple filter runs in subfilter and am a bit suspicious that the second run is doing a tagging[] on everything in your wiki). But recursive macros are easy and functional, and I suspect they'll work for your use case. Here's one version that does an outline of the TOC on tiddlywiki.com, using the current tiddler to easily pass the state through to the next level:

\define recurse()
<$list filter="[all[current]tagging[]]">
  <li><<currentTiddler>><ul>
  <<recurse>></ul></li>
</$list>
\end

<$tiddler tiddler="TableOfContents">
  <ul>
    <<recurse>>
  </ul>
</$tiddler>

Soren Bjornstad

unread,
Apr 18, 2021, 3:43:26 PM4/18/21
to TiddlyWiki
Update, I did another experiment and subfilter[] does work the way you were using it. I'm guessing Mark is right and it tries to create the filter before it actually runs it and sees there are no results.

Jean-Pierre Rivière

unread,
Apr 19, 2021, 1:41:48 PM4/19/21
to TiddlyWiki
About recursive macro, they may be possible, but the problem is how to stop the recursion. Your example works if there is no loop created by tagging.

The problem is we don't have a proper <<if>> macro to decide what to do (control recursion call in that case). Or how do you do that?

Soren Bjornstad

unread,
Apr 19, 2021, 2:29:26 PM4/19/21
to TiddlyWiki
This is a good point. The built-in table-of-contents macro appears to avoid the issue by excluding the current tiddler at each level, which could be applied to my example above:

\define toc-body(tag,sort:"",itemClassFilter,exclude,path)
<ol class="tc-toc">
  <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[enlist<__exclude__>]""">
    <$vars item=<<currentTiddler>> path={{{ [<__path__>addsuffix[/]addsuffix<__tag__>] }}}>
      <$set name="excluded" filter="""[enlist<__exclude__>] [<__tag__>]""">
        <$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
          <li class=<<toc-item-class>>>
            <$list filter="[all[current]toc-link[no]]" emptyMessage="<$link to={{{ [<currentTiddler>get[target]else<currentTiddler>] }}}><$view field='caption'><$view field='title'/></$view></$link>">
              <<toc-caption>>
            </$list>
            <$macrocall $name="toc-body" tag=<<item>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> exclude=<<excluded>> path=<<path>>/>
          </li>
        </$set>
      </$set>
    </$vars>
  </$list>
</ol>
\end

Yaisog Bonegnasher

unread,
Apr 19, 2021, 2:31:06 PM4/19/21
to TiddlyWiki
Hi all,
thanks for the comments.
I was careful about tagging, not to create tag loops, but I can see how that would be a problem. You'd have the same problem with the TOC macros – which are recursive – though, wouldn't you (at least with the types that are always fully expanded)?
If Mark is right that would at least explain why TW would always run into a recursion error, no matter if there even are *any* tagged tiddlers. The filter would recurse indefinitely during setup. However, the numbered subfilters are not really pretty.
I cannot use macros as Soren proposed since I need to store the whole tagging tree in a variable for later use in an :intersection filter.
If there at least was a way to integrate a counter into the recursion, one could make it stop after a flexible number of iterations, which number could be passed into the filter expression via a variable.
Right now,I resorted to just create a single, long filter expression and stopped after 3 levels. To add another level, I need to modify the filter expression itself, though.

Yaisog

Yaisog Bonegnasher

unread,
Apr 19, 2021, 2:35:32 PM4/19/21
to TiddlyWiki
Yeah, the TOC macros are very clever in avoiding recursion. It's hard to imagine putting all this into a filter expression...

Mark S.

unread,
Apr 19, 2021, 3:48:56 PM4/19/21
to TiddlyWiki
If you write your own recursion macro, you can put braces around each item in the output. Then set that output to a name inside a wikify widget. Then use that variable with the enlist operator to create your list that you want to use in an intersection. I just posted some similar code in another thread, though it didn't use recursion.

Mohammad Rahmani

unread,
Apr 19, 2021, 4:06:05 PM4/19/21
to tiddl...@googlegroups.com
Have a look in TW-Script! A solution by Eric Shulman shows how to exclude a tag and how simply prevent a recursion loop! much simpler than the official toc macro!


Best wishes
Mohammad


--
You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tiddlywiki/8c52e9b8-e9a4-4682-9152-8ce19b5335aan%40googlegroups.com.

Yaisog Bonegnasher

unread,
Apr 20, 2021, 9:11:43 AM4/20/21
to TiddlyWiki
I have to admit that I am beginning to like Mark's solution with the numbered subfilter definitions, even though I unfairly called it "not really pretty". The neat thing about it is that adding another level of "pseudo-recursion" is very easy and very clear. Putting everything into a single filter is complicated to maintain with all sub-expressions having different length (different number of tagging[] constructs). Here, everything looks the same, with just some numbers changed. I think I'm gonna implement this!

Yaisog Bonegnasher

unread,
Apr 20, 2021, 10:20:42 AM4/20/21
to TiddlyWiki
Here is my 5-level-deep tiddler collection beneath a tag á la Mark:
\define tiddlertree-level5() [tagging[]]
\define tiddlertree-level4() [tagging[]] [tagging[]subfilter<tiddlertree-level5>]
\define tiddlertree-level3() [tagging[]] [tagging[]subfilter<tiddlertree-level4>]
\define tiddlertree-level2() [tagging[]] [tagging[]subfilter<tiddlertree-level3>]
\define tiddlertree()        [tagging[]] [tagging[]subfilter<tiddlertree-level2>]
where before it was this line-breaker:
\define tiddlertree() [tagging[]] [tagging[]tagging[]] [tagging[]tagging[]tagging[]] [tagging[]tagging[]tagging[]tagging[]] [tagging[]tagging[]tagging[]tagging[]tagging[]]
Cheers.
Reply all
Reply to author
Forward
0 new messages