Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

676 lines
32 KiB

  1. ;;; ahk-mode.el --- Major mode for editing AHK (AutoHotkey and AutoHotkey_L) -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2015-2016 by Rich Alesi
  3. ;; Author: Rich Alesi
  4. ;; URL: https://github.com/ralesi/ahk-mode
  5. ;; Package-Version: 1.5.6
  6. ;; Package-Commit: bf3205efe7b7a40f3c8978f68f14ea3a939cffa8
  7. ;; Version: 1.5.6
  8. ;; Keywords: ahk, AutoHotkey, hotkey, keyboard shortcut, automation
  9. ;; Package-Requires: ((emacs "24.3"))
  10. ;; Based on work from
  11. ;; xahk-mode - Author: Xah Lee ( http://xahlee.org/ ) - 2012
  12. ;; ahk-mode - Author: Robert Widhopf-Fenk
  13. ;; You can redistribute this program and/or modify it under the terms of the GNU
  14. ;; General Public License as published by the Free Software Foundation; either
  15. ;; GPL version 2 or 3.
  16. ;;; Commentary:
  17. ;; A major mode for editing AutoHotkey (AHK) script. Supports commenting,
  18. ;; indentation, syntax highlighting, and help lookup both localling and on
  19. ;; the web.
  20. ;;; INSTALL
  21. ;; Open the file, then type “M-x eval-buffer”. You are done. Open
  22. ;; any ahk script, then type “M-x ahk-mode”, you'll see the
  23. ;; source code syntax colored.
  24. ;; To have Emacs automatically load the file when it restarts, and
  25. ;; automatically use the mode when opening files ending in “.ahk”, do this:
  26. ;; This package is located within Melpa. To install, add
  27. ;; ("melpa" . "http://melpa.org/packages/") to package-archives and
  28. ;; execute "M-x package-install RET ahk-mode RET".
  29. ;;; FEATURES
  30. ;; When opening a script file you will get:
  31. ;; - syntax highlighting
  32. ;; - Commenting - provide functions for block and standard commenting
  33. ;; - Imenu - jump to a function / label within a buffer
  34. ;; - Execute scripts
  35. ;; - Auto complete - adds options for `company-mode' and `auto-complete-mode'
  36. ;; TODO:
  37. ;; - smart identification of ahk_l and ahk - use chm file
  38. ;; - Indentation - indent based on current style
  39. ;; - Lookup reference - both on the web and through the installed CHM file
  40. ;; - Execute scripts - support redirects of error to stdout
  41. ;; - Debugging features - work with dgdb.ahk
  42. ;; - add yasnippet support
  43. ;; Notes on indentation
  44. ;; Indentation is styled with bracing on current line of if / else statements
  45. ;; or on empty next line.
  46. ;; Block types that can affect indentation
  47. ;; comments - ; AAA
  48. ;; - previous block beginning brace = +0
  49. ;; - indentation level is skipped when determining position for current line
  50. ;; function - AAA(.*) { .\n. } = +1
  51. ;; function - AAA(.*) { } = +0
  52. ;; label - AAA: = 0
  53. ;; Keybindings (next line) AAA:: = +1
  54. ;; Keybindings (current line) AAA:: =+0
  55. ;; Open block - {( +1 on next
  56. ;; Close block - {( -1 on current
  57. ;; Class AAA.* { ... } = +1
  58. ;; #if block open - #[iI]f[^ \n]* (.*) = +1
  59. ;; #if block close - #[iI]f[^ \n]*$ = -1
  60. ;; return block - [Rr]eturn = -1
  61. ;; for .*\n { .. } = +1
  62. ;; loop .*\n { .. } = +1
  63. ;; open assignment - .*operator-regexp$ = +1
  64. ;; existing issues :
  65. ;; paren broken across multiple lines
  66. ;; DllCall("SetWindowPos", "uint", Window%PrevRowText%, "uint", Window%PPrevRowText%
  67. ;; , "int", 0, "int", 0, "int", 0, "int", 0
  68. ;; , "uint", 0x13) ; NOSIZE|NOMOVE|NOACTIVATE (0x1|0x2|0x10)
  69. ;;; HISTORY
  70. ;; version 1.5.2, 2015-03-07 improved auto complete to work with ac and company-mode
  71. ;; version 1.5.3, 2015-04-05 improved commenting and added imenu options
  72. ;; version 1.5.4, 2015-04-06 indentation is working, with bugs
  73. ;; version 1.5.5, 2015-07-20 added load website
  74. ;; version 1.5.6, 2016-03-20 execute script works
  75. ;;; Code:
  76. ;;; Requirements
  77. (require 'font-lock)
  78. (require 'thingatpt)
  79. (require 'rx)
  80. (defvar ac-modes)
  81. (defvar company-tooltip-align-annotations)
  82. ;; add to auto-complete sources if ac is loaded
  83. (eval-after-load "auto-complete"
  84. '(progn
  85. (require 'auto-complete-config)
  86. (add-to-list 'ac-modes 'ahk-mode)))
  87. ;;; Customization
  88. (defconst ahk-mode-version "1.5.5"
  89. "Version of `ahk-mode'")
  90. (defgroup ahk-mode nil
  91. "Major mode for editing AutoHotkey script."
  92. :group 'languages
  93. :prefix "ahk-"
  94. :link '(url-link :tag "Github" "https://github.com/ralesi/ahk-mode")
  95. :link '(emacs-commentary-link :tag "Commentary" "ahk-mode"))
  96. (defcustom ahk-indentation (or tab-width 2)
  97. "The indentation level."
  98. :type 'integer
  99. :group 'ahk-mode)
  100. (defvar ahk-debug nil
  101. "Allows additional output when set to non-nil.")
  102. ;;;###autoload
  103. (add-to-list 'auto-mode-alist '("\\.ahk\\'" . ahk-mode))
  104. ;;; keymap
  105. (defvar ahk-mode-map
  106. (let ((map (make-sparse-keymap)))
  107. ;; key bindings
  108. (define-key map (kbd "C-c C-?") #'ahk-lookup-web)
  109. (define-key map (kbd "C-c M-i") #'ahk-indent-message)
  110. (define-key map (kbd "C-c C-c") #'ahk-comment-dwim)
  111. (define-key map (kbd "C-c C-b") #'ahk-comment-block-dwim)
  112. (define-key map (kbd "C-c C-k") #'ahk-run-script)
  113. map)
  114. "Keymap for Autohotkey major mode.")
  115. ;;; menu
  116. (easy-menu-define ahk-menu ahk-mode-map
  117. "AHK Mode Commands"
  118. '("AHK"
  119. ["Lookup webdocs on command" ahk-lookup-web]
  120. ["Execute script" ahk-run-script]
  121. "---"
  122. ["Version" ahk-version]))
  123. ;;; syntax table
  124. (defvar ahk-mode-syntax-table
  125. (let ((syntax-table (make-syntax-table)))
  126. ;; these are also allowed in variable names
  127. (modify-syntax-entry ?# "w" syntax-table)
  128. (modify-syntax-entry ?_ "w" syntax-table)
  129. (modify-syntax-entry ?@ "w" syntax-table)
  130. ;; some additional characters used in paths and switches
  131. (modify-syntax-entry ?\\ "w" syntax-table)
  132. (modify-syntax-entry ?\; "< b" syntax-table)
  133. ;; for multiline comments
  134. (modify-syntax-entry ?\/ ". 14" syntax-table)
  135. (modify-syntax-entry ?* ". 23" syntax-table)
  136. ;; New line
  137. (modify-syntax-entry ?\n "> b" syntax-table)
  138. ;; ` is escape
  139. (modify-syntax-entry ?` "\\" syntax-table)
  140. syntax-table)
  141. "Syntax table for `ahk-mode'.")
  142. ;;; imenu support
  143. (defconst ahk-imenu-generic-expression
  144. '(("Functions" "^\s*\\(.*\\)(.*)[\n]{" 1)
  145. ("Labels" "^\s*\\([^:]+\\):\n" 1)
  146. ("Keybindings" "^\s*\\(.+?\\)::" 1)
  147. ("Comments" "^; \\(.+\\)" 1))
  148. "imenu index for `ahk-mode'")
  149. (defun ahk-run-script ()
  150. "Run the ahk-script in the current buffer."
  151. (interactive)
  152. (let ((file (shell-quote-argument
  153. (replace-regexp-in-string " " "\ "
  154. (replace-regexp-in-string "\/" "\\\\" (buffer-file-name) t t)))))
  155. (message "Executing script %s" file)
  156. (w32-shell-execute "open" file)))
  157. (defun ahk-command-at-point ()
  158. "Determine command at point, and prompt if nothing found."
  159. (let ((command (or (if (region-active-p)
  160. (buffer-substring-no-properties
  161. (region-beginning)
  162. (region-end))
  163. (thing-at-point 'symbol))
  164. (read-string "Command: "))))
  165. command))
  166. (defun ahk-lookup-web ()
  167. "Look up current word in AutoHotkey's reference doc.
  168. Launches default browser and opens the doc's url."
  169. (interactive)
  170. (let* ((acap (ahk-command-at-point))
  171. (url (concat "http://ahkscript.org/docs/commands/" acap ".htm")))
  172. (browse-url url)))
  173. (defun ahk-version ()
  174. "Show the `ahk-mode' version in the echo area."
  175. (interactive)
  176. (message "ahk-mode version %s" ahk-mode-version))
  177. ;;;; indentation
  178. (defun ahk-calc-indentation (str &optional offset)
  179. "Calculate the current indentation level of argument"
  180. (let ((i (* (or offset 0) ahk-indentation)))
  181. (while (string-match "\t" str)
  182. (setq i (+ i tab-width)
  183. str (replace-match "" nil t str)))
  184. (setq i (+ i (length str)))
  185. i))
  186. ;; the follwing regexp is used to detect if a condition is a one line statement or not,
  187. ;; i.e. it matches one line statements but should not match those where the THEN resp.
  188. ;; ELSE body is on its own line ...
  189. (defvar ahk-one-line-if-regexp
  190. (concat "^\\([ \t]*\\)" ;; this is used for indentation
  191. "\\("
  192. "If\\(Not\\)?\\("
  193. (regexp-opt '("InString" "InStr"
  194. "Less" "Greater" "Equal"
  195. "LessOrEqual" "GreaterOrEqual"
  196. ))
  197. "\\)[^,\n]*,[^,\n]*,[^,\n]*,"
  198. "\\|"
  199. "If\\(Not\\)?Exist[^,\n]*,[^,\n]*,"
  200. "\\|"
  201. "Else[ \t]+\\([^I\n][^f\n][^ \n]\\)"
  202. "\\)"))
  203. (defun ahk-previous-indent ()
  204. "Return the indentation level of the previous non-blank line."
  205. (save-excursion
  206. (forward-line -1)
  207. (while (and (looking-at "^[ \t]*$") (not (bobp)))
  208. (forward-line -1))
  209. (current-indentation)))
  210. (defun ahk-indent-message ()
  211. "Show message for current indentation level"
  212. (interactive)
  213. (message (format "%s" (current-indentation))))
  214. (defun ahk-indent-line ()
  215. "Indent the current line."
  216. (interactive)
  217. (let ((indent 0)
  218. (opening-brace nil)
  219. (else nil)
  220. (label nil)
  221. (closing-brace nil)
  222. (loop nil)
  223. (prev-single nil)
  224. (return nil)
  225. (empty-brace nil)
  226. (block-skip nil)
  227. (case-fold-search t))
  228. ;; do a backward search to determine the indentation level
  229. (save-excursion
  230. (beginning-of-line)
  231. ;; save type of current line
  232. (setq opening-brace (looking-at "^[ \t]*{[^}]"))
  233. (setq opening-paren (looking-at "^[ \t]*([^)]"))
  234. (setq if-else (looking-at "^[ \t]*\\([iI]f\\|[Ee]lse\\)"))
  235. (setq loop (looking-at "^[ \t]*\\([Ll]oop\\)[^{]+"))
  236. (setq closing-brace (looking-at "^[ \t]*\\([)}]\\|\\*\\/\\)$"))
  237. (setq label (looking-at "^[ \t]*[^:\n ]+:$"))
  238. (setq keybinding (looking-at "^[ \t]*[^:\n ]+::\\(.*\\)$"))
  239. (setq return (looking-at "^\\([ \t]*\\)[rR]eturn"))
  240. (setq blank (looking-at "^\\([ \t]*\\)\n"))
  241. ;; skip previous empty lines and commented lines
  242. (setq indent (ahk-previous-indent))
  243. (setq prev (ahk-previous-indent))
  244. (save-excursion
  245. (when closing-brace
  246. (progn
  247. (beginning-of-line)
  248. (re-search-forward "\\(}\\|)\\)" nil t)
  249. (backward-list)
  250. (setq block-skip t)
  251. (setq indent (current-indentation))
  252. )
  253. )
  254. )
  255. (forward-line -1)
  256. (while (and
  257. (or (looking-at "^[ \t]*$") (looking-at "^;"))
  258. (not (bobp)))
  259. (forward-line -1))
  260. ;; we are now at the previous non-empty /non comment line
  261. (beginning-of-line)
  262. ;; default to previous indentation
  263. (cond
  264. (block-skip
  265. nil)
  266. (blank
  267. (setq indent 0))
  268. (closing-brace
  269. (setq indent (- indent ahk-indentation)))
  270. ;; if beginning with a comment, indent based on previous line
  271. ((looking-at "^\\([ \t*]\\);")
  272. (setq indent (ahk-previous-indent)))
  273. ;; keybindings
  274. ((and (looking-at "^[ \t]*[^:\n ]+:$")
  275. (not label))
  276. (setq indent (+ indent ahk-indentation)))
  277. ;; return
  278. ((looking-at "^\\([ \t]*\\)[rR]eturn")
  279. (setq indent (- indent ahk-indentation)))
  280. ;; label
  281. (label
  282. (setq indent 0))
  283. ((and
  284. (not opening-brace)
  285. (not block-skip)
  286. (looking-at "^[^: \n]+:$")
  287. (looking-at "^[^:\n]+:\\([^:\n]*\\)?[ ]*$"))
  288. (setq indent (+ indent ahk-indentation)))
  289. ;; opening brace
  290. ((looking-at "^\\([ \t]*\\)[{(]$")
  291. (and
  292. (setq empty-brace t)
  293. (setq indent (+ indent ahk-indentation))))
  294. ;; brace at end of line
  295. ((or
  296. (looking-at "^\\([ \t]*\\).*[{][^}]*$")
  297. (looking-at "^\\([ \t]*\\).*[(][^)]*$"))
  298. (setq indent (+ indent ahk-indentation)))
  299. ;; If/Else with body on next line, but not opening { or (
  300. ((and (not opening-brace)
  301. (not block-skip)
  302. ;; (or if-else loop)
  303. (or
  304. (looking-at "^[ \t]*\\([Ll]oop\\)[^{=\n]*")
  305. (looking-at "^\\([ \t]*\\)\\([iI]f\\|[eE]lse\\)[^{]*\n"))
  306. )
  307. (and
  308. (setq prev-single t)
  309. (setq indent (+ indent ahk-indentation))))
  310. ;; (return
  311. ;; (setq indent (- indent ahk-indentation)))
  312. ;; subtract indentation if closing bracket only
  313. ;; ((looking-at "^[ \t]*[})]")
  314. ;; (setq indent (- indent ahk-indentation)))
  315. ;; zero indentation when at label or keybinding
  316. ((or (looking-at "^[ \t]*[^,: \t\n]*:$")
  317. (looking-at "^;;;"))
  318. (setq indent 0)))
  319. ;; check for single line if/else
  320. (forward-line -1)
  321. (when (and
  322. (not block-skip)
  323. (not empty-brace)
  324. (or
  325. (looking-at "^[ \t]*\\([Ll]oop\\)[^{\n]+")
  326. (looking-at "^\\([ ]*\\)\\([iI]f\\|[eE]lse\\)[^{\n]+")))
  327. ;; adjust when stacking multiple single line commands
  328. (setq indent (- indent (if prev-single (- (* 2 ahk-indentation)) 0) ahk-indentation)))
  329. )
  330. ;; set negative indentation to 0
  331. (save-excursion
  332. (beginning-of-line)
  333. (if (< indent 0)
  334. (setq indent 0))
  335. ;; actual indentation performed here
  336. (if (looking-at "^[ \t]+")
  337. (replace-match ""))
  338. (indent-to indent))
  339. (when ahk-debug
  340. (message (format
  341. "indent: %s, current: %s previous: %s
  342. ob: %s, op: %s, cb: %s, bs: %s,
  343. if-else: %s, l: %s, kb: %s, ret: %s, bl: %s"
  344. indent
  345. (current-indentation)
  346. (ahk-previous-indent)
  347. opening-brace
  348. opening-paren
  349. closing-brace
  350. block-skip
  351. if-else
  352. label
  353. keybinding
  354. return
  355. blank
  356. )))))
  357. (defun ahk-indent-region (start end)
  358. (interactive "r")
  359. (save-excursion
  360. (goto-char start)
  361. (while (and (not (eobp)) (< (point) end))
  362. (ahk-indent-line)
  363. (forward-line 1))))
  364. ;;;; commenting
  365. (defun ahk-comment-dwim (arg)
  366. "Comment or uncomment current line or region in a smart way.
  367. For details, see `comment-dwim'."
  368. (interactive "*P")
  369. (require 'newcomment)
  370. (let ((comment-start ";")
  371. (comment-end ""))
  372. (comment-dwim arg)))
  373. (defun ahk-comment-block-dwim (arg)
  374. "Comment or uncomment current line or region using block notation.
  375. For details, see `comment-dwim'."
  376. (interactive "*P")
  377. (require 'newcomment)
  378. (let ((comment-style 'extra-line)
  379. (comment-start "/*")
  380. (comment-end "*/"))
  381. (comment-dwim arg)))
  382. ;;; font-lock
  383. (defvar ahk-commands
  384. '("Abort" "AboveNormal" "Add" "All" "Alnum" "Alpha" "AltSubmit" "AlwaysOnTop" "And" "Asc" "AutoSize" "AutoTrim" "Background" "BackgroundTrans" "BelowNormal" "Between" "BitAnd" "BitNot" "BitOr" "BitShiftLeft" "BitShiftRight" "BitXOr" "BlockInput" "Border" "Bottom" "Break" "Button" "Buttons" "ByRef" "Cancel" "Capacity" "Caption" "Catch" "Ceil" "Center" "Check" "Check3" "Checkbox" "Checked" "CheckedGray" "Checks" "Choose" "ChooseString" "Chr" "Click" "ClipWait" "Close" "Color" "ComboBox" "Contains" "Continue" "Control" "ControlClick" "ControlFocus" "ControlGet" "ControlGetFocus" "ControlGetPos" "ControlGetText" "ControlList" "ControlMove" "ControlSend" "ControlSendRaw" "ControlSetText" "CoordMode" "Count" "Critical" "DDL" "Date" "DateTime" "Days" "Default" "Delete" "DeleteAll" "Delimiter" "Deref" "Destroy" "DetectHiddenText" "DetectHiddenWindows" "Digit" "Disable" "Disabled" "Displays" "Drive" "DriveGet" "DriveSpaceFree" "DropDownList" "Edit" "Eject" "Else" "Enable" "Enabled" "EnvAdd" "EnvDiv" "EnvGet" "EnvMult" "EnvSet" "EnvSub" "EnvUpdate" "Error" "ExStyle" "Exist" "Exit" "ExitApp" "Exp" "Expand" "FileAppend" "FileCopy" "FileCopyDir" "FileCreateDir" "FileCreateShortcut" "FileDelete" "FileEncoding" "FileGetAttrib" "FileGetShortcut" "FileGetSize" "FileGetTime" "FileGetVersion" "FileInstall" "FileMove" "FileMoveDir" "FileOpen" "FileRead" "FileReadLine" "FileRecycle" "FileRecycleEmpty" "FileRemoveDir" "FileSelectFile" "FileSelectFolder" "FileSetAttrib" "FileSetTime" "FileSystem" "Finally" "First" "Flash" "Float" "FloatFast" "Floor" "Focus" "Font" "For" "Format" "FormatTime" "GetKeyState" "Gosub" "Goto" "Grid" "Group" "GroupActivate" "GroupAdd" "GroupBox" "GroupClose" "GroupDeactivate" "Gui" "GuiClose" "GuiContextMenu" "GuiControl" "GuiControlGet" "GuiDropFiles" "GuiEscape" "GuiSize" "HKCC" "HKCR" "HKCU" "HKEY_CLASSES_ROOT" "HKEY_CURRENT_CONFIG" "HKEY_CURRENT_USER" "HKEY_LOCAL_MACHINE" "HKEY_USERS" "HKLM" "HKU" "HScroll" "Hdr" "Hidden" "Hide" "High" "Hotkey" "Hours" "ID" "IDLast" "Icon" "IconSmall" "If" "IfEqual" "IfExist" "IfGreater" "IfGreaterOrEqual" "IfInString" "IfLess" "IfLessOrEqual" "IfMsgBox" "IfNotEqual" "IfWinActive" "IfWinExist" "IfWinNotActive" "IfWinNotExist" "Ignore" "ImageList" "ImageSearch" "In" "IniDelete" "IniRead" "IniWrite" "Input" "InputBox" "Integer" "IntegerFast" "Interrupt" "Is" "Join" "KeyHistory" "KeyWait" "LTrim" "Label" "LastFound" "LastFoundExist" "Left" "Limit" "Lines" "List" "ListBox" "ListHotkeys" "ListLines" "ListVars" "ListView" "Ln" "Lock" "Log" "Logoff" "Loop" "Low" "Lower" "Lowercase" "MainWindow" "Margin" "MaxSize" "Maximize" "MaximizeBox" "Menu" "MinMax" "MinSize" "Minimize" "MinimizeBox" "Minutes" "Mod" "MonthCal" "Mouse" "MouseClick" "MouseClickDrag" "MouseGetPos" "MouseMove" "Move" "MsgBox" "Multi" "NA" "No" "NoActivate" "NoDefault" "NoHide" "NoIcon" "NoMainWindow" "NoSort" "NoSortHdr" "NoStandard" "NoTab" "NoTimers" "Normal" "Not" "Number" "Off" "Ok" "On" "OnExit" "Or" "OutputDebug" "OwnDialogs" "Owner" "Parse" "Password" "Pause" "Pic" "Picture" "Pixel" "PixelGetColor" "PixelSearch" "Pos" "PostMessage" "Pow" "Priority" "Process" "ProcessName" "Progress" "REG_BINARY" "REG_DWORD" "REG_EXPAND_SZ" "REG_MULTI_SZ" "REG_SZ" "RGB" "RTrim" "Radio" "Random" "Range" "Read" "ReadOnly" "Realtime" "Redraw" "RegDelete" "RegRead" "RegWrite" "Region" "Relative" "Reload" "Rename" "Report" "Resize" "Restore" "Retry" "Return" "Right" "Round" "Run" "RunAs" "RunWait" "Screen" "Seconds" "Section" "See" "Send" "SendInput" "SendLevel" "SendMessage" "SendMode" "SendPlay" "SendRaw" "Serial" "SetBatchLines" "SetCapslockState" "SetControlDelay" "SetDefaultMouseSpeed" "SetEnv" "SetFormat" "SetKeyDelay" "SetLabel" "SetMouseDelay" "SetNumlockState" "SetRegView" "SetScrollLockState" "SetStoreCapslockMode" "SetTimer" "SetTitleMatchMode" "SetWinDelay" "SetWorkingDir" "ShiftAltTab" "Show" "Shutdown" "Sin" "Single" "Sleep" "Slider" "Sort" "SortDesc" "SoundBeep" "SoundGet" "SoundGetWaveVolume" "SoundPlay" "SoundSet" "SoundSetWaveVolume" "SplashImage" "SplashTextOff" "SplashTextOn" "SplitPath" "Sqrt" "Standard" "Status
  385. "AHK keywords.")
  386. (defvar ahk-directives
  387. '("#ClipboardTimeout" "#CommentFlag" "#ErrorStdOut" "#EscapeChar" "#HotkeyInterval" "#HotkeyModifierTimeout" "#Hotstring" "#If" "#IfTimeout" "#IfWinActive" "#IfWinExist" "#Include" "#InputLevel" "#InstallKeybdHook" "#InstallMouseHook" "#KeyHistory" "#LTrim" "#MaxHotkeysPerInterval" "#MaxMem" "#MaxThreads" "#MaxThreadsBuffer" "#MaxThreadsPerHotkey" "#MenuMaskKey" "#NoEnv" "#NoTrayIcon" "#Persistent" "#SingleInstance" "#UseHook" "#Warn" "#WinActivateForce")
  388. "AHK directives")
  389. (defvar ahk-functions
  390. '("ACos" "ASin" "ATan" "Abs" "Asc" "Ceil" "Chr" "ComObjActive" "ComObjArray" "ComObjConnect" "ComObjCreate" "ComObjEnwrap" "ComObjError" "ComObjFlags" "ComObjGet" "ComObjMissing" "ComObjParameter" "ComObjQuery" "ComObjType" "ComObjUnwrap" "ComObjValue" "Cos" "DllCall" "Exp" "FileExist" "Floor" "Func" "Functions" "GetKeyName" "GetKeySC" "GetKeyState" "GetKeyVK" "IL_Add" "IL_Create" "IL_Destroy" "InStr" "IsByRef" "IsFunc" "IsLabel" "IsObject" "LV_Add" "LV_Delete" "LV_DeleteCol" "LV_GetCount" "LV_GetNext" "LV_GetText" "LV_Insert" "LV_InsertCol" "LV_Modify" "LV_ModifyCol" "LV_SetImageList" "Ln" "Log" "Mod" "NumGet" "NumPut" "OnMessage" "RegExMatch" "RegExReplace" "RegisterCallback" "Round" "SB_SetIcon" "SB_SetParts" "SB_SetText" "Sin" "Sqrt" "StrGet" "StrLen" "StrPut" "SubStr" "TV_Add" "TV_Delete" "TV_Get" "TV_GetChild" "TV_GetCount" "TV_GetNext" "TV_GetParent" "TV_GetPrev" "TV_GetSelection" "TV_GetText" "TV_Modify" "Tan" "VarSetCapacity" "WinActive" "WinExist")
  391. "AHK functions.")
  392. (defvar ahk-variables
  393. '("A_AhkPath" "A_AhkVersion" "A_AppData" "A_AppDataCommon" "A_AutoTrim" "A_BatchLines" "A_CaretX" "A_CaretY" "A_ComputerName" "A_ControlDelay" "A_Cursor" "A_DD" "A_DDD" "A_DDDD" "A_DefaultMouseSpeed" "A_Desktop" "A_DesktopCommon" "A_DetectHiddenText" "A_DetectHiddenWindows" "A_EndChar" "A_EventInfo" "A_ExitReason" "A_FileEncoding" "A_FormatFloat" "A_FormatInteger" "A_Gui" "A_GuiControl" "A_GuiControlEvent" "A_GuiEvent" "A_GuiHeight" "A_GuiWidth" "A_GuiX" "A_GuiY" "A_Hour" "A_IPAddress1" "A_IPAddress2" "A_IPAddress3" "A_IPAddress4" "A_ISAdmin" "A_IconFile" "A_IconHidden" "A_IconNumber" "A_IconTip" "A_Index" "A_Is64bitOS" "A_IsAdmin" "A_IsCompiled" "A_IsCritical" "A_IsPaused" "A_IsSuspended" "A_IsUnicode" "A_KeyDelay" "A_Language" "A_LastError" "A_LineFile" "A_LineNumber" "A_LoopField" "A_LoopFileAttrib" "A_LoopFileDir" "A_LoopFileExt" "A_LoopFileFullPath" "A_LoopFileLongPath" "A_LoopFileName" "A_LoopFileName," "A_LoopFileShortName" "A_LoopFileShortPath" "A_LoopFileSize" "A_LoopFileSizeKB" "A_LoopFileSizeMB" "A_LoopFileTimeAccessed" "A_LoopFileTimeCreated" "A_LoopFileTimeModified" "A_LoopReadLine" "A_LoopRegKey" "A_LoopRegName" "A_LoopRegName," "A_LoopRegSubkey" "A_LoopRegTimeModified" "A_LoopRegType" "A_MDAY" "A_MM" "A_MMM" "A_MMMM" "A_MSec" "A_Min" "A_Mon" "A_MouseDelay" "A_MyDocuments" "A_Now" "A_NowUTC" "A_NumBatchLines" "A_OSType" "A_OSVersion" "A_PriorHotkey" "A_PriorKey" "A_ProgramFiles" "A_Programs" "A_ProgramsCommon" "A_PtrSize" "A_RegView" "A_ScreenDPI" "A_ScreenHeight" "A_ScreenWidth" "A_ScriptDir" "A_ScriptFullPath" "A_ScriptHwnd" "A_ScriptName" "A_Sec" "A_Space" "A_StartMenu" "A_StartMenuCommon" "A_Startup" "A_StartupCommon" "A_StringCaseSense" "A_Tab" "A_Temp" "A_ThisFunc" "A_ThisHotkey" "A_ThisLabel" "A_ThisMenu" "A_ThisMenuItem" "A_ThisMenuItemPos" "A_TickCount" "A_TimeIdle" "A_TimeIdlePhysical" "A_TimeSincePriorHotkey" "A_TimeSinceThisHotkey" "A_TitleMatchMode" "A_TitleMatchModeSpeed" "A_UserName" "A_WDay" "A_WinDelay" "A_WinDir" "A_WorkingDir" "A_YDay" "A_YEAR" "A_YWeek" "A_YYYY" "Clipboard" "ClipboardAll" "ComSpec" "ErrorLevel" "False" "ProgramFiles" "True" "Variable")
  394. "AHK variables.")
  395. (defvar ahk-keys
  396. '("Alt" "AltDown" "AltTab" "AltTabAndMenu" "AltTabMenu" "AltTabMenuDismiss" "AltUp" "AppsKey" "BS" "BackSpace" "Browser_Back" "Browser_Favorites" "Browser_Forward" "Browser_Home" "Browser_Refresh" "Browser_Search" "Browser_Stop" "CapsLock" "Control" "Ctrl" "CtrlBreak" "CtrlDown" "CtrlUp" "Del" "Delete" "Down" "End" "Enter" "Esc" "Escape" "F1" "F10" "F11" "F12" "F13" "F14" "F15" "F16" "F17" "F18" "F19" "F2" "F20" "F21" "F22" "F23" "F24" "F3" "F4" "F5" "F6" "F7" "F8" "F9" "Home" "Ins" "Insert" "Joy1" "Joy10" "Joy11" "Joy12" "Joy13" "Joy14" "Joy15" "Joy16" "Joy17" "Joy18" "Joy19" "Joy2" "Joy20" "Joy21" "Joy22" "Joy23" "Joy24" "Joy25" "Joy26" "Joy27" "Joy28" "Joy29" "Joy3" "Joy30" "Joy31" "Joy32" "Joy4" "Joy5" "Joy6" "Joy7" "Joy8" "Joy9" "JoyAxes" "JoyButtons" "JoyInfo" "JoyName" "JoyPOV" "JoyR" "JoyU" "JoyV" "JoyX" "JoyY" "JoyZ" "LAlt" "LButton" "LControl" "LCtrl" "LShift" "LWin" "LWinDown" "LWinUp" "Launch_App1" "Launch_App2" "Launch_Mail" "Launch_Media" "Left" "MButton" "Media_Next" "Media_Play_Pause" "Media_Prev" "Media_Stop" "NumLock" "Numpad0" "Numpad1" "Numpad2" "Numpad3" "Numpad4" "Numpad5" "Numpad6" "Numpad7" "Numpad8" "Numpad9" "NumpadAdd" "NumpadClear" "NumpadDel" "NumpadDiv" "NumpadDot" "NumpadDown" "NumpadEnd" "NumpadEnter" "NumpadHome" "NumpadIns" "NumpadLeft" "NumpadMult" "NumpadPgdn" "NumpadPgup" "NumpadRight" "NumpadSub" "NumpadUp" "PGDN" "PGUP" "Pause" "PrintScreen" "RAlt" "RButton" "RControl" "RCtrl" "RShift" "RWin" "RWinDown" "RWinUp" "Right" "ScrollLock" "Shift" "ShiftDown" "ShiftUp" "Space" "Tab" "Up" "Volume_Down" "Volume_Mute" "Volume_Up" "WheelDown" "WheelLeft" "WheelRight" "WheelUp" "XButton1" "XButton2")
  397. "AHK keywords for keys.")
  398. (defvar ahk-operators
  399. '("\\!" "!=" "&" "&&" "&=" "*" "**" "*=" "+" "++" "+=" "-" "--" "-=" "." "." ".=" "/" "//" "//=" "/=" ":=" "<" "<<" "<<=" "<=" "<>" "=" "==" ">" ">=" ">>" ">>=" "?:" "AND" "NOT" "OR" "^" "^=" "|" "|=" "||" "~" "~=" ",")
  400. "AHK operators.")
  401. (defvar ahk-commands-regexp (regexp-opt ahk-commands 'words))
  402. (defvar ahk-functions-regexp (regexp-opt ahk-functions 'words))
  403. (defvar ahk-directives-regexp (regexp-opt ahk-directives 'words))
  404. (defvar ahk-variables-regexp (regexp-opt ahk-variables 'words))
  405. (defvar ahk-keys-regexp (regexp-opt ahk-keys 'words))
  406. (defvar ahk-operators-regexp (regexp-opt ahk-operators))
  407. (defvar ahk-double-quote-string-re "[\"]\\(\\\\.\\|[^\"\n]\\)*[\"]"
  408. "Regexp used to match a double-quoted string literal")
  409. (defvar ahk-single-quote-string-re "[']\\(\\\\.\\|[^'\n]\\)*[']"
  410. "Regexp used to match a single-quoted string literal")
  411. (defvar ahk-font-lock-keywords
  412. `(("\\s-*;.*$" . font-lock-comment-face)
  413. ;; lLTrim0 usage
  414. ("(LTrim0\\(.*\n\\)+" . font-lock-string-face)
  415. (,ahk-double-quote-string-re . font-lock-string-face)
  416. (,ahk-single-quote-string-re . font-lock-string-face)
  417. ;; block comments
  418. ("^/\\*\\(.*\r?\n\\)*\\(\\*/\\)?" . font-lock-comment-face)
  419. ;; bindings
  420. ("^\\([^\t\n:=]+\\)::" . (1 font-lock-constant-face))
  421. ;; labels
  422. ("^\\([^\t\n :=]+\\):[^=]" . (1 font-lock-doc-face))
  423. ;; return
  424. ("[Rr]eturn" . font-lock-warning-face)
  425. ;; functions
  426. ("^\\([^\t\n (]+\\)\\((.*)\\)" . (1 font-lock-function-name-face))
  427. ;; variables
  428. ("%[^% ]+%" . font-lock-variable-name-face)
  429. (,ahk-commands-regexp . font-lock-keyword-face)
  430. (,ahk-functions-regexp . font-lock-function-name-face)
  431. (,ahk-directives-regexp . font-lock-preprocessor-face)
  432. (,ahk-variables-regexp . font-lock-variable-name-face)
  433. (,ahk-keys-regexp . font-lock-constant-face)
  434. (,ahk-operators-regexp . font-lock-builtin-face)
  435. ;; note: order matters
  436. ))
  437. ;; keyword completion
  438. (defvar ahk-kwd-list (make-hash-table :test 'equal)
  439. "AHK keywords.")
  440. (defvar ahk-all-keywords (append ahk-commands ahk-functions ahk-variables)
  441. "List of all ahk keywords.")
  442. (mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-commands)
  443. (mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-functions)
  444. (mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-directives)
  445. (mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-variables)
  446. (mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-keys)
  447. (put 'ahk-kwd-list 'risky-local-variable t)
  448. (defun ahk-completion-at-point ()
  449. "Complete the current work using the list of all syntax's."
  450. (interactive)
  451. (let ((pt (point)))
  452. (if (and (or (save-excursion (re-search-backward "\\<\\w+"))
  453. (looking-at "\\<\\w+"))
  454. (= (match-end 0) pt))
  455. (let ((start (match-beginning 0))
  456. (prefix (match-string 0))
  457. (completion-ignore-case t)
  458. completions)
  459. (list start pt (all-completions prefix ahk-all-keywords) :exclusive 'no :annotation-function 'ahk-company-annotation)))))
  460. (defun ahk-company-annotation (candidate)
  461. "Annotate company mode completions based on source."
  462. (cond
  463. ((member candidate ahk-commands)
  464. "c")
  465. ((member candidate ahk-functions)
  466. "f")
  467. ((member candidate ahk-variables)
  468. "v")
  469. ((member candidate ahk-directives)
  470. "d")
  471. ((member candidate ahk-keys)
  472. "k")
  473. (t "")))
  474. (defvar ac-source-ahk
  475. '((candidates . (all-completions ac-prefix ahk-all-keywords))
  476. (limit . nil)
  477. (symbol . "f"))
  478. "Completion for AHK mode")
  479. (defvar ac-source-keys-ahk
  480. '((candidates . (all-completions ac-prefix ahk-keys))
  481. (limit . nil)
  482. (symbol . "k"))
  483. "Completion for AHK keys mode")
  484. (defvar ac-source-directives-ahk
  485. '((candidates . (all-completions ac-prefix ahk-directives))
  486. (limit . nil)
  487. (symbol . "d"))
  488. "Completion for AHK directives mode")
  489. ;; clear memory
  490. ;; (setq ahk-commands nil)
  491. ;; (setq ahk-functions nil)
  492. ;; (setq ahk-directives nil)
  493. ;; (setq ahk-variables nil)
  494. ;; (setq ahk-keys nil)
  495. (defun ahk-font-lock-extend-region ()
  496. "Extend the search region to include an entire block of text."
  497. ;; Avoid compiler warnings about these global variables from font-lock.el.
  498. ;; See the documentation for variable `font-lock-extend-region-functions'.
  499. (eval-when-compile (defvar font-lock-beg) (defvar font-lock-end))
  500. (save-excursion
  501. (goto-char font-lock-beg)
  502. (let ((found (or (re-search-backward "(LTrim0" nil t) (point-min))))
  503. (goto-char font-lock-end)
  504. (when (re-search-forward "\n)" nil t)
  505. (beginning-of-line)
  506. (setq font-lock-end (point)))
  507. (setq font-lock-beg found))))
  508. (defun ahk-ltrim-blocks ()
  509. "Match JavaScript blocks from the point to LAST."
  510. (cond ((re-search-backward "(LTrim0" nil t)
  511. (let ((beg (match-beginning 0)))
  512. (cond ((re-search-forward "\n)" nil t)
  513. (set-match-data (list beg (point)))
  514. t)
  515. (t nil))))
  516. (t nil)))
  517. ;;;###autoload
  518. (define-derived-mode ahk-mode prog-mode "AutoHotkey Mode"
  519. "Major mode for editing AutoHotkey script (AHK).
  520. The hook functions in `ahk-mode-hook' are run after mode initialization.
  521. Key Bindings
  522. \\{ahk-mode-map}"
  523. (kill-all-local-variables)
  524. (set-syntax-table ahk-mode-syntax-table)
  525. (setq major-mode 'ahk-mode
  526. mode-name "AHK"
  527. local-abbrev-table ahk-mode-abbrev-table)
  528. ;; ui
  529. (use-local-map ahk-mode-map)
  530. (easy-menu-add ahk-menu)
  531. ;; imenu
  532. (setq-local imenu-generic-expression ahk-imenu-generic-expression)
  533. (setq-local imenu-sort-function 'imenu--sort-by-position)
  534. ;; font-lock
  535. (make-local-variable 'font-lock-defaults)
  536. (setq font-lock-defaults '((ahk-font-lock-keywords) nil t))
  537. ;; (set (make-local-variable 'font-lock-multiline) t)
  538. ;; (add-hook 'font-lock-extend-region-functions
  539. ;; 'ahk-font-lock-extend-region)
  540. ;; (setq syntax-propertize-function)
  541. ;; clear memory
  542. ;; (setq ahk-commands-regexp nil)
  543. ;; (setq ahk-functions-regexp nil)
  544. ;; (setq ahk-variables-regexp nil)
  545. ;; (setq ahk-keys-regexp nil)
  546. (if (boundp 'evil-shift-width)
  547. (setq-local evil-shift-width ahk-indentation))
  548. (setq-local comment-start ";")
  549. (setq-local comment-end "")
  550. (setq-local comment-start-skip ";+ *")
  551. (setq-local block-comment-start "/*")
  552. (setq-local block-comment-end "*/")
  553. (setq-local block-comment-left " * ")
  554. (setq-local block-comment-right " *")
  555. (setq-local block-comment-top-right "")
  556. (setq-local block-comment-bot-left " ")
  557. (setq-local block-comment-char ?*)
  558. (setq-local indent-line-function 'ahk-indent-line)
  559. (setq-local indent-region-function 'ahk-indent-region)
  560. (setq-local parse-sexp-ignore-comments t)
  561. (setq-local parse-sexp-lookup-properties t)
  562. (setq-local paragraph-start (concat "$\\|" page-delimiter))
  563. (setq-local paragraph-separate paragraph-start)
  564. (setq-local paragraph-ignore-fill-prefix t)
  565. ;; completion
  566. (setq-local company-tooltip-align-annotations t)
  567. (add-hook 'completion-at-point-functions 'ahk-completion-at-point nil t)
  568. (eval-after-load "auto-complete"
  569. '(when (listp 'ac-sources)
  570. (progn
  571. (make-local-variable 'ac-sources)
  572. (add-to-list 'ac-sources 'ac-source-ahk)
  573. (add-to-list 'ac-sources 'ac-source-directives-ahk)
  574. (add-to-list 'ac-sources 'ac-source-keys-ahk))))
  575. (run-mode-hooks 'ahk-mode-hook))
  576. (when ahk-debug
  577. (mapc (lambda (buffer)
  578. (with-current-buffer buffer
  579. (when (eq major-mode 'ahk-mode)
  580. (message "%s" buffer)
  581. (font-lock-mode -1)
  582. (ahk-mode))))
  583. (buffer-list)))
  584. (provide 'ahk-mode)
  585. ;;; ahk-mode.el ends here