How I limit which macros are listed by "macro by name"

What this is about

I have started using the Trigger macro by name action much more lately, now that I have worked out a convenient system that shows only the macros that I want to see in the list presented by the prompt.

By a "system" I mean a "hack that works for me" rather than something that you will necessarily want to use in its current form. Perhaps this will help spark your own ideas, or feedback of more educational value.

I detail the path I took, so there are opportunities to respond with either "oh, I didn't know about that" or "why the heck didn't you just..?", depending on the level of your experience with Keyboard Maestro. Also it illustrates either how I have overlooked something obvious or else how smart groups have what was, to me, one surprising limitation.

This is a long post, sorry… You can, if you like, jump to the conclusion, or read something else.

Related topics might include Trigger Macro by Name issue.

What used to put me off using "macro by name"

To quickly trigger macros that I use very often within certain contexts, I have, for a long time, used conflict palettes. For example, within an macro group that is active for the Finder only, there could be macros for "MF Move to folder Foo", "MB Move to folder Bar" and so on. Select the files, press M then B and they have been moved.

That's a very quick and convenient system, but sometimes I want to have to confirm an action first by hitting Return, or I want to have freer matching of the text I enter. The obvious solution is to use a macro based on trigger macro by name, but I was put off using it because, by default, it listed too many macros. If you don't care about such things, you have the right priorities in life, and should stop reading now.

Standard ways of limiting which macros are listed

Setting the search range to "Active macros" helps, but that will still include lots of macros that one never wants to trigger in this way—for example, subroutines and macros that are to be triggered by hotkeys.

However, the search range can be limited further by specifying the macros that are to be included, by either specifying each one (no thanks) or by specifying a group, including smart groups. So, we just need to set up a suitable smart group, and point trigger macro by name at that.

Good… but how should the smart group be set up?

The challenge of setting up a suitable smart group

Remember that my aim was to be able to choose which macros should be presented by trigger by macro name. Those are idiosyncratic choices. That is to say, rather than trying to codify why I might want one macro in the list and not another, it is far simpler just to somehow mark the macros that are to be included, and have the smart group match on that mark. Therefore the smart group should simply match some text that I have added somewhere in the relevant macros.

So what sort of text would be suitable, and where should it be added?

A long string such as "include in macro list" is likely to be unique, whereas something shorter might be contained in many macros that should not be included in the list, so let's start from there. Set up a smart group to match on the arbitrarily chosen string "include in macro list".

Putting "include in macro list" in the name of the desired macros would of course look unacceptably distracting in the KM Editor, let alone the list of macros that is to appear in! So we would need to put that string elsewhere in the relevant macros.

I wanted to be able to add (and remove) the text string easily and quickly, I wanted them to be easy to see in KM's editor, and I did not want them to be a visual distraction in the presented list of macros.

That ruled out putting the string in, for example in the notes for any action (the nuisance of having to click on the cog wheel, select "notes"… and afterwards not being able to see the note without checking on it) so I tried using a comment action. The text to be matched would be a string such as "include in macro list" and the comment could be saved as a "favourite" (or "favorite" of course), so that it could be added to any macro fairly quickly.

That could have been the end of the tale, but for me, this solution felt clunky in practice and it added a bit of visual clutter to the macro. I wanted a system that would be very quick and convenient to use and which would be unobtrusive.

So… what if I just added a full stop (period) after the names of macros that I wanted to appear in the list? Macro names don't normally end in a full stop. A simple, unobtrusive "." could be my way of marking the wanted macros.

How then, would I get the smart group to match a macro name containing a full stop? It would be even better to match a full stop that appears only at the end of the name (in case, say, I ever import a macro that uses periods elsewhere in its name) but let's start with the simpler case.

Set the smart group to match "name: ." and... oh dear, there are far too many matches.

What does "has a name" mean?

The Search Strings page in the Wiki tells us that "name: " matches "any macro that has a name matching the specified string". It appears that "has a name matching" does not simply mean "has the match in its name" but rather "contains a matching name". Consequently our smart group contains all the macros that use an action titled "pause for 0.5 seconds", just for a start!

So let's try to match a period that appears at the end of a name.

What does "matches" mean here?

Actions are as unlikely as macros to end in a full stop, so we would not have to worry about excluding them. Since we are looking at matching here, we can perhaps try a regular expression? The regular expression to match a period at the end of a string is \.$, so let's try that.

name: \.$

No matches.

Hmm. name: "\.$"

Nope.

How about name: (\.$)

Not today.

So either I am doing something wrong or it turns out that "matching" in this context does not imply that regex "matching" is possible when setting up a smart group.

Looking for a replacement for the full stop, and settling on another full stop

Instead of using a full stop to mark macros for collection in the smart group, perhaps another unobtrusive character could be used instead. How about using an invisible character? They are unobtrusive!

There are certainly many of them, but such a character would be invisible not just in the list prompt, but in the Editor, and that would surely cause confusion at some point. Also, the more mundane space characters might be treated as being the same as the conventional, ubiquitous space character, and the weirder invisible characters might cause unexpected difficulties.

How about using the smallest character you can find? That would be visible in the Editor, just about, and might not be too distracting in the list. Well it turns out that the smallest characters appear in Semitic languages which are written in right-to-left order. Adding right-to-left characters to left-to-right strings is easy enough to do in a Mac app, but trying to edit the resulting string can be a little confusing for the text cursor, for a start (for an example of a Mac app which seems confused about how to handle bi-directional text, Exhibit A is, unsurprisingly, Apple's Music.app).

What about other kinds of full stop, then? Do they exist? They do! And I settled on using a fullwidth full stop, which looks like this: , a small version of the familiar full stop but with a space after it.

The conclusion, FFS

My smart group is now set to look for name: . and my "Run macro by name" macro consists of just a trigger macro by name action set to "active macros" only in that smart group.

And here are some example of some macros appearing in a prompt by virtue of ending in "."

Screenshot 2024-12-10 at 15.10.13

That's pretty, and unwanted macros do not appear, but was it worth spending time on?

Yes, because I learned some things (not by any means all shown here) along the way.

Was posting this worthwhile?

Probably not, but it's done now. Next question.

How would I enter that Fullwidth Full Stop, in the unlikely event that I might want to?

I use a text expander to type the Fullwidth Full Stop character, and you will understand why the typed trigger is ;ffs.

3 Likes

You could limit a lot of the noise with

n:. -w:.

...but I prefer your solution. I've used ⌥⇧9 in the past -- no idea of the character name but it's "a period in the middle of the line".

1 Like

Technically, those are "interpuncts," but i prefer the more friendly sounding "middot," though spell check hates that one.

-rob.

1 Like

So that would be a search for a full stop but not at a word boundary, going by the Search Strings page.

"An interpunct, also known as an interpoint, middle dot, middot, centered dot or centred dot, is a punctuation mark consisting of a vertically centered dot used for interword separation in Classical Latin. (Word-separating spaces did not appear until some time between 600 and 800 CE.) It appears in a variety of uses in some modern languages." says codepoints.net.

I gather that I was not mistaken about smart groups lacking support for regular expression matching, and wonder whether that should go on a wish list… but perhaps there is not much of a demand for such an enhancement, because I don't recall anyone mentioning such a need… Perhaps, though, the use of the term "matches" in the Wiki deserves a disclaimer? Or again, maybe it's not generally an issue!

2 Likes

To run (or select) a macro by name, we can, of course, also use the partial matching of the Prompt with List action:

Macro chosen by partial name matching.kmmacros (5.6 KB)

1 Like

Very interesting, thank you. So what you are doing here is dealing with the matching purely within Prompt with List rather than using a smart group.

If I understand correctly, the basic approach to the challenge would be unchanged. With your macro, we lose the need for a smart group (at the expense of more code in a macro, but we can afford either!) but the prompt would now have to take on the group's responsibility for matching against a unique character (e.g. such as my choice of fullwidth full stop).

If you wanted to paste a particular Group UUID at at the top of a script
(from Edit > Copy as > Copy UUID in the KM editor,
you could, of course, restrict the prompt list to the macros of that group.

MACRO chosen from GROUP members by partial name matching.kmmacros (6.5 KB)


Expand disclosure triangle to view JS source
const
    // kmvar = { "GroupID": "69C314D6-DEDC-446E-8722-6794D05E7555" },
    group = Application("Keyboard Maestro").macroGroups.byId(
        kmvar.GroupID
    );

return group.exists()
    ? (() => {
        const
            macros = group.macros,
            ids = macros.id();

        return JSON.stringify({
            groupName: group.name(),
            groupMenu: macros.name().map(
                (k, i) => `${ids[i]}__${k}`
            )
                .join("\n")
        });
    })()
    : `No Group found by UUID: '${kmvar.GroupID}'`;

Thanks, but as far as I can see, that still does not address the issue of what determines how those macros are grouped together—or in other words, what makes them members of the set of macros that I want to see.

Yes, we can specify the UUID from a smart group, but what marks the macros as members of that group?

To recap:

If the wanted macros were to be dragged to an ordinary (not smart) group, the group would have to be made available within all applications, which means that macros would appear in the prompt list even in contexts that were not appropriate—for example, a macro of relevance only to Safari would appear in the list when I called the prompt from the Finder.

Therefore, an ordinary group is not an appropriate way to collect the macros. The alternative is a smart list, and since smart lists work by matching text, that returns us to the obligation to mark, using text, the macros that are to be permitted to be in the prompt.

1 Like

You mean like this?

If it was easier to add and remove Macros from that list that seems like it would fit your need to choose which Macros to include in the search? The fact that is is only looking for "Active" macros means it will only find macros that relate to the Group you are in, taking care of your next concern:

If we copy the XML of the example Action I posted above, we get this:

> <?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>ActionUID</key>
		<integer>16518402</integer>
		<key>MacroActionType</key>
		<string>TriggerByName</string>
		<key>Macros</key>
		<array>
			<string>79CF26C1-D1E0-4030-9B3F-8ECD96FF506C</string>
			<string>9F5B2800-657A-447E-8801-3A460F783477</string>
		</array>
		<key>TimeOutAbortsMacro</key>
		<true/>
	</dict>
</array>
</plist>

Notice the lines in there:

		<array>
			<string>79CF26C1-D1E0-4030-9B3F-8ECD96FF506C</string>
			<string>9F5B2800-657A-447E-8801-3A460F783477</string>
		</array>

That is the part that lists the two selected Macros by their UUIDs.

This means it is possible to make a Macro where you input a list of Macro UUIDs and it generates a custom Trigger Macro by Name Action with a list of the Macros you want to include.

I haven't got time to play with that tonight but could do tomorrow if it is of interest?:

You are right to draw attention to the process of adding macros in that way. After all, it isn't a difficult process. However, for me it was a distraction that I didn't want. Perhaps the main reason I use KM is because it helps me avoid UI annoyances (hi Apple!) that break flow, so it's a prime concern. Personally I find the process of adding a macro by pressing a little plus button and then selecting from a dropdown list a much more distracting (flow-breaking) and fiddly procedure than clicking on the relevant macro group and then the desired macro, which requires no thought. Going to macros in that way is such a familiar and easy (user) action.

I am not saying my strange method is inherently better, of course. I deliberately chose the words "How I limit…" for the subject line rather than "How to…"! It perhaps didn't belong in "Tips and Tutorials", but it wasn't a "Question"… Anyway, by zeroing in on what I don't like about the more conventional method, you have helped me analyse and verbalise why. :+1:

Thanks for posting the interesting idea, which might appeal to a reader who does want to be able to specify a list upfront, rather than have an ad hoc (and frictionless) way of marking macros for inclusion, which was my aim.

Yeah, it was more like an account of an expedition… :face_with_monocle:

I wondered about a normal Group to which added/removed aliases -- but, AFAIK, that'll ignore any activation contexts on the originals. You could set the aliases Group to the same contexts, but that seems to be adding unnecessary complexity...

If ever there was a time for Smart Group-searchable macro tags, this is it!

Absent tags, I'm a fan of comments for this kind of metadata. And I find that having them as the first action and undisclosed doesn't distract from the macro itself when editing. You could have the Comment as a favourite action, but dragging that in or deleting it every time you want to make a change is so last year...

Given a Smart Group like this:

...here's a quick hack that'll add exactly this:

image

...to the top of the currently selected macro:

set theXML to "<dict>
		<key>ActionName</key>
		<string>Include in “Prompt Test” Smart Group</string>
		<key>IsDisclosed</key>
		<false/>
		<key>MacroActionType</key>
		<string>Comment</string>
		<key>StyledText</key>
		<data></data>
	</dict>"

tell application "Keyboard Maestro"
	set theMacro to macro id (item 1 of (get selectedMacros))
	make new action at beginning of theMacro with properties {xml:theXML}
end tell

...and to remove:

tell application "Keyboard Maestro"
	set macroName to choose from list (get name of every macro of smart group "Prompt Test")
	set theUUID to item 1 of (get id of every macro of smart group "Prompt test" whose name is macroName)
	if name of first action of macro id theUUID is "Include in “Prompt Test” Smart Group" then delete first action of macro id theUUID
end tell

As usual with my hacks, error handling is "somebody else's problem" :wink: So watch out for two macros with the same name...

These could easily be expanded to batch process, could be rolled into KM macros with prettier (and more useful) dialogs, etc.

1 Like

Yes, I mentioned:

Part of what made this feel "clunky" to me was that I am in the habit of adding macros using the prompt that is brought up via -A, which is more efficient (= less distracting) than dragging from the list brought up by -K but which inserts the action after any selected action in a macro. That meant that I would have to drag the macro to the top, where, of course, comments belong… That's where I put my "read me" comments about macros. Another habit.

What I didn't realise, until the possibility occurred to me and I tested the idea just now, is that if one makes sure that no action in the macro is selected (which rarely seems to be the case by default, for my usage of the Editor!), the new macro is added to the end of the macro! That means that the comment is out of the way. If I had realised that, I would have stopped my explorations there. :man_facepalming:

Something I hadn't forgotten was that customised actions can be saved as favo(u)rites, so a comment action containing the text to be matched could be recalled quickly via that -A prompt. Your alternative to doing that is really interesting, however!

Yes, is was reiterating your points to set up the next bit :wink:

To do the same with the ASs, change beginning to end in the first and first to last in the second.

What is piquing my interest in this approach is that you can batch add/remove from the Smart Group. You could even pass in a list of macro UUIDs from another macro fired by contextual trigger -- a Smart Group whose contents changes by context.

What I'm undecided on is whether it's better to use multiple name-only comments for multiple "tags" or a single comment containing multiple "tag lines". Hmmm...

1 Like

I was doing exactly the same, don't worry! :smile: My original post was so long that it seemed better to quote from it again rather than just refer to it.

The rabbit hole becomes a warren! :+1:

Speaking of metadata, would the Note search qualifier serve?

"note: not: Match any action that contains the text in a Note note:custom"

Then use something like this to add/remove tags:

set theTag to "my tag"
tell application "Keyboard Maestro"
	set theMacro to macro id (item 1 of (get selectedMacros))
	if exists (first action of theMacro whose notes contains theTag) then
		set notedActions to actions of theMacro whose notes contains theTag
		repeat with notedAction in notedActions --remove theTag from the notes from any action in which it occurs
			set theNote to notes of notedAction
			tell application id "com.stairways.keyboardmaestro.engine"
				set theUntaggedNote to search theNote ¬
					for theTag ¬
					replace ""
			end tell
			set notes of notedAction to theUntaggedNote
		end repeat
	else -- theTag to the macro's first action
		set notes of first action of theMacro to notes of first action of theMacro & " " & theTag
	end if
end tell

Gah!

The add part doesn't work. Sorry.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set theTag to "can you find me"
tell application "Keyboard Maestro"
	set theMacro to macro id (item 1 of (get selectedMacros))
	if exists (first action of theMacro whose notes contains theTag) then
		set notedActions to actions of theMacro whose notes contains theTag
		repeat with notedAction in notedActions --remove theTag from the notes from any action in which it occurs
			set theNote to notes of notedAction
			tell application id "com.stairways.keyboardmaestro.engine"
				set theUntaggedNote to search theNote ¬
					for theTag ¬
					replace ""
			end tell
			set notes of notedAction to theUntaggedNote
		end repeat
	else -- add theTag to the macro's first action
		set notes of first action of theMacro to theTag
	end if
end tell

Personally (I'm just recapping again here) I find accessing an action's notes to be a little too fiddly, and don't like to have the entered text hidden away. However, this discussion has, I've been pleased to note (er, no pun intended), expanded past being about this one user's opinions!

This is an interesting topic, @kevinb. Thanks for starting it!

Every time a topic like this comes up, I'm amazed by the power of Keyboard Maestro and the knowledge and creativity of the forum members.


@Zabobon, thanks for pointing out that feature. I've thought of that in the context of the Show Palette of Macros action but not with the Trigger Macro by Name action. So now I will use it and leverage some of the related capability that's built in. For example, the Select Macros by Name....


BTW, it's not obvious when the macro list appears, but the Trigger Macro by Name action includes this nice feature...

If you wish to open Keyboard Maestro directly to the selected macro rather than triggering it, use Return or +click.

As a reminder, I suppose I could add this information-only macro to the list.

Download: ⌥+Return to 'Edit' the selected macro.kmmacros (1.7 KB)

Fortuitously that macro will likely sort to the top...

2 Likes

Yep. For sure.
FWIW and others interested in messing around with notes as a location for metadata.

set theTag to "can you find me"
--This gets the id of the macros of containing the action where the note occurs.
tell application "Keyboard Maestro" to set theIDs to id of macros whose notes of its actions contains theTag
--This batch clears notes from all actions's notes where it occurs.
tell application "Keyboard Maestro" to set notes of actions of macros whose notes contains theTag to ""
--This get a count of the notes containing the tag.
tell application "Keyboard Maestro" to count of (actions of macros whose notes contains theText)
2 Likes