Macro: [GTAP] Go to Action(s) Path

[GTAP] Go to Action(s) Path

PURPOSE

When triggered, this macro (should) reveal in the finder any path starting ~/ or /Users/ in the selected action.

LIMITATIONS

I have only written and tested it today with a handful of actions I use often.

Test Actions (Image)

It may need to be modified for your use. I have not caught every error (for instance, when an action has no path, nothing happens, including no error).

Download:

[GTAP] Go to Action(s) Path.kmmacros (37.7 KB)

Macro Image

Macro Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.
System Information
  • macOS 12.6.5 (21G531)
  • Keyboard Maestro 10.2

Hey Christian,

It's all your fault! You made me spend my afternoon learning the basics of how to parse XML with AppleScriptObjC.

I've only scratched the surface, but it's a start... :sunglasses:

  • This macro only supports single actions directly selected.
  • When I have more time I'll work on recursively extracting any and all paths from all levels of the selected action(s) or macro(s).

-Chris


Download: Extract the Path from the Selected Action and Reveal the File in the Finder v1.00.kmmacros (17 KB)

Macro-Image

Macro-Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.

System Information
  • macOS 10.14.6
  • Keyboard Maestro v10.2

I'm honored! My inelegance has drawn you in :joy: Thanks!

When I run your macro on my system I get this error for

actions with valid paths

ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
ERROR!
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท

Execute an AppleScript failed with script error: text-script:1532:1575: execution error: No Path was Found in the Selected Action! (-2700)

ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท

And this error for

actions with no valid path

ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
ERROR!
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท

Execute an AppleScript failed with script error: text-script:1475:1505: execution error: Finder got an error: Handler canโ€™t handle objects of this class. (-10010)

ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท

Hey Christian,

Thanks for the feedback.

It looks like I'll have to add some more keywords...

It's late here, so I'll probably pick that up again tomorrow. If you feel like fooling with it the relevant line in the script is:

set thePath to (theNSArray's firstObject()'s valueForKey:"ImagePath") as text

You can pick the field names out of the action XML.

-Chris

1 Like

I haven't dug deeply into this, but is the following too much of a blunt instrument?

"For Each... the substrings matching" <string>(:?~/|/Users/)[^<]*, extracting the path with a regex search for <string>(.+) and append it to a variable? Then line-by-line through the variable revealing the items?

It does feel a little simplistic, but you never know!

PoC, in which I've targeted selected macros rather than actions, but the AS could easily be changed -- but I wanted to see if I could extract from multiple macros at once :wink:

Paths POC.kmmacros (4.9 KB)

Image

1 Like

That is much nicer! Could you please show the AppleScript targeting actions as well?

Hey Christian,

  • Here's a complete script that does all the heavy lifting for you.
  • It works with selected actions, macros, or macro-groups.
    • Reveals in the Finder the the files/folders whose paths are found.
--------------------------------------------------------
# Auth: Christopher Stone <scriptmeister@thestoneforge.com>
# dCre: 2023/05/08 03:27
# dMod: 2023/05/08 04:05
# Appl: Keyboard Maestro Editor
# Task: Reveal in the Finder Items Whose Paths Are Listed in the Selected Items {Action(s), Macro(s), or Macro-Group(s)}.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Keyboard_Maestro, @XML, @Selected, @Items, @Reveal, @Finder, @Path, @ASObjC

--------------------------------------------------------
use AppleScript version "2.4" --ยป Yosemite or later
use framework "Foundation"
use scripting additions
--------------------------------------------------------
property LF : linefeed
--------------------------------------------------------

set {oldTIDS, AppleScript's text item delimiters} to {AppleScript's text item delimiters, LF}

tell application "Keyboard Maestro"
   set selectedItemList to selection
   if selectedItemList โ‰  {} then
      repeat with theItem in selectedItemList
         set contents of theItem to xml of theItem -- Extracting XML
      end repeat
   else
      display notification "Nothing was selected in the Keyboard Maestro Editor!" with title "# Path Search #" subtitle "ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท" sound name "Tink"
      return
   end if
end tell

set selectedItemList to selectedItemList as text
set AppleScript's text item delimiters to oldTIDS

# selectedItemList can be used at this point and contains the XML of the selected items.

set foundPathList to my regexFindWithCapture:"(?m)<string>(~?/.+)</string>" fromString:selectedItemList resultTemplate:"$1"
set collatedFurlList to {}

if foundPathList โ‰  {} then
   repeat with thePath in foundPathList
      set theFURL to (((current application's NSString's stringWithString:thePath)'s stringByStandardizingPath) as text) as ยซclass furlยป
      if theFURL is not in collatedFurlList then
         set end of collatedFurlList to theFURL
      end if
   end repeat
   tell application "Finder"
      activate
      repeat with theFURL in collatedFurlList
         reveal theFURL
      end repeat
   end tell
else
   display notification "No Viable Path Strings Were Found!" with title "# Path Search #" subtitle "ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท" sound name "Tink"
end if

--------------------------------------------------------
--ยป HANDLERS
--------------------------------------------------------
on regexFindWithCapture:thePattern fromString:theString resultTemplate:templateStr
   set theString to current application's NSString's stringWithString:theString
   set theRegEx to current application's NSRegularExpression's regularExpressionWithPattern:thePattern options:0 |error|:(missing value)
   set theFinds to theRegEx's matchesInString:theString options:0 range:{0, theString's |length|()}
   set theResult to current application's NSMutableArray's array()
   repeat with aFind in theFinds
      set foundString to (theRegEx's replacementStringForResult:aFind inString:theString |offset|:0 template:templateStr)
      (theResult's addObject:foundString)
   end repeat
   return theResult as list
end regexFindWithCapture:fromString:resultTemplate:
--------------------------------------------------------

Here's the part that extracts the XML if you want to play around with other methods of getting the paths.

--------------------------------------------------------
property LF : linefeed
--------------------------------------------------------

set {oldTIDS, AppleScript's text item delimiters} to {AppleScript's text item delimiters, LF}

tell application "Keyboard Maestro"
   set selectedItemList to selection
   if selectedItemList โ‰  {} then
      repeat with theItem in selectedItemList
         set contents of theItem to xml of theItem -- Extracting XML
      end repeat
   else
      display notification "Nothing was selected in the Keyboard Maestro Editor!" with title "# Path Search #" subtitle "ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท" sound name "Tink"
      return
   end if
end tell

set selectedItemList to selectedItemList as text
set AppleScript's text item delimiters to oldTIDS

return selectedItemList

Note the technique of reusing the selectedItemList in place to extract the XML. This is very fast and efficient.

-Chris

2 Likes

Since JavaScript has built-in regular expression support, I took this as an opportunity to practice using JXA.

  • I've only gotten to the point of extracting the strings and am not showing files in the Finder.
// --------------------------------------------------------
// Auth: Christopher Stone <scriptmeister@thestoneforge.com>
// dCre: 2023/05/08 02:10
// dMod: 2023/05/08 19:47
// Appl: JXA โ‡ข Keyboard Maestro Editor
// Task: Return Selected Items in the Keyboard Maestro Editor.
// Tags: @ccstone, @JXA, @Script, @Selected, @Items, @Keyboard_Maestro_Editor, @Extract, @Path, @Strings
// --------------------------------------------------------

(() => {
   
   "use strict";
   const cApp = Application.currentApplication();
   cApp.includeStandardAdditions = true;
   const itemXML = Application("Keyboard Maestro").selection().map(item => item.xml()).join('\n');
   const pathStrings = itemXML.match(/<string>~?\/.+<\/string>/gm);

   if (pathStrings) {
      return pathStrings.join('\n').replace(/<\/?string>/g, '');
   } else {
      return 'No Path Strings Found!'
   }

})();

// --------------------------------------------------------

I'm pretty pleased with the efficiency of the code.

I'm less pleased at how awkward it is for me to write, but then again that's what practice is for...

:sunglasses:

2 Likes

This is very nice, thank you @ccstone and @Nige_S for demonstrating better ways to do this. I have added an action to reveal them in finder and will upgrade if you wind up addressing that in the script.

I appreciate the help! This will save me a lot of strain.

The first script I posted already does that...

I changed the Task description to make this more clear.

1 Like

It's bit dirty, and could be prone to false positives. Luckily Chris is at hand to tidy up after me :wink:

Nice. JXA (indeed, JS in general) still has me scratching my head -- looking forward to working through that.

1 Like

A funny unexpected side effect, running the macro on this action:

Reveals /System/Library/Sounds/Submarine.aiff in the finder.

1 Like

I noticed that...

It's because Keyboard Maestro defines the sound with a path in the XML of the action:

<key>Path</key>
<string>/System/Library/Sounds/Tink.aiff</string>

This is one of several reasons why I'd rather talk to the XML directly with JavaScript or AppleScriptObjC instead of parsing with RegEx. It makes certain kinds of filtering a whole lot easier.

However โ€“ my understanding of XPath and the tools to utilize it are insufficient to tackle that modality at present โ€“ so I'm stuck with the more painfully brute-force methods of RegEx.

1 Like

Now you've got the path you can go all Dr Who in the Terminal or an "Execute Shell Script" action:

afplay -r 0.05 /System/Library/Sounds/Submarine.aiff

...or even

for i in {1..5}
do
	afplay -r 0.05 /System/Library/Sounds/Submarine.aiff &
	sleep 2
done
1 Like