home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2005 August / PCpro_2005_08.ISO / files / freeware / autohot / AutoHotkeyInstall.exe / Extras / ahk-mode.el < prev    next >
Encoding:
Text File  |  2005-03-22  |  14.8 KB  |  462 lines

  1. ;;; ahk-mode.el --- major mode for editing AutoHotKey scripts for X/GNU Emacs
  2.  
  3. ;; Copyright (C) 2005 Robert Widhopf-Fenk
  4.  
  5. ;; Author:   Robert Widhopf-Fenk
  6. ;; Keywords: AutoHotKey, major mode
  7. ;; X-URL:    http://www.robf.de/Hacking/elisp
  8. ;; arch-tag: 1ae180cb-002e-4656-bd9e-a209acd4a3d4
  9. ;; Version:  $Id: ahk-mode--main--0.1--patch-17$
  10.  
  11. ;; This code is free software; you can redistribute it and/or modify
  12. ;; it under the terms of the GNU General Public License as published by
  13. ;; the Free Software Foundation; either version 2, or (at your option)
  14. ;; any later version.
  15. ;;
  16. ;; This program is distributed in the hope that it will be useful,
  17. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. ;; GNU General Public License for more details.
  20. ;;
  21.  
  22. ;;; Commentary:
  23. ;;
  24. ;; AutoHotKey: Automation, Hotkeys and Scripting for Windows at
  25. ;; http://www.autohotkey.com/ is a cool tool to make daily life
  26. ;; with Windows easier or even fun!
  27. ;;
  28. ;; This is a X/GNU Emacs mode for editing AutoHotKey scripts.
  29. ;;
  30. ;; Place this file somewhere in your load-path, byte-compile it and add the
  31. ;; following line to your ~/.xemacs/init.el resp. ~/.emacs:
  32. ;;   (require 'ahk-mode)
  33. ;; 
  34. ;; The first time ahk-mode.el is started it will ask you for the path to the
  35. ;; Syntax directory which you will find in the subdirectory of your AHK
  36. ;; installation.
  37. ;;
  38. ;; For example if you installed AHK at C:\Programms\AutoHotKey it will be
  39. ;; C:/Programms/AutoHotKey/Extras/Editors/Syntax !
  40. ;;
  41. ;; When opening a script file you will get:
  42. ;; - syntax highlighting
  43. ;; - indention, completion and command help (bound to "TAB")
  44. ;; - insertion of command templates (bound to "C-c C-i") 
  45. ;; - electric braces (typing "{" will also insert "}" and place point in
  46. ;;   between) 
  47. ;;
  48. ;; Please send bug-reports or feature suggestions to hackATrobfDOTde.
  49.  
  50. ;;; Bugs:
  51. ;;
  52. ;; - completions is not context aware
  53. ;; - there should be a way to lookup the web docs from within Emacs 
  54. ;; - multi-line comments are not fontified on the fly 
  55.  
  56. ;;; History:
  57. ;;
  58. ;; The CHANGELOG is stored in my arch repository.
  59. ;; If you wonder what arch is, take a look at http://wiki.gnuarch.org/ !
  60.  
  61. (eval-when-compile
  62.   (require 'font-lock)
  63.   (require 'cl))
  64.  
  65. ;;; Code:
  66. (defgroup ahk-mode nil
  67.   "A mode for AutoHotKey"
  68.   :group 'languages
  69.   :prefix "ahk-")
  70.  
  71. (defcustom ahk-mode-hook nil
  72.   "Hook run by `ahk-mode'."
  73.   :type 'hook
  74.   :group 'ahk-mode)
  75.  
  76. (defcustom ahk-indetion 2
  77.   "The indetion level."
  78.   :type 'integer
  79.   :group 'ahk-mode)
  80.  
  81. (defcustom ahk-syntax-directory nil
  82.   "The indetion level."
  83.   :type 'directory
  84.   :group 'ahk-mode)
  85.  
  86. ;;;###autoload
  87. (add-to-list 'auto-mode-alist
  88.              '("\\.ahk$"  . ahk-mode))
  89.  
  90. (defvar ahk-mode-syntax-table
  91.   (let ((table (make-syntax-table)))
  92.     ;; these are also allowed in variable names
  93.     (modify-syntax-entry ?#  "w" table)
  94.     (modify-syntax-entry ?_  "w" table)
  95.     (modify-syntax-entry ?@  "w" table)
  96.     (modify-syntax-entry ?$  "w" table)
  97.     (modify-syntax-entry ??  "w" table)
  98.     (modify-syntax-entry ?[  "w" table)
  99.     (modify-syntax-entry ?]  "w" table)
  100.     ;; some additional characters used in paths and switches  
  101.     (modify-syntax-entry ?\\  "w" table)
  102.     (modify-syntax-entry ?/  "w" table)
  103.     (modify-syntax-entry ?-  "w" table)
  104.     (modify-syntax-entry ?:  "w" table)
  105.     (modify-syntax-entry ?.  "w" table)
  106.     ;; for multiline comments (taken from cc-mode)
  107.     (modify-syntax-entry ?*  ". 23"   table)
  108.     ;; Give CR the same syntax as newline, for selective-display
  109. ;    (modify-syntax-entry ?\^m "> b" table)
  110. ;    (modify-syntax-entry ?\n "> b"  table)
  111.     table)
  112.   "Syntax table used in `ahk-mode' buffers.")
  113.  
  114. (defvar ahk-mode-abbrev-table
  115.   (let ((a (make-abbrev-table)))
  116.     a)
  117.   "Abbreviation table used in `ahk-mode' buffers.")
  118.  
  119. (defvar ahk-mode-map
  120.   (let ((map (make-sparse-keymap)))
  121.     (define-key map "\C-c\C-c" 'comment-region)
  122.     (define-key map "\C-c\C-i" 'ahk-insert-command-template)
  123.     (define-key map "\t" 'ahk-indent-line-and-complete)
  124.     (define-key map "{" 'ahk-electric-brace)
  125.     (define-key map "}" 'ahk-electric-brace)
  126.     (define-key map "\r" 'ahk-electric-return)
  127.     map)
  128.   "Keymap used in `ahk-mode' buffers.")
  129.  
  130. (defvar ahk-Commands-list nil
  131.   "A list of ahk commands and parameters.
  132. Will be initialized by `ahk-init'")
  133.  
  134. (defvar ahk-Keys-list nil
  135.   "A list of ahk key names.
  136. Will be initialized by `ahk-init'")
  137.  
  138. (defvar ahk-Keywords-list nil
  139.   "A list of ahks keywords.
  140. Will be initialized by `ahk-init'")
  141.  
  142. (defvar ahk-Variables-list nil
  143.   "A list of ahks variables.
  144. Will be initialized by `ahk-init'")
  145.  
  146. (defvar ahk-mode-font-lock-keywords nil
  147.   "Syntax highlighting for `ahk-mode'.
  148. Will be initialized by `ahk-init'")
  149.  
  150. (defvar ahk-completion-list nil
  151.   "A list of all symbols available for completion
  152. Will be initialized by `ahk-init'")
  153.  
  154.  
  155. ;(easy-menu-define ahk-menu ahk-mode-map "AHK Mode Commands"
  156. ;          (cons "AHK" ("0.1" ahk-mode-menu ahk)))
  157.  
  158. (defun ahk-init ()
  159.   "Initialize ahk-mode variables.
  160. An AHK installation provides a subdirectory \"Extras/Editors/Syntax\"
  161. containing a list of keywords, variables, commands and keys.
  162.  
  163. This directory must be specified in the variable `ahk-syntax-directory'."
  164.   (interactive)
  165.  
  166.   (message "Initializing ahk-mode variables ...")
  167.   (when (null ahk-syntax-directory)
  168.     (customize-save-variable
  169.      'ahk-syntax-directory
  170.      (read-file-name "Please give the AHK-Syntax directory: "))
  171.     (custom-save-all))
  172.  
  173.   (save-excursion
  174.     (set-buffer (get-buffer-create " *ahk-mode-temp*"))
  175.   
  176.     ;; read commands
  177.     (erase-buffer)
  178.     (insert-file-contents (expand-file-name "Commands.txt"
  179.                                             ahk-syntax-directory))
  180.     (setq ahk-Commands-list nil)
  181.     (goto-char 0)
  182.     (while (not (eobp))
  183.       (if (not (looking-at "\\([^;\r\n][^\t\r\n, ]+\\)\\([^\r\n]*\\)"))
  184.           nil;; (error "Unknown file syntax")
  185.         (setq ahk-Commands-list (cons (list
  186.                                        (match-string 1)
  187.                                        (match-string 2))
  188.                                       ahk-Commands-list)))
  189.       (forward-line 1))
  190.     
  191.     ;; read keys
  192.     (erase-buffer)
  193.     (insert-file-contents (expand-file-name "Keys.txt"
  194.                                             ahk-syntax-directory))
  195.     (setq ahk-Keys-list nil)
  196.     (goto-char 0)
  197.     (while (not (eobp))
  198.       (if (not (looking-at "\\([^;\r\n][^\t\r\n ]+\\)"))
  199.           nil;; (error "Unknown file syntax of Keys.txt")
  200.         (setq ahk-Keys-list (cons (match-string 1) ahk-Keys-list)))
  201.       (forward-line 1))
  202.     
  203.     ;; read keywords
  204.     (erase-buffer)
  205.     (insert-file-contents (expand-file-name "Keywords.txt"
  206.                                             ahk-syntax-directory))
  207.     (setq ahk-Keywords-list nil)
  208.     (goto-char 0)
  209.     (while (not (eobp))
  210.       (if (not (looking-at "\\([^;\r\n][^\t\r\n ]+\\)"))
  211.           nil;; (error "Unknown file syntax of Keywords.txt")
  212.         (setq ahk-Keywords-list (cons (match-string 1) ahk-Keywords-list)))
  213.       (forward-line 1))
  214.     ;; read variables
  215.     (erase-buffer)
  216.     (insert-file-contents (expand-file-name "Variables.txt"
  217.                                             ahk-syntax-directory))
  218.     (setq ahk-Variables-list nil)
  219.     (goto-char 0)
  220.     (while (not (eobp))
  221.       (if (not (looking-at "\\([^;\r\n][^\t\r\n]+\\)"))
  222.           nil;; (error "Unknown file syntax of Variables.txt")
  223.         (setq ahk-Variables-list (cons (match-string 1) ahk-Variables-list)))
  224.       (forward-line 1))
  225.   
  226.     ;; built completion list
  227.     (setq ahk-completion-list
  228.           (mapcar (lambda (c) (list c))
  229.                   (append (mapcar 'car ahk-Commands-list)
  230.                           ahk-Keywords-list
  231.                           ahk-Variables-list
  232.                           ahk-Keys-list)))
  233.  
  234.     (setq ahk-mode-font-lock-keywords
  235.           (list
  236.            '("\\s-*;.*$" .
  237.              font-lock-comment-face)
  238.            '("^/\\*\\(.*\r?\n\\)*\\(\\*/\\)?" .
  239.              font-lock-comment-face)
  240.            '("^\\([^ \t\n:]+\\):" .
  241.              (1 font-lock-builtin-face))
  242.            '("[^, %\"]*%[^% ]+%" .
  243.              font-lock-variable-name-face)
  244.            ;; I get an error when using regexp-opt instead of simply
  245.            ;; concatenating the keywords and I do not understand why ;-(
  246.            ;; (warning/warning) Error caught in `font-lock-pre-idle-hook': (invalid-regexp Invalid preceding regular expression)
  247.            (cons
  248.             (concat "\\b\\("
  249.                     (mapconcat 'regexp-quote ahk-Variables-list "\\|")
  250.                     "\\)\\b")
  251.             'font-lock-variable-name-face)
  252.            (list
  253.             (concat "\\(^[ \t]*\\|::[ \t]*\\)\\("
  254.                     (mapconcat 'regexp-quote (mapcar 'car ahk-Commands-list) "\\|")
  255.                     "\\)")
  256.             2
  257.             'font-lock-function-name-face)
  258.            (cons
  259.             (concat "\\b\\("
  260.                     (mapconcat 'regexp-quote ahk-Keywords-list "\\|")
  261.                     "\\)\\b")
  262.             'font-lock-keyword-face)
  263.            (cons
  264.             (concat "\\b\\("
  265.                     (mapconcat 'regexp-quote ahk-Keys-list "\\|")
  266.                     "\\)\\b")
  267.             'font-lock-constant-face)
  268.            )))
  269.   
  270.   (message "Initializing ahk-mode variables done."))
  271.  
  272. ;;;###autoload
  273. (defun ahk-mode ()
  274.   "Major mode for editing AutoHotKey Scripts.
  275.  
  276. The hook `ahk-mode-hook' is run at mode initialization.
  277.  
  278. Key bindings:
  279. \\{ahk-mode-map}"
  280.   (interactive)
  281.   (if (null ahk-Commands-list)
  282.       (ahk-init))
  283.   (kill-all-local-variables)
  284.   (set-syntax-table ahk-mode-syntax-table)
  285.   (setq major-mode 'ahk-mode
  286.     mode-name "AHK"
  287.     local-abbrev-table ahk-mode-abbrev-table
  288.     abbrev-mode t
  289.         indent-region-function 'ahk-indent-region)
  290.   (put 'ahk-mode 'font-lock-defaults '(ahk-mode-font-lock-keywords t))
  291.   (put 'ahk-mode 'font-lock-keywords-case-fold-search t)
  292.  
  293.   (when (not (featurep 'xemacs))
  294.     (setq font-lock-defaults '(ahk-mode-font-lock-keywords))
  295.     (setq font-lock-keywords-case-fold-search t))
  296.   
  297.   (use-local-map ahk-mode-map)
  298. ;  (easy-menu-add ahk-menu)
  299.   (setq comment-start ";")
  300.   (font-lock-mode 1)
  301.   (force-mode-line-update)
  302.   (run-hooks 'ahk-mode-hook))
  303.  
  304. (defun ahk-indent-line ()
  305.   "Indent the current line."
  306.   (interactive)
  307.  
  308.   (let ((indent 0)
  309.         (case-fold-search t))
  310.     ;; do a backward search to determin the indention level
  311.     (save-excursion
  312.       (beginning-of-line)
  313.       (skip-chars-backward " \t\n")
  314.       (beginning-of-line)
  315.       (if (looking-at "^[^: ]+:")
  316.           (if (looking-at "^[^: ]+:\\([^:]*:\\)?[ \t]*$")
  317.               (setq indent ahk-indetion)
  318.             (setq indent 0))
  319.         (if (looking-at "^\\([ \t]*\\){")
  320.             (setq indent (+ (length (match-string 1)) ahk-indetion))
  321.           (if (looking-at "^\\([ \t]*\\)")
  322.               (setq indent (+ (length (match-string 1))))))))
  323.     ;; check for special tokens
  324.     (save-excursion
  325.       (beginning-of-line)
  326.       (if (looking-at "^\\([ \t]*\\)\\}")
  327.           (setq indent (- indent ahk-indetion))
  328.         (if (or (looking-at "^[ \t]*[^,: \t\n]*:")
  329.                 (and (looking-at "^\\([ \t]*\\)\\(Return\\|Exit\\)")
  330.                      (or (<= (length (match-string 1)) ahk-indetion)
  331.                          (= indent ahk-indetion)))
  332.                 (looking-at "^;;;"))
  333.             (setq indent 0))))
  334.     (let ((p (point-marker)))
  335.       (beginning-of-line)
  336.       (if (looking-at "^[ \t]+")
  337.           (replace-match ""))
  338.       (indent-to indent)
  339.       (goto-char p)
  340.       (set-marker p nil)
  341.       (if (bolp)
  342.           (goto-char (+ (point) indent))))))
  343.  
  344. (defun ahk-indent-region (start end)
  345.   "Indent lines in region START to END."
  346.   (interactive "r")
  347.   (save-excursion
  348.     (goto-char end)
  349.     (setq end (point-marker))
  350.     (goto-char start)
  351.     (while (< (point) end)
  352.       (beginning-of-line)
  353.       (ahk-indent-line)
  354.       (forward-line 1))
  355.     (ahk-indent-line)
  356.     (set-marker end nil)))
  357.   
  358. (defun ahk-complete ()
  359.   "Indent current line when at the beginning or complete current command."
  360.   (interactive)
  361.  
  362.   (if (looking-at "\\w+")
  363.       (goto-char (match-end 0)))
  364.   
  365.   (let ((end (point)))
  366.     (if (and (or (save-excursion (re-search-backward "\\<\\w+"))
  367.                  (looking-at "\\<\\w+"))
  368.              (= (match-end 0) end))
  369.         (let ((start (match-beginning 0))
  370.               (prefix (match-string 0))
  371.               (completion-ignore-case t)
  372.               completions)
  373.           (setq completions (all-completions prefix ahk-completion-list))
  374.           (if (eq completions nil)
  375.               nil;(error "Unknown command prefix <%s>!" prefix)
  376.             (if (> (length completions) 1)
  377.                 (setq completions
  378.                       (completing-read "Complete command: "
  379.                                        (mapcar (lambda (c) (list c))
  380.                                                completions)
  381.                                        nil t prefix)))
  382.             (if (stringp completions)
  383.                 ;; this is a trick to upcase "If" and other prefixes
  384.                 (let ((c (try-completion completions ahk-completion-list)))
  385.                   (if (stringp c)
  386.                       (setq completions c))))
  387.             
  388.             (delete-region start end)
  389.             (if (listp completions) (setq completions (car completions)))
  390.             (insert completions)
  391.             (let ((help (assoc completions ahk-Commands-list)))
  392.               (if help (message "%s" (mapconcat 'identity help ""))))
  393.             )))))
  394.  
  395. (defun ahk-indent-line-and-complete ()
  396.   "Combines indetion and completion."
  397.   (interactive)
  398.   (ahk-indent-line)
  399.   (ahk-complete))
  400.  
  401. (defun ahk-electric-brace (arg)
  402.   "Insert character ARG and correct line's indentation."
  403.   (interactive "p")
  404.   (if (save-excursion
  405.         (skip-chars-backward " \t")
  406.         (bolp))
  407.       nil
  408.     (ahk-indent-line)
  409.     (newline))
  410.   (self-insert-command arg)
  411.   (ahk-indent-line)
  412.   (newline)
  413.   (ahk-indent-line)
  414.  
  415.   (let ((event  last-input-event))
  416.     (setq event (if (featurep 'xemacs)
  417.             (event-to-character event)
  418.           (setq event (if (stringp event) (aref event 0) event))))
  419.  
  420.     (when (equal event ?{)
  421.       (newline)
  422.       (ahk-indent-line)
  423.       (insert ?})
  424.       (ahk-indent-line)
  425.       (forward-line -1)
  426.       (ahk-indent-line))))
  427.  
  428. (defun ahk-electric-return ()
  429.   "Insert newline and indent."
  430.   (interactive)
  431.   (ahk-indent-line)
  432.   (newline)
  433.   (ahk-indent-line))
  434.  
  435. (defun ahk-insert-command-template ()
  436.   "Insert a command template."
  437.   (interactive)
  438.   (let ((completions (mapcar (lambda (c) (list (mapconcat 'identity c "")))
  439.                              ahk-Commands-list))
  440.         (completion-ignore-case t)
  441.         (start (point))
  442.         end 
  443.         command)
  444.     (setq command (completing-read "AHK command template: " completions))
  445.     (insert command)
  446.     (ahk-indent-line)
  447.     (setq end (point-marker))
  448.     (goto-char start)
  449.     (while (re-search-forward "[`][nt]" end t)
  450.       (if (string= (match-string 0) "`n")
  451.       (replace-match "\n")
  452.     (replace-match "")))
  453.     (ahk-indent-region start end)
  454.     (goto-char (1+ start))
  455.     ;; jump to first parameter 
  456.     (re-search-forward "\\<" end nil)
  457.     (set-marker end nil)))
  458.  
  459. (provide 'ahk-mode)
  460.  
  461. ;;; ahk-mode.el ends here
  462.