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

;;; ahk-mode.el --- Major mode for editing AHK (AutoHotkey and AutoHotkey_L) -*- lexical-binding: t -*-
;; Copyright (C) 2015-2016 by Rich Alesi
;; Author: Rich Alesi
;; URL: https://github.com/ralesi/ahk-mode
;; Package-Version: 1.5.6
;; Package-Commit: bf3205efe7b7a40f3c8978f68f14ea3a939cffa8
;; Version: 1.5.6
;; Keywords: ahk, AutoHotkey, hotkey, keyboard shortcut, automation
;; Package-Requires: ((emacs "24.3"))
;; Based on work from
;; xahk-mode - Author: Xah Lee ( http://xahlee.org/ ) - 2012
;; ahk-mode - Author: Robert Widhopf-Fenk
;; You can redistribute this program and/or modify it under the terms of the GNU
;; General Public License as published by the Free Software Foundation; either
;; GPL version 2 or 3.
;;; Commentary:
;; A major mode for editing AutoHotkey (AHK) script. Supports commenting,
;; indentation, syntax highlighting, and help lookup both localling and on
;; the web.
;;; INSTALL
;; Open the file, then type “M-x eval-buffer”. You are done. Open
;; any ahk script, then type “M-x ahk-mode”, you'll see the
;; source code syntax colored.
;; To have Emacs automatically load the file when it restarts, and
;; automatically use the mode when opening files ending in “.ahk”, do this:
;; This package is located within Melpa. To install, add
;; ("melpa" . "http://melpa.org/packages/") to package-archives and
;; execute "M-x package-install RET ahk-mode RET".
;;; FEATURES
;; When opening a script file you will get:
;; - syntax highlighting
;; - Commenting - provide functions for block and standard commenting
;; - Imenu - jump to a function / label within a buffer
;; - Execute scripts
;; - Auto complete - adds options for `company-mode' and `auto-complete-mode'
;; TODO:
;; - smart identification of ahk_l and ahk - use chm file
;; - Indentation - indent based on current style
;; - Lookup reference - both on the web and through the installed CHM file
;; - Execute scripts - support redirects of error to stdout
;; - Debugging features - work with dgdb.ahk
;; - add yasnippet support
;; Notes on indentation
;; Indentation is styled with bracing on current line of if / else statements
;; or on empty next line.
;; Block types that can affect indentation
;; comments - ; AAA
;; - previous block beginning brace = +0
;; - indentation level is skipped when determining position for current line
;; function - AAA(.*) { .\n. } = +1
;; function - AAA(.*) { } = +0
;; label - AAA: = 0
;; Keybindings (next line) AAA:: = +1
;; Keybindings (current line) AAA:: =+0
;; Open block - {( +1 on next
;; Close block - {( -1 on current
;; Class AAA.* { ... } = +1
;; #if block open - #[iI]f[^ \n]* (.*) = +1
;; #if block close - #[iI]f[^ \n]*$ = -1
;; return block - [Rr]eturn = -1
;; for .*\n { .. } = +1
;; loop .*\n { .. } = +1
;; open assignment - .*operator-regexp$ = +1
;; existing issues :
;; paren broken across multiple lines
;; DllCall("SetWindowPos", "uint", Window%PrevRowText%, "uint", Window%PPrevRowText%
;; , "int", 0, "int", 0, "int", 0, "int", 0
;; , "uint", 0x13) ; NOSIZE|NOMOVE|NOACTIVATE (0x1|0x2|0x10)
;;; HISTORY
;; version 1.5.2, 2015-03-07 improved auto complete to work with ac and company-mode
;; version 1.5.3, 2015-04-05 improved commenting and added imenu options
;; version 1.5.4, 2015-04-06 indentation is working, with bugs
;; version 1.5.5, 2015-07-20 added load website
;; version 1.5.6, 2016-03-20 execute script works
;;; Code:
;;; Requirements
(require 'font-lock)
(require 'thingatpt)
(require 'rx)
(defvar ac-modes)
(defvar company-tooltip-align-annotations)
;; add to auto-complete sources if ac is loaded
(eval-after-load "auto-complete"
'(progn
(require 'auto-complete-config)
(add-to-list 'ac-modes 'ahk-mode)))
;;; Customization
(defconst ahk-mode-version "1.5.5"
"Version of `ahk-mode'")
(defgroup ahk-mode nil
"Major mode for editing AutoHotkey script."
:group 'languages
:prefix "ahk-"
:link '(url-link :tag "Github" "https://github.com/ralesi/ahk-mode")
:link '(emacs-commentary-link :tag "Commentary" "ahk-mode"))
(defcustom ahk-indentation (or tab-width 2)
"The indentation level."
:type 'integer
:group 'ahk-mode)
(defvar ahk-debug nil
"Allows additional output when set to non-nil.")
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.ahk\\'" . ahk-mode))
;;; keymap
(defvar ahk-mode-map
(let ((map (make-sparse-keymap)))
;; key bindings
(define-key map (kbd "C-c C-?") #'ahk-lookup-web)
(define-key map (kbd "C-c M-i") #'ahk-indent-message)
(define-key map (kbd "C-c C-c") #'ahk-comment-dwim)
(define-key map (kbd "C-c C-b") #'ahk-comment-block-dwim)
(define-key map (kbd "C-c C-k") #'ahk-run-script)
map)
"Keymap for Autohotkey major mode.")
;;; menu
(easy-menu-define ahk-menu ahk-mode-map
"AHK Mode Commands"
'("AHK"
["Lookup webdocs on command" ahk-lookup-web]
["Execute script" ahk-run-script]
"---"
["Version" ahk-version]))
;;; syntax table
(defvar ahk-mode-syntax-table
(let ((syntax-table (make-syntax-table)))
;; these are also allowed in variable names
(modify-syntax-entry ?# "w" syntax-table)
(modify-syntax-entry ?_ "w" syntax-table)
(modify-syntax-entry ?@ "w" syntax-table)
;; some additional characters used in paths and switches
(modify-syntax-entry ?\\ "w" syntax-table)
(modify-syntax-entry ?\; "< b" syntax-table)
;; for multiline comments
(modify-syntax-entry ?\/ ". 14" syntax-table)
(modify-syntax-entry ?* ". 23" syntax-table)
;; New line
(modify-syntax-entry ?\n "> b" syntax-table)
;; ` is escape
(modify-syntax-entry ?` "\\" syntax-table)
syntax-table)
"Syntax table for `ahk-mode'.")
;;; imenu support
(defconst ahk-imenu-generic-expression
'(("Functions" "^\s*\\(.*\\)(.*)[\n]{" 1)
("Labels" "^\s*\\([^:]+\\):\n" 1)
("Keybindings" "^\s*\\(.+?\\)::" 1)
("Comments" "^; \\(.+\\)" 1))
"imenu index for `ahk-mode'")
(defun ahk-run-script ()
"Run the ahk-script in the current buffer."
(interactive)
(let ((file (shell-quote-argument
(replace-regexp-in-string " " "\ "
(replace-regexp-in-string "\/" "\\\\" (buffer-file-name) t t)))))
(message "Executing script %s" file)
(w32-shell-execute "open" file)))
(defun ahk-command-at-point ()
"Determine command at point, and prompt if nothing found."
(let ((command (or (if (region-active-p)
(buffer-substring-no-properties
(region-beginning)
(region-end))
(thing-at-point 'symbol))
(read-string "Command: "))))
command))
(defun ahk-lookup-web ()
"Look up current word in AutoHotkey's reference doc.
Launches default browser and opens the doc's url."
(interactive)
(let* ((acap (ahk-command-at-point))
(url (concat "http://ahkscript.org/docs/commands/" acap ".htm")))
(browse-url url)))
(defun ahk-version ()
"Show the `ahk-mode' version in the echo area."
(interactive)
(message "ahk-mode version %s" ahk-mode-version))
;;;; indentation
(defun ahk-calc-indentation (str &optional offset)
"Calculate the current indentation level of argument"
(let ((i (* (or offset 0) ahk-indentation)))
(while (string-match "\t" str)
(setq i (+ i tab-width)
str (replace-match "" nil t str)))
(setq i (+ i (length str)))
i))
;; the follwing regexp is used to detect if a condition is a one line statement or not,
;; i.e. it matches one line statements but should not match those where the THEN resp.
;; ELSE body is on its own line ...
(defvar ahk-one-line-if-regexp
(concat "^\\([ \t]*\\)" ;; this is used for indentation
"\\("
"If\\(Not\\)?\\("
(regexp-opt '("InString" "InStr"
"Less" "Greater" "Equal"
"LessOrEqual" "GreaterOrEqual"
))
"\\)[^,\n]*,[^,\n]*,[^,\n]*,"
"\\|"
"If\\(Not\\)?Exist[^,\n]*,[^,\n]*,"
"\\|"
"Else[ \t]+\\([^I\n][^f\n][^ \n]\\)"
"\\)"))
(defun ahk-previous-indent ()
"Return the indentation level of the previous non-blank line."
(save-excursion
(forward-line -1)
(while (and (looking-at "^[ \t]*$") (not (bobp)))
(forward-line -1))
(current-indentation)))
(defun ahk-indent-message ()
"Show message for current indentation level"
(interactive)
(message (format "%s" (current-indentation))))
(defun ahk-indent-line ()
"Indent the current line."
(interactive)
(let ((indent 0)
(opening-brace nil)
(else nil)
(label nil)
(closing-brace nil)
(loop nil)
(prev-single nil)
(return nil)
(empty-brace nil)
(block-skip nil)
(case-fold-search t))
;; do a backward search to determine the indentation level
(save-excursion
(beginning-of-line)
;; save type of current line
(setq opening-brace (looking-at "^[ \t]*{[^}]"))
(setq opening-paren (looking-at "^[ \t]*([^)]"))
(setq if-else (looking-at "^[ \t]*\\([iI]f\\|[Ee]lse\\)"))
(setq loop (looking-at "^[ \t]*\\([Ll]oop\\)[^{]+"))
(setq closing-brace (looking-at "^[ \t]*\\([)}]\\|\\*\\/\\)$"))
(setq label (looking-at "^[ \t]*[^:\n ]+:$"))
(setq keybinding (looking-at "^[ \t]*[^:\n ]+::\\(.*\\)$"))
(setq return (looking-at "^\\([ \t]*\\)[rR]eturn"))
(setq blank (looking-at "^\\([ \t]*\\)\n"))
;; skip previous empty lines and commented lines
(setq indent (ahk-previous-indent))
(setq prev (ahk-previous-indent))
(save-excursion
(when closing-brace
(progn
(beginning-of-line)
(re-search-forward "\\(}\\|)\\)" nil t)
(backward-list)
(setq block-skip t)
(setq indent (current-indentation))
)
)
)
(forward-line -1)
(while (and
(or (looking-at "^[ \t]*$") (looking-at "^;"))
(not (bobp)))
(forward-line -1))
;; we are now at the previous non-empty /non comment line
(beginning-of-line)
;; default to previous indentation
(cond
(block-skip
nil)
(blank
(setq indent 0))
(closing-brace
(setq indent (- indent ahk-indentation)))
;; if beginning with a comment, indent based on previous line
((looking-at "^\\([ \t*]\\);")
(setq indent (ahk-previous-indent)))
;; keybindings
((and (looking-at "^[ \t]*[^:\n ]+:$")
(not label))
(setq indent (+ indent ahk-indentation)))
;; return
((looking-at "^\\([ \t]*\\)[rR]eturn")
(setq indent (- indent ahk-indentation)))
;; label
(label
(setq indent 0))
((and
(not opening-brace)
(not block-skip)
(looking-at "^[^: \n]+:$")
(looking-at "^[^:\n]+:\\([^:\n]*\\)?[ ]*$"))
(setq indent (+ indent ahk-indentation)))
;; opening brace
((looking-at "^\\([ \t]*\\)[{(]$")
(and
(setq empty-brace t)
(setq indent (+ indent ahk-indentation))))
;; brace at end of line
((or
(looking-at "^\\([ \t]*\\).*[{][^}]*$")
(looking-at "^\\([ \t]*\\).*[(][^)]*$"))
(setq indent (+ indent ahk-indentation)))
;; If/Else with body on next line, but not opening { or (
((and (not opening-brace)
(not block-skip)
;; (or if-else loop)
(or
(looking-at "^[ \t]*\\([Ll]oop\\)[^{=\n]*")
(looking-at "^\\([ \t]*\\)\\([iI]f\\|[eE]lse\\)[^{]*\n"))
)
(and
(setq prev-single t)
(setq indent (+ indent ahk-indentation))))
;; (return
;; (setq indent (- indent ahk-indentation)))
;; subtract indentation if closing bracket only
;; ((looking-at "^[ \t]*[})]")
;; (setq indent (- indent ahk-indentation)))
;; zero indentation when at label or keybinding
((or (looking-at "^[ \t]*[^,: \t\n]*:$")
(looking-at "^;;;"))
(setq indent 0)))
;; check for single line if/else
(forward-line -1)
(when (and
(not block-skip)
(not empty-brace)
(or
(looking-at "^[ \t]*\\([Ll]oop\\)[^{\n]+")
(looking-at "^\\([ ]*\\)\\([iI]f\\|[eE]lse\\)[^{\n]+")))
;; adjust when stacking multiple single line commands
(setq indent (- indent (if prev-single (- (* 2 ahk-indentation)) 0) ahk-indentation)))
)
;; set negative indentation to 0
(save-excursion
(beginning-of-line)
(if (< indent 0)
(setq indent 0))
;; actual indentation performed here
(if (looking-at "^[ \t]+")
(replace-match ""))
(indent-to indent))
(when ahk-debug
(message (format
"indent: %s, current: %s previous: %s
ob: %s, op: %s, cb: %s, bs: %s,
if-else: %s, l: %s, kb: %s, ret: %s, bl: %s"
indent
(current-indentation)
(ahk-previous-indent)
opening-brace
opening-paren
closing-brace
block-skip
if-else
label
keybinding
return
blank
)))))
(defun ahk-indent-region (start end)
(interactive "r")
(save-excursion
(goto-char start)
(while (and (not (eobp)) (< (point) end))
(ahk-indent-line)
(forward-line 1))))
;;;; commenting
(defun ahk-comment-dwim (arg)
"Comment or uncomment current line or region in a smart way.
For details, see `comment-dwim'."
(interactive "*P")
(require 'newcomment)
(let ((comment-start ";")
(comment-end ""))
(comment-dwim arg)))
(defun ahk-comment-block-dwim (arg)
"Comment or uncomment current line or region using block notation.
For details, see `comment-dwim'."
(interactive "*P")
(require 'newcomment)
(let ((comment-style 'extra-line)
(comment-start "/*")
(comment-end "*/"))
(comment-dwim arg)))
;;; font-lock
(defvar ahk-commands
'("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" "StatusBar" "StatusBarGetText" "StatusBarWait" "StatusCD" "StringCaseSense" "StringGetPos" "StringLeft" "StringLen" "StringLower" "StringMid" "StringReplace" "StringRight" "StringSplit" "StringTrimLeft" "StringTrimRight" "StringUpper" "Style" "Submit" "Suspend" "SysGet" "SysMenu" "Tab" "Tab2" "TabStop" "Tan" "Text" "Theme" "Thread" "Throw" "Tile" "Time" "Tip" "ToggleCheck" "ToggleEnable" "ToolTip" "ToolWindow" "Top" "Topmost" "TransColor" "Transform" "Transparent" "Tray" "TrayTip" "TreeView" "Trim" "Try" "TryAgain" "Type" "UnCheck" "Unicode" "Unlock" "Until" "UpDown" "Upper" "Uppercase" "UrlDownloadToFile" "UseErrorLevel" "VScroll" "Var" "Vis" "VisFirst" "Visible" "Wait" "WaitClose" "WantCtrlA" "WantF2" "WantReturn" "While-loop" "WinActivate" "WinActivateBottom" "WinClose" "WinGet" "WinGetActiveStats" "WinGetActiveTitle" "WinGetClass" "WinGetPos" "WinGetText" "WinGetTitle" "WinHide" "WinKill" "WinMaximize" "WinMenuSelectItem" "WinMinimize" "WinMinimizeAll" "WinMinimizeAllUndo" "WinMove" "WinRestore" "WinSet" "WinSetTitle" "WinShow" "WinWait" "WinWaitActive" "WinWaitClose" "WinWaitNotActive" "Wrap" "Xdigit" "Yes" "ahk_class" "ahk_group" "ahk_id" "ahk_pid" "bold" "global" "italic" "local" "norm" "static" "strike" "underline" "xm" "xp" "xs" "ym" "yp" "ys")
"AHK keywords.")
(defvar ahk-directives
'("#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")
"AHK directives")
(defvar ahk-functions
'("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")
"AHK functions.")
(defvar ahk-variables
'("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")
"AHK variables.")
(defvar ahk-keys
'("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")
"AHK keywords for keys.")
(defvar ahk-operators
'("\\!" "!=" "&" "&&" "&=" "*" "**" "*=" "+" "++" "+=" "-" "--" "-=" "." "." ".=" "/" "//" "//=" "/=" ":=" "<" "<<" "<<=" "<=" "<>" "=" "==" ">" ">=" ">>" ">>=" "?:" "AND" "NOT" "OR" "^" "^=" "|" "|=" "||" "~" "~=" ",")
"AHK operators.")
(defvar ahk-commands-regexp (regexp-opt ahk-commands 'words))
(defvar ahk-functions-regexp (regexp-opt ahk-functions 'words))
(defvar ahk-directives-regexp (regexp-opt ahk-directives 'words))
(defvar ahk-variables-regexp (regexp-opt ahk-variables 'words))
(defvar ahk-keys-regexp (regexp-opt ahk-keys 'words))
(defvar ahk-operators-regexp (regexp-opt ahk-operators))
(defvar ahk-double-quote-string-re "[\"]\\(\\\\.\\|[^\"\n]\\)*[\"]"
"Regexp used to match a double-quoted string literal")
(defvar ahk-single-quote-string-re "[']\\(\\\\.\\|[^'\n]\\)*[']"
"Regexp used to match a single-quoted string literal")
(defvar ahk-font-lock-keywords
`(("\\s-*;.*$" . font-lock-comment-face)
;; lLTrim0 usage
("(LTrim0\\(.*\n\\)+" . font-lock-string-face)
(,ahk-double-quote-string-re . font-lock-string-face)
(,ahk-single-quote-string-re . font-lock-string-face)
;; block comments
("^/\\*\\(.*\r?\n\\)*\\(\\*/\\)?" . font-lock-comment-face)
;; bindings
("^\\([^\t\n:=]+\\)::" . (1 font-lock-constant-face))
;; labels
("^\\([^\t\n :=]+\\):[^=]" . (1 font-lock-doc-face))
;; return
("[Rr]eturn" . font-lock-warning-face)
;; functions
("^\\([^\t\n (]+\\)\\((.*)\\)" . (1 font-lock-function-name-face))
;; variables
("%[^% ]+%" . font-lock-variable-name-face)
(,ahk-commands-regexp . font-lock-keyword-face)
(,ahk-functions-regexp . font-lock-function-name-face)
(,ahk-directives-regexp . font-lock-preprocessor-face)
(,ahk-variables-regexp . font-lock-variable-name-face)
(,ahk-keys-regexp . font-lock-constant-face)
(,ahk-operators-regexp . font-lock-builtin-face)
;; note: order matters
))
;; keyword completion
(defvar ahk-kwd-list (make-hash-table :test 'equal)
"AHK keywords.")
(defvar ahk-all-keywords (append ahk-commands ahk-functions ahk-variables)
"List of all ahk keywords.")
(mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-commands)
(mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-functions)
(mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-directives)
(mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-variables)
(mapc (lambda (x) (puthash x t ahk-kwd-list)) ahk-keys)
(put 'ahk-kwd-list 'risky-local-variable t)
(defun ahk-completion-at-point ()
"Complete the current work using the list of all syntax's."
(interactive)
(let ((pt (point)))
(if (and (or (save-excursion (re-search-backward "\\<\\w+"))
(looking-at "\\<\\w+"))
(= (match-end 0) pt))
(let ((start (match-beginning 0))
(prefix (match-string 0))
(completion-ignore-case t)
completions)
(list start pt (all-completions prefix ahk-all-keywords) :exclusive 'no :annotation-function 'ahk-company-annotation)))))
(defun ahk-company-annotation (candidate)
"Annotate company mode completions based on source."
(cond
((member candidate ahk-commands)
"c")
((member candidate ahk-functions)
"f")
((member candidate ahk-variables)
"v")
((member candidate ahk-directives)
"d")
((member candidate ahk-keys)
"k")
(t "")))
(defvar ac-source-ahk
'((candidates . (all-completions ac-prefix ahk-all-keywords))
(limit . nil)
(symbol . "f"))
"Completion for AHK mode")
(defvar ac-source-keys-ahk
'((candidates . (all-completions ac-prefix ahk-keys))
(limit . nil)
(symbol . "k"))
"Completion for AHK keys mode")
(defvar ac-source-directives-ahk
'((candidates . (all-completions ac-prefix ahk-directives))
(limit . nil)
(symbol . "d"))
"Completion for AHK directives mode")
;; clear memory
;; (setq ahk-commands nil)
;; (setq ahk-functions nil)
;; (setq ahk-directives nil)
;; (setq ahk-variables nil)
;; (setq ahk-keys nil)
(defun ahk-font-lock-extend-region ()
"Extend the search region to include an entire block of text."
;; Avoid compiler warnings about these global variables from font-lock.el.
;; See the documentation for variable `font-lock-extend-region-functions'.
(eval-when-compile (defvar font-lock-beg) (defvar font-lock-end))
(save-excursion
(goto-char font-lock-beg)
(let ((found (or (re-search-backward "(LTrim0" nil t) (point-min))))
(goto-char font-lock-end)
(when (re-search-forward "\n)" nil t)
(beginning-of-line)
(setq font-lock-end (point)))
(setq font-lock-beg found))))
(defun ahk-ltrim-blocks ()
"Match JavaScript blocks from the point to LAST."
(cond ((re-search-backward "(LTrim0" nil t)
(let ((beg (match-beginning 0)))
(cond ((re-search-forward "\n)" nil t)
(set-match-data (list beg (point)))
t)
(t nil))))
(t nil)))
;;;###autoload
(define-derived-mode ahk-mode prog-mode "AutoHotkey Mode"
"Major mode for editing AutoHotkey script (AHK).
The hook functions in `ahk-mode-hook' are run after mode initialization.
Key Bindings
\\{ahk-mode-map}"
(kill-all-local-variables)
(set-syntax-table ahk-mode-syntax-table)
(setq major-mode 'ahk-mode
mode-name "AHK"
local-abbrev-table ahk-mode-abbrev-table)
;; ui
(use-local-map ahk-mode-map)
(easy-menu-add ahk-menu)
;; imenu
(setq-local imenu-generic-expression ahk-imenu-generic-expression)
(setq-local imenu-sort-function 'imenu--sort-by-position)
;; font-lock
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults '((ahk-font-lock-keywords) nil t))
;; (set (make-local-variable 'font-lock-multiline) t)
;; (add-hook 'font-lock-extend-region-functions
;; 'ahk-font-lock-extend-region)
;; (setq syntax-propertize-function)
;; clear memory
;; (setq ahk-commands-regexp nil)
;; (setq ahk-functions-regexp nil)
;; (setq ahk-variables-regexp nil)
;; (setq ahk-keys-regexp nil)
(if (boundp 'evil-shift-width)
(setq-local evil-shift-width ahk-indentation))
(setq-local comment-start ";")
(setq-local comment-end "")
(setq-local comment-start-skip ";+ *")
(setq-local block-comment-start "/*")
(setq-local block-comment-end "*/")
(setq-local block-comment-left " * ")
(setq-local block-comment-right " *")
(setq-local block-comment-top-right "")
(setq-local block-comment-bot-left " ")
(setq-local block-comment-char ?*)
(setq-local indent-line-function 'ahk-indent-line)
(setq-local indent-region-function 'ahk-indent-region)
(setq-local parse-sexp-ignore-comments t)
(setq-local parse-sexp-lookup-properties t)
(setq-local paragraph-start (concat "$\\|" page-delimiter))
(setq-local paragraph-separate paragraph-start)
(setq-local paragraph-ignore-fill-prefix t)
;; completion
(setq-local company-tooltip-align-annotations t)
(add-hook 'completion-at-point-functions 'ahk-completion-at-point nil t)
(eval-after-load "auto-complete"
'(when (listp 'ac-sources)
(progn
(make-local-variable 'ac-sources)
(add-to-list 'ac-sources 'ac-source-ahk)
(add-to-list 'ac-sources 'ac-source-directives-ahk)
(add-to-list 'ac-sources 'ac-source-keys-ahk))))
(run-mode-hooks 'ahk-mode-hook))
(when ahk-debug
(mapc (lambda (buffer)
(with-current-buffer buffer
(when (eq major-mode 'ahk-mode)
(message "%s" buffer)
(font-lock-mode -1)
(ahk-mode))))
(buffer-list)))
(provide 'ahk-mode)
;;; ahk-mode.el ends here