[Feature Request] To include the Macro Group of the target macro besides its name, in the "Execute Macro" macro

This is how a given "Execute Macro" currently looks in Keyboard maestro

10%20PM

As you can see, the target macro in this case is named "Collection - Add".

My request is that the "Execute Macro" is updated to include the Macro Group of the target macro, besides the Name of the target macro, or at least a way to open the target macro somehow to see which one it is being pointed to.

The context around my request is that I make heavy use of the "Execute Macro" macro so that I can have helper macros but I also have multiple macros named the same but located in different macro groups so that different apps are affected differently — My current workaround is to include the name of the group in the macro itself but that seems redundant.

Hopefully this helps other users too ヘ( ^o^)ノ\(:slight_smile: )

2 Likes

Thanks for pointing me in the right direction @peternlewis that is exactly what I was looking for.

A visual cue that would save me from having to open the target macro would be preferred but at this point that is just a nice to have given that the feature you are referencing does get the job done.

Many thanks!

Yes, I'm reviving an old topic, because I think it's something that Keyboard Maestro really should have. Consider this:

image

One of those execute macro actions will work, and one will fail—that's because one refers to a macro within the current macro group, and the other refers to one outside the current macro group. The code in both macros is similar, but not identical, and calling the wrong one leads to incorrect results for a pretty critical task in my macros (confirming that a downloaded macro update is legitimate).

There should be some sort of visible indicator for macros called outside the current group. Right now, it reuires clicking into each macro call to use the menu item to find its source. Color those external calls red, put a flag icon next to them, include the macro group name, whatever ... just something to help us quickly and easily find these rogue calls that can occur quite often for those of us who share multiple versions of our macros.

Had this feature existed, I wouldn't have just spent 15 minutes trying to figure out why I was getting the wrong results.

Thanks for considering it;

-rob.

1 Like

I personally love that about this forum and it drives me batty going to forums like Apple where they close the forums all the time or places that they have a new forum for each new version release. So much faster to just read the dates and scroll and see if things have been solved and see modern things about the same topic instead of hitting dead ends so frequently. I know how to scroll and see what is the newest post and read backwards if I need to.

As I have posted elsewhere fairly recently, I'd also love to be able to just option click on this and not wait the 3 to 4 seconds it takes to populate the list and then move the cursor over to get to the Go To menu.

I do love the ability to quickly jump to the macro with these 3 macros. You can jump to the selected action in the current window, in a new window, and find all the macros that use your macro.

Example(s) - Skillet Macros.kmmacros (123 KB)

I use similar macros, but it would greatly speed debugging with a visual indicator that required no menus or clicks, only scrolling.

-rob.

1 Like

I 100% agree with you!

Working off the top of my head, as a POC...

This script

tell application "Keyboard Maestro"
	set theMacroID to item 1 of (get selectedMacros)
	set macroGroup to macro group of macro id theMacroID
	set actionList to every action of macro id theMacroID whose xml contains ">ExecuteSubroutine<"
	repeat with eachAction in actionList
		set theXML to xml of eachAction
		tell application "Keyboard Maestro Engine"
			set subUUID to search theXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
		end tell
		if macro group of macro id subUUID = macroGroup then
			set color of eachAction to "Green"
		else
			set color of eachAction to "Red"
		end if
	end repeat
end tell

...will colour all top-level "Execute a Subroutine" actions of the currently-selected macro green if the subroutine is in the same Macro Group, red if the sub is in a different Group. Perhaps you could expand on it -- including "Execute a Macro" actions is easy, delving into nested actions a little harder but should be doable...

4 Likes

That looks very useful, thanks!

I think you have greatly overestimated my AppleScript abilities! :slight_smile:

-rob.

This is very handy and helpful to learn from. It took me a little bit to break it apart but here is the Execute a Macro. I will really need to dive into subroutines vs Execute a Macro because I have always just executed a macro instead of a subroutine.

tell application "Keyboard Maestro"
  set theMacroID to item 1 of (get selectedMacros)
  set macroGroup to macro group of macro id theMacroID
  set actionList to every action of macro id theMacroID whose xml contains ">ExecuteMacro<"
  repeat with eachAction in actionList
    set theXML to xml of eachAction
    tell application "Keyboard Maestro Engine"
      set subUUID to search theXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
    end tell
    if macro group of macro id subUUID = macroGroup then
      set color of eachAction to "Green"
    else
      set color of eachAction to "Red"
    end if
  end repeat
end tell

If you want to change them both back to the default color of none.

Default Color
tell application "Keyboard Maestro"
  set theMacroID to item 1 of (get selectedMacros)
  set macroGroup to macro group of macro id theMacroID
  set actionList to every action of macro id theMacroID whose xml contains ">ExecuteMacro<"
  repeat with eachAction in actionList
    set theXML to xml of eachAction
    tell application "Keyboard Maestro Engine"
      set subUUID to search theXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
    end tell
    if macro group of macro id subUUID = macroGroup then
      set color of eachAction to "None"
    else
      set color of eachAction to "None"
    end if
  end repeat
end tell


tell application "Keyboard Maestro"
  set theMacroID to item 1 of (get selectedMacros)
  set macroGroup to macro group of macro id theMacroID
  set actionList to every action of macro id theMacroID whose xml contains ">ExecuteSubroutine<"
  repeat with eachAction in actionList
    set theXML to xml of eachAction
    tell application "Keyboard Maestro Engine"
      set subUUID to search theXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
    end tell
    if macro group of macro id subUUID = macroGroup then
      set color of eachAction to "None"
    else
      set color of eachAction to "None"
    end if
  end repeat
end tell

Nested actions can be also be searched from a macro's xml with xpath.

However, directly getting/setting the properties of the found actions still requires interacting with the nested actions either thru UI selections or as script objects. Either approach can work by using the Editor's "selectAction" and the actions ids from the xpath query to select each found action.

At the bottom of the post is a macro that scripts nested actions.

Rather than rely on the selection object to get action objects, I've tried to implement a novel, I think, approach: getting a "runtime" AppleScript object from xpath results that can be used as an ordinary action by Editor AppleScript.

Not even in the proof-of-concept phase, this is more experimental ... or just plain ill-advised.

This demo just searches for user-specified types of actions at any nest levels and displays the names of found "runtime actions".

If, by chance, it does not fail utterly, its "runtime" list of actions could be fed into @Nige_S's script to color code nested inside/outside-current-group subroutine actions. All color changes could be made in one pass, requiring only that the macro be selected.

(It may be useful to know that when setting properties, such as color, xml or name of an action with AppleScript, pressing the ⌘+z keys will restore the old values.)

PURPOSE:

Demo how to query nested actions in a macro with xpath and use results as "runtime actions" .

ACTION:

Displays the names of types (specified by the user) of actions at all levels as "runtime actions" in the currently selected macro.

USAGE:

  1. Enter the type of actions to display in the value field of the "localMacroActionTypes" variable.

  2.    Each action type must be on a separate line.
    
  3.    Select a macro that contains the specified types in the Editor. Run the macro by the trigger of your choice.
    
Configuration Screenshot

Select any action in the Editor, click the gear icon :gear:, select Copy As XML, copy the value of the "MacroActionType" key and paste it into the value field of the "localMacroActionTypes" variable. Each type must be on a separate line. The default type is "SetVariableToText". The macro will do nothing if the specified MacroActionType is invalid.

Tested on Mojave running KM Version 11.0.3

Implementation Overview
  1. Calculate a list of AppleScript action paths from the ancestors of an xpath found "action nodes" (NSXMLNode).
  2. Convert the list of paths to a list of references with "run script()" and dereference the list of references by rapping and unwrapping each reference "as list" to get a list of AppleScript action objects.

Editor AppleScript can then use the "runtime actions" as ordinary actions to get/set action property values:

tell application id "com.stairways.keyboardmaestro.editor"
set anActionName to name of action id "16957523" of macro id "3E27DB15-15B0-4E8E-BE6B-FA1460FB884D"
set name of action id "16957523" of macro id "3E27DB15-15B0-4E8E-BE6B-FA1460FB884D" to "01)" & anActionName
end tell

Thanks and apologies in advance to those patient enough to read this far much less try the macro.

Test Runtime Actions Macros.kmmacros (23.8 KB)

You can also do it with simple text matching -- and I thought I was onto a winner with that until I tried to target actions inside actions ("Group", "If... Then", etc), at which point a simple action id 1234567 of macro id 7654321 won't do it.

But I have a plan, involving a whose clause...

Edit to add: Nope -- whose only goes one level deep too. Back to the drawing board which, at the moment, contains just the word "Recursion"...

1 Like

Hello Folks :wave:

For acting recursively on KM Actions there is a script written by Kevin Funderberg on his GitHub - if I remember it correctly.

Hopefully he’s still sharing it.

Since I don’t have any of my Macs with me currently and in case the Repo doesn’t exist anymore I’ll call my friend Taj (@tiffle) to help out here since he’s one of the persons who posted about this a few times.

Greetings from Germany :de:

Tobias

1 Like

Thanks for the distraction Tobias (@Nr.5-need_input ) :smile:

You can find the above-mentioned AppleScript code here.

A while back I wrote something similar for a private project and then some years later discovered this which happens to be freely available. I've used its recursive behaviour in modified form for a number of KM projects because it runs a lot quicker than the code I produced originally!

I leave it as an exercise for the reader to understand how it works/can be modified :thinking:

1 Like

Darn, I was getting excited!

Is this the post you were thinking of?

Oh haha, just saw you posted already :).

Thanks for sharing.

This AppleScript is pretty darn close with coloring some of the parent groups red. Thanks for the post and getting us closer to the results.

Color Red Outside or Green if Inside Macro Group
-- Recursive Macro Action Colorizer
-- This script finds and colors all ExecuteMacro and ExecuteSubroutine actions
-- in a selected macro, excluding container actions like If/Group

global allActions
global execActions

tell application "Keyboard Maestro"
  set theMacroID to item 1 of (get selectedMacros)
  set macroGroup to macro group of macro id theMacroID
  
  set allActions to {}
  set execActions to {}
  
  my getAllActions(actions of macro id theMacroID)
  
  repeat with eachAction in execActions
    set actionXML to xml of eachAction
    if actionXML contains ">ExecuteMacro<" or actionXML contains ">ExecuteSubroutine<" then
      tell application "Keyboard Maestro Engine"
        set subUUID to search actionXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
      end tell
      
      if length of subUUID is 36 then
        try
          set subMacroName to name of macro id subUUID
          if actionXML contains ">ExecuteMacro<" then
            set name of eachAction to "Execute: " & subMacroName
          else
            set name of eachAction to "Subroutine: " & subMacroName
          end if
          
          if macro group of macro id subUUID = macroGroup then
            set color of eachAction to "Green"
          else
            set color of eachAction to "Red"
          end if
          
        on error
          set color of eachAction to "Orange"
          set name of eachAction to "ERROR: Invalid Reference"
        end try
      end if
    end if
  end repeat
  
  display dialog "Completed! Colored " & (count of execActions) & " Execute Macro/Subroutine actions." buttons {"OK"} default button "OK"
end tell

-- Recursive function to get only valid Execute actions (skipping containers)
on getAllActions(actionList)
  tell application "Keyboard Maestro"
    if class of actionList is list or class of actionList is action list then
      repeat with act in actionList
        my getAllActions(act)
      end repeat
      
    else if class of actionList is action then
      -- Skip container-type actions (like Group, If, Switch, etc.)
      set actionXML to xml of actionList
      if actionXML does not contain "<Actions>" then
        -- Not a container, check if it's an execute action
        if actionXML contains ">ExecuteMacro<" or actionXML contains ">ExecuteSubroutine<" then
          set end of execActions to actionList
        end if
      end if
      
      -- Recurse into child actions, if any (Group, If, Switch, etc.)
      try
        if (count of actionList's actions) > 0 then
          my getAllActions(actionList's actions)
        end if
      end try
      try
        if (count of actionList's case entries) > 0 then
          my getAllActions(actionList's case entries)
        end if
      end try
      try
        if actionList's thenactions ≠ missing value then
          my getAllActions(actionList's thenactions's actions)
        end if
      end try
      try
        if actionList's elseactions ≠ missing value then
          my getAllActions(actionList's elseactions's actions)
        end if
      end try
    end if
  end tell
end getAllActions

It is nice to have a second option in extending scripting actions to lists of nested actions.

It returns a nice, complete list of resolved action objects at any level, free of verbose, arcane AppleScriptObjC.

In this case, filtering on type of action is done on all actions manually with regex, after the actions are output--unless the handler is modified for each type of filter.

AppleScript

https://github.com/kevin-funderburg/AppleScripts/blob/master/Keyboard-Maestro/Recursively-Get-Every-Action.applescript
https://forum.keyboardmaestro.com/t/feature-request-to-include-the-macro-group-of-the-target-macro-besides-its-name-in-the-execute-macro-macro/10885/15

global allActions
set allActions to {}
tell application "Keyboard Maestro"
	set m to first macro whose selected is true
	set end of allActions to my getAllActions(m's actions)
end tell

tell application "Keyboard Maestro"
	set theMacroID to item 1 of (get selectedMacros)
	set macroGroup to macro group of macro id theMacroID
	repeat with eachAction in allActions
		set theXML to the xml of eachAction
		if theXML contains ">ExecuteMacro<" then
			tell application "Keyboard Maestro Engine"
				set subUUID to search theXML for "(?s).*([:xdigit:]{8}(-[:xdigit:]{4}){3}-[:xdigit:]{12}).*" replace "$1" with regex
			end tell
			if macro group of macro id subUUID = macroGroup then
				set color of eachAction to "Green"
			else
				set color of eachAction to "Red"
			end if
		end if
	end repeat
end tell
on getAllActions(actionList)
	local actionList
	tell application "Keyboard Maestro"
		get class of actionList
		if (class of actionList = list or ¬
			class of actionList = action list) and ¬
			(count of items of actionList) > 0 then
			
			repeat with act in actionList
				my getAllActions(act)
			end repeat
			
		else if class of actionList = case entry then
			
			if (count of actionList's actions) > 0 then
				my getAllActions(actionList's actions)
			end if
			
		else if class of actionList = action then
			
			--set end of allActions to actionList
			set end of allActions to a reference to contents of actionList
			
			-- groups
			try
				if (count of actionList's actions) > 0 then
					my getAllActions(actionList's actions)
				end if
			end try
			-- switch statements
			try
				if (count of actionList's case entries) > 0 then
					my getAllActions(actionList's case entries)
				end if
			end try
			--if then actions
			try
				if actionList's thenactions ≠ missing value then
					my getAllActions(actionList's thenactions's actions)
				end if
			end try
			-- if else actions
			try
				if actionList's elseactions ≠ missing value then
					my getAllActions(actionList's elseactions's actions)
				end if
			end try
			
		end if
		
	end tell
end getAllActions

Filtering

I would note that XPath input offers structurally filtered action nodes before paths are calculated.

Action paths are already only ExecuteMacro actions, so once resolved, the above code can be used without the: if theXML contains ">ExecuteMacro<". The in/out group filter could also be perform upstream.

Using regex to filter on a single xml key or even multiple keys is doable: MacroActionType, option values:MacroUID, Path etcetera.

Filtering on combinations of keys, values and values of properties gets trickier.

FWIW, the handler using xpath is about 2x as fast on exceptionally large macros or groups of macros.

Beyond filtering

The recursion handler requires an AppleScript macro object.

Input for NSXMLDocument is xml string.
It can from a macro object or clipboard or stored plist.
(Yes, the handler will process action nodes from xml loaded directly from the Keyboard Maestro Macros.plist)

This means getting filtered actions from multiple macros's xml.

Beyond read-only:

Structured xml editing is also a plus for anyone trying to edit action options programmatically.

Paths don't have to come as string; they as a key on an edited action's xml, which is easily removed when no longer needed.

FWIW, NSXMLDocument will ensure proper encoding for xml.

I didn't expect too much interest in this handler and may be the only one who ends up using it.

I wouldn't be the first time--and probably not the last. :sunglasses:

For me this fails and doesn't quite get as close as the one I posted above. It also colors parent groups like mine does but then misses subroutine actions.

Hi Skillet,

Gosh, I am really not good at reading posts.

Sorry, I missed your upload completely. :grimacing:

Nice work!

I will certainy take a closer look at it and see what's wrong with my version, but won't be able to get to it right away.

:blush:

No you did just fine, I was updating it and I think we posted near the same time. Thank you for your posts here and the insights and work you have done.

Also my post wasn't meant to be criticle above, and thank you for not taking it that way. There are so many moving parts it is hard to find them all and have the time to digest everything so thank you again.

1 Like

OK, here's my effort. It could do with cleaning up, but it works with everything I've tried it on (which probably means I just haven't tested it enough!).

Unlike the linked-to script this only processes actions that are, or groups that contain, "Execute a Subroutine" or "Execute a Macro" actions which should make it faster than getting then checking every single action in a macro.

tell application "Keyboard Maestro"
	set theMacro to item 1 of (get selectedMacros)
	set groupID to id of macro group of macro id theMacro
	repeat with eachAction in (get every action of macro id theMacro whose xml contains ">ExecuteSubroutine<" or xml contains ">ExecuteMacro<")
		processAction(eachAction, groupID) of me
	end repeat
end tell

on processAction(theAction, groupID)
	tell application "Keyboard Maestro"
		if class of theAction is action or class of theAction is case entry or length of theAction > 0 then
			repeat with eachAction in theAction
				set theXML to xml of eachAction
				if theXML contains "
	<string>ExecuteSubroutine<" or theXML contains "
	<string>ExecuteMacro<" then
					tell application "Keyboard Maestro Engine"
						set theUUID to search theXML for "(?s).*>([:xdigit:]{8}(?:-[:xdigit:]{4}){3}-[:xdigit:]{12})<.*" replace "$1" with regex
					end tell
					if macro group of macro id theUUID = groupID then
						set color of eachAction to "Green"
					else
						set color of eachAction to "Red"
					end if
				else
					if theXML contains ">ExecuteSubroutine<" or theXML contains ">ExecuteMacro<" then
						set theActionList to {}
						if actions of eachAction is not missing value then
							repeat with eachAct in (get actions of eachAction)
								copy contents of eachAct to end of theActionList
							end repeat
						end if
						if thenactions of eachAction is not missing value then
							repeat with eachAct in (get actions of thenactions of eachAction)
								copy contents of eachAct to end of theActionList
							end repeat
						end if
						if elseactions of eachAction is not missing value then
							repeat with eachAct in (get actions of elseactions of eachAction)
								copy contents of eachAct to end of theActionList
							end repeat
						end if
						if tryactions of eachAction is not missing value then
							repeat with eachAct in (get actions of tryactions of eachAction)
								copy contents of eachAct to end of theActionList
							end repeat
						end if
						if catchactions of eachAction is not missing value then
							repeat with eachAct in (get actions of cacthactions of eachAction)
								copy contents of eachAct to end of theActionList
							end repeat
						end if
						if case entries of eachAction is not missing value then
							repeat with eachCase in (get case entries of eachAction)
								repeat with eachAct in (get actions of eachCase)
									copy contents of eachAct to end of theActionList
								end repeat
							end repeat
						end if
					end if
					repeat with eachAction in theActionList
						processAction(eachAction, groupID) of me
					end repeat
				end if
			end repeat
		end if
	end tell
end processAction
1 Like