dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

lua-mode.el (92809B)


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