wok view emacs-pkg-lua-mode/stuff/lua-mode.el @ rev 24190
updated perl-crypt-cbc (2.33 -> 3.04)
author | Hans-G?nter Theisgen |
---|---|
date | Fri Dec 31 14:34:21 2021 +0100 (2021-12-31) |
parents | 755fb3544005 |
children |
line source
1 ;;; lua-mode.el --- a major-mode for editing Lua scripts
3 ;; Copyright (C) 1997, 2001, 2004, 2006, 2007, 2010, 2011 Free Software Foundation, Inc.
5 ;; Author: 2011 immerrr <immerrr+lua@gmail.com>
6 ;; 2010-2011 Reuben Thomas <rrt@sc3d.org>
7 ;; 2006 Juergen Hoetzel <juergen@hoetzel.info>
8 ;; 2004 various (support for Lua 5 and byte compilation)
9 ;; 2001 Christian Vogler <cvogler@gradient.cis.upenn.edu>
10 ;; 1997 Bret Mogilefsky <mogul-lua@gelatinous.com> starting from
11 ;; tcl-mode by Gregor Schmid <schmid@fb3-s7.math.tu-berlin.de>
12 ;; with tons of assistance from
13 ;; Paul Du Bois <pld-lua@gelatinous.com> and
14 ;; Aaron Smith <aaron-lua@gelatinous.com>.
15 ;;
16 ;; URL: http://immerrr.github.com/lua-mode
17 ;; Version: 20111107
18 ;;
19 ;; This file is NOT part of Emacs.
20 ;;
21 ;; This program is free software; you can redistribute it and/or
22 ;; modify it under the terms of the GNU General Public License
23 ;; as published by the Free Software Foundation; either version 2
24 ;; of the License, or (at your option) any later version.
25 ;;
26 ;; This program is distributed in the hope that it will be useful,
27 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 ;; GNU General Public License for more details.
30 ;;
31 ;; You should have received a copy of the GNU General Public License
32 ;; along with this program; if not, write to the Free Software
33 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
34 ;; MA 02110-1301, USA.
36 ;; Keywords: languages, processes, tools
39 ;;; Commentary:
41 ;; Thanks to d87 <github.com/d87> for an idea of highlighting lua
42 ;; builtins/numbers
44 ;; Thanks to Vedat Hallac <github.com/vhallac> for sharing some of
45 ;; his fixes and updates to core indentation logics
47 ;; Thanks to Rafael Sanchez <rafael@cornerdimension.com> for patch
48 ;; adding lua-mode to interpreter-mode-alist
50 ;; Thanks to Leonardo Etcheverry <leo@kalio.net> for enabling
51 ;; narrow-to-defun functionality
53 ;; Thanks to Tobias Polzin <polzin@gmx.de> for function indenting
54 ;; patch: Indent "(" like "{"
56 ;; Thanks to Fabien <fleutot@gmail.com> for imenu patches.
58 ;; Thanks to Simon Marshall <simonm@mail.esrin.esa.it> and Olivier
59 ;; Andrieu <oandrieu@gmail.com> for font-lock patches.
61 ;; Additional font-lock highlighting and indentation tweaks by
62 ;; Adam D. Moss <adam@gimp.org>.
64 ;; INSTALLATION:
66 ;; To install, just copy this file into a directory on your load-path
67 ;; (and byte-compile it). To set up Emacs to automatically edit files
68 ;; ending in ".lua" or with a lua hash-bang line using lua-mode add
69 ;; the following to your init file:
70 ;;
71 ;; (autoload 'lua-mode "lua-mode" "Lua editing mode." t)
72 ;; (add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode))
73 ;; (add-to-list 'interpreter-mode-alist '("lua" . lua-mode))
75 ;; Usage
77 ;; Lua-mode supports c-mode style formatting and sending of
78 ;; lines/regions/files to a Lua interpreter. An interpreter (see
79 ;; variable `lua-default-application') will be started if you try to
80 ;; send some code and none is running. You can use the process-buffer
81 ;; (named after the application you chose) as if it were an
82 ;; interactive shell. See the documentation for `comint.el' for
83 ;; details.
85 ;; Lua-mode works with Hide Show minor mode (see ``hs-minor-mode``).
87 ;; Key-bindings
89 ;; To see all the keybindings for Lua mode, look at `lua-setup-keymap'
90 ;; or start `lua-mode' and type `\C-h m'.
91 ;; The keybindings may seem strange, since I prefer to use them with
92 ;; lua-prefix-key set to nil, but since those keybindings are already used
93 ;; the default for `lua-prefix-key' is `\C-c', which is the conventional
94 ;; prefix for major-mode commands.
96 ;; You can customise the keybindings either by setting `lua-prefix-key'
97 ;; or by putting the following in your .emacs
98 ;; (define-key lua-mode-map <your-key> <function>)
99 ;; for all the functions you need.
102 ;;; Code:
103 (eval-when-compile
104 (require 'cl))
106 (require 'comint)
108 (eval-and-compile
109 ;; Backward compatibility for Emacsen < 24.1
110 (defalias 'lua--prog-mode
111 (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
113 (defalias 'lua--cl-assert
114 (if (fboundp 'cl-assert) 'cl-assert 'assert))
116 (defalias 'lua--cl-labels
117 (if (fboundp 'cl-labels) 'cl-labels 'flet))
119 ;; for Emacsen < 22.1
120 (defalias 'lua--with-no-warnings
121 (if (fboundp 'with-no-warnings) 'with-no-warnings 'progn))
123 ;; provide backward compatibility for Emacs < 23.2
124 ;; called-interactively-p receives an argument starting from Emacs 23.2
125 ;; In Emacs 22 & Emacs 23.1 it didn't expect an argument
126 ;; In Emacs 21 it was called interactively-p
127 (condition-case nil
128 (progn (called-interactively-p nil)
129 ;; if first call succeeds, make lua-called-interactively-p an alias
130 (defalias 'lua--called-interactively-p 'called-interactively-p))
132 (wrong-number-of-arguments
133 ;; wrong number of arguments means it's 22.1 <= Emacs < 23.2
134 ;;
135 ;; Newer and smarter Emacsen will warn about obsolete functions
136 ;; and/or wrong number of arguments. Turning these warnings off,
137 ;; since it's backward-compatibility-oriented code anyway.
138 (lua--with-no-warnings
139 (defun lua--called-interactively-p (kind)
140 "Return t if containing function was called interactively.
142 This function provides lua-mode backward compatibility for
143 pre-23.2 Emacsen."
144 (if (eq kind 'interactive)
145 (interactive-p)
146 (called-interactively-p)))))
148 ;; if not, it's probably < 22.1, provide partial compatibility
149 ;;
150 ;; Once again, turning obsolete-function warnings off (see above).
151 (error
152 (lua--with-no-warnings
153 (defun lua--called-interactively-p (&rest opts)
154 "Return t if containing function was called interactively.
156 This function provides lua-mode backward compatibility for pre-22
157 Emacsen."
158 (interactive-p)))))
160 ;; backward compatibility for Emacsen < 23.3
161 ;; Emacs 23.3 introduced with-silent-modifications macro
162 (if (fboundp 'with-silent-modifications)
163 (defalias 'lua--with-silent-modifications 'with-silent-modifications)
165 (defmacro lua--with-silent-modifications (&rest body)
166 "Execute BODY, pretending it does not modifies the buffer.
168 This is a reimplementation of macro `with-silent-modifications'
169 for Emacsen that doesn't contain one (pre-23.3)."
170 `(let ((old-modified-p (buffer-modified-p))
171 (inhibit-modification-hooks t)
172 (buffer-undo-list t))
174 (unwind-protect
175 ,@body
176 (set-buffer-modified-p old-modified-p))))))
178 ;; Local variables
179 (defgroup lua nil
180 "Major mode for editing lua code."
181 :prefix "lua-"
182 :group 'languages)
184 (defcustom lua-indent-level 3
185 "Amount by which Lua subexpressions are indented."
186 :type 'integer
187 :group 'lua)
189 (defcustom lua-comment-start "-- "
190 "Default value of `comment-start'."
191 :type 'string
192 :group 'lua)
194 (defcustom lua-comment-start-skip "-- "
195 "Default value of `comment-start-skip'."
196 :type 'string
197 :group 'lua)
199 (defcustom lua-default-application "lua"
200 "Default application to run in lua subprocess."
201 :type 'string
202 :group 'lua)
204 (defcustom lua-default-command-switches (list "-i")
205 "Command switches for `lua-default-application'.
206 Should be a list of strings."
207 :type '(repeat string)
208 :group 'lua)
210 (defcustom lua-always-show t
211 "*Non-nil means display lua-process-buffer after sending a command."
212 :type 'boolean
213 :group 'lua)
215 (defcustom lua-search-url-prefix "http://www.lua.org/manual/5.1/manual.html#pdf-"
216 "*URL at which to search for documentation on a word"
217 :type 'string
218 :group 'lua)
220 (defvar lua-process nil
221 "The active Lua subprocess")
223 (defvar lua-process-buffer nil
224 "Buffer used for communication with Lua subprocess")
226 (defun lua--customize-set-prefix-key (prefix-key-sym prefix-key-val)
227 (lua--cl-assert (eq prefix-key-sym 'lua-prefix-key))
228 (set prefix-key-sym (if (and prefix-key-val (> (length prefix-key-val) 0))
229 ;; read-kbd-macro returns a string or a vector
230 ;; in both cases (elt x 0) is ok
231 (elt (read-kbd-macro prefix-key-val) 0)))
232 (if (fboundp 'lua-prefix-key-update-bindings)
233 (lua-prefix-key-update-bindings))
234 (message "prefix key set to %S" (single-key-description (eval prefix-key-sym))))
236 (defcustom lua-prefix-key "\C-c"
237 "Prefix for all lua-mode commands."
238 :type 'string
239 :group 'lua
240 :set 'lua--customize-set-prefix-key
241 :get '(lambda (sym)
242 (let ((val (eval sym))) (if val (single-key-description (eval sym)) ""))))
244 (defvar lua-mode-menu (make-sparse-keymap "Lua")
245 "Keymap for lua-mode's menu.")
247 (defvar lua-prefix-mode-map
248 (eval-when-compile
249 (let ((result-map (make-sparse-keymap)))
250 (mapc (lambda (key_defn)
251 (define-key result-map (read-kbd-macro (car key_defn)) (cdr key_defn)))
252 '(("C-l" . lua-send-buffer)
253 ("C-f" . lua-search-documentation)
254 ("C-;" . lua-mark-all-multiline-literals)))
255 result-map))
256 "Keymap that is used to define keys accessible by `lua-prefix-key'.
258 If the latter is nil, the keymap translates into `lua-mode-map' verbatim.")
260 (defvar lua-mode-map
261 (let ((result-map (make-sparse-keymap))
262 prefix-key)
263 (mapc (lambda (key_defn)
264 (define-key result-map (read-kbd-macro (car key_defn)) (cdr key_defn)))
265 ;; here go all the default bindings
266 ;; backquote enables evaluating certain symbols by comma
267 `(("}" . lua-electric-match)
268 ("]" . lua-electric-match)
269 (")" . lua-electric-match)))
270 (define-key result-map [menu-bar lua-mode] (cons "Lua" lua-mode-menu))
272 ;; FIXME: see if the declared logic actually works
273 ;; handle prefix-keyed bindings:
274 ;; * if no prefix, set prefix-map as parent, i.e.
275 ;; if key is not defined look it up in prefix-map
276 ;; * if prefix is set, bind the prefix-map to that key
277 (if (boundp 'lua-prefix-key)
278 (define-key result-map (vector lua-prefix-key) lua-prefix-mode-map)
279 (set-keymap-parent result-map lua-prefix-mode-map))
280 result-map)
281 "Keymap used in lua-mode buffers.")
283 (defvar lua-electric-flag t
284 "If t, electric actions (like automatic reindentation) will happen when an electric
285 key like `{' is pressed")
286 (make-variable-buffer-local 'lua-electric-flag)
288 (defcustom lua-prompt-regexp "[^\n]*\\(>[\t ]+\\)+$"
289 "Regexp which matches the Lua program's prompt."
290 :type 'regexp
291 :group 'lua)
293 (defcustom lua-traceback-line-re
294 "^\\(?:[\t ]*\\|.*>[\t ]+\\)\\([^\n\t ]+\\):\\([0-9]+\\):"
295 "Regular expression that describes tracebacks and errors."
296 :type 'regexp
297 :group 'lua)
299 (defcustom lua-indent-string-contents nil
300 "If non-nil, contents of multiline string will be indented.
301 Otherwise leading amount of whitespace on each line is preserved."
302 :group 'lua
303 :type 'boolean)
305 (defcustom lua-jump-on-traceback t
306 "*Jump to innermost traceback location in *lua* buffer. When this
307 variable is non-nil and a traceback occurs when running Lua code in a
308 subprocess, jump immediately to the source code of the innermost
309 traceback location."
310 :type 'boolean
311 :group 'lua)
313 (defcustom lua-mode-hook nil
314 "Hooks called when Lua mode fires up."
315 :type 'hook
316 :group 'lua)
318 (defvar lua-region-start (make-marker)
319 "Start of special region for Lua communication.")
321 (defvar lua-region-end (make-marker)
322 "End of special region for Lua communication.")
324 (defvar lua-emacs-menu
325 '(["Restart With Whole File" lua-restart-with-whole-file t]
326 ["Kill Process" lua-kill-process t]
327 ["Hide Process Buffer" lua-hide-process-buffer t]
328 ["Show Process Buffer" lua-show-process-buffer t]
329 ["Beginning Of Proc" lua-beginning-of-proc t]
330 ["End Of Proc" lua-end-of-proc t]
331 ["Set Lua-Region Start" lua-set-lua-region-start t]
332 ["Set Lua-Region End" lua-set-lua-region-end t]
333 ["Send Lua-Region" lua-send-lua-region t]
334 ["Send Current Line" lua-send-current-line t]
335 ["Send Region" lua-send-region t]
336 ["Send Proc" lua-send-proc t]
337 ["Send Buffer" lua-send-buffer t]
338 ["Search Documentation" lua-search-documentation t])
339 "Emacs menu for Lua mode.")
341 ;; the whole defconst is inside eval-when-compile, because it's later referenced
342 ;; inside another eval-and-compile block
343 (eval-and-compile
344 (defconst
345 lua--builtins
346 (let*
347 ((modules
348 '("_G" "_VERSION" "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable"
349 "ipairs" "load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print"
350 "rawequal" "rawget" "rawlen" "rawset" "require" "select" "setfenv" "setmetatable"
351 "tonumber" "tostring" "type" "unpack" "xpcall"
352 ("bit32" . ("arshift" "band" "bnot" "bor" "btest" "bxor" "extract" "lrotate" "lshift"
353 "replace" "rrotate" "rshift"))
354 ("coroutine" . ("create" "resume" "running" "status" "wrap" "yield"))
355 ("debug" . ("debug" "getfenv" "gethook" "getinfo" "getlocal" "getmetatable"
356 "getregistry" "getupvalue" "getuservalue" "setfenv" "sethook" "setlocal"
357 "setmetatable" "setupvalue" "setuservalue" "traceback" "upvalueid"
358 "upvaluejoin"))
359 ("io" . ("close" "flush" "input" "lines" "open" "output" "popen" "read" "stderr"
360 "stdin" "stdout" "tmpfile" "type" "write"))
361 ("math" . ("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "cosh" "deg" "exp" "floor"
362 "fmod" "frexp" "huge" "ldexp" "log" "log10" "max" "min" "modf" "pi" "pow"
363 "rad" "random" "randomseed" "sin" "sinh" "sqrt" "tan" "tanh"))
364 ("os" . ("clock" "date" "difftime" "execute" "exit" "getenv" "remove" "rename"
365 "setlocale" "time" "tmpname"))
366 ("package" . ("config" "cpath" "loaded" "loaders" "loadlib" "path" "preload"
367 "searchers" "searchpath" "seeall"))
368 ("string" . ("byte" "char" "dump" "find" "format" "gmatch" "gsub" "len" "lower"
369 "match" "rep" "reverse" "sub" "upper"))
370 ("table" . ("concat" "insert" "maxn" "pack" "remove" "sort" "unpack")))))
372 ;; This code uses \\< and \\> to delimit builtin symbols instead of
373 ;; \\_< and \\_>, because -- a necessity -- '.' syntax class is hacked
374 ;; to 'symbol' and \\_> won't detect a symbol boundary in 'foo.bar' and
375 ;; -- sufficiency -- conveniently, underscore '_' is hacked to count as
376 ;; word constituent, but only for font-locking. Neither of these hacks
377 ;; makes sense to me, I'm going to wipe them out as soon as I'm sure
378 ;; that indentation won't get hurt. --immerrr
379 ;;
380 (lua--cl-labels
381 ((module-name-re (x)
382 (concat "\\(?1:\\<"
383 (if (listp x) (car x) x)
384 "\\>\\)"))
385 (module-members-re (x) (if (listp x)
386 (concat "\\(?:[ \t]*\\.[ \t]*"
387 "\\<\\(?2:"
388 (regexp-opt (cdr x))
389 "\\)\\>\\)?")
390 "")))
392 (concat
393 ;; common prefix - beginning-of-line or neither of [ '.', ':' ] to
394 ;; exclude "foo.string.rep"
395 "\\(?:\\`\\|[^:. \n\t]\\)"
396 ;; optional whitespace
397 "[ \n\t]*"
398 "\\(?:"
399 ;; any of modules/functions
400 (mapconcat (lambda (x) (concat (module-name-re x)
401 (module-members-re x)))
402 modules
403 "\\|")
404 "\\)"))))
406 "A regexp that matches lua builtin functions & variables.
408 This is a compilation of 5.1 and 5.2 builtins taken from the
409 index of respective Lua reference manuals.")
411 (defvar lua-font-lock-keywords
412 (eval-when-compile
413 (list
414 ;; highlight the hash-bang line "#!/foo/bar/lua" as comment
415 '("^#!.*$" . font-lock-comment-face)
416 ;; Handle variable names
417 ;; local blalba =
418 ;; ^^^^^^
419 '("\\(local[ \t]+\\(\\sw+\\)[ \t]*=\\)"
420 (2 font-lock-variable-name-face))
422 ;; Function name declarations.
423 '("^[ \t]*\\_<\\(\\(local[ \t]+\\)?function\\)\\_>[ \t]+\\(\\(\\sw:\\|\\sw\\.\\|\\sw_\\|\\sw\\)+\\)"
424 (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
426 ;; Highlight lua builtin functions and variables
427 `(,lua--builtins
428 (1 font-lock-builtin-face) (2 font-lock-builtin-face nil noerror))
430 ;; Handle function names in assignments
431 '("\\(\\(\\sw:\\|\\sw\\.\\|\\sw_\\|\\sw\\)+\\)[ \t]*=[ \t]*\\(function\\)\\_>"
432 (1 font-lock-function-name-face nil t) (3 font-lock-keyword-face))
434 ;; octal numbers
435 '("\\_<0x[[:xdigit:]]+\\_>" . font-lock-constant-face)
437 ;; regular numbers
438 ;;
439 ;; This regexp relies on '.' being symbol constituent. Whenever this
440 ;; changes, the regexp needs revisiting --immerrr
441 `(,(concat "\\_<\\(?1:"
442 ;; make a digit on either side of dot mandatory
443 "\\(?:[0-9]+\\.?[0-9]*\\|[0-9]*\\.?[0-9]+\\)"
444 "\\(?:[eE][+-]?[0-9]+\\)?"
445 "\\)\\_>")
446 . font-lock-constant-face)
448 ;; Keywords.
449 (concat "\\_<"
450 (regexp-opt '("and" "break" "do" "else" "elseif" "end" "false"
451 "for" "function" "if" "in" "local" "nil" "not"
452 "or" "repeat" "return" "then" "true" "until"
453 "while") t)
454 "\\_>")
456 "Default expressions to highlight in Lua mode.")))
458 (defvar lua-imenu-generic-expression
459 '((nil "^[ \t]*\\(?:local[ \t]+\\)?function[ \t]+\\(\\(\\sw:\\|\\sw_\\|\\sw\\.\\|\\sw\\)+\\)" 1))
460 "Imenu generic expression for lua-mode. See `imenu-generic-expression'.")
462 (defvar lua-sexp-alist '(("then" . "end")
463 ("function" . "end")
464 ("do" . "end")))
466 (defvar lua-mode-abbrev-table nil
467 "Abbreviation table used in lua-mode buffers.")
469 (define-abbrev-table 'lua-mode-abbrev-table
470 ;; Emacs 23 introduced :system property that prevents abbrev
471 ;; entries from being written to file specified by abbrev-file-name
472 ;;
473 ;; Emacs 22 and earlier had this functionality implemented
474 ;; by simple nil/non-nil flag as positional parameter
475 (if (>= emacs-major-version 23)
476 '(("end" "end" lua-indent-line :system t)
477 ("else" "else" lua-indent-line :system t)
478 ("elseif" "elseif" lua-indent-line :system t))
479 '(("end" "end" lua-indent-line nil 'system)
480 ("else" "else" lua-indent-line nil 'system)
481 ("elseif" "elseif" lua-indent-line nil 'system))))
483 (eval-and-compile
484 (defalias 'lua-make-temp-file
485 (if (fboundp 'make-temp-file)
486 'make-temp-file
487 (lambda (prefix &optional dir-flag) ;; Simple implementation
488 (expand-file-name
489 (make-temp-name prefix)
490 (if (fboundp 'temp-directory)
491 (temp-directory)
492 temporary-file-directory))))))
494 (defvar lua-mode-syntax-table
495 (with-syntax-table (copy-syntax-table)
496 (modify-syntax-entry ?+ ".")
497 (modify-syntax-entry ?- ". 12")
498 (modify-syntax-entry ?* ".")
499 (modify-syntax-entry ?/ ".")
500 (modify-syntax-entry ?^ ".")
501 ;; This might be better as punctuation, as for C, but this way you
502 ;; can treat table index as symbol.
503 (modify-syntax-entry ?. "_") ; e.g. `io.string'
504 (modify-syntax-entry ?> ".")
505 (modify-syntax-entry ?< ".")
506 (modify-syntax-entry ?= ".")
507 (modify-syntax-entry ?~ ".")
508 (modify-syntax-entry ?\n ">")
509 (modify-syntax-entry ?\' "\"")
510 (modify-syntax-entry ?\" "\"")
511 (syntax-table))
512 "Syntax table used while in `lua-mode'.")
514 ;;;###autoload
515 (define-derived-mode lua-mode lua--prog-mode "Lua"
516 "Major mode for editing Lua code."
517 :abbrev-table lua-mode-abbrev-table
518 :syntax-table lua-mode-syntax-table
519 :group 'lua
520 (let ((switches nil)
521 s)
522 (setq comint-prompt-regexp lua-prompt-regexp)
523 (make-local-variable 'lua-default-command-switches)
524 (set (make-local-variable 'beginning-of-defun-function)
525 'lua-beginning-of-proc)
526 (set (make-local-variable 'end-of-defun-function) 'lua-end-of-proc)
527 (set (make-local-variable 'indent-line-function) 'lua-indent-line)
528 (set (make-local-variable 'comment-start) lua-comment-start)
529 (set (make-local-variable 'comment-start-skip) lua-comment-start-skip)
530 (set (make-local-variable 'font-lock-defaults)
531 '(lua-font-lock-keywords
532 nil nil ((?_ . "w"))))
533 (set (make-local-variable 'imenu-generic-expression)
534 lua-imenu-generic-expression)
535 (make-local-variable 'lua-default-eval)
536 ;; setup menu bar entry (XEmacs style)
537 (if (and (featurep 'menubar)
538 (boundp 'current-menubar)
539 (fboundp 'set-buffer-menubar)
540 (fboundp 'add-menu)
541 (not (assoc "Lua" current-menubar)))
542 (progn
543 (set-buffer-menubar (copy-sequence current-menubar))
544 (add-menu nil "Lua" lua-emacs-menu)))
545 ;; Append Lua menu to popup menu for Emacs.
546 (if (boundp 'mode-popup-menu)
547 (setq mode-popup-menu
548 (cons (concat mode-name " Mode Commands") lua-emacs-menu)))
550 ;; hideshow setup
551 (unless (assq 'lua-mode hs-special-modes-alist)
552 (add-to-list 'hs-special-modes-alist
553 `(lua-mode
554 ,(regexp-opt (mapcar 'car lua-sexp-alist) 'words) ;start
555 ,(regexp-opt (mapcar 'cdr lua-sexp-alist) 'words) ;end
556 nil lua-forward-sexp)))
558 (set (make-local-variable 'parse-sexp-lookup-properties) t)
559 (lua-mark-all-multiline-literals)
560 (lua--automark-multiline-update-timer)))
562 ;;;###autoload
563 (add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode))
565 ;;;###autoload
566 (add-to-list 'interpreter-mode-alist '("lua" . lua-mode))
568 (defun lua-electric-match (arg)
569 "Insert character and adjust indentation."
570 (interactive "P")
571 (insert-char last-command-event (prefix-numeric-value arg))
572 (if lua-electric-flag
573 (lua-indent-line))
574 (blink-matching-open))
576 ;; private functions
578 (defun lua-prefix-key-update-bindings ()
579 (let (old-cons)
580 (if (eq lua-prefix-mode-map (keymap-parent lua-mode-map))
581 ;; if prefix-map is a parent, delete the parent
582 (set-keymap-parent lua-mode-map nil)
583 ;; otherwise, look for it among children
584 (if (setq old-cons (rassoc lua-prefix-mode-map lua-mode-map))
585 (delq old-cons lua-mode-map)))
587 (if (null lua-prefix-key)
588 (set-keymap-parent lua-mode-map lua-prefix-mode-map)
589 (define-key lua-mode-map (vector lua-prefix-key) lua-prefix-mode-map))))
591 (defun lua-set-prefix-key (new-key-str)
592 "Changes `lua-prefix-key' properly and updates keymaps
594 This function replaces previous prefix-key binding with a new one."
595 (interactive "sNew prefix key (empty string means no key): ")
596 (lua--customize-set-prefix-key 'lua-prefix-key new-key-str)
597 (lua-prefix-key-update-bindings))
599 (defun lua-string-p (&optional pos)
600 "Returns true if the point is in a string."
601 (save-excursion (elt (syntax-ppss pos) 3)))
603 (defun lua-comment-p (&optional pos)
604 "Returns true if the point is in a comment."
605 (save-excursion (elt (syntax-ppss pos) 4)))
607 (defun lua-comment-or-string-p (&optional pos)
608 "Returns true if the point is in a comment or string."
609 (save-excursion (let ((parse-result (syntax-ppss pos)))
610 (or (elt parse-result 3) (elt parse-result 4)))))
612 (defun lua-comment-or-string-start (&optional pos)
613 "Returns start position of string or comment which contains point.
615 If point is not inside string or comment, return nil."
616 (save-excursion (elt (syntax-ppss pos) 8)))
618 (defun lua-indent-line ()
619 "Indent current line for Lua mode.
620 Return the amount the indentation changed by."
621 (let (indent
622 (case-fold-search nil)
623 ;; save point as a distance to eob - it's invariant w.r.t indentation
624 (pos (- (point-max) (point))))
625 (back-to-indentation)
626 (if (lua-comment-or-string-p)
627 (setq indent (lua-calculate-string-or-comment-indentation)) ;; just restore point position
628 (setq indent (max 0 (lua-calculate-indentation nil))))
630 (when (not (equal indent (current-column)))
631 (delete-region (line-beginning-position) (point))
632 (indent-to indent))
634 ;; If initial point was within line's indentation,
635 ;; position after the indentation. Else stay at same point in text.
636 (if (> (- (point-max) pos) (point))
637 (goto-char (- (point-max) pos)))
639 indent))
641 (defun lua-calculate-string-or-comment-indentation ()
642 "This function should be run when point at (current-indentation) is inside string"
643 (if (and (lua-string-p) (not lua-indent-string-contents))
644 ;; if inside string and strings aren't to be indented, return current indentation
645 (current-indentation)
646 ;; Otherwise, indent as a comment
647 (save-excursion
648 (cond
649 ;; If it is the end of a multi-line comment, simply mirror the opening
650 ;; line's indent.
651 ((looking-at "\\s *\\(?:--\\)?\\]\\(?1:=*\\)\\]")
652 (re-search-backward (format "\\[%s\\["
653 (or (match-string-no-properties 1) ""))
654 (lua-get-multiline-start)
655 'noerror)
656 (current-indentation))
657 ;; otherwise indent by lua-indent-level relative to the line where literal starts
658 (t
659 (goto-char (lua-get-multiline-start))
660 (+ (current-indentation) lua-indent-level))))))
662 (defun lua-find-regexp (direction regexp &optional limit ignore-p)
663 "Searches for a regular expression in the direction specified.
664 Direction is one of 'forward and 'backward.
665 By default, matches in comments and strings are ignored, but what to ignore is
666 configurable by specifying ignore-p. If the regexp is found, returns point
667 position, nil otherwise.
668 ignore-p returns true if the match at the current point position should be
669 ignored, nil otherwise."
670 (let ((ignore-func (or ignore-p 'lua-comment-or-string-p))
671 (search-func (if (eq direction 'forward)
672 're-search-forward 're-search-backward))
673 (case-fold-search nil))
674 (catch 'found
675 (while (funcall search-func regexp limit t)
676 (if (and (not (funcall ignore-func (match-beginning 0)))
677 (not (funcall ignore-func (match-end 0))))
678 (throw 'found (point)))))))
680 (defconst lua-block-regexp
681 (eval-when-compile
682 (concat
683 "\\(\\_<"
684 (regexp-opt '("do" "function" "repeat" "then"
685 "else" "elseif" "end" "until") t)
686 "\\_>\\)\\|"
687 (regexp-opt '("{" "(" "[" "]" ")" "}") t))))
689 (defconst lua-block-token-alist
690 '(("do" "\\<end\\>" "\\<for\\|while\\>" middle-or-open)
691 ("function" "\\<end\\>" nil open)
692 ("repeat" "\\<until\\>" nil open)
693 ("then" "\\<\\(e\\(lse\\(if\\)?\\|nd\\)\\)\\>" "\\<\\(else\\)?if\\>" middle)
694 ("{" "}" nil open)
695 ("[" "]" nil open)
696 ("(" ")" nil open)
697 ("if" "\\<then\\>" nil open)
698 ("for" "\\<do\\>" nil open)
699 ("while" "\\<do\\>" nil open)
700 ("else" "\\<end\\>" "\\<then\\>" middle)
701 ("elseif" "\\<then\\>" "\\<then\\>" middle)
702 ("end" nil "\\<\\(do\\|function\\|then\\|else\\)\\>" close)
703 ("until" nil "\\<repeat\\>" close)
704 ("}" nil "{" close)
705 ("]" nil "\\[" close)
706 (")" nil "(" close))
707 "This is a list of block token information blocks.
708 Each token information entry is of the form:
709 KEYWORD FORWARD-MATCH-REGEXP BACKWARDS-MATCH-REGEXP TOKEN-TYPE
710 KEYWORD is the token.
711 FORWARD-MATCH-REGEXP is a regexp that matches all possble tokens when going forward.
712 BACKWARDS-MATCH-REGEXP is a regexp that matches all possble tokens when going backwards.
713 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.")
715 (defconst lua-indentation-modifier-regexp
716 ;; The absence of else is deliberate, since it does not modify the
717 ;; indentation level per se. It only may cause the line, in which the
718 ;; else is, to be shifted to the left.
719 (concat
720 "\\(\\_<"
721 (regexp-opt '("do" "function" "repeat" "then" "if" "else" "elseif" "for" "while") t)
722 "\\_>\\|"
723 (regexp-opt '("{" "(" "["))
724 "\\)\\|\\(\\_<"
725 (regexp-opt '("end" "until") t)
726 "\\_>\\|"
727 (regexp-opt '("]" ")" "}"))
728 "\\)")
729 )
731 (defun lua-get-block-token-info (token)
732 "Returns the block token info entry for TOKEN from lua-block-token-alist"
733 (assoc token lua-block-token-alist))
735 (defun lua-get-token-match-re (token-info direction)
736 "Returns the relevant match regexp from token info"
737 (cond
738 ((eq direction 'forward) (cadr token-info))
739 ((eq direction 'backward) (caddr token-info))
740 (t nil)))
742 (defun lua-get-token-type (token-info)
743 "Returns the relevant match regexp from token info"
744 (cadddr token-info))
746 (defun lua-backwards-to-block-begin-or-end ()
747 "Move backwards to nearest block begin or end. Returns nil if not successful."
748 (interactive)
749 (lua-find-regexp 'backward lua-block-regexp))
751 (defun lua-find-matching-token-word (token search-start &optional direction)
752 (let* ((token-info (lua-get-block-token-info token))
753 (match-type (lua-get-token-type token-info))
754 ;; If we are on a middle token, go backwards. If it is a middle or open,
755 ;; go forwards
756 (search-direction (or direction
757 (if (or (eq match-type 'open)
758 (eq match-type 'middle-or-open))
759 'forward
760 'backward)
761 'backward))
762 (match (lua-get-token-match-re token-info search-direction))
763 maybe-found-pos)
764 ;; if we are searching forward from the token at the current point
765 ;; (i.e. for a closing token), need to step one character forward
766 ;; first, or the regexp will match the opening token.
767 (if (eq search-direction 'forward) (forward-char 1))
768 (if search-start (goto-char search-start))
769 (catch 'found
770 ;; If we are attempting to find a matching token for a terminating token
771 ;; (i.e. a token that starts a statement when searching back, or a token
772 ;; that ends a statement when searching forward), then we don't need to look
773 ;; any further.
774 (if (or (and (eq search-direction 'forward)
775 (eq match-type 'close))
776 (and (eq search-direction 'backward)
777 (eq match-type 'open)))
778 (throw 'found nil))
779 (while (lua-find-regexp search-direction lua-indentation-modifier-regexp)
780 ;; have we found a valid matching token?
781 (let ((found-token (match-string 0))
782 (found-pos (match-beginning 0)))
783 (let ((found-type (lua-get-token-type
784 (lua-get-block-token-info found-token))))
785 (if (not (and match (string-match match found-token)))
786 ;; no - then there is a nested block. If we were looking for
787 ;; a block begin token, found-token must be a block end
788 ;; token; likewise, if we were looking for a block end token,
789 ;; found-token must be a block begin token, otherwise there
790 ;; is a grammatical error in the code.
791 (if (not (and
792 (or (eq match-type 'middle)
793 (eq found-type 'middle)
794 (eq match-type 'middle-or-open)
795 (eq found-type 'middle-or-open)
796 (eq match-type found-type))
797 (lua-find-matching-token-word found-token nil
798 search-direction)))
799 (when maybe-found-pos
800 (goto-char maybe-found-pos)
801 (throw 'found maybe-found-pos)))
802 ;; yes.
803 ;; if it is a not a middle kind, report the location
804 (when (not (or (eq found-type 'middle)
805 (eq found-type 'middle-or-open)))
806 (throw 'found found-pos))
807 ;; if it is a middle-or-open type, record location, but keep searching.
808 ;; If we fail to complete the search, we'll report the location
809 (when (eq found-type 'middle-or-open)
810 (setq maybe-found-pos found-pos))
811 ;; Cannot use tail recursion. too much nesting on long chains of
812 ;; if/elseif. Will reset variables instead.
813 (setq token found-token)
814 (setq token-info (lua-get-block-token-info token))
815 (setq match (lua-get-token-match-re token-info search-direction))
816 (setq match-type (lua-get-token-type token-info))))))
817 maybe-found-pos)))
819 (defun lua-goto-matching-block-token (&optional search-start parse-start direction)
820 "Find block begion/end token matching the one at the point.
821 This function moves the point to the token that matches the one
822 at the current point. Returns the point position of the first character of
823 the matching token if successful, nil otherwise."
824 (if parse-start (goto-char parse-start))
825 (let ((case-fold-search nil))
826 (if (looking-at lua-indentation-modifier-regexp)
827 (let ((position (lua-find-matching-token-word (match-string 0)
828 search-start direction)))
829 (and position
830 (goto-char position))))))
832 (defun lua-goto-matching-block (&optional noreport)
833 "Go to the keyword balancing the one under the point.
834 If the point is on a keyword/brace that starts a block, go to the
835 matching keyword that ends the block, and vice versa."
836 (interactive)
837 ;; search backward to the beginning of the keyword if necessary
838 (if (eq (char-syntax (following-char)) ?w)
839 (re-search-backward "\\_<" nil t))
840 (let ((position (lua-goto-matching-block-token)))
841 (if (and (not position)
842 (not noreport))
843 (error "Not on a block control keyword or brace")
844 position)))
846 (defun lua-forward-line-skip-blanks (&optional back)
847 "Move 1 line forward (back if BACK is non-nil) skipping blank lines.
849 Moves point 1 line forward (or backward) skipping lines that contain
850 no Lua code besides comments. The point is put to the beginning of
851 the line.
853 Returns final value of point as integer or nil if operation failed."
854 (catch 'found
855 (while t
856 (unless (eql (forward-line (if back -1 1)) 0) ;; 0 means success
857 (throw 'found nil))
858 (unless (or (looking-at "\\s *\\(--.*\\)?$")
859 (lua-comment-or-string-p))
860 (throw 'found (point))))))
862 (eval-when-compile
863 (defconst lua-operator-class
864 "-+*/^.=<>~"))
866 (defconst lua-cont-eol-regexp
867 (eval-when-compile
868 (concat
869 "\\(\\_<"
870 (regexp-opt '("and" "or" "not" "in" "for" "while"
871 "local" "function" "if" "until" "elseif" "return") t)
872 "\\_>\\|"
873 "\\(^\\|[^" lua-operator-class "]\\)"
874 (regexp-opt '("+" "-" "*" "/" "^" ".." "==" "=" "<" ">" "<=" ">=" "~=") t)
875 "\\)"
876 "\\s *\\=")
877 )
878 "Regexp that matches the ending of a line that needs continuation
880 This regexp starts from eol and looks for a binary operator or an unclosed
881 block intro (i.e. 'for' without 'do' or 'if' without 'then') followed by
882 an optional whitespace till the end of the line.")
884 (defconst lua-cont-bol-regexp
885 (eval-when-compile
886 (concat
887 "\\=\\s *"
888 "\\(\\_<"
889 (regexp-opt '("and" "or" "not") t)
890 "\\_>\\|"
891 (regexp-opt '("+" "-" "*" "/" "^" ".." "==" "=" "<" ">" "<=" ">=" "~=") t)
892 "\\($\\|[^" lua-operator-class "]\\)"
893 "\\)")
895 )
896 "Regexp that matches a line that continues previous one
898 This regexp means, starting from point there is an optional whitespace followed
899 by Lua binary operator. Lua is very liberal when it comes to continuation line,
900 so we're safe to assume that every line that starts with a binop continues
901 previous one even though it looked like an end-of-statement.")
903 (defun lua-last-token-continues-p ()
904 "Returns true if the last token on this line is a continuation token."
905 (let ((line-begin (line-beginning-position))
906 (line-end (line-end-position)))
907 (save-excursion
908 (end-of-line)
909 ;; we need to check whether the line ends in a comment and
910 ;; skip that one.
911 (while (lua-find-regexp 'backward "-" line-begin 'lua-string-p)
912 (if (looking-at "--")
913 (setq line-end (point))))
914 (goto-char line-end)
915 (re-search-backward lua-cont-eol-regexp line-begin t))))
917 (defun lua-first-token-continues-p ()
918 "Returns true if the first token on this line is a continuation token."
919 (let ((line-end (line-end-position)))
920 (save-excursion
921 (beginning-of-line)
922 ;; if first character of the line is inside string, it's a continuation
923 ;; if strings aren't supposed to be indented, `lua-calculate-indentation' won't even let
924 ;; the control inside this function
925 (re-search-forward lua-cont-bol-regexp line-end t))))
927 (defun lua-is-continuing-statement-p (&optional parse-start)
928 "Return non-nil if the line continues a statement.
929 More specifically, return the point in the line that is continued.
930 The criteria for a continuing statement are:
932 * the last token of the previous line is a continuing op,
933 OR the first token of the current line is a continuing op
935 "
936 (let ((prev-line nil))
937 (save-excursion
938 (if parse-start (goto-char parse-start))
939 (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back)))
940 (and prev-line
941 (or (lua-first-token-continues-p)
942 (and (goto-char prev-line)
943 ;; check last token of previous nonblank line
944 (lua-last-token-continues-p)))))))
946 (defun lua-make-indentation-info-pair (found-token found-pos)
947 "This is a helper function to lua-calculate-indentation-info. Don't
948 use standalone."
949 (cond
950 ;; function is a bit tricky to indent right. They can appear in a lot ot
951 ;; different contexts. Until I find a shortcut, I'll leave it with a simple
952 ;; relative indentation.
953 ;; The special cases are for indenting according to the location of the
954 ;; function. i.e.:
955 ;; (cons 'absolute (+ (current-column) lua-indent-level))
956 ;; TODO: Fix this. It causes really ugly indentations for in-line functions.
957 ((string-equal found-token "function")
958 (cons 'relative lua-indent-level))
960 ;; block openers
961 ((member found-token (list "{" "(" "["))
962 (save-excursion
963 ;; expression follows -> indent at start of next expression
964 ;; Last token on the line -> simple relative indent
965 (if (and (not (search-forward-regexp "[[:space:]]--" (line-end-position) t))
966 (search-forward-regexp "[^[:space:]]" (line-end-position) t))
967 (cons 'absolute (1- (current-column)))
968 (cons 'relative lua-indent-level))))
970 ;; These are not really block starters. They should not add to indentation.
971 ;; The corresponding "then" and "do" handle the indentation.
972 ((member found-token (list "if" "for" "while"))
973 (cons 'relative 0))
974 ;; closing tokens follow: These are usually taken care of by
975 ;; lua-calculate-indentation-override.
976 ;; elseif is a bit of a hack. It is not handled separately, but it needs to
977 ;; nullify a previous then if on the same line.
978 ((member found-token (list "until" "elseif"))
979 (save-excursion
980 (let ((line (line-number-at-pos)))
981 (if (and (lua-goto-matching-block-token nil found-pos 'backward)
982 (= line (line-number-at-pos)))
983 (cons 'remove-matching 0)
984 (cons 'relative 0)))))
986 ;; else is a special case; if its matching block token is on the same line,
987 ;; instead of removing the matching token, it has to replace it, so that
988 ;; either the next line will be indented correctly, or the end on the same
989 ;; line will remove the effect of the else.
990 ((string-equal found-token "else")
991 (save-excursion
992 (let ((line (line-number-at-pos)))
993 (if (and (lua-goto-matching-block-token nil found-pos 'backward)
994 (= line (line-number-at-pos)))
995 (cons 'replace-matching (cons 'relative lua-indent-level))
996 (cons 'relative lua-indent-level)))))
998 ;; Block closers. If they are on the same line as their openers, they simply
999 ;; eat up the matching indentation modifier. Otherwise, they pull
1000 ;; indentation back to the matching block opener.
1001 ((member found-token (list ")" "}" "]" "end"))
1002 (save-excursion
1003 (let ((line (line-number-at-pos)))
1004 (lua-goto-matching-block-token nil found-pos 'backward)
1005 (if (/= line (line-number-at-pos))
1006 (cons 'absolute
1007 (+ (current-indentation)
1008 (lua-calculate-indentation-block-modifier
1009 nil (point))))
1010 (cons 'remove-matching 0)))))
1012 ;; Everything else. This is from the original code: If opening a block
1013 ;; (match-data 1 exists), then push indentation one level up, if it is
1014 ;; closing a block, pull it one level down.
1015 ('other-indentation-modifier
1016 (cons 'relative (if (nth 2 (match-data))
1017 ;; beginning of a block matched
1018 lua-indent-level
1019 ;; end of a block matched
1020 (- lua-indent-level))))))
1022 (defun lua-add-indentation-info-pair (pair info)
1023 "Add the given indentation info pair to the list of indentation information.
1024 This function has special case handling for two tokens: remove-matching,
1025 and replace-matching. These two tokens are cleanup tokens that remove or
1026 alter the effect of a previously recorded indentation info.
1028 When a remove-matching token is encountered, the last recorded info, i.e.
1029 the car of the list is removed. This is used to roll-back an indentation of a
1030 block opening statement when it is closed.
1032 When a replace-matching token is seen, the last recorded info is removed,
1033 and the cdr of the replace-matching info is added in its place. This is used
1034 when a middle-of the block (the only case is 'else') is seen on the same line
1035 the block is opened."
1036 (cond
1037 ( (eq 'remove-matching (car pair))
1038 ; Remove head of list
1039 (cdr info))
1040 ( (eq 'replace-matching (car pair))
1041 ; remove head of list, and add the cdr of pair instead
1042 (cons (cdr pair) (cdr info)))
1043 ( t
1044 ; Just add the pair
1045 (cons pair info))))
1047 (defun lua-calculate-indentation-info (&optional parse-start parse-end)
1048 "For each block token on the line, computes how it affects the indentation.
1049 The effect of each token can be either a shift relative to the current
1050 indentation level, or indentation to some absolute column. This information
1051 is collected in a list of indentation info pairs, which denote absolute
1052 and relative each, and the shift/column to indent to."
1053 (let ((combined-line-end (line-end-position))
1054 (start-indentation (current-indentation)))
1055 (save-excursion
1056 (while (lua-last-token-continues-p)
1057 (lua-forward-line-skip-blanks)
1058 (setq combined-line-end (line-end-position))))
1059 (let ((search-stop (if parse-end
1060 (min parse-end combined-line-end)
1061 combined-line-end))
1062 (indentation-info nil))
1063 (if parse-start (goto-char parse-start))
1064 (save-excursion
1065 (beginning-of-line)
1066 (while (lua-find-regexp 'forward lua-indentation-modifier-regexp
1067 search-stop)
1068 (let ((found-token (match-string 0))
1069 (found-pos (match-beginning 0))
1070 (found-end (match-end 0))
1071 (data (match-data)))
1072 (setq indentation-info
1073 (lua-add-indentation-info-pair
1074 (lua-make-indentation-info-pair found-token found-pos)
1075 indentation-info))))
1076 (or indentation-info
1077 (list (cons 'absolute start-indentation)))))))
1079 (defun lua-accumulate-indentation-info (info)
1080 "Accumulates the indentation information previously calculated by
1081 lua-calculate-indentation-info. Returns either the relative indentation
1082 shift, or the absolute column to indent to."
1083 (let ((info-list (reverse info))
1084 (type 'relative)
1085 (accu 0))
1086 (mapc (lambda (x)
1087 (setq accu (if (eq 'absolute (car x))
1088 (progn (setq type 'absolute)
1089 (cdr x))
1090 (+ accu (cdr x)))))
1091 info-list)
1092 (cons type accu)))
1094 (defun lua-calculate-indentation-block-modifier (&optional parse-start
1095 parse-end)
1096 "Return amount by which this line modifies the indentation.
1097 Beginnings of blocks add lua-indent-level once each, and endings
1098 of blocks subtract lua-indent-level once each. This function is used
1099 to determine how the indentation of the following line relates to this
1100 one."
1101 (if parse-start (goto-char parse-start))
1102 ;; First go back to the line that starts it all
1103 ;; lua-calculate-indentation-info will scan through the whole thing
1104 (while (lua-is-continuing-statement-p)
1105 (lua-forward-line-skip-blanks 'back))
1106 (let ((case-fold-search nil)
1107 (indentation-info (lua-accumulate-indentation-info
1108 (lua-calculate-indentation-info nil parse-end))))
1109 (if (eq (car indentation-info) 'absolute)
1110 (- (cdr indentation-info) (current-indentation))
1111 (cdr indentation-info))))
1113 (defun lua-point-is-after-left-shifter-p ()
1114 "Check if point is at a left-shifter.
1115 A left-shifter is a partial lua expression which should be ignored for line up purposes when closing a block. An example of this is:
1116 local a = function()
1117 ....
1118 end
1119 ^ ^
1120 | +- not here
1121 +- Close here"
1122 (save-excursion
1123 (let ((old-point (point)))
1124 (back-to-indentation)
1125 (and
1126 (or (looking-at "local\\s +\\(?:\\(?:\\sw\\|\\s_\\)+\\s *\\(,\\s *\\(?:\\sw\\|\\s_\\)+\\s *\\)*=\\s *\\)?")
1127 ;; This is too generic, and will screw up a lot of indentations. Will need
1128 ;; a better regexp for assignments
1129 (looking-at "[^=]*=\\s *"))
1130 (= old-point (match-end 0))))))
1132 (defun lua-calculate-indentation-override (&optional parse-start)
1133 "Return overriding indentation amount for special cases.
1134 Look for an uninterrupted sequence of block-closing tokens that starts
1135 at the beginning of the line. For each of these tokens, shift indentation
1136 to the left by the amount specified in lua-indent-level."
1137 (let ((indentation-modifier 0)
1138 (case-fold-search nil)
1139 (block-token nil))
1140 (save-excursion
1141 (if parse-start (goto-char parse-start))
1142 ;; Look for the last block closing token
1143 (back-to-indentation)
1144 (if (and (not (lua-comment-or-string-p))
1145 (looking-at lua-indentation-modifier-regexp)
1146 (let ((token-info (lua-get-block-token-info (match-string 0))))
1147 (and token-info
1148 (not (eq 'open (lua-get-token-type token-info))))))
1149 (when (lua-goto-matching-block-token nil nil 'backward)
1150 ;; Exception cases: when the start of the line is an assignment,
1151 ;; go to the start of the assignment instead of the matching item
1152 (let ((block-start-column (current-column))
1153 (block-start-point (point)))
1154 (if (lua-point-is-after-left-shifter-p)
1155 (current-indentation)
1156 block-start-column)))))))
1158 (defun lua-calculate-indentation (&optional parse-start)
1159 "Return appropriate indentation for current line as Lua code."
1160 (save-excursion
1161 (let ((continuing-p (lua-is-continuing-statement-p)))
1162 (or
1163 ;; when calculating indentation, do the following:
1164 ;; 1. check, if the line starts with indentation-modifier (open/close brace)
1165 ;; and if it should be indented/unindented in special way
1166 (lua-calculate-indentation-override)
1168 ;; 2. otherwise, use indentation modifiers from previous line + it's own indentation
1169 ;; 3. if previous line doesn't contain indentation modifiers, additionally check
1170 ;; if current line is a continuation line and add lua-indent-level if it is
1171 (when (lua-forward-line-skip-blanks 'back)
1172 ;; the order of function calls here is important. block modifier
1173 ;; call may change the point to another line
1174 (let ((modifier
1175 (lua-calculate-indentation-block-modifier nil (line-end-position))))
1176 (+ (if (and continuing-p (= 0 modifier))
1177 lua-indent-level
1178 modifier)
1179 (current-indentation))))
1181 ;; 4. if there's no previous line, indentation is 0
1182 0))))
1184 (defun lua-beginning-of-proc (&optional arg)
1185 "Move backward to the beginning of a lua proc (or similar).
1186 With argument, do it that many times. Negative arg -N
1187 means move forward to Nth following beginning of proc.
1188 Returns t unless search stops due to beginning or end of buffer."
1189 (interactive "P")
1190 (or arg
1191 (setq arg 1))
1192 (let ((found nil)
1193 (ret t))
1194 (while (< arg 0)
1195 (if (re-search-forward "^function[ \t]" nil t)
1196 (setq arg (1+ arg)
1197 found t)
1198 (setq ret nil
1199 arg 0)))
1200 (if found
1201 (beginning-of-line))
1202 (if (> arg 0)
1203 (if (re-search-forward "^function[ \t]" nil t)
1204 (setq arg (1+ arg))
1205 (goto-char (point-max))))
1206 (while (> arg 0)
1207 (if (re-search-backward "^function[ \t]" nil t)
1208 (setq arg (1- arg))
1209 (setq ret nil
1210 arg 0)))
1211 ret))
1213 (defun lua-end-of-proc (&optional arg)
1214 "Move forward to next end of lua proc (or similar).
1215 With argument, do it that many times. Negative argument -N means move
1216 back to Nth preceding end of proc.
1218 This function just searches for a `end' at the beginning of a line."
1219 (interactive "P")
1220 (or arg
1221 (setq arg 1))
1222 (let ((found nil)
1223 (ret t))
1224 (if (and (< arg 0)
1225 (not (bolp))
1226 (save-excursion
1227 (beginning-of-line)
1228 (eq (following-char) ?})))
1229 (forward-char -1))
1230 (while (> arg 0)
1231 (if (re-search-forward "^end" nil t)
1232 (setq arg (1- arg)
1233 found t)
1234 (setq ret nil
1235 arg 0)))
1236 (while (< arg 0)
1237 (if (re-search-backward "^end" nil t)
1238 (setq arg (1+ arg)
1239 found t)
1240 (setq ret nil
1241 arg 0)))
1242 (if found
1243 (progn
1244 (beginning-of-line)
1245 (forward-line)))
1246 ret))
1248 (defun lua-start-process (&optional name program startfile &rest switches)
1249 "Start a lua process named NAME, running PROGRAM.
1250 PROGRAM defaults to NAME, which defaults to `lua-default-application'.
1251 When called interactively, switch to the process buffer."
1252 (interactive)
1253 (or switches
1254 (setq switches lua-default-command-switches))
1255 (setq name (or name lua-default-application))
1256 (setq program (or program name))
1257 (setq lua-process-buffer (apply 'make-comint name program startfile switches))
1258 (setq lua-process (get-buffer-process lua-process-buffer))
1259 ;; wait for prompt
1260 (with-current-buffer lua-process-buffer
1261 (while (not (lua-prompt-line))
1262 (accept-process-output (get-buffer-process (current-buffer)))
1263 (goto-char (point-max))))
1264 ;; when called interactively, switch to process buffer
1265 (if (lua--called-interactively-p 'any)
1266 (switch-to-buffer lua-process-buffer)))
1268 (defun lua-kill-process ()
1269 "Kill lua subprocess and its buffer."
1270 (interactive)
1271 (if lua-process-buffer
1272 (kill-buffer lua-process-buffer)))
1274 (defun lua-set-lua-region-start (&optional arg)
1275 "Set start of region for use with `lua-send-lua-region'."
1276 (interactive)
1277 (set-marker lua-region-start (or arg (point))))
1279 (defun lua-set-lua-region-end (&optional arg)
1280 "Set end of region for use with `lua-send-lua-region'."
1281 (interactive)
1282 (set-marker lua-region-end (or arg (point))))
1284 (defun lua-send-current-line ()
1285 "Send current line to lua subprocess, found in `lua-process'.
1286 If `lua-process' is nil or dead, start a new process first."
1287 (interactive)
1288 (lua-send-region (line-beginning-position) (line-end-position)))
1290 (defun lua-send-region (start end)
1291 "Send region to lua subprocess."
1292 (interactive "r")
1293 ;; make temporary lua file
1294 (let ((tempfile (lua-make-temp-file "lua-"))
1295 (last-prompt nil)
1296 (prompt-found nil)
1297 (lua-stdin-line-offset (count-lines (point-min) start))
1298 (lua-stdin-buffer (current-buffer))
1299 current-prompt )
1300 (write-region start end tempfile)
1301 (or (and lua-process
1302 (comint-check-proc lua-process-buffer))
1303 (lua-start-process lua-default-application))
1304 ;; kill lua process without query
1305 (if (fboundp 'process-kill-without-query)
1306 (process-kill-without-query lua-process))
1307 ;; send dofile(tempfile)
1308 (with-current-buffer lua-process-buffer
1309 (goto-char (point-max))
1310 (setq last-prompt (point-max))
1311 (comint-simple-send (get-buffer-process (current-buffer))
1312 (format "dofile(\"%s\")"
1313 (replace-regexp-in-string "\\\\" "\\\\\\\\" tempfile)))
1314 ;; wait for prompt
1315 (while (not prompt-found)
1316 (accept-process-output (get-buffer-process (current-buffer)))
1317 (goto-char (point-max))
1318 (setq prompt-found (and (lua-prompt-line) (< last-prompt (point-max)))))
1319 ;; remove temp. lua file
1320 (delete-file tempfile)
1321 (lua-postprocess-output-buffer lua-process-buffer last-prompt lua-stdin-line-offset)
1322 (if lua-always-show
1323 (display-buffer lua-process-buffer)))))
1325 (defun lua-postprocess-output-buffer (buf start &optional lua-stdin-line-offset)
1326 "Highlight tracebacks found in buf. If an traceback occurred return
1327 t, otherwise return nil. BUF must exist."
1328 (let ((lua-stdin-line-offset (or lua-stdin-line-offset 0))
1329 line file bol err-p)
1330 (with-current-buffer buf
1331 (goto-char start)
1332 (beginning-of-line)
1333 (if (re-search-forward lua-traceback-line-re nil t)
1334 (setq file (match-string 1)
1335 line (string-to-number (match-string 2)))))
1336 (when (and lua-jump-on-traceback line)
1337 (beep)
1338 ;; FIXME: highlight
1339 (lua-jump-to-traceback file line lua-stdin-line-offset)
1340 (setq err-p t))
1341 err-p))
1343 (defun lua-jump-to-traceback (file line lua-stdin-line-offset)
1344 "Jump to the Lua code in FILE at LINE."
1345 ;; sanity check: temporary-file-directory
1346 (if (string= (substring file 0 3) "...")
1347 (message "Lua traceback output truncated: customize 'temporary-file-directory' or increase 'LUA_IDSIZE' in 'luaconf.h'.")
1348 (let ((buffer (cond ((or (string-equal file tempfile) (string-equal file "stdin"))
1349 (setq line (+ line lua-stdin-line-offset))
1350 lua-stdin-buffer)
1351 (t (find-file-noselect file)))))
1352 (pop-to-buffer buffer)
1353 ;; Force Lua mode
1354 (if (not (eq major-mode 'lua-mode))
1355 (lua-mode))
1356 ;; FIXME: fix offset when executing region
1357 (goto-char (point-min)) (forward-line (1- line))
1358 (message "Jumping to error in file %s on line %d" file line))))
1360 (defun lua-prompt-line ()
1361 (save-excursion
1362 (save-match-data
1363 (forward-line 0)
1364 (if (looking-at comint-prompt-regexp)
1365 (match-end 0)))))
1367 (defun lua-send-lua-region ()
1368 "Send preset lua region to lua subprocess."
1369 (interactive)
1370 (or (and lua-region-start lua-region-end)
1371 (error "lua-region not set"))
1372 (or (and lua-process
1373 (comint-check-proc lua-process-buffer))
1374 (lua-start-process lua-default-application))
1375 (comint-simple-send lua-process
1376 (buffer-substring lua-region-start lua-region-end)
1377 )
1378 (if lua-always-show
1379 (display-buffer lua-process-buffer)))
1381 (defun lua-send-proc ()
1382 "Send proc around point to lua subprocess."
1383 (interactive)
1384 (let (beg end)
1385 (save-excursion
1386 (lua-beginning-of-proc)
1387 (setq beg (point))
1388 (lua-end-of-proc)
1389 (setq end (point)))
1390 (or (and lua-process
1391 (comint-check-proc lua-process-buffer))
1392 (lua-start-process lua-default-application))
1393 (comint-simple-send lua-process
1394 (buffer-substring beg end))
1395 (if lua-always-show
1396 (display-buffer lua-process-buffer))))
1398 ;; FIXME: This needs work... -Bret
1399 (defun lua-send-buffer ()
1400 "Send whole buffer to lua subprocess."
1401 (interactive)
1402 (lua-send-region (point-min) (point-max)))
1404 (defun lua-restart-with-whole-file ()
1405 "Restart lua subprocess and send whole file as input."
1406 (interactive)
1407 (lua-kill-process)
1408 (lua-start-process lua-default-application)
1409 (lua-send-buffer))
1411 (defun lua-show-process-buffer ()
1412 "Make sure `lua-process-buffer' is being displayed."
1413 (interactive)
1414 (display-buffer lua-process-buffer))
1416 (defun lua-hide-process-buffer ()
1417 "Delete all windows that display `lua-process-buffer'."
1418 (interactive)
1419 (delete-windows-on lua-process-buffer))
1421 (defun lua-search-documentation ()
1422 "Search Lua documentation for the word at the point."
1423 (interactive)
1424 (browse-url (concat lua-search-url-prefix (current-word t))))
1426 (defun lua-toggle-electric-state (&optional arg)
1427 "Toggle the electric indentation feature.
1428 Optional numeric ARG, if supplied, turns on electric indentation when
1429 positive, turns it off when negative, and just toggles it when zero or
1430 left out."
1431 (interactive "P")
1432 (let ((num_arg (prefix-numeric-value arg)))
1433 (setq lua-electric-flag (cond ((or (null arg)
1434 (zerop num_arg)) (not lua-electric-flag))
1435 ((< num_arg 0) nil)
1436 ((> num_arg 0) t))))
1437 (message "%S" lua-electric-flag))
1439 (defun lua-forward-sexp (&optional count)
1440 "Forward to block end"
1441 (interactive "p")
1442 (save-match-data
1443 (let* ((count (or count 1))
1444 (block-start (mapcar 'car lua-sexp-alist))
1445 (block-end (mapcar 'cdr lua-sexp-alist))
1446 (block-regex (regexp-opt (append block-start block-end) 'words))
1447 current-exp
1448 )
1449 (while (> count 0)
1450 ;; skip whitespace
1451 (skip-chars-forward " \t\n")
1452 (if (looking-at (regexp-opt block-start 'words))
1453 (let ((keyword (match-string 1)))
1454 (lua-find-matching-token-word keyword nil))
1455 ;; If the current keyword is not a "begin" keyword, then just
1456 ;; perform the normal forward-sexp.
1457 (forward-sexp 1))
1458 (setq count (1- count))))))
1461 ;; menu bar
1463 (define-key lua-mode-menu [restart-with-whole-file]
1464 '("Restart With Whole File" . lua-restart-with-whole-file))
1465 (define-key lua-mode-menu [kill-process]
1466 '("Kill Process" . lua-kill-process))
1468 (define-key lua-mode-menu [hide-process-buffer]
1469 '("Hide Process Buffer" . lua-hide-process-buffer))
1470 (define-key lua-mode-menu [show-process-buffer]
1471 '("Show Process Buffer" . lua-show-process-buffer))
1473 (define-key lua-mode-menu [end-of-proc]
1474 '("End Of Proc" . lua-end-of-proc))
1475 (define-key lua-mode-menu [beginning-of-proc]
1476 '("Beginning Of Proc" . lua-beginning-of-proc))
1478 (define-key lua-mode-menu [send-lua-region]
1479 '("Send Lua-Region" . lua-send-lua-region))
1480 (define-key lua-mode-menu [set-lua-region-end]
1481 '("Set Lua-Region End" . lua-set-lua-region-end))
1482 (define-key lua-mode-menu [set-lua-region-start]
1483 '("Set Lua-Region Start" . lua-set-lua-region-start))
1485 (define-key lua-mode-menu [send-current-line]
1486 '("Send Current Line" . lua-send-current-line))
1487 (define-key lua-mode-menu [send-region]
1488 '("Send Region" . lua-send-region))
1489 (define-key lua-mode-menu [send-proc]
1490 '("Send Proc" . lua-send-proc))
1491 (define-key lua-mode-menu [send-buffer]
1492 '("Send Buffer" . lua-send-buffer))
1493 (define-key lua-mode-menu [search-documentation]
1494 '("Search Documentation" . lua-search-documentation))
1496 (defsubst lua-put-char-property (pos property value &optional object)
1497 (lua--with-silent-modifications
1499 (if value
1500 (put-text-property pos (1+ pos) property value object)
1501 (remove-text-properties pos (1+ pos) (list property nil))))
1503 ;; `lua--with-silent-modifications' inhibits modification hooks, one of which
1504 ;; is the hook that keeps `syntax-ppss' internal cache up-to-date. If this
1505 ;; isn't done, the results of subsequent calls to `syntax-ppss' are
1506 ;; invalid. To avoid such cache discrepancy, the hook must be run manually.
1507 (syntax-ppss-flush-cache pos))
1509 (defsubst lua-put-char-syntax-table (pos value &optional object)
1510 (lua-put-char-property pos 'syntax-table value object))
1512 (defsubst lua-get-multiline-delim-syntax (type)
1513 (cond ((eq type 'string) '(15))
1514 ((eq type 'comment) '(14))
1515 (nil)))
1517 (defun lua-mark-char-multiline-delim (pos type)
1518 "Mark character as a delimiter of Lua multiline construct
1520 If TYPE is string, mark char as string delimiter. If TYPE is comment,
1521 mark char as comment delimiter. Otherwise, remove the mark if any."
1522 (lua-put-char-syntax-table pos (lua-get-multiline-delim-syntax type)))
1524 (defsubst lua-inside-multiline-p (&optional pos)
1525 (let ((status (syntax-ppss pos)))
1526 (or (eq (elt status 3) t) ;; inside generic string
1527 (eq (elt status 7) 'syntax-table)))) ;; inside generic comment
1529 (defun lua-get-multiline-start (&optional pos)
1530 (interactive)
1531 (when (lua-inside-multiline-p pos) ;; return string/comment start
1532 (elt (syntax-ppss pos) 8)))
1534 (defun lua-unmark-multiline-literals (&optional begin end)
1535 "Clears all Lua multiline construct markers in region
1537 If BEGIN is nil, start from `beginning-of-buffer'.
1538 If END is nil, stop at `end-of-buffer'."
1539 (interactive)
1541 (setq begin (or begin (point-min))
1542 end (or end (point-max)))
1544 (lua--with-silent-modifications
1545 (remove-text-properties begin end '(syntax-table ())))
1547 ;; `lua--with-silent-modifications' inhibits modification hooks, one of which
1548 ;; is the hook that keeps `syntax-ppss' internal cache up-to-date. If this
1549 ;; isn't done, the results of subsequent calls to `syntax-ppss' are
1550 ;; invalid. To avoid such cache discrepancy, the hook must be run manually.
1551 (syntax-ppss-flush-cache begin)
1553 (font-lock-fontify-buffer))
1555 (defun lua-mark-multiline-region (begin end)
1556 (let ((type (if (eq ?- (char-after begin)) 'comment 'string)))
1557 (lua-mark-char-multiline-delim begin type)
1558 (when end
1559 (lua-mark-char-multiline-delim (1- end) type))))
1561 (defun lua-mark-all-multiline-literals (&optional begin end)
1562 "Marks all Lua multiline constructs in region
1564 If BEGIN is nil, start from `beginning-of-buffer'.
1565 If END is nil, stop at `end-of-buffer'."
1566 (interactive)
1568 (if (and (lua--called-interactively-p 'any) (use-region-p))
1569 (setq begin (region-beginning)
1570 end (region-end)))
1572 (lua-unmark-multiline-literals begin end)
1573 (save-excursion
1574 (goto-char (or begin (point-min)))
1576 (while (and
1577 ;; must check for point range, because matching previous
1578 ;; multiline end might move point beyond end and this
1579 ;; drives `re-search-forward' crazy
1580 (if end (< (point) end) t)
1581 ;; look for
1582 ;; 1. (optional) two or more dashes followed by
1583 ;; 2. lua multiline delimiter [[
1584 (re-search-forward "\\(?2:--\\)?\\[\\(?1:=*\\)\\[" end 'noerror))
1585 ;; match-start + 1 is considered instead of match-start, because
1586 ;; such approach handles '---[[' situation correctly: Emacs
1587 ;; thinks 2nd dash (i.e. match-start) is not yet a comment, but
1588 ;; the third one is, hence the +1. In all the other situations,
1589 ;; '+1' is safe to use because it bears the same syntactic
1590 ;; properties, i.e. if match-start is inside string-or-comment,
1591 ;; then '+1' is too and vice versa.
1592 ;;
1593 ;; PS. ping me if you find a situation in which this is not true
1594 (unless (lua-comment-or-string-p (1+ (match-beginning 0)))
1595 (let (ml-begin ml-end)
1596 (setq ml-begin (match-beginning 0))
1597 (when (re-search-forward (format "\\]%s\\]" (or (match-string 1) "")) nil 'noerror)
1598 ;; (message "found match %s" (match-string 0))
1599 (setq ml-end (match-end 0)))
1600 (lua-mark-multiline-region ml-begin ml-end))))))
1602 (defvar lua-automark-multiline-timer nil
1603 "Contains idle-timer object used for automatical multiline literal markup which must be cleaned up on exit.")
1604 (make-variable-buffer-local 'lua-automark-multiline-timer)
1606 (defvar lua-automark-multiline-start-pos nil
1607 "Contains position from which automark procedure should start.
1609 Automarking shall start at the point before which no modification has been
1610 made since last automark. Additionally, if such point is inside string or
1611 comment, rewind start position to its beginning.
1613 nil means automark is unnecessary because there were no updates.")
1614 (make-variable-buffer-local 'lua-automark-multiline-start-pos)
1616 (defun lua--automark-update-start-pos (change-begin change-end old-len)
1617 "Updates `lua-automark-multiline-start-pos' upon buffer modification."
1618 (save-excursion
1619 (goto-char change-begin)
1620 (beginning-of-line)
1621 (setq lua-automark-multiline-start-pos
1622 (or (lua-comment-or-string-start) (point)))))
1624 (defun lua--automark-multiline-update-timer ()
1625 (lua--automark-multiline-cleanup) ;; reset previous timer if it existed
1626 (when lua-automark-multiline-interval
1627 (add-hook 'change-major-mode-hook 'lua--automark-multiline-cleanup nil 'local)
1628 (add-hook 'after-change-functions 'lua--automark-update-start-pos nil 'local)
1629 (setq lua-automark-multiline-timer
1630 (run-with-idle-timer lua-automark-multiline-interval 'repeat
1631 'lua--automark-multiline-run))))
1633 (defun lua--automark-multiline-cleanup ()
1634 "Disable automatical multiline construct marking"
1635 (unless (null lua-automark-multiline-timer)
1636 (cancel-timer lua-automark-multiline-timer)
1637 (setq lua-automark-multiline-timer nil)))
1639 (defun lua--automark-multiline-run ()
1640 (when (<= (buffer-size) lua-automark-multiline-maxsize)
1641 (when lua-automark-multiline-start-pos
1642 (lua-mark-all-multiline-literals lua-automark-multiline-start-pos)
1643 (setq lua-automark-multiline-start-pos nil))))
1645 (defun lua--customize-set-automark-multiline-interval (symbol value)
1646 (set symbol value)
1647 (dolist (buf (buffer-list))
1648 (with-current-buffer buf
1649 (when (eq major-mode 'lua-mode)
1650 (lua--automark-multiline-update-timer)))))
1652 (defcustom lua-automark-multiline-interval 1
1653 "If not 0, specifies idle time in seconds after which lua-mode will mark multiline literals."
1654 :group 'lua
1655 :type 'integer
1656 :set 'lua--customize-set-automark-multiline-interval)
1658 (defcustom lua-automark-multiline-maxsize 100000
1659 "Maximum buffer size for which lua-mode will mark multiline literals automatically."
1660 :group 'lua
1661 :type 'integer)
1663 (provide 'lua-mode)
1665 ;;; lua-mode.el ends here