;;; applescript-mode.el --- major mode for editing AppleScript source ;; Copyright (C) 2004 MacEmacs JP Project ;;; Credits: ;; Copyright (C) 2003,2004 FUJIMOTO Hisakuni ;; http://www.fobj.com/~hisa/w/applescript.el.html ;; Copyright (C) 2003 443,435 (Public Domain?) ;; http://pc.2ch.net/test/read.cgi/mac/1034581863/ ;; Copyright (C) 2004 Harley Gorrell ;; http://www.mahalito.net/~harley/elisp/osx-osascript.el ;; Author: sakito ;; Keywords: languages, tools (defconst applescript-mode-version "$Revision: 1.4 $" "The current version of the AppleScript mode.") (defconst applescript-mode-help-address "sakito@users.sourceforge.jp" "Address accepting submission of bug reports.") ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; AppleScript Mode ;;; Usage: ;; To use applescript-mode.el put the following line in your .emacs: ;; (autoload 'applescript-mode "applescript-mode" "major mode for editing AppleScript source." t) ;; (setq auto-mode-alist ;; (cons '("\\.applescript$" . applescript-mode) auto-mode-alist) ;; ) ;; Please use the SourceForge MacEmacs JP Project to submit bugs or ;; patches: ;; ;; http://sourceforge.jp/projects/macemacsjp ;; FOR MORE INFORMATION: ;; There is some information on applescript-mode.el at ;; http://macemacsjp.sourceforge.jp/documents/applescript-mode/ ;;; Code: ;; user customize variables (defgroup applescript nil "Support for the AppleScript, " :group 'languages :prefix "as-") (defcustom as-osascript-command "osascript" "*execute AppleScripts and other OSA language scripts." :type 'string :group 'applescript) (defcustom as-osacompile-command "osacompile" "*compile AppleScripts and other OSA language scripts." :type 'string :group 'applescript) (defcustom as-osascript-command-args '("-ss") "*List of string arguments to be used when starting a osascript." :type '(repeat string) :group 'applescript) (defcustom as-indent-offset 4 "*Amount of offset per level of indentation. `\\[as-guess-indent-offset]' can usually guess a good value when you're editing someone else's AppleScript code." :type 'integer :group 'applescript) (defcustom as-continuation-offset 4 "*Additional amount of offset to give for some continuation lines. Continuation lines are those that immediately follow a Continuation sign terminated line. Only those continuation lines for a block opening statement are given this extra offset." :type 'integer :group 'applescript) ;; Face Setting ;; Face for true, false, me, it (defvar as-pseudo-keyword-face 'as-pseudo-keyword-face "Face for pseudo keywords in AppleScript mode, like me, true, false.") (make-face 'as-pseudo-keyword-face) ;; Face for command (defvar as-command-face 'as-command-face "Face for command like copy, get, set, and error.") (make-face 'as-command-face) (defun as-font-lock-mode-hook () (or (face-differs-from-default-p 'as-pseudo-keyword-face) (copy-face 'font-lock-keyword-face 'as-pseudo-keyword-face)) (or (face-differs-from-default-p 'as-command-face) (copy-face 'font-lock-keyword-face 'as-command-face)) ) (add-hook 'font-lock-mode-hook 'as-font-lock-mode-hook) (defvar applescript-font-lock-keywords (let ( ;; expressions and control Statements (kw1 (mapconcat 'identity '("and" "app" "application" "considering" "div" "else" "end" "exit" "is" "mod" "not" "on" "or" "if" "ignoring" "reopen" "repeat" "tell" "then" "to" "using[ \t]terms[ \t]from" "with[ \t]timeout" "with[ \t]transaction" ) "\\|")) ;; commands (kw2 (mapconcat 'identity '("ASCII[ \t]character" "ASCII[ \t]number" "activate" "AGStart" "beep" "copy" "count" "choose[ \t]application" "choose[ \t]file" "choose[ \t]folder" "close[ \t]access" "current[ \t]date" "display[ \t]dialog" "get" "get[ \t]EOF" "info[ \t]for" "launch" "list[ \t]disks" "list[ \t]folder" "load[ \t]script" "log" "monitor[ \t]depth" "max[ \t]monitor[ \t]depth" "min[ \t]monitor[ \t]depth" "new[ \t]file" "offset" "open[ \t]for[ \t]access" "path[ \t]to" "random[ \t]number" "read" "round" "run" "run[ \t]script" "scripting[ \t]component" "set" "set[ \t]EOF" "set[ \t]monitor[ \t]depth" "set[ \t]volume" "start[ \t]log" "stop[ \t]log" "store[ \t]script" "time[ \t]to[ \t]GMT" "write" ) "\\|")) ;; misc (kw3 (mapconcat 'identity '("buttons" "default[ \t]answer" "default[ \t]button" "to[ \t]begining[ \t]of" "to[ \t]word" "starting[ \t]at" "with[ \t]icon" "write[ \t]permission" ) "\\|")) ) (list ;; keywords (cons (concat "\\b\\(" kw1 "\\)\\b[ \n\t(]") 1) (cons (concat "\\b\\(" kw2 "\\)\\b[ \n\t(]") 1) ;; kw3 not yet.. (cons (concat "\\b\\(" kw3 "\\)\\b[ \n\t(]") 1) ;; functions '("\\bon[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" 1 font-lock-function-name-face) '("\\bto[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" 1 font-lock-function-name-face) ;; pseudo-keywords '("\\b\\(it\\|me\\|my\\|true\\|false\\)\\b" 1 as-pseudo-keyword-face) ) )) (put 'applescript-mode 'font-lock-defaults '(applescript-font-lock-keywords)) ;; Major mode boilerplate ;; define a mode-specific abbrev table for those who use such things (defvar applescript-mode-abbrev-table nil "Abbrev table in use in `applescript-mode' buffers.") (define-abbrev-table 'applescript-mode-abbrev-table nil) (defvar applescript-mode-hook nil "*Hook called by `applescript-mode'.") (defvar as-mode-map () "Keymap used in `applescript-mode' buffers.") (if as-mode-map nil (setq as-mode-map (make-sparse-keymap)) ;; Key bindings ;; subprocess commands (define-key as-mode-map "\C-c\C-c" 'as-execute-buffer) (define-key as-mode-map "\C-c\C-s" 'as-execute-string) (define-key as-mode-map "\C-c|" 'as-execute-region) ;; Miscellaneous (define-key as-mode-map "\C-c;" 'comment-region) (define-key as-mode-map "\C-c:" 'uncomment-region) ;; information ;(define-key as-mode-map "\C-c\C-v" 'as-mode-version) ) (defvar as-mode-syntax-table nil "Syntax table used in `applescript-mode' buffers.") (when (not as-mode-syntax-table) (setq as-mode-syntax-table (make-syntax-table)) (modify-syntax-entry ?\" "\"" as-mode-syntax-table) (modify-syntax-entry ?: "." as-mode-syntax-table) (modify-syntax-entry ?\; "." as-mode-syntax-table) (modify-syntax-entry ?& "." as-mode-syntax-table) (modify-syntax-entry ?\| "." as-mode-syntax-table) (modify-syntax-entry ?+ "." as-mode-syntax-table) (modify-syntax-entry ?/ "." as-mode-syntax-table) (modify-syntax-entry ?= "." as-mode-syntax-table) (modify-syntax-entry ?< "." as-mode-syntax-table) (modify-syntax-entry ?> "." as-mode-syntax-table) (modify-syntax-entry ?$ "." as-mode-syntax-table) (modify-syntax-entry ?\[ "." as-mode-syntax-table) (modify-syntax-entry ?\] "." as-mode-syntax-table) (modify-syntax-entry ?\{ "." as-mode-syntax-table) (modify-syntax-entry ?\} "." as-mode-syntax-table) (modify-syntax-entry ?. "." as-mode-syntax-table) (modify-syntax-entry ?\\ "." as-mode-syntax-table) (modify-syntax-entry ?\' "." as-mode-syntax-table) ;; a double hyphen starts a comment (modify-syntax-entry ?- ". 12" as-mode-syntax-table) ;; comment delimiters (modify-syntax-entry ?\f "> " as-mode-syntax-table) (modify-syntax-entry ?\n "> " as-mode-syntax-table) ;; define parentheses to match (modify-syntax-entry ?\( "()1" as-mode-syntax-table) (modify-syntax-entry ?\) ")(4" as-mode-syntax-table) (modify-syntax-entry ?* ". 23b" as-mode-syntax-table) ) ;; Utilities (defmacro as-safe (&rest body) "Safely execute BODY, return nil if an error occurred." (` (condition-case nil (progn (,@ body)) (error nil)))) (defsubst as-keep-region-active () "Keep the region active in XEmacs." ;; Ignore byte-compiler warnings you might see. Also note that ;; FSF's Emacs 19 does it differently; its policy doesn't require us ;; to take explicit action. (when (featurep 'xemacs) (and (boundp 'zmacs-region-stays) (setq zmacs-region-stays t)))) (defsubst as-point (position) "Returns the value of point at certain commonly referenced POSITIONs. POSITION can be one of the following symbols: bol -- beginning of line eol -- end of line bod -- beginning of on or to eod -- end of on or to bob -- beginning of buffer eob -- end of buffer boi -- back to indentation bos -- beginning of statement This function does not modify point or mark." (let ((here (point))) (cond ((eq position 'bol) (beginning-of-line)) ((eq position 'eol) (end-of-line)) ((eq position 'bod) (as-beginning-of-handler 'either)) ((eq position 'eod) (as-end-of-handler 'either)) ;; Kind of funny, I know, but useful for as-up-exception. ((eq position 'bob) (beginning-of-buffer)) ((eq position 'eob) (end-of-buffer)) ((eq position 'boi) (back-to-indentation)) ((eq position 'bos) (as-goto-initial-line)) (t (error "Unknown buffer position requested: %s" position)) ) (prog1 (point) (goto-char here)))) ;; Menu definitions, only relevent if you have the easymenu.el package ;; (standard in the latest Emacs 19 and XEmacs 19 distributions). (defvar as-menu nil "Menu for AppleScript Mode. This menu will get created automatically if you have the `easymenu' package. Note that the latest X/Emacs releases contain this package.") (and (as-safe (require 'easymenu) t) (easy-menu-define as-menu as-mode-map "AppleScript Mode menu" '("AppleScript" ["Comment Out Region" comment-region (mark)] ["Uncomment Region" uncomment-region (mark)] "-" ["Execute buffer" as-execute-buffer t] ["Execute region" as-execute-region (mark)] ["Execute string" as-execute-string t] "-" ["Mode Version" as-mode-version t] ["AppleScript Version" as-language-version t] ))) ;;;###autoload (defun applescript-mode () "Major mode for editing AppleScript files." (interactive) ;; set up local variables (kill-all-local-variables) (make-local-variable 'font-lock-defaults) (make-local-variable 'paragraph-separate) (make-local-variable 'paragraph-start) (make-local-variable 'require-final-newline) (make-local-variable 'comment-start) (make-local-variable 'comment-end) (make-local-variable 'comment-start-skip) (make-local-variable 'comment-column) (make-local-variable 'comment-indent-function) (make-local-variable 'indent-region-function) (make-local-variable 'indent-line-function) (make-local-variable 'add-log-current-defun-function) (make-local-variable 'fill-paragraph-function) ;; ;; Maps and tables (use-local-map as-mode-map) (set-syntax-table as-mode-syntax-table) ;; Names (setq major-mode 'applescript-mode mode-name "AppleScript" local-abbrev-table applescript-mode-abbrev-table font-lock-defaults '(applescript-font-lock-keywords) paragraph-separate "[ \t\n\f]*$" paragraph-start "[ \t\n\f]*$" require-final-newline t comment-start "-- " comment-end "" comment-start-skip "---*[ \t]*" comment-column 40 ) ;; Support for outline-minor-mode (set (make-local-variable 'outline-regexp) "\\([ \t]*\\(on\\|to\\|if\\|repeat\\|tell\\|end\\)\\|--\\)") (set (make-local-variable 'outline-level) 'as-outline-level) ;; add the menu (if as-menu (easy-menu-add as-menu)) ;; Run the mode hook. Note that applescript-mode-hook is deprecated. (if applescript-mode-hook (run-hooks 'applescript-mode-hook) (run-hooks 'applescript-mode-hook)) ) (when (not (or (rassq 'applescript-mode auto-mode-alist) (push '("\\.applescript$" . applescript-mode) auto-mode-alist)))) ;;; Subprocess commands ;; function use variables (defconst as-output-buffer "*AppleScript Output*" "Name for the buffer to display AppleScript output.") (make-variable-buffer-local 'as-output-buffer) ;; Code execution commands (defun as-execute-buffer (&optional async) "Execute the whole buffer as an Applescript" (interactive "P") (as-execute-region (point-min) (point-max) async)) (defun as-execute-string (string &optional async) "Send the argument STRING to an AppleScript." (interactive "sExecute AppleScript: ") (save-excursion (set-buffer (get-buffer-create (generate-new-buffer-name as-output-buffer))) (insert string) (as-execute-region (point-min) (point-max) async))) (defun as-execute-region (start end &optional async) "Execute the region as an Applescript" (interactive "r\nP") (let ((region (buffer-substring start end)) (as-current-win (selected-window))) (pop-to-buffer as-output-buffer) (insert (as-execute-code region)) (select-window as-current-win) )) (defun as-execute-code (code) "pop to the AppleScript buffer, run the code and display the results." (as-decode-string (do-applescript (as-string-to-sjis-string-with-escape code)))) (defun as-mode-version () "Echo the current version of `applescript-mode' in the minibuffer." (interactive) (message "Using `applescript-mode' version %s" applescript-mode-version) (as-keep-region-active)) (defun as-language-version() "Echo the current version of AppleScript Version in the minibuffer." (interactive) (message "Using AppleScript version %s" (as-execute-code "AppleScript's version")) (as-keep-region-active)) ;; as-beginning-of-handler, as-end-of-handler,as-goto-initial-line not yet ;; Support for outline.el (defun as-outline-level () "This is so that `current-column` DTRT in otherwise-hidden text" (let (buffer-invisibility-spec) (save-excursion (back-to-indentation) (current-column)))) ;; for Japanese (defun as-unescape-string (str) "delete escape char '\'" (replace-regexp-in-string "\\\\\\(.\\)" "\\1" str)) (defun as-escape-string (str) "add escape char '\'" (replace-regexp-in-string "\\\\" "\\\\\\\\" str)) (defun as-sjis-byte-list-escape (lst) (cond ((null lst) nil) ((= (car lst) 92) (append (list 92 (car lst)) (as-sjis-byte-list-escape (cdr lst)))) (t (cons (car lst) (as-sjis-byte-list-escape (cdr lst)))))) (defun as-string-to-sjis-string-with-escape (str) "String convert to SJIS, and escape \"\\\" " (apply 'string (as-sjis-byte-list-escape (string-to-list (as-encode-string str))))) (defun as-decode-string (str) "do-applescript return values decode sjis-mac" (decode-coding-string str 'sjis-mac)) (defun as-encode-string (str) "do-applescript return values encode sjis-mac" (encode-coding-string str 'sjis-mac)) (defun as-parse-result (retstr) "String convert to Emacs Lisp Object" (cond ;; as list ((string-match "\\`\\s-*{\\(.*\\)}\\s-*\\'" retstr) (let ((mstr (match-string 1 retstr))) (mapcar (lambda (item) (as-parse-result item)) (split-string mstr ",")))) ;; AERecord item as cons ((string-match "\\`\\s-*\\([^:\\\"]+\\):\\(.*\\)\\s-*\\'" retstr) (cons (intern (match-string 1 retstr)) (as-parse-result (match-string 2 retstr)))) ;; as string ((string-match "\\`\\s-*\\\"\\(.*\\)\\\"\\s-*\\'" retstr) (as-decode-string (as-unescape-string (match-string 1 retstr)))) ;; as integer ((string-match "\\`\\s-*\\([0-9]+\\)\\s-*\\'" retstr) (string-to-int (match-string 1 retstr))) ;; else (t (intern retstr)))) (provide 'applescript-mode) ;;; applescript-mode.el ends here