Extracting tags from a filter or, how can I make this better?

Hey folks,

I’m working on packaging my task/project management macros as a plugin. This tutorial does much of what I’ve already been doing, but I’m trying to add a new task button and am wondering if there’s a way to take a filter like [tag[Combat]tag[Monster]] and extract a list of included tags? Failing that, is there some way to make what I’m doing work?

Essentially, I have this macro for listing tasks:

\define task-list(filter)
  <ul>
    <$list filter="[tag[task]$filter$sort[]]">
      <li><$checkbox tag="done"> ~~ <$link/>~~</$checkbox></li>
    </$list>
  </ul>
\end

I then have this to create a dashboard of done and undone tasks:

\define task-dashboard(filter, title:"Tasks")
  <details>
    <summary><h3>$title$</h3></summary>
    <<new-task-button>>
    <details>
      <summary><h4>Open</h4></summary>
      <<task-list "$filter$!tag[done]">>
    </details>
    <details>
      <summary><h4>Closed</h4></summary>
      <<task-list "$filter$tag[done]">>
    </details>
  </details>
\end

For creating new tasks:

\define new-task()
    <$action-sendmessage $message="tm-new-tiddler" title="New task" tags="task" />
\end

\define new-task-button()
    <$button actions=<<new-task>>>New task</$button>
\end

What I’d like to do is something like <<task-dashboard "tag[distribution]tag[steam]">> to filter tasks. Dashboards include a button to create new tasks tagged with whatever tags are included in the dashboard filter, but obviously this won’t work because the action triggered by the button wants a list of tags, not a filter.

Thoughts on a path forward? I guess I can ditch the idea of using filters everywhere and instead accept a list of tags, but then I have the opposite problem in that I don’t know how to go from a title list of tags to a filter.

Also just open to any general suggestions of how to make this better. I’m aiming for a simple task tracker so I can go from tiddlers linked from a design document, to checklists of tasks possibly with completion percentages (but obviously those aren’t there yet.)

Thanks.

Here’s how you might pass in a list of tags. The text list is converted to a title list. The title list is mapped to form a new filter. The filter is then used in the body of the macro.

\define task-list(tags)
<$let 
lb="[" rb="]"
tagfilter={{{[enlist<__tags__>]:map[addprefix<lb>addprefix[tag]addsuffix<rb>]+[join[]addprefix<lb>addsuffix<rb>]}}} 
>
  <ul>
    <$list filter="[tag[task]subfilter<tagfilter>sort[]]">
      <li><$checkbox tag="done"> ~~ <$link/>~~</$checkbox></li>
    </$list>
  </ul>
</$let>
\end

<<task-list "Stuff HelloThere">>

Thanks, I’d likely never have figured that out on my own.

One minor issue is that I still need to be able to append an arbitrary filter for tag[done] or !tag[done]. So I changed your code to this:

\define task-list(tags, filter)
  <$let 
    lb="[" rb="]"
    tagfilter={{{[enlist<__tags__>]:map[addprefix<lb>addprefix[tag]addsuffix<rb>]+[join[]addprefix<lb>addsuffix<rb>]}}} 
    >
    <ul>
      <$list filter="[tag[task]$filter$subfilter<tagfilter>sort[]]">
        <li><$checkbox tag="done"> ~~ <$link/>~~</$checkbox></li>
      </$list>
    </ul>
  </$let>
\end

And my dashboard macro:

\define task-dashboard(tags, filter, title:"Tasks")
  <details>
    <summary><h3>$title$</h3></summary>
    <<new-task-button "$tags$">>
    <details>
      <summary><h4>Open</h4></summary>
      <<task-list tags="$tags$" filter="!tag[done]">>
    </details>
    <details>
      <summary><h4>Closed</h4></summary>
      <<task-list tags="$tags$" filter="tag[done]">>
    </details>
  </details>
\end

When I create a tiddler tagged “task”, it doesn’t appear on my main dashboard. Any idea what I’m doing wrong this time? :slight_smile:

You don’t use equal sign symbols (=) inside of wikitext macros, though you do use them inside of widgets. So something like this:

<<task-list tags:"Stuff HelloThere" filter:"tag[done]">>

Ah, nice, thanks. I think I fixed those, but I’m still not seeing my task.

Not sure what I’m doing wrong, but I uploaded my wiki to Dropbox - wiki.html - Simplify your life if it helps.

Thanks a bunch.

Well your dashboard invocation under “Home” doesn’t specify any tags (e.g. “Enemies”). As a side note, the “filter” option of the dashboard doesn’t seem to get used for anything.

Right, the intent there is that I want to list all tasks, so I don’t
specify any tags. Other tiddlers will have similar dashboards but scoped
to specific tags. Do I need to handle that differently in the macro?

And yeah, I’m not using the filter option everywhere–just trying to get
the base case working for now.

Thanks.

The simplest solution would be to provide a default of “task” for when you want all tasks:

\define task-list(tags:"task", filter)

The problem is that all tasks should have a task tag, and I’d rather not have to specify that. It’s just that they also can have one or more optional tags, and I’d like the option of not specifying any if they aren’t needed.

I’m having a lot of trouble with specifying complex anything in TiddlyWiki because this language is like a giant run-on sentence for my screen reader. There’s no function_call(arg1, arg2) or similar breaking things up. I either listen to the entire line or go character by character.

I changed my task list to this:

\define task-list(tags, filter)
  <$let 
    lb="[" rb="]"
    tagfilter={{{[enlist<__tags__>]:map[addprefix<lb>addprefix[tag]addsuffix<rb>]+[join[]addprefix<lb>addsuffix<rb>]}}}
    >
Filter: [tag[task]$filter$subfilter<tagfilter>sort[]]
    <ul>
      <$list filter="[tag[task]$filter$subfilter<tagfilter>sort[]]">
        <li><$checkbox tag="done"> ~~ <$link/>~~</$checkbox></li>
      </$list>
    </ul>
  </$let>
\end

I wanted to output the filter to see what was being generated. What I get is:

Filter: [tag[task]!tag[done]subfiltersort[]]
</$let>

Not sure if I used the wrong variable syntax, but I’m a lot more concerned by the </$let> as it suggests I didn’t balance a bracket somewhere, and that’s going to be a lot harder for me to figure out. Unfortunately there’s no widely available mechanism for making syntax highlighting accessible. Usually I don’t need it because languages other than Lisp/Haskell have enough punctuation that I can parse the tokens in my head.

Thanks for all the help.

It looks like the raw filter may be being parsed erroneously in wikitext. A better way to preview the filter with the text substitutions is as follows:

Filter: <$text text="[tag[task]$filter$subfilter<tagfilter>sort[]]"/>

Ugh, still didn’t work. FWIW, here’s my entire macro. I’m out of ideas here:

\define task-list(tags, filter)
  <$let 
    lb="[" rb="]"
    tagfilter={{{[enlist<__tags__>]:map[addprefix<lb>addprefix[tag]addsuffix<rb>]+[join[]addprefix<lb>addsuffix<rb>]}}}
    >
Filter: <$text text="[tag[task]$filter$subfilter<tagfilter>sort[]]"/>
    <ul>
      <$list filter="[tag[task]$filter$subfilter<tagfilter>sort[]]">
        <li><$checkbox tag="done"> ~~ <$link/>~~</$checkbox></li>
      </$list>
    </ul>
  </$let>
\end

Filter: [tag[task]!tag[done]subfiltersort[]]

Also tried:

Filter: <$text text="[tag[task]$filter$<<tagfilter>>sort[]]"/>

Filter: [tag[task]!tag[done]<<tagfilter>>sort[]] Edited by Tones to show code

So the closing </$let> is indeed gone, but I don’t know if the subfilter is being incorrectly interpolated or calculated.

If it’d help/make things clearer, we can prepend/append the “task” tag in the let definition. I just don’t want tags:"task" as the default.

Thanks.

I’m confused. If all tasks should have a task tag, then using just "task’ as the default should find all tasks, whether they have additional tags or not. So I’m not sure what selection logic you want.

Without looking at the rest of the logic, you can’t concatenate a macro into a string using the double angle wikitext (e.g. <<stuff>>).

Also, on discourse, you need to put any angle braces into code quotes or discourse will render them as though html.

  • All tasks have a “task” tag.
  • Some tasks can have tags in addition to “task”.
  • Completed tasks have a “done” tag.

So I’d like to avoid having to specify the “task” tag in my macros since it would be redundant.

Sorry I missed an instance of quoting code. I try to do that everywhere but sometimes I miss one.

It might be slightly redundant, but I doubt there would be any noticeable performance difference. There are other solutions, like creating two alternate filter runs depending on whether tags is blank or not, but they would be more complicated, bulkier, and harder to understand. And they probably wouldn’t be more performant since they would involve additional comparison checks.

Without looking in detail;

What are you doing here, As I read it you are setting tagfilter to use as a subfilter later but since its in triple curly braces, Filtered transclusion, it will be evaluated right there.

I suggest having a separate and simpler list(s) in which to test your filter(s) then build on that or display intermediate values (like filter).

This is incorrect
<$text text="[tag[task]$filter$<<tagfilter>>sort[]]"/
Possibly you mean;
<tagfilter> not <<tagfilter>> or should you be using subfilter<tagfilter>

Sorry I don’t have time to review in full just now, I hope these are clues to your solution.

TBH, I’m just copying code and trying to get something that works. Earlier I said that this was a dense language and I’m not wrong. :slight_smile: There’s <foo>, <<foo>>, <$foo>, {{, {{{`, and it’s very difficult to keep it all straight with a screen reader.

I used << because I thought this was a variable. I used the subfilter because it was what was used in the example I was given. Now I’m just trying to output the text of the filter I’m trying to use so I can see why it isn’t working. I regularly work in 3-4 languages, currently mainly with Rust, so in general I know what I’m doing. But TiddlyWiki’s languages pack a lot into symbols that my screen reader just glosses over at 500+ WPM (| vs. `||, anyone?) so I’m having a hard time with it.

Anyhow, I’ll try replacing ``<>withand see what that gets me. I thoughttagfilterwas a variable and<>` was how the variable’s content was included in wikitext.

Thanks for all the help.

Actually, it occurred to me that I can just do this in JavaScript. That’d be much easier for me to work with. I’ll probably give that a shot next week when I get back to this.

One quick question about working with JavaScript macros in the in-browser editor: do I need to update the content-type to text/javascript? Guessing so since that represents the content-type of the tiddler text, but just wanted to be sure.

Thanks again.

@nolan it is worth considering tiddlywiki another language, and I know it is foreign to many people but I would urge you to persist. TiddlyWiki has a meta language built from widgets, macros and filters. Filters are very powerful.

  • I don’t know if you know about or recall 4th generation programing languages? They are a higher level of abstraction where you tend to operate on lists or sets without needing to code while and other loops.
  • Filters can also manipulate set, lists, strings according to info anywhere in tiddlywiki, do basic maths, be used as conditions and much more.

As usual with most languages if you know one thing others follow. In the body of a tiddler we can have html <table>, macros/ variables <<macro-variable>> and widgets <$widget name. This includes transclusions {{tiddlername}} note how html is a single < thus the others need two characters to be unique << <$ {{ this notation is this forced on us.

Filters do not accept html and widgets so there is no need to delimit macros and variables in a filter with double <<macro-variable>> and the same for transclusions {{tiddlername}} {{!!fieldname}}.

In filters we only use <macro-variable> and {tiddlername} or {!!fieldname}but if inside a macro we also get to use the replaceable parameters $param$ $(variable)$ eg [$param$].

My personal view is to avoid going to a javascript solution, because all you do is increase your dependencies and avoid using the core facilities, and further delay your learning. Java script is your “hammer” and every thing looks like a nail. What you seem to want is simple and core tiddlywiki.

Rather than point to code that may or may not be right perhaps you can go back to what you need. For example;

In your macro
<<task-dashboard "tag[distribution]tag[steam]">>
you can turn this into a stand alone filter
<<task-dashboard "[tag[distribution]tag[steam]]">>
then in your macro refer to it as a [filter[$parameter$]] [subfilter[$parameter$]] or with a leading filter run +[subfilter[$parameter$]]

I see what you are hoping todo and perhaps these functional global macros will give you some insight;

\define active-filter() [!tag[done]!tag[archive]]
\define inactive-filter()  [tag[done]] [tag[archive]] +[limit[1]]
\define active-todo() [tag[todo]filter<active-filter>]
\define active-project() [tag[Project]filter<active-filter>]
\define inactive-todo() [tag[todo]filter<inactive-filter>]
\define inactive-project() [tag[Project]filter<inactive-filter>]
\define created-today() [days:created[0]]
\define modified-today() [days:modified[0]]
\define due-today() [days:due[0]]

<$list filter="[filter<active-todo>subfilter<modified-today>!has[draft.of]]">
   <$link/> {{!!tags}} <$view field=modified format=relativedate /> {{!!due}}<br>
</$list>

<$list filter="[filter<active-todo>!subfilter<modified-today>!has[draft.of]]">
   <$link/> {{!!tags}} <$view field=modified format=relativedate /> {{!!due}}<br>
</$list>
  • So in the above examples note active-todo is also constructed from active-filter and here I use subfilter inline the “!” can negate it. But you can also use +[subfilter<filtername>] or -[subfilter<filtername>]

Regards

Sure, but counterpoint: imagine how these lines sound when read with a screen reader. “dollar filter dollar less than less than subfilter less than tag filter greater than greater than greater than” at > 500 WPM. Miscount your “less than” or "greater than"s and you’re sunk. And It is what it is and the language has been designed, but there are genuine accessibility issues that can make languages harder to learn (Lisp and Haskell’s lack of punctuation, for instance). And while there may be dozens of other ways to achieve this, the way I’ve chosen to do so seems like it should be sound yet I’ve gotten complete rewrites rather than “hey, you used the wrong number of brackets around the variable name.” :slight_smile:

Again, thanks, I appreciate the help. I think JavaScript is the best path forward for me with this one, so that’s what I’ll do, I just felt like the language accessibility issues were losing ground here, and while it’s probably too late to change anything, symbol density is a real issue with which I’ve struggled in my nearly 3 decades of coding. I’m not choosing to not learn this because I don’t know about different language styles. It’s just mentally taxing that I can’t just concatenate strings without using lots of different symbols, without using symbols to denote that something is a macro parameter vs. a separate variable vs. one of these single <foo> references which aren’t bounded by the <</>> that other variables seem to be, etc.

The easiest way to do javascript macros is to find an existing macro (often “now”), clone it, and make relevant changes. Note that unlike wikitext macros, you will have to save and reload your TW. Be sure to make a backup as you work, since an error can create a Red Screen of Embarrassment that prevents you from doing further work or fixing your code.

One problem with JS macros is that they are not as backward compatible as wikitext macros.

The other problem with using JS is that none of the helper functions are officially documented anywhere. So you have to look at lots of existing code to figure out how to grab the contents of a tiddler. Oh, and if you want to change anything, then you need to use widgets, not macros. And they’re … inscrutable.

In essence, wikitext was meant to allow ordinary users to do complicated things without getting involved with the internals. But the thing is, there are tons of tutorials on every aspect of JS, but almost nothing for wikitex other than the reference docs.

As for accessibility, I think originally wikitext was meant to be as accessible as HTML. But then the “attributes” (e.g. “filter”) grew in complexity.

Thinking about it, I guess maybe python is an accessible language? No brackets, though you do have to keep track of indents.