JUMP to called Subroutine or Macro (or toggle back)

When an Execute Subroutine, Execute Macro, or Open URL to macro action is selected,
this macro will jump to the called subroutine / macro.

If the hotkey is used a second time, it will jump back to the calling action.


See, for example, the request in the Jump to Subroutine thread.


JUMP to called Subroutine Macro or Group (or toggle back).kmmacros (13 KB)

4 Likes

This is incredibly helpful. Thank you so much, @ComplexPoint! :blush:

I've modified the JXA code so it now works with both "Execute Subroutine" and "Execute Macro":

Go To Subroutine/Macro selected
ObjC.import("AppKit");

// Edit the Keyboard Maestro macro or subroutine that is 
// executed by the copied KM Action (if any)

// Rob Trew @2025
// Ver 0.02 - Adjusted to handle both ExecuteSubroutine and ExecuteMacro

// main :: () -> IO Bool
const main = () => {
    const
        pb = $.NSPasteboard.generalPasteboard,
        nsData = pb.dataForType(
            "com.stairways.keyboardmaestro.actionarray"
        );

    return either(
        alert("Jump to Macro or Subroutine")
    )(uuid => {
        const
            km = Application("Keyboard Maestro"),
            macro = km.macros.byId(uuid);

        return macro.exists()
            ? (
                km.editmacro(uuid),
                `Editing macro/subroutine: "${macro.name()}"`
            )
            : `Macro or subroutine not found: "${uuid}"`
    })(
        bindLR(
            nsData.isNil()
                ? Left("No 'Execute Subroutine' or 'ExecuteMacro' action copied to clipboard.")
                : Right(
                    ObjC.deepUnwrap(
                        $.NSPropertyListSerialization
                            .propertyListWithDataOptionsFormatError(
                                nsData, $.NSPropertyListImmutable,
                                null, null
                            )
                    )
                )
        )(kmArray => {
            const n = kmArray.length;

            return bindLR(
                0 < n
                    ? 1 < n
                        ? Left("Select just one 'Execute Subroutine' or 'ExecuteMacro' action.")
                        : Right(kmArray[0])
                    : Left("No KM Actions found in clipboard.")
            )(dict => {
                const
                    actionType = dict.MacroActionType;

                return ("ExecuteSubroutine" === actionType || "ExecuteMacro" === actionType)
                    ? Right(dict.MacroUID)
                    : Left(`Expected an 'Execute Subroutine' or 'ExecuteMacro' action, but saw: "${actionType}"`);
            });
        }
        )
    );
};


// ------------------------- JXA -------------------------

// alert :: String => String -> IO String
const alert = title =>
    s => {
        const sa = Object.assign(
            Application("System Events"), {
            includeStandardAdditions: true
        });

        return (
            sa.activate(),
            sa.displayDialog(s, {
                withTitle: title,
                buttons: ["OK"],
                defaultButton: "OK"
            }),
            s
        );
    };

// ----------------------- GENERIC -----------------------

// Left :: a -> Either a b
const Left = x => ({
    type: "Either",
    Left: x
});


// Right :: b -> Either a b
const Right = x => ({
    type: "Either",
    Right: x
});


// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = lr =>
    // Bind operator for the Either option type.
    // If lr has a Left value then lr unchanged,
    // otherwise the function mf applied to the
    // Right value in lr.
    mf => "Left" in lr
        ? lr
        : mf(lr.Right);


// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
    // Application of the function fl to the
    // contents of any Left value in e, or
    // the application of fr to its Right value.
    fr => e => "Left" in e
        ? fl(e.Left)
        : fr(e.Right);

// MAIN ---
main();
2 Likes

Good thought – updated above.

2 Likes

Nice macro, @ComplexPoint. I also like the addition.

I have two other suggestions:

  1. Add the Open a URL action when the address begins with: keyboardmaestro://m=

  2. Provide an option to move to the target macro (in the current editor) rather than opening a new KM editor window. As you likely know, the editor becomes very sluggish when more than one window is open.

1 Like

Just in case it is unknown, from the Execute a Macro/Subroutine macro selector you can go to the macro directly - not quite as quickly as this macro since you have to select from the popup menu of course.

2 Likes

Much better !

(I was failing to discover that – foolishly looking in the gear-wheel menu,
rather than in the sub-routine selection menu)


and for jumping back from the subroutine to the caller,
I now notice the Callers dropdown to the right of the Returning a value checkbox.


Many thanks for all this thoughtful design, which we seem to be slow to notice :slight_smile:

4 Likes

There is also the History buttons in the title bar.

2 Likes

...and their corresponding menu items and keyboard shortcuts. Last Edited is my personal favorite, especially since simply viewing a macro in edit mode marks it as "edited." This means the shortcut effectively lets you switch quickly between two macros.

1 Like

FWIW now updated, in first post, at top of thread, aiming to:

  1. Include Open URL actions where the address points to a macro (keyboardmaestro://m=),
  2. use the current editor by default (though a disabled action to open an additional window is provided), and
  3. toggle back (from the called subroutine or macro) to the calling action, if the hotkey is tapped a second time.
3 Likes

Great Macro! I've been using it a lot already and these new additions have proven useful. I don't know if it would be possible, but if I could make another suggestion. I have several macros that show a palette for one action and sometimes I find I want to navigate to that macro group. Unlike the execute a macro, or subroutine, actions...when I click on it there is no "Go to Macro" option, or in this case "Go to Group". It would be nice if I could select the action, then run your macro and quickly navigate to the group.

image

In the example above, running your macro would take me to the Macro Group called Finder Palette.

Updated to ver 0.07 above (first post in the thread)

Added jump and return for actions which specify a macro group.

(also made return a little more flexible – selection can be anywhere in a called group, macro, or subroutine)

3 Likes

Thank you! It's working as expected...appreciate you adding this so quickly.

1 Like

FWIW I've since bumped it up to Ver 0.08, simplifying a little to avoid the need for a Copy action.

(Now reads action details through XML, rather than the clipboard).

i.e.

  1. Leaves any existing clipboard contents intact, and
  2. may feel fractionally snappier, though I haven't measured.

And should, I think, now work with any action which references a subroutine, macro or group.
Ver 0.14+

3 Likes