Run AppleScript With Specified Parameters

This looks brilliant, @JMichaelTX! Looking forward to the finished (or beta-rated) product.

P.S.

What if you want a parameter that starts with "LIST:" ?

I don't see a problem. A parameter can be anything you want. "LIST:" is only used to convert a parameter line from text into a list.

If you want "LIST:" to be one of the items, then you would use:
LIST: LIST:,TEXT:,OBJECT: etc.

Otherwise, "LIST:" appearing at the beginning of a parameter line has no effect, and is included as part of the parameter.

Make sense?

I think it is almost ready. Just been adding some more error checking. After I sleep on it, and test tomorrow, I will probably publish.

I might be having a slow day (or nightβ€”it's the wee hours here), so my tiredness might be impeding my ability to analyse; I was considering a scenario like this:

Desired Parameter List: { "𝑖", "LIST: Imaginary, Irrational, Algebraic", {2.718, 3.1416}, "LIST: Real, Irrational, Trascendental" }

i.e. four parameters, three of which are unary string values, and one of which (the third parameter) is a list of two numbers (which will parse, of course, into a list of two strings).

The second and third parameters are the issue; inputting the parameters like this:

𝑖
LIST: Imaginary, Irrational, Algebraic
LIST: 2.718, 3.1416
LIST: Real, Irrational, Trascendental

would (if I'm doing this correctly) yield { "𝑖", {"Imaginary", "Irrational", "Algebraic"}, {"2.718", "3.1416"}, ("Real", "Irrational", "Transcendental"} }.

Obviously, this is a very minor point and artificially constructed beyond the likely real world scenarios. But I'm intrigued.

So, if you are saying that you do NOT want this parameter converted to a list, but you do want to start the parameter with "LIST:", then yes that would be a problem.
Of course, this parameter would also be converted into a list using your method, just because it contains commas.

I suppose I could implement an escape method, so you you could enter:
\LIST: Imaginary, Irrational, Algebraic

and the script would NOT convert to a list, but would remove the backslash.
If you want to retain \LIST then you would need to enter two backslashes:
\\LIST: Imaginary, Irrational, Algebraic

So the script would need to check for this RegEx:
\\+LIST:
and then remove the first backslash.

How does that look? Think it will work? Or do you have a better suggestion?

@CJK, OK, this seems to work. Here's my test script.
Let me know if you can break it, or see any flaws.


set testParams to "first line
\\LIST: a,b,c
LIST: x,y,z"

set myList to paragraphs of testParams
my splitItems(myList, ",")

on splitItems(pList, pDelim)
  set oldTID to AppleScript's text item delimiters
  set AppleScript's text item delimiters to pDelim
  
  repeat with oItem in pList
    if (oItem starts with "LIST:") then
      set contents of oItem to (text items of LTrim(text 6 thru -1 of oItem))
    else if (my isFoundKME("^\\\\+LIST:", oItem)) then
      set contents of oItem to (text 2 thru -1 of oItem)
    end if
  end repeat
  
  set AppleScript's text item delimiters to oldTID
  return -- original List was updated
  
end splitItems


on isFoundKME(pRegEx, pString)
  tell application "Keyboard Maestro Engine"
    set isFound to found in pString for pRegEx with regex
  end tell
  
  return isFound
  
end isFoundKME


on LTrim(pString)
  set len to length of pString
  repeat with i from 1 to len
    set iChar to character i of pString
    if (iChar β‰  " " and iChar β‰  tab) then exit repeat
  end repeat
  set trimedStr to text i thru -1 of pString
end LTrim

Example Results

image

@CJK, as long as we are in enhancement/debug mode, I went ahead and added code to convert a text number to an actual number. After crashing SD7 a few times (my bad), I finally arrived at this script.

Would appreciate it if you have time, to take a look and test it.


set testParams to "first line
\\LIST: a,b,c
LIST: 1,X,3
123.45"

set myList to paragraphs of testParams
my splitItems(myList, ",")

on splitItems(pList, pDelim)
  set oldTID to AppleScript's text item delimiters
  set AppleScript's text item delimiters to pDelim
  
  repeat with oItem in pList
    if (oItem starts with "LIST:") then
      set contents of oItem to (text items of LTrim(text 6 thru -1 of oItem))
      repeat with oSubItem in oItem
        set contents of oSubItem to my textToNum(contents of oSubItem)
      end repeat
    else if (my isFoundKME("^\\\\+LIST:", oItem)) then
      set contents of oItem to (text 2 thru -1 of oItem)
    else
      set contents of oItem to my textToNum(contents of oItem)
    end if
  end repeat
  
  set AppleScript's text item delimiters to oldTID
  return -- original List was updated
  
end splitItems


on isFoundKME(pRegEx, pString)
  tell application "Keyboard Maestro Engine"
    set isFound to found in pString for pRegEx with regex
  end tell
  
  return isFound
  
end isFoundKME


on LTrim(pString)
  set len to length of pString
  repeat with i from 1 to len
    set iChar to character i of pString
    if (iChar β‰  " " and iChar β‰  tab) then exit repeat
  end repeat
  set trimedStr to text i thru -1 of pString
end LTrim

on textToNum(pString)
  set numOrText to pString
  try
    set numOrText to numOrText as number
  on error errMsg number errNum
    set x to numOrText
  end try
  return numOrText
end textToNum

Example Results

image

Thanks.

BTW, all of this is a bit off-topic from your OP. Would you like me to move it to a new topic?

I don't see this as off-topic at all. It certainly seems like you're implementing some great improvements/enhancements to my original action.

Only if the inline delimiter is set to the comma. To avoid parameterising the comma-delimited list in my action as it currently stands, one would change the inline delimiter to something other than comma.

The self-destruct scenario in my set up is where one has an exhaustive list of parameters that collectively make use of all the available inline delimiter character options, but without the desire to have any inline parameterisation occur.

I'll do some testing of your scripts and get back to you. Having skimmed them, it would make more sense to me if \LIST: were the keyword that parameterises a comma-delimited list, whilst LIST: remained insignificant. It would be more conventional and more intuitive that way round, as it actively then tokenises an ordinary word and imbues it with a function in the mind of the user. It would also have the benefit of negating the need to test twice to determine whether the parameter string starts with "LIST:" or starts with "\LIST:", as the first would be inconsequential, and the latter would be the token of interest. If you make \ a global escape character (i.e. applied to the whole string rather than only when LIST: is involved), it too would seem to me to make more cohesive sense, and also make the programming easier as you can simply do a global replacement that substitutes a single \ for every two, negating the need for a regular expression match.

I'll let you ponder on that and you can decide to go with what you feel is best.

This is looking really good.

I've had a few hours to overlook your script, and also saw the related posts on the Late Night Software (Script Debugger) forum.

I wonder if there might be more suited to this debugging-and-enhancing stage of the AppleScript, as there seem to be some very active input from other AppleScripters, and Shane Stanley always has great input.

But, for now, I'll post my current findings and thoughts here.

You and I have quite different scripting styles when it comes to AppleScript, although I'm aware I have unorthodox preferences with my use of syntax etc. But it does make it interesting reading other people's style of coding.

The actual implementation looks all totally fine to me, with the slight exception of your LTrim() handler, which felt a bit inefficient. Alone, this was simply more of an irk than a reason to do anything about it, but it also has two fail case scenarios:

β‘  LTrim(" ") returns " " where the more appropriate return value would be ""
β‘‘ LTrim("") returns an error

Whilst I'm listing debugging issues, I'll just note another two that cropped up elsewhere in the script:

β‘’ A parameter value of "LIST:" (without the quotes) returns an error where the more appropraite return value would be an empty list {}
β‘£ Attempting to use the LIST: token/function with leading white space isn't catered for. Therefore, a parameter value of " LIST: 1,x,3" returns itself as a unary string value parameter, where my personal feeling is that it ought to parameterise the list
β‘€ textToNum() returns 0 for the empty string where the appropriate return value would be the empty string

Your regex implementation to remove one backslash from a sequence preceding "\LIST:" works really nicely. It made me re-consider my earlier suggestion of globalising the backslash as an escape character for the entire parameter text, hence the strikethrough text in my previous post.

I do still consider it sensible to use the backslash as a tokenising character rather than an escaping character, i.e. \LIST: being the keyword, and not LIST:.
[ As a side-note, you might consider using some sort of enclosing token that resembles a function call, such as \LIST:[...] or \LIST{...} or perhaps even just \[...] or \{...}, because this will enable you to further expand your efforts in the future should you decide you want to be able to parse nested lists of parameters. ]

One thing I really like is how you update the contents of the original list passed to splitItems. I've never thought of doing this, but I think it's pretty slick.

Now I'm going to offer some small code adjustments, which I hope you feel might be improvements, but I'll leave you to decide what to keep and what to bin. Firstly, the LTrim() handler that has two exceptional fail cases that warranted a rewrite to also improve efficiency (or, so I think, but I haven't conducted speed tests). I have two versions, each with merits and drawbacks (forgive me for renaming variables and such, but I drafted them from scratch):

on Ltrim(s)
	set my text item delimiters to {null, space, tab}
	
	set t to the text items of s as text
	if t is "" then return ""
	set n to the offset of (text 1 of t) in s
	text n thru -1 of s
end Ltrim

The good: 1) handles a single space or an empty string appropriately; 2) bypasses the need for a repeat loop by utilising the builtin offset function; 3) reads more user-friendly.

The bad: 1) utilises text item delimiters, which potentially inteferes with other parts of the script [ You and I have different coding practises here: you always reset your TIDs after using them; I, however, never do this, as I elect to always set them directly prior to using them. Thus, you might want to adjust this handler to reset them before the handler exits, whereas my method was to adjust splitItems() to place its TID call immediately before text items of... line) ]; 2) has a fail case scenario when the string passed to it starts with any leading white space followed by "null" as its first non-whitespace substring (I have a fix for this if necessary).

Here's the second variation of the handler:

on Ltrim(s)
	set s to {} & id of s
	repeat while s β‰  {} and item 1 of s is in [9, 32]
		set s to rest of s
	end repeat
	
	character id s
end Ltrim

The good: 1) handles " " and "" appropriately; 2) utilises list manipulation rather than string manipulation, meaning it can combine progression and trimming in a single operation; 3) no fail cases identified so far; 4) reads easier.

The bad: Nothing glaring.

I think the benefits of using list manipulation over text manipulation are debatable, but perhaps you can conduct a speed test on the two (if you like, but don't bother if you don't want to. I've never done a speed test before, so perhaps I can make this my first when I have a clear plate).

Finally, I think the idea of coercing classes to their appropriate/likely intended type is a brilliant one. I wish I had thought of that when I'd made the action, as you've demonstrated it's very easy to implement. One slight issue I mentioned above is its false-positive coercion of the "" into 0. One solution is obvious:

on textToNum(s)
	if s is "" then return ""
	
	try
		s as number
	on error
		s
	end try
end textToNum

Shane Stanley on the LNF forum offered a lovely streamlined piece of code:

on textToNum(pString)
	try
		if "0123456789-." contains character 1 of pString then
			set pString to pString as number
		end if
	end try
	return pString
end textToNum

However, this doesn't coerce numeric strings that have leading white space. A call to the Ltrim() handler might be one workaround, but it then negates the efficiency benefits his offering promised.

Some on the LNF forum also offered an ObjC flavour option, but his chosen implementation and his regex pattern leave it wide open for many fail cases. And, I don't know what your thoughts are on pulling in ObjC to do these minor tasks, even though for larger tasks, it is clearly much faster. If anything, for this KM action, my inclination (as you may have gleaned) is to use shell scripts more freely than I would typically, simply because it was necessary to retrieve the $KMPARAM_ variables this way in order to preserve non-traditional characters, including international ones and technical symbols often found in the %TriggerValue% KM token. Plus, bash functions are usually pretty fast compared to AppleScript, though there's an overhead in calling a shell process.

But, as a flavour, one why to trim excess white space from either side of a string and from in between characters is:

do shell script "echo " & quoted form of s & " | xargs"

In fact, thinking about it, I suspect the bulk of the parameter parsing could be done with shell script quite efficiently. I'll have a think.

That's it for now. Thanks so much for working on this. You're clearly doing a great job and spinning out some really neat ideas.

If you wish to post a specific question at LNS, please feel free to do so.
The script I posted there was just to report a SD7 bug/crash, not go get help with the script itself.
I generally don't ask for help there unless I can't resolve the problem, or feel like there must be a better solution. As you may have noted, I did post a question about converting text to numbers.

No problem. I am always willing to learn new things. But then I may or may not adopt them to my core style. :wink:

Thanks for doing more exhaustive testing than I did.
I did review your solutions, but found that with just a minor mod to my script the bugs were fixed. IMO, I don't see any material inefficiencies in this script:

LTrim

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on LTrim(pString)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 1.1    2018-06-17
    PURPOSE:  Remove all Spaces & Tabs From Beginning of String
    PARAMETERS:
      β€’ pString    | text |  Source string

    RETURNS:  | text |  String with No Spaces/Tabs at Beginning
    AUTHOR:  JMichaelTX
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  local trimmedStr, i, iChar
  
  set trimmedStr to ""
  set len to length of pString
  
  if (len > 0) then
    repeat with i from 1 to len
      set iChar to character i of pString
      if (iChar β‰  " " and iChar β‰  tab) then
        set trimmedStr to (text i thru -1 of pString)
        exit repeat
      end if
    end repeat
  end if
  
  return trimmedStr
  
end LTrim
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I think we are on the same page here.
As I looked at this, it occurred to me that if the parameter is empty, then we don't know the intended class. So I decided on missing value. Of course, if you prefer, you can just change this to ""

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on textToNum(pString)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 1.1    2018-06-17
    PURPOSE:  
    PARAMETERS:
      β€’ pString    | text |  Source String

    RETURNS:  | number or text or missing value |  
      β€’ Number IF string can be converted;
      β€’ Else source string unchanged.
      β€’ missing value IF empty pString
      
    AUTHOR:  JMichaelTX
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  if (length of pString > 0) then
    try
      set numOrText to pString as number
    on error -- errMsg number errNum
      set numOrText to pString
    end try
  else -- IF pString is empty
    set numOrText to missing value
  end if
  
  return numOrText
end textToNum
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Well, you are the one with the original idea and implementation. So thanks go to you.

Here's my complete updated test script. As always, feel free to test and critique.

Test Script with updates


set testParams to "first line
\\LIST: a,b,c
LIST: 1,X,, ,   ,-3.14
123.45"

set myList to paragraphs of testParams
my splitItems(myList, ",")
set trimEmpty to my LTrim("")
set trimSpace to my LTrim(" ")
set trimNeedsTrim to my LTrim(" Needs Trim")
set trimNoneNeeded to my LTrim("No Trim Needed")

set myNum to my textToNum("   3.14159")

--~~~~~~~~~~~~~~~~~~~~~~~~ HANDLERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on splitItems(pList, pDelim)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 2.0    2018-06-16
    PURPOSE: (1) Split Each Item in List into a Sub-List where requested
                (2) Convert Numeric Text Itens to Actual Numbers where possible
                
    PARAMETERS:
      β€’ pList    | list |  Source list of text items
      β€’ pDelim    | text   |  Delimiter to be Used for split

    RETURNS:  Nothing.  The Original List in the Parameters is updated with changes.

    AUTHOR:  JMichaelTX
  ## REQUIRES:  Keyboard Maestro Engine for RegEx
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  
  set oldTID to AppleScript's text item delimiters
  set AppleScript's text item delimiters to pDelim
  
  repeat with oItem in pList
    
    --- IF Item starts with "LIST:", then split text into sub-list ---
    if (oItem starts with "LIST:") then
      set contents of oItem to (text items of LTrim(text 6 thru -1 of oItem))
      
      --- Convert Text Items to Numbers if Possible ---
      repeat with oSubItem in oItem
        set contents of oSubItem to my textToNum(contents of oSubItem)
      end repeat
      
      --- IF Item Start with one or more Backslashses (\) & "LIST:", remove first \ ---
    else if (my isFoundKME("^\\\\+LIST:", oItem)) then
      set contents of oItem to (text 2 thru -1 of oItem)
      
      --- Convert Text Item to Number if Possible ---
    else
      set contents of oItem to my textToNum(contents of oItem)
    end if
  end repeat
  
  set AppleScript's text item delimiters to oldTID
  return -- original List was updated
  
end splitItems
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~


--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on isFoundKME(pRegEx, pString)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 1.0    2018-06-16
    PURPOSE:  Determine if RegEx is Matched in String
    PARAMETERS:
      β€’ pRegEx    | text |  Regular Expression
      β€’ pString    | text |  Source string to search

    RETURNS:  true if match is found
    AUTHOR:  JMichaelTX
  ## REQUIRES:  Keyboard Maestro Engine
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  tell application "Keyboard Maestro Engine"
    set isFound to found in pString for pRegEx with regex
  end tell
  
  return isFound
  
end isFoundKME
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~


--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on LTrim(pString)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 1.1    2018-06-17
    PURPOSE:  Remove all Spaces & Tabs From Beginning of String
    PARAMETERS:
      β€’ pString    | text |  Source string

    RETURNS:  | text |  String with No Spaces/Tabs at Beginning
    AUTHOR:  JMichaelTX
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  local trimmedStr, i, iChar
  
  set trimmedStr to ""
  set len to length of pString
  
  if (len > 0) then
    repeat with i from 1 to len
      set iChar to character i of pString
      if (iChar β‰  " " and iChar β‰  tab) then
        set trimmedStr to (text i thru -1 of pString)
        exit repeat
      end if
    end repeat
  end if
  
  return trimmedStr
  
end LTrim
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~


--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on textToNum(pString)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 1.1    2018-06-17
    PURPOSE:  
    PARAMETERS:
      β€’ pString    | text |  Source String

    RETURNS:  | number or text or missing value |  
      β€’ Number IF string can be converted;
      β€’ Else source string unchanged.
      β€’ missing value IF empty pString
      
    AUTHOR:  JMichaelTX
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  if (length of pString > 0) then
    try
      set numOrText to pString as number
    on error -- errMsg number errNum
      set numOrText to pString
    end try
  else -- IF pString is empty
    set numOrText to missing value
  end if
  
  return numOrText
end textToNum
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example Results

image

Agreed. sorry I missed this in my reply above.
So here's the updated handler:

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on splitItems(pList, pDelim)
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  (*  VER: 2.1    2018-06-17
    PURPOSE: (1) Split Each Item in List into a Sub-List where requested
                (2) Convert Numeric Text Itens to Actual Numbers where possible
                
    PARAMETERS:
      β€’ pList    | list |  Source list of text items
      β€’ pDelim    | text   |  Delimiter to be Used for split

    RETURNS:  Nothing.  The Original List in the Parameters is updated with changes.

    AUTHOR:  JMichaelTX
  ## REQUIRES:  Keyboard Maestro Engine for RegEx
  --–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
  *)
  
  set oldTID to AppleScript's text item delimiters
  set AppleScript's text item delimiters to pDelim
  
  repeat with oItem in pList
    
    --- IF Item starts with "LIST:", then split text into sub-list ---
    if (oItem starts with "LIST:") then
      if (length of oItem > 5) then
        
        set contents of oItem to (text items of LTrim(text 6 thru -1 of oItem))
        
        --- Convert Text Items to Numbers if Possible ---
        repeat with oSubItem in oItem
          set contents of oSubItem to my textToNum(contents of oSubItem)
        end repeat
        
      else -- length ≀ 5
        set contents of oItem to {}
      end if
      
      --- IF Item Start with one or more Backslashses (\) & "LIST:", remove first \ ---
    else if (my isFoundKME("^\\\\+LIST:", oItem)) then
      set contents of oItem to (text 2 thru -1 of oItem)
      
      --- Convert Text Item to Number if Possible ---
    else
      set contents of oItem to my textToNum(contents of oItem)
    end if
  end repeat
  
  set AppleScript's text item delimiters to oldTID
  return -- original List was updated
  
end splitItems
--~~~~~~~~~~~~~~~~~~~~ END of Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Debatable. But for now I'll have to disagree. The user has rules which must be followed (Parameter MUST START with "LIST:" ...), and I don't want to complicate the code to handle this edge case. It would just be a convenience, not loss of feature.

Sensible, but still just a design call. Since I already have it working as it, I think I'll just leave it as plain "LIST:" to invoke the list function. I think this is also easier for most users. Again, I think even using the "LIST:" function is an edge case, so it is not worth complicating the code, IMO.

OK, I think I now have covered all of your points. If I missed any, please feel free to reply.

Hi @JMichaelTX, what's the latest regarding your fork of this plug-in ? Did you carpark it whilst you deal with other projects ?

In the interim, I've created a second version of my plug-in, that implements some added functionality inspired by your ideas. Specifically, it tries to identify the most likely AppleScript type class from a string value and coerce or substitute the appropriate AppleScript object type in its place.

Do you mind if I upload my updated plug-in here, whilst crediting you for the idea ? Or would you like me to send you a copy privately so you can review it and decide if there's a potential conflict of interest ?

Or am I over-thinking this ?

Lastly, if I do upload my version 2.0, should I edit the original post and overwrite that file with the update; or should I post it as a reply to this post; or create a new post ?

Yep, just been busy with other stuff.
I've pretty much finished the plugin, I just need to write the documentation.

Feel free to go ahead and update your plugin. I don't need to review it, unless you specifically want me to. I don't see any conflict of interest.

When I get my plugin ready to publish, I will post to a new topic, with a credit/link to your plugin.

I just made a post about this:

Credit to @JMichaelTX whose inspired idea it was to type class the parameters.

1 Like

Run AppleScript With Specified Parameters.zip (50.3 KB) [ UPDATED: 2018-09-02 ]


Changes

Fixed an issue where paths to the script file were incorrectly processed if they contained characters that have special meaning in bash without first being properly escaped.

Under the hood, some code refactoring has, in theory, made the script faster to execute, whilst also shortening the script syntactically.

1 Like

@CJK, do you know if it would be possible to create a similar plugin, but for JXA scripts instead of AppleScripts?

I just did a quick test and I don't see any reason why it wouldn't be possible. I created a small JXA script:

function run(input) {
	var app=Application.currentApplication()
	app.includeStandardAdditions = true
	return app.displayNotification(input[0], {withTitle: input[1]})
}

Then I ran it from an AppleScript:

set message to "Success!"
set title to "Hello, World"

run script "/Users/CK/Documents/Scripts/AppleScript/scripts/jxa.scpt" with parameters {message, title} in "JavaScript"

The script executed correctly:

The only issue, which as far as I can tell is a minor one, is that the script returns an error -4960, about which there appears to be no information online:

This can be handled easily enough:

try
	run script "/Users/CK/Documents/Scripts/AppleScript/scripts/jxa.scpt" with parameters {message, title} in "JavaScript"
on error E number N
	if N = -4960 then return
end try

However, I can't find a way as yet to obtain a return value from the JXA script through conventional means.

In fact, given this, my plugin ought to work for JXA scripts too:

JXA.scpt is the same JavaScript script as above, and it executed correctly:

However, as I stated, there's no return value (although there's also no error -4960 from Keyboard Maestro).

Finally, I also just tested a quick mirror-version of my plugin where main.scpt (the script that runs within the plugin itself, behind the scenes) is written in JXA rather than AppleScript. Early signs are encouraging, meaning you can create the entire plugin using only JXA, without having to rely on AppleScript at all.

@JMichaelTX, apologies for the third message relating to this query, but upon further experimentation, it appears that using osascript to execute the JXA script is successful, without error -4960, and provides a return value.

Therefore, the plugin could make use of do shell script to execute the JXA with parameters, however this would, of course, mean a shell process is running for the duration of the script, which is less than ideal.

However, I suspect AppleScriptObjC's OSAKit would probably be the ultimate solution. My next iteration of this plugin was intending to convert the entire thing to ASObjC anyway, so I will make sure to test it with JXA scripts too.

No apologies are needed. Many thanks for all of your experimentation.
I agree with you that it seems like JXA should also work in this fashion.
Unfortunately, I'm consumed in a major project, so I've no time to experiment, for now.
But, "I'll be back". :wink:

Great tip. Made the macro availiableKeyboard Maestro Plugin Update.kmmacros (2.0 KB)