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