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 onehyphen
orunderscore
in either thetitle
orcaption
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 withXXX
, then replace all underscores with hyphens, then replace allXXX
with underscores.
- Note the use of
-
$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.
- Note the use of
- 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 thetitle
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.
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
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