AppleScript Dictionary Script

Thanks again.

I will save this script so that I don’t forget it.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 8 janvier 2020 13:38:23

While working on this topic, I wrote a simple script which is functionally little different than selecting Open Dictionary from the File menu of Script Editor. The very-slight advantages of this script are 1) it contains only the dictionaries the user wants, and 2) it can be run just about any way a script can be run, which is not the case with my script included above. I tested this with most applications that have a dictionary but there may be a few that won’t work.

property defaultItem : missing value

set appList to {"Finder", "Mail", "Preview", "Safari", "Script Editor", "StandardAdditions", "System Events", "TextEdit"}

choose from list appList default items {defaultItem} with prompt "Select a Dictionary:"

if result = false then
	error number -128
else
	set selectedApp to item 1 of result
end if

if selectedApp = "StandardAdditions" then
	set appPath to "Macintosh HD:System:Library:ScriptingAdditions:StandardAdditions.osax:" as alias
else
	set appPath to path to application selectedApp
end if

tell application "Script Editor"
	if selectedApp is in (name of every window) then
		my raiseWindow(selectedApp)
	else
		activate
		open appPath
	end if
end tell

set defaultItem to selectedApp

on raiseWindow(selectedApp)
	tell application "System Events" to tell process "Script Editor"
		perform action "AXRaise" of window selectedApp
	end tell
end raiseWindow

The posted script issued :

“Erreur dans Script Editor : Il est impossible d’obtenir alias (alias "SSD 1000:Applications:Safari.app:").” number -1728 from alias (alias “SSD 1000:Applications:Safari.app:”)
A small edit is required

tell application "Script Editor"
	activate
	open appPath -- EDITED because appPath is already an alias
end tell

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 8 janvier 2020 15:58:59

Thanks Yvan. I’ve corrected that.

You may use the simple :

set theApp to choose application as alias

tell application "Script Editor"
	activate
	open theApp 
end tell

At least with 10.13.6, as long as you select an app in the proposed list you will have a scriptable one.

I would be glad to know if other systems behave the same way.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 8 janvier 2020 18:25:15

Yvan. Your script works fine on Catalina and is probably preferred for its simplicity and speed.

@peavine

Thank you.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 8 janvier 2020 18:48:23

If you want simplicity and speed, Script Debugger’s OpenDictionary command is your answer. Separate lists of running, recent and favorite apps and script libraries, as well as Standard Additions and a general open command. As a bonus, you get a much more detailed presentation of the dictionary.

My script works fine and I’ll use it a lot, but I did encounter one small issue. To replicate:

  1. Copy and paste the script contained below into Script Editor.

  2. Run the script, which loads the Script Editor dictionary.

  3. Without closing the dictionary, activate Script Editor and run the script again. This brings the Script Editor dictionary to the front but Script Editor continues to show “running” for about 5 seconds.

I wondered if anyone knew how to prevent my script from running as described in 3 above. This does have a practical consequence during normal use in that FastScripts is not responsive during the 5-second period. Thanks.

tell application "System Events"
	set appProcess to first application process whose frontmost is true
	set appName to name of appProcess
	set appDictionary to has scripting terminology of appProcess
end tell

set appFile to path to application appName

if appDictionary then
	tell application "Script Editor"
		activate
		open appFile
	end tell
else
	display dialog appName & " does not have a dictionary." buttons {"OK"} ¬
		cancel button 1 default button 1 with icon stop
end if

I get the same result on my Mac with Script editor.

Even more interesting is: if I run it with SD with the Debugging option active, I get an error at:
open file of activeApp as alias
This is the error:
Can’t make alias “iMac_HD:Applications:Script Debugger.app:” of application “System Events” into type alias.

This might help you in fix this strange behaviour.

L.

This is the script I used in SD:

tell application "System Events"
	set activeApp to first application process whose frontmost is true
	set appName to name of activeApp
	set appDictionary to has scripting terminology of activeApp
end tell

if appDictionary then
	tell application "Script Debugger"
		open file of activeApp as alias
		activate
	end tell
else
	display dialog appName & " does not have a dictionary." buttons {"OK"} ¬
		cancel button 1 default button 1 with icon stop
end if

This is the corresponding screenshot:
https://funkyimg.com/i/31dfQ.png

Try to use :

tell application "System Events"
	set activeApp to first application process whose frontmost is true
	set appName to name of activeApp
	set appDictionary to has scripting terminology of activeApp
	set thePath to path of (file of activeApp)
end tell

if appDictionary then
	set maybe to appName & ".sdef"
	tell application "System Events" to tell process "Script Editor"
		tell menu bar 1 to tell menu bar item 9 to tell menu 1
			set menuItems to name of menu items
			--> {"Placer dans le Dock", "Placer toutes les fenêtres dans le Dock", "Réduire/agrandir", "Réduire/agrandir toutes les fenêtres", missing value, "Afficher l’onglet précédent", "Afficher l’onglet suivant", "Placer l’onglet dans une nouvelle fenêtre", "Fusionner toutes les fenêtres", missing value, "Tout ramener au premier plan", "Mettre au premier plan", missing value, "Historique", "Bibliothèque", missing value, "Enregistrer comme réglage par défaut", missing value, "get language code.scpt", "Pages.sdef", "Sans titre", "Sans titre 2", "System Events.sdef"}
			if menuItems contains maybe then -- if Finder show extensions
				click menu item maybe
				return -- exit the script
			end if
			
			if menuItems contains appName then -- if Finder doesn't show extensions
				click menu item appName
				return -- exit the script
			end if
		end tell
	end tell
	
	tell application "Script Editor"
		open file thePath
		activate
	end tell
else
	display dialog appName & " does not have a dictionary." buttons {"OK"} ¬
		cancel button 1 default button 1 with icon stop
end if

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 9 janvier 2020 19:10:39

Thanks Yvan and ldicroce for the responses. I’ve revised my script in Post 18 to fix the issue identified by ldicroce. So, all is well.

Would be more logical to put the instruction

set appFile to path to application appName

in the tell application “System Events” block.

But as far as I know, this “enhanced” version doesn’t solve your problem #3.

It’s what my added instructions were supposed to achieve.
If the frontmost application is Script Editor and if there is a window “Script Editor.sdef” available, it doesn’t open the file, it just reveal the window.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 10 janvier 2020 15:00:18

Yvan. Thanks for looking at my revised script and for your comments.

As regards the “path to” command line in my script in Post 18, it has always been my understanding that it’s best not to put a command in a tell statement if that’s not necessary. Because the “path to” command is in Standard Additions, I did not put that instruction in System Events. I’d be interested to learn how this is best handled.

You are correct that the revised version of my script in Post 18 does not fix the error identified as issue #3 in that post. Perhaps I misled, but this revision was only intended to fix the issue identified by ldicroce.

I did try your script which worked well, but it only seemed to fix issue #3 with Script Editor. I tested it with Finder, Safari, and Mail, and the 5-second delay was still present.

I have further refined this issue in the following script. Run it once and there is no delay. Run it again with the Safari dictionary open and there is a 5-second delay, as reflected by the word “running” in the Script Editor Result pane.

set theApp to "Macintosh HD:Applications:Safari.app:" as alias

tell application "Script Editor"
	open theApp
end tell

Actually the ideal solution. which entirely avoids this issue, is to get the path to the app’s dictionary and to use Finder to open it, as in the following:

set appPath to "Macintosh HD:System:Library:ScriptingAdditions:StandardAdditions.osax:" as alias

tell application "Finder" to open appPath

With other apps, like Safari, this approach just opens the app.

Running the script :

tell application "System Events"
	set activeApp to first application process whose frontmost is true
end tell

returns something :
application process “Script Editor” of application “System Events”

In System Events every disk item has its own path property so it seems logical to ask the app to grab the property.

I enhanced my script.
If I made no error, it would be OK with every scriptable application.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 10 janvier 2020 18:38:23

Yvan. I tested the following scripts in Script Geek:

tell application "System Events"
	set activeApp to first application process whose frontmost is true
	set activeName to name of activeApp
	set thePath to path of (file of activeApp) as alias
end tell
tell application "System Events"
	set activeApp to first application process whose frontmost is true
	set activeName to name of activeApp
end tell

set appPath to path to application activeName

With 100 iterations, the first script took 2.5 milliseconds per iteration and the second script 2.3 milliseconds. So, timewise, they are pretty much the same. From a design/style perspective, grouping all the commands under System Events makes sense.

I tried your revised script and encountered a delay as before. I suspect this is something I’m doing wrong.

I rewrote my script to operate in a fashion similar to Nigel’s script in the thread linked below. My script displays a dialog with default apps, which are specified in the script, plus apps that are active at the time the script is run. The script no longer has the delay-on-reopen issue.

Thanks again for the help.

https://www.macscripter.net/viewtopic.php?pid=191643

property defaultItem : missing value

set defaultApps to {"StandardAdditions", "System Events"}

tell application "System Events"
	set activeApps to name of every process whose visible is true and has scripting terminology is true
end tell

set appList to activeApps & defaultApps

if defaultItem is not in appList then set defaultItem to item 1 of appList

choose from list appList with title "AppleScript" with prompt "Dictionaries" default items {defaultItem} OK button name "Open"

if result = false then
	error number -128
else
	set selectedApp to item 1 of result
end if

if selectedApp = "StandardAdditions" then
	set appFile to ((path to scripting additions folder as text) & "StandardAdditions.osax:") as alias
else
	set appFile to path to application selectedApp
end if

tell application "Script Editor"
	if selectedApp is in (name of every window) then
		raiseWindow(selectedApp) of me
	else
		activate
		open appFile
		-- set bounds of window 1 to {10, 33, 750, 1065} # option to resize dictionary window
	end if
end tell

set defaultItem to selectedApp

on raiseWindow(selectedApp)
	tell application "Script Editor" to activate # duplicate activate commands are intentional
	tell application "System Events" to tell process "Script Editor" to perform action "AXRaise" of window selectedApp
	tell application "Script Editor" to activate
end raiseWindow

I made a library a while ago that dealt with this sort of thing, so I dug out a couple of handlers from it and checked it still works (in High Sierra). Despite my undying protestations against using Finder for anything AppleScript related except telling it to quit, I actually elected to use it for this task because it can enumerate a folder structure recursively with entire contents (if one knows how to use it optimally—I wrote a post about this a while ago outlining the principles of this), and because it allows one to enumerate scriptable applications that need not be running processes: every application file class object in Finder has the has scripting terminology property too.

On my clunky machine, the ScriptableApplications handler enumerates all scriptable applications within my /Applications directory in zero seconds, and does the same for /System/Library/CoreServices folder. These are the only two I’ve used it on, as they’re the only two that contain applications of any value to the user. If you want it to enumerate the entire hard drive, then you deserve what ensues (just don’t).

The ScriptingDictionary handler accepts a bundle identifier for the scriptable application for which you want to retrieve its .sdef file to open in Script Editor. There’s no error handling incorporated into this, so it’s not idiot-proof. If you pass it an application name, or a bundle identifier for an application that isn’t scriptable, it will throw an error. Like a lot of things, failure is the worst case scenario when calculating efficiency of an algorithm, as it only knows it has failed after checking every possibility. But if you pass it an option it can succeed with, it’ll do so reasonably quickly (about a second, maybe two). It only searches for .sdef files currently, so it will actually fail trying to get Script Editor’s dictionary file.

property directory : path to applications folder

use Finder : application id "com.apple.Finder"
property folder : a reference to folder (a reference to directory)
property contents : a reference to (my folder)'s entire contents
property list : missing value

on ScriptableApplications at path
		local path
		
		set my directory to the path as POSIX file
		set my list to a reference to application files in its contents
		
		script
				property parent : my list
				property id : my id
				property key : my has scripting terminology
		end script
		
		tell the result
				repeat with A in (a reference to id)
						set [[x], key] to key's [it, rest]
						if not x then set A's contents to false
				end repeat
				
				return id's text
		end tell
end ScriptableApplications

on ScriptingDictionary for bundleid
		local bundleid
		
		set my directory to folder "Contents:Resources:" in ¬
				application file id bundleid as alias
		set my list to a reference to files in its contents
		set my text item delimiters to linefeed
		
		script
				property parent : my list
				property name : my name
				property key : my name extension as text
		end script
		
		tell the result
				set my text item delimiters to "sdef"
				set i to the number of paragraphs in the key's first text item
				set f to item i of the name
				if f does not end with ".sdef" then return false
				return file f in the folder directory as alias
		end tell
end ScriptingDictionary

return ScriptableApplications at "/Applications"
tell application id "com.apple.ScriptEditor2" to open my (ScriptingDictionary for "com.apple.Reminders")

Whether this will work or not in Catalina is not something I’ll be in a rush to attend to, as I don’t use Catalina except for development, and I use Swift to do this job now. Although, I also just found this handler I wrote in AppleScriptObjC but never really used it:

to readSDEF for id
		local id
		
		(NSString's stringWithContentsOfFile:(firstObject() in ((NSBundle's ¬
				bundleWithIdentifier:id)'s pathsForResourcesOfType:("sdef") ¬
				inDrectory:(missing value)))) as text
end readSDEF

You’d need to add suitable headers to import Foundation and declare/define the two classes, but that can obviously replace the vanilla handler above very easily.

Model: MacBook 12" 2016
AppleScript: 2.7
Operating System: macOS 10.13

But not all scriptable apps have an sdef file.

May you test the script from the Script Editor ?

Here I got:


Don't click upon [Open this Scriplet in your Editor:] this is a log history

tell application "System Events"
	get application process 1 whose frontmost = true
		--> application process "Script Editor"
	get name of application process "Script Editor"
		--> "Script Editor"
	get has scripting terminology of application process "Script Editor"
		--> true
	get path of file of application process "Script Editor"
		--> "SSD 1000:Applications:Utilities:Script Editor.app:"
	get name of every menu item of menu 1 of menu bar item 9 of menu bar 1 of process "Script Editor"
		--> {"Placer dans le Dock", "Placer toutes les fenêtres dans le Dock", "Réduire/agrandir", "Réduire/agrandir toutes les fenêtres", missing value, "Afficher l’onglet précédent", "Afficher l’onglet suivant", "Placer l’onglet dans une nouvelle fenêtre", "Fusionner toutes les fenêtres", missing value, "Tout ramener au premier plan", "Mettre au premier plan", missing value, "Historique", "Bibliothèque", missing value, "Enregistrer comme réglage par défaut", missing value, "Sans titre", "Sans titre 2", "Sans titre 3", "Sans titre 4", "search in lProjs.scpt", "set windows settings.applescript"}
end tell
tell application "Script Editor"
	open file "SSD 1000:Applications:Utilities:Script Editor.app:"
		--> missing value
end tell
tell current application
	activate
end tell

As you see, they were not “Script Editor.sdef” window open so the script opened the file.


Don't click upon [Open this Scriplet in your Editor:] this is a log history

tell application "System Events"
	get application process 1 whose frontmost = true
		--> application process "Script Editor"
	get name of application process "Script Editor"
		--> "Script Editor"
	get has scripting terminology of application process "Script Editor"
		--> true
	get path of file of application process "Script Editor"
		--> "SSD 1000:Applications:Utilities:Script Editor.app:"
	get name of every menu item of menu 1 of menu bar item 9 of menu bar 1 of process "Script Editor"
		--> {"Placer dans le Dock", "Placer toutes les fenêtres dans le Dock", "Réduire/agrandir", "Réduire/agrandir toutes les fenêtres", missing value, "Afficher l’onglet précédent", "Afficher l’onglet suivant", "Placer l’onglet dans une nouvelle fenêtre", "Fusionner toutes les fenêtres", missing value, missing value, "Tout ramener au premier plan", "Mettre au premier plan", missing value, "Historique", "Bibliothèque", missing value, "Enregistrer comme réglage par défaut", missing value, "Sans titre", "Sans titre 2", "Sans titre 3", "Sans titre 4", "Script Editor.sdef", "search in lProjs.scpt", "set windows settings.applescript"}
	click menu item "Script Editor.sdef" of menu 1 of menu bar item 9 of menu bar 1 of process "Script Editor"
		--> menu item "Script Editor.sdef" of menu "Fenêtre" of menu bar item "Fenêtre" of menu bar 1 of application process "Script Editor"
end tell

This time,
As you see, a window “Script Editor.sdef” was available so the script just click the menu item bringing the window at front.

Of course, if the dictionary of a given application doesn’t appear in a xx.sdef window the scheme will not apply.
If you meet such case, it would be fine to post the name of the window displaying the dictionary.
It would be quite easy to enhance the script accordingly.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 12 janvier 2020 10:57:48

Yvan. When you provided your script in Post 20, I was busy working on my own script, and I was more than a little frustrated that I couldn’t get it to work. You took the time to help, and I was negligent not to spend some additional time on this.

I did some troubleshooting with your script in Post 20 and discovered why it didn’t work as expected. I have Finder set not to show extensions and so the script did not find:

I changed the setting in Finder to show extensions and now the script works perfect. Many thanks for the help.