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.

2106 lines
83 KiB

  1. ;;; lua-mode.el --- a major-mode for editing Lua scripts -*- lexical-binding: t -*-
  2. ;; Author: 2011-2013 immerrr <immerrr+lua@gmail.com>
  3. ;; 2010-2011 Reuben Thomas <rrt@sc3d.org>
  4. ;; 2006 Juergen Hoetzel <juergen@hoetzel.info>
  5. ;; 2004 various (support for Lua 5 and byte compilation)
  6. ;; 2001 Christian Vogler <cvogler@gradient.cis.upenn.edu>
  7. ;; 1997 Bret Mogilefsky <mogul-lua@gelatinous.com> starting from
  8. ;; tcl-mode by Gregor Schmid <schmid@fb3-s7.math.tu-berlin.de>
  9. ;; with tons of assistance from
  10. ;; Paul Du Bois <pld-lua@gelatinous.com> and
  11. ;; Aaron Smith <aaron-lua@gelatinous.com>.
  12. ;;
  13. ;; URL: http://immerrr.github.com/lua-mode
  14. ;; Version: 20151025
  15. ;; Package-Requires: ((emacs "24.3"))
  16. ;;
  17. ;; This file is NOT part of Emacs.
  18. ;;
  19. ;; This program is free software; you can redistribute it and/or
  20. ;; modify it under the terms of the GNU General Public License
  21. ;; as published by the Free Software Foundation; either version 2
  22. ;; of the License, or (at your option) any later version.
  23. ;;
  24. ;; This program is distributed in the hope that it will be useful,
  25. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. ;; GNU General Public License for more details.
  28. ;;
  29. ;; You should have received a copy of the GNU General Public License
  30. ;; along with this program; if not, write to the Free Software
  31. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  32. ;; MA 02110-1301, USA.
  33. ;; Keywords: languages, processes, tools
  34. ;; This field is expanded to commit SHA and commit date during the
  35. ;; archive creation.
  36. ;; Revision: $Format:%h (%cD)$
  37. ;;
  38. ;;; Commentary:
  39. ;; lua-mode provides support for editing Lua, including automatic
  40. ;; indentation, syntactical font-locking, running interactive shell,
  41. ;; interacting with `hs-minor-mode' and online documentation lookup.
  42. ;; The following variables are available for customization (see more via
  43. ;; `M-x customize-group lua`):
  44. ;; - Var `lua-indent-level':
  45. ;; indentation offset in spaces
  46. ;; - Var `lua-indent-string-contents':
  47. ;; set to `t` if you like to have contents of multiline strings to be
  48. ;; indented like comments
  49. ;; - Var `lua-indent-nested-block-content-align':
  50. ;; set to `nil' to stop aligning the content of nested blocks with the
  51. ;; open parenthesis
  52. ;; - Var `lua-indent-close-paren-align':
  53. ;; set to `t' to align close parenthesis with the open parenthesis,
  54. ;; rather than with the beginning of the line
  55. ;; - Var `lua-mode-hook':
  56. ;; list of functions to execute when lua-mode is initialized
  57. ;; - Var `lua-documentation-url':
  58. ;; base URL for documentation lookup
  59. ;; - Var `lua-documentation-function': function used to
  60. ;; show documentation (`eww` is a viable alternative for Emacs 25)
  61. ;; These are variables/commands that operate on the Lua process:
  62. ;; - Var `lua-default-application':
  63. ;; command to start the Lua process (REPL)
  64. ;; - Var `lua-default-command-switches':
  65. ;; arguments to pass to the Lua process on startup (make sure `-i` is there
  66. ;; if you expect working with Lua shell interactively)
  67. ;; - Cmd `lua-start-process': start new REPL process, usually happens automatically
  68. ;; - Cmd `lua-kill-process': kill current REPL process
  69. ;; These are variables/commands for interaction with the Lua process:
  70. ;; - Cmd `lua-show-process-buffer': switch to REPL buffer
  71. ;; - Cmd `lua-hide-process-buffer': hide window showing REPL buffer
  72. ;; - Var `lua-always-show': show REPL buffer after sending something
  73. ;; - Cmd `lua-send-buffer': send whole buffer
  74. ;; - Cmd `lua-send-current-line': send current line
  75. ;; - Cmd `lua-send-defun': send current top-level function
  76. ;; - Cmd `lua-send-region': send active region
  77. ;; - Cmd `lua-restart-with-whole-file': restart REPL and send whole buffer
  78. ;; See "M-x apropos-command ^lua-" for a list of commands.
  79. ;; See "M-x customize-group lua" for a list of customizable variables.
  80. ;;; Code:
  81. (eval-when-compile
  82. (require 'cl-lib))
  83. (require 'comint)
  84. (require 'newcomment)
  85. (require 'rx)
  86. ;; rx-wrappers for Lua
  87. (eval-when-compile
  88. ;; Silence compilation warning about `compilation-error-regexp-alist' defined
  89. ;; in compile.el.
  90. (require 'compile))
  91. (eval-and-compile
  92. (if (fboundp #'rx-let)
  93. (progn
  94. ;; Emacs 27+ way of customizing rx
  95. (defvar lua--rx-bindings)
  96. (setq
  97. lua--rx-bindings
  98. '((symbol (&rest x) (seq symbol-start (or x) symbol-end))
  99. (ws (* (any " \t")))
  100. (ws+ (+ (any " \t")))
  101. (lua-name (symbol (seq (+ (any alpha "_")) (* (any alnum "_")))))
  102. (lua-funcname (seq lua-name (* ws "." ws lua-name)
  103. (opt ws ":" ws lua-name)))
  104. (lua-funcheader
  105. ;; Outer (seq ...) is here to shy-group the definition
  106. (seq (or (seq (symbol "function") ws (group-n 1 lua-funcname))
  107. (seq (group-n 1 lua-funcname) ws "=" ws
  108. (symbol "function")))))
  109. (lua-number
  110. (seq (or (seq (+ digit) (opt ".") (* digit))
  111. (seq (* digit) (opt ".") (+ digit)))
  112. (opt (regexp "[eE][+-]?[0-9]+"))))
  113. (lua-assignment-op (seq "=" (or buffer-end (not (any "=")))))
  114. (lua-operator (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<"
  115. ">" "=" ";" ":" "," "." ".." "..."))
  116. (lua-keyword-operator (symbol "and" "not" "or"))
  117. (lua-keyword
  118. (symbol "break" "do" "else" "elseif" "end" "for" "function"
  119. "goto" "if" "in" "local" "repeat" "return"
  120. "then" "until" "while"))
  121. (lua-up-to-9-variables
  122. (seq (group-n 1 lua-name) ws
  123. (? "," ws (group-n 2 lua-name) ws
  124. (? "," ws (group-n 3 lua-name) ws
  125. (? "," ws (group-n 4 lua-name) ws
  126. (? "," ws (group-n 5 lua-name) ws
  127. (? "," ws (group-n 6 lua-name) ws
  128. (? "," ws (group-n 7 lua-name) ws
  129. (? "," ws (group-n 8 lua-name) ws
  130. (? "," ws (group-n 9 lua-name) ws))))))))))))
  131. (defmacro lua-rx (&rest regexps)
  132. (eval `(rx-let ,lua--rx-bindings
  133. (rx ,@regexps))))
  134. (defun lua-rx-to-string (form &optional no-group)
  135. (rx-let-eval lua--rx-bindings
  136. (rx-to-string form no-group))))
  137. (progn
  138. ;; Pre-Emacs 27 way of customizing rx
  139. (defvar lua-rx-constituents)
  140. (defvar rx-parent)
  141. (defun lua-rx-to-string (form &optional no-group)
  142. "Lua-specific replacement for `rx-to-string'.
  143. See `rx-to-string' documentation for more information FORM and
  144. NO-GROUP arguments."
  145. (let ((rx-constituents lua-rx-constituents))
  146. (rx-to-string form no-group)))
  147. (defmacro lua-rx (&rest regexps)
  148. "Lua-specific replacement for `rx'.
  149. See `rx' documentation for more information about REGEXPS param."
  150. (cond ((null regexps)
  151. (error "No regexp"))
  152. ((cdr regexps)
  153. (lua-rx-to-string `(and ,@regexps) t))
  154. (t
  155. (lua-rx-to-string (car regexps) t))))
  156. (defun lua--new-rx-form (form)
  157. "Add FORM definition to `lua-rx' macro.
  158. FORM is a cons (NAME . DEFN), see more in `rx-constituents' doc.
  159. This function enables specifying new definitions using old ones:
  160. if DEFN is a list that starts with `:rx' symbol its second
  161. element is itself expanded with `lua-rx-to-string'. "
  162. (let ((form-definition (cdr form)))
  163. (when (and (listp form-definition) (eq ':rx (car form-definition)))
  164. (setcdr form (lua-rx-to-string (cadr form-definition) 'nogroup)))
  165. (push form lua-rx-constituents)))
  166. (defun lua--rx-symbol (form)
  167. ;; form is a list (symbol XXX ...)
  168. ;; Skip initial 'symbol
  169. (setq form (cdr form))
  170. ;; If there's only one element, take it from the list, otherwise wrap the
  171. ;; whole list into `(or XXX ...)' form.
  172. (setq form (if (eq 1 (length form))
  173. (car form)
  174. (append '(or) form)))
  175. (and (fboundp 'rx-form) ; Silence Emacs 27's byte-compiler.
  176. (rx-form `(seq symbol-start ,form symbol-end) rx-parent)))
  177. (setq lua-rx-constituents (copy-sequence rx-constituents))
  178. (mapc 'lua--new-rx-form
  179. `((symbol lua--rx-symbol 1 nil)
  180. (ws . "[ \t]*") (ws+ . "[ \t]+")
  181. (lua-name :rx (symbol (regexp "[[:alpha:]_]+[[:alnum:]_]*")))
  182. (lua-funcname
  183. :rx (seq lua-name (* ws "." ws lua-name)
  184. (opt ws ":" ws lua-name)))
  185. (lua-funcheader
  186. ;; Outer (seq ...) is here to shy-group the definition
  187. :rx (seq (or (seq (symbol "function") ws (group-n 1 lua-funcname))
  188. (seq (group-n 1 lua-funcname) ws "=" ws
  189. (symbol "function")))))
  190. (lua-number
  191. :rx (seq (or (seq (+ digit) (opt ".") (* digit))
  192. (seq (* digit) (opt ".") (+ digit)))
  193. (opt (regexp "[eE][+-]?[0-9]+"))))
  194. (lua-assignment-op
  195. :rx (seq "=" (or buffer-end (not (any "=")))))
  196. (lua-operator
  197. :rx (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<"
  198. ">" "=" ";" ":" "," "." ".." "..."))
  199. (lua-keyword-operator
  200. :rx (symbol "and" "not" "or"))
  201. (lua-keyword
  202. :rx (symbol "break" "do" "else" "elseif" "end" "for" "function"
  203. "goto" "if" "in" "local" "repeat" "return"
  204. "then" "until" "while"))
  205. (lua-up-to-9-variables
  206. :rx (seq (group-n 1 lua-name) ws
  207. (? "," ws (group-n 2 lua-name) ws
  208. (? "," ws (group-n 3 lua-name) ws
  209. (? "," ws (group-n 4 lua-name) ws
  210. (? "," ws (group-n 5 lua-name) ws
  211. (? "," ws (group-n 6 lua-name) ws
  212. (? "," ws (group-n 7 lua-name) ws
  213. (? "," ws (group-n 8 lua-name) ws
  214. (? "," ws (group-n 9 lua-name) ws)))))))))))))))
  215. ;; Local variables
  216. (defgroup lua nil
  217. "Major mode for editing Lua code."
  218. :prefix "lua-"
  219. :group 'languages)
  220. (defcustom lua-indent-level 3
  221. "Amount by which Lua subexpressions are indented."
  222. :type 'integer
  223. :group 'lua
  224. :safe #'integerp)
  225. (defcustom lua-comment-start "-- "
  226. "Default value of `comment-start'."
  227. :type 'string
  228. :group 'lua)
  229. (defcustom lua-comment-start-skip "---*[ \t]*"
  230. "Default value of `comment-start-skip'."
  231. :type 'string
  232. :group 'lua)
  233. (defcustom lua-default-application "lua"
  234. "Default application to run in Lua process.
  235. Can be a string, where it denotes a command to be executed to
  236. start Lua process, or a (HOST . PORT) cons, that can be used to
  237. connect to Lua process running remotely."
  238. :type '(choice (string)
  239. (cons string integer))
  240. :group 'lua)
  241. (defcustom lua-default-command-switches (list "-i")
  242. "Command switches for `lua-default-application'.
  243. Should be a list of strings."
  244. :type '(repeat string)
  245. :group 'lua)
  246. (make-variable-buffer-local 'lua-default-command-switches)
  247. (defcustom lua-always-show t
  248. "*Non-nil means display lua-process-buffer after sending a command."
  249. :type 'boolean
  250. :group 'lua)
  251. (defcustom lua-documentation-function 'browse-url
  252. "Function used to fetch the Lua reference manual."
  253. :type `(radio (function-item browse-url)
  254. ,@(when (fboundp 'eww) '((function-item eww)))
  255. ,@(when (fboundp 'w3m-browse-url) '((function-item w3m-browse-url)))
  256. (function :tag "Other function"))
  257. :group 'lua)
  258. (defcustom lua-documentation-url
  259. (or (and (file-readable-p "/usr/share/doc/lua/manual.html")
  260. "file:///usr/share/doc/lua/manual.html")
  261. "http://www.lua.org/manual/5.1/manual.html")
  262. "URL pointing to the Lua reference manual."
  263. :type 'string
  264. :group 'lua)
  265. (defvar lua-process nil
  266. "The active Lua process")
  267. (defvar lua-process-buffer nil
  268. "Buffer used for communication with the Lua process")
  269. (defun lua--customize-set-prefix-key (prefix-key-sym prefix-key-val)
  270. (cl-assert (eq prefix-key-sym 'lua-prefix-key))
  271. (set prefix-key-sym (if (and prefix-key-val (> (length prefix-key-val) 0))
  272. ;; read-kbd-macro returns a string or a vector
  273. ;; in both cases (elt x 0) is ok
  274. (elt (read-kbd-macro prefix-key-val) 0)))
  275. (if (fboundp 'lua-prefix-key-update-bindings)
  276. (lua-prefix-key-update-bindings)))
  277. (defcustom lua-prefix-key "\C-c"
  278. "Prefix for all lua-mode commands."
  279. :type 'string
  280. :group 'lua
  281. :set 'lua--customize-set-prefix-key
  282. :get '(lambda (sym)
  283. (let ((val (eval sym))) (if val (single-key-description (eval sym)) ""))))
  284. (defvar lua-mode-menu (make-sparse-keymap "Lua")
  285. "Keymap for lua-mode's menu.")
  286. (defvar lua-prefix-mode-map
  287. (eval-when-compile
  288. (let ((result-map (make-sparse-keymap)))
  289. (mapc (lambda (key_defn)
  290. (define-key result-map (read-kbd-macro (car key_defn)) (cdr key_defn)))
  291. '(("C-l" . lua-send-buffer)
  292. ("C-f" . lua-search-documentation)))
  293. result-map))
  294. "Keymap that is used to define keys accessible by `lua-prefix-key'.
  295. If the latter is nil, the keymap translates into `lua-mode-map' verbatim.")
  296. (defvar lua--electric-indent-chars
  297. (mapcar #'string-to-char '("}" "]" ")")))
  298. (defvar lua-mode-map
  299. (let ((result-map (make-sparse-keymap)))
  300. (unless (boundp 'electric-indent-chars)
  301. (mapc (lambda (electric-char)
  302. (define-key result-map
  303. (read-kbd-macro
  304. (char-to-string electric-char))
  305. #'lua-electric-match))
  306. lua--electric-indent-chars))
  307. (define-key result-map [menu-bar lua-mode] (cons "Lua" lua-mode-menu))
  308. (define-key result-map [remap backward-up-list] 'lua-backward-up-list)
  309. ;; FIXME: see if the declared logic actually works
  310. ;; handle prefix-keyed bindings:
  311. ;; * if no prefix, set prefix-map as parent, i.e.
  312. ;; if key is not defined look it up in prefix-map
  313. ;; * if prefix is set, bind the prefix-map to that key
  314. (if (boundp 'lua-prefix-key)
  315. (define-key result-map (vector lua-prefix-key) lua-prefix-mode-map)
  316. (set-keymap-parent result-map lua-prefix-mode-map))
  317. result-map)
  318. "Keymap used in lua-mode buffers.")
  319. (defvar lua-electric-flag t
  320. "If t, electric actions (like automatic reindentation) will happen when an electric
  321. key like `{' is pressed")
  322. (make-variable-buffer-local 'lua-electric-flag)
  323. (defcustom lua-prompt-regexp "[^\n]*\\(>[\t ]+\\)+$"
  324. "Regexp which matches the Lua program's prompt."
  325. :type 'regexp
  326. :group 'lua)
  327. (defcustom lua-traceback-line-re
  328. ;; This regexp skips prompt and meaningless "stdin:N:" prefix when looking
  329. ;; for actual file-line locations.
  330. "^\\(?:[\t ]*\\|.*>[\t ]+\\)\\(?:[^\n\t ]+:[0-9]+:[\t ]*\\)*\\(?:\\([^\n\t ]+\\):\\([0-9]+\\):\\)"
  331. "Regular expression that describes tracebacks and errors."
  332. :type 'regexp
  333. :group 'lua)
  334. (defvar lua--repl-buffer-p nil
  335. "Buffer-local flag saying if this is a Lua REPL buffer.")
  336. (make-variable-buffer-local 'lua--repl-buffer-p)
  337. (defadvice compilation-find-file (around lua--repl-find-file
  338. (marker filename directory &rest formats)
  339. activate)
  340. "Return Lua REPL buffer when looking for \"stdin\" file in it."
  341. (if (and
  342. lua--repl-buffer-p
  343. (string-equal filename "stdin")
  344. ;; NOTE: this doesn't traverse `compilation-search-path' when
  345. ;; looking for filename.
  346. (not (file-exists-p (expand-file-name
  347. filename
  348. (when directory (expand-file-name directory))))))
  349. (setq ad-return-value (current-buffer))
  350. ad-do-it))
  351. (defadvice compilation-goto-locus (around lua--repl-goto-locus
  352. (msg mk end-mk)
  353. activate)
  354. "When message points to Lua REPL buffer, go to the message itself.
  355. Usually, stdin:XX line number points to nowhere."
  356. (let ((errmsg-buf (marker-buffer msg))
  357. (error-buf (marker-buffer mk)))
  358. (if (and (with-current-buffer errmsg-buf lua--repl-buffer-p)
  359. (eq error-buf errmsg-buf))
  360. (progn
  361. (compilation-set-window (display-buffer (marker-buffer msg)) msg)
  362. (goto-char msg))
  363. ad-do-it)))
  364. (defcustom lua-indent-string-contents nil
  365. "If non-nil, contents of multiline string will be indented.
  366. Otherwise leading amount of whitespace on each line is preserved."
  367. :group 'lua
  368. :type 'boolean
  369. :safe #'booleanp)
  370. (defcustom lua-indent-nested-block-content-align t
  371. "If non-nil, the contents of nested blocks are indented to
  372. align with the column of the opening parenthesis, rather than
  373. just forward by `lua-indent-level'."
  374. :group 'lua
  375. :type 'boolean
  376. :safe #'booleanp)
  377. (defcustom lua-indent-close-paren-align t
  378. "If non-nil, close parenthesis are aligned with their open
  379. parenthesis. If nil, close parenthesis are aligned to the
  380. beginning of the line."
  381. :group 'lua
  382. :type 'boolean
  383. :safe #'booleanp)
  384. (defcustom lua-jump-on-traceback t
  385. "*Jump to innermost traceback location in *lua* buffer. When this
  386. variable is non-nil and a traceback occurs when running Lua code in a
  387. process, jump immediately to the source code of the innermost
  388. traceback location."
  389. :type 'boolean
  390. :group 'lua)
  391. (defcustom lua-mode-hook nil
  392. "Hooks called when Lua mode fires up."
  393. :type 'hook
  394. :group 'lua)
  395. (defvar lua-region-start (make-marker)
  396. "Start of special region for Lua communication.")
  397. (defvar lua-region-end (make-marker)
  398. "End of special region for Lua communication.")
  399. (defvar lua-emacs-menu
  400. '(["Restart With Whole File" lua-restart-with-whole-file t]
  401. ["Kill Process" lua-kill-process t]
  402. ["Hide Process Buffer" lua-hide-process-buffer t]
  403. ["Show Process Buffer" lua-show-process-buffer t]
  404. ["Beginning Of Proc" lua-beginning-of-proc t]
  405. ["End Of Proc" lua-end-of-proc t]
  406. ["Set Lua-Region Start" lua-set-lua-region-start t]
  407. ["Set Lua-Region End" lua-set-lua-region-end t]
  408. ["Send Lua-Region" lua-send-lua-region t]
  409. ["Send Current Line" lua-send-current-line t]
  410. ["Send Region" lua-send-region t]
  411. ["Send Proc" lua-send-proc t]
  412. ["Send Buffer" lua-send-buffer t]
  413. ["Search Documentation" lua-search-documentation t])
  414. "Emacs menu for Lua mode.")
  415. ;; the whole defconst is inside eval-when-compile, because it's later referenced
  416. ;; inside another eval-and-compile block
  417. (eval-and-compile
  418. (defconst
  419. lua--builtins
  420. (let*
  421. ((modules
  422. '("_G" "_VERSION" "assert" "collectgarbage" "dofile" "error" "getfenv"
  423. "getmetatable" "ipairs" "load" "loadfile" "loadstring" "module"
  424. "next" "pairs" "pcall" "print" "rawequal" "rawget" "rawlen" "rawset"
  425. "require" "select" "setfenv" "setmetatable" "tonumber" "tostring"
  426. "type" "unpack" "xpcall" "self"
  427. ("bit32" . ("arshift" "band" "bnot" "bor" "btest" "bxor" "extract"
  428. "lrotate" "lshift" "replace" "rrotate" "rshift"))
  429. ("coroutine" . ("create" "isyieldable" "resume" "running" "status"
  430. "wrap" "yield"))
  431. ("debug" . ("debug" "getfenv" "gethook" "getinfo" "getlocal"
  432. "getmetatable" "getregistry" "getupvalue" "getuservalue"
  433. "setfenv" "sethook" "setlocal" "setmetatable"
  434. "setupvalue" "setuservalue" "traceback" "upvalueid"
  435. "upvaluejoin"))
  436. ("io" . ("close" "flush" "input" "lines" "open" "output" "popen"
  437. "read" "stderr" "stdin" "stdout" "tmpfile" "type" "write"))
  438. ("math" . ("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "cosh"
  439. "deg" "exp" "floor" "fmod" "frexp" "huge" "ldexp" "log"
  440. "log10" "max" "maxinteger" "min" "mininteger" "modf" "pi"
  441. "pow" "rad" "random" "randomseed" "sin" "sinh" "sqrt"
  442. "tan" "tanh" "tointeger" "type" "ult"))
  443. ("os" . ("clock" "date" "difftime" "execute" "exit" "getenv"
  444. "remove" "rename" "setlocale" "time" "tmpname"))
  445. ("package" . ("config" "cpath" "loaded" "loaders" "loadlib" "path"
  446. "preload" "searchers" "searchpath" "seeall"))
  447. ("string" . ("byte" "char" "dump" "find" "format" "gmatch" "gsub"
  448. "len" "lower" "match" "pack" "packsize" "rep" "reverse"
  449. "sub" "unpack" "upper"))
  450. ("table" . ("concat" "insert" "maxn" "move" "pack" "remove" "sort"
  451. "unpack"))
  452. ("utf8" . ("char" "charpattern" "codepoint" "codes" "len"
  453. "offset")))))
  454. (cl-labels
  455. ((module-name-re (x)
  456. (concat "\\(?1:\\_<"
  457. (if (listp x) (car x) x)
  458. "\\_>\\)"))
  459. (module-members-re (x) (if (listp x)
  460. (concat "\\(?:[ \t]*\\.[ \t]*"
  461. "\\_<\\(?2:"
  462. (regexp-opt (cdr x))
  463. "\\)\\_>\\)?")
  464. "")))
  465. (concat
  466. ;; common prefix:
  467. ;; - beginning-of-line
  468. ;; - or neither of [ '.', ':' ] to exclude "foo.string.rep"
  469. ;; - or concatenation operator ".."
  470. "\\(?:^\\|[^:. \t]\\|[.][.]\\)"
  471. ;; optional whitespace
  472. "[ \t]*"
  473. "\\(?:"
  474. ;; any of modules/functions
  475. (mapconcat (lambda (x) (concat (module-name-re x)
  476. (module-members-re x)))
  477. modules
  478. "\\|")
  479. "\\)"))))
  480. "A regexp that matches Lua builtin functions & variables.
  481. This is a compilation of 5.1, 5.2 and 5.3 builtins taken from the
  482. index of respective Lua reference manuals.")
  483. (defvar lua-font-lock-keywords
  484. `(;; highlight the hash-bang line "#!/foo/bar/lua" as comment
  485. ("^#!.*$" . font-lock-comment-face)
  486. ;; Builtin constants
  487. (,(lua-rx (symbol "true" "false" "nil"))
  488. . font-lock-constant-face)
  489. ;; Keywords
  490. (, (lua-rx (or lua-keyword lua-keyword-operator))
  491. . font-lock-keyword-face)
  492. ;; Labels used by the "goto" statement
  493. ;; Highlights the following syntax: ::label::
  494. (,(lua-rx "::" ws lua-name ws "::")
  495. . font-lock-constant-face)
  496. ;; Highlights the name of the label in the "goto" statement like
  497. ;; "goto label"
  498. (,(lua-rx (symbol (seq "goto" ws+ (group-n 1 lua-name))))
  499. (1 font-lock-constant-face))
  500. ;; Highlight Lua builtin functions and variables
  501. (,lua--builtins
  502. (1 font-lock-builtin-face) (2 font-lock-builtin-face nil noerror))
  503. (,(lua-rx (symbol "for") ws+ lua-up-to-9-variables)
  504. (1 font-lock-variable-name-face)
  505. (2 font-lock-variable-name-face nil noerror)
  506. (3 font-lock-variable-name-face nil noerror)
  507. (4 font-lock-variable-name-face nil noerror)
  508. (5 font-lock-variable-name-face nil noerror)
  509. (6 font-lock-variable-name-face nil noerror)
  510. (7 font-lock-variable-name-face nil noerror)
  511. (8 font-lock-variable-name-face nil noerror)
  512. (9 font-lock-variable-name-face nil noerror))
  513. (,(lua-rx (symbol "function") (? ws+ lua-funcname) ws "(" ws lua-up-to-9-variables)
  514. (1 font-lock-variable-name-face)
  515. (2 font-lock-variable-name-face nil noerror)
  516. (3 font-lock-variable-name-face nil noerror)
  517. (4 font-lock-variable-name-face nil noerror)
  518. (5 font-lock-variable-name-face nil noerror)
  519. (6 font-lock-variable-name-face nil noerror)
  520. (7 font-lock-variable-name-face nil noerror)
  521. (8 font-lock-variable-name-face nil noerror)
  522. (9 font-lock-variable-name-face nil noerror))
  523. (,(lua-rx lua-funcheader)
  524. (1 font-lock-function-name-face))
  525. ;; local x, y, z
  526. ;; local x = .....
  527. ;;
  528. ;; NOTE: this is intentionally below funcheader matcher, so that in
  529. ;;
  530. ;; local foo = function() ...
  531. ;;
  532. ;; "foo" is fontified as function-name-face, and variable-name-face is not applied.
  533. (,(lua-rx (symbol "local") ws+ lua-up-to-9-variables)
  534. (1 font-lock-variable-name-face)
  535. (2 font-lock-variable-name-face nil noerror)
  536. (3 font-lock-variable-name-face nil noerror)
  537. (4 font-lock-variable-name-face nil noerror)
  538. (5 font-lock-variable-name-face nil noerror)
  539. (6 font-lock-variable-name-face nil noerror)
  540. (7 font-lock-variable-name-face nil noerror)
  541. (8 font-lock-variable-name-face nil noerror)
  542. (9 font-lock-variable-name-face nil noerror))
  543. (,(lua-rx (or (group-n 1
  544. "@" (symbol "author" "copyright" "field" "release"
  545. "return" "see" "usage" "description"))
  546. (seq (group-n 1 "@" (symbol "param" "class" "name")) ws+
  547. (group-n 2 lua-name))))
  548. (1 font-lock-keyword-face t)
  549. (2 font-lock-variable-name-face t noerror)))
  550. "Default expressions to highlight in Lua mode.")
  551. (defvar lua-imenu-generic-expression
  552. `(("Requires" ,(lua-rx (or bol ";") ws (opt (seq (symbol "local") ws)) (group-n 1 lua-name) ws "=" ws (symbol "require")) 1)
  553. (nil ,(lua-rx (or bol ";") ws (opt (seq (symbol "local") ws)) lua-funcheader) 1))
  554. "Imenu generic expression for lua-mode. See `imenu-generic-expression'.")
  555. (defvar lua-sexp-alist '(("then" . "end")
  556. ("function" . "end")
  557. ("do" . "end")
  558. ("repeat" . "until")))
  559. (defvar lua-mode-abbrev-table nil
  560. "Abbreviation table used in lua-mode buffers.")
  561. (define-abbrev-table 'lua-mode-abbrev-table
  562. '(("end" "end" lua-indent-line :system t)
  563. ("else" "else" lua-indent-line :system t)
  564. ("elseif" "elseif" lua-indent-line :system t)))
  565. (defvar lua-mode-syntax-table
  566. (with-syntax-table (copy-syntax-table)
  567. ;; main comment syntax: begins with "--", ends with "\n"
  568. (modify-syntax-entry ?- ". 12")
  569. (modify-syntax-entry ?\n ">")
  570. ;; main string syntax: bounded by ' or "
  571. (modify-syntax-entry ?\' "\"")
  572. (modify-syntax-entry ?\" "\"")
  573. ;; single-character binary operators: punctuation
  574. (modify-syntax-entry ?+ ".")
  575. (modify-syntax-entry ?* ".")
  576. (modify-syntax-entry ?/ ".")
  577. (modify-syntax-entry ?^ ".")
  578. (modify-syntax-entry ?% ".")
  579. (modify-syntax-entry ?> ".")
  580. (modify-syntax-entry ?< ".")
  581. (modify-syntax-entry ?= ".")
  582. (modify-syntax-entry ?~ ".")
  583. (syntax-table))
  584. "`lua-mode' syntax table.")
  585. ;;;###autoload
  586. (define-derived-mode lua-mode prog-mode "Lua"
  587. "Major mode for editing Lua code."
  588. :abbrev-table lua-mode-abbrev-table
  589. :syntax-table lua-mode-syntax-table
  590. :group 'lua
  591. (setq-local font-lock-defaults '(lua-font-lock-keywords ;; keywords
  592. nil ;; keywords-only
  593. nil ;; case-fold
  594. nil ;; syntax-alist
  595. nil ;; syntax-begin
  596. ))
  597. (setq-local syntax-propertize-function
  598. 'lua--propertize-multiline-bounds)
  599. (setq-local parse-sexp-lookup-properties t)
  600. (setq-local indent-line-function 'lua-indent-line)
  601. (setq-local beginning-of-defun-function 'lua-beginning-of-proc)
  602. (setq-local end-of-defun-function 'lua-end-of-proc)
  603. (setq-local comment-start lua-comment-start)
  604. (setq-local comment-start-skip lua-comment-start-skip)
  605. (setq-local comment-use-syntax t)
  606. (setq-local fill-paragraph-function #'lua--fill-paragraph)
  607. (with-no-warnings
  608. (setq-local comment-use-global-state t))
  609. (setq-local imenu-generic-expression lua-imenu-generic-expression)
  610. (when (boundp 'electric-indent-chars)
  611. ;; If electric-indent-chars is not defined, electric indentation is done
  612. ;; via `lua-mode-map'.
  613. (setq-local electric-indent-chars
  614. (append electric-indent-chars lua--electric-indent-chars)))
  615. ;; setup menu bar entry (XEmacs style)
  616. (if (and (featurep 'menubar)
  617. (boundp 'current-menubar)
  618. (fboundp 'set-buffer-menubar)
  619. (fboundp 'add-menu)
  620. (not (assoc "Lua" current-menubar)))
  621. (progn
  622. (set-buffer-menubar (copy-sequence current-menubar))
  623. (add-menu nil "Lua" lua-emacs-menu)))
  624. ;; Append Lua menu to popup menu for Emacs.
  625. (if (boundp 'mode-popup-menu)
  626. (setq mode-popup-menu
  627. (cons (concat mode-name " Mode Commands") lua-emacs-menu)))
  628. ;; hideshow setup
  629. (unless (assq 'lua-mode hs-special-modes-alist)
  630. (add-to-list 'hs-special-modes-alist
  631. `(lua-mode
  632. ,(regexp-opt (mapcar 'car lua-sexp-alist) 'words) ;start
  633. ,(regexp-opt (mapcar 'cdr lua-sexp-alist) 'words) ;end
  634. nil lua-forward-sexp))))
  635. ;;;###autoload
  636. (add-to-list 'auto-mode-alist '("\\.lua\\'" . lua-mode))
  637. ;;;###autoload
  638. (add-to-list 'interpreter-mode-alist '("lua" . lua-mode))
  639. (defun lua-electric-match (arg)
  640. "Insert character and adjust indentation."
  641. (interactive "P")
  642. (let (blink-paren-function)
  643. (self-insert-command (prefix-numeric-value arg)))
  644. (if lua-electric-flag
  645. (lua-indent-line))
  646. (blink-matching-open))
  647. ;; private functions
  648. (defun lua--fill-paragraph (&optional justify region)
  649. ;; Implementation of forward-paragraph for filling.
  650. ;;
  651. ;; This function works around a corner case in the following situations:
  652. ;;
  653. ;; <>
  654. ;; -- some very long comment ....
  655. ;; some_code_right_after_the_comment
  656. ;;
  657. ;; If point is at the beginning of the comment line, fill paragraph code
  658. ;; would have gone for comment-based filling and done the right thing, but it
  659. ;; does not find a comment at the beginning of the empty line before the
  660. ;; comment and falls back to text-based filling ignoring comment-start and
  661. ;; spilling the comment into the code.
  662. (save-excursion
  663. (while (and (not (eobp))
  664. (progn (move-to-left-margin)
  665. (looking-at paragraph-separate)))
  666. (forward-line 1))
  667. (let ((fill-paragraph-handle-comment t))
  668. (fill-paragraph justify region))))
  669. (defun lua-prefix-key-update-bindings ()
  670. (let (old-cons)
  671. (if (eq lua-prefix-mode-map (keymap-parent lua-mode-map))
  672. ;; if prefix-map is a parent, delete the parent
  673. (set-keymap-parent lua-mode-map nil)
  674. ;; otherwise, look for it among children
  675. (if (setq old-cons (rassoc lua-prefix-mode-map lua-mode-map))
  676. (delq old-cons lua-mode-map)))
  677. (if (null lua-prefix-key)
  678. (set-keymap-parent lua-mode-map lua-prefix-mode-map)
  679. (define-key lua-mode-map (vector lua-prefix-key) lua-prefix-mode-map))))
  680. (defun lua-set-prefix-key (new-key-str)
  681. "Changes `lua-prefix-key' properly and updates keymaps
  682. This function replaces previous prefix-key binding with a new one."
  683. (interactive "sNew prefix key (empty string means no key): ")
  684. (lua--customize-set-prefix-key 'lua-prefix-key new-key-str)
  685. (message "Prefix key set to %S" (single-key-description lua-prefix-key))
  686. (lua-prefix-key-update-bindings))
  687. (defun lua-string-p (&optional pos)
  688. "Returns true if the point is in a string."
  689. (save-excursion (elt (syntax-ppss pos) 3)))
  690. (defun lua-comment-start-pos (parsing-state)
  691. "Return position of comment containing current point.
  692. If point is not inside a comment, return nil."
  693. (and parsing-state (nth 4 parsing-state) (nth 8 parsing-state)))
  694. (defun lua-comment-or-string-p (&optional pos)
  695. "Returns true if the point is in a comment or string."
  696. (save-excursion (let ((parse-result (syntax-ppss pos)))
  697. (or (elt parse-result 3) (elt parse-result 4)))))
  698. (defun lua-comment-or-string-start-pos (&optional pos)
  699. "Returns start position of string or comment which contains point.
  700. If point is not inside string or comment, return nil."
  701. (save-excursion (elt (syntax-ppss pos) 8)))
  702. ;; They're propertized as follows:
  703. ;; 1. generic-comment
  704. ;; 2. generic-string
  705. ;; 3. equals signs
  706. (defconst lua-ml-begin-regexp
  707. "\\(?:\\(?1:-\\)-\\[\\|\\(?2:\\[\\)\\)\\(?3:=*\\)\\[")
  708. (defun lua-try-match-multiline-end (end)
  709. "Try to match close-bracket for multiline literal around point.
  710. Basically, detect form of close bracket from syntactic
  711. information provided at point and re-search-forward to it."
  712. (let ((comment-or-string-start-pos (lua-comment-or-string-start-pos)))
  713. ;; Is there a literal around point?
  714. (and comment-or-string-start-pos
  715. ;; It is, check if the literal is a multiline open-bracket
  716. (save-excursion
  717. (goto-char comment-or-string-start-pos)
  718. (looking-at lua-ml-begin-regexp))
  719. ;; Yes it is, look for it matching close-bracket. Close-bracket's
  720. ;; match group is determined by match-group of open-bracket.
  721. (re-search-forward
  722. (format "]%s\\(?%s:]\\)"
  723. (match-string-no-properties 3)
  724. (if (match-beginning 1) 1 2))
  725. end 'noerror))))
  726. (defun lua-try-match-multiline-begin (limit)
  727. "Try to match multiline open-brackets.
  728. Find next opening long bracket outside of any string/comment.
  729. If none can be found before reaching LIMIT, return nil."
  730. (let (last-search-matched)
  731. (while
  732. ;; This loop will iterate skipping all multiline-begin tokens that are
  733. ;; inside strings or comments ending either at EOL or at valid token.
  734. (and (setq last-search-matched
  735. (re-search-forward lua-ml-begin-regexp limit 'noerror))
  736. ;; Handle triple-hyphen '---[[' situation in which the multiline
  737. ;; opener should be skipped.
  738. ;;
  739. ;; In HYPHEN1-HYPHEN2-BRACKET1-BRACKET2 situation (match-beginning
  740. ;; 0) points to HYPHEN1, but if there's another hyphen before
  741. ;; HYPHEN1, standard syntax table will only detect comment-start
  742. ;; at HYPHEN2.
  743. ;;
  744. ;; We could check for comment-start at HYPHEN2, but then we'd have
  745. ;; to flush syntax-ppss cache to remove the result saying that at
  746. ;; HYPHEN2 there's no comment or string, because under some
  747. ;; circumstances that would hide the fact that we put a
  748. ;; comment-start property at HYPHEN1.
  749. (or (lua-comment-or-string-start-pos (match-beginning 0))
  750. (and (eq ?- (char-after (match-beginning 0)))
  751. (eq ?- (char-before (match-beginning 0)))))))
  752. last-search-matched))
  753. (defun lua-match-multiline-literal-bounds (limit)
  754. ;; First, close any multiline literal spanning from previous block. This will
  755. ;; move the point accordingly so as to avoid double traversal.
  756. (or (lua-try-match-multiline-end limit)
  757. (lua-try-match-multiline-begin limit)))
  758. (defun lua--propertize-multiline-bounds (start end)
  759. "Put text properties on beginnings and ends of multiline literals.
  760. Intended to be used as a `syntax-propertize-function'."
  761. (save-excursion
  762. (goto-char start)
  763. (while (lua-match-multiline-literal-bounds end)
  764. (when (match-beginning 1)
  765. (put-text-property (match-beginning 1) (match-end 1)
  766. 'syntax-table (string-to-syntax "!")))
  767. (when (match-beginning 2)
  768. (put-text-property (match-beginning 2) (match-end 2)
  769. 'syntax-table (string-to-syntax "|"))))))
  770. (defun lua-indent-line ()
  771. "Indent current line for Lua mode.
  772. Return the amount the indentation changed by."
  773. (let (indent
  774. (case-fold-search nil)
  775. ;; save point as a distance to eob - it's invariant w.r.t indentation
  776. (pos (- (point-max) (point))))
  777. (back-to-indentation)
  778. (if (lua-comment-or-string-p)
  779. (setq indent (lua-calculate-string-or-comment-indentation)) ;; just restore point position
  780. (setq indent (max 0 (lua-calculate-indentation))))
  781. (when (not (equal indent (current-column)))
  782. (delete-region (line-beginning-position) (point))
  783. (indent-to indent))
  784. ;; If initial point was within line's indentation,
  785. ;; position after the indentation. Else stay at same point in text.
  786. (if (> (- (point-max) pos) (point))
  787. (goto-char (- (point-max) pos)))
  788. indent))
  789. (defun lua-calculate-string-or-comment-indentation ()
  790. "This function should be run when point at (current-indentation) is inside string"
  791. (if (and (lua-string-p) (not lua-indent-string-contents))
  792. ;; if inside string and strings aren't to be indented, return current indentation
  793. (current-indentation)
  794. ;; At this point, we know that we're inside comment, so make sure
  795. ;; close-bracket is unindented like a block that starts after
  796. ;; left-shifter.
  797. (let ((left-shifter-p (looking-at "\\s *\\(?:--\\)?\\]\\(?1:=*\\)\\]")))
  798. (save-excursion
  799. (goto-char (lua-comment-or-string-start-pos))
  800. (+ (current-indentation)
  801. (if (and left-shifter-p
  802. (looking-at (format "--\\[%s\\["
  803. (match-string-no-properties 1))))
  804. 0
  805. lua-indent-level))))))
  806. (defun lua-find-regexp (direction regexp &optional limit ignore-p)
  807. "Searches for a regular expression in the direction specified.
  808. Direction is one of 'forward and 'backward.
  809. By default, matches in comments and strings are ignored, but what to ignore is
  810. configurable by specifying ignore-p. If the regexp is found, returns point
  811. position, nil otherwise.
  812. ignore-p returns true if the match at the current point position should be
  813. ignored, nil otherwise."
  814. (let ((ignore-func (or ignore-p 'lua-comment-or-string-p))
  815. (search-func (if (eq direction 'forward)
  816. 're-search-forward 're-search-backward))
  817. (case-fold-search nil))
  818. (catch 'found
  819. (while (funcall search-func regexp limit t)
  820. (if (and (not (funcall ignore-func (match-beginning 0)))
  821. (not (funcall ignore-func (match-end 0))))
  822. (throw 'found (point)))))))
  823. (defconst lua-block-regexp
  824. (eval-when-compile
  825. (concat
  826. "\\(\\_<"
  827. (regexp-opt '("do" "function" "repeat" "then"
  828. "else" "elseif" "end" "until") t)
  829. "\\_>\\)\\|"
  830. (regexp-opt '("{" "(" "[" "]" ")" "}") t))))
  831. (defconst lua-block-token-alist
  832. '(("do" "\\_<end\\_>" "\\_<for\\|while\\_>" middle-or-open)
  833. ("function" "\\_<end\\_>" nil open)
  834. ("repeat" "\\_<until\\_>" nil open)
  835. ("then" "\\_<\\(e\\(lse\\(if\\)?\\|nd\\)\\)\\_>" "\\_<\\(else\\)?if\\_>" middle)
  836. ("{" "}" nil open)
  837. ("[" "]" nil open)
  838. ("(" ")" nil open)
  839. ("if" "\\_<then\\_>" nil open)
  840. ("for" "\\_<do\\_>" nil open)
  841. ("while" "\\_<do\\_>" nil open)
  842. ("else" "\\_<end\\_>" "\\_<then\\_>" middle)
  843. ("elseif" "\\_<then\\_>" "\\_<then\\_>" middle)
  844. ("end" nil "\\_<\\(do\\|function\\|then\\|else\\)\\_>" close)
  845. ("until" nil "\\_<repeat\\_>" close)
  846. ("}" nil "{" close)
  847. ("]" nil "\\[" close)
  848. (")" nil "(" close))
  849. "This is a list of block token information blocks.
  850. Each token information entry is of the form:
  851. KEYWORD FORWARD-MATCH-REGEXP BACKWARDS-MATCH-REGEXP TOKEN-TYPE
  852. KEYWORD is the token.
  853. FORWARD-MATCH-REGEXP is a regexp that matches all possible tokens when going forward.
  854. BACKWARDS-MATCH-REGEXP is a regexp that matches all possible tokens when going backwards.
  855. TOKEN-TYPE determines where the token occurs on a statement. open indicates that the token appears at start, close indicates that it appears at end, middle indicates that it is a middle type token, and middle-or-open indicates that it can appear both as a middle or an open type.")
  856. (defconst lua-indentation-modifier-regexp
  857. ;; The absence of else is deliberate, since it does not modify the
  858. ;; indentation level per se. It only may cause the line, in which the
  859. ;; else is, to be shifted to the left.
  860. (concat
  861. "\\(\\_<"
  862. (regexp-opt '("do" "function" "repeat" "then" "if" "else" "elseif" "for" "while") t)
  863. "\\_>\\|"
  864. (regexp-opt '("{" "(" "["))
  865. "\\)\\|\\(\\_<"
  866. (regexp-opt '("end" "until") t)
  867. "\\_>\\|"
  868. (regexp-opt '("]" ")" "}"))
  869. "\\)")
  870. )
  871. (defun lua-get-block-token-info (token)
  872. "Returns the block token info entry for TOKEN from lua-block-token-alist"
  873. (assoc token lua-block-token-alist))
  874. (defun lua-get-token-match-re (token-info direction)
  875. "Returns the relevant match regexp from token info"
  876. (cond
  877. ((eq direction 'forward) (cadr token-info))
  878. ((eq direction 'backward) (nth 2 token-info))
  879. (t nil)))
  880. (defun lua-get-token-type (token-info)
  881. "Returns the relevant match regexp from token info"
  882. (nth 3 token-info))
  883. (defun lua-backwards-to-block-begin-or-end ()
  884. "Move backwards to nearest block begin or end. Returns nil if not successful."
  885. (interactive)
  886. (lua-find-regexp 'backward lua-block-regexp))
  887. (defun lua-find-matching-token-word (token &optional direction)
  888. "Find matching open- or close-token for TOKEN in DIRECTION.
  889. Point has to be exactly at the beginning of TOKEN, e.g. with | being point
  890. {{ }|} -- (lua-find-matching-token-word \"}\" 'backward) will return
  891. -- the first {
  892. {{ |}} -- (lua-find-matching-token-word \"}\" 'backward) will find
  893. -- the second {.
  894. DIRECTION has to be either 'forward or 'backward."
  895. (let* ((token-info (lua-get-block-token-info token))
  896. (match-type (lua-get-token-type token-info))
  897. ;; If we are on a middle token, go backwards. If it is a middle or open,
  898. ;; go forwards
  899. (search-direction (or direction
  900. (if (or (eq match-type 'open)
  901. (eq match-type 'middle-or-open))
  902. 'forward
  903. 'backward)
  904. 'backward))
  905. (match (lua-get-token-match-re token-info search-direction))
  906. maybe-found-pos)
  907. ;; if we are searching forward from the token at the current point
  908. ;; (i.e. for a closing token), need to step one character forward
  909. ;; first, or the regexp will match the opening token.
  910. (if (eq search-direction 'forward) (forward-char 1))
  911. (catch 'found
  912. ;; If we are attempting to find a matching token for a terminating token
  913. ;; (i.e. a token that starts a statement when searching back, or a token
  914. ;; that ends a statement when searching forward), then we don't need to look
  915. ;; any further.
  916. (if (or (and (eq search-direction 'forward)
  917. (eq match-type 'close))
  918. (and (eq search-direction 'backward)
  919. (eq match-type 'open)))
  920. (throw 'found nil))
  921. (while (lua-find-regexp search-direction lua-indentation-modifier-regexp)
  922. ;; have we found a valid matching token?
  923. (let ((found-token (match-string 0))
  924. (found-pos (match-beginning 0)))
  925. (let ((found-type (lua-get-token-type
  926. (lua-get-block-token-info found-token))))
  927. (if (not (and match (string-match match found-token)))
  928. ;; no - then there is a nested block. If we were looking for
  929. ;; a block begin token, found-token must be a block end
  930. ;; token; likewise, if we were looking for a block end token,
  931. ;; found-token must be a block begin token, otherwise there
  932. ;; is a grammatical error in the code.
  933. (if (not (and
  934. (or (eq match-type 'middle)
  935. (eq found-type 'middle)
  936. (eq match-type 'middle-or-open)
  937. (eq found-type 'middle-or-open)
  938. (eq match-type found-type))
  939. (goto-char found-pos)
  940. (lua-find-matching-token-word found-token
  941. search-direction)))
  942. (when maybe-found-pos
  943. (goto-char maybe-found-pos)
  944. (throw 'found maybe-found-pos)))
  945. ;; yes.
  946. ;; if it is a not a middle kind, report the location
  947. (when (not (or (eq found-type 'middle)
  948. (eq found-type 'middle-or-open)))
  949. (throw 'found found-pos))
  950. ;; if it is a middle-or-open type, record location, but keep searching.
  951. ;; If we fail to complete the search, we'll report the location
  952. (when (eq found-type 'middle-or-open)
  953. (setq maybe-found-pos found-pos))
  954. ;; Cannot use tail recursion. too much nesting on long chains of
  955. ;; if/elseif. Will reset variables instead.
  956. (setq token found-token)
  957. (setq token-info (lua-get-block-token-info token))
  958. (setq match (lua-get-token-match-re token-info search-direction))
  959. (setq match-type (lua-get-token-type token-info))))))
  960. maybe-found-pos)))
  961. (defun lua-goto-matching-block-token (&optional parse-start direction)
  962. "Find block begion/end token matching the one at the point.
  963. This function moves the point to the token that matches the one
  964. at the current point. Returns the point position of the first character of
  965. the matching token if successful, nil otherwise.
  966. Optional PARSE-START is a position to which the point should be moved first.
  967. DIRECTION has to be 'forward or 'backward ('forward by default)."
  968. (if parse-start (goto-char parse-start))
  969. (let ((case-fold-search nil))
  970. (if (looking-at lua-indentation-modifier-regexp)
  971. (let ((position (lua-find-matching-token-word (match-string 0)
  972. direction)))
  973. (and position
  974. (goto-char position))))))
  975. (defun lua-goto-matching-block (&optional noreport)
  976. "Go to the keyword balancing the one under the point.
  977. If the point is on a keyword/brace that starts a block, go to the
  978. matching keyword that ends the block, and vice versa.
  979. If optional NOREPORT is non-nil, it won't flag an error if there
  980. is no block open/close open."
  981. (interactive)
  982. ;; search backward to the beginning of the keyword if necessary
  983. (if (eq (char-syntax (following-char)) ?w)
  984. (re-search-backward "\\_<" nil t))
  985. (let ((position (lua-goto-matching-block-token)))
  986. (if (and (not position)
  987. (not noreport))
  988. (error "Not on a block control keyword or brace")
  989. position)))
  990. (defun lua-forward-line-skip-blanks (&optional back)
  991. "Move 1 line forward (back if BACK is non-nil) skipping blank lines.
  992. Moves point 1 line forward (or backward) skipping lines that contain
  993. no Lua code besides comments. The point is put to the beginning of
  994. the line.
  995. Returns final value of point as integer or nil if operation failed."
  996. (catch 'found
  997. (while t
  998. (unless (eql (forward-line (if back -1 1)) 0) ;; 0 means success
  999. (throw 'found nil))
  1000. (unless (or (looking-at "\\s *\\(--.*\\)?$")
  1001. (lua-comment-or-string-p))
  1002. (throw 'found (point))))))
  1003. (eval-when-compile
  1004. (defconst lua-operator-class
  1005. "-+*/^.=<>~:&|"))
  1006. (defconst lua-cont-eol-regexp
  1007. (eval-when-compile
  1008. (concat
  1009. "\\(?:\\(?1:\\_<"
  1010. (regexp-opt '("and" "or" "not" "in" "for" "while"
  1011. "local" "function" "if" "until" "elseif" "return")
  1012. t)
  1013. "\\_>\\)\\|"
  1014. "\\(?:^\\|[^" lua-operator-class "]\\)\\(?2:"
  1015. (regexp-opt '("+" "-" "*" "/" "%" "^" ".." "=="
  1016. "=" "<" ">" "<=" ">=" "~=" "." ":"
  1017. "&" "|" "~" ">>" "<<" "~" ",")
  1018. t)
  1019. "\\)\\)"
  1020. "\\s *\\="))
  1021. "Regexp that matches the ending of a line that needs continuation.
  1022. This regexp starts from eol and looks for a binary operator or an unclosed
  1023. block intro (i.e. 'for' without 'do' or 'if' without 'then') followed by
  1024. an optional whitespace till the end of the line.")
  1025. (defconst lua-cont-bol-regexp
  1026. (eval-when-compile
  1027. (concat
  1028. "\\=\\s *"
  1029. "\\(?:\\(?1:\\_<"
  1030. (regexp-opt '("and" "or" "not") t)
  1031. "\\_>\\)\\|\\(?2:"
  1032. (regexp-opt '("," "+" "-" "*" "/" "%" "^" ".." "=="
  1033. "=" "<" ">" "<=" ">=" "~=" "." ":"
  1034. "&" "|" "~" ">>" "<<" "~")
  1035. t)
  1036. "\\)\\(?:$\\|[^" lua-operator-class "]\\)"
  1037. "\\)"))
  1038. "Regexp that matches a line that continues previous one.
  1039. This regexp means, starting from point there is an optional whitespace followed
  1040. by Lua binary operator. Lua is very liberal when it comes to continuation line,
  1041. so we're safe to assume that every line that starts with a binop continues
  1042. previous one even though it looked like an end-of-statement.")
  1043. (defun lua-last-token-continues-p ()
  1044. "Return non-nil if the last token on this line is a continuation token."
  1045. (let ((line-begin (line-beginning-position))
  1046. (line-end (line-end-position))
  1047. return-value)
  1048. (save-excursion
  1049. (end-of-line)
  1050. ;; we need to check whether the line ends in a comment and
  1051. ;; skip that one.
  1052. (while (lua-find-regexp 'backward "-" line-begin 'lua-string-p)
  1053. (if (looking-at "--")
  1054. (setq line-end (point))))
  1055. (goto-char line-end)
  1056. (setq return-value (and (re-search-backward lua-cont-eol-regexp line-begin t)
  1057. (or (match-beginning 1)
  1058. (match-beginning 2))))
  1059. (if (and return-value
  1060. (string-equal (match-string-no-properties 0) "return"))
  1061. ;; "return" keyword is ambiguous and depends on next token
  1062. (unless (save-excursion
  1063. (goto-char (match-end 0))
  1064. (forward-comment (point-max))
  1065. (and
  1066. ;; Not continuing: at end of file
  1067. (not (eobp))
  1068. (or
  1069. ;; "function" keyword: it is a continuation, e.g.
  1070. ;;
  1071. ;; return
  1072. ;; function() return 123 end
  1073. ;;
  1074. (looking-at (lua-rx (symbol "function")))
  1075. ;; Looking at semicolon or any other keyword: not continuation
  1076. (not (looking-at (lua-rx (or ";" lua-keyword)))))))
  1077. (setq return-value nil)))
  1078. return-value)))
  1079. (defun lua-first-token-continues-p ()
  1080. "Return non-nil if the first token on this line is a continuation token."
  1081. (let ((line-end (line-end-position)))
  1082. (save-excursion
  1083. (beginning-of-line)
  1084. ;; if first character of the line is inside string, it's a continuation
  1085. ;; if strings aren't supposed to be indented, `lua-calculate-indentation' won't even let
  1086. ;; the control inside this function
  1087. (and
  1088. (re-search-forward lua-cont-bol-regexp line-end t)
  1089. (or (match-beginning 1)
  1090. (match-beginning 2))))))
  1091. (defun lua--backward-up-list-noerror ()
  1092. "Safe version of lua-backward-up-list that does not signal an error."
  1093. (condition-case nil
  1094. (lua-backward-up-list)
  1095. (scan-error nil)))
  1096. (defun lua-backward-up-list ()
  1097. "Goto starter/opener of the block that contains point."
  1098. (interactive)
  1099. (let ((start-pos (point))
  1100. end-pos)
  1101. (or
  1102. ;; Return parent block opener token if it exists.
  1103. (cl-loop
  1104. ;; Search indentation modifier backward, return nil on failure.
  1105. always (lua-find-regexp 'backward lua-indentation-modifier-regexp)
  1106. ;; Fetch info about the found token
  1107. for token = (match-string-no-properties 0)
  1108. for token-info = (lua-get-block-token-info token)
  1109. for token-type = (lua-get-token-type token-info)
  1110. ;; If the token is a close token, continue to skip its opener. If not
  1111. ;; close, stop and return found token.
  1112. while (eq token-type 'close)
  1113. ;; Find matching opener to skip it and continue from beginning.
  1114. ;;
  1115. ;; Return nil on failure.
  1116. always (let ((position (lua-find-matching-token-word token 'backward)))
  1117. (and position (goto-char position)))
  1118. finally return token-info)
  1119. (progn
  1120. (setq end-pos (point))
  1121. (goto-char start-pos)
  1122. (signal 'scan-error
  1123. (list "Block open token not found"
  1124. ;; If start-pos == end-pos, the "obstacle" is current
  1125. (if (eql start-pos end-pos) start-pos (match-beginning 0))
  1126. (if (eql start-pos end-pos) start-pos (match-end 0))))))))
  1127. (defun lua-is-continuing-statement-p-1 ()
  1128. "Return non-nil if current lined continues a statement.
  1129. More specifically, return the point in the line that is continued.
  1130. The criteria for a continuing statement are:
  1131. * the last token of the previous line is a continuing op,
  1132. OR the first token of the current line is a continuing op
  1133. * the expression is not enclosed by a parentheses/braces/brackets"
  1134. (let (prev-line continuation-pos parent-block-opener)
  1135. (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back)))
  1136. (and prev-line
  1137. (or
  1138. ;; Binary operator or keyword that implies continuation.
  1139. (save-excursion
  1140. (and (setq continuation-pos
  1141. (or (lua-first-token-continues-p)
  1142. (save-excursion (and (goto-char prev-line)
  1143. ;; check last token of previous nonblank line
  1144. (lua-last-token-continues-p)))))
  1145. (not
  1146. ;; Operators/keywords does not create continuation inside some blocks:
  1147. (and
  1148. (setq parent-block-opener (car-safe (lua--backward-up-list-noerror)))
  1149. (or
  1150. ;; - inside parens/brackets
  1151. (member parent-block-opener '("(" "["))
  1152. ;; - inside braces if it is a comma
  1153. (and (eq (char-after continuation-pos) ?,)
  1154. (equal parent-block-opener "{")))))
  1155. continuation-pos))
  1156. ;; "for" expressions (until the next do) imply continuation.
  1157. (when (string-equal (car-safe (lua--backward-up-list-noerror)) "for")
  1158. (point))))))
  1159. (defun lua-is-continuing-statement-p (&optional parse-start)
  1160. "Returns non-nil if the line at PARSE-START should be indented as continuation line.
  1161. This true is when the line :
  1162. * is continuing a statement itself
  1163. * starts with a 1+ block-closer tokens, an top-most block opener is on a continuation line
  1164. "
  1165. (save-excursion
  1166. (if parse-start (goto-char parse-start))
  1167. ;; If line starts with a series of closer tokens, whether or not the line
  1168. ;; is a continuation line is decided by the opener line, e.g.
  1169. ;;
  1170. ;; x = foo +
  1171. ;; long_function_name(
  1172. ;; long_parameter_1,
  1173. ;; long_parameter_2,
  1174. ;; long_parameter_3,
  1175. ;; ) + long_function_name2({
  1176. ;; long_parameter_1,
  1177. ;; long_parameter_2,
  1178. ;; long_parameter_3,
  1179. ;; })
  1180. ;;
  1181. ;; Final line, "})" is a continuation line, but it is decided by the
  1182. ;; opener line, ") + long_function_name2({", which in its turn is decided
  1183. ;; by the "long_function_name(" line, which is a continuation line
  1184. ;; because the line before it ends with a binary operator.
  1185. (while (and (lua--goto-line-beginning-rightmost-closer)
  1186. (lua--backward-up-list-noerror)
  1187. (lua-is-continuing-statement-p-1)))
  1188. (lua-is-continuing-statement-p-1)))
  1189. (defun lua-make-indentation-info-pair (found-token found-pos)
  1190. "Create a pair from FOUND-TOKEN and FOUND-POS for indentation calculation.
  1191. This is a helper function to lua-calculate-indentation-info.
  1192. Don't use standalone."
  1193. (cond
  1194. ;; function is a bit tricky to indent right. They can appear in a lot ot
  1195. ;; different contexts. Until I find a shortcut, I'll leave it with a simple
  1196. ;; relative indentation.
  1197. ;; The special cases are for indenting according to the location of the
  1198. ;; function. i.e.:
  1199. ;; (cons 'absolute (+ (current-column) lua-indent-level))
  1200. ;; TODO: Fix this. It causes really ugly indentations for in-line functions.
  1201. ((string-equal found-token "function")
  1202. (cons 'relative lua-indent-level))
  1203. ;; block openers
  1204. ((and lua-indent-nested-block-content-align
  1205. (member found-token (list "{" "(" "[")))
  1206. (save-excursion
  1207. (let ((found-bol (line-beginning-position)))
  1208. (forward-comment (point-max))
  1209. ;; If the next token is on this line and it's not a block opener,
  1210. ;; the next line should align to that token.
  1211. (if (and (zerop (count-lines found-bol (line-beginning-position)))
  1212. (not (looking-at lua-indentation-modifier-regexp)))
  1213. (cons 'absolute (current-column))
  1214. (cons 'relative lua-indent-level)))))
  1215. ;; These are not really block starters. They should not add to indentation.
  1216. ;; The corresponding "then" and "do" handle the indentation.
  1217. ((member found-token (list "if" "for" "while"))
  1218. (cons 'relative 0))
  1219. ;; closing tokens follow: These are usually taken care of by
  1220. ;; lua-calculate-indentation-override.
  1221. ;; elseif is a bit of a hack. It is not handled separately, but it needs to
  1222. ;; nullify a previous then if on the same line.
  1223. ((member found-token (list "until" "elseif"))
  1224. (save-excursion
  1225. (let* ((line-beginning (line-beginning-position))
  1226. (same-line (and (lua-goto-matching-block-token found-pos 'backward)
  1227. (<= line-beginning (point)))))
  1228. (if same-line
  1229. (cons 'remove-matching 0)
  1230. (cons 'relative 0)))))
  1231. ;; else is a special case; if its matching block token is on the same line,
  1232. ;; instead of removing the matching token, it has to replace it, so that
  1233. ;; either the next line will be indented correctly, or the end on the same
  1234. ;; line will remove the effect of the else.
  1235. ((string-equal found-token "else")
  1236. (save-excursion
  1237. (let* ((line-beginning (line-beginning-position))
  1238. (same-line (and (lua-goto-matching-block-token found-pos 'backward)
  1239. (<= line-beginning (point)))))
  1240. (if same-line
  1241. (cons 'replace-matching (cons 'relative lua-indent-level))
  1242. (cons 'relative lua-indent-level)))))
  1243. ;; Block closers. If they are on the same line as their openers, they simply
  1244. ;; eat up the matching indentation modifier. Otherwise, they pull
  1245. ;; indentation back to the matching block opener.
  1246. ((member found-token (list ")" "}" "]" "end"))
  1247. (save-excursion
  1248. (let* ((line-beginning (line-beginning-position))
  1249. (same-line (and (lua-goto-matching-block-token found-pos 'backward)
  1250. (<= line-beginning (point)))))
  1251. (if (not same-line)
  1252. (lua-calculate-indentation-info (point))
  1253. (cons 'remove-matching 0)))))
  1254. ((member found-token '("do" "then"))
  1255. `(multiple . ((cancel-continued-line . nil) (relative . ,lua-indent-level))))
  1256. ;; Everything else. This is from the original code: If opening a block
  1257. ;; (match-data 1 exists), then push indentation one level up, if it is
  1258. ;; closing a block, pull it one level down.
  1259. ('other-indentation-modifier
  1260. (cons 'relative (if (nth 2 (match-data))
  1261. ;; beginning of a block matched
  1262. lua-indent-level
  1263. ;; end of a block matched
  1264. (- lua-indent-level))))))
  1265. (defun lua-add-indentation-info-pair (pair info-list)
  1266. "Add the given indentation info PAIR to the list of indentation INFO-LIST.
  1267. This function has special case handling for two tokens: remove-matching,
  1268. and replace-matching. These two tokens are cleanup tokens that remove or
  1269. alter the effect of a previously recorded indentation info.
  1270. When a remove-matching token is encountered, the last recorded info, i.e.
  1271. the car of the list is removed. This is used to roll-back an indentation of a
  1272. block opening statement when it is closed.
  1273. When a replace-matching token is seen, the last recorded info is removed,
  1274. and the cdr of the replace-matching info is added in its place. This is used
  1275. when a middle-of the block (the only case is 'else') is seen on the same line
  1276. the block is opened."
  1277. (cond
  1278. ( (eq 'multiple (car pair))
  1279. (let ((info-pair-elts (cdr pair)))
  1280. (while info-pair-elts
  1281. (setq info-list (lua-add-indentation-info-pair (car info-pair-elts) info-list)
  1282. info-pair-elts (cdr info-pair-elts)))
  1283. info-list))
  1284. ( (eq 'cancel-continued-line (car pair))
  1285. (if (eq (caar info-list) 'continued-line)
  1286. (cdr info-list)
  1287. info-list))
  1288. ( (eq 'remove-matching (car pair))
  1289. ;; Remove head of list
  1290. (cdr info-list))
  1291. ( (eq 'replace-matching (car pair))
  1292. ;; remove head of list, and add the cdr of pair instead
  1293. (cons (cdr pair) (cdr info-list)))
  1294. ( (listp (cdr-safe pair))
  1295. (nconc pair info-list))
  1296. ( t
  1297. ;; Just add the pair
  1298. (cons pair info-list))))
  1299. (defun lua-calculate-indentation-info-1 (indentation-info bound)
  1300. "Helper function for `lua-calculate-indentation-info'.
  1301. Return list of indentation modifiers from point to BOUND."
  1302. (while (lua-find-regexp 'forward lua-indentation-modifier-regexp
  1303. bound)
  1304. (let ((found-token (match-string 0))
  1305. (found-pos (match-beginning 0)))
  1306. (setq indentation-info
  1307. (lua-add-indentation-info-pair
  1308. (lua-make-indentation-info-pair found-token found-pos)
  1309. indentation-info))))
  1310. indentation-info)
  1311. (defun lua-calculate-indentation-info (&optional parse-end)
  1312. "For each block token on the line, computes how it affects the indentation.
  1313. The effect of each token can be either a shift relative to the current
  1314. indentation level, or indentation to some absolute column. This information
  1315. is collected in a list of indentation info pairs, which denote absolute
  1316. and relative each, and the shift/column to indent to."
  1317. (let (indentation-info cont-stmt-pos)
  1318. (while (setq cont-stmt-pos (lua-is-continuing-statement-p))
  1319. (lua-forward-line-skip-blanks 'back)
  1320. (when (< cont-stmt-pos (point))
  1321. (goto-char cont-stmt-pos)))
  1322. ;; calculate indentation modifiers for the line itself
  1323. (setq indentation-info (list (cons 'absolute (current-indentation))))
  1324. (back-to-indentation)
  1325. (setq indentation-info
  1326. (lua-calculate-indentation-info-1
  1327. indentation-info (min parse-end (line-end-position))))
  1328. ;; and do the following for each continuation line before PARSE-END
  1329. (while (and (eql (forward-line 1) 0)
  1330. (<= (point) parse-end))
  1331. ;; handle continuation lines:
  1332. (if (lua-is-continuing-statement-p)
  1333. ;; if it's the first continuation line, add one level
  1334. (unless (eq (car (car indentation-info)) 'continued-line)
  1335. (push (cons 'continued-line lua-indent-level) indentation-info))
  1336. ;; if it's the first non-continued line, subtract one level
  1337. (when (eq (car (car indentation-info)) 'continued-line)
  1338. (push (cons 'stop-continued-line (- lua-indent-level)) indentation-info)))
  1339. ;; add modifiers found in this continuation line
  1340. (setq indentation-info
  1341. (lua-calculate-indentation-info-1
  1342. indentation-info (min parse-end (line-end-position)))))
  1343. indentation-info))
  1344. (defun lua-accumulate-indentation-info (reversed-indentation-info)
  1345. "Accumulates the indentation information previously calculated by
  1346. lua-calculate-indentation-info. Returns either the relative indentation
  1347. shift, or the absolute column to indent to."
  1348. (let (indentation-info
  1349. (type 'relative)
  1350. (accu 0))
  1351. ;; Aggregate all neighbouring relative offsets, reversing the INFO list.
  1352. (cl-dolist (elt reversed-indentation-info)
  1353. (if (and (eq (car elt) 'relative)
  1354. (eq (caar indentation-info) 'relative))
  1355. (setcdr (car indentation-info) (+ (cdar indentation-info) (cdr elt)))
  1356. (push elt indentation-info)))
  1357. ;; Aggregate indentation info, taking 'absolute modifiers into account.
  1358. (mapc (lambda (x)
  1359. (let ((new-val (cdr x)))
  1360. (if (eq 'absolute (car x))
  1361. (progn (setq type 'absolute
  1362. accu new-val))
  1363. (setq accu (+ accu new-val)))))
  1364. indentation-info)
  1365. (cons type accu)))
  1366. (defun lua-calculate-indentation-block-modifier (&optional parse-end)
  1367. "Return amount by which this line modifies the indentation.
  1368. Beginnings of blocks add lua-indent-level once each, and endings
  1369. of blocks subtract lua-indent-level once each. This function is used
  1370. to determine how the indentation of the following line relates to this
  1371. one."
  1372. (let (indentation-info)
  1373. (save-excursion
  1374. ;; First go back to the line that starts it all
  1375. ;; lua-calculate-indentation-info will scan through the whole thing
  1376. (let ((case-fold-search nil))
  1377. (setq indentation-info
  1378. (lua-accumulate-indentation-info
  1379. (lua-calculate-indentation-info parse-end)))))
  1380. (if (eq (car indentation-info) 'absolute)
  1381. (- (cdr indentation-info) (current-indentation))
  1382. (cdr indentation-info))))
  1383. (eval-when-compile
  1384. (defconst lua--function-name-rx
  1385. '(seq symbol-start
  1386. (+ (any alnum "_"))
  1387. (* "." (+ (any alnum "_")))
  1388. (? ":" (+ (any alnum "_")))
  1389. symbol-end)
  1390. "Lua function name regexp in `rx'-SEXP format."))
  1391. (defconst lua--left-shifter-regexp
  1392. (eval-when-compile
  1393. (rx
  1394. ;; This regexp should answer the following questions:
  1395. ;; 1. is there a left shifter regexp on that line?
  1396. ;; 2. where does block-open token of that left shifter reside?
  1397. (or (seq (group-n 1 symbol-start "local" (+ blank)) "function" symbol-end)
  1398. (seq (group-n 1 (eval lua--function-name-rx) (* blank)) (any "{("))
  1399. (seq (group-n 1 (or
  1400. ;; assignment statement prefix
  1401. (seq (* nonl) (not (any "<=>~")) "=" (* blank))
  1402. ;; return statement prefix
  1403. (seq word-start "return" word-end (* blank))))
  1404. ;; right hand side
  1405. (or "{"
  1406. "function"
  1407. "("
  1408. (seq (group-n 1 (eval lua--function-name-rx) (* blank))
  1409. (any "({")))))))
  1410. "Regular expression that matches left-shifter expression.
  1411. Left-shifter expression is defined as follows. If a block
  1412. follows a left-shifter expression, its contents & block-close
  1413. token should be indented relative to left-shifter expression
  1414. indentation rather then to block-open token.
  1415. For example:
  1416. -- 'local a = ' is a left-shifter expression
  1417. -- 'function' is a block-open token
  1418. local a = function()
  1419. -- block contents is indented relative to left-shifter
  1420. foobarbaz()
  1421. -- block-end token is unindented to left-shifter indentation
  1422. end
  1423. The following left-shifter expressions are currently handled:
  1424. 1. local function definition with function block, begin-end
  1425. 2. function call with arguments block, () or {}
  1426. 3. assignment/return statement with
  1427. - table constructor block, {}
  1428. - function call arguments block, () or {} block
  1429. - function expression a.k.a. lambda, begin-end block.")
  1430. (defun lua-point-is-after-left-shifter-p ()
  1431. "Check if point is right after a left-shifter expression.
  1432. See `lua--left-shifter-regexp' for description & example of
  1433. left-shifter expression. "
  1434. (save-excursion
  1435. (let ((old-point (point)))
  1436. (back-to-indentation)
  1437. (and
  1438. (/= (point) old-point)
  1439. (looking-at lua--left-shifter-regexp)
  1440. (= old-point (match-end 1))))))
  1441. (defun lua--goto-line-beginning-rightmost-closer (&optional parse-start)
  1442. (let (case-fold-search pos line-end-pos return-val)
  1443. (save-excursion
  1444. (if parse-start (goto-char parse-start))
  1445. (setq line-end-pos (line-end-position))
  1446. (back-to-indentation)
  1447. (unless (lua-comment-or-string-p)
  1448. (cl-loop while (and (<= (point) line-end-pos)
  1449. (looking-at lua-indentation-modifier-regexp))
  1450. for token-info = (lua-get-block-token-info (match-string 0))
  1451. for token-type = (lua-get-token-type token-info)
  1452. while (not (eq token-type 'open))
  1453. do (progn
  1454. (setq pos (match-beginning 0)
  1455. return-val token-info)
  1456. (goto-char (match-end 0))
  1457. (forward-comment (line-end-position))))))
  1458. (when pos
  1459. (progn
  1460. (goto-char pos)
  1461. return-val))))
  1462. (defun lua-calculate-indentation-override (&optional parse-start)
  1463. "Return overriding indentation amount for special cases.
  1464. If there's a sequence of block-close tokens starting at the
  1465. beginning of the line, calculate indentation according to the
  1466. line containing block-open token for the last block-close token
  1467. in the sequence.
  1468. If not, return nil."
  1469. (let (case-fold-search rightmost-closer-info opener-info opener-pos)
  1470. (save-excursion
  1471. (when (and (setq rightmost-closer-info (lua--goto-line-beginning-rightmost-closer parse-start))
  1472. (setq opener-info (lua--backward-up-list-noerror))
  1473. ;; Ensure opener matches closer.
  1474. (string-match (lua-get-token-match-re rightmost-closer-info 'backward)
  1475. (car opener-info)))
  1476. ;; Special case: "middle" tokens like for/do, while/do, if/then,
  1477. ;; elseif/then: corresponding "end" or corresponding "else" must be
  1478. ;; unindented to the beginning of the statement, which is not
  1479. ;; necessarily the same as beginning of string that contains "do", e.g.
  1480. ;;
  1481. ;; while (
  1482. ;; foo and
  1483. ;; bar) do
  1484. ;; hello_world()
  1485. ;; end
  1486. (setq opener-pos (point))
  1487. (unless (or
  1488. (and (string-equal (car opener-info) "do")
  1489. (member (car (lua--backward-up-list-noerror)) '("while" "for")))
  1490. (and (string-equal (car opener-info) "then")
  1491. (member (car (lua--backward-up-list-noerror)) '("if" "elseif"))))
  1492. (goto-char opener-pos))
  1493. ;; (let (cont-stmt-pos)
  1494. ;; (while (setq cont-stmt-pos (lua-is-continuing-statement-p))
  1495. ;; (goto-char cont-stmt-pos)))
  1496. ;; Exception cases: when the start of the line is an assignment,
  1497. ;; go to the start of the assignment instead of the matching item
  1498. (if (and lua-indent-close-paren-align
  1499. (member (car opener-info) '("{" "(" "["))
  1500. (not (lua-point-is-after-left-shifter-p)))
  1501. (current-column)
  1502. (current-indentation))))))
  1503. (defun lua-calculate-indentation ()
  1504. "Return appropriate indentation for current line as Lua code."
  1505. (save-excursion
  1506. (let ((cur-line-begin-pos (line-beginning-position)))
  1507. (or
  1508. ;; when calculating indentation, do the following:
  1509. ;; 1. check, if the line starts with indentation-modifier (open/close brace)
  1510. ;; and if it should be indented/unindented in special way
  1511. (lua-calculate-indentation-override)
  1512. (when (lua-forward-line-skip-blanks 'back)
  1513. ;; the order of function calls here is important. block modifier
  1514. ;; call may change the point to another line
  1515. (let* ((modifier
  1516. (lua-calculate-indentation-block-modifier cur-line-begin-pos)))
  1517. (+ (current-indentation) modifier)))
  1518. ;; 4. if there's no previous line, indentation is 0
  1519. 0))))
  1520. (defvar lua--beginning-of-defun-re
  1521. (lua-rx-to-string '(: bol (? (symbol "local") ws+) lua-funcheader))
  1522. "Lua top level (matches only at the beginning of line) function header regex.")
  1523. (defun lua-beginning-of-proc (&optional arg)
  1524. "Move backward to the beginning of a Lua proc (or similar).
  1525. With argument, do it that many times. Negative arg -N
  1526. means move forward to Nth following beginning of proc.
  1527. Returns t unless search stops due to beginning or end of buffer."
  1528. (interactive "P")
  1529. (or arg (setq arg 1))
  1530. (while (and (> arg 0)
  1531. (re-search-backward lua--beginning-of-defun-re nil t))
  1532. (setq arg (1- arg)))
  1533. (while (and (< arg 0)
  1534. (re-search-forward lua--beginning-of-defun-re nil t))
  1535. (beginning-of-line)
  1536. (setq arg (1+ arg)))
  1537. (zerop arg))
  1538. (defun lua-end-of-proc (&optional arg)
  1539. "Move forward to next end of Lua proc (or similar).
  1540. With argument, do it that many times. Negative argument -N means move
  1541. back to Nth preceding end of proc.
  1542. This function just searches for a `end' at the beginning of a line."
  1543. (interactive "P")
  1544. (or arg
  1545. (setq arg 1))
  1546. (let ((found nil)
  1547. (ret t))
  1548. (if (and (< arg 0)
  1549. (not (bolp))
  1550. (save-excursion
  1551. (beginning-of-line)
  1552. (eq (following-char) ?})))
  1553. (forward-char -1))
  1554. (while (> arg 0)
  1555. (if (re-search-forward "^end" nil t)
  1556. (setq arg (1- arg)
  1557. found t)
  1558. (setq ret nil
  1559. arg 0)))
  1560. (while (< arg 0)
  1561. (if (re-search-backward "^end" nil t)
  1562. (setq arg (1+ arg)
  1563. found t)
  1564. (setq ret nil
  1565. arg 0)))
  1566. (if found
  1567. (progn
  1568. (beginning-of-line)
  1569. (forward-line)))
  1570. ret))
  1571. (defvar lua-process-init-code
  1572. (mapconcat
  1573. 'identity
  1574. '("local loadstring = loadstring or load"
  1575. "function luamode_loadstring(str, displayname, lineoffset)"
  1576. " if lineoffset > 1 then"
  1577. " str = string.rep('\\n', lineoffset - 1) .. str"
  1578. " end"
  1579. ""
  1580. " local x, e = loadstring(str, '@'..displayname)"
  1581. " if e then"
  1582. " error(e)"
  1583. " end"
  1584. " return x()"
  1585. "end")
  1586. " "))
  1587. (defun lua-make-lua-string (str)
  1588. "Convert string to Lua literal."
  1589. (save-match-data
  1590. (with-temp-buffer
  1591. (insert str)
  1592. (goto-char (point-min))
  1593. (while (re-search-forward "[\"'\\\t\\\n]" nil t)
  1594. (cond
  1595. ((string= (match-string 0) "\n")
  1596. (replace-match "\\\\n"))
  1597. ((string= (match-string 0) "\t")
  1598. (replace-match "\\\\t"))
  1599. (t
  1600. (replace-match "\\\\\\&" t))))
  1601. (concat "'" (buffer-string) "'"))))
  1602. ;;;###autoload
  1603. (defalias 'run-lua #'lua-start-process)
  1604. ;;;###autoload
  1605. (defun lua-start-process (&optional name program startfile &rest switches)
  1606. "Start a Lua process named NAME, running PROGRAM.
  1607. PROGRAM defaults to NAME, which defaults to `lua-default-application'.
  1608. When called interactively, switch to the process buffer."
  1609. (interactive)
  1610. (or switches
  1611. (setq switches lua-default-command-switches))
  1612. (setq name (or name (if (consp lua-default-application)
  1613. (car lua-default-application)
  1614. lua-default-application)))
  1615. (setq program (or program lua-default-application))
  1616. (setq lua-process-buffer (apply 'make-comint name program startfile switches))
  1617. (setq lua-process (get-buffer-process lua-process-buffer))
  1618. (set-process-query-on-exit-flag lua-process nil)
  1619. (with-current-buffer lua-process-buffer
  1620. ;; enable error highlighting in stack traces
  1621. (require 'compile)
  1622. (setq lua--repl-buffer-p t)
  1623. (make-local-variable 'compilation-error-regexp-alist)
  1624. (setq compilation-error-regexp-alist
  1625. (cons (list lua-traceback-line-re 1 2)
  1626. compilation-error-regexp-alist))
  1627. (compilation-shell-minor-mode 1)
  1628. (setq-local comint-prompt-regexp lua-prompt-regexp)
  1629. ;; Don't send initialization code until seeing the prompt to ensure that
  1630. ;; the interpreter is ready.
  1631. (while (not (lua-prompt-line))
  1632. (accept-process-output (get-buffer-process (current-buffer)))
  1633. (goto-char (point-max)))
  1634. (lua-send-string lua-process-init-code))
  1635. ;; when called interactively, switch to process buffer
  1636. (if (called-interactively-p 'any)
  1637. (switch-to-buffer lua-process-buffer)))
  1638. (defun lua-get-create-process ()
  1639. "Return active Lua process creating one if necessary."
  1640. (unless (comint-check-proc lua-process-buffer)
  1641. (lua-start-process))
  1642. lua-process)
  1643. (defun lua-kill-process ()
  1644. "Kill Lua process and its buffer."
  1645. (interactive)
  1646. (when (buffer-live-p lua-process-buffer)
  1647. (kill-buffer lua-process-buffer)
  1648. (setq lua-process-buffer nil)))
  1649. (defun lua-set-lua-region-start (&optional arg)
  1650. "Set start of region for use with `lua-send-lua-region'."
  1651. (interactive)
  1652. (set-marker lua-region-start (or arg (point))))
  1653. (defun lua-set-lua-region-end (&optional arg)
  1654. "Set end of region for use with `lua-send-lua-region'."
  1655. (interactive)
  1656. (set-marker lua-region-end (or arg (point))))
  1657. (defun lua-send-string (str)
  1658. "Send STR plus a newline to the Lua process.
  1659. If `lua-process' is nil or dead, start a new process first."
  1660. (unless (string-equal (substring str -1) "\n")
  1661. (setq str (concat str "\n")))
  1662. (process-send-string (lua-get-create-process) str))
  1663. (defun lua-send-current-line ()
  1664. "Send current line to the Lua process, found in `lua-process'.
  1665. If `lua-process' is nil or dead, start a new process first."
  1666. (interactive)
  1667. (lua-send-region (line-beginning-position) (line-end-position)))
  1668. (defun lua-send-defun (pos)
  1669. "Send the function definition around point to the Lua process."
  1670. (interactive "d")
  1671. (save-excursion
  1672. (let ((start (if (save-match-data (looking-at "^function[ \t]"))
  1673. ;; point already at the start of "function".
  1674. ;; We need to handle this case explicitly since
  1675. ;; lua-beginning-of-proc will move to the
  1676. ;; beginning of the _previous_ function.
  1677. (point)
  1678. ;; point is not at the beginning of function, move
  1679. ;; there and bind start to that position
  1680. (lua-beginning-of-proc)
  1681. (point)))
  1682. (end (progn (lua-end-of-proc) (point))))
  1683. ;; make sure point is in a function definition before sending to
  1684. ;; the process
  1685. (if (and (>= pos start) (< pos end))
  1686. (lua-send-region start end)
  1687. (error "Not on a function definition")))))
  1688. (defun lua-maybe-skip-shebang-line (start)
  1689. "Skip shebang (#!/path/to/interpreter/) line at beginning of buffer.
  1690. Return a position that is after Lua-recognized shebang line (1st
  1691. character in file must be ?#) if START is at its beginning.
  1692. Otherwise, return START."
  1693. (save-restriction
  1694. (widen)
  1695. (if (and (eq start (point-min))
  1696. (eq (char-after start) ?#))
  1697. (save-excursion
  1698. (goto-char start)
  1699. (forward-line)
  1700. (point))
  1701. start)))
  1702. (defun lua-send-region (start end)
  1703. (interactive "r")
  1704. (setq start (lua-maybe-skip-shebang-line start))
  1705. (let* ((lineno (line-number-at-pos start))
  1706. (lua-file (or (buffer-file-name) (buffer-name)))
  1707. (region-str (buffer-substring-no-properties start end))
  1708. (command
  1709. ;; Print empty line before executing the code so that the first line
  1710. ;; of output doesn't end up on the same line as current prompt.
  1711. (format "print(''); luamode_loadstring(%s, %s, %s);\n"
  1712. (lua-make-lua-string region-str)
  1713. (lua-make-lua-string lua-file)
  1714. lineno)))
  1715. (lua-send-string command)
  1716. (when lua-always-show (lua-show-process-buffer))))
  1717. (defun lua-prompt-line ()
  1718. (save-excursion
  1719. (save-match-data
  1720. (forward-line 0)
  1721. (if (looking-at comint-prompt-regexp)
  1722. (match-end 0)))))
  1723. (defun lua-send-lua-region ()
  1724. "Send preset Lua region to Lua process."
  1725. (interactive)
  1726. (unless (and lua-region-start lua-region-end)
  1727. (error "lua-region not set"))
  1728. (lua-send-region lua-region-start lua-region-end))
  1729. (defalias 'lua-send-proc 'lua-send-defun)
  1730. (defun lua-send-buffer ()
  1731. "Send whole buffer to Lua process."
  1732. (interactive)
  1733. (lua-send-region (point-min) (point-max)))
  1734. (defun lua-restart-with-whole-file ()
  1735. "Restart Lua process and send whole file as input."
  1736. (interactive)
  1737. (lua-kill-process)
  1738. (lua-send-buffer))
  1739. (defun lua-show-process-buffer ()
  1740. "Make sure `lua-process-buffer' is being displayed.
  1741. Create a Lua process if one doesn't already exist."
  1742. (interactive)
  1743. (display-buffer (process-buffer (lua-get-create-process))))
  1744. (defun lua-hide-process-buffer ()
  1745. "Delete all windows that display `lua-process-buffer'."
  1746. (interactive)
  1747. (when (buffer-live-p lua-process-buffer)
  1748. (delete-windows-on lua-process-buffer)))
  1749. (defun lua-funcname-at-point ()
  1750. "Get current Name { '.' Name } sequence."
  1751. ;; FIXME: copying/modifying syntax table for each call may incur a penalty
  1752. (with-syntax-table (copy-syntax-table)
  1753. (modify-syntax-entry ?. "_")
  1754. (current-word t)))
  1755. (defun lua-search-documentation ()
  1756. "Search Lua documentation for the word at the point."
  1757. (interactive)
  1758. (let ((url (concat lua-documentation-url "#pdf-" (lua-funcname-at-point))))
  1759. (funcall lua-documentation-function url)))
  1760. (defun lua-toggle-electric-state (&optional arg)
  1761. "Toggle the electric indentation feature.
  1762. Optional numeric ARG, if supplied, turns on electric indentation when
  1763. positive, turns it off when negative, and just toggles it when zero or
  1764. left out."
  1765. (interactive "P")
  1766. (let ((num_arg (prefix-numeric-value arg)))
  1767. (setq lua-electric-flag (cond ((or (null arg)
  1768. (zerop num_arg)) (not lua-electric-flag))
  1769. ((< num_arg 0) nil)
  1770. ((> num_arg 0) t))))
  1771. (message "%S" lua-electric-flag))
  1772. (defun lua-forward-sexp (&optional count)
  1773. "Forward to block end"
  1774. (interactive "p")
  1775. ;; negative offsets not supported
  1776. (cl-assert (or (not count) (>= count 0)))
  1777. (save-match-data
  1778. (let ((count (or count 1))
  1779. (block-start (mapcar 'car lua-sexp-alist)))
  1780. (while (> count 0)
  1781. ;; skip whitespace
  1782. (skip-chars-forward " \t\n")
  1783. (if (looking-at (regexp-opt block-start 'words))
  1784. (let ((keyword (match-string 1)))
  1785. (lua-find-matching-token-word keyword 'forward))
  1786. ;; If the current keyword is not a "begin" keyword, then just
  1787. ;; perform the normal forward-sexp.
  1788. (forward-sexp 1))
  1789. (setq count (1- count))))))
  1790. ;; menu bar
  1791. (define-key lua-mode-menu [restart-with-whole-file]
  1792. '("Restart With Whole File" . lua-restart-with-whole-file))
  1793. (define-key lua-mode-menu [kill-process]
  1794. '("Kill Process" . lua-kill-process))
  1795. (define-key lua-mode-menu [hide-process-buffer]
  1796. '("Hide Process Buffer" . lua-hide-process-buffer))
  1797. (define-key lua-mode-menu [show-process-buffer]
  1798. '("Show Process Buffer" . lua-show-process-buffer))
  1799. (define-key lua-mode-menu [end-of-proc]
  1800. '("End Of Proc" . lua-end-of-proc))
  1801. (define-key lua-mode-menu [beginning-of-proc]
  1802. '("Beginning Of Proc" . lua-beginning-of-proc))
  1803. (define-key lua-mode-menu [send-lua-region]
  1804. '("Send Lua-Region" . lua-send-lua-region))
  1805. (define-key lua-mode-menu [set-lua-region-end]
  1806. '("Set Lua-Region End" . lua-set-lua-region-end))
  1807. (define-key lua-mode-menu [set-lua-region-start]
  1808. '("Set Lua-Region Start" . lua-set-lua-region-start))
  1809. (define-key lua-mode-menu [send-current-line]
  1810. '("Send Current Line" . lua-send-current-line))
  1811. (define-key lua-mode-menu [send-region]
  1812. '("Send Region" . lua-send-region))
  1813. (define-key lua-mode-menu [send-proc]
  1814. '("Send Proc" . lua-send-proc))
  1815. (define-key lua-mode-menu [send-buffer]
  1816. '("Send Buffer" . lua-send-buffer))
  1817. (define-key lua-mode-menu [search-documentation]
  1818. '("Search Documentation" . lua-search-documentation))
  1819. (provide 'lua-mode)
  1820. ;;; lua-mode.el ends here