From AlphaWiki

Main: AppleScripting Alpha

<JK 2005-01-06>

The information found on this page should eventually find its way into the Alpha Help Files.


Alpha is highly AppleScriptable: In addition to a couple of required classes and commands, Alpha sports only one applescript command (quite a minimal set, indeed):

 
    DoScript

This command allows for the execution of 'any' Tcl script or command. Hence 'every' existing or imaginable command in Alpha, including all menu items, keystrokes, event hooks, advanced searches and filter actions, preference settings, can be executed via an applescript. This makes Alpha one of the most scriptable of all applications, and this design is extremely flexible and powerful. No need to update a dictionary each time a command is added; user-defined Alpha commands are immediately callable through AppleScripts too.

However, it would be nice to implement native support for a few of the most standard commands. This has been requested by Claus Gerhardt on the OSX-TeX mailing list, and it is supported by Jon Guyer who furthermore illustrates how it should be implemented -- see below.

One question is: which are those standard commands? Currently the emphasis is on TeX related commands (as needed in synchronisation and flash mode implementations). The second question is how to do such things in AlphaTcl. The following list is based on Claus's requests on OSX-TeX.

The next problem is how to build these Tcl scripts in your AppleScript. Greetings from Quoting Hell... In fact, this is mostly a problem within applescripting, so perhaps it is slightly off-topic here. But a couple of examples are included below.

Finally, at the end of this page there are some code snippets from Jon, illustrating how to implement native event handlers.


Which AppleScript commands are needed, and how do such things in Tcl

simply do

 
    DoScript "win::Current"

simply do

 
    DoScript "win::getInfo texName dirty"

this will return 1 or 0 (for true or false).

 
    find "string"
    replace by ""

Assuming that the string is a literal string (not a regexp), the AlphaTcl command would be

 
    eval replaceText [search -r 0 -- $findPat [minPos]] {""}

where $findPat is the string you want to delete.

(The -r 0 flag means literal string, not regexp. (The command minPos returns the numerical value of the start position of the document. This is always 0 in AlphaX, but it is 1.0 in AlphaTk, because it uses the Tk text rendering engine. By using minPos instead of 0, you make sure the script will work cross platform.) If no match is found an error is raised. The -- means 'end of options' for search, and is only needed if the $findPat starts with a hyphen. (The eval command has the effect of 'unpacking' the list returned by search into two arguments, as required by replaceText, and the empty string "" is protected by {} in order not to be evaluated.))

 
    lindex [pos::toRowCol [getPos]] 0

(getPos gives the cursor position in chars; pos::toRowCol translates this into a two-element list consisting of line number and column number, and lindex picks out entry 0 of this list.)

 
    file::gotoLine fileName lineNumber

resp.

 
    file::gotoLine [win::Current] lineNumber

This command returns the path of the file. The file does not have to be open in advance.

 
    typeText string

or

 
    insertText string

The two versions are nearly identical. The difference is that typeText trigger a characterInsertedHook for each character in string. This can for example trigger a hard wrap, an auto-indentation, a spellcheck action, or whatever fancy package is active


How to build such scripts in AppleScript

As an example, suppose we have two applescript variables

 
    searchString
    replaceString

and we want to search and replace in the frontmost Alpha window. The naive solution would be something like

 
    tell application "AlphaX"
        DoScript "eval replaceText " & "[search -r 0  " & searchString & " " 
& "[minPos]]" & " " & "{" & replaceString & "}"
    end tell

(The & string concatenation operator is needed in order to build a string mixing constants and variable values.)

However this will fail if the value of one of the applescript variables is the empty string or a string starting with '{' and ending with '}'. The empty string will disappear (instead of being represented by ""), and the surrounding braces will be interpreted as a token delimiter by Tcl.

One solution is to wrap the string representation of the values of searchString and replaceString with quotes or braces -- these will then be stripped off by the Tcl delimiter, and hence they protect the string itself. It seems to be best to use braces. Like this:

 
    tell application "AlphaX"
        DoScript "eval replaceText " & "[search -r 0  {" & searchString & "} " 
& "[minPos]]" & " " & "{{" & replaceString & "}}"
    end tell

Looks ugly, doesn't it? But it is probably better that the first version, at least the variables may now be the empty string or something embraced. It is not the perfect solution, however, for the Tcl interpreter will still choke if one of the strings contains unmatched braces, brackets, or quotes...

There was a longer discussion of such issues on OSX-TeX. Let me just quote what Jon wrote, since it also leads to the next section of this page:

<quote> ... In the end, Tcl's quoting rules are fundamentally different from AppleScript's, which are fundamentally different from the shell's, which are fundamentally different from Python's, which are fundamentally different...

I think your only solutions are to either come up with the AppleScript equivalent of Alpha's

 
    proc quote::Insert str {
        regsub -all {[][\\$"\{\}]} $str {\\&} str
        string map [list "\r" "\\r" "\n" "\\n" "\t" "\\t"] $str
    }

or for us to give you a proper AppleScript interface.

The first choice assumes that AppleScript has access to the same string mangling facilities that Alpha does, which, short of a regular expression OSAX (which do exist, I know), I don't think it does. Languages like Tcl and Perl are fundamentally better at string handling than AppleScript is.

The second choice involves some things that Alpha already has, and some careful thought about what the crucial set and syntax of AppleScript commands should be. Alpha has thousands of Tcl commands, and there's no point in trying to replicate them all for AppleScripters, but I think we're going to be better off providing a proper AppleScript interface to at least basic information about windows, window state, as well as providing mechanisms for selecting and replacing text.</quote>


How to write true event handlers

This is something Jon wrote:

 
    tclAE::installEventHandler core getd aeom::handleGet

    proc aeom::handleGet {theAppleEvent theReplyAE} {
	 set objDesc [tclAE::getKeyDesc $theAppleEvent ----]
	 set token [tclAE::resolve $objDesc]
	 switch -- [tclAE::getDescType $token] {
	     "CHAR" {
		 set text [eval getText [tclAE::getData $token]]
		 tclAE::putKeyData $theReplyAE ---- utf8 $text
	     }
	     "WIND" {
		 tclAE::putKeyDesc $theReplyAE ---- $objDesc
	     }
	     "FILE" {
		 set fileDesc [tclAE::build::path [tclAE::getData $token]]
		 tclAE::putKeyDesc $theReplyAE ---- $fileDesc
	     }
	     default {
		 error -code 接1708
	     }
	 }
    }

    tclAE::installObjectAccessor prop WIND aeom::accessor::prop<WIND

    proc aeom::accessor::prop<WIND {desiredClass containerToken containerClass keyForm keyData theToken} {
	 set win [tclAE::getData $containerToken ****]

	 switch -- [tclAE::getData $keyData type] {
	     "file" {
		 if {[win::IsFile $win file]} {
		     tclAE::replaceDescData $theToken FILE $file
		 } else {
		     error -code 接1728
		 }
	     }
	     default {
		 error -code 接1708
	     }
	 }
    }

    # fixes a bug in the shipping version
    proc aeom::accessor::cwin<null {desiredClass containerToken 
    containerClass keyForm keyData theToken} {
	 set wins [winNames]

	 switch -- $keyForm {
	     "name" {
		 set winNum [lsearch $wins [tclAE::getData $keyData TEXT]]
		 if {$winNum < 0} {
		     error -code 接1728
		 }
	     }
	     "indx" {
		 # absolute positions are 1-based
		 set winNum [expr {[tclAE::getData $keyData long] - 1}]

		 if {($winNum >= [llength $wins]) || ($winNum < 0)} {
		     error -code 接1728
		 }
	     }
	     default {
		 error -code 接1708
	     }
	 }
	 tclAE::replaceDescData $theToken WIND [lindex [winNames -f] $winNum]
    }

At least at present, you can't write

get POSIX path of file of front document

I'm not sure whether this is a failing in the AppleEvent Object Model (I think so) or a problem in Alpha's AEOM resolution code.

Retrieved from http://alphatcl.sourceforge.net/wiki/pmwiki.php/Main/AppleScriptingAlpha
Page last modified on November 01, 2007, at 02:52 AM