1.1.1
by Jan Christoph Nordholz
Import upstream version 6.31.1+dfsg |
1 |
;;; inform-mode.el --- Inform mode for Emacs
|
2 |
||
3 |
;; Original-Author: Gareth Rees <Gareth.Rees@cl.cam.ac.uk>
|
|
4 |
;; Maintainer: Rupert Lane <rupert@rupert-lane.org>
|
|
5 |
;; Created: 1 Dec 1994
|
|
6 |
;; Version: 1.5.8
|
|
7 |
;; Released: 3 Sep 2002
|
|
8 |
;; Keywords: languages
|
|
9 |
||
10 |
;;; Copyright:
|
|
11 |
||
12 |
;; Copyright (c) by Gareth Rees 1996
|
|
13 |
;; Portions copyright (c) by Michael Fessler 1997-1998
|
|
14 |
;; Portions copyright (c) by Rupert Lane 1999-2000, 2002
|
|
15 |
||
16 |
;; inform-mode is free software; you can redistribute it and/or modify
|
|
17 |
;; it under the terms of the GNU General Public License as published by
|
|
18 |
;; the Free Software Foundation; either version 2, or (at your option)
|
|
19 |
;; any later version.
|
|
20 |
;;
|
|
21 |
;; inform-mode is distributed in the hope that it will be useful, but
|
|
22 |
;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
23 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
24 |
;; General Public License for more details.
|
|
25 |
||
26 |
;;; Commentary:
|
|
27 |
||
28 |
;; Inform is a compiler for adventure games by Graham Nelson,
|
|
29 |
;; available by anonymous FTP from
|
|
30 |
;; /ftp.ifarchive.org:/if-archive/programming/inform/
|
|
31 |
;;
|
|
32 |
;; This file implements a major mode for editing Inform programs. It
|
|
33 |
;; understands most Inform syntax and is capable of indenting lines
|
|
34 |
;; and formatting quoted strings. Type `C-h m' within Inform mode for
|
|
35 |
;; more details.
|
|
36 |
;;
|
|
37 |
;; Because Inform header files use the extension ".h" just as C header
|
|
38 |
;; files do, the function `inform-maybe-mode' is provided. It looks at
|
|
39 |
;; the contents of the current buffer; if it thinks the buffer is in
|
|
40 |
;; Inform, it selects inform-mode; otherwise it selects the mode given
|
|
41 |
;; by the variable `inform-maybe-other'.
|
|
42 |
||
43 |
;; Put this file somewhere on your load-path, and the following code in
|
|
44 |
;; your .emacs file:
|
|
45 |
;;
|
|
46 |
;; (autoload 'inform-mode "inform-mode" "Inform editing mode." t)
|
|
47 |
;; (autoload 'inform-maybe-mode "inform-mode" "Inform/C header editing mode.")
|
|
48 |
;; (setq auto-mode-alist
|
|
49 |
;; (append '(("\\.h\\'" . inform-maybe-mode)
|
|
50 |
;; ("\\.inf\\'" . inform-mode))
|
|
51 |
;; auto-mode-alist))
|
|
52 |
;;
|
|
53 |
;; To turn on font locking add:
|
|
54 |
;; (add-hook 'inform-mode-hook 'turn-on-font-lock)
|
|
55 |
||
56 |
;; If you use XEmacs and intend to use `inform-run-project' with a
|
|
57 |
;; console-mode interpreter, you need to have the eterm package
|
|
58 |
;; installed. It should already be installed if you use XEmacs < 21,
|
|
59 |
;; but starting with XEmacs 21.1 you may need to download and install
|
|
60 |
;; it separately.
|
|
61 |
||
62 |
;; Please send any bugs or comments to rupert@rupert-lane.org
|
|
63 |
||
64 |
;;; Code:
|
|
65 |
||
66 |
(require 'font-lock) |
|
67 |
(require 'regexp-opt) |
|
68 |
||
69 |
||
70 |
;;;
|
|
71 |
;;; General variables
|
|
72 |
;;;
|
|
73 |
||
74 |
(defconst inform-mode-version "1.5.8") |
|
75 |
||
76 |
(defvar inform-maybe-other 'c-mode |
|
77 |
"*`inform-maybe-mode' runs this if current file is not in Inform mode.") |
|
78 |
||
79 |
(defvar inform-startup-message t |
|
80 |
"*Non-nil means display a message when Inform mode is loaded.") |
|
81 |
||
82 |
(defvar inform-auto-newline t |
|
83 |
"*Non-nil means automatically newline before and after braces, and after
|
|
84 |
semicolons.
|
|
85 |
If you do not want a leading newline before opening braces then use:
|
|
86 |
\(define-key inform-mode-map \"{\" 'inform-electric-semi\)") |
|
87 |
||
88 |
(defvar inform-mode-map nil |
|
89 |
"Keymap for Inform mode.") |
|
90 |
||
91 |
(if inform-mode-map nil |
|
92 |
(let ((map (make-sparse-keymap "Inform"))) |
|
93 |
(setq inform-mode-map (make-sparse-keymap)) |
|
94 |
(define-key inform-mode-map "\C-m" 'newline-and-indent) |
|
95 |
(define-key inform-mode-map "\177" 'backward-delete-char-untabify) |
|
96 |
(define-key inform-mode-map "\C-c\C-r" 'inform-retagify) |
|
97 |
(define-key inform-mode-map "\C-c\C-t" 'visit-tags-table) |
|
98 |
(define-key inform-mode-map "\C-c\C-b" 'inform-build-project) |
|
99 |
(define-key inform-mode-map "\C-c\C-c" 'inform-run-project) |
|
100 |
(define-key inform-mode-map "\C-c\C-a" 'inform-toggle-auto-newline) |
|
101 |
(define-key inform-mode-map "\C-c\C-s" 'inform-spell-check-buffer) |
|
102 |
(define-key inform-mode-map "\M-n" 'inform-next-object) |
|
103 |
(define-key inform-mode-map "\M-p" 'inform-prev-object) |
|
104 |
(define-key inform-mode-map "{" 'inform-electric-brace) |
|
105 |
(define-key inform-mode-map "}" 'inform-electric-brace) |
|
106 |
(define-key inform-mode-map "]" 'inform-electric-brace) |
|
107 |
(define-key inform-mode-map ";" 'inform-electric-semi) |
|
108 |
(define-key inform-mode-map ":" 'inform-electric-key) |
|
109 |
(define-key inform-mode-map "!" 'inform-electric-key) |
|
110 |
(define-key inform-mode-map "," 'inform-electric-comma) |
|
111 |
(define-key inform-mode-map [menu-bar] (make-sparse-keymap)) |
|
112 |
(define-key inform-mode-map [menu-bar inform] (cons "Inform" map)) |
|
113 |
(define-key map [separator4] '("--" . nil)) |
|
114 |
(define-key map [inform-spell-check-buffer] |
|
115 |
'("Spellcheck buffer" . inform-spell-check-buffer)) |
|
116 |
(define-key map [ispell-region] '("Spellcheck region" . ispell-region)) |
|
117 |
(define-key map [ispell-word] '("Spellcheck word" . ispell-word)) |
|
118 |
(define-key map [separator3] '("--" . nil)) |
|
119 |
(define-key map [load-tags] '("Load tags table" . visit-tags-table)) |
|
120 |
(define-key map [retagify] '("Rebuild tags table" . inform-retagify)) |
|
121 |
(define-key map [build] '("Build project" . inform-build-project)) |
|
122 |
(define-key map [run] '("Run project" . inform-run-project)) |
|
123 |
(define-key map [separator2] '("--" . nil)) |
|
124 |
(define-key map [next-object] '("Next object" . inform-next-object)) |
|
125 |
(define-key map [prev-object] '("Previous object" . inform-prev-object)) |
|
126 |
(define-key map [separator1] '("--" . nil)) |
|
127 |
(define-key map [comment-region] '("Comment Out Region" . comment-region)) |
|
128 |
(put 'comment-region 'menu-enable 'mark-active) |
|
129 |
(define-key map [indent-region] '("Indent Region" . indent-region)) |
|
130 |
(put 'indent-region 'menu-enable 'mark-active) |
|
131 |
(define-key map [indent-line] '("Indent Line" . indent-for-tab-command)))) |
|
132 |
||
133 |
(defvar inform-mode-abbrev-table nil |
|
134 |
"Abbrev table used while in Inform mode.") |
|
135 |
||
136 |
(define-abbrev-table 'inform-mode-abbrev-table nil) |
|
137 |
||
138 |
(defvar inform-project-file nil |
|
139 |
"*The top-level Inform project file to which the current file belongs.") |
|
140 |
(make-variable-buffer-local 'inform-project-file) |
|
141 |
||
142 |
(defvar inform-autoload-tags t |
|
143 |
"*Non-nil means automatically load tags table when entering Inform mode.") |
|
144 |
||
145 |
(defvar inform-etags-program "etags" |
|
146 |
"The shell command with which to run the etags program.") |
|
147 |
||
148 |
(defvar inform-command "inform" |
|
149 |
"*The shell command with which to run the Inform compiler.") |
|
150 |
||
151 |
(defvar inform-libraries-directory nil |
|
152 |
"*If non-NIL, gives the directory in which libraries are found.") |
|
153 |
||
154 |
(defvar inform-command-options "" |
|
155 |
"*Options with which to call the Inform compiler.") |
|
156 |
||
157 |
(defvar inform-interpreter-command "frotz" |
|
158 |
"*The command with which to run the ZCode interpreter.
|
|
159 |
If a string, the name of a command. If a symbol or a function value, an
|
|
160 |
Emacs-lisp function to be called with the name of the story file.") |
|
161 |
||
162 |
(defvar inform-interpreter-options "" |
|
163 |
"*Additional options with which to call the ZCode interpreter.
|
|
164 |
Only used if `inform-interpreter-command' is a string.") |
|
165 |
||
166 |
(defvar inform-interpreter-kill-old-process t |
|
167 |
"*Whether to kill the old interpreter process when starting a new one.") |
|
168 |
||
169 |
(defvar inform-interpreter-is-graphical nil |
|
170 |
"*Controls whether `inform-interpreter-command' will be run in a buffer. |
|
171 |
If NIL, `inform-run-project' will switch to the interpreter buffer after |
|
172 |
running the interpreter.") |
|
173 |
||
174 |
||
175 |
||
176 |
;;;
|
|
177 |
;;; Indentation parameters
|
|
178 |
;;;
|
|
179 |
||
180 |
(defvar inform-indent-property 8 |
|
181 |
"*Indentation of the start of a property declaration.") |
|
182 |
||
183 |
(defvar inform-indent-has-with-class 1 |
|
184 |
"*Indentation of has/with/class lines in object declarations.") |
|
185 |
||
186 |
(defvar inform-indent-level 4 |
|
187 |
"*Indentation of lines of block relative to first line of block.") |
|
188 |
||
189 |
(defvar inform-indent-label-offset -3 |
|
190 |
"*Indentation of label relative to where it should be.") |
|
191 |
||
192 |
(defvar inform-indent-cont-statement 4 |
|
193 |
"*Indentation of continuation relative to start of statement.") |
|
194 |
||
195 |
(defvar inform-indent-fixup-space t |
|
196 |
"*If non-NIL, fix up space in object declarations.") |
|
197 |
||
198 |
(defvar inform-indent-action-column 40 |
|
199 |
"*Column at which action names should be placed in verb declarations.") |
|
200 |
||
201 |
(defvar inform-comments-line-up-p nil |
|
202 |
"*If non-nil, comments spread over several lines will line up with the first.") |
|
203 |
||
204 |
(defvar inform-strings-line-up-p nil |
|
205 |
"*Variable controlling indentation of multi-line strings.
|
|
206 |
If nil (default), string will be indented according to context.
|
|
207 |
If a number, will always set the indentation to that column.
|
|
208 |
If 'char', will line up with the first character of the string.
|
|
209 |
If 'quote', or other non-nil value, will line up with open quote on
|
|
210 |
first line.") |
|
211 |
||
212 |
;;;
|
|
213 |
;;; Syntax variables
|
|
214 |
;;;
|
|
215 |
||
216 |
(defvar inform-mode-syntax-table nil |
|
217 |
"Syntax table to use in Inform mode buffers.") |
|
218 |
||
219 |
(if inform-mode-syntax-table |
|
220 |
nil
|
|
221 |
(setq inform-mode-syntax-table (make-syntax-table)) |
|
222 |
(modify-syntax-entry ?\\ "\\" inform-mode-syntax-table) |
|
223 |
(modify-syntax-entry ?\n ">" inform-mode-syntax-table) |
|
224 |
(modify-syntax-entry ?! "<" inform-mode-syntax-table) |
|
225 |
(modify-syntax-entry ?# "_" inform-mode-syntax-table) |
|
226 |
(modify-syntax-entry ?% "." inform-mode-syntax-table) |
|
227 |
(modify-syntax-entry ?& "." inform-mode-syntax-table) |
|
228 |
(modify-syntax-entry ?\' "." inform-mode-syntax-table) |
|
229 |
(modify-syntax-entry ?* "." inform-mode-syntax-table) |
|
230 |
(modify-syntax-entry ?- "." inform-mode-syntax-table) |
|
231 |
(modify-syntax-entry ?/ "." inform-mode-syntax-table) |
|
232 |
(modify-syntax-entry ?\; "." inform-mode-syntax-table) |
|
233 |
(modify-syntax-entry ?< "." inform-mode-syntax-table) |
|
234 |
(modify-syntax-entry ?= "." inform-mode-syntax-table) |
|
235 |
(modify-syntax-entry ?> "." inform-mode-syntax-table) |
|
236 |
(modify-syntax-entry ?+ "." inform-mode-syntax-table) |
|
237 |
(modify-syntax-entry ?| "." inform-mode-syntax-table) |
|
238 |
(modify-syntax-entry ?^ "w" inform-mode-syntax-table)) |
|
239 |
||
240 |
||
241 |
;;; Keyword definitions-------------------------------------------------------
|
|
242 |
||
243 |
;; These are used for syntax and font-lock purposes.
|
|
244 |
;; They combine words used in Inform 5 and Inform 6 for full compatability.
|
|
245 |
;; You can add new keywords directly to this list as the regexps for
|
|
246 |
;; font-locking are defined when this file is byte-compiled or eval'd.
|
|
247 |
||
248 |
(eval-and-compile |
|
249 |
(defvar inform-directive-list |
|
250 |
'("abbreviate" "array" "attribute" "btrace" "class" "constant" |
|
251 |
"default" "dictionary" "end" "endif" "etrace" "extend" "fake_action" |
|
252 |
"global" "ifdef" "ifndef" "iftrue" "iffalse" "ifv3" "ifv5" "import" |
|
253 |
"include" "link" "listsymbols" "listdict" "listverbs" "lowstring" |
|
254 |
"ltrace" "message" "nearby" "nobtrace" "noetrace" "noltrace" "notrace" |
|
255 |
"object" "property" "release" "replace" "serial" "statusline" "stub" |
|
256 |
"switches" "system_file" "trace" "verb" "zcharacter") |
|
257 |
"List of Inform directives that shouldn't appear embedded in code.") |
|
258 |
||
259 |
(defvar inform-defining-list |
|
260 |
'("[" "array" "attribute" "class" "constant" "fake_action" "global" |
|
261 |
"lowstring" "nearby" "object" "property") |
|
262 |
"List of Inform directives that define a variable/constant name.
|
|
263 |
Used to build a font-lock regexp; the name defined must follow the
|
|
264 |
keyword.") |
|
265 |
||
266 |
;; We have to hardcode the regexp for inform-defining-list due to the way
|
|
267 |
;; regexp-opt works on different emacsen.
|
|
268 |
;; On Emacs 20 it always uses regular \( \) grouping
|
|
269 |
;; On Emacs 21 it always uses shy \(?: \) grouping
|
|
270 |
;; On XEmacs it can use either based on the shy parameter.
|
|
271 |
;; This means it is impossible to write a match-string expression in
|
|
272 |
;; inform-font-lock-keywords using regexp-opt that will work on all emacsen.
|
|
273 |
;; If Emacs 20 support is dropped this should be removed and shy grouping
|
|
274 |
;; used.
|
|
275 |
(defvar inform-defining-list-regexp |
|
276 |
"\\[\\|a\\(rray\\|ttribute\\)\\|c\\(lass\\|onstant\\)\\|fake_action\\|global\\|lowstring\\|nearby\\|object\\|property"
|
|
277 |
"Regexp based on inform-defining-list, hardcoded for portability.") |
|
278 |
||
279 |
(defvar inform-attribute-list |
|
280 |
'("absent" "animate" "clothing" "concealed" "container" "door" |
|
281 |
"edible" "enterable" "female" "general" "light" "lockable" "locked" |
|
282 |
"male" "moved" "neuter" "on" "open" "openable" "pluralname" "proper" |
|
283 |
"scenery" "scored" "static" "supporter" "switchable" "talkable" |
|
284 |
"transparent" "visited" "workflag" "worn") |
|
285 |
"List of Inform attributes defined in the library.") |
|
286 |
||
287 |
(defvar inform-property-list |
|
288 |
'("n_to" "s_to" "e_to" "w_to" "ne_to" "se_to" "nw_to" "sw_to" "u_to" |
|
289 |
"d_to" "in_to" "out_to" "add_to_scope" "after" "article" "articles" |
|
290 |
"before" "cant_go" "capacity" "daemon" "describe" "description" |
|
291 |
"door_dir" "door_to" "each_turn" "found_in" "grammar" "initial" |
|
292 |
"inside_description" "invent" "life" "list_together" "name" "number" |
|
293 |
"orders" "parse_name" "plural" "react_after" "react_before" |
|
294 |
"short_name" "time_left" "time_out" "when_closed" "when_open" |
|
295 |
"when_on" "when_off" "with_key") |
|
296 |
"List of Inform properties defined in the library.") |
|
297 |
||
298 |
(defvar inform-code-keyword-list |
|
299 |
'("box" "break" "continue" "do" "else" "font off" "font on" "for" |
|
300 |
"give" "has" "hasnt" "if" "inversion" "jump" "move" "new_line" "notin" |
|
301 |
"objectloop" "ofclass" "print" "print_ret" "quit" "read" "remove" |
|
302 |
"restore" "return" "rfalse" "rtrue" "save" "spaces" "string" |
|
303 |
"style bold" "style fixed" "style reverse" "style roman" "style underline" |
|
304 |
"switch" "to" "until" "while") |
|
305 |
"List of Inform code keywords.") |
|
306 |
)
|
|
307 |
||
308 |
;; Some regular expressions are needed at compile-time too so as to
|
|
309 |
;; avoid postponing the work to load time.
|
|
310 |
||
311 |
;; To do the work of building the regexps we use regexp-opt, which has
|
|
312 |
;; different behaviour on XEmacs and GNU Emacs and may not even be
|
|
313 |
;; available on ancient versions
|
|
314 |
(eval-and-compile |
|
315 |
(defun inform-make-regexp (strings &optional paren shy) |
|
316 |
(cond |
|
317 |
((string-match "XEmacs\\|Lucid" emacs-version) |
|
318 |
;; XEmacs
|
|
319 |
(regexp-opt strings paren shy)) |
|
320 |
(t |
|
321 |
;; GNU Emacs
|
|
322 |
(regexp-opt strings))))) |
|
323 |
||
324 |
(eval-and-compile |
|
325 |
(defvar inform-directive-regexp |
|
326 |
(concat "\\<#?\\(" |
|
327 |
(inform-make-regexp inform-directive-list) |
|
328 |
"\\)\\>") |
|
329 |
"Regular expression matching an Inform directive.") |
|
330 |
||
331 |
(defvar inform-object-regexp |
|
332 |
"#?\\<\\(object\\|nearby\\|class\\)\\>"
|
|
333 |
"Regular expression matching start of object declaration.") |
|
334 |
||
335 |
(defvar inform-property-regexp |
|
336 |
(concat "\\s-*\\(" |
|
337 |
(inform-make-regexp inform-property-list) |
|
338 |
"\\)") |
|
339 |
"Regular expression matching Inform properties.")) |
|
340 |
||
341 |
||
342 |
(defvar inform-real-object-regexp |
|
343 |
(eval-when-compile (concat "^" inform-object-regexp)) |
|
344 |
"Regular expression matching the start of a real object declaration.
|
|
345 |
That is, one found at the start of a line.") |
|
346 |
||
347 |
(defvar inform-label-regexp "[^]:\"!\(\n]+\\(:\\|,\\)" |
|
348 |
"Regular expression matching a label.") |
|
349 |
||
350 |
(defvar inform-action-regexp "\\s-*\\*" |
|
351 |
"Regular expression matching an action line in a verb declaration.") |
|
352 |
||
353 |
(defvar inform-statement-terminators '(?\; ?{ ?} ?: ?\) do else) |
|
354 |
"Tokens which precede the beginning of a statement.") |
|
355 |
||
356 |
||
357 |
;;;
|
|
358 |
;;; Font-lock keywords
|
|
359 |
;;;
|
|
360 |
||
361 |
(defvar inform-font-lock-defaults |
|
362 |
'(inform-font-lock-keywords nil t ((?_ . "w") (?' . "$")) inform-prev-object) |
|
363 |
"Font Lock defaults for Inform mode.") |
|
364 |
||
365 |
(defface inform-dictionary-word-face |
|
366 |
'((((class color) (background light)) (:foreground "Red")) |
|
367 |
(((class color) (background dark)) (:foreground "Pink")) |
|
368 |
(t (:italic t :bold t))) |
|
369 |
"Font lock mode face used to highlight dictionary words.") |
|
370 |
||
371 |
(defvar inform-dictionary-word-face 'inform-dictionary-word-face |
|
372 |
"Variable for Font lock mode face used to highlight dictionary words.") |
|
373 |
||
374 |
(defvar inform-font-lock-keywords |
|
375 |
(eval-when-compile |
|
376 |
(list |
|
377 |
||
378 |
;; Inform code keywords
|
|
379 |
(cons (concat "\\s-\\(" |
|
380 |
(inform-make-regexp inform-code-keyword-list) |
|
381 |
"\\)\\(\\s-\\|$\\|;\\)") |
|
382 |
'(1 font-lock-keyword-face)) |
|
383 |
||
384 |
;; Keywords that declare variable or constant names.
|
|
385 |
(list |
|
386 |
(concat "^#?\\(" |
|
387 |
inform-defining-list-regexp
|
|
388 |
"\\)\\s-+\\(->\\s-+\\)*\\(\\(\\w\\|\\s_\\)+\\)") |
|
389 |
'(1 font-lock-keyword-face) |
|
390 |
'(5 font-lock-function-name-face)) |
|
391 |
||
392 |
;; Other directives.
|
|
393 |
(cons inform-directive-regexp 'font-lock-keyword-face) |
|
394 |
||
395 |
;; Single quoted strings, length > 1, are dictionary words
|
|
396 |
'("'\\(\\(-\\|\\w\\)\\(\\(-\\|\\w\\)+\\(//\\w*\\)?\\|//\\w*\\)\\)'" |
|
397 |
(1 inform-dictionary-word-face append)) |
|
398 |
||
399 |
;; Double-quoted dictionary words
|
|
400 |
'("\\(\\s-name\\s-\\|^Verb\\|^Extend\\|^\\s-+\\*\\)" |
|
401 |
("\"\\(\\(-\\|\\w\\)+\\)\"" nil nil |
|
402 |
(1 inform-dictionary-word-face t))) |
|
403 |
||
404 |
;; More double-quoted dictionary words
|
|
405 |
'("^\\s-+\"\\(\\(-\\|\\w\\)+\\)\"\\s-+\"\\(\\(-\\|\\w\\)+\\)\"" |
|
406 |
(1 inform-dictionary-word-face t) |
|
407 |
(3 inform-dictionary-word-face t) |
|
408 |
("\"\\(\\(-\\|\\w\\)+\\)\"" nil nil |
|
409 |
(1 inform-dictionary-word-face t))) |
|
410 |
||
411 |
;; `private', `class', `has' and `with' in objects.
|
|
412 |
'("^\\s-+\\(private\\|class\\|has\\|with\\)\\(\\s-\\|$\\)" |
|
413 |
(1 font-lock-keyword-face)) |
|
414 |
||
415 |
;; Attributes and properties.
|
|
416 |
(cons (concat "[^#]\\<\\(" |
|
417 |
(inform-make-regexp (append inform-attribute-list |
|
418 |
inform-property-list)) |
|
419 |
"\\)\\>") |
|
420 |
'(1 font-lock-variable-name-face)))) |
|
421 |
"Expressions to fontify in Inform mode.") |
|
422 |
||
423 |
||
424 |
;;;
|
|
425 |
;;; Inform mode
|
|
426 |
;;;
|
|
427 |
||
428 |
(defun inform-mode () |
|
429 |
"Major mode for editing Inform programs.
|
|
430 |
||
431 |
* Inform syntax:
|
|
432 |
||
433 |
Type \\[indent-for-tab-command] to indent the current line.
|
|
434 |
Type \\[indent-region] to indent the region.
|
|
435 |
||
436 |
Type \\[fill-paragraph] to fill strings or comments.
|
|
437 |
This compresses multiple spaces into single spaces.
|
|
438 |
||
439 |
* Multi-file projects:
|
|
440 |
||
441 |
The variable `inform-project-file' gives the name of the root file of |
|
442 |
the project \(i.e., the one that you run Inform on\)\; it is best to
|
|
443 |
set this as a local variable in each file, for example by making
|
|
444 |
! -*- inform-project-file:\"game.inf\" -*-
|
|
445 |
the first line of the file.
|
|
446 |
||
447 |
* Tags tables:
|
|
448 |
||
449 |
Type \\[inform-retagify] to build \(and load\) a Tags table.
|
|
450 |
Type \\[visit-tags-table] to load an existing Tags table.
|
|
451 |
If it exists, and if the variable `inform-autoload-tags' is non-NIL, |
|
452 |
the Tags table is loaded on entry to Inform Mode.
|
|
453 |
With a Tags table loaded, type \\[find-tag] to find the declaration of
|
|
454 |
the object, class or function under point.
|
|
455 |
||
456 |
* Navigating in a file:
|
|
457 |
||
458 |
Type \\[inform-prev-object] to go to the previous object/class declaration.
|
|
459 |
Type \\[inform-next-object] to go to the next one.
|
|
460 |
||
461 |
* Compilation:
|
|
462 |
||
463 |
Type \\[inform-build-project] to build the current project.
|
|
464 |
Type \\[next-error] to go to the next error.
|
|
465 |
||
466 |
* Running:
|
|
467 |
||
468 |
Type \\[inform-run-project] to run the current project in an
|
|
469 |
interpreter, either as a sepaarte process or in an Emacs terminal buffer.
|
|
470 |
||
471 |
* Font-lock support:
|
|
472 |
||
473 |
Put \(add-hook 'inform-mode-hook 'turn-on-font-lock) in your .emacs.
|
|
474 |
||
475 |
* Spell checking:
|
|
476 |
||
477 |
Type \\[inform-spell-check-buffer] to spell check all strings in the buffer.
|
|
478 |
Type \\[ispell-word] to check the single word at point.
|
|
479 |
||
480 |
* Key definitions:
|
|
481 |
||
482 |
\\{inform-mode-map}
|
|
483 |
* Functions:
|
|
484 |
||
485 |
inform-maybe-mode
|
|
486 |
Looks at the contents of a file, guesses whether it is an Inform
|
|
487 |
program, runs `inform-mode' if so, or `inform-maybe-other' if not. |
|
488 |
The latter defaults to `c-mode'. Used for header files which might |
|
489 |
be Inform or C programs.
|
|
490 |
||
491 |
* Miscellaneous user options:
|
|
492 |
||
493 |
inform-startup-message
|
|
494 |
Set to nil to inhibit message first time Inform mode is used.
|
|
495 |
||
496 |
inform-maybe-other
|
|
497 |
The mode used by `inform-maybe-mode' if it guesses that the file is |
|
498 |
not an Inform program.
|
|
499 |
||
500 |
inform-mode-hook
|
|
501 |
This hook is run after entry to Inform Mode.
|
|
502 |
||
503 |
inform-autoload-tags
|
|
504 |
If non-nil, then a tags table will automatically be loaded when
|
|
505 |
entering Inform mode.
|
|
506 |
||
507 |
inform-auto-newline
|
|
508 |
If non-nil, then newlines are automatically inserted before and
|
|
509 |
after braces, and after semicolons in Inform code, and after commas
|
|
510 |
in object declarations.
|
|
511 |
||
512 |
* User options controlling indentation style:
|
|
513 |
||
514 |
Values in parentheses are the default indentation style.
|
|
515 |
||
516 |
inform-indent-property \(8\)
|
|
517 |
Indentation of a property or attribute in an object declaration.
|
|
518 |
||
519 |
inform-indent-has-with-class \(1\)
|
|
520 |
Indentation of has/with/class/private lines in object declaration.
|
|
521 |
||
522 |
inform-indent-level \(4\)
|
|
523 |
Indentation of line of code in a block relative to the first line of
|
|
524 |
the block.
|
|
525 |
||
526 |
inform-indent-label-offset \(-3\)
|
|
527 |
Indentation of a line starting with a label, relative to the
|
|
528 |
indentation if the label were absent.
|
|
529 |
||
530 |
inform-indent-cont-statement \(4\)
|
|
531 |
Indentation of second and subsequent lines of a statement, relative
|
|
532 |
to the first.
|
|
533 |
||
534 |
inform-indent-fixup-space \(T\)
|
|
535 |
If non-NIL, fix up space after `Object', `Class', `Nearby', `has', |
|
536 |
`private' and `with', so that all the object's properties line up. |
|
537 |
||
538 |
inform-indent-action-column \(40\)
|
|
539 |
Column at which action names should be placed in verb declarations.
|
|
540 |
If NIL, then action names are not moved.
|
|
541 |
||
542 |
inform-comments-line-up-p \(NIL\)
|
|
543 |
If non-NIL, comments spread out over several lines will start on the
|
|
544 |
same column as the first comment line.
|
|
545 |
||
546 |
inform-strings-line-up-p \(NIL\)
|
|
547 |
Variable controlling indentation of multi-line strings.
|
|
548 |
If nil (default), string will be indented according to context.
|
|
549 |
If a number, will always set the indentation to that column.
|
|
550 |
If 'char', will line up with the first character of the string.
|
|
551 |
If 'quote', or other non-nil value, will line up with open quote on
|
|
552 |
first line.
|
|
553 |
||
554 |
* User options to do with compilation:
|
|
555 |
||
556 |
inform-command
|
|
557 |
The shell command with which to run the Inform compiler.
|
|
558 |
||
559 |
inform-libraries-directory
|
|
560 |
If non-NIL, gives the directory in which the Inform libraries are
|
|
561 |
found.
|
|
562 |
||
563 |
inform-command-options
|
|
564 |
Additional options with which to call the Inform compiler.
|
|
565 |
||
566 |
* User options to do with an interpreter:
|
|
567 |
||
568 |
inform-interpreter-command
|
|
569 |
The command with which to run the ZCode interpreter. Can be a
|
|
570 |
string (a command to be run), a symbol (name of function to call)
|
|
571 |
or a function.
|
|
572 |
||
573 |
inform-interpreter-options
|
|
574 |
Additional options with which to call the ZCode interpreter. Only
|
|
575 |
used if `inform-interpreter-command' is a string. |
|
576 |
||
577 |
inform-interpreter-kill-old-process
|
|
578 |
If non-NIL, `inform-run-project' will kill any running interpreter |
|
579 |
process and start a new one. If not, will switch to the interpreter's
|
|
580 |
buffer (if necessary - see documentation for `inform-run-project' for |
|
581 |
details).
|
|
582 |
||
583 |
inform-interpreter-is-graphical
|
|
584 |
If NIL, `inform-run-project' will switch to the interpreter buffer |
|
585 |
after running the interpreter.
|
|
586 |
||
587 |
||
588 |
* Please send any bugs or comments to rupert@rupert-lane.org
|
|
589 |
"
|
|
590 |
||
591 |
(interactive) |
|
592 |
(if inform-startup-message |
|
593 |
(message "Emacs Inform mode version %s." inform-mode-version)) |
|
594 |
(kill-all-local-variables) |
|
595 |
(use-local-map inform-mode-map) |
|
596 |
(set-syntax-table inform-mode-syntax-table) |
|
597 |
(make-local-variable 'comment-column) |
|
598 |
(make-local-variable 'comment-end) |
|
599 |
(make-local-variable 'comment-indent-function) |
|
600 |
(make-local-variable 'comment-start) |
|
601 |
(make-local-variable 'comment-start-skip) |
|
602 |
(make-local-variable 'fill-paragraph-function) |
|
603 |
(make-local-variable 'font-lock-defaults) |
|
604 |
(make-local-variable 'imenu-extract-index-name-function) |
|
605 |
(make-local-variable 'imenu-prev-index-position-function) |
|
606 |
(make-local-variable 'indent-line-function) |
|
607 |
(make-local-variable 'indent-region-function) |
|
608 |
(make-local-variable 'parse-sexp-ignore-comments) |
|
609 |
(make-local-variable 'require-final-newline) |
|
610 |
(setq comment-column 40 |
|
611 |
comment-end "" |
|
612 |
comment-indent-function 'inform-comment-indent |
|
613 |
comment-start "!" |
|
614 |
comment-start-skip "!+\\s-*" |
|
615 |
fill-paragraph-function 'inform-fill-paragraph |
|
616 |
font-lock-defaults inform-font-lock-defaults |
|
617 |
imenu-extract-index-name-function 'inform-imenu-extract-name |
|
618 |
imenu-prev-index-position-function 'inform-prev-object |
|
619 |
indent-line-function 'inform-indent-line |
|
620 |
indent-region-function 'inform-indent-region |
|
621 |
inform-startup-message nil |
|
622 |
local-abbrev-table inform-mode-abbrev-table |
|
623 |
major-mode 'inform-mode |
|
624 |
mode-name "Inform" |
|
625 |
parse-sexp-ignore-comments t |
|
626 |
require-final-newline t) |
|
627 |
(auto-fill-mode 1) |
|
628 |
(if inform-autoload-tags |
|
629 |
(inform-auto-load-tags-table)) |
|
630 |
(run-hooks 'inform-mode-hook)) |
|
631 |
||
632 |
(defun inform-maybe-mode () |
|
633 |
"Starts Inform mode if file is in Inform; `inform-maybe-other' otherwise." |
|
634 |
(let ((case-fold-search t)) |
|
635 |
(if (save-excursion |
|
636 |
(re-search-forward |
|
637 |
"^\\(!\\|object\\|nearby\\|\\[ \\)"
|
|
638 |
nil t)) |
|
639 |
(inform-mode) |
|
640 |
(funcall inform-maybe-other)))) |
|
641 |
||
642 |
||
643 |
;;;
|
|
644 |
;;; Syntax and indentation
|
|
645 |
;;;
|
|
646 |
||
647 |
;; Go to the start of the current Inform definition. Just goes to the
|
|
648 |
;; most recent line with a function beginning [, or a directive.
|
|
649 |
||
650 |
(defun inform-beginning-of-defun () |
|
651 |
(let ((case-fold-search t)) |
|
652 |
(catch 'found |
|
653 |
(end-of-line 1) |
|
654 |
(while (re-search-backward "\n[[a-z]" nil 'move) |
|
655 |
(forward-char 1) |
|
656 |
(if (or (and (looking-at "\\[") |
|
657 |
(eq (inform-preceding-char) ?\;)) |
|
658 |
(looking-at inform-directive-regexp)) |
|
659 |
(throw 'found nil)) |
|
660 |
(forward-char -1))))) |
|
661 |
||
662 |
;; Returns preceding non-blank, non-comment character in buffer. It is
|
|
663 |
;; assumed that point is not inside a string or comment.
|
|
664 |
||
665 |
(defun inform-preceding-char () |
|
666 |
(save-excursion |
|
667 |
(while (/= (point) (progn (forward-comment -1) (point)))) |
|
668 |
(skip-syntax-backward " ") |
|
669 |
(if (bobp) ?\; |
|
670 |
(preceding-char)))) |
|
671 |
||
672 |
;; Returns preceding non-blank, non-comment token in buffer, either the
|
|
673 |
;; character itself, or the tokens 'do or 'else. It is assumed that
|
|
674 |
;; point is not inside a string or comment.
|
|
675 |
||
676 |
(defun inform-preceding-token () |
|
677 |
(save-excursion |
|
678 |
(while (/= (point) (progn (forward-comment -1) (point)))) |
|
679 |
(skip-syntax-backward " ") |
|
680 |
(if (bobp) ?\; |
|
681 |
(let ((p (preceding-char))) |
|
682 |
(cond ((and (eq p ?o) |
|
683 |
(>= (- (point) 2) (point-min))) |
|
684 |
(goto-char (- (point) 2)) |
|
685 |
(if (looking-at "\\<do") 'do p)) |
|
686 |
((and (eq p ?e) |
|
687 |
(>= (- (point) 4) (point-min))) |
|
688 |
(goto-char (- (point) 4)) |
|
689 |
(if (looking-at "\\<else") 'else p)) |
|
690 |
(t p)))))) |
|
691 |
||
692 |
;; `inform-syntax-class' returns a list describing the syntax at point.
|
|
693 |
||
694 |
;; Optional argument DEFUN-START gives the point from which parsing
|
|
695 |
;; should start, and DATA is the list returned by a previous invocation
|
|
696 |
;; of `inform-syntax-class'.
|
|
697 |
||
698 |
;; The returned list is of the form (SYNTAX IN-OBJ SEXPS STATE).
|
|
699 |
;; SYNTAX is one of
|
|
700 |
||
701 |
;; directive An Inform directive (given by `inform-directive-list')
|
|
702 |
;; has The "has" keyword
|
|
703 |
;; with The "with" keyword
|
|
704 |
;; class The "class" keyword
|
|
705 |
;; private The "private" keyword
|
|
706 |
;; property A property or attribute
|
|
707 |
;; other Any other line not in a function body
|
|
708 |
;; string The line begins inside a string
|
|
709 |
;; comment The line starts with a comment
|
|
710 |
;; label Line contains a label (i.e. has a colon in it)
|
|
711 |
;; code Any other line inside a function body
|
|
712 |
;; blank A blank line
|
|
713 |
;; action An action line in a verb declaration
|
|
714 |
||
715 |
;; IN-OBJ is non-NIL if the line appears to be inside an Object, Nearby,
|
|
716 |
;; or Class declaration.
|
|
717 |
||
718 |
;; SEXPS is a list of pairs (D . P) where P is the start of a sexp
|
|
719 |
;; containing point and D is its nesting depth. The pairs are in
|
|
720 |
;; descreasing order of nesting depth.
|
|
721 |
||
722 |
;; STATE is the list returned by `parse-partial-sexp'.
|
|
723 |
||
724 |
;; For reasons of speed, `inform-syntax-class' looks for directives only
|
|
725 |
;; at the start of lines. If the source contains top-level directives
|
|
726 |
;; not at the start of lines, or anything else at the start of a line
|
|
727 |
;; that might be mistaken for a directive, the wrong syntax class may be
|
|
728 |
;; returned.
|
|
729 |
||
730 |
;; There are circumstances in which SEXPS might not be complete (namely
|
|
731 |
;; if there were multiple opening brackets and some but not all have
|
|
732 |
;; been closed since the last call to `inform-syntax-class'), and rare
|
|
733 |
;; circumstances in which it might be wrong (namely if there are
|
|
734 |
;; multiple closing brackets and fewer, but at least two, opening
|
|
735 |
;; bracket since the last call). I consider these cases not worth
|
|
736 |
;; worrying about - and the speed hit of checking for them is
|
|
737 |
;; considerable.
|
|
738 |
||
739 |
(defun inform-syntax-class (&optional defun-start data) |
|
740 |
(let ((line-start (point)) |
|
741 |
in-obj state |
|
742 |
(case-fold-search t)) |
|
743 |
(save-excursion |
|
744 |
(cond (defun-start |
|
745 |
(setq state (parse-partial-sexp defun-start line-start nil nil |
|
746 |
(nth 3 data))) |
|
747 |
(setq in-obj |
|
748 |
(cond ((or (> (car state) 0) (nth 3 state) (nth 4 state)) |
|
749 |
(nth 1 data)) |
|
750 |
((nth 1 data) (/= (inform-preceding-char) ?\;)) |
|
751 |
(t (looking-at inform-object-regexp))))) |
|
752 |
(t |
|
753 |
(inform-beginning-of-defun) |
|
754 |
(setq in-obj (looking-at inform-object-regexp) |
|
755 |
state (parse-partial-sexp (point) line-start))))) |
|
756 |
||
757 |
(list |
|
758 |
(if (> (car state) 0) |
|
759 |
;; If there's a containing sexp then it's easy.
|
|
760 |
(cond ((nth 3 state) 'string) |
|
761 |
((nth 4 state) 'comment) |
|
762 |
((looking-at (concat "\\s-*" comment-start)) 'comment) |
|
763 |
((looking-at inform-label-regexp) 'label) |
|
764 |
(t 'code)) |
|
765 |
||
766 |
;; Otherwise there are a bunch of special cases (has, with, class,
|
|
767 |
;; and private properties) that must be checked for. Note that
|
|
768 |
;; we have to distinguish between global class declarations and
|
|
769 |
;; class membership in an object declaration. This is done by
|
|
770 |
;; looking for a preceding semicolon.
|
|
771 |
(cond ((nth 3 state) 'string) |
|
772 |
((nth 4 state) 'comment) |
|
773 |
((looking-at (concat "\\s-*" comment-start)) 'comment) |
|
774 |
((and in-obj (looking-at "\\s-*class\\>") |
|
775 |
(/= (inform-preceding-char) ?\;)) |
|
776 |
'class) |
|
777 |
((looking-at inform-action-regexp) 'action) |
|
778 |
((looking-at inform-directive-regexp) 'directive) |
|
779 |
((and (looking-at "\\[") (eq (inform-preceding-char) ?\;)) |
|
780 |
'directive) |
|
781 |
((and (not in-obj) (eq (inform-preceding-char) ?\;)) |
|
782 |
'directive) |
|
783 |
((looking-at "\\s-*$") 'blank) |
|
784 |
((not in-obj) 'other) |
|
785 |
((looking-at "\\s-*has\\(\\s-\\|$\\)") 'has) |
|
786 |
((looking-at "\\s-*with\\(\\s-\\|$\\)") 'with) |
|
787 |
((looking-at "\\s-*private\\(\\s-\\|$\\)") 'private) |
|
788 |
((or (eq (inform-preceding-char) ?,) |
|
789 |
(looking-at inform-property-regexp)) |
|
790 |
'property) |
|
791 |
;; This handles declarations of objects in a class eg
|
|
792 |
;; Bird "swallow";
|
|
793 |
;; It assumes that class names follow the convention of being
|
|
794 |
;; capitalised. This is not the most elegent way of handling
|
|
795 |
;; this case but in practice works well.
|
|
796 |
((looking-at "\\s-*[A-Z]") |
|
797 |
'directive) |
|
798 |
(t |
|
799 |
'other))) |
|
800 |
||
801 |
;; Are we in an object?
|
|
802 |
(if (and in-obj |
|
803 |
(not (looking-at inform-object-regexp)) |
|
804 |
(zerop (car state)) |
|
805 |
(eq (inform-preceding-char) ?\;)) |
|
806 |
nil
|
|
807 |
in-obj) |
|
808 |
||
809 |
;; List of known enclosing sexps.
|
|
810 |
(let ((sexps (nth 2 data)) ; the old list of sexps |
|
811 |
(depth (car state)) ; current nesting depth |
|
812 |
(sexp-start (nth 1 state))) ; enclosing sexp, if any |
|
813 |
(if sexps |
|
814 |
;; Strip away closed sexps.
|
|
815 |
(let ((sexp-depth (car (car sexps)))) |
|
816 |
(while (and sexps (or (> sexp-depth depth) |
|
817 |
(and (eq sexp-depth depth) |
|
818 |
sexp-start))) |
|
819 |
(setq sexps (cdr sexps) |
|
820 |
sexp-depth (if sexps (car (car sexps))))))) |
|
821 |
(if sexp-start |
|
822 |
(setq sexps (cons (cons depth sexp-start) sexps))) |
|
823 |
sexps) |
|
824 |
||
825 |
;; State from the parse algorithm.
|
|
826 |
state))) |
|
827 |
||
828 |
;; Returns the correct indentation for the line at point. DATA is the
|
|
829 |
;; syntax class for the start of the line (as returned by
|
|
830 |
;; `inform-syntax-class'). It is assumed that point is somewhere in the
|
|
831 |
;; indentation for the current line (i.e., everything to the left is
|
|
832 |
;; whitespace).
|
|
833 |
||
834 |
(defun inform-calculate-indentation (data) |
|
835 |
(let ((syntax (car data)) ; syntax class of start of line |
|
836 |
(in-obj (nth 1 data)) ; inside an object? |
|
837 |
(depth (car (nth 3 data))) ; depth of nesting of start of line |
|
838 |
(case-fold-search t)) ; searches are case-insensitive |
|
839 |
(cond |
|
840 |
||
841 |
;; Directives should never be indented or else the directive-
|
|
842 |
;; finding code won't run fast enough. Hence the magic
|
|
843 |
;; constant 0.
|
|
844 |
((eq syntax 'directive) 0) |
|
845 |
((eq syntax 'blank) 0) |
|
846 |
||
847 |
;; Various standard indentations.
|
|
848 |
((eq syntax 'property) inform-indent-property) |
|
849 |
((eq syntax 'other) |
|
850 |
(cond ((looking-at "\\s-*\\[") inform-indent-property) |
|
851 |
(in-obj (+ inform-indent-property inform-indent-level)) |
|
852 |
(t inform-indent-level))) |
|
853 |
((and (eq syntax 'string) (zerop depth)) |
|
854 |
(cond (in-obj (+ inform-indent-property inform-indent-level)) |
|
855 |
(t inform-indent-level))) |
|
856 |
((and (eq syntax 'comment) (zerop depth)) |
|
857 |
(inform-line-up-comment |
|
858 |
(if in-obj inform-indent-property 0))) |
|
859 |
((eq syntax 'action) inform-indent-level) |
|
860 |
((memq syntax '(has with class private)) inform-indent-has-with-class) |
|
861 |
||
862 |
;; We are inside a sexp of some sort.
|
|
863 |
(t |
|
864 |
(let ((indent 0) ; calculated indent column |
|
865 |
paren ; where the enclosing sexp begins |
|
866 |
string-start ; where string (if any) starts |
|
867 |
(string-indent 0) ; indentation for the current str |
|
868 |
cont-p ; true if line is a continuation |
|
869 |
paren-char ; the parenthesis character |
|
870 |
prec-token ; token preceding line |
|
871 |
this-char) ; character under consideration |
|
872 |
(save-excursion |
|
873 |
||
874 |
;; Skip back to the start of a string, if any. (Note that
|
|
875 |
;; we can't be in a comment since the syntax class applies
|
|
876 |
;; to the start of the line.)
|
|
877 |
(if (eq syntax 'string) |
|
878 |
(progn |
|
879 |
(skip-syntax-backward "^\"") |
|
880 |
(forward-char -1) |
|
881 |
(setq string-start (point)) |
|
882 |
(setq string-indent (current-column)) |
|
883 |
))
|
|
884 |
||
885 |
;; Now find the start of the sexp containing point. Most
|
|
886 |
;; likely, the location was found by `inform-syntax-class';
|
|
887 |
;; if not, call `up-list' now and save the result in case
|
|
888 |
;; it's useful in future.
|
|
889 |
(save-excursion |
|
890 |
(let ((sexps (nth 2 data))) |
|
891 |
(if (and sexps (eq (car (car sexps)) depth)) |
|
892 |
(goto-char (cdr (car sexps))) |
|
893 |
(up-list -1) |
|
894 |
(setcar (nthcdr 2 data) |
|
895 |
(cons (cons depth (point)) (nth 2 data))))) |
|
896 |
(setq paren (point) |
|
897 |
paren-char (following-char))) |
|
898 |
||
899 |
;; If we were in a string, now skip back to the start of the
|
|
900 |
;; line. We have to do this *after* calling `up-list' just
|
|
901 |
;; in case there was an opening parenthesis on the line
|
|
902 |
;; including the start of the string.
|
|
903 |
(if (eq syntax 'string) |
|
904 |
(forward-line 0)) |
|
905 |
||
906 |
;; The indentation depends on what kind of sexp we are in.
|
|
907 |
;; If line is in parentheses, indent to opening parenthesis.
|
|
908 |
(if (eq paren-char ?\() |
|
909 |
(setq indent (progn (goto-char paren) (1+ (current-column)))) |
|
910 |
||
911 |
;; Line not in parentheses.
|
|
912 |
(setq prec-token (inform-preceding-token) |
|
913 |
this-char (following-char)) |
|
914 |
(cond |
|
915 |
||
916 |
;; Each 'else' should have the same indentation as the
|
|
917 |
;; matching 'if'
|
|
918 |
((looking-at "\\s-*else") |
|
919 |
;; Find the matching 'if' by counting 'if's and 'else's
|
|
920 |
;; in this sexp
|
|
921 |
(let ((if-count 0) found) |
|
922 |
(while (and (not found) |
|
923 |
(progn (forward-sexp -1) t) ; skip over sub-sexps |
|
924 |
(re-search-backward "\\s-*\\(else\\|if\\)" |
|
925 |
paren t)) |
|
926 |
(setq if-count (+ if-count |
|
927 |
(if (string= (match-string 1) "else") |
|
928 |
-1 1))) |
|
929 |
(if (eq if-count 1) (setq found t))) |
|
930 |
(if (not found) |
|
931 |
(setq indent 0) |
|
932 |
(forward-line 0) |
|
933 |
(skip-syntax-forward " ") |
|
934 |
(setq indent (current-column))))) |
|
935 |
||
936 |
;; Line is an inlined directive-- always put on column 0
|
|
937 |
((looking-at "\\s-*#[^#]") |
|
938 |
(setq indent 0)) |
|
939 |
||
940 |
;; Line is in an implicit block: take indentation from
|
|
941 |
;; the line that introduces the block, plus one level.
|
|
942 |
((memq prec-token '(?\) do else)) |
|
943 |
(forward-sexp -1) |
|
944 |
(forward-line 0) |
|
945 |
(skip-syntax-forward " ") |
|
946 |
(setq indent |
|
947 |
(+ (current-column) |
|
948 |
(if (eq this-char ?{) 0 inform-indent-level)))) |
|
949 |
||
950 |
;; Line is a continued statement.
|
|
951 |
((not (or (memq prec-token inform-statement-terminators) |
|
952 |
(eq syntax 'label))) |
|
953 |
(setq cont-p t) |
|
954 |
(forward-line -1) |
|
955 |
(let ((token (inform-preceding-token))) |
|
956 |
;; Is it the first continuation line?
|
|
957 |
(if (memq token inform-statement-terminators) |
|
958 |
(setq indent inform-indent-cont-statement))) |
|
959 |
(skip-syntax-forward " ") |
|
960 |
(setq indent (+ indent (current-column)))) |
|
961 |
||
962 |
;; Line is in a function, take indentation from start of
|
|
963 |
;; function, ignoring `with'.
|
|
964 |
((eq paren-char ?\[) |
|
965 |
(goto-char paren) |
|
966 |
(forward-line 0) |
|
967 |
(looking-at "\\(\\s-*with\\s-\\)?\\s-*") |
|
968 |
(goto-char (match-end 0)) |
|
969 |
(setq indent |
|
970 |
(+ (current-column) |
|
971 |
(if (eq this-char ?\]) 0 inform-indent-level)))) |
|
972 |
||
973 |
;; Line is in a block: take indentation from block.
|
|
974 |
(t |
|
975 |
(goto-char paren) |
|
976 |
(if (eq (inform-preceding-char) ?\)) |
|
977 |
(forward-sexp -1)) |
|
978 |
(forward-line 0) |
|
979 |
(skip-syntax-forward " ") |
|
980 |
||
981 |
(setq indent |
|
982 |
(+ (current-column) |
|
983 |
(if (memq this-char '(?} ?{)) |
|
984 |
0
|
|
985 |
inform-indent-level))) |
|
986 |
))
|
|
987 |
||
988 |
;; We calculated the indentation for the start of the
|
|
989 |
;; string; correct this for the remainder of the string if
|
|
990 |
;; appropriate.
|
|
991 |
(cond |
|
992 |
((eq syntax 'string) |
|
993 |
;; do conditional line-up
|
|
994 |
(cond |
|
995 |
((numberp inform-strings-line-up-p) |
|
996 |
(setq indent inform-strings-line-up-p)) |
|
997 |
((eq inform-strings-line-up-p 'char) |
|
998 |
(setq indent (1+ string-indent))) |
|
999 |
(inform-strings-line-up-p |
|
1000 |
(setq indent string-indent)) |
|
1001 |
((not cont-p) |
|
1002 |
(goto-char string-start) |
|
1003 |
(let ((token (inform-preceding-token))) |
|
1004 |
(if (not (memq token inform-statement-terminators)) |
|
1005 |
(setq indent |
|
1006 |
(+ indent inform-indent-cont-statement))))))) |
|
1007 |
||
1008 |
;; Indent for label, if any.
|
|
1009 |
((eq syntax 'label) |
|
1010 |
(setq indent (+ indent inform-indent-label-offset)))))) |
|
1011 |
||
1012 |
;; Handle comments specially if told to line them up
|
|
1013 |
(if (looking-at (concat "\\s-*" comment-start)) |
|
1014 |
(setq indent (inform-line-up-comment indent))) |
|
1015 |
||
1016 |
indent))))) |
|
1017 |
||
1018 |
||
1019 |
(defun inform-line-up-comment (current-indent) |
|
1020 |
"Return the indentation to line up this comment with the previous one.
|
|
1021 |
If inform-comments-line-up-p is nil, or the preceeding lines do not contain
|
|
1022 |
comments, return CURRENT-INDENT."
|
|
1023 |
(if inform-comments-line-up-p |
|
1024 |
(save-excursion |
|
1025 |
(let ((indent current-indent) |
|
1026 |
done limit) |
|
1027 |
(while (and (not done) |
|
1028 |
(> (point) 1)) |
|
1029 |
(forward-line -1) |
|
1030 |
(setq limit (point)) |
|
1031 |
(cond ((looking-at (concat "\\s-*" comment-start)) |
|
1032 |
;; a full-line comment, keep searching
|
|
1033 |
nil) |
|
1034 |
((and |
|
1035 |
(or (end-of-line) t) |
|
1036 |
(re-search-backward comment-start limit t) |
|
1037 |
(eq (car (inform-syntax-class)) 'comment)) |
|
1038 |
;; a line with a comment char at the end
|
|
1039 |
;; that is not part of the code
|
|
1040 |
(setq indent (current-column)) |
|
1041 |
(setq done t)) |
|
1042 |
(t |
|
1043 |
;; a non-comment line so we do not need to change
|
|
1044 |
(setq done t)))) |
|
1045 |
indent)) |
|
1046 |
current-indent)) |
|
1047 |
||
1048 |
||
1049 |
;; Modifies whitespace to the left of point so that the character after
|
|
1050 |
;; point is at COLUMN. If this is impossible, one whitespace character
|
|
1051 |
;; is left. Avoids changing buffer gratuitously, and returns non-NIL if
|
|
1052 |
;; it actually changed the buffer. If a change is made, point is moved
|
|
1053 |
;; to the end of any inserted or deleted whitespace. (If not, it may be
|
|
1054 |
;; moved at random.)
|
|
1055 |
||
1056 |
(defun inform-indent-to (column) |
|
1057 |
(let ((col (current-column))) |
|
1058 |
(cond ((eq col column) nil) |
|
1059 |
((< col column) (indent-to column) t) |
|
1060 |
(t (let ((p (point)) |
|
1061 |
(mincol (progn (skip-syntax-backward " ") |
|
1062 |
(current-column)))) |
|
1063 |
(if (eq mincol (1- col)) |
|
1064 |
nil
|
|
1065 |
(delete-region (point) p) |
|
1066 |
(indent-to (max (if (bolp) mincol (1+ mincol)) column)) |
|
1067 |
t)))))) |
|
1068 |
||
1069 |
;; Indent the line containing point; DATA is assumed to have been
|
|
1070 |
;; returned from `inform-syntax-class', called at the *start* of the
|
|
1071 |
;; current line. It is assumed that point is at the start of the line.
|
|
1072 |
;; Fixes up the spacing on `has', `with', `object', `nearby', `private'
|
|
1073 |
;; and `class' lines. Returns T if a change was made, NIL otherwise.
|
|
1074 |
;; Moves point.
|
|
1075 |
||
1076 |
(defun inform-do-indent-line (data) |
|
1077 |
(skip-syntax-forward " ") |
|
1078 |
(let ((changed-p (inform-indent-to (inform-calculate-indentation data))) |
|
1079 |
(syntax (car data))) |
|
1080 |
||
1081 |
;; Fix up space if appropriate, return changed flag.
|
|
1082 |
(or |
|
1083 |
(cond |
|
1084 |
((and (memq syntax '(directive has with class private)) |
|
1085 |
inform-indent-fixup-space
|
|
1086 |
(looking-at |
|
1087 |
"\\(object\\|class\\|nearby\\|has\\|with\\|private\\)\\(\\s-+\\|$\\)")) |
|
1088 |
(goto-char (match-end 0)) |
|
1089 |
(inform-indent-to inform-indent-property)) |
|
1090 |
((and (eq syntax 'action) |
|
1091 |
inform-indent-action-column
|
|
1092 |
(or (looking-at "\\*.*\\(->\\)") |
|
1093 |
(looking-at "\\*.*\\($\\)"))) |
|
1094 |
(goto-char (match-beginning 1)) |
|
1095 |
(inform-indent-to inform-indent-action-column)) |
|
1096 |
(t nil)) |
|
1097 |
changed-p))) |
|
1098 |
||
1099 |
;; Calculate and return the indentation for a comment (assume point is
|
|
1100 |
;; on the comment).
|
|
1101 |
||
1102 |
(defun inform-comment-indent () |
|
1103 |
(skip-syntax-backward " ") |
|
1104 |
(if (bolp) |
|
1105 |
(inform-calculate-indentation (inform-syntax-class)) |
|
1106 |
(max (1+ (current-column)) comment-column))) |
|
1107 |
||
1108 |
;; Indent line containing point.
|
|
1109 |
;; Keep point at the "logically" same place, unless point was before
|
|
1110 |
;; new indentation, in which case place point at indentation.
|
|
1111 |
||
1112 |
(defun inform-indent-line () |
|
1113 |
(let ((oldpos (- (point-max) (point)))) |
|
1114 |
(forward-line 0) |
|
1115 |
(inform-do-indent-line (inform-syntax-class)) |
|
1116 |
(and (< oldpos (- (point-max) (point))) |
|
1117 |
(goto-char (- (point-max) oldpos))))) |
|
1118 |
||
1119 |
;; Indent all the lines in region.
|
|
1120 |
||
1121 |
(defun inform-indent-region (start end) |
|
1122 |
(save-restriction |
|
1123 |
(let ((endline (progn (goto-char (max end start)) |
|
1124 |
(or (bolp) (end-of-line)) |
|
1125 |
(point))) |
|
1126 |
data linestart) |
|
1127 |
(narrow-to-region (point-min) endline) |
|
1128 |
(goto-char (min start end)) |
|
1129 |
(forward-line 0) |
|
1130 |
(while (not (eobp)) |
|
1131 |
(setq data (if data (inform-syntax-class linestart data) |
|
1132 |
(inform-syntax-class)) |
|
1133 |
linestart (point)) |
|
1134 |
(inform-do-indent-line data) |
|
1135 |
(forward-line 1))))) |
|
1136 |
||
1137 |
||
1138 |
;;;
|
|
1139 |
;;; Filling paragraphs
|
|
1140 |
;;;
|
|
1141 |
||
1142 |
;; Fill quoted string or comment containing point. To fill a quoted
|
|
1143 |
;; string, point must be between the quotes. Deals appropriately with
|
|
1144 |
;; trailing backslashes.
|
|
1145 |
||
1146 |
(defun inform-fill-paragraph (&optional arg) |
|
1147 |
(let* ((data (inform-syntax-class)) |
|
1148 |
(syntax (car data)) |
|
1149 |
(case-fold-search t)) |
|
1150 |
(cond ((eq syntax 'comment) |
|
1151 |
(if (save-excursion |
|
1152 |
(forward-line 0) |
|
1153 |
(looking-at "\\s-*!+\\s-*")) |
|
1154 |
(let ((fill-prefix (match-string 0))) |
|
1155 |
(fill-paragraph nil) |
|
1156 |
t) |
|
1157 |
(error "Can't fill comments not at start of line."))) |
|
1158 |
((eq syntax 'string) |
|
1159 |
(save-excursion |
|
1160 |
(let* ((indent-col (prog2 |
|
1161 |
(insert ?\n) |
|
1162 |
(inform-calculate-indentation data) |
|
1163 |
(delete-backward-char 1))) |
|
1164 |
(start (search-backward "\"")) |
|
1165 |
(end (search-forward "\"" nil nil 2)) |
|
1166 |
(fill-column (- fill-column 2)) |
|
1167 |
linebeg) |
|
1168 |
(save-restriction |
|
1169 |
(narrow-to-region (point-min) end) |
|
1170 |
||
1171 |
;; Fold all the lines together, removing backslashes
|
|
1172 |
;; and multiple spaces as we go.
|
|
1173 |
(subst-char-in-region start end ?\n ? ) |
|
1174 |
(subst-char-in-region start end ?\\ ? ) |
|
1175 |
(subst-char-in-region start end ?\t ? ) |
|
1176 |
(goto-char start) |
|
1177 |
(while (re-search-forward " +" end t) |
|
1178 |
(delete-region (match-beginning 0) (1- (match-end 0)))) |
|
1179 |
||
1180 |
;; Split this line; reindent after first split,
|
|
1181 |
;; otherwise indent to point where first split ended
|
|
1182 |
;; up.
|
|
1183 |
(goto-char start) |
|
1184 |
(setq linebeg start) |
|
1185 |
(while (not (eobp)) |
|
1186 |
(move-to-column (1+ fill-column)) |
|
1187 |
(if (eobp) |
|
1188 |
nil
|
|
1189 |
(skip-chars-backward "^ " linebeg) |
|
1190 |
(if (eq (point) linebeg) |
|
1191 |
(progn |
|
1192 |
(skip-chars-forward "^ ") |
|
1193 |
(skip-chars-forward " "))) |
|
1194 |
(insert "\n") |
|
1195 |
(indent-to-column indent-col 1) |
|
1196 |
(setq linebeg (point)))))) |
|
1197 |
||
1198 |
;; Return T so that `fill-paragaph' doesn't try anything.
|
|
1199 |
t)) |
|
1200 |
||
1201 |
(t (error "Point is neither in a comment nor a string."))))) |
|
1202 |
||
1203 |
||
1204 |
;;;
|
|
1205 |
;;; Tags
|
|
1206 |
;;;
|
|
1207 |
||
1208 |
;; Return the project file to which the current file belongs. This is
|
|
1209 |
;; either the value of `inform-project-file', the current file.
|
|
1210 |
||
1211 |
(defun inform-project-file () |
|
1212 |
(or inform-project-file (buffer-file-name))) |
|
1213 |
||
1214 |
;; Builds a list of files in the current project and returns it. It
|
|
1215 |
;; recursively searches through included files, but tries to avoid
|
|
1216 |
;; loops.
|
|
1217 |
||
1218 |
(defun inform-project-file-list () |
|
1219 |
(let* ((project-file (expand-file-name (inform-project-file))) |
|
1220 |
(project-dir (file-name-directory project-file)) |
|
1221 |
(in-file-list (list project-file)) |
|
1222 |
out-file-list
|
|
1223 |
(temp-buffer (generate-new-buffer "*Inform temp*"))) |
|
1224 |
(message "Building list of files in project...") |
|
1225 |
(save-excursion |
|
1226 |
(while in-file-list |
|
1227 |
(if (member (car in-file-list) out-file-list) |
|
1228 |
nil
|
|
1229 |
(set-buffer temp-buffer) |
|
1230 |
(erase-buffer) |
|
1231 |
(insert-file-contents (car in-file-list)) |
|
1232 |
(setq out-file-list (cons (car in-file-list) out-file-list) |
|
1233 |
in-file-list (cdr in-file-list)) |
|
1234 |
(goto-char (point-min)) |
|
1235 |
(while (re-search-forward "\\<#?include\\s-+\">\\([^\"]+\\)\"" nil t) |
|
1236 |
(let ((file (match-string 1))) |
|
1237 |
;; We need to duplicate Inform's file-finding algorithm:
|
|
1238 |
(if (not (string-match "\\." file)) |
|
1239 |
(setq file (concat file ".inf"))) |
|
1240 |
(if (not (file-name-absolute-p file)) |
|
1241 |
(setq file (expand-file-name file project-dir))) |
|
1242 |
(setq in-file-list (cons file in-file-list)))))) |
|
1243 |
(kill-buffer nil)) |
|
1244 |
(message "Building list of files in project...done") |
|
1245 |
out-file-list)) |
|
1246 |
||
1247 |
;; Visit tags table for current project, if it exists, or do nothing if
|
|
1248 |
;; there is no current project, or no tags table.
|
|
1249 |
||
1250 |
(defun inform-auto-load-tags-table () |
|
1251 |
(let (tf (project (inform-project-file))) |
|
1252 |
(if project |
|
1253 |
(progn |
|
1254 |
(setq tf (expand-file-name "TAGS" (file-name-directory project))) |
|
1255 |
(if (file-readable-p tf) |
|
1256 |
;; visit-tags-table seems to just take first parameter in XEmacs
|
|
1257 |
(visit-tags-table tf)))))) |
|
1258 |
||
1259 |
(defun inform-retagify () |
|
1260 |
"Create a tags table for the files in the current project.
|
|
1261 |
The current project contains all the files included using Inform's
|
|
1262 |
`Include \">file\";' syntax by the project file, which is that given by
|
|
1263 |
the variable `inform-project-file' \(if this is set\), or the current |
|
1264 |
file \(if not\). Files included recursively are included in the tags
|
|
1265 |
table."
|
|
1266 |
(interactive) |
|
1267 |
(let* ((project-file (inform-project-file)) |
|
1268 |
(project-dir (file-name-directory project-file)) |
|
1269 |
(files (inform-project-file-list)) |
|
1270 |
(tags-file (expand-file-name "TAGS" project-dir))) |
|
1271 |
(message "Running external tags program...") |
|
1272 |
||
1273 |
;; Uses call-process to work on windows/nt systems (not tested)
|
|
1274 |
;; Regexp matches routines or object/class definitions
|
|
1275 |
(apply (function call-process) |
|
1276 |
inform-etags-program
|
|
1277 |
nil nil nil |
|
1278 |
"--regex=/\\([oO]bject\\|[nN]earby\\|[cC]lass\\|\\[\\)\\([ \\t]*->\\)*[ \\t]*\\([A-Za-z0-9_]+\\)/"
|
|
1279 |
(concat "--output=" tags-file) |
|
1280 |
"--language=none"
|
|
1281 |
files) |
|
1282 |
||
1283 |
(message "Running external tags program...done") |
|
1284 |
(inform-auto-load-tags-table))) |
|
1285 |
||
1286 |
||
1287 |
||
1288 |
||
1289 |
;;;
|
|
1290 |
;;; Electric keys
|
|
1291 |
;;;
|
|
1292 |
||
1293 |
(defun inform-toggle-auto-newline (arg) |
|
1294 |
"Toggle auto-newline feature.
|
|
1295 |
Optional numeric ARG, if supplied turns on auto-newline when positive,
|
|
1296 |
turns it off when negative, and just toggles it when zero."
|
|
1297 |
(interactive "P") |
|
1298 |
(setq inform-auto-newline |
|
1299 |
(if (or (not arg) |
|
1300 |
(zerop (setq arg (prefix-numeric-value arg)))) |
|
1301 |
(not inform-auto-newline) |
|
1302 |
(> arg 0)))) |
|
1303 |
||
1304 |
(defun inform-electric-key (arg) |
|
1305 |
"Insert the key typed and correct indentation."
|
|
1306 |
(interactive "P") |
|
1307 |
(if (and (not arg) (eolp)) |
|
1308 |
(progn |
|
1309 |
(self-insert-command 1) |
|
1310 |
(inform-indent-line) |
|
1311 |
(end-of-line)) |
|
1312 |
(self-insert-command (prefix-numeric-value arg)))) |
|
1313 |
||
1314 |
(defun inform-electric-semi (arg) |
|
1315 |
"Insert the key typed and correct line's indentation, as for semicolon.
|
|
1316 |
Special handling does not occur inside strings and comments.
|
|
1317 |
Inserts newline after the character if `inform-auto-newline' is non-NIL." |
|
1318 |
(interactive "P") |
|
1319 |
(if (and (not arg) |
|
1320 |
(eolp) |
|
1321 |
(let ((data (inform-syntax-class))) |
|
1322 |
(not (memq (car data) '(string comment))))) |
|
1323 |
(progn |
|
1324 |
(self-insert-command 1) |
|
1325 |
(inform-indent-line) |
|
1326 |
(end-of-line) |
|
1327 |
(if inform-auto-newline (newline-and-indent))) |
|
1328 |
(self-insert-command (prefix-numeric-value arg)))) |
|
1329 |
||
1330 |
(defun inform-electric-comma (arg) |
|
1331 |
"Insert the key typed and correct line's indentation, as for comma.
|
|
1332 |
Special handling only occurs in object declarations.
|
|
1333 |
Inserts newline after the character if `inform-auto-newline' is non-NIL." |
|
1334 |
(interactive "P") |
|
1335 |
(if (and (not arg) |
|
1336 |
(eolp) |
|
1337 |
(let ((data (inform-syntax-class))) |
|
1338 |
(and (not (memq (car data) '(string comment))) |
|
1339 |
(nth 1 data) |
|
1340 |
(zerop (car (nth 3 data)))))) |
|
1341 |
(progn |
|
1342 |
(self-insert-command 1) |
|
1343 |
(inform-indent-line) |
|
1344 |
(end-of-line) |
|
1345 |
(if inform-auto-newline (newline-and-indent))) |
|
1346 |
(self-insert-command (prefix-numeric-value arg)))) |
|
1347 |
||
1348 |
(defun inform-electric-brace (arg) |
|
1349 |
"Insert the key typed and correct line's indentation.
|
|
1350 |
Insert newlines before and after if `inform-auto-newline' is non-NIL." |
|
1351 |
;; This logic is the same as electric-c-brace.
|
|
1352 |
(interactive "P") |
|
1353 |
(let (insertpos) |
|
1354 |
(if (and (not arg) |
|
1355 |
(eolp) |
|
1356 |
(let ((data (inform-syntax-class))) |
|
1357 |
(memq (car data) '(code label))) |
|
1358 |
(or (save-excursion (skip-syntax-backward " ") (bolp)) |
|
1359 |
(if inform-auto-newline |
|
1360 |
(progn (inform-indent-line) (newline) t) nil))) |
|
1361 |
(progn |
|
1362 |
(insert last-command-char) |
|
1363 |
(inform-indent-line) |
|
1364 |
(end-of-line) |
|
1365 |
(if (and inform-auto-newline (/= last-command-char ?\])) |
|
1366 |
(progn |
|
1367 |
(newline) |
|
1368 |
(setq insertpos (1- (point))) |
|
1369 |
(inform-indent-line))) |
|
1370 |
(save-excursion |
|
1371 |
(if insertpos (goto-char insertpos)) |
|
1372 |
(delete-char -1)))) |
|
1373 |
(if insertpos |
|
1374 |
(save-excursion |
|
1375 |
(goto-char (1- insertpos)) |
|
1376 |
(self-insert-command (prefix-numeric-value arg))) |
|
1377 |
(self-insert-command (prefix-numeric-value arg))))) |
|
1378 |
||
1379 |
||
1380 |
;;;
|
|
1381 |
;;; Miscellaneous
|
|
1382 |
;;;
|
|
1383 |
||
1384 |
(defun inform-next-object (&optional arg) |
|
1385 |
"Go to the next object or class declaration in the file.
|
|
1386 |
With a prefix arg, go forward that many declarations.
|
|
1387 |
With a negative prefix arg, search backwards."
|
|
1388 |
(interactive "P") |
|
1389 |
(let ((fun 're-search-forward) |
|
1390 |
(errstring "more") |
|
1391 |
(n (prefix-numeric-value arg))) |
|
1392 |
(cond ((< n 0) |
|
1393 |
(setq fun 're-search-backward errstring "previous" n (- n))) |
|
1394 |
((looking-at inform-real-object-regexp) |
|
1395 |
(setq n (1+ n)))) |
|
1396 |
(prog1 |
|
1397 |
(funcall fun inform-real-object-regexp nil 'move n) |
|
1398 |
(forward-line 0)))) |
|
1399 |
||
1400 |
;; This function doubles as an `imenu-prev-name' function, so when
|
|
1401 |
;; called noninteractively it must return T if it was successful and NIL
|
|
1402 |
;; if not. Argument NIL must correspond to moving backwards by 1.
|
|
1403 |
||
1404 |
(defun inform-prev-object (&optional arg) |
|
1405 |
"Go to the previous object or class declaration in the file.
|
|
1406 |
With a prefix arg, go back many declarations.
|
|
1407 |
With a negative prefix arg, go forwards."
|
|
1408 |
(interactive "P") |
|
1409 |
(inform-next-object (- (prefix-numeric-value arg)))) |
|
1410 |
||
1411 |
(defun inform-imenu-extract-name () |
|
1412 |
(if (looking-at |
|
1413 |
"^#?\\(object\\|nearby\\|class\\)\\s-+\\(->\\s-+\\)*\\(\\(\\w\\|\\s_\\)+\\)") |
|
1414 |
(concat (if (string= "class" (downcase (match-string 1))) |
|
1415 |
"Class ") |
|
1416 |
(buffer-substring-no-properties (match-beginning 3) |
|
1417 |
(match-end 3))))) |
|
1418 |
||
1419 |
||
1420 |
;;;
|
|
1421 |
;;; Build and run project
|
|
1422 |
;;;
|
|
1423 |
||
1424 |
(defun inform-build-project () |
|
1425 |
"Compile the current Inform project.
|
|
1426 |
The current project is given by `inform-project-file', or the current |
|
1427 |
file if this is NIL."
|
|
1428 |
(interactive) |
|
1429 |
(let ((project-file (file-name-nondirectory (inform-project-file)))) |
|
1430 |
(compile |
|
1431 |
(concat inform-command |
|
1432 |
(if (and inform-libraries-directory |
|
1433 |
(file-directory-p inform-libraries-directory)) |
|
1434 |
(concat " +" inform-libraries-directory) |
|
1435 |
"") |
|
1436 |
;; Note the use of Microsoft style errors. The
|
|
1437 |
;; Archimedes-style errors don't give the correct file
|
|
1438 |
;; name.
|
|
1439 |
" " inform-command-options " -E1 " |
|
1440 |
(if (string-match "\\`[^.]+\\(\\.inf\\'\\)" project-file) |
|
1441 |
(substring project-file 0 (match-beginning 1)) |
|
1442 |
project-file))))) |
|
1443 |
||
1444 |
(defun inform-run-project () |
|
1445 |
"Run the current Inform project using `inform-interpreter-command'. |
|
1446 |
The current project is given by `inform-project-file', or the current |
|
1447 |
file if this is NIL. Will kill any running interpreter if
|
|
1448 |
`inform-interpreter-kill-old-process' is non-NIL. Switches to the |
|
1449 |
interpreter's output buffer if `inform-interpreter-is-graphical' is |
|
1450 |
NIL."
|
|
1451 |
(interactive) |
|
1452 |
(let* ((project-file (inform-project-file)) |
|
1453 |
(story-file-base (if (string-match "\\`[^.]+\\(\\.inf\\'\\)" |
|
1454 |
project-file) |
|
1455 |
(substring project-file 0 (match-beginning 1)) |
|
1456 |
project-file)) |
|
1457 |
(story-file (concat story-file-base |
|
1458 |
(if (string-match "-v8" inform-command-options) |
|
1459 |
".z8"
|
|
1460 |
".z5"))) |
|
1461 |
(name "Inform interpreter")) |
|
1462 |
(if (or (symbolp inform-interpreter-command) |
|
1463 |
(functionp inform-interpreter-command)) |
|
1464 |
;; Emacs interpreter (or custom function)
|
|
1465 |
(funcall inform-interpreter-command story-file) |
|
1466 |
;; inform-interpreter-command is truly a command
|
|
1467 |
(let* ((buffer (get-buffer-create (concat "*" name "*"))) |
|
1468 |
(proc (get-buffer-process buffer))) |
|
1469 |
(and inform-interpreter-kill-old-process |
|
1470 |
proc
|
|
1471 |
(kill-process proc)) |
|
1472 |
(if (or inform-interpreter-is-graphical |
|
1473 |
(eq window-system 'w32)) ; Windows can't handle |
|
1474 |
; term-exec anyway
|
|
1475 |
(progn |
|
1476 |
;; X gets confused if an application is restarted too quickly
|
|
1477 |
;; Assume X if not Win32
|
|
1478 |
(unless (eq window-system 'w32) |
|
1479 |
(message "Waiting for X...") |
|
1480 |
;; On my system 0.1 seconds was enough - double it for safety
|
|
1481 |
(sleep-for 0.2) |
|
1482 |
(message "")) |
|
1483 |
(when (or inform-interpreter-kill-old-process |
|
1484 |
(not proc)) |
|
1485 |
(apply (function start-process) |
|
1486 |
name buffer inform-interpreter-command |
|
1487 |
;; Some shells barf on "empty" arguments
|
|
1488 |
(if (string-equal "" inform-interpreter-options) |
|
1489 |
(list story-file) |
|
1490 |
(list inform-interpreter-options |
|
1491 |
story-file))))) |
|
1492 |
;; Console-mode 'terp
|
|
1493 |
(require 'term) |
|
1494 |
(when (or inform-interpreter-kill-old-process |
|
1495 |
(not proc)) |
|
1496 |
(set-buffer buffer) |
|
1497 |
(term-mode) |
|
1498 |
(erase-buffer) |
|
1499 |
(term-exec buffer name inform-interpreter-command nil |
|
1500 |
(if (string-equal "" inform-interpreter-options) |
|
1501 |
(list story-file) |
|
1502 |
(list inform-interpreter-options |
|
1503 |
story-file))) |
|
1504 |
(term-char-mode) |
|
1505 |
(term-pager-disable)) |
|
1506 |
(switch-to-buffer buffer) |
|
1507 |
(goto-char (point-max))))))) |
|
1508 |
||
1509 |
||
1510 |
||
1511 |
;;;
|
|
1512 |
;;; Spell checking
|
|
1513 |
;;;
|
|
1514 |
||
1515 |
(defun inform-spell-check-buffer () |
|
1516 |
"Spellcheck all strings in the buffer using ispell."
|
|
1517 |
(interactive) |
|
1518 |
(let (start (spell-continue t)) |
|
1519 |
(save-excursion |
|
1520 |
(goto-char (point-min)) |
|
1521 |
(while (and (search-forward "\"" nil t) |
|
1522 |
spell-continue) |
|
1523 |
(if (and (eq (car (inform-syntax-class)) 'string) |
|
1524 |
;; don't spell check include directives etc
|
|
1525 |
(not (save-excursion |
|
1526 |
(forward-line 0) |
|
1527 |
(looking-at inform-directive-regexp)))) |
|
1528 |
(progn |
|
1529 |
(forward-char -1) ; move point to quotation mark |
|
1530 |
(setq start (point)) |
|
1531 |
(forward-sexp) |
|
1532 |
(ispell-region start (point)) |
|
1533 |
;; If user quit out (eg by pressing q while in ispell)
|
|
1534 |
;; don't continue looking for strings to check.
|
|
1535 |
(setq spell-continue |
|
1536 |
(and ispell-process |
|
1537 |
(eq (process-status ispell-process) 'run))))))))) |
|
1538 |
||
1539 |
||
1540 |
||
1541 |
(provide 'inform-mode) |
|
1542 |
||
1543 |
;;; inform-mode.el ends here
|