Run AppleScript With Specified Parameters

Overview

Action: This action, as the name implies, executes an AppleScript allowing the user to pass parameters to the AppleScript's run handler.

About AppleScript Run Handlers

The action essentially executes the following AppleScript statement:

    run script %script_file% with parameters %parameters%

However, it first parses the parameters and ensures they are sent to the script in a useful manner, preserving international characters, and converting an inline-delimited list of parameters to an AppleScript list.

The script to be executed will possess a run handler, which will receive the parameters in one of two ways, depending how the run handler is declared.

The first way is to specify a single variable that can receive an arbitrary input:

    on run args

Here, args (which can be named however one wishes) will receive the parameters as an ordered list. Using the values from the above screen shot, args will contain the following items when the action is triggered by its hotkey and the inline delimiter is set to comma:

    { "Hello World",
      "~/Dropbox/File Requests/Music",
      "βŒƒβŒ₯⌘T",
      {"1", "1", "2", "3", "5", "8", "13"},
      "Anders Jonas Γ…ngstrΓΆm" }

Note that item 4 of args is itself a list, containing 7 items. If the inline delimiter is set to slash, the contents of args would be:

    { "Hello World",
      {"~", "Dropbox", "File Requests", "Music"},
      "βŒƒβŒ₯⌘T",
      "1,1,2,3,5,13",
      "Anders Jonas Γ…ngstrΓΆm" }

where item 4 of args is now a unary string value, but item 2 of args is a list containing 4 items.

The second way to define the run handler is to specify a list of variables, which restricts the number of parameters that can be passed to, and stores them individually:

    on run {hello, filepath, trigger, fibonacci, angstrom}

Here is the content of each variable, firstly using the comma delimiter, then secondly using the slash delimiter:

If the run handler specifies more parameters than are supplied, AppleScript will throw an error. If the run handler specifies fewer parameters than are supplied, the remaining parameters are disregarded. For example, if the above action executes an AppleScript containing the following run handler, using the same parameters as above:

    on run {input, parameters}

then the value of input will be "Hello World", and the value of parameters will either be the string "~/Dropbox/File Requests/Music" or the list {"~", "Dropbox", "File Requests", "Music"}, depending on the inline delimiter selection.

Input: The AppleScript to be executed is specified by supplying a path to a .scpt or .applescript file in the field labelled Script File, which can include text tokens. The parameters are listed in the text box labelled Parameters. Each parameter is written on a single line, and may be a unary value or a list. A list of values is delimited by a single character chosen from the Inline Delimiter dropdown list. The default delimiter is a comma. A single space may optionally follow each delimiter, and will be disregarded during parsing. Therefore, the comma-delimited set of values 1,1,2,3,5,8,13 is the same as 1, 1, 2,3,5, 8,13 (however, the unary string expressions of these two would remain "as is"). Multiple spaces following a delimiter are retained, but one is always lost in parsing, i.e. two spaces become one, three become two, etc. The exception to this is when the chosen delimiter is the space character: this can produce unusual results if more than two spaces are inserted between list items. Parameters may contain text tokens and international characters.

Output: The action returns the result of the AppleScript following its execution. This result may be ignored, displayed in a window, displayed in a notification, saved to a variable, or copied to the clipboard.


Run AppleScript With Specified Parameters.zip (48.2 KB) [ UPDATED: 2018-07-15 ]

Changes

Parameters are now coerced from their string values to the most suitable AppleScript type class object before being sent to the run handler of the receiving script. Coercions take place on numerical, boolean, "null" and "missing value" parameters, and on parameters that are recognised as one of the following AppleScript constants: pi, yes, and no. Leading and/or trailing white space doesn't prevent the type classing. A parameter of zero length is converted to missing value.

8 Likes

Thanks for sharing. This looks very cool! :+1:

I can't wait to try it out!

1 Like

Thanks again for sharing this great plugin.
I have been playing with it, and it works great! :+1:

I know nothing about building plugins, so I'm wondering how hard it would be to make these changes:

  1. Allow the "Save to Variable" to be one or more KM variable names
    • These would correspond to the output of the script.
    • So either the script, or most ideal, the plugin would set these variables according to a list the script returns.
    • For example, if the script returns
      {fileName, fileDate, filePath},
      then the plugin would set the KM Variables entered:
      Local__FileName, Local__FileDate, Local__FilePath
  2. IF only one KM Variable name is entered in the "Save to" block, but the script returns a list with more than one item, then set the KM Variable using the plugin Delimiter the user has chosen.
    • So if the user chose "Bar", then it would be like doing this in script:
set AppleScript's text item delimiters to "|"
return {fileName, fileDate, filePath} as text
  1. Set a Default folder for the script, so that only the script file name needs to be entered.
    • Maybe show the Default folder path in the text above the fields.
    • If user enterer only a file name, use the Default folder
    • Else use the full path provided by the user.

So, can you give me any guidance on how to do this?

@CJK, looks like it is not that hard. I have a proof-of-concept script working now, and have modified the plist file (using Xcode editor). So hopefully I'll be finished soon.

Wow, good job :+1:t3:

I just read your initial message and my brain started pondering. Since the option to save the results to a variable is a built-in feature, that in itself can't be customised. However, there's nothing stopping us from adding a field of our own containing a list of variable names, and getting AppleScript to fill those variables using tell app "Keyboard Maestro Engine" to set [value of var1, value of var2, ...] to the resultsList.

With regards to the default folder, this is a nice idea and one that would be of great value to me too. I'm wondering whether a property value for the plugin's script gets stored and shared across separate instances of the plugin. I'm going to test this now.

It would be really useful if the plugin actions were more richly dynamic like the built in actions, so that we could set particular fields to appear or disappear depending on the value of another field.

Quick tip: I have this folder trigger watching the Keyboard Maestro Actions plugin folder for any changes:

2 Likes

Thanks. Looks good.

It appears to me from limited experimentation that I can update the Plugin AppleScript and just re-trigger the macro without doing any reloads. But if I change the Plist, then I have to reload.

I have now got my version of your PlugIn working. So far, it is looking good, but I need to do some more testing before I post.

But here's a sneak preview of how it looks when you add it to a macro:

image

Significant changes I have made:

  1. Provide for a Script Folder, which defaults to a KM Dictionary token
  2. Parameters -- if you want one parameter to be a list, then you have to start it with "LIST:"
    • This is so that other parameter that happen to have the Delimiter will NOT be processed
  3. Output to KM Variables
    • Generally you would provide one Var for each AppleScript variable that is returned
    • But you can just provide one KM Var, and the AppleScript variables will be output comma delimited (which makes it easy to use the KM "Array" technique)

Thanks again for this great idea, and for sharing your solution. It really helped me get started.

1 Like

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