Case-Sensitive Search in Editor

Howdy all,

I am trying to find out if the search field in the KM Editor has a case-sensitive option. I’ve read the Wiki Page on Search Strings, but don’t see anything that references it. Does anybody know if this is possible?

For context, I’m trying to search for macros that contain “YYYY” to replace them with “yyyy” (because I used the wrong ICU syntax in a few macros recently :sweat_smile:). Any help is appreciated!

-Chris

It crossed my mind that you could export your macros as XML, use find and replace in a text editor, and then reimport them. The last step is where complexity arises, but it looks as though @Zabobon came up with a solution earlier this month:

You could also make a smart group which includes only recently created (or modified) macros and search in just that.

AFAIK it isn't case sensitive. AppleScript can be, though, so this will search all your macros for YYYY, add a comment to the top of each, then make a Smart Group based on that comment string.

Script
set theXML to "<dict>
		<key>ActionUID</key>
		<integer>16622779</integer>
		<key>IsDisclosed</key>
		<false/>
		<key>MacroActionType</key>
		<string>Comment</string>
		<key>StyledText</key>
		<data>
		cnRmZAAAAAADAAAAAgAAAAcAAABUWFQucnRmAQAAAC5uAAAAKwAAAAEAAABm
		AAAAe1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNDA0XGNvY29h
		c3VicnRmNDcwCntcZm9udHRibH0Ke1xjb2xvcnRibDtccmVkMjU1XGdyZWVu
		MjU1XGJsdWUyNTU7fQp9AQAAACMAAAABAAAABwAAAFRYVC5ydGYQAAAAd3Kj
		V7YBAAAAAAAAAAAAAA==
		</data>
		<key>Title</key>
		<string>Replace YYYY with yyyy?</string>
	</dict>"

tell application "Keyboard Maestro"
	considering case
		repeat with eachMacro in (every macro whose xml contains "YYYY")
			make new action at beginning of eachMacro with properties {xml:theXML}
		end repeat
	end considering
	make new smart group with properties {name:"Maybe Replace 'YYYY'?", search strings:{"\"Replace YYYY with yyyy?\""}}
end tell

Could take a while if you've a lot of macros to search through, so you might want to target the search on a "recent macros" Smart Group...

Of course, if you're feeling brave (and don't want to check each macro as you go...):

tell application "Keyboard Maestro"
	considering case
		set macroList to every macro whose xml contains "YYYY"
		repeat with eachMacro in macroList
			set actionList to (every action of eachMacro whose xml contains "YYYY")
			repeat with eachAction in actionList
				set theXML to xml of eachAction
				set AppleScript's text item delimiters to "YYYY"
				set theXML to every text item of theXML
				set AppleScript's text item delimiters to "yyyy"
				set theXML to theXML as text
				set xml of eachAction to theXML
			end repeat
		end repeat
	end considering
end tell

Make a backup first, obvs!

2 Likes

Thanks @Nige_S for the AppleScript, unfortunately it doesn’t appear to be working for me. It catches any macro who has lowercase yyyy, adding the comment, instead of just macros who contain uppercase YYYY.

Attached is a screenshot of a macro who has one action containing yyyy, as well as it’s XML copied directly from KM. The yyyy strings start at line 31.

Action Screenshot (click to expand/collapse)

Macro XML (click to expand/collapse)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>ActionName</key>
		<string>Prompt for reminders data</string>
		<key>ActionUID</key>
		<integer>15475196</integer>
		<key>Buttons</key>
		<array>
			<dict>
				<key>Button</key>
				<string>Continue ↩/</string>
				<key>Cancel</key>
				<false/>
			</dict>
			<dict>
				<key>Button</key>
				<string>Cancel ⎋/.</string>
				<key>Cancel</key>
				<false/>
			</dict>
		</array>
		<key>MacroActionType</key>
		<string>PromptForUserInput</string>
		<key>NotifyOnTimeOut</key>
		<true/>
		<key>Prompt</key>
		<string>Future dates:
%ICUDateTimePlus%1%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%2%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%3%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%4%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%5%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%6%Days%MMMM d, yyyy (EEEE)%
%ICUDateTimePlus%7%Days%MMMM d, yyyy (EEEE)%
</string>
		<key>TimeOutAbortsMacro</key>
		<false/>
		<key>TimeOutPeriod</key>
		<real>300</real>
		<key>Title</key>
		<string>Add new reminder</string>
		<key>Variables</key>
		<array>
			<dict>
				<key>Default</key>
				<string>|%Variable%Reminders__List%|%Variable%local__Reminders List%</string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__List</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>%Variable%local__Person%</string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__Text</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>%Variable%local__Notes%</string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__Notes</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>None|Low|Medium|High</string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__Priority</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>%Variable%local__URL%</string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__URL</string>
			</dict>
			<dict>
				<key>Default</key>
				<string></string>
				<key>Type</key>
				<string>Automatic</string>
				<key>Variable</key>
				<string>Reminders__Tags</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>NOW() + (3600 * 4)</string>
				<key>Type</key>
				<string>DateAndTime</string>
				<key>Variable</key>
				<string>Reminders__Alert</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>0|1</string>
				<key>Type</key>
				<string>Checkbox</string>
				<key>Variable</key>
				<string>Reminders__Flag</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>0|1</string>
				<key>Type</key>
				<string>Checkbox</string>
				<key>Variable</key>
				<string>local__Open Reminders</string>
			</dict>
			<dict>
				<key>Default</key>
				<string>0|1</string>
				<key>Type</key>
				<string>Checkbox</string>
				<key>Variable</key>
				<string>local__Delete Variables</string>
			</dict>
		</array>
	</dict>
</array>
</plist>

Side note, I was always aware of the Revert Macros feature, but haven’t used it much. But the “At Editor Launch” option is fantastic for cases like this.

My apologies -- it looks like the whose clause doesn't respect considering case.

I swear it worked in my tests, but I obviously didn't test well enough.

considering does work in a TIDs-based search and replace, so while the second script will touch far too many actions it will only change the ones we want to change. We can save a bit of time (and some pointless XML updating) by checking:

tell application "Keyboard Maestro"
	set macroList to every macro whose xml contains "YYYY"
	considering case
		repeat with eachMacro in macroList
			set actionList to (every action of eachMacro whose xml contains "YYYY")
			repeat with eachAction in actionList
				if xml of eachAction contains "YYYY" then
					set theXML to xml of eachAction
					set AppleScript's text item delimiters to "YYYY"
					set theXML to every text item of theXML
					set AppleScript's text item delimiters to "yyyy"
					set theXML to theXML as text
					set xml of eachAction to theXML
				else
					--do nothing
				end if
			end repeat
		end repeat
	end considering
end tell

How about using BBEdit to do a Search and replace directly in Keyboard Maestro Macros.kmsync(.xml)? Would that be too dangerous?

I prefer not to directly modify those files. Besides, there are some macros that contain that string where I do want it capitalized, namely in bash scripts since the syntax is different.

Thanks @Nige_S, while I don’t want to automatically modify the macros (since in some I want to retain YYYY), this should give me what I need with some tweaking. Thanks again!

I understand. But you can do that in BBEdit too, of course.

In that case, why would this not have got the job done, or at least been the way to identify the macros? I understand that there was a need to individually review a few macros, created recently. I ask because maybe I've missed a factor here, or if not, it's very interesting to me always how people think of these problems in different ways!

At some point I realized I wanted to go ahead and audit all of my macros instead of just the recently created/modified ones. I don’t think I mentioned that however, so the smart group idea would indeed work for what I initially stated. But I changed gears about halfway through this process and probably didn’t mention it. :sweat_smile:

1 Like

Ah! Thanks. :gear: