Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/lua-mode.el
987 views
1
;;; lua-mode.el --- a major-mode for editing Lua scripts
2
3
;; Copyright (C) 1997, 2001, 2004, 2006, 2007, 2010 Free Software Foundation, Inc.
4
5
;; Author: 2010 Reuben Thomas <[email protected]>
6
;; 2006 Juergen Hoetzel <[email protected]>
7
;; 2004 various (support for Lua 5 and byte compilation)
8
;; 2001 Christian Vogler <[email protected]>
9
;; 1997 Bret Mogilefsky <[email protected]> starting from
10
;; tcl-mode by Gregor Schmid <[email protected]>
11
;; with tons of assistance from
12
;; Paul Du Bois <[email protected]> and
13
;; Aaron Smith <[email protected]>.
14
;; URL: http://lua-mode.luaforge.net/
15
;; Version: 20100404
16
;; This file is NOT part of Emacs.
17
;;
18
;; This program is free software; you can redistribute it and/or
19
;; modify it under the terms of the GNU General Public License
20
;; as published by the Free Software Foundation; either version 2
21
;; of the License, or (at your option) any later version.
22
;;
23
;; This program is distributed in the hope that it will be useful,
24
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
25
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
;; GNU General Public License for more details.
27
;;
28
;; You should have received a copy of the GNU General Public License
29
;; along with this program; if not, write to the Free Software
30
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
31
;; MA 02110-1301, USA.
32
33
;; Keywords: languages, processes, tools
34
35
36
;;; Commentary:
37
38
;; Thanks to Tobias Polzin <polzin<at>gmx.de> for function indenting
39
;; patch: Indent "(" like "{"
40
41
;; Thanks to Fabien <fleutot<at>gmail.com> for imenu patches.
42
43
;; Special Thanks to Simon Marshall <[email protected]> for
44
;; font-lock patches.
45
46
;; Additional font-lock highlighting and indentation tweaks by
47
;; Adam D. Moss <[email protected]> <[email protected]>
48
49
;; INSTALLATION:
50
51
;; To install, just drop this file into a directory on your load-path (and
52
;; byte-compile it). To set up Emacs to automatically edit files ending in
53
;; ".lua" using lua-mode add the following to your init file:
54
;; (setq auto-mode-alist (cons '("\\.lua$" . lua-mode) auto-mode-alist))
55
;; (autoload 'lua-mode "lua-mode" "Lua editing mode." t)
56
57
;; Usage
58
59
;; Lua-mode supports c-mode style formatting and sending of
60
;; lines/regions/files to a Lua interpreter. An interpreter (see
61
;; variable `lua-default-application') will be started if you try to
62
;; send some code and none is running. You can use the process-buffer
63
;; (named after the application you chose) as if it were an
64
;; interactive shell. See the documentation for `comint.el' for
65
;; details.
66
67
;; Lua-mode works with Hide Show minor mode (see ``hs-minor-mode``).
68
69
;; Key-bindings
70
71
;; To see all the keybindings for Lua mode, look at `lua-setup-keymap'
72
;; or start `lua-mode' and type `\C-h m'.
73
;; The keybindings may seem strange, since I prefer to use them with
74
;; lua-prefix-key set to nil, but since those keybindings are already used
75
;; the default for `lua-prefix-key' is `\C-c', which is the conventional
76
;; prefix for major-mode commands.
77
78
;; You can customise the keybindings either by setting `lua-prefix-key'
79
;; or by putting the following in your .emacs
80
;; (setq lua-mode-map (make-sparse-keymap))
81
;; and
82
;; (define-key lua-mode-map <your-key> <function>)
83
;; for all the functions you need.
84
85
86
;;; Code:
87
(require 'comint)
88
89
;; Local variables
90
(defgroup lua nil
91
"Major mode for editing lua code."
92
:prefix "lua-"
93
:group 'languages)
94
95
(defcustom lua-default-application "lua"
96
"Default application to run in lua subprocess."
97
:type 'string
98
:group 'lua)
99
100
(defcustom lua-default-command-switches (list "-i")
101
"Command switches for `lua-default-application'.
102
Should be a list of strings."
103
:type '(repeat string)
104
:group 'lua)
105
106
(defcustom lua-always-show t
107
"*Non-nil means display lua-process-buffer after sending a command."
108
:type 'boolean
109
:group 'lua)
110
111
(defcustom lua-search-url-prefix "http://www.lua.org/manual/5.1/manual.html#pdf-"
112
"*URL at which to search for documentation on a word"
113
:type 'string
114
:group 'lua)
115
116
(defvar lua-process nil
117
"The active Lua subprocess")
118
119
(defvar lua-process-buffer nil
120
"Buffer used for communication with Lua subprocess")
121
122
(defvar lua-mode-map nil
123
"Keymap used with lua-mode.")
124
125
(defvar lua-electric-flag t
126
"If t, electric actions (like automatic reindentation) will happen when an electric
127
key like `{' is pressed")
128
(make-variable-buffer-local 'lua-electric-flag)
129
130
(defcustom lua-prefix-key "\C-c"
131
"Prefix for all lua-mode commands."
132
:type 'string
133
:group 'lua)
134
135
(defcustom lua-prompt-regexp "[^\n]*\\(>[\t ]+\\)+$"
136
"Regexp which matches the Lua program's prompt."
137
:group 'lua
138
:type 'regexp
139
)
140
141
(defcustom lua-traceback-line-re
142
"^\\(?:[\t ]*\\|.*>[\t ]+\\)\\([^\n\t ]+\\):\\([0-9]+\\):"
143
"Regular expression that describes tracebacks and errors."
144
:group 'lua
145
:type 'regexp
146
)
147
148
(defcustom lua-jump-on-traceback t
149
"*Jump to innermost traceback location in *lua* buffer. When this
150
variable is non-nil and a traceback occurs when running Lua code in a
151
subprocess, jump immediately to the source code of the innermost
152
traceback location."
153
:group 'lua
154
:type 'boolean
155
)
156
157
(defvar lua-mode-hook nil
158
"Hooks called when Lua mode fires up.")
159
160
(defvar lua-region-start (make-marker)
161
"Start of special region for Lua communication.")
162
163
(defvar lua-region-end (make-marker)
164
"End of special region for Lua communication.")
165
166
(defvar lua-indent-level 3
167
"Amount by which Lua subexpressions are indented.")
168
169
(defvar lua-mode-menu (make-sparse-keymap "Lua")
170
"Keymap for lua-mode's menu.")
171
172
(defvar lua-emacs-menu
173
'(["Restart With Whole File" lua-restart-with-whole-file t]
174
["Kill Process" lua-kill-process t]
175
["Hide Process Buffer" lua-hide-process-buffer t]
176
["Show Process Buffer" lua-show-process-buffer t]
177
["Beginning Of Proc" lua-beginning-of-proc t]
178
["End Of Proc" lua-end-of-proc t]
179
["Set Lua-Region Start" lua-set-lua-region-start t]
180
["Set Lua-Region End" lua-set-lua-region-end t]
181
["Send Lua-Region" lua-send-lua-region t]
182
["Send Current Line" lua-send-current-line t]
183
["Send Region" lua-send-region t]
184
["Send Proc" lua-send-proc t]
185
["Send Buffer" lua-send-buffer t]
186
["Search Documentation" lua-search-documentation t])
187
"Emacs menu for Lua mode.")
188
189
(defvar lua-font-lock-keywords
190
(eval-when-compile
191
(list
192
;; Handle variable names
193
;; local blalba =
194
;; ^^^^^^
195
'("\\(local[ \t]+\\(\\sw+\\)[ \t]*=\\)"
196
(2 font-lock-variable-name-face))
197
198
;; Function name declarations.
199
'("^[ \t]*\\<\\(\\(local[ \t]+\\)?function\\)\\>[ \t]+\\(\\(\\sw:\\|\\sw\\.\\|\\sw_\\|\\sw\\)+\\)"
200
(1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
201
202
;; Handle function names in assignments
203
'("\\(\\(\\sw:\\|\\sw\\.\\|\\sw_\\|\\sw\\)+\\)[ \t]*=[ \t]*\\(function\\)\\>"
204
(1 font-lock-function-name-face nil t) (3 font-lock-keyword-face))
205
206
;; Long comment blocks.
207
'("\\(?:^\\|[^-]\\)\\(--\\[\\(=*\\)\\[\\(?:.\\|\n\\)*?\\]\\2\\]\\)"
208
(1 font-lock-comment-face t))
209
210
;; Long strings.
211
'("\\(?:^\\|[^[-]\\)\\(\\[\\(=*\\)\\[\\(?:.\\|\n\\)*?\\]\\2\\]\\)"
212
(1 font-lock-string-face t))
213
214
;; Keywords.
215
(concat "\\<"
216
(regexp-opt '("and" "break" "do" "else" "elseif" "end" "false"
217
"for" "function" "if" "in" "local" "nil" "not"
218
"or" "repeat" "return" "then" "true" "until"
219
"while") t)
220
"\\>")
221
222
"Default expressions to highlight in Lua mode.")))
223
224
(defvar lua-imenu-generic-expression
225
'((nil "^[ \t]*\\(?:local[ \t]+\\)?function[ \t]+\\(\\(\\sw:\\|\\sw_\\|\\sw\\.\\|\\sw\\)+\\)" 1))
226
"Imenu generic expression for lua-mode. See `imenu-generic-expression'.")
227
228
(defvar lua-mode-abbrev-table nil
229
"Abbreviation table used in lua-mode buffers.")
230
231
(defvar lua-sexp-alist '(("then" . "end")
232
("function" . "end")
233
("do" . "end")))
234
235
(define-abbrev-table 'lua-mode-abbrev-table
236
'(
237
("end" "end" lua-indent-line 0)
238
("else" "else" lua-indent-line 0)
239
("elseif" "elseif" lua-indent-line 0)
240
))
241
242
(defconst lua-indent-whitespace " \t"
243
"Character set that constitutes whitespace for indentation in lua.")
244
245
(eval-and-compile
246
(defalias 'lua-make-temp-file
247
(if (fboundp 'make-temp-file)
248
'make-temp-file
249
(lambda (prefix &optional dir-flag) ;; Simple implementation
250
(expand-file-name
251
(make-temp-name prefix)
252
(if (fboundp 'temp-directory)
253
(temp-directory)
254
temporary-file-directory))))))
255
256
;;;###autoload
257
(defun lua-mode ()
258
"Major mode for editing Lua code.
259
The following keys are bound:
260
\\{lua-mode-map}
261
"
262
(interactive)
263
(let ((switches nil)
264
s)
265
(kill-all-local-variables)
266
(setq major-mode 'lua-mode)
267
(setq mode-name "Lua")
268
(setq comint-prompt-regexp lua-prompt-regexp)
269
(make-local-variable 'lua-default-command-switches)
270
(set (make-local-variable 'indent-line-function) 'lua-indent-line)
271
(set (make-local-variable 'comment-start) "--")
272
(set (make-local-variable 'comment-start-skip) "--")
273
(set (make-local-variable 'font-lock-defaults)
274
'(lua-font-lock-keywords nil nil ((?_ . "w"))))
275
(set (make-local-variable 'imenu-generic-expression)
276
lua-imenu-generic-expression)
277
(setq local-abbrev-table lua-mode-abbrev-table)
278
(abbrev-mode 1)
279
(make-local-variable 'lua-default-eval)
280
(or lua-mode-map
281
(lua-setup-keymap))
282
(use-local-map lua-mode-map)
283
(set-syntax-table (copy-syntax-table))
284
(modify-syntax-entry ?+ ".")
285
(modify-syntax-entry ?- ". 12")
286
(modify-syntax-entry ?* ".")
287
(modify-syntax-entry ?/ ".")
288
(modify-syntax-entry ?^ ".")
289
;; This might be better as punctuation, as for C, but this way you
290
;; can treat table index as symbol.
291
(modify-syntax-entry ?. "_") ; e.g. `io.string'
292
(modify-syntax-entry ?> ".")
293
(modify-syntax-entry ?< ".")
294
(modify-syntax-entry ?= ".")
295
(modify-syntax-entry ?~ ".")
296
(modify-syntax-entry ?\n ">")
297
(modify-syntax-entry ?\' "\"")
298
(modify-syntax-entry ?\" "\"")
299
;; _ needs to be part of a word, or the regular expressions will
300
;; incorrectly regognize end_ to be matched by "\\<end\\>"!
301
(modify-syntax-entry ?_ "w")
302
(if (and (featurep 'menubar)
303
current-menubar
304
(not (assoc "Lua" current-menubar)))
305
(progn
306
(set-buffer-menubar (copy-sequence current-menubar))
307
(add-menu nil "Lua" lua-emacs-menu)))
308
;; Append Lua menu to popup menu for Emacs.
309
(if (boundp 'mode-popup-menu)
310
(setq mode-popup-menu
311
(cons (concat mode-name " Mode Commands") lua-emacs-menu)))
312
313
;; hideshow setup
314
(unless (assq 'lua-mode hs-special-modes-alist)
315
(add-to-list 'hs-special-modes-alist
316
`(lua-mode
317
,(regexp-opt (mapcar 'car lua-sexp-alist) 'words) ;start
318
,(regexp-opt (mapcar 'cdr lua-sexp-alist) 'words) ;end
319
nil lua-forward-sexp)))
320
(run-hooks 'lua-mode-hook)))
321
322
;;;###autoload
323
(add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode))
324
325
(defun lua-setup-keymap ()
326
"Set up keymap for Lua mode.
327
If the variable `lua-prefix-key' is nil, the bindings go directly
328
to `lua-mode-map', otherwise they are prefixed with `lua-prefix-key'."
329
(setq lua-mode-map (make-sparse-keymap))
330
(define-key lua-mode-map [menu-bar lua-mode]
331
(cons "Lua" lua-mode-menu))
332
(define-key lua-mode-map "}" 'lua-electric-match)
333
(define-key lua-mode-map "]" 'lua-electric-match)
334
(define-key lua-mode-map ")" 'lua-electric-match)
335
(define-key lua-mode-map (kbd "C-M-a") 'lua-beginning-of-proc)
336
(define-key lua-mode-map (kbd "C-M-e") 'lua-end-of-proc)
337
(define-key lua-mode-map (kbd "C-M-<home>") 'lua-beginning-of-proc)
338
(define-key lua-mode-map (kbd "C-M-<end>") 'lua-end-of-proc)
339
(let ((map (if lua-prefix-key
340
(make-sparse-keymap)
341
lua-mode-map)))
342
343
;; communication
344
(define-key map "\C-l" 'lua-send-buffer)
345
(define-key map "\C-f" 'lua-search-documentation)
346
(if lua-prefix-key
347
(define-key lua-mode-map lua-prefix-key map))
348
))
349
350
(defun lua-electric-match (arg)
351
"Insert character and adjust indentation."
352
(interactive "P")
353
(insert-char last-command-char (prefix-numeric-value arg))
354
(if lua-electric-flag
355
(lua-indent-line))
356
(blink-matching-open))
357
358
;; private functions
359
(defun lua-syntax-status ()
360
"Returns the syntactic status of the character after the point."
361
(parse-partial-sexp (save-excursion (beginning-of-line) (point))
362
(point)))
363
364
365
(defun lua-string-p ()
366
"Returns true if the point is in a string."
367
(elt (lua-syntax-status) 3))
368
369
(defun lua-comment-p ()
370
"Returns true if the point is in a comment."
371
(elt (lua-syntax-status) 4))
372
373
(defun lua-comment-or-string-p ()
374
"Returns true if the point is in a comment or string."
375
(let ((parse-result (lua-syntax-status)))
376
(or (elt parse-result 3) (elt parse-result 4))))
377
378
(defun lua-indent-line ()
379
"Indent current line for Lua mode.
380
Return the amount the indentation changed by."
381
(let ((indent (max 0 (- (lua-calculate-indentation nil)
382
(lua-calculate-indentation-left-shift))))
383
beg shift-amt
384
(case-fold-search nil)
385
(pos (- (point-max) (point))))
386
(beginning-of-line)
387
(setq beg (point))
388
(skip-chars-forward lua-indent-whitespace)
389
(setq shift-amt (- indent (current-column)))
390
(when (not (zerop shift-amt))
391
(delete-region beg (point))
392
(indent-to indent))
393
;; If initial point was within line's indentation,
394
;; position after the indentation. Else stay at same point in text.
395
(if (> (- (point-max) pos) (point))
396
(goto-char (- (point-max) pos)))
397
shift-amt
398
indent))
399
400
(defun lua-find-regexp (direction regexp &optional limit ignore-p)
401
"Searches for a regular expression in the direction specified.
402
Direction is one of 'forward and 'backward.
403
By default, matches in comments and strings are ignored, but what to ignore is
404
configurable by specifying ignore-p. If the regexp is found, returns point
405
position, nil otherwise.
406
ignore-p returns true if the match at the current point position should be
407
ignored, nil otherwise."
408
(let ((ignore-func (or ignore-p 'lua-comment-or-string-p))
409
(search-func (if (eq direction 'forward)
410
're-search-forward 're-search-backward))
411
(case-fold-search nil))
412
(catch 'found
413
(while (funcall search-func regexp limit t)
414
(if (not (funcall ignore-func))
415
(throw 'found (point)))))))
416
417
(defun lua-backwards-to-block-begin-or-end ()
418
"Move backwards to nearest block begin or end. Returns nil if not successful."
419
(interactive)
420
(lua-find-regexp 'backward lua-block-regexp))
421
422
(defconst lua-block-regexp
423
(eval-when-compile
424
;; This is the code we used to generate the regexp:
425
(concat
426
"\\(\\<"
427
(regexp-opt '("do" "function" "repeat" "then"
428
"else" "elseif" "end" "until") t)
429
"\\>\\)\\|"
430
(regexp-opt '("{" "(" "[" "]" ")" "}") t))
431
432
))
433
434
(defconst lua-block-token-alist
435
;; The absence of "else" is deliberate. This construct in a way both
436
;; opens and closes a block. As a result, it is difficult to handle
437
;; cleanly. It is also ambiguous - if we are looking for the match
438
;; of "else", should we look backward for "then/elseif" or forward
439
;; for "end"?
440
;; Maybe later we will find a way to handle it.
441
'(("do" "\\<end\\>" open)
442
("function" "\\<end\\>" open)
443
("repeat" "\\<until\\>" open)
444
("then" "\\<\\(e\\(lseif\\|nd\\)\\)\\>" open)
445
("{" "}" open)
446
("[" "]" open)
447
("(" ")" open)
448
("elseif" "\\<then\\>" close)
449
("end" "\\<\\(do\\|function\\|then\\)\\>" close)
450
("until" "\\<repeat\\>" close)
451
("}" "{" close)
452
("]" "\\[" close)
453
(")" "(" close)))
454
455
456
(defconst lua-indentation-modifier-regexp
457
;; The absence of else is deliberate, since it does not modify the
458
;; indentation level per se. It only may cause the line, in which the
459
;; else is, to be shifted to the left.
460
;; This is the code we used to generate the regexp:
461
(concat
462
"\\(\\<"
463
; n.b. "local function" is a bit of a hack, allowing only a single space
464
(regexp-opt '("do" "local function" "function" "repeat" "then") t)
465
"\\>\\|"
466
(regexp-opt '("{" "(" "["))
467
"\\)\\|\\(\\<"
468
(regexp-opt '("elseif" "end" "until") t)
469
"\\>\\|"
470
(regexp-opt '("]" ")" "}"))
471
"\\)")
472
473
)
474
475
(defun lua-find-matching-token-word (token search-start)
476
(let* ((token-info (assoc token lua-block-token-alist))
477
(match (car (cdr token-info)))
478
(match-type (car (cdr (cdr token-info))))
479
(search-direction (if (eq match-type 'open) 'forward 'backward)))
480
;; if we are searching forward from the token at the current point
481
;; (i.e. for a closing token), need to step one character forward
482
;; first, or the regexp will match the opening token.
483
(if (eq match-type 'open) (forward-char 1))
484
(if search-start (goto-char search-start))
485
(catch 'found
486
(while (lua-find-regexp search-direction lua-indentation-modifier-regexp)
487
;; have we found a valid matching token?
488
(let ((found-token (match-string 0))
489
(found-pos (match-beginning 0)))
490
(if (string-match match found-token)
491
(throw 'found found-pos))
492
;; no - then there is a nested block. If we were looking for
493
;; a block begin token, found-token must be a block end
494
;; token; likewise, if we were looking for a block end token,
495
;; found-token must be a block begin token, otherwise there
496
;; is a grammatical error in the code.
497
(if (not (and
498
(eq (car (cdr (cdr (assoc found-token lua-block-token-alist))))
499
match-type)
500
(lua-find-matching-token-word found-token nil)))
501
(throw 'found nil)))))))
502
503
(defun lua-goto-matching-block-token (&optional search-start parse-start)
504
"Find block begion/end token matching the one at the point.
505
This function moves the point to the token that matches the one
506
at the current point. Returns the point position of the first character of
507
the matching token if successful, nil otherwise."
508
(if parse-start (goto-char parse-start))
509
(let ((case-fold-search nil))
510
(if (looking-at lua-indentation-modifier-regexp)
511
(let ((position (lua-find-matching-token-word (match-string 0)
512
search-start)))
513
(and position
514
(goto-char position))))))
515
516
(defun lua-goto-matching-block (&optional noreport)
517
"Go to the keyword balancing the one under the point.
518
If the point is on a keyword/brace that starts a block, go to the
519
matching keyword that ends the block, and vice versa."
520
(interactive)
521
;; search backward to the beginning of the keyword if necessary
522
(if (eq (char-syntax (following-char)) ?w)
523
(re-search-backward "\\<" nil t))
524
(let ((position (lua-goto-matching-block-token)))
525
(if (and (not position)
526
(not noreport))
527
(error "Not on a block control keyword or brace.")
528
position)))
529
530
(defun lua-goto-nonblank-previous-line ()
531
"Puts the point at the first previous line that is not blank.
532
Returns the point, or nil if it reached the beginning of the buffer"
533
(catch 'found
534
(beginning-of-line)
535
(while t
536
(if (bobp) (throw 'found nil))
537
(forward-char -1)
538
(beginning-of-line)
539
(if (not (looking-at "\\s *\\(--.*\\)?$")) (throw 'found (point))))))
540
541
(defun lua-goto-nonblank-next-line ()
542
"Puts the point at the first next line that is not blank.
543
Returns the point, or nil if it reached the end of the buffer"
544
(catch 'found
545
(end-of-line)
546
(while t
547
(forward-line)
548
(if (eobp) (throw 'found nil))
549
(beginning-of-line)
550
(if (not (looking-at "\\s *\\(--.*\\)?$")) (throw 'found (point))))))
551
552
(eval-when-compile
553
(defconst lua-operator-class
554
"-+*/^.=<>~"))
555
556
(defconst lua-cont-eol-regexp
557
(eval-when-compile
558
;; expression used to generate the regexp
559
(concat
560
"\\(\\<"
561
(regexp-opt '("and" "or" "not" "in" "for" "while"
562
"local" "function") t)
563
"\\>\\|"
564
"\\(^\\|[^" lua-operator-class "]\\)"
565
(regexp-opt '("+" "-" "*" "/" "^" ".." "==" "=" "<" ">" "<=" ">=" "~=") t)
566
"\\)"
567
"\\s *\\=")
568
569
))
570
571
572
(defconst lua-cont-bol-regexp
573
(eval-when-compile
574
;; expression used to generate the regexp
575
(concat
576
"\\=\\s *"
577
"\\(\\<"
578
(regexp-opt '("and" "or" "not") t)
579
"\\>\\|"
580
(regexp-opt '("+" "-" "*" "/" "^" ".." "==" "=" "<" ">" "<=" ">=" "~=") t)
581
"\\($\\|[^" lua-operator-class "]\\)"
582
"\\)")
583
584
))
585
586
(defun lua-last-token-continues-p ()
587
"Returns true if the last token on this line is a continuation token."
588
(let (line-begin
589
line-end)
590
(save-excursion
591
(beginning-of-line)
592
(setq line-begin (point))
593
(end-of-line)
594
(setq line-end (point))
595
;; we need to check whether the line ends in a comment and
596
;; skip that one.
597
(while (lua-find-regexp 'backward "-" line-begin 'lua-string-p)
598
(if (looking-at "--")
599
(setq line-end (point))))
600
(goto-char line-end)
601
(re-search-backward lua-cont-eol-regexp line-begin t))))
602
603
(defun lua-first-token-continues-p ()
604
"Returns true if the first token on this line is a continuation token."
605
(let (line-end)
606
(save-excursion
607
(end-of-line)
608
(setq line-end (point))
609
(beginning-of-line)
610
(re-search-forward lua-cont-bol-regexp line-end t))))
611
612
(defun lua-is-continuing-statement-p (&optional parse-start)
613
"Return non-nil if the line continues a statement.
614
More specifically, return the point in the line that is continued.
615
The criteria for a continuing statement are:
616
617
* the last token of the previous line is a continuing op,
618
OR the first token of the current line is a continuing op
619
620
"
621
(let ((prev-line nil))
622
(save-excursion
623
(if parse-start (goto-char parse-start))
624
(save-excursion (setq prev-line (lua-goto-nonblank-previous-line)))
625
(and prev-line
626
(or (lua-first-token-continues-p)
627
(and (goto-char prev-line)
628
;; check last token of previous nonblank line
629
(lua-last-token-continues-p)))))))
630
631
(defun lua-make-indentation-info-pair ()
632
"This is a helper function to lua-calculate-indentation-info. Don't
633
use standalone."
634
(cond ((string-equal found-token "function")
635
;; this is the location where we need to start searching for the
636
;; matching opening token, when we encounter the next closing token.
637
;; It is primarily an optimization to save some searching time.
638
(cons 'absolute (+ (save-excursion (goto-char found-pos)
639
(current-column))
640
lua-indent-level)))
641
((or (string-equal found-token "{")
642
(string-equal found-token "("))
643
(save-excursion
644
;; expression follows -> indent at start of next expression
645
(if (and (not (search-forward-regexp "[[:space:]]--" (line-end-position) t))
646
(search-forward-regexp "[^[:space:]]" (line-end-position) t))
647
(cons 'absolute (1- (current-column)))
648
(cons 'relative lua-indent-level))))
649
;; closing tokens follow
650
((string-equal found-token "end")
651
(save-excursion
652
(lua-goto-matching-block-token nil found-pos)
653
(if (looking-at "\\<function\\>")
654
(cons 'absolute
655
(+ (current-indentation)
656
(lua-calculate-indentation-block-modifier
657
nil (point))))
658
(cons 'relative (- lua-indent-level)))))
659
((or (string-equal found-token ")")
660
(string-equal found-token "}"))
661
(save-excursion
662
(lua-goto-matching-block-token nil found-pos)
663
(cons 'absolute
664
(+ (current-indentation)
665
(lua-calculate-indentation-block-modifier
666
nil (point))))))
667
(t
668
(cons 'relative (if (nth 2 (match-data))
669
;; beginning of a block matched
670
lua-indent-level
671
;; end of a block matched
672
(- lua-indent-level))))))
673
674
675
(defun lua-calculate-indentation-info (&optional parse-start parse-end)
676
"For each block token on the line, computes how it affects the indentation.
677
The effect of each token can be either a shift relative to the current
678
indentation level, or indentation to some absolute column. This information
679
is collected in a list of indentation info pairs, which denote absolute
680
and relative each, and the shift/column to indent to."
681
(let* ((line-end (save-excursion (end-of-line) (point)))
682
(search-stop (if parse-end (min parse-end line-end) line-end))
683
(indentation-info nil))
684
(if parse-start (goto-char parse-start))
685
(save-excursion
686
(beginning-of-line)
687
(while (lua-find-regexp 'forward lua-indentation-modifier-regexp
688
search-stop)
689
(let ((found-token (match-string 0))
690
(found-pos (match-beginning 0))
691
(found-end (match-end 0))
692
(data (match-data)))
693
(setq indentation-info
694
(cons (lua-make-indentation-info-pair) indentation-info)))))
695
indentation-info))
696
697
(defun lua-accumulate-indentation-info (info)
698
"Accumulates the indentation information previously calculated by
699
lua-calculate-indentation-info. Returns either the relative indentation
700
shift, or the absolute column to indent to."
701
(let ((info-list (reverse info))
702
(type 'relative)
703
(accu 0))
704
(mapcar (lambda (x)
705
(setq accu (if (eq 'absolute (car x))
706
(progn (setq type 'absolute)
707
(cdr x))
708
(+ accu (cdr x)))))
709
info-list)
710
(cons type accu)))
711
712
(defun lua-calculate-indentation-block-modifier (&optional parse-start
713
parse-end)
714
"Return amount by which this line modifies the indentation.
715
Beginnings of blocks add lua-indent-level once each, and endings
716
of blocks subtract lua-indent-level once each. This function is used
717
to determine how the indentation of the following line relates to this
718
one."
719
(if parse-start (goto-char parse-start))
720
(let ((case-fold-search nil)
721
(indentation-info (lua-accumulate-indentation-info
722
(lua-calculate-indentation-info nil parse-end))))
723
(if (eq (car indentation-info) 'absolute)
724
(- (cdr indentation-info)
725
(current-indentation)
726
;; reduce indentation if this line also starts new continued statement
727
;; or next line cont. this line
728
;;This is for aesthetic reasons: the indentation should be
729
;;dosomething(d +
730
;; e + f + g)
731
;;not
732
;;dosomething(d +
733
;; e + f + g)"
734
(save-excursion
735
(or (and (lua-last-token-continues-p) lua-indent-level)
736
(and (lua-goto-nonblank-next-line) (lua-first-token-continues-p) lua-indent-level)
737
0)))
738
(+ (lua-calculate-indentation-left-shift)
739
(cdr indentation-info)
740
(if (lua-is-continuing-statement-p) (- lua-indent-level) 0)))))
741
742
(defconst lua-left-shift-regexp-1
743
(concat "\\("
744
"\\(\\<" (regexp-opt '("else" "elseif" "until") t)
745
"\\>\\)\\($\\|\\s +\\)"
746
"\\)"))
747
748
(defconst lua-left-shift-regexp-2
749
(concat "\\(\\<"
750
(regexp-opt '("end") t)
751
"\\>\\)"))
752
753
(defconst lua-left-shift-regexp
754
;; This is the code we used to generate the regexp:
755
;; ("else", "elseif", "until" followed by whitespace, or "end"/closing
756
;; brackets followed by
757
;; whitespace, punctuation, or closing parentheses)
758
(concat lua-left-shift-regexp-1
759
"\\|\\(\\("
760
lua-left-shift-regexp-2
761
"\\|\\("
762
(regexp-opt '("]" "}" ")"))
763
"\\)\\)\\($\\|\\(\\s \\|\\s.\\)*\\)"
764
"\\)"))
765
766
(defconst lua-left-shift-pos-1
767
2)
768
769
(defconst lua-left-shift-pos-2
770
(+ 3 (regexp-opt-depth lua-left-shift-regexp-1)))
771
772
(defconst lua-left-shift-pos-3
773
(+ lua-left-shift-pos-2
774
(regexp-opt-depth lua-left-shift-regexp-2)))
775
776
(defun lua-calculate-indentation-left-shift (&optional parse-start)
777
"Return amount, by which this line should be shifted left.
778
Look for an uninterrupted sequence of block-closing tokens that starts
779
at the beginning of the line. For each of these tokens, shift indentation
780
to the left by the amount specified in lua-indent-level."
781
(let (line-begin
782
(indentation-modifier 0)
783
(case-fold-search nil)
784
(block-token nil))
785
(save-excursion
786
(if parse-start (goto-char parse-start))
787
(beginning-of-line)
788
(setq line-begin (point))
789
;; Look for the block-closing token sequence
790
(skip-chars-forward lua-indent-whitespace)
791
(catch 'stop
792
(while (and (looking-at lua-left-shift-regexp)
793
(not (lua-comment-or-string-p)))
794
(let ((last-token (or (match-string lua-left-shift-pos-1)
795
(match-string lua-left-shift-pos-2)
796
(match-string lua-left-shift-pos-3))))
797
(if (not block-token) (setq block-token last-token))
798
(if (not (string-equal block-token last-token)) (throw 'stop nil))
799
(setq indentation-modifier (+ indentation-modifier
800
lua-indent-level))
801
(forward-char (length (match-string 0))))))
802
indentation-modifier)))
803
804
(defun lua-calculate-indentation (&optional parse-start)
805
"Return appropriate indentation for current line as Lua code.
806
In usual case returns an integer: the column to indent to."
807
(let ((pos (point))
808
shift-amt)
809
(save-excursion
810
(if parse-start (setq pos (goto-char parse-start)))
811
(beginning-of-line)
812
(setq shift-amt (if (lua-is-continuing-statement-p) lua-indent-level 0))
813
(if (bobp) ; If we're at the beginning of the buffer, no change.
814
(+ (current-indentation) shift-amt)
815
;; This code here searches backwards for a "block beginning/end"
816
;; It snarfs the indentation of that, plus whatever amount the
817
;; line was shifted left by, because of block end tokens. It
818
;; then adds the indentation modifier of that line to obtain the
819
;; final level of indentation.
820
;; Finally, if this line continues a statement from the
821
;; previous line, add another level of indentation.
822
(if (lua-backwards-to-block-begin-or-end)
823
;; now we're at the line with block beginning or end.
824
(max (+ (current-indentation)
825
(lua-calculate-indentation-block-modifier)
826
shift-amt)
827
0)
828
;; Failed to find a block begin/end.
829
;; Just use the previous line's indent.
830
(goto-char pos)
831
(beginning-of-line)
832
(forward-line -1)
833
(+ (current-indentation) shift-amt))))))
834
835
(defun lua-beginning-of-proc (&optional arg)
836
"Move backward to the beginning of a lua proc (or similar).
837
With argument, do it that many times. Negative arg -N
838
means move forward to Nth following beginning of proc.
839
Returns t unless search stops due to beginning or end of buffer."
840
(interactive "P")
841
(or arg
842
(setq arg 1))
843
(let ((found nil)
844
(ret t))
845
(if (and (< arg 0)
846
(looking-at "^function[ \t]"))
847
(forward-char 1))
848
(while (< arg 0)
849
(if (re-search-forward "^function[ \t]" nil t)
850
(setq arg (1+ arg)
851
found t)
852
(setq ret nil
853
arg 0)))
854
(if found
855
(beginning-of-line))
856
(while (> arg 0)
857
(if (re-search-backward "^function[ \t]" nil t)
858
(setq arg (1- arg))
859
(setq ret nil
860
arg 0)))
861
ret))
862
863
(defun lua-end-of-proc (&optional arg)
864
"Move forward to next end of lua proc (or similar).
865
With argument, do it that many times. Negative argument -N means move
866
back to Nth preceding end of proc.
867
868
This function just searches for a `end' at the beginning of a line."
869
(interactive "P")
870
(or arg
871
(setq arg 1))
872
(let ((found nil)
873
(ret t))
874
(if (and (< arg 0)
875
(not (bolp))
876
(save-excursion
877
(beginning-of-line)
878
(eq (following-char) ?})))
879
(forward-char -1))
880
(while (> arg 0)
881
(if (re-search-forward "^end" nil t)
882
(setq arg (1- arg)
883
found t)
884
(setq ret nil
885
arg 0)))
886
(while (< arg 0)
887
(if (re-search-backward "^end" nil t)
888
(setq arg (1+ arg)
889
found t)
890
(setq ret nil
891
arg 0)))
892
(if found
893
(end-of-line))
894
ret))
895
896
(defun lua-start-process (name &optional program startfile &rest switches)
897
"Start a lua process named NAME, running PROGRAM."
898
(or switches
899
(setq switches lua-default-command-switches))
900
(setq program (or program name))
901
(setq lua-process-buffer (apply 'make-comint name program startfile switches))
902
(setq lua-process (get-buffer-process lua-process-buffer))
903
;; wait for prompt
904
(with-current-buffer lua-process-buffer
905
(while (not (lua-prompt-line))
906
(accept-process-output (get-buffer-process (current-buffer)))
907
(goto-char (point-max)))))
908
909
(defun lua-kill-process ()
910
"Kill lua subprocess and its buffer."
911
(interactive)
912
(if lua-process-buffer
913
(kill-buffer lua-process-buffer)))
914
915
(defun lua-set-lua-region-start (&optional arg)
916
"Set start of region for use with `lua-send-lua-region'."
917
(interactive)
918
(set-marker lua-region-start (or arg (point))))
919
920
(defun lua-set-lua-region-end (&optional arg)
921
"Set end of region for use with `lua-send-lua-region'."
922
(interactive)
923
(set-marker lua-region-end (or arg (point))))
924
925
(defun lua-send-current-line ()
926
"Send current line to lua subprocess, found in `lua-process'.
927
If `lua-process' is nil or dead, start a new process first."
928
(interactive)
929
(let ((start (save-excursion (beginning-of-line) (point)))
930
(end (save-excursion (end-of-line) (point))))
931
(lua-send-region start end)))
932
933
(defun lua-send-region (start end)
934
"Send region to lua subprocess."
935
(interactive "r")
936
;; make temporary lua file
937
(let ((tempfile (lua-make-temp-file "lua-"))
938
(last-prompt nil)
939
(prompt-found nil)
940
(lua-stdin-line-offset (count-lines (point-min) start))
941
(lua-stdin-buffer (current-buffer))
942
current-prompt )
943
(write-region start end tempfile)
944
(or (and lua-process
945
(comint-check-proc lua-process-buffer))
946
(lua-start-process lua-default-application))
947
;; kill lua process without query
948
(if (fboundp 'process-kill-without-query)
949
(process-kill-without-query lua-process))
950
;; send dofile(tempfile)
951
(with-current-buffer lua-process-buffer
952
(goto-char (point-max))
953
(setq last-prompt (point-max))
954
(comint-simple-send (get-buffer-process (current-buffer))
955
(format "dofile(\"%s\")"
956
(replace-regexp-in-string "\\\\" "\\\\\\\\" tempfile)))
957
;; wait for prompt
958
(while (not prompt-found)
959
(accept-process-output (get-buffer-process (current-buffer)))
960
(goto-char (point-max))
961
(setq prompt-found (and (lua-prompt-line) (< last-prompt (point-max)))))
962
;; remove temp. lua file
963
(delete-file tempfile)
964
(lua-postprocess-output-buffer lua-process-buffer last-prompt lua-stdin-line-offset)
965
(if lua-always-show
966
(display-buffer lua-process-buffer)))))
967
968
(defun lua-postprocess-output-buffer (buf start &optional lua-stdin-line-offset)
969
"Highlight tracebacks found in buf. If an traceback occurred return
970
t, otherwise return nil. BUF must exist."
971
(let ((lua-stdin-line-offset (or lua-stdin-line-offset 0))
972
line file bol err-p)
973
(save-excursion
974
(set-buffer buf)
975
(goto-char start)
976
(beginning-of-line)
977
(if (re-search-forward lua-traceback-line-re nil t)
978
(setq file (match-string 1)
979
line (string-to-number (match-string 2)))))
980
(when (and lua-jump-on-traceback line)
981
(beep)
982
;; FIXME: highlight
983
(lua-jump-to-traceback file line lua-stdin-line-offset)
984
(setq err-p t))
985
err-p))
986
987
(defun lua-jump-to-traceback (file line lua-stdin-line-offset)
988
"Jump to the Lua code in FILE at LINE."
989
;; sanity check: temporary-file-directory
990
(if (string= (substring file 0 3) "...")
991
(message "Lua traceback output truncated: customize 'temporary-file-directory' or increase 'LUA_IDSIZE' in 'luaconf.h'.")
992
(let ((buffer (cond ((or (string-equal file tempfile) (string-equal file "stdin"))
993
(setq line (+ line lua-stdin-line-offset))
994
lua-stdin-buffer)
995
(t (find-file-noselect file)))))
996
(pop-to-buffer buffer)
997
;; Force Lua mode
998
(if (not (eq major-mode 'lua-mode))
999
(lua-mode))
1000
;; FIXME: fix offset when executing region
1001
(goto-line line)
1002
(message "Jumping to error in file %s on line %d" file line))))
1003
1004
(defun lua-prompt-line ()
1005
(save-excursion
1006
(save-match-data
1007
(forward-line 0)
1008
(if (looking-at comint-prompt-regexp)
1009
(match-end 0)))))
1010
1011
(defun lua-send-lua-region ()
1012
"Send preset lua region to lua subprocess."
1013
(interactive)
1014
(or (and lua-region-start lua-region-end)
1015
(error "lua-region not set"))
1016
(or (and lua-process
1017
(comint-check-proc lua-process-buffer))
1018
(lua-start-process lua-default-application))
1019
(comint-simple-send lua-process
1020
(buffer-substring lua-region-start lua-region-end)
1021
)
1022
(if lua-always-show
1023
(display-buffer lua-process-buffer)))
1024
1025
(defun lua-send-proc ()
1026
"Send proc around point to lua subprocess."
1027
(interactive)
1028
(let (beg end)
1029
(save-excursion
1030
(lua-beginning-of-proc)
1031
(setq beg (point))
1032
(lua-end-of-proc)
1033
(setq end (point)))
1034
(or (and lua-process
1035
(comint-check-proc lua-process-buffer))
1036
(lua-start-process lua-default-application))
1037
(comint-simple-send lua-process
1038
(buffer-substring beg end))
1039
(if lua-always-show
1040
(display-buffer lua-process-buffer))))
1041
1042
; FIXME: This needs work... -Bret
1043
(defun lua-send-buffer ()
1044
"Send whole buffer to lua subprocess."
1045
(interactive)
1046
(lua-send-region (point-min) (point-max)))
1047
1048
(defun lua-restart-with-whole-file ()
1049
"Restart lua subprocess and send whole file as input."
1050
(interactive)
1051
(lua-kill-process)
1052
(lua-start-process lua-default-application)
1053
(lua-send-buffer))
1054
1055
(defun lua-show-process-buffer ()
1056
"Make sure `lua-process-buffer' is being displayed."
1057
(interactive)
1058
(display-buffer lua-process-buffer))
1059
1060
(defun lua-hide-process-buffer ()
1061
"Delete all windows that display `lua-process-buffer'."
1062
(interactive)
1063
(delete-windows-on lua-process-buffer))
1064
1065
(defun lua-search-documentation ()
1066
"Search Lua documentation for the word at the point."
1067
(interactive)
1068
(browse-url (concat lua-search-url-prefix (current-word t))))
1069
1070
(defun lua-calculate-state (arg prevstate)
1071
;; Calculate the new state of PREVSTATE, t or nil, based on arg. If
1072
;; arg is nil or zero, toggle the state. If arg is negative, turn
1073
;; the state off, and if arg is positive, turn the state on
1074
(if (or (not arg)
1075
(zerop (setq arg (prefix-numeric-value arg))))
1076
(not prevstate)
1077
(> arg 0)))
1078
1079
(defun lua-toggle-electric-state (&optional arg)
1080
"Toggle the electric indentation feature.
1081
Optional numeric ARG, if supplied, turns on electric indentation when
1082
positive, turns it off when negative, and just toggles it when zero or
1083
left out."
1084
(interactive "P")
1085
(setq lua-electric-flag (lua-calculate-state arg lua-electric-flag)))
1086
1087
(defun lua-forward-sexp (&optional count)
1088
"Forward to block end"
1089
(interactive "p")
1090
(save-match-data
1091
(let* ((count (or count 1))
1092
(stackheight 0)
1093
(block-start (mapcar 'car lua-sexp-alist))
1094
(block-end (mapcar 'cdr lua-sexp-alist))
1095
(block-regex (regexp-opt (append block-start block-end) 'words))
1096
current-exp
1097
)
1098
(while (> count 0)
1099
;; skip whitespace
1100
(skip-chars-forward " \t\n")
1101
(if (looking-at (regexp-opt block-start 'words))
1102
(let ((keyword (match-string 1)))
1103
(lua-find-matching-token-word keyword nil))
1104
;; If the current keyword is not a "begin" keyword, then just
1105
;; perform the normal forward-sexp.
1106
(forward-sexp 1))
1107
(setq count (1- count))))))
1108
1109
1110
;; menu bar
1111
1112
(define-key lua-mode-menu [restart-with-whole-file]
1113
'("Restart With Whole File" . lua-restart-with-whole-file))
1114
(define-key lua-mode-menu [kill-process]
1115
'("Kill Process" . lua-kill-process))
1116
1117
(define-key lua-mode-menu [hide-process-buffer]
1118
'("Hide Process Buffer" . lua-hide-process-buffer))
1119
(define-key lua-mode-menu [show-process-buffer]
1120
'("Show Process Buffer" . lua-show-process-buffer))
1121
1122
(define-key lua-mode-menu [end-of-proc]
1123
'("End Of Proc" . lua-end-of-proc))
1124
(define-key lua-mode-menu [beginning-of-proc]
1125
'("Beginning Of Proc" . lua-beginning-of-proc))
1126
1127
(define-key lua-mode-menu [send-lua-region]
1128
'("Send Lua-Region" . lua-send-lua-region))
1129
(define-key lua-mode-menu [set-lua-region-end]
1130
'("Set Lua-Region End" . lua-set-lua-region-end))
1131
(define-key lua-mode-menu [set-lua-region-start]
1132
'("Set Lua-Region Start" . lua-set-lua-region-start))
1133
1134
(define-key lua-mode-menu [send-current-line]
1135
'("Send Current Line" . lua-send-current-line))
1136
(define-key lua-mode-menu [send-region]
1137
'("Send Region" . lua-send-region))
1138
(define-key lua-mode-menu [send-proc]
1139
'("Send Proc" . lua-send-proc))
1140
(define-key lua-mode-menu [send-buffer]
1141
'("Send Buffer" . lua-send-buffer))
1142
(define-key lua-mode-menu [search-documentation]
1143
'("Search Documentation" . lua-search-documentation))
1144
1145
(provide 'lua-mode)
1146
1147
1148
;;; lua-mode.el ends here
1149
1150