Search and replace certain characters in tiddler caption and title

I have some tiddler titles and captions which have hyphen and underscore. I want to replace hyphen with underscore and underscore with hyphen in these titles and caption in bulk. I tried commander plug in- but it didn’t work.
How to create a button to do this action in bulk

You may use SNR in Tiddler Commander. Of course, it searches and replace in a batch, so you need the title and caption one by one.

TBH if it’s possible to capture all the tiddlers that need to be processed with a filter, I would just cut the Gordian knot by exporting them all to JSON, fire up vim, do the changes and import them back. Just because TiddlyWiki is so cool, doesn’t mean I’m willing to give up on using external tools that are better suited at niche tasks like text crunching.

Give this a try:

IMPORTANT: as with all BULK tiddler operations, be sure to back up your TiddlyWiki before doing this action!

<$button> go
<$list filter="[search:title,caption:regexp[-|_]]">
<$let newtitle={{{ [{!!title}search-replace:g[-],[XXX]search-replace:g[_],[-]search-replace:g[XXX],[_]] }}}
   newcaption={{{ [{!!caption}search-replace:g[-],[XXX]search-replace:g[_],[-]search-replace:g[XXX],[_]] }}}>
<$action-setfield $timestamp=no title=<<newtitle>> caption=<<newcaption>>/>
<$list filter="[{!!title}!match<newtitle>]"><$action-deletetiddler $tiddler={{!!title}}/></$list>
</$let>
</$list>
</$button>

<table>
<$list filter="[search:title,caption:regexp[-|_]sort[]]">
<tr><td><$link/></td><td>{{!!caption}}</td></tr>
</$list>
</table>

Notes:

  • The outer $list finds all tiddlers that have at least one hyphen or underscore in either the title or caption field.
  • $let newtitle={{{ ... }}} newcaption={{{ ...}}} does a search-replace for hyphen and underscore.
    • Note the use of [XXX] as an intermediate value in the filter syntax. This allows the “swapping” of hyphen and underscore by using a unique text value that must not occur within the tiddler’s title or caption value: first replace all hyphens with XXX, then replace all underscores with hyphens, then replace all XXX with underscores.
  • $action-setfield assigns the newtitle and newcaption to the current tiddler
    • Note the use of $timestamp=no to prevent the tiddler’s modification date from being changed.
  • The inner $list filter checks to see if the tiddler’s title has changed. If it has, then delete the old tiddler, since using $action-setfield to set the title field doesn’t actually RENAME the existing tiddler, but instead creates a NEW tiddler with the indicated title.

As a final touch, display all the matching tiddlers, so you can quickly verify that the titles/captions have changed in the way you want.

2 Likes

Thankyou @EricShulman for this solution. This is working in my testing in mobile phone …I will confirm once I am back home. Is there any way to use relinking in this code since tiddler titles are being changed?

Edit:

Since its not renaming, relinking won’t work I guess.

$action-setfield assigns the newtitle and newcaption to the current tiddler

Do I correctly understand here that “current tiddler” means https://tiddlywiki.com/#currentTiddler%20Variable (in context of the outer list) rather than the tiddler that holds the whole code snippet above?

I find it so hard to comprehend because the list code doesn’t loop explicitly and there’s no explicit variable name that contains the current list item :man_facepalming:

Yes. That is correct.

The outer $list widget loops over all tiddlers that match the search filter operator; and, because the $list widget doesn’t have an explicit variable, currentTiddler is automatically set. This is what permits references to !!title and !!caption within the loop. It also allows the implied use of $tiddler=<<currentTiddler>> as the default target title for the $action-setfield widget.

The same code can be written using tid as an explicitly-declared variable name:

<$button> go
<$list filter="[search:title,caption:regexp[-|_]]" variable=tid>
<$let newtitle={{{ [<tid>get[title]search-replace:g[-],[XXX]search-replace:g[_],[-]search-replace:g[XXX],[_]] }}}
   newcaption={{{ [<tid>get[caption]search-replace:g[-],[XXX]search-replace:g[_],[-]search-replace:g[XXX],[_]] }}}>
<$action-setfield $tiddler=<<tid>> $timestamp=no title=<<newtitle>> caption=<<newcaption>>/>
<$list filter="[<tid>!match<newtitle>]"><$action-deletetiddler $tiddler=<<tid>>/></$list>
</$let>
</$list>
</$button>

<table>
<$list filter="[search:title,caption:regexp[-|_]]" variable=tid>
<tr><td><$link to=<<tid>>/></td><td>{{{ [<tid>get[caption]] }}}}</td></tr>
</$list>
</table>

While this might be slightly more instructive from a pedagogical perpective, it doesn’t affect the actual results, and is somewhat less performant because it needs to use <tid>get[fieldname] instead of {!!fieldname}.

-e

2 Likes