It’s easy to get the contents of a handler-block and the my-handler-blocks referencing to the first block, as I can build it in an “mechanical” way.
But I wanna get all sub-handler-blocks connected to the first handler-block
eg. lets say handler block 1 contains 2 my handler, those my-handlers contain more my handler - I need to collect all blocks connected to the first block), and therefore my code needs to be dynamic, looking recursively not just in down to two levels
I’m wondering how to approach my problem. Maybe with those incredible sorting routines of Nigel Garvey? (sweat) How to deal with them?
Can you provide some example code here to clarify what you mean? An handler-block is nothing more than code between the onhandlerName and endhandlerName placeholders. I assume you’re referring to something else than a handler-block, right?
Don’t you mean inheritance in script objects? I mean something like this where you create the parenting chain at run time and not at compile time?
on createParent(_parent)
script --anonymous
property parent : _parent
property description : "parent"
on getDescription()
return my description
end getDescription
end script
end createParent
on createChild(_parent)
script --anonymous
property parent : _parent
property description : "child object"
on getDescription()
return description
end getDescription
on getParentDescription()
parent's getDescription()
end getParentDescription
end script
end createChild
set p to createParent(me)
set c1 to createChild(p)
set c2 to createChild(p)
return {p's getDescription(), c1's getParentDescription(), c2's getParentDescription()}
To me it sounds like he’s just asking for a meta-script that would pull a handler and all additional handlers the first handler depends on (regardless of how deep the nesting goes) out of a script.
IE:
-you run a script
it asks you to select the script you’d like to operate on
it presents a list of all handlers in that script and asks you to select one
it creates a new script file that contains the handler you selected, and every other handler referenced by the one you selected, and every handler referenced by those handlers, recursive until all possible dependencies are included.
Let me know if that’s correct… if so, it doesn’t sound all that bad to script… except I’m thinking it might be hard to detect and avoid possible loop situations.
This is the piece of code i wrote. Its not a final solution, but a limited code, as its not good to collect more than a handler and its sub-handlers. Stopping there - need some input how to improve that and collect all handlers.:o
set Appscr_app to "Script Editor"
tell application Appscr_app
tell document 2 #1
#collect handlers
set all_ppr to paragraphs
set dd to 0
set ls to {}
repeat with a in all_ppr
set dd to dd + 1
set a to a as text
if a begins with "on " then
if "(" is in a then
set get_routine to text 4 thru ((offset of "(" in a) - 1) of a
else
set get_routine to text 4 thru -2 of a
end if
if number of words of get_routine = 2 then
set get_routine to word 1 of get_routine
end if
copy (dd & " • " & get_routine as text) to end of ls
end if
end repeat
#set ls to ls as text
#return nrhdl
if ls is "" then return
activate
set ch to choose from list ls with prompt "Wähle Block den Ihr kopieren wollt..." default items (item 1 of ls)
if ch is false then return
set ch to ch as text
set get_offset to (offset of " • " in ch)
set find_parag to text 1 thru (get_offset - 1) of ch as number
set handlerNm to text (get_offset + 3) thru -1 of ch as text
set cc_all_ppr to number of items in all_ppr
set {copy_handler, my_hdlLs} to {{}, {}}
repeat with b from find_parag to cc_all_ppr
set get_parag to item b of all_ppr
if get_parag begins with "end " & handlerNm then
copy get_parag to end of copy_handler
exit repeat
else
if " my " is in get_parag or "my " is in get_parag and "(" is in get_parag then
copy b to end of my_hdlLs
end if
copy get_parag to end of copy_handler
end if
end repeat
set copy_handler to (copy_handler as text)
#return copy_handler
end tell
#copy all my handlers connected to our handler if any
set copy_handler to (copy_handler as text)
if my_hdlLs is not {} then
#log my_hdlLs
repeat with a in (my_hdlLs as list) #just paragraph numbers
set get_ppg to (item a of all_ppr)
#log get_ppg
set all_words to words of get_ppg
#log all_words
#find my in a paragraph
set dd to 0
repeat with b in all_words
set dd to dd + 1
set b to b as text
if b is "my" then
set handlerVar to item (dd + 1) of all_words
exit repeat
end if
end repeat
#log handlerVar
set bb to 0
repeat with c in ls
set c to c as text
set bb to bb + 1
if handlerVar is in c then
set get_offset to (offset of " • " in c)
set handlerNm to text (get_offset + 3) thru -1 of c as text
set find_parag to text 1 thru (get_offset - 1) of c as number
exit repeat
end if
end repeat
#log handlerNm
#log find_parag
set copy_Myhandler to {}
repeat with d from find_parag to cc_all_ppr
set get_parag to item d of all_ppr
if get_parag begins with "end " & handlerNm then
copy get_parag to end of copy_Myhandler
exit repeat
else
copy get_parag to end of copy_Myhandler
end if
end repeat
#exit repeat
set copy_handler to copy_handler & return & (copy_Myhandler as text)
end repeat
end if
#return copy_handler
set res to make new document with properties {contents:copy_handler}
compile res
end tell
Here’s an effort using the Satimage OSAX. It could no doubt be done with ASObjC instead, but with more code. Caveats at the top:
(*
Choose a handler from a script document in Script Editor and copy it and any other handlers used during its execution to a new document.
THIS SCRIPT REQUIRES THE SATIMAGE OSAX (<http://www.satimage.fr/software/en/downloads/downloads_companion_osaxen.html>).
It's only intended for use where all the handlers are at the top level of the source script. It's not recommended for handlers in script objects, although it may sometimes work with them.
Not fully proofed against text values or barred labels containing characters which might fool the regices.
Only handlers are inserted into the new script. Any required properties, 'use' statements, or script objects are your own problem!
*)
on getHandlerAndSubhandlers(scriptText)
script o
property handlerMatches : missing value
property handlerCount : missing value
property handlerTextList : missing value
property matchIndexList : missing value
on doTheBusiness(scriptText)
-- Get the text of all the handlers in the script and, as separate entities, the text after "end " in their last lines.
set handlerMatches to (find text "(?m)^[ \\t]*+(?:on (?!error)|to )([[:alnum:]_\\|]++)(?:.(?!end \\1))++.end (\\1[^\\R]*+)" in scriptText using {0, 2} regexpflag {"MULTILINE"} with regexp, string result and all occurrences)
-- Build a list of the "end" labels to present to the user.
set labelList to {}
set handlerCount to (count handlerMatches)
repeat with i from 1 to handlerCount
set thisMatch to item i of handlerMatches
set end of labelList to (i as text) & " • " & end of thisMatch
end repeat
if (labelList is {}) then display dialog "\"No handlers found in the script.\" in German!" buttons {"OK"} default button 1 cancel button 1 with icon stop
-- Get the user's choice of "top" handler and start a list of handler texts with it.
set cflResult to (choose from list labelList with prompt "Wähle Block den Ihr kopieren wollt..." default items {item 1 of labelList})
if (cflResult is false) then error number -128
set chosenHandlerMatchIndex to (word 1 of beginning of cflResult) as integer
set chosenHandlerMatch to item chosenHandlerMatchIndex of handlerMatches
set chosenHandlerText to beginning of chosenHandlerMatch
set handlerTextList to {chosenHandlerText}
set matchIndexList to {chosenHandlerMatchIndex}
-- Add the texts of any other handlers called during the execution of the chosen one.
addSubhandlers()
-- Return a single text containing all the handler texts collected.
return (join handlerTextList using (linefeed & linefeed))
end doTheBusiness
on addSubhandlers() -- Recursive handler to identify and add any subhandlers called by the most recent addition to handlerTextList.
set callingHandlerText to item -1 of handlerTextList
repeat with i from 1 to handlerCount
-- Only check handlers which aren't in handlerTextList already.
if (i is not in matchIndexList) then
-- Get the full text and "end" label of each.
set thisMatch to item i of my handlerMatches
set {thisHandlerText, handlerEndLabel} to thisMatch
-- Make up a suitable regex to identify any call to this handler in the current calling handler.
if (handlerEndLabel contains ":") then
-- Interleaved parameters. All must be in the call.
set handlerCallRegex to (change {"[|(){}]", ":", " $"} into {"\\\\\\0", ":(?:\"[^\"]*+\"|\\\\{[^\\\\}]*+\\\\}|\\\\([^\\\\)]*+\\\\)|\\\\|[^\\\\|]*+\\\\||[^[:space:]]++) ", "(?:(?! )|(?= [-#(]))"} in handlerEndLabel with regexp)
else
-- Not interleaved parameters. Check for parenthesis in the top line of the handler.
set handlerTopLine to paragraph 1 of thisHandlerText
if (handlerTopLine contains "(") then
-- Positional parameters. The handler label must be followed by parenthesis in the call.
set handlerCallRegex to handlerEndLabel & "\\([^\\)]*+\\)"
else
-- Labelled parameters. The handler label and the first parameter label must both be in the call.
set handlerCallRegex to (change "^\\s*+(?:on|to) ([^ ]++)( [^ ]++).++" into "\\1.*\\2" in handlerTopLine with regexp)
end if
end if
-- Apply the regex to the text of the calling handler. If any hits, store this subhandler text and recurse to check _its_ subhandlers.
if ((find text handlerCallRegex in callingHandlerText with regexp and all occurrences) is not {}) then
set end of my handlerTextList to thisHandlerText
set end of my matchIndexList to i
addSubhandlers()
end if
end if
end repeat
end addSubhandlers
end script
return o's doTheBusiness(scriptText)
end getHandlerAndSubhandlers
tell application "Script Editor" to set scriptText to text of document 2 #1
set extractedHandlersText to getHandlerAndSubhandlers(scriptText)
tell application "Script Editor" to compile (make new document with properties {text:extractedHandlersText})
(*
Choose a handler from a script document in Script Editor and copy it and any other handlers used during its execution to a new document.
This version of the script uses ASObjC and required Mac OS 10.10 (Yosemite) or later.
It's only intended for use where all the handlers are at the top level of the source script. It's not recommended for handlers in script objects, although it may sometimes work with them.
Not fully proofed against text values or barred labels containing characters which might fool the regices.
Only handlers are inserted into the new script. Any required properties, 'use' statements, or script objects are your own problem!
*)
on getHandlerAndSubhandlers(scriptText)
script o
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property |⌘| : current application
property scriptNSString : missing value
property handlerMatches : missing value
property handlerCount : missing value
property handlerTextArray : missing value
property matchIndexList : missing value
on doTheBusiness(scriptText)
set scriptNSString to |⌘|'s class "NSString"'s stringWithString:(scriptText)
-- Get the text of all the handlers in the script and, as separate entities, the text after "end " in their last lines.
set handlerMatchRegex to |⌘|'s class "NSRegularExpression"'s regularExpressionWithPattern:("(?ms)^[ \\t]*+(?:on (?!error)|to )([[:alnum:]_\\|]++)(?:.(?!end \\1))++.end (?-s)(\\1.*+)$") options:(0) |error|:(missing value)
set handlerMatches to handlerMatchRegex's matchesInString:(scriptNSString) options:(0) range:({0, scriptNSString's |length|()})
-- Build a list of the "end" labels to present to the user.
set labelList to {}
set handlerCount to handlerMatches's |count|()
repeat with i from 1 to handlerCount
set thisMatch to item i of handlerMatches
set thisLabelRange to (thisMatch's rangeAtIndex:(2))
set end of labelList to (i as text) & " • " & (scriptNSString's substringWithRange:(thisLabelRange)) -- Automatic coercion of NSString to text.
end repeat
if (labelList is {}) then display dialog "\"No handlers found in the script.\" in German!" buttons {"OK"} default button 1 cancel button 1 with icon stop
-- Get the user's choice of "top" handler and start a list of handler texts with it.
set cflResult to (choose from list labelList with prompt "Wähle Block den Ihr kopieren wollt..." default items {item 1 of labelList})
if (cflResult is false) then error number -128
set chosenHandlerMatchIndex to (word 1 of beginning of cflResult) as integer
set chosenHandlerMatch to item chosenHandlerMatchIndex of handlerMatches
set chosenHandlerText to scriptNSString's substringWithRange:(chosenHandlerMatch's range())
set handlerTextArray to |⌘|'s class "NSMutableArray"'s arrayWithObject:(chosenHandlerText)
set matchIndexList to {chosenHandlerMatchIndex}
-- Add the texts of any other handlers called during the execution of the chosen one.
addSubhandlers()
-- Return a single text containing all the handler texts collected.
return (handlerTextArray's componentsJoinedByString:(linefeed & linefeed)) as text
end doTheBusiness
on addSubhandlers()
set callingHandlerText to handlerTextArray's lastObject()
repeat with i from 1 to handlerCount
-- Only check handlers which aren't in handlerTextArray already.
if (i is not in matchIndexList) then
-- Get the full text and "end" label of each.
set thisMatch to item i of handlerMatches
set thisHandlerText to (scriptNSString's substringWithRange:(thisMatch's range()))
set handlerEndLabel to (scriptNSString's substringWithRange:(thisMatch's rangeAtIndex:(2)))
-- Make up a suitable regex to identify any call to this handler in the current calling handler.
if ((handlerEndLabel's containsString:(":")) as boolean) then
-- Interleaved parameters. All must be in the call.
set handlerCallRegex to (handlerEndLabel's stringByReplacingOccurrencesOfString:("[|(){}]") withString:("\\\\$0") options:(|⌘|'s NSRegularExpressionSearch) range:({0, handlerEndLabel's |length|()}))
set handlerCallRegex to (handlerCallRegex's stringByReplacingOccurrencesOfString:(":") withString:(":(?:\"[^\"]*+\"|\\\\{[^\\\\}]*+\\\\}|\\\\([^\\\\)]*+\\\\)|\\\\|[^\\\\|]*+\\\\||[^[:space:]]++) ") options:(|⌘|'s NSRegularExpressionSearch) range:({0, handlerCallRegex's |length|()}))
set handlerCallRegex to (handlerCallRegex's stringByReplacingOccurrencesOfString:(" $") withString:("(?:(?! )|(?= [-#(]))") options:(|⌘|'s NSRegularExpressionSearch) range:({0, handlerCallRegex's |length|()}))
else
-- Not interleaved parameters. Check for parenthesis in the top line of the handler.
set handlerTopLineRange to (thisHandlerText's rangeOfString:(".+") options:(|⌘|'s NSRegularExpressionSearch) range:({0, thisHandlerText's |length|()}))
if ((thisHandlerText's rangeOfString:("(") options:(0) range:(handlerTopLineRange))'s |length|() > 0) then
-- Positional parameters. The handler label must be followed by parenthesis in the call.
set handlerCallRegex to (handlerEndLabel's stringByAppendingString:("\\([^\\)]*+\\)"))
else
-- Labelled parameters. The handler label and the first parameter label must both be in the call.
set handlerTopLine to (thisHandlerText's substringWithRange:(handlerTopLineRange))
set handlerCallRegex to (handlerTopLine's stringByReplacingOccurrencesOfString:("^\\s*+(?:on|to) ([^ ]++)( [^ ]++).++") withString:("$1.*$2") options:(|⌘|'s NSRegularExpressionSearch) range:(handlerTopLineRange))
end if
end if
-- Apply the regex to the text of the calling handler. If any hits, store this subhandler text and recurse to check _its_ subhandlers.
if ((callingHandlerText's rangeOfString:(handlerCallRegex) options:(|⌘|'s NSRegularExpressionSearch) range:({0, callingHandlerText's |length|()}))'s |length|() > 0) then
tell handlerTextArray to addObject:(thisHandlerText)
set end of matchIndexList to i
addSubhandlers()
end if
end if
end repeat
end addSubhandlers
end script
return o's doTheBusiness(scriptText)
end getHandlerAndSubhandlers
tell application "Script Editor" to set scriptText to text of document 2 #1
set extractedHandlersText to getHandlerAndSubhandlers(scriptText)
tell application "Script Editor" to compile (make new document with properties {text:extractedHandlersText})
Just awesome - it looks like a lot more work if done in pure AppleScript . OKk stop here, it’s just a thought and no request at all- as I’m very happy to get more ApplescriptObjC under my hands to study… Which is a step in the future. It seems to me like going back in time when I studied AppleScript for the first time :o :o
I wrote here for the funs of the plain AppleScript simple but effective version. It isn’t recommended with handlers in script objects too (like with ASObjC version):
property ScripEditorName : "Script Debugger"
global allHandlers
global theHandlerNames
set {allHandlers, theHandlerNames} to {{}, {}}
-- GET HANDLERS and ITS NAMES
tell application ScripEditorName to tell document 2 to set {theHandlers, theHandlerNames} to {script handlers, name of every script handler}
-- CHOOSE THE TOP HANDLER NAME FROM LIST
set chosenHandlerName to choose from list theHandlerNames with prompt "Choose Top Handler's Name here..." default items {item 1 of theHandlerNames}
if (chosenHandlerName is false) then
display notification "error number -128: User Cancelled"
return
else
set chosenHandlerName to item 1 of chosenHandlerName
end if
-- RECURSIVELY COLLECT SUBHANDLER'S NAMES
my addHandlerNames(chosenHandlerName)
return allHandlers
-- THE RECURSIVE HANDLER
on addHandlerNames(chosenHandlerName)
tell application ScripEditorName to tell document 2 to set chosenHandlerText to source text of script handler chosenHandlerName
if (chosenHandlerText begins with "on ") or (chosenHandlerText begins with "to") then set chosenHandlerText to paragraphs 2 thru -2 of chosenHandlerText as text
repeat with aHandlerName in theHandlerNames
if (((aHandlerName & "(") is in chosenHandlerText) or ((aHandlerName & ":") is in chosenHandlerText)) and (allHandlers does not contain aHandlerName) then
set end of allHandlers to contents of aHandlerName
my addHandlerNames(contents of aHandlerName)
end if
end repeat
end addHandlerNames