Dictionary takes FOREVER to load

Hi folks.

I like to have application dictionaries open as a reference when I frustrate myself in AppleScript. A time-honoured tradition. The problem now is that I have backup drives, render drives, other drives…all on my Mac Pro box. I’m thinking it’s the culprit in that Script Editor takes several minutes to display the Open Dictionaries… dialog box, then it takes several more to scroll to the application I want, then choose the selected dictionary.

In the time it takes to open the desired dictionary, I could have completed the script’s purpose manually.

How can I fix this? Can I restrict the scope of the dictionaries to my startup volume only?

EDITED: It seems it was polling for scriptable applications through my LAN to my SAN, which would probably take hours to compile items. That’s not good.

Cheers

Model: Mac Pro
Browser: Safari 603.3.8
Operating System: Mac OS X (10.11.6)

I’ve been using this for years. It only offers the dictionaries of scriptable apps which are already open. It could probably do with a rewrite. :wink:

main()

on main()
	tell application "Finder" to set sysv to (system attribute "sysv")
	if (sysv < 4160) then abort("This version of the script only supports Mac OS X 10.4.0 or later.")
	
	-- Nobbled always to open dictionaries in Script Editor, as Script Debugger’s dictionary viewer’s pretty awful and ASObjC Explorer 4 doesn’t have one.
	set editorName to "Script Editor" --getEditorName()
	openDictionaries(editorName)
end main

on getEditorName()
	-- Check that either Script Editor, Smile, or Script Debugger is running.
	tell application "System Events" to set {editorNames, editorFrontmosts} to {name, frontmost} of (every application process whose creator type is "ToyS" or creator type is "VIZF" or creator type is "asDB")
	
	set editorCount to (count editorNames)
	if (editorCount is 0) then
		my abort("Please open (Apple)Script Editor, Smile, or Script Debugger before using this script.")
	else if (editorCount is 1) then
		-- One of the editors is running.
		set editorName to beginning of editorNames
	else if (editorFrontmosts contains true) then
		-- More than one of the editors is running. If one is the frontmost app, use that.
		repeat with i from 1 to editorCount
			if (item i of editorFrontmosts) then
				set editorName to item i of editorNames
				exit repeat
			end if
		end repeat
	else
		-- Otherwise consult the user for which editor to use.
		tell application (path to frontmost application as Unicode text) to set theChoice to (choose from list editorNames with prompt "In which editor do you want to view the dictionaries?" with title "Dictionaries - Running Apps")
		if (theChoice is false) then error number -128
		set editorName to beginning of theChoice
	end if
	
	return editorName
end getEditorName

on abort(msg)
	tell application (path to frontmost application as Unicode text) to display dialog msg buttons {"Cancel"} default button 1 with icon stop cancel button 1 with title "Dictionaries - Running Apps"
end abort

on openDictionaries(editorName)
	set specialNames to {"Finder"}
	
	tell application "System Events" to tell (application processes whose has scripting terminology is true and name is not "Image Capture Scripting") to set {procNames, procFiles} to {its name, its file}
	specialSort(procNames, procFiles, specialNames)
	
	tell application (path to frontmost application as Unicode text) to set theChoice to (choose from list procNames default items specialNames with prompt "Which dictionaries do you want to open?" with title "Dictionaries - Running Apps" with multiple selections allowed)
	if (theChoice is false) then error number -128
	
	set theFiles to {}
	repeat with i from 1 to (count procNames)
		if (item i of procNames is in theChoice) then set end of theFiles to item i of procFiles as alias -- Explicit coercion for OS 10.10, where the 'files' we've obtained are System Events references.
	end repeat
	
	tell application editorName
		activate
		open theFiles
	end tell
end openDictionaries

on specialSort(procNames, procFiles, specialNames)
	-- This script customises the sort to group any "special" application names before the others. It does this by judging non-special names to have higher comparison values.
	script specialsFirst
		property specials : specialNames
		
		on isGreater(a, b)
			set |b's special| to (b is in specials)
			if ((|b's special|) = (a is in specials)) then
				(a > b)
			else
				(|b's special|)
			end if
		end isGreater
	end script
	
	-- This script duplicates in procFiles moves performed in procNames by the custom insertion sort.
	script parallel
		property lst : procFiles
		
		on shift(a, b)
			tell item b of my lst
				repeat with i from b - 1 to a by -1
					set item (i + 1) of my lst to item i of my lst
				end repeat
				set item a of my lst to it
			end tell
		end shift
	end script
	
	CustomInsertionSort(procNames, 1, -1, {comparer:specialsFirst, slave:parallel})
end specialSort

-- Custom insertion sort from A Dose of Sorts by Nigel Garvey.
on CustomInsertionSort(theList, l, r, customiser)
	script o
		property comparer : me
		property slave : me
		property lst : theList
		
		on isrt(l, r)
			set u to item l of o's lst
			repeat with j from (l + 1) to r
				set v to item j of o's lst
				if (comparer's isGreater(u, v)) then
					set here to l
					set item j of o's lst to u
					repeat with i from (j - 2) to l by -1
						tell item i of o's lst
							if (comparer's isGreater(it, v)) then
								set item (i + 1) of o's lst to it
							else
								set here to i + 1
								exit repeat
							end if
						end tell
					end repeat
					set item here of o's lst to v
					slave's shift(here, j)
				else
					set u to v
				end if
			end repeat
		end isrt
		
		-- Default comparison and slave handlers to customise for an ordinary sort.
		on isGreater(a, b)
			(a > b)
		end isGreater
		
		on shift(a, b)
		end shift
	end script
	
	-- Process the input parmeters.
	set listLen to (count theList)
	if (listLen > 1) then
		-- Negative and/or transposed range indices.
		if (l < 0) then set l to listLen + l + 1
		if (r < 0) then set r to listLen + r + 1
		if (l > r) then set {l, r} to {r, l}
		
		-- Supplied or default customisation scripts.
		if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
		
		-- Do the sort.
		o's isrt(l, r)
	end if
	
	return -- nothing.
end CustomInsertionSort

Wow, thanks for that.

I realized there is an actual window for these dictionaries, and I can drop the app onto the Script Editor dock icon and the dictionary comes up. Works great with Launchbar as well.

Cheers

Here it is rewritten using ASObjC. It doesn’t seem to be any more effective in this form than the original, but it is more — er — modern. :wink:

main()

on main()
	script ASObjC
		use AppleScript version "2.4" -- Yosemite (10.10) or later
		use framework "Foundation"
		use framework "AppKit"
		use scripting additions
		
		property specialNames : {"Finder"} -- Any localised application names that should appear already selected at the top of the 'choose from list' dialog.
		property |⌘| : missing value
		property workspace : missing value
		property runningApps : missing value
		
		on main()
			set |⌘| to current application
			set workspace to |⌘|'s class "NSWorkspace"'s sharedWorkspace()
			set runningApps to workspace's runningApplications()
			
			-- Nobbled always to open dictionaries in Script Editor, as Script Debugger’s dictionary viewer’s pretty awful (and has its own "running applications" selector anyway) and ASObjC Explorer 4 doesn’t have one.
			set theEditor to getScriptEditor() -- getEditor()
			openDictionaries(theEditor)
		end main
		
		(* Open Script Editor and return an NSRunningApplication object for it. *)
		on getScriptEditor()
			set SEURL to workspace's URLForApplicationWithBundleIdentifier:("com.apple.ScriptEditor2")
			return workspace's launchApplicationAtURL:(SEURL) options:(0) configuration:(|⌘|'s class "NSDictionary"'s new()) |error|:(missing value)
		end getScriptEditor
		
		(* Return an NSRunningApplication object for the most likely running editor. *)
		on getEditor()
			-- Check that either Script Editor or Script Debugger is running. Smile's bundle identifier can be added to the array if known.
			set editorIDPredicate to |⌘|'s class "NSPredicate"'s predicateWithFormat:("bundleIdentifier IN %@") argumentArray:({{"com.apple.ScriptEditor2", "com.latenightsw.ScriptDebugger6"}})
			set runningEditors to runningApps's filteredArrayUsingPredicate:(editorIDPredicate)
			
			set editorCount to runningEditors's |count|()
			if (editorCount is 0) then
				abort("Please open AppleScript Editor or Script Debugger before using this script.")
			else if (editorCount is 1) then
				-- One of the editors is running.
				set theEditor to runningEditors's firstObject()
			else
				-- More than one of the editors is running. If one currently owns the Menu Bar owner, use it.
				set menuBarOwner to workspace's menuBarOwningApplication()
				if (runningEditors's containsObject:(menuBarOwner)) then
					set theEditor to menuBarOwner
				else
					-- Otherwise consult the user for which editor to use.
					set editorNames to runningEditors's valueForKey:("localizedName")
					tell application (path to frontmost application as text) to set theChoice to (choose from list (editorNames as list) with prompt "In which editor do you want to view the dictionaries?" with title "Dictionaries - Running Apps")
					if (theChoice is false) then error number -128
					set chosenEditorPredicate to |⌘|'s class "NSPredicate"'s predicateWithFormat:("localizedName == %@") argumentArray:(theChoice)
					set theEditor to (runningEditors's filteredArrayUsingPredicate:(chosenEditorPredicate))'s firstObject()
				end if
			end if
			
			return theEditor
		end getEditor
		
		on abort(msg)
			tell application (path to frontmost application as text) to display dialog msg buttons {"Cancel"} default button 1 with icon stop cancel button 1 with title "Dictionaries - Running Apps"
		end abort
		
		(* Choose and open the required dictionaries. *)
		on openDictionaries(theEditor)
			set runningAppBundleURLs to runningApps's valueForKey:("bundleURL")
			set runningAppNames to runningApps's valueForKey:("localizedName")
			
			-- Preset some stuff for identifying scriptable applications.
			set isScriptableKey to |⌘|'s NSURLApplicationIsScriptableKey
			set isScriptableKeyArray to |⌘|'s class "NSArray"'s arrayWithObject:(isScriptableKey)
			set isScriptableResult to |⌘|'s class "NSDictionary"'s dictionaryWithObject:(true) forKey:(isScriptableKey)
			set specialNames to |⌘|'s class "NSArray"'s arrayWithArray:(specialNames)
			-- Initialise separate arrays in which to collect the names of any "special" scriptable applications and of any others.
			set scriptableAppNames to |⌘|'s class "NSMutableArray"'s new()
			set unspecialScriptableAppNames to |⌘|'s class "NSMutableArray"'s new()
			-- Work through the bundle URLs in turn. If any has a resource value indicating scriptability, add the corresponding localised name to the appropriate array.
			repeat with i from 1 to (count runningAppBundleURLs)
				set thisURL to item i of runningAppBundleURLs
				if (((thisURL's resourceValuesForKeys:(isScriptableKeyArray) |error|:(missing value))'s isEqualToDictionary:(isScriptableResult)) as boolean) then
					set thisAppName to item i of runningAppNames
					if ((specialNames's containsObject:(thisAppName)) as boolean) then
						tell scriptableAppNames to addObject:(thisAppName)
					else
						tell unspecialScriptableAppNames to addObject:(thisAppName)
					end if
				end if
			end repeat
			-- Sort the two collections of names separately, then concatenate them together with the "special" ones in front.
			tell scriptableAppNames to sortUsingSelector:("compare:")
			tell unspecialScriptableAppNames to sortUsingSelector:("compare:")
			tell scriptableAppNames to addObjectsFromArray:(unspecialScriptableAppNames)
			
			-- Ask the user to choose from the found names.
			tell application (path to frontmost application as text) to set theChoice to (choose from list (scriptableAppNames as list) default items (specialNames as list) with prompt "Which dictionaries do you want to open?" with title "Dictionaries - Running Apps" with multiple selections allowed)
			if (theChoice is false) then error number -128
			set theChoice to |⌘|'s class "NSArray"'s arrayWithArray:(theChoice)
			
			-- Open the application files corresponding to the chosen names using the editor passed to this handler.
			set editorPath to theEditor's bundleURL()'s |path|()
			repeat with thisName in theChoice
				set i to (runningAppNames's indexOfObject:(thisName))
				set chosenAppPath to (runningAppBundleURLs's objectAtIndex:(i))'s |path|()
				tell workspace to openFile:(chosenAppPath) withApplication:(editorPath)
			end repeat
		end openDictionaries
	end script
	
	ASObjC's main()
end main