Tm-remove-tag effectively removes a title from a list field (tags)

Given “tm-remove-tag effectively removes a title from a list field” would it not be complete to have a tm-remove-item/tm-remove-title that effectively removes a title from a named list field? including text fields such as in the case of $:/DefaultTiddlers. The complementary tm-add-tag could be made into tm-add-title as well.

I know action listops is able to do this but it is far more complicated than need be for this basic action. It would further support alt-tag fields and other list-field manipulation.

  • Interestingly listops does not required the fieldmangler widget.

Is the fieldmangler widget something we can remove in 5.4.0?

They don’t seem complicated at all. Consider the following example:

<$button>add to tags       <$action-listops $tags="[[thistag]]"/></$button>
<$button>remove from tags  <$action-listops $tags="-[[thistag]]"/></$button>
<$button>add to list       <$action-listops $subfilter="[[thisitem]]"/></$button>
<$button>remove from list  <$action-listops $subfilter="-[[thisitem]]"/></$button>
<$button>add to items      <$action-listops $field=items $subfilter="[[thisitem]]"/></$button>
<$button>remove from items <$action-listops $field=items $subfilter="-[[thisitem]]"/></$button>

TAGS={{!!tags}}

LIST={{!!list}}

Notes:

  • $tags=... is equivalent to $field=tags $subfilter=...
  • $subfilter=... defaults to using $field=list

-e

3 Likes

I understand what you are saying, but add $tiddler then leverage variables or references it just gets more complex. I was building an easier way to leverage the “Extended Listops Filters” but thought the basic add/remove list field items could be simpler, no need to know its subfilter.

In some ways I am just pointing out an inconsistency, the more inconsistencies we have, the harder it is, the more we must read the documentation often, until we remember it.

The above are the only two that address the OT and include the use of $listops then $subfilter parameter which hardly seems relevant to a simple add/remove, and this it must be coded in the filter form, with the remove even harder to indicate.

For example you can’t provide <<currentTiddler>> as the subfilter value when removing, as you need the - prefix.

One solution would be if in the actions listops widget when $field is used allow a simple $add=valueorlist and/or $remove=valueorlist, however this still demands remembering action-listops.

An action-add-listitem(s) or action-remove-listitem(s) used with the new MVV multi-value variables would be nice. It requires no rocket science.

  • Of course it could be expanded to allow action against a data tiddler as well, perhaps following history with the $index parameter.
<$button>add to items      <$action-add-listitem $field=mylist $item=<<thisitem>>/></$button>
<$button>remove from items <$action-remove-listitem $field=mylist $item=<<thisitem>>/></$button>
  • Given you must use $field and $item perhaps the actions can be reduced to action-add and action-remove, it add -title

That is not true. All of the following work just fine:

<$button>add to tags       <$action-listops $tags="[<currentTiddler>]"/></$button>
<$button>remove from tags  <$action-listops $tags="-[<currentTiddler>]"/></$button>
<$button>add to list       <$action-listops $subfilter="[<currentTiddler>]"/></$button>
<$button>remove from list  <$action-listops $subfilter="-[<currentTiddler>]"/></$button>
<$button>add to items      <$action-listops $field=items $subfilter="[<currentTiddler>]"/></$button>
<$button>remove from items <$action-listops $field=items $subfilter="-[<currentTiddler>]"/></$button>

-e

You proved my point, the last line contains "-[<currentTiddler>]" (note -) not <<currentTiddler>> and on the subfilter parameter, each small distance takes this from a simple, memorable form to one with a number of quirky details you must recall. Compared to <$action-remove-listitem $field=mylist $item=<<currentTiddler>>/> or other forms.

I also propose the message tm-remove-item and tm-add-item equivalent to the tm-remove-tag but with optional field name, this also would support the use of alternative tag fields.

these could be added with little additional code to $:/core/modules/widgets/fieldmangler.js although it would be unfortunate needing to wrap with fieldmangler.

Perhaps there are better ways to introduce this feature?

I could see other features that could be introduced without adding too much cognitive load if we had a simpler way to address list fields than the listops filter or within the field mangler, for example list field actions to add/remove, clear, push and pop, tail/head etc…

Perhaps action-listitems with $tiddler (default currentTiddler) $field $item $items $value (set whole field) $push $pop $head $tail and other features?

Just wanted to note that $fieldmangler seems likely to be deprecated in the future, so I suspect we’re not likely to see any additional tm- messages along those lines.

I tend to agree with Eric here (and Saq had a similar point at GH): most of these seem like syntactic sugar for $action-listops operations we already have. And notably, I think your proposed $action-listitems $value= is just duplicating $action-setfield…?

1 Like

Have you considered using custom widget definitions?

Something like this:

\widget $action.add(tiddler,field:"tags",item)
<$action-listops $tiddler={{{ [<tiddler>!match[]else<currentTiddler>] }}}
   $field=<<field>> $subfilter="[<item>]"/>
\end
\widget $action.remove(tiddler,field:"tags",item)
<$action-listops $tiddler={{{ [<tiddler>!match[]else<currentTiddler>] }}}
   $field=<<field>> $subfilter="-[<item>]"/>
\end

Then, you can write:

<$let thisitem="some text here">

<$button>add to mylist       <$action.add    field=mylist item=<<thisitem>>/></$button>
<$button>remove from mylist  <$action.remove field=mylist item=<<thisitem>>/></$button>

MYLIST={{!!mylist}}

-e

3 Likes

Can you see my implied criticism that listops is an unnecessarily complex way to do something?, Its fine to say “it can already be done so leave it alone”, but this works well for people who have learned its “listops” quirks, but not for naïve users, the learning process and return visits after some time away.

Syntactic sugar refers to programming syntax designed to make code easier to read, write, or understand, without adding new functionality.

  • I think we should focus on the selected text above, yes.

You could argue that the listops action was overloaded. It certainly has less headroom for the addition of features.

I did yes, but manipulating list fields is perhaps as close to the core as it can get, and we don’t have an established path for the distribution and use of custom widgets (beyond plugins). I am fine with doing that as a first draft, but by considering it as it relates to the existing core is important, should the argument be won that they should be in the core.

Where to from here?

Actually I have established practices with an LLM that could easily vibe code this based on existing action widgets that could spit these out in an hour or so.

This is more about the holistic approach, needs and names, that I ask this for socialisation with the wonderful community, even if you don’t agree with the idea but it was going ahead what would you recommend?

  • What is the simplest approach?

Then if providing simpler list item management, please consider what else could be added, which would not overload listops.

  • The MVV’s being one example, since these are lists we are manipulating.
  • Even just extending the addition of one title to a list, to multiple titles to/from a list

To an extent… though I’ve always found it significantly easier than using tm- messages (which I always have to look up) or $fieldmangler (which I don’t think I’ve ever used… and honestly, I’m not sure I’ve even seen in in the wild — certainly not in any “modern” TW5 code.)

IMO, $action-listops is very intuitive if you’re already accustomed to the idea of using filters to modify items in a list. I certainly agree that there’s a bit of a learning curve to reach this point, and I think you’re probably also right that “naive users” may use TW without ever learning any filter syntax. But I do wonder how many people fall into both the “uncomfortable with filters” and the “interested in using widgets to modify non-standard fields” categories… and I also wonder how many of this set of users don’t think we have too many widgets with too many parameters already. So I suppose that’s where you begin to lose me, if you’re proposing additions to the core code. So far, I haven’t seen a lot of evidence that there’s much demand for this. But I am, of course, willing and happy to be proven wrong.

I’m not sure I really agree. Currently, $action-listops has 6 potential attributes

$tiddler -> defaults to <<currentTiddler>>; can often be omitted
$field OR $index
$filter OR $subfilter -> depending on whether you want to modify the extant field/index value ($subfilter) or start fresh ($filter)
$tags -> itself a shortcut for $field=tags $subfilter="..."

Which seems a lot simpler than the list of potential $action-listitems attributes you were floating…

A number of these aren’t terms currently used in TW syntax (so we’d have to assume they’d be novel to at least some of our hypothetical set of users). I’d hypothesize that $head and $tail would be similar to +[prepend[...]] and +[append[...]], but as a non-programmer, I can’t even begin to guess what $push or $pop would do. So while this might be simpler for someone, I can already guarantee it wouldn’t be simpler for everyone.

I’m sorry, I’m not trying to be contrarian, but surely this is exactly what $action-listops is designed for? Is this not just $action-listops $field=myField $subfilter="[[Title to be added]] [[Another title]] [tag[Add these too]]"?

2 Likes

I have no problem with you taking an opposite view. That is why it is valuable to chat here.

Actually action listops is also apparently 17 “Extended Listops Filters”. It is a Swiss army knife, great when you need one.

I was only asking about what if we could add to separate actions widgets for add list item and remove list item. Pop and Push are just versions of head and tail which are about queuing and sets. Yes it can be implemented within listops, but does not provide tiddler store output, it needs more than one action, with corresponding parameters. I was just grasping at examples of what we may add.

More details push and pop

  • Push - Push something into a list (field)
  • Pop - Pop something out of a list (field)

The difference being that such a list if often defined as either being LIFO (last In First Out, most overdue first) or FIFO or (First in First Out, most recent first). The implementation I was thinking of may be to retrieve that value in a list, remove it from the list and save it in another field/list field.

  • I am not sure how many people explore this idea but it is one that resounds with typical GTD solutions in tiddlywiki.
  • It’s like append and prepend but also removes an item when popped.

The thing is I am not asking for anything unusual, listops itself has special handling for $tags and tags also have tm-add-tag and tm-remove-tag if the actions I propose were present you need not use them at all (if you wanted).

It’s bedtime for me so I won’t add much, but I do want to point out that, despite the name, none of these operators are specific to $action-listops filters — they work just the same in any filter context, anywhere in TW. I think this is where your proposed additions most risk complicating what they seek to simplify: we’d have “the $action-listops way to do it” (or “the $action-listitems way”, etc.) and then the way to do it everywhere else, and the two methods might not even use the same terminology. It’s difficult for me to imagine a scenario in which this wouldn’t increase user confusion and cognitive load.

I continue to invite suggestions, and research my own.

More thoughts:

I think I’m not entirely understanding which problem(s) you’re trying to solve and how your proposed improvements would function. Could you provide some pseudo-code illustrating what you’ve envisioning? For instance (just pulling from the attributes you suggested in a previous post; this may or may not capture your intended usage):

To add multiple items to a designated list field:

Current method:
<$action-listops
	$tiddler="My Tiddler"
	$field="my-field"
	$subfilter="[[New Tiddler]] [<currentTiddler>] [[New Tiddler 2]]" />

Pseudocode: 
<$action-listitems
	$tiddler="My Tiddler"
	$field="my-field"
	$items="[[New Tiddler]] [<currentTiddler>] [[New Tiddler 2]]" />

Obviously, this pseudocode example is nearly identical to the extant $action-listops method — the only thing I changed was the widget and attribute names. But I want to emphasize that this was a good-faith effort; I was genuinely struggling to imagine a different way to do it.

  • We need $tiddler to specify the tiddler to be acted upon. (As you said, both the real widget and the hypothetical could support the fallback value $tiddler=<<currentTiddler>>, so I don’t see this as a point for or against either.)
  • Similarly, we need to specify $field. $action-listops uses $field="list" as its default if neither $field nor $index are defined; I suppose a new widget could do the same.
    • Notably, $action-listops and $action-setfield both allow you to condense the field-setting and value-setting attributes into a single pair in certain situations:
      • <$action-listops $tags="[[new tag]] -[[remove this tag]]" />
      • <$action-setfield myField="My new field value" /> or <$action-setfield myfield={{{ My [[new field]] value +[join[ ]] }}} />
    • $action-listitems would have to set the field name (if not = list) since it’s intended to work with any field. It could conceivably offer the fieldname=fieldvalue shortcut used in $action-setfield, but I would then expect it to follow the same rules regarding values in quote marks vs. filtered transclusions. ($action-listops does use quotes around the values of $filter="..." or $subfilter="..." despite not treating their values as literal strings to be added to the field… but IMO, since filter appears in the names of both attributes, this usage is clear and consistent with $list filter="...". If $action-listitems isn’t using a $filter attribute, it wouldn’t be reasonable to expect that myField="[[new item]]" means anything but a literal string.)
  • The title list is the standard TW way to provide a list of multiple strings that may contain spaces, so [[New Tiddler]] [[New Tiddler 2]] seemed like the obvious way to format the $items value. Since you’d previous alluded to adding/removing <<currentTiddler>> from a list, I added [<currentTiddler>] as well… using the filter syntax because [[New Tiddler]] <<currentTiddler>> [[New Tiddler 2]] isn’t proper syntax for items in a list anywhere in TW. Of course, this means we end up with something that looks very much like a simple filter, at which point I would begin to wonder, “Can I use other filter syntax? Is [<currentTiddler>] [tag[HelloThere]] a valid value for $items?”
  • Your list of potential attributes didn’t include separate named attributes for items-to-be-added and items-to-be-removed — reasonable, I think, to avoid bloating the list.
    • I’d assume this means we’d use something like $items="-[<currentTiddler>] -[[Remove me]]" to remove items from a field.
    • … and we could probably mix items-to-be-added and items-to-be-removed in the same value, giving us something like $items="[[New Tiddler]] [[New Tiddler 2]] -[<currentTiddler>] -[[Remove me]]".
    • Again, this just looks like a filter.

You also suggested an $item attribute. I assume this would take a single string, which could contain spaces — i.e, more similar to $action-setfield $field=my-Field $value="This multi-word string doesn't have to be wrapped in square brackets." /> So I’d imagine the following would be equivalent:

All pseudocode:

<$action-listitems
	$field="my-field"
	$items="[[New Tiddler]]" />

<$action-listitems
	$field="my-field"
	$item="New Tiddler" />

or 

<$action-listitems
	$field="my-field"
	$items="[<currentTiddler>]" />

<$action-listitems
	$field="my-field"
	$item=<<currentTiddler>> />

As attribute names, $item and $items are clearly extremely similar and easy to typo or misread — but they seem to demand distinctly formatted values, as one accepts a title list and the other does not. IMO, this is a situation we certainly want to avoid.

It’s also unclear to me how you’d indicate a single $item to be removed from a field. <$action-listitems $field="my-field" $item="-New Tiddler" /> doesn’t work, because -New could be a valid string in its own right. So we’d seem to need either two distinct attributes, which could potentially be combined in a single widget…

(still pseudocode)
<$action-listitems
	$field="my-field"
	$addItem=<<currentTiddler>> />

<$action-listitems
	$field="my-field"
	$removeItem=<<currentTiddler>> />

<$action-listitems
	$field="my-field"
	$addItem=<<currentTiddler>>
	$removeItem="Extant value" />

or we’d need an additional attribute that specifies how the value of $item should be handled…

<$action-listitems
	$field="my-field"
	$item=<<currentTiddler>>
	$action="add" />

<$action-listitems
	$field="my-field"
	$item=<<currentTiddler>>
	$action="remove" />

which would preclude mixing multiple action types in a single widget.

In either case, we’d have to define a list of valid actions as either named attributes or potential values of an $action attribute. By contrast, any extant filter operation (including but not limited to all the “listops” operators is automatically valid in $action-listops $subfilter="...".

While the examples above may make it pretty clear, I do also want to note that none of these imagined alternatives are even particularly shorter than their $action-listops or $action-setfield equivalents. To me, this means they don’t qualify as useful syntactic sugar: they’d require me to learn new conventions and/or new syntax for very little benefit in terms of time or characters saved.

I should say that I do make heavy use $action-listops and $action-setfield in my own wikis, and I do get tired of typing them, so I’ve made a few shortcuts for my own use. For instance, many of my tiddlers have associated data-tiddlers with programmatically-generated titles, so I’ve added the following to my global definitions:

\function data() [[$:/data/]] [<currentTiddler>] +[join[]]

<!-- macros rather than procedures only because this is old code I haven't bothered to update yet --->

\define set-index(index, value)
<$wikify name="set" text="{{{ [<__value__>!match[]] ~[<currentTiddler>] }}}">
<$action-setfield $tiddler=<<data>> $index=<<__index__>> $value=<<set>> />
</$wikify>
\end

\define delete-index(index) 
<$action-setfield $tiddler=<<data>> $index=<<__index__>> />
\end

This allows me to use shortcuts like

<<set-index newIndex>>
in place of
<$action-setfield $tiddler=<<data>> $index="newIndex" $value=<<currentTiddler>> />

<<delete-index newIndex>>
in place of 
<$action-setfield $tiddler=<<data>> $index="newIndex" />

While I’d probably write these as procedures rather than macros if I were starting fresh today, I would stand by my choice not to use widgets for this syntactic sugar — specifically because it allows me to use short-form macrocalls and positional parameters. Of course, this also means that (at least as of 5.3.x) I can’t use dynamic parameters without returning to widget syntax…

<<set-index newIndex>>
vs.
<$macrocall $name=set-index index={{{ [<currentTiddler>tag[myTag]then[X]] ~Y }}} />
or 
<$transclude $variable=set-index index={{{ [<currentTiddler>tag[myTag]then[X]] ~Y }}} />

… at which point I might as well just use $action-setfield directly because I’m really not saving myself much typing.

I think this illustrates the fundamental tension between brevity/convenience and flexibility. IMO, any syntactic sugar worth using must be visually simpler than the alternative — and this means we have to make some assumptions about what a given shortcut doesn’t do and what the user can’t customize. If $tiddler=<<data>> wasn’t hard-coded into my macro definitions, I’d have to add another named parameter, and probably a fallback value… and I couldn’t use a <<variable>> value of that parameter without resorting to a long-form macrocall, at which point the shortcut is no longer truly short.

(As a side note, these examples also illustrate what I do see as a moderate gap in working with index values — and particularly in deleting them. While $action-setfield can take either a $field or $index value, $action-deletefield doesn’t have a corresponding $index attribute, so we have to use this unintuitive <$action-setfield $index="indexName" /> workaround. That missing parallel is something I would actually really like to see added, even if we never get an $action-setindex or $action-deleteindex, but given Jeremy’s views on data tiddlers in general, I’m not particularly optimistic.)

All this to say: I think I must be missing something about your imagined implementation, because what I can come up with myself doesn’t seem simpler at all.


As a bit of a digression, your comment here also stuck out to me:

In the past, you’ve been quite critical of user solutions that introduced new JS features to accomplish things that can be done with extant TW script. In fact, in your user profile, you still define yourself as a “super user intentionally understanding tiddlywiki from an advanced user perspective, rather than solving problems via javascript and development.” And historically, you’ve also been a major proponent of maximally flexible solutions over ones specialized for one particular use-case. So I’m curious what it is about this particular issue that makes you feel new JS widgets are the best solution. Do you simply feel that it’s no longer as important to delve into extant features now that we can all “vibe-code” equally well?

I’ve never worked with ChatGPT myself, but I have noticed (in reviewing others’ broken code) that while it has a notably poor grasp of extant syntax, it’s very fond of hallucinating new widgets and operators. I can imagine that, given the much greater sample size available for Javascript, it would also be easier to get it to spit out a new JS widget than to force it to provide a working solution using core TW script. Do you see this as the best way forward for “naive” and/or “power” users?

Thankfully from v5.4.0 it will be possible to use dynamic parameters with the shortcut syntax, these changes were merged today: Dynamic parameters for macro/procedure/function calls by Jermolene · Pull Request #9055 · TiddlyWiki/TiddlyWiki5 · GitHub

Here is an example of some syntactic sugar that I use very often to reduce typing (the $$prefix for the widget name helps make it clear that these are actions):

\widget $$set.field(fields)
\function ignore.fields() [all[]] :except[[fields]]
<$parameters $params="all">
<$genesis
	$type="$action-setfield"
	$remappable="no"
	$names="[<all>jsonindexes[]ignore.fields[]] :all[<fields>enlist-input[]]" 
	$values="[<all>jsonindexes[]ignore.fields[]] :map[<all>jsonget<currentTiddler>] :all[<fields>enlist-input[]getvariable[]]"
/>
</$paramaters>
\end $$set.field

which is a shortcut for:
<$action-setfield var1=<<var1>> var2=<<var2>> var3=<<var3>>.../>

For draggy/droppy work I often have a helper that calls $$set.fields specifying the state tiddler to target and a prefix for the field names.

2 Likes

Thank you for sharing! I’ve been excited about this possibility since Jeremy first shared it here; it’s lovely to see that it will be available so soon.

2 Likes

I haven’t really been able to follow the proposal @TW_Tones is making. But instead of these, what if we had

<$action-add-listitems
	$field="my-field"
	$items="[[The Weekend]] [<currentTiddler>]" />

<$action-remove-listitem
	$field="my-field"
	$items="[[Wednesday]]" />

<$action-insert-listitem
	$field="my-field"
	$after="[[Tuesday]]"
	$items="[[Twednuesday]]" />

<$action-prepend-listitem
	$field="my-field"
	$items="[[FirstDay]]" />

To my mind, these would be quite readable. Whether they are worth expanding the already large surface area of the TW interface is a big question, though.

I hadn’t considered using multiple new action widgets with their own sets of parameters… I think your examples are in some ways clearer that what Tony seemed to be proposing (i.e. one new action widget with a lot of potential attributes), but they’re still not particularly shorter than their $action-listops equivalents. And they still share what is, IMO, the biggest issue: as you note, each of these would require new syntax, more core code, and more documentation just to duplicate features that are already available.

Moreover, if $items uses filter-like syntax to accommodate multiple literal strings or variable values, we’d need to establish whether it would permit the full range of filter functionality.

<$action-add-listitems
	$field="my-field"
	$items="[tag[HelloThere]] [[The Weekend]] [<currentTiddler>]" />
                   ^
             Could I do this?

<$action-add-listitems
	$field="my-field"
	$items="[tag[HelloThere]] -[[The Weekend]] [<currentTiddler>]" />
                              ^
                     What about this?

And if the latter were valid syntax, would it remove “The Weekend” from the list of items in “my-field”, or simply from the list of items to be added (e.g. if “The Weekend” had tag “HelloThere”)?

IMO, this gets more complicated and potentially confusing than just using $subfilter, which is already clear about its function:

  • modifies the current contents of $field
  • … using the full range of filter syntax.

Shorter, no. But I do think they’d be much more readable. And that is significant.

That’s always the tradeoff for syntactic sugar. I haven’t decided for myself if that seems like a good trade here, but I’m leaning that way: the - for remove is elegant but also hard to see.

1 Like

Actually if there is any problem I am trying to solve is inconsistencies such as;

  • explicit support for the tags field and not other list fields.
  • Simpler ways to add and remove items for list fields

The first complication is that tm- messages are behind fieldmangler, and fieldmangler potentially to be deprecated. Given this in this topic I have endeavoured to solicit alternate views, but as if often the case I am asked to defend examples I gave, rather than receive new and innovative suggestions that would help us simplify adding and removing items for list fields.

Thanks for your effort @etardiff I have being on a similar journey to your documented one above;

  • You could say that is my point, I think basic list field manipulation can be simplified and it is hard to do so for all the reasons you raise. I am open to custom widgets, potentiality even some helpful documentation but on exploring this we find in the core an asymmetry between tags (a list field) and another list fields. We also see different approaches needed to manipulate list fields rather than tag fields, to do so, the user must go from messages to action-listops

If you were a follow of my world view about LLM’s you would realise my perspective is a nuanced one.

I stand by all of the comments and position above, and there is no hypocrisy, in what I suggest here.

One of the outputs of my approach to TiddlyWiki is to have items moved into the core when they expand functionality, address gaps, or make the usage simpler. My point was “even I could make these core changes with an LLM”.

  • You will also see I have pointed out the value of the right question using LLM’s for example using an existing filteroperator module to ask for a new one, and I would use these privately.
  • I maintain that asking a question in talk.tiddlywiki, or proposing a solution based wholly on an LLM and no testing, is always a disaster.

The idea that " new JS widgets are the best solution" was not my intention

Core mods are not the complex part of the questions posed here.

  • The hard part is simplifying while allowing flexibility.

Note:
My above responses try to answer at a high level and do not address all the details raised in @etardiff’s post. Why is the subject of simplification so complex?