Merge upstream source for version “1.5.6”.
[debian_inform6-mode.git] / inform-mode.el
blob521f1c50274ce4483c347637d737a769d972ee7a
1 ;;; inform-mode.el --- Inform mode for Emacs
3 ;; Original-Author: Gareth Rees <Gareth.Rees@cl.cam.ac.uk>
4 ;; Maintainer: Rupert Lane <rupert@merguez.demon.co.uk>
5 ;; Created: 1 Dec 1994
6 ;; Version: 1.5.6
7 ;; Released: 17 Dec 2000
8 ;; Keywords: languages
10 ;;; Copyright:
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
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.
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.
26 ;;; Commentary:
28 ;; Inform is a compiler for adventure games by Graham Nelson,
29 ;; available by anonymous FTP from
30 ;; /ftp.gmd.de:/if-archive/programming/inform/
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.
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'.
43 ;; Put this file somewhere on your load-path, and the following code in
44 ;; your .emacs file:
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))
53 ;; To turn on font locking add:
54 ;; (add-hook 'inform-mode-hook 'turn-on-font-lock)
56 ;; Please send any bugs or comments to rupert@merguez.demon.co.uk
58 ;;; Code:
60 (require 'font-lock)
61 (require 'regexp-opt)
64 ;;;
65 ;;; General variables
66 ;;;
68 (defconst inform-mode-version "1.5.6")
70 (defvar inform-maybe-other 'c-mode
71 "*`inform-maybe-mode' runs this if current file is not in Inform mode.")
73 (defvar inform-startup-message t
74 "*Non-nil means display a message when Inform mode is loaded.")
76 (defvar inform-auto-newline t
77 "*Non-nil means automatically newline before and after braces, and after
78 semicolons.
79 If you do not want a leading newline before opening braces then use:
80 \(define-key inform-mode-map \"{\" 'inform-electric-semi\)")
82 (defvar inform-mode-map nil
83 "Keymap for Inform mode.")
85 (if inform-mode-map nil
86 (let ((map (make-sparse-keymap "Inform")))
87 (setq inform-mode-map (make-sparse-keymap))
88 (define-key inform-mode-map "\C-m" 'newline-and-indent)
89 (define-key inform-mode-map "\177" 'backward-delete-char-untabify)
90 (define-key inform-mode-map "\C-c\C-r" 'inform-retagify)
91 (define-key inform-mode-map "\C-c\C-t" 'visit-tags-table)
92 (define-key inform-mode-map "\C-c\C-o" 'inform-convert-old-format)
93 (define-key inform-mode-map "\C-c\C-b" 'inform-build-project)
94 (define-key inform-mode-map "\C-c\C-a" 'inform-toggle-auto-newline)
95 (define-key inform-mode-map "\C-c\C-s" 'inform-spell-check-buffer)
96 (define-key inform-mode-map "\M-n" 'inform-next-object)
97 (define-key inform-mode-map "\M-p" 'inform-prev-object)
98 (define-key inform-mode-map "{" 'inform-electric-brace)
99 (define-key inform-mode-map "}" 'inform-electric-brace)
100 (define-key inform-mode-map "]" 'inform-electric-brace)
101 (define-key inform-mode-map ";" 'inform-electric-semi)
102 (define-key inform-mode-map ":" 'inform-electric-key)
103 (define-key inform-mode-map "!" 'inform-electric-key)
104 (define-key inform-mode-map "," 'inform-electric-comma)
105 (define-key inform-mode-map [menu-bar] (make-sparse-keymap))
106 (define-key inform-mode-map [menu-bar inform] (cons "Inform" map))
107 (define-key map [convert]
108 '("Convert old format" . inform-convert-old-format))
109 (define-key map [separator4] '("--" . nil))
110 (define-key map [inform-spell-check-buffer]
111 '("Spellcheck buffer" . inform-spell-check-buffer))
112 (define-key map [ispell-region] '("Spellcheck region" . ispell-region))
113 (define-key map [ispell-word] '("Spellcheck word" . ispell-word))
114 (define-key map [separator3] '("--" . nil))
115 (define-key map [load-tags] '("Load tags table" . visit-tags-table))
116 (define-key map [retagify] '("Rebuild tags table" . inform-retagify))
117 (define-key map [build] '("Build project" . inform-build-project))
118 (define-key map [separator2] '("--" . nil))
119 (define-key map [next-object] '("Next object" . inform-next-object))
120 (define-key map [prev-object] '("Previous object" . inform-prev-object))
121 (define-key map [separator1] '("--" . nil))
122 (define-key map [comment-region] '("Comment Out Region" . comment-region))
123 (put 'comment-region 'menu-enable 'mark-active)
124 (define-key map [indent-region] '("Indent Region" . indent-region))
125 (put 'indent-region 'menu-enable 'mark-active)
126 (define-key map [indent-line] '("Indent Line" . indent-for-tab-command))))
128 (defvar inform-mode-abbrev-table nil
129 "Abbrev table used while in Inform mode.")
131 (define-abbrev-table 'inform-mode-abbrev-table nil)
133 (defvar inform-project-file nil
134 "*The top-level Inform project file to which the current file belongs.")
135 (make-variable-buffer-local 'inform-project-file)
137 (defvar inform-autoload-tags t
138 "*Non-nil means automatically load tags table when entering Inform mode.")
140 (defvar inform-etags-program "etags"
141 "The shell command with which to run the etags program.")
143 (defvar inform-command "inform"
144 "*The shell command with which to run the Inform compiler.")
146 (defvar inform-libraries-directory nil
147 "*If non-NIL, gives the directory in which libraries are found.")
149 (defvar inform-command-options ""
150 "*Options with which to call the Inform compiler.")
154 ;;; Indentation parameters
157 (defvar inform-indent-property 8
158 "*Indentation of the start of a property declaration.")
160 (defvar inform-indent-has-with-class 1
161 "*Indentation of has/with/class lines in object declarations.")
163 (defvar inform-indent-level 4
164 "*Indentation of lines of block relative to first line of block.")
166 (defvar inform-indent-label-offset -3
167 "*Indentation of label relative to where it should be.")
169 (defvar inform-indent-cont-statement 4
170 "*Indentation of continuation relative to start of statement.")
172 (defvar inform-indent-fixup-space t
173 "*If non-NIL, fix up space in object declarations.")
175 (defvar inform-indent-action-column 40
176 "*Column at which action names should be placed in verb declarations.")
178 (defvar inform-comments-line-up-p nil
179 "*If non-nil, comments spread over several lines will line up with the first.")
181 (defvar inform-strings-line-up-p nil
182 "*Variable controlling indentation of multi-line strings.
183 If nil (default), string will be indented according to context.
184 If a number, will always set the indentation to that column.
185 If 'char', will line up with the first character of the string.
186 If 'quote', or other non-nil value, will line up with open quote on
187 first line.")
190 ;;; Syntax variables
193 (defvar inform-mode-syntax-table nil
194 "Syntax table to use in Inform mode buffers.")
196 (if inform-mode-syntax-table
198 (setq inform-mode-syntax-table (make-syntax-table))
199 (modify-syntax-entry ?\\ "\\" inform-mode-syntax-table)
200 (modify-syntax-entry ?\n ">" inform-mode-syntax-table)
201 (modify-syntax-entry ?! "<" inform-mode-syntax-table)
202 (modify-syntax-entry ?# "_" inform-mode-syntax-table)
203 (modify-syntax-entry ?% "." inform-mode-syntax-table)
204 (modify-syntax-entry ?& "." inform-mode-syntax-table)
205 (modify-syntax-entry ?\' "." inform-mode-syntax-table)
206 (modify-syntax-entry ?* "." inform-mode-syntax-table)
207 (modify-syntax-entry ?- "." inform-mode-syntax-table)
208 (modify-syntax-entry ?/ "." inform-mode-syntax-table)
209 (modify-syntax-entry ?\; "." inform-mode-syntax-table)
210 (modify-syntax-entry ?< "." inform-mode-syntax-table)
211 (modify-syntax-entry ?= "." inform-mode-syntax-table)
212 (modify-syntax-entry ?> "." inform-mode-syntax-table)
213 (modify-syntax-entry ?+ "." inform-mode-syntax-table)
214 (modify-syntax-entry ?| "." inform-mode-syntax-table)
215 (modify-syntax-entry ?^ "w" inform-mode-syntax-table))
218 ;;; Keyword definitions-------------------------------------------------------
220 ;; These are used for syntax and font-lock purposes.
221 ;; They combine words used in Inform 5 and Inform 6 for full compatability.
222 ;; You can add new keywords directly to this list as the regexps for
223 ;; font-locking are defined when this file is byte-compiled or eval'd.
225 (eval-and-compile
226 (defvar inform-directive-list
227 '("abbreviate" "array" "attribute" "btrace" "class" "constant"
228 "default" "dictionary" "end" "endif" "etrace" "extend" "fake_action"
229 "global" "ifdef" "ifndef" "iftrue" "iffalse" "ifv3" "ifv5" "import"
230 "include" "link" "listsymbols" "listdict" "listverbs" "lowstring"
231 "ltrace" "message" "nearby" "nobtrace" "noetrace" "noltrace" "notrace"
232 "object" "property" "release" "replace" "serial" "statusline" "stub"
233 "switches" "system_file" "trace" "verb" "zcharacter")
234 "List of Inform directives that shouldn't appear embedded in code.")
236 (defvar inform-defining-list
237 '("[" "array" "attribute" "class" "constant" "fake_action" "global"
238 "lowstring" "nearby" "object" "property")
239 "List of Inform directives that define a variable/constant name.
240 Used to build a font-lock regexp; the name defined must follow the
241 keyword.")
243 (defvar inform-attribute-list
244 '("absent" "animate" "clothing" "concealed" "container" "door"
245 "edible" "enterable" "female" "general" "light" "lockable" "locked"
246 "male" "moved" "neuter" "on" "open" "openable" "pluralname" "proper"
247 "scenery" "scored" "static" "supporter" "switchable" "talkable"
248 "transparent" "visited" "workflag" "worn")
249 "List of Inform attributes defined in the library.")
251 (defvar inform-property-list
252 '("n_to" "s_to" "e_to" "w_to" "ne_to" "se_to" "nw_to" "sw_to" "u_to"
253 "d_to" "in_to" "out_to" "add_to_scope" "after" "article" "articles"
254 "before" "cant_go" "capacity" "daemon" "describe" "description"
255 "door_dir" "door_to" "each_turn" "found_in" "grammar" "initial"
256 "inside_description" "invent" "life" "list_together" "name" "number"
257 "orders" "parse_name" "plural" "react_after" "react_before"
258 "short_name" "time_left" "time_out" "when_closed" "when_open"
259 "when_on" "when_off" "with_key")
260 "List of Inform properties defined in the library.")
262 (defvar inform-code-keyword-list
263 '("box" "break" "continue" "do" "else" "font off" "font on" "for"
264 "give" "has" "hasnt" "if" "inversion" "jump" "move" "new_line" "notin"
265 "objectloop" "ofclass" "print" "print_ret" "quit" "read" "remove"
266 "restore" "return" "rfalse" "rtrue" "save" "spaces" "string"
267 "style bold" "style fixed" "style reverse" "style roman" "style underline"
268 "switch" "to" "until" "while")
269 "List of Inform code keywords.")
272 ;; Some regular expressions are needed at compile-time too so as to
273 ;; avoid postponing the work to load time.
275 ;; To do the work of building the regexps we use regexp-opt, which has
276 ;; different behaviour on XEmacs and GNU Emacs and may not even be
277 ;; available on ancient versions
278 (eval-and-compile
279 (defun inform-make-regexp (strings &optional paren shy)
280 (cond
281 ((string-match "XEmacs\\|Lucid" emacs-version)
282 ;; XEmacs
283 (regexp-opt strings paren shy))
285 ;; GNU Emacs
286 (regexp-opt strings)))))
288 (eval-and-compile
289 (defvar inform-directive-regexp
290 (concat "\\<#?\\("
291 (inform-make-regexp inform-directive-list)
292 "\\)\\>")
293 "Regular expression matching an Inform directive.")
295 (defvar inform-object-regexp
296 "#?\\<\\(object\\|nearby\\|class\\)\\>"
297 "Regular expression matching start of object declaration.")
299 (defvar inform-property-regexp
300 (concat "\\s-*\\("
301 (inform-make-regexp inform-property-list)
302 "\\)")
303 "Regular expression matching Inform properties."))
306 (defvar inform-real-object-regexp
307 (eval-when-compile (concat "^" inform-object-regexp))
308 "Regular expression matching the start of a real object declaration.
309 That is, one found at the start of a line.")
311 (defvar inform-label-regexp "[^]:\"!\(\n]+\\(:\\|,\\)"
312 "Regular expression matching a label.")
314 (defvar inform-action-regexp "\\s-*\\*"
315 "Regular expression matching an action line in a verb declaration.")
317 (defvar inform-statement-terminators '(?\; ?{ ?} ?: ?\) do else)
318 "Tokens which precede the beginning of a statement.")
322 ;;; Font-lock keywords
325 (defvar inform-font-lock-defaults
326 '(inform-font-lock-keywords nil t ((?_ . "w") (?' . "$")) inform-prev-object)
327 "Font Lock defaults for Inform mode.")
329 (defface inform-dictionary-word-face
330 '((((class color) (background light)) (:foreground "Red"))
331 (((class color) (background dark)) (:foreground "Pink"))
332 (t (:italic t :bold t)))
333 "Font lock mode face used to highlight dictionary words.")
335 (defvar inform-dictionary-word-face 'inform-dictionary-word-face
336 "Variable for Font lock mode face used to highlight dictionary words.")
338 (defvar inform-font-lock-keywords
339 (eval-when-compile
340 (list
342 ;; Inform code keywords
343 (cons (concat "\\s-+\\("
344 (inform-make-regexp inform-code-keyword-list)
345 "\\)\\(\\s-\\|$\\|;\\)")
346 'font-lock-keyword-face)
348 ;; Keywords that declare variable or constant names.
349 (list (concat "^#?\\("
350 (inform-make-regexp inform-defining-list nil t)
351 "\\)\\s-+\\(->\\s-+\\)*\\(\\(\\w\\|\\s_\\)+\\)")
352 '(1 font-lock-keyword-face)
353 '(5 font-lock-function-name-face))
355 ;; Other directives.
356 (cons inform-directive-regexp 'font-lock-keyword-face)
358 ;; Single quoted strings, length > 1, are dictionary words
359 '("'\\(\\(-\\|\\w\\)\\(\\(-\\|\\w\\)+\\(//\\w*\\)?\\|//\\w*\\)\\)'"
360 (1 inform-dictionary-word-face append))
362 ;; Also quoted words after "name" property
363 '("\\s-name\\s-+"
364 ("\"\\(\\(-\\|\\w\\)+\\)\"" nil nil
365 (1 inform-dictionary-word-face t)))
367 ;; `private', `class', `has' and `with' in objects.
368 '("^\\s-+\\(private\\|class\\|has\\|with\\)\\(\\s-\\|$\\)"
369 (1 font-lock-keyword-face))
371 ;; Attributes and properties.
372 (cons (concat "[^#]\\<\\("
373 (inform-make-regexp (append inform-attribute-list
374 inform-property-list))
375 "\\)\\>")
376 '(1 font-lock-variable-name-face))))
377 "Expressions to fontify in Inform mode.")
381 ;;; Inform mode
384 (defun inform-mode ()
385 "Major mode for editing Inform programs.
387 * Inform syntax:
389 Type \\[indent-for-tab-command] to indent the current line.
390 Type \\[indent-region] to indent the region.
392 Type \\[fill-paragraph] to fill strings or comments.
393 This compresses multiple spaces into single spaces.
395 * Multi-file projects:
397 The variable `inform-project-file' gives the name of the root file of
398 the project \(i.e., the one that you run Inform on\)\; it is best to
399 set this as a local variable in each file, for example by making
400 ! -*- inform-project-file:\"game.inf\" -*-
401 the first line of the file.
403 * Tags tables:
405 Type \\[inform-retagify] to build \(and load\) a Tags table.
406 Type \\[visit-tags-table] to load an existing Tags table.
407 If it exists, and if the variable `inform-autoload-tags' is non-NIL,
408 the Tags table is loaded on entry to Inform Mode.
409 With a Tags table loaded, type \\[find-tag] to find the declaration of
410 the object, class or function under point.
412 * Navigating in a file:
414 Type \\[inform-prev-object] to go to the previous object/class declaration.
415 Type \\[inform-next-object] to go to the next one.
417 * Compilation:
419 Type \\[inform-build-project] to build the current project.
420 Type \\[next-error] to go to the next error.
422 * Font-lock support:
424 Put \(add-hook 'inform-mode-hook 'turn-on-font-lock) in your .emacs.
426 * Spell checking:
428 Type \\[inform-spell-check-buffer] to spell check all strings in the buffer.
429 Type \\[ispell-word] to check the single word at point.
431 * Old versions of Inform Mode:
433 Versions of Inform Mode prior to 0.5 used tab stops every 4 characters
434 to control the formatting. This was the Wrong Thing To Do.
435 Type \\[inform-convert-old-format] to undo the broken formatting.
437 * Key definitions:
439 \\{inform-mode-map}
440 * Functions:
442 inform-maybe-mode
443 Looks at the contents of a file, guesses whether it is an Inform
444 program, runs `inform-mode' if so, or `inform-maybe-other' if not.
445 The latter defaults to `c-mode'. Used for header files which might
446 be Inform or C programs.
448 * Miscellaneous user options:
450 inform-startup-message
451 Set to nil to inhibit message first time Inform mode is used.
453 inform-maybe-other
454 The mode used by `inform-maybe-mode' if it guesses that the file is
455 not an Inform program.
457 inform-mode-hook
458 This hook is run after entry to Inform Mode.
460 inform-autoload-tags
461 If non-nil, then a tags table will automatically be loaded when
462 entering Inform mode.
464 inform-auto-newline
465 If non-nil, then newlines are automatically inserted before and
466 after braces, and after semicolons in Inform code, and after commas
467 in object declarations.
469 * User options controlling indentation style:
471 Values in parentheses are the default indentation style.
473 inform-indent-property \(8\)
474 Indentation of a property or attribute in an object declaration.
476 inform-indent-has-with-class \(1\)
477 Indentation of has/with/class/private lines in object declaration.
479 inform-indent-level \(4\)
480 Indentation of line of code in a block relative to the first line of
481 the block.
483 inform-indent-label-offset \(-3\)
484 Indentation of a line starting with a label, relative to the
485 indentation if the label were absent.
487 inform-indent-cont-statement \(4\)
488 Indentation of second and subsequent lines of a statement, relative
489 to the first.
491 inform-indent-fixup-space \(T\)
492 If non-NIL, fix up space after `Object', `Class', `Nearby', `has',
493 `private' and `with', so that all the object's properties line up.
495 inform-indent-action-column \(40\)
496 Column at which action names should be placed in verb declarations.
497 If NIL, then action names are not moved.
499 inform-comments-line-up-p \(NIL\)
500 If non-NIL, comments spread out over several lines will start on the
501 same column as the first comment line.
503 inform-strings-line-up-p \(NIL\)
504 Variable controlling indentation of multi-line strings.
505 If nil (default), string will be indented according to context.
506 If a number, will always set the indentation to that column.
507 If 'char', will line up with the first character of the string.
508 If 'quote', or other non-nil value, will line up with open quote on
509 first line.
511 * User options to do with compilation:
513 inform-command
514 The shell command with which to run the Inform compiler.
516 inform-libraries-directory
517 If non-NIL, gives the directory in which the Inform libraries are
518 found.
520 inform-command-options
521 Additional options with which to call the Inform compiler.
523 * Please send any bugs or comments to rupert@merguez.demon.co.uk
526 (interactive)
527 (if inform-startup-message
528 (message "Emacs Inform mode version %s." inform-mode-version))
529 (kill-all-local-variables)
530 (use-local-map inform-mode-map)
531 (set-syntax-table inform-mode-syntax-table)
532 (make-local-variable 'comment-column)
533 (make-local-variable 'comment-end)
534 (make-local-variable 'comment-indent-function)
535 (make-local-variable 'comment-start)
536 (make-local-variable 'comment-start-skip)
537 (make-local-variable 'fill-paragraph-function)
538 (make-local-variable 'font-lock-defaults)
539 (make-local-variable 'imenu-extract-index-name-function)
540 (make-local-variable 'imenu-prev-index-position-function)
541 (make-local-variable 'indent-line-function)
542 (make-local-variable 'indent-region-function)
543 (make-local-variable 'parse-sexp-ignore-comments)
544 (make-local-variable 'require-final-newline)
545 (setq comment-column 40
546 comment-end ""
547 comment-indent-function 'inform-comment-indent
548 comment-start "!"
549 comment-start-skip "!+\\s-*"
550 fill-paragraph-function 'inform-fill-paragraph
551 font-lock-defaults inform-font-lock-defaults
552 imenu-extract-index-name-function 'inform-imenu-extract-name
553 imenu-prev-index-position-function 'inform-prev-object
554 indent-line-function 'inform-indent-line
555 indent-region-function 'inform-indent-region
556 inform-startup-message nil
557 local-abbrev-table inform-mode-abbrev-table
558 major-mode 'inform-mode
559 mode-name "Inform"
560 parse-sexp-ignore-comments t
561 require-final-newline t)
562 (auto-fill-mode 1)
563 (if inform-autoload-tags
564 (inform-auto-load-tags-table))
565 (run-hooks 'inform-mode-hook))
567 (defun inform-maybe-mode ()
568 "Starts Inform mode if file is in Inform; `inform-maybe-other' otherwise."
569 (let ((case-fold-search t))
570 (if (save-excursion
571 (re-search-forward
572 "^\\(!\\|object\\|nearby\\|\\[ \\)"
573 nil t))
574 (inform-mode)
575 (funcall inform-maybe-other))))
579 ;;; Syntax and indentation
582 ;; Go to the start of the current Inform definition. Just goes to the
583 ;; most recent line with a function beginning [, or a directive.
585 (defun inform-beginning-of-defun ()
586 (let ((case-fold-search t))
587 (catch 'found
588 (end-of-line 1)
589 (while (re-search-backward "\n[[a-z]" nil 'move)
590 (forward-char 1)
591 (if (or (and (looking-at "\\[")
592 (eq (inform-preceding-char) ?\;))
593 (looking-at inform-directive-regexp))
594 (throw 'found nil))
595 (forward-char -1)))))
597 ;; Returns preceding non-blank, non-comment character in buffer. It is
598 ;; assumed that point is not inside a string or comment.
600 (defun inform-preceding-char ()
601 (save-excursion
602 (while (/= (point) (progn (forward-comment -1) (point))))
603 (skip-syntax-backward " ")
604 (if (bobp) ?\;
605 (preceding-char))))
607 ;; Returns preceding non-blank, non-comment token in buffer, either the
608 ;; character itself, or the tokens 'do or 'else. It is assumed that
609 ;; point is not inside a string or comment.
611 (defun inform-preceding-token ()
612 (save-excursion
613 (while (/= (point) (progn (forward-comment -1) (point))))
614 (skip-syntax-backward " ")
615 (if (bobp) ?\;
616 (let ((p (preceding-char)))
617 (cond ((and (eq p ?o)
618 (>= (- (point) 2) (point-min)))
619 (goto-char (- (point) 2))
620 (if (looking-at "\\<do") 'do p))
621 ((and (eq p ?e)
622 (>= (- (point) 4) (point-min)))
623 (goto-char (- (point) 4))
624 (if (looking-at "\\<else") 'else p))
625 (t p))))))
627 ;; `inform-syntax-class' returns a list describing the syntax at point.
629 ;; Optional argument DEFUN-START gives the point from which parsing
630 ;; should start, and DATA is the list returned by a previous invocation
631 ;; of `inform-syntax-class'.
633 ;; The returned list is of the form (SYNTAX IN-OBJ SEXPS STATE).
634 ;; SYNTAX is one of
636 ;; directive An Inform directive (given by `inform-directive-list')
637 ;; has The "has" keyword
638 ;; with The "with" keyword
639 ;; class The "class" keyword
640 ;; private The "private" keyword
641 ;; property A property or attribute
642 ;; other Any other line not in a function body
643 ;; string The line begins inside a string
644 ;; comment The line starts with a comment
645 ;; label Line contains a label (i.e. has a colon in it)
646 ;; code Any other line inside a function body
647 ;; blank A blank line
648 ;; action An action line in a verb declaration
650 ;; IN-OBJ is non-NIL if the line appears to be inside an Object, Nearby,
651 ;; or Class declaration.
653 ;; SEXPS is a list of pairs (D . P) where P is the start of a sexp
654 ;; containing point and D is its nesting depth. The pairs are in
655 ;; descreasing order of nesting depth.
657 ;; STATE is the list returned by `parse-partial-sexp'.
659 ;; For reasons of speed, `inform-syntax-class' looks for directives only
660 ;; at the start of lines. If the source contains top-level directives
661 ;; not at the start of lines, or anything else at the start of a line
662 ;; that might be mistaken for a directive, the wrong syntax class may be
663 ;; returned.
665 ;; There are circumstances in which SEXPS might not be complete (namely
666 ;; if there were multiple opening brackets and some but not all have
667 ;; been closed since the last call to `inform-syntax-class'), and rare
668 ;; circumstances in which it might be wrong (namely if there are
669 ;; multiple closing brackets and fewer, but at least two, opening
670 ;; bracket since the last call). I consider these cases not worth
671 ;; worrying about - and the speed hit of checking for them is
672 ;; considerable.
674 (defun inform-syntax-class (&optional defun-start data)
675 (let ((line-start (point))
676 in-obj state
677 (case-fold-search t))
678 (save-excursion
679 (cond (defun-start
680 (setq state (parse-partial-sexp defun-start line-start nil nil
681 (nth 3 data)))
682 (setq in-obj
683 (cond ((or (> (car state) 0) (nth 3 state) (nth 4 state))
684 (nth 1 data))
685 ((nth 1 data) (/= (inform-preceding-char) ?\;))
686 (t (looking-at inform-object-regexp)))))
688 (inform-beginning-of-defun)
689 (setq in-obj (looking-at inform-object-regexp)
690 state (parse-partial-sexp (point) line-start)))))
692 (list
693 (if (> (car state) 0)
694 ;; If there's a containing sexp then it's easy.
695 (cond ((nth 3 state) 'string)
696 ((nth 4 state) 'comment)
697 ((looking-at (concat "\\s-*" comment-start)) 'comment)
698 ((looking-at inform-label-regexp) 'label)
699 (t 'code))
701 ;; Otherwise there are a bunch of special cases (has, with, class,
702 ;; and private properties) that must be checked for. Note that
703 ;; we have to distinguish between global class declarations and
704 ;; class membership in an object declaration. This is done by
705 ;; looking for a preceding semicolon.
706 (cond ((nth 3 state) 'string)
707 ((nth 4 state) 'comment)
708 ((looking-at (concat "\\s-*" comment-start)) 'comment)
709 ((and in-obj (looking-at "\\s-*class\\>")
710 (/= (inform-preceding-char) ?\;))
711 'class)
712 ((looking-at inform-action-regexp) 'action)
713 ((looking-at inform-directive-regexp) 'directive)
714 ((and (looking-at "\\[") (eq (inform-preceding-char) ?\;))
715 'directive)
716 ((and (not in-obj) (eq (inform-preceding-char) ?\;))
717 'directive)
718 ((looking-at "\\s-*$") 'blank)
719 ((not in-obj) 'other)
720 ((looking-at "\\s-*has\\(\\s-\\|$\\)") 'has)
721 ((looking-at "\\s-*with\\(\\s-\\|$\\)") 'with)
722 ((looking-at "\\s-*private\\(\\s-\\|$\\)") 'private)
723 ((or (eq (inform-preceding-char) ?,)
724 (looking-at inform-property-regexp))
725 'property)
726 ;; This handles declarations of objects in a class eg
727 ;; Bird "swallow";
728 ;; It assumes that class names follow the convention of being
729 ;; capitalised. This is not the most elegent way of handling
730 ;; this case but in practice works well.
731 ((looking-at "\\s-*[A-Z]")
732 'directive)
734 'other)))
736 ;; Are we in an object?
737 (if (and in-obj
738 (not (looking-at inform-object-regexp))
739 (zerop (car state))
740 (eq (inform-preceding-char) ?\;))
742 in-obj)
744 ;; List of known enclosing sexps.
745 (let ((sexps (nth 2 data)) ; the old list of sexps
746 (depth (car state)) ; current nesting depth
747 (sexp-start (nth 1 state))) ; enclosing sexp, if any
748 (if sexps
749 ;; Strip away closed sexps.
750 (let ((sexp-depth (car (car sexps))))
751 (while (and sexps (or (> sexp-depth depth)
752 (and (eq sexp-depth depth)
753 sexp-start)))
754 (setq sexps (cdr sexps)
755 sexp-depth (if sexps (car (car sexps)))))))
756 (if sexp-start
757 (setq sexps (cons (cons depth sexp-start) sexps)))
758 sexps)
760 ;; State from the parse algorithm.
761 state)))
763 ;; Returns the correct indentation for the line at point. DATA is the
764 ;; syntax class for the start of the line (as returned by
765 ;; `inform-syntax-class'). It is assumed that point is somewhere in the
766 ;; indentation for the current line (i.e., everything to the left is
767 ;; whitespace).
769 (defun inform-calculate-indentation (data)
770 (let ((syntax (car data)) ; syntax class of start of line
771 (in-obj (nth 1 data)) ; inside an object?
772 (depth (car (nth 3 data))) ; depth of nesting of start of line
773 (case-fold-search t)) ; searches are case-insensitive
774 (cond
776 ;; Directives should never be indented or else the directive-
777 ;; finding code won't run fast enough. Hence the magic
778 ;; constant 0.
779 ((eq syntax 'directive) 0)
780 ((eq syntax 'blank) 0)
782 ;; Various standard indentations.
783 ((eq syntax 'property) inform-indent-property)
784 ((eq syntax 'other)
785 (cond ((looking-at "\\s-*\\[") inform-indent-property)
786 (in-obj (+ inform-indent-property inform-indent-level))
787 (t inform-indent-level)))
788 ((and (eq syntax 'string) (zerop depth))
789 (cond (in-obj (+ inform-indent-property inform-indent-level))
790 (t inform-indent-level)))
791 ((and (eq syntax 'comment) (zerop depth))
792 (inform-line-up-comment
793 (if in-obj inform-indent-property 0)))
794 ((eq syntax 'action) inform-indent-level)
795 ((memq syntax '(has with class private)) inform-indent-has-with-class)
797 ;; We are inside a sexp of some sort.
799 (let ((indent 0) ; calculated indent column
800 paren ; where the enclosing sexp begins
801 string-start ; where string (if any) starts
802 (string-indent 0) ; indentation for the current str
803 cont-p ; true if line is a continuation
804 paren-char ; the parenthesis character
805 prec-token ; token preceding line
806 this-char) ; character under consideration
807 (save-excursion
809 ;; Skip back to the start of a string, if any. (Note that
810 ;; we can't be in a comment since the syntax class applies
811 ;; to the start of the line.)
812 (if (eq syntax 'string)
813 (progn
814 (skip-syntax-backward "^\"")
815 (forward-char -1)
816 (setq string-start (point))
817 (setq string-indent (current-column))
820 ;; Now find the start of the sexp containing point. Most
821 ;; likely, the location was found by `inform-syntax-class';
822 ;; if not, call `up-list' now and save the result in case
823 ;; it's useful in future.
824 (save-excursion
825 (let ((sexps (nth 2 data)))
826 (if (and sexps (eq (car (car sexps)) depth))
827 (goto-char (cdr (car sexps)))
828 (up-list -1)
829 (setcar (nthcdr 2 data)
830 (cons (cons depth (point)) (nth 2 data)))))
831 (setq paren (point)
832 paren-char (following-char)))
834 ;; If we were in a string, now skip back to the start of the
835 ;; line. We have to do this *after* calling `up-list' just
836 ;; in case there was an opening parenthesis on the line
837 ;; including the start of the string.
838 (if (eq syntax 'string)
839 (forward-line 0))
841 ;; The indentation depends on what kind of sexp we are in.
842 ;; If line is in parentheses, indent to opening parenthesis.
843 (if (eq paren-char ?\()
844 (setq indent (progn (goto-char paren) (1+ (current-column))))
846 ;; Line not in parentheses.
847 (setq prec-token (inform-preceding-token)
848 this-char (following-char))
849 (cond
851 ;; Each 'else' should have the same indentation as the
852 ;; matching 'if'
853 ((looking-at "\\s-*else")
854 ;; Find the matching 'if' by counting 'if's and 'else's
855 ;; in this sexp
856 (let ((if-count 0) found)
857 (while (and (not found)
858 (progn (forward-sexp -1) t) ; skip over sub-sexps
859 (re-search-backward "\\s-*\\(else\\|if\\)"
860 paren t))
861 (setq if-count (+ if-count
862 (if (string= (match-string 1) "else")
863 -1 1)))
864 (if (eq if-count 1) (setq found t)))
865 (if (not found)
866 (setq indent 0)
867 (forward-line 0)
868 (skip-syntax-forward " ")
869 (setq indent (current-column)))))
871 ;; Line is an inlined directive-- always put on column 0
872 ((looking-at "\\s-*#[^#]")
873 (setq indent 0))
875 ;; Line is in an implicit block: take indentation from
876 ;; the line that introduces the block, plus one level.
877 ((memq prec-token '(?\) do else))
878 (forward-sexp -1)
879 (forward-line 0)
880 (skip-syntax-forward " ")
881 (setq indent
882 (+ (current-column)
883 (if (eq this-char ?{) 0 inform-indent-level))))
885 ;; Line is a continued statement.
886 ((not (or (memq prec-token inform-statement-terminators)
887 (eq syntax 'label)))
888 (setq cont-p t)
889 (forward-line -1)
890 (let ((token (inform-preceding-token)))
891 ;; Is it the first continuation line?
892 (if (memq token inform-statement-terminators)
893 (setq indent inform-indent-cont-statement)))
894 (skip-syntax-forward " ")
895 (setq indent (+ indent (current-column))))
897 ;; Line is in a function, take indentation from start of
898 ;; function, ignoring `with'.
899 ((eq paren-char ?\[)
900 (goto-char paren)
901 (forward-line 0)
902 (looking-at "\\(\\s-*with\\s-\\)?\\s-*")
903 (goto-char (match-end 0))
904 (setq indent
905 (+ (current-column)
906 (if (eq this-char ?\]) 0 inform-indent-level))))
908 ;; Line is in a block: take indentation from block.
910 (goto-char paren)
911 (if (eq (inform-preceding-char) ?\))
912 (forward-sexp -1))
913 (forward-line 0)
914 (skip-syntax-forward " ")
916 (setq indent
917 (+ (current-column)
918 (if (memq this-char '(?} ?{))
920 inform-indent-level)))
923 ;; We calculated the indentation for the start of the
924 ;; string; correct this for the remainder of the string if
925 ;; appropriate.
926 (cond
927 ((eq syntax 'string)
928 ;; do conditional line-up
929 (cond
930 ((numberp inform-strings-line-up-p)
931 (setq indent inform-strings-line-up-p))
932 ((eq inform-strings-line-up-p 'char)
933 (setq indent (1+ string-indent)))
934 (inform-strings-line-up-p
935 (setq indent string-indent))
936 ((not cont-p)
937 (goto-char string-start)
938 (let ((token (inform-preceding-token)))
939 (if (not (memq token inform-statement-terminators))
940 (setq indent
941 (+ indent inform-indent-cont-statement)))))))
943 ;; Indent for label, if any.
944 ((eq syntax 'label)
945 (setq indent (+ indent inform-indent-label-offset))))))
947 ;; Handle comments specially if told to line them up
948 (if (looking-at (concat "\\s-*" comment-start))
949 (setq indent (inform-line-up-comment indent)))
951 indent)))))
954 (defun inform-line-up-comment (current-indent)
955 "Return the indentation to line up this comment with the previous one.
956 If inform-comments-line-up-p is nil, or the preceeding lines do not contain
957 comments, return CURRENT-INDENT."
958 (if inform-comments-line-up-p
959 (save-excursion
960 (let ((indent current-indent)
961 done limit)
962 (while (and (not done)
963 (> (point) 1))
964 (forward-line -1)
965 (setq limit (point))
966 (cond ((looking-at (concat "\\s-*" comment-start))
967 ;; a full-line comment, keep searching
968 nil)
969 ((and
970 (or (end-of-line) t)
971 (re-search-backward comment-start limit t)
972 (eq (car (inform-syntax-class)) 'comment))
973 ;; a line with a comment char at the end
974 ;; that is not part of the code
975 (setq indent (current-column))
976 (setq done t))
978 ;; a non-comment line so we do not need to change
979 (setq done t))))
980 indent))
981 current-indent))
984 ;; Modifies whitespace to the left of point so that the character after
985 ;; point is at COLUMN. If this is impossible, one whitespace character
986 ;; is left. Avoids changing buffer gratuitously, and returns non-NIL if
987 ;; it actually changed the buffer. If a change is made, point is moved
988 ;; to the end of any inserted or deleted whitespace. (If not, it may be
989 ;; moved at random.)
991 (defun inform-indent-to (column)
992 (let ((col (current-column)))
993 (cond ((eq col column) nil)
994 ((< col column) (indent-to column) t)
995 (t (let ((p (point))
996 (mincol (progn (skip-syntax-backward " ")
997 (current-column))))
998 (if (eq mincol (1- col))
1000 (delete-region (point) p)
1001 (indent-to (max (if (bolp) mincol (1+ mincol)) column))
1002 t))))))
1004 ;; Indent the line containing point; DATA is assumed to have been
1005 ;; returned from `inform-syntax-class', called at the *start* of the
1006 ;; current line. It is assumed that point is at the start of the line.
1007 ;; Fixes up the spacing on `has', `with', `object', `nearby', `private'
1008 ;; and `class' lines. Returns T if a change was made, NIL otherwise.
1009 ;; Moves point.
1011 (defun inform-do-indent-line (data)
1012 (skip-syntax-forward " ")
1013 (let ((changed-p (inform-indent-to (inform-calculate-indentation data)))
1014 (syntax (car data)))
1016 ;; Fix up space if appropriate, return changed flag.
1018 (cond
1019 ((and (memq syntax '(directive has with class private))
1020 inform-indent-fixup-space
1021 (looking-at
1022 "\\(object\\|class\\|nearby\\|has\\|with\\|private\\)\\(\\s-+\\|$\\)"))
1023 (goto-char (match-end 0))
1024 (inform-indent-to inform-indent-property))
1025 ((and (eq syntax 'action)
1026 inform-indent-action-column
1027 (or (looking-at "\\*.*\\(->\\)")
1028 (looking-at "\\*.*\\($\\)")))
1029 (goto-char (match-beginning 1))
1030 (inform-indent-to inform-indent-action-column))
1031 (t nil))
1032 changed-p)))
1034 ;; Calculate and return the indentation for a comment (assume point is
1035 ;; on the comment).
1037 (defun inform-comment-indent ()
1038 (skip-syntax-backward " ")
1039 (if (bolp)
1040 (inform-calculate-indentation (inform-syntax-class))
1041 (max (1+ (current-column)) comment-column)))
1043 ;; Indent line containing point. If the indentation changes or if point
1044 ;; is before the first non-whitespace character on the line,
1045 ;; move point to indentation.
1047 (defun inform-indent-line ()
1048 (let ((old-point (point)))
1049 (forward-line 0)
1050 (or (inform-do-indent-line (inform-syntax-class))
1051 (< old-point (point))
1052 (goto-char old-point))))
1054 ;; Indent all the lines in region.
1056 (defun inform-indent-region (start end)
1057 (save-restriction
1058 (let ((endline (progn (goto-char (max end start))
1059 (or (bolp) (end-of-line))
1060 (point)))
1061 data linestart)
1062 (narrow-to-region (point-min) endline)
1063 (goto-char (min start end))
1064 (forward-line 0)
1065 (while (not (eobp))
1066 (setq data (if data (inform-syntax-class linestart data)
1067 (inform-syntax-class))
1068 linestart (point))
1069 (inform-do-indent-line data)
1070 (forward-line 1)))))
1074 ;;; Filling paragraphs
1077 ;; Fill quoted string or comment containing point. To fill a quoted
1078 ;; string, point must be between the quotes. Deals appropriately with
1079 ;; trailing backslashes.
1081 (defun inform-fill-paragraph (&optional arg)
1082 (let* ((data (inform-syntax-class))
1083 (syntax (car data))
1084 (case-fold-search t))
1085 (cond ((eq syntax 'comment)
1086 (if (save-excursion
1087 (forward-line 0)
1088 (looking-at "\\s-*!+\\s-*"))
1089 (let ((fill-prefix (match-string 0)))
1090 (fill-paragraph nil)
1092 (error "Can't fill comments not at start of line.")))
1093 ((eq syntax 'string)
1094 (save-excursion
1095 (let* ((indent-col (prog2
1096 (insert ?\n)
1097 (inform-calculate-indentation data)
1098 (delete-backward-char 1)))
1099 (start (search-backward "\""))
1100 (end (search-forward "\"" nil nil 2))
1101 (fill-column (- fill-column 2))
1102 linebeg)
1103 (save-restriction
1104 (narrow-to-region (point-min) end)
1106 ;; Fold all the lines together, removing backslashes
1107 ;; and multiple spaces as we go.
1108 (subst-char-in-region start end ?\n ? )
1109 (subst-char-in-region start end ?\\ ? )
1110 (subst-char-in-region start end ?\t ? )
1111 (goto-char start)
1112 (while (re-search-forward " +" end t)
1113 (delete-region (match-beginning 0) (1- (match-end 0))))
1115 ;; Split this line; reindent after first split,
1116 ;; otherwise indent to point where first split ended
1117 ;; up.
1118 (goto-char start)
1119 (setq linebeg start)
1120 (while (not (eobp))
1121 (move-to-column (1+ fill-column))
1122 (if (eobp)
1124 (skip-chars-backward "^ " linebeg)
1125 (if (eq (point) linebeg)
1126 (progn
1127 (skip-chars-forward "^ ")
1128 (skip-chars-forward " ")))
1129 (insert "\n")
1130 (indent-to-column indent-col 1)
1131 (setq linebeg (point))))))
1133 ;; Return T so that `fill-paragaph' doesn't try anything.
1136 (t (error "Point is neither in a comment nor a string.")))))
1140 ;;; Tags
1143 ;; Return the project file to which the current file belongs. This is
1144 ;; either the value of `inform-project-file', the current file.
1146 (defun inform-project-file ()
1147 (or inform-project-file (buffer-file-name)))
1149 ;; Builds a list of files in the current project and returns it. It
1150 ;; recursively searches through included files, but tries to avoid
1151 ;; loops.
1153 (defun inform-project-file-list ()
1154 (let* ((project-file (inform-project-file))
1155 (project-dir (file-name-directory project-file))
1156 (in-file-list (list project-file))
1157 out-file-list
1158 (temp-buffer (generate-new-buffer "*Inform temp*")))
1159 (message "Building list of files in project...")
1160 (save-excursion
1161 (while in-file-list
1162 (if (member (car in-file-list) out-file-list)
1164 (set-buffer temp-buffer)
1165 (erase-buffer)
1166 (insert-file-contents (car in-file-list))
1167 (setq out-file-list (cons (car in-file-list) out-file-list)
1168 in-file-list (cdr in-file-list))
1169 (goto-char (point-min))
1170 (while (re-search-forward "\\<#?include\\s-+\">\\([^\"]+\\)\"" nil t)
1171 (let ((file (match-string 1)))
1172 ;; We need to duplicate Inform's file-finding algorithm:
1173 (if (not (string-match "\\." file))
1174 (setq file (concat file ".inf")))
1175 (if (not (file-name-absolute-p file))
1176 (setq file (expand-file-name file project-dir)))
1177 (setq in-file-list (cons file in-file-list))))))
1178 (kill-buffer nil))
1179 (message "Building list of files in project...done")
1180 out-file-list))
1182 ;; Visit tags table for current project, if it exists, or do nothing if
1183 ;; there is no current project, or no tags table.
1185 (defun inform-auto-load-tags-table ()
1186 (let (tf (project (inform-project-file)))
1187 (if project
1188 (progn
1189 (setq tf (expand-file-name "TAGS" (file-name-directory project)))
1190 (if (file-readable-p tf)
1191 ;; visit-tags-table seems to just take first parameter in XEmacs
1192 (visit-tags-table tf))))))
1194 (defun inform-retagify ()
1195 "Create a tags table for the files in the current project.
1196 The current project contains all the files included using Inform's
1197 `Include \">file\";' syntax by the project file, which is that given by
1198 the variable `inform-project-file' \(if this is set\), or the current
1199 file \(if not\). Files included recursively are included in the tags
1200 table."
1201 (interactive)
1202 (let* ((project-file (inform-project-file))
1203 (project-dir (file-name-directory project-file))
1204 (files (inform-project-file-list))
1205 (tags-file (expand-file-name "TAGS" project-dir)))
1206 (message "Running external tags program...")
1208 ;; Uses call-process to work on windows/nt systems (not tested)
1209 ;; Regexp matches routines or object/class definitions
1210 (call-process inform-etags-program
1211 nil nil nil
1212 "--regex=/\\([oO]bject\\|[nN]earby\\|[cC]lass\\|\\[\\)\\([ \\t]*->\\)*[ \\t]*\\([A-Za-z0-9_]+\\)/"
1213 (concat "--output=" tags-file)
1214 "--language=none"
1215 (mapconcat (function (lambda (x) x)) files ""))
1217 (message "Running external tags program...done")
1218 (inform-auto-load-tags-table)))
1224 ;;; Electric keys
1227 (defun inform-toggle-auto-newline (arg)
1228 "Toggle auto-newline feature.
1229 Optional numeric ARG, if supplied turns on auto-newline when positive,
1230 turns it off when negative, and just toggles it when zero."
1231 (interactive "P")
1232 (setq inform-auto-newline
1233 (if (or (not arg)
1234 (zerop (setq arg (prefix-numeric-value arg))))
1235 (not inform-auto-newline)
1236 (> arg 0))))
1238 (defun inform-electric-key (arg)
1239 "Insert the key typed and correct indentation."
1240 (interactive "P")
1241 (if (and (not arg) (eolp))
1242 (progn
1243 (self-insert-command 1)
1244 (inform-indent-line)
1245 (end-of-line))
1246 (self-insert-command (prefix-numeric-value arg))))
1248 (defun inform-electric-semi (arg)
1249 "Insert the key typed and correct line's indentation, as for semicolon.
1250 Special handling does not occur inside strings and comments.
1251 Inserts newline after the character if `inform-auto-newline' is non-NIL."
1252 (interactive "P")
1253 (if (and (not arg)
1254 (eolp)
1255 (let ((data (inform-syntax-class)))
1256 (not (memq (car data) '(string comment)))))
1257 (progn
1258 (self-insert-command 1)
1259 (inform-indent-line)
1260 (end-of-line)
1261 (if inform-auto-newline (newline-and-indent)))
1262 (self-insert-command (prefix-numeric-value arg))))
1264 (defun inform-electric-comma (arg)
1265 "Insert the key typed and correct line's indentation, as for comma.
1266 Special handling only occurs in object declarations.
1267 Inserts newline after the character if `inform-auto-newline' is non-NIL."
1268 (interactive "P")
1269 (if (and (not arg)
1270 (eolp)
1271 (let ((data (inform-syntax-class)))
1272 (and (not (memq (car data) '(string comment)))
1273 (nth 1 data)
1274 (zerop (car (nth 3 data))))))
1275 (progn
1276 (self-insert-command 1)
1277 (inform-indent-line)
1278 (end-of-line)
1279 (if inform-auto-newline (newline-and-indent)))
1280 (self-insert-command (prefix-numeric-value arg))))
1282 (defun inform-electric-brace (arg)
1283 "Insert the key typed and correct line's indentation.
1284 Insert newlines before and after if `inform-auto-newline' is non-NIL."
1285 ;; This logic is the same as electric-c-brace.
1286 (interactive "P")
1287 (let (insertpos)
1288 (if (and (not arg)
1289 (eolp)
1290 (let ((data (inform-syntax-class)))
1291 (memq (car data) '(code label)))
1292 (or (save-excursion (skip-syntax-backward " ") (bolp))
1293 (if inform-auto-newline
1294 (progn (inform-indent-line) (newline) t) nil)))
1295 (progn
1296 (insert last-command-char)
1297 (inform-indent-line)
1298 (end-of-line)
1299 (if (and inform-auto-newline (/= last-command-char ?\]))
1300 (progn
1301 (newline)
1302 (setq insertpos (1- (point)))
1303 (inform-indent-line)))
1304 (save-excursion
1305 (if insertpos (goto-char insertpos))
1306 (delete-char -1))))
1307 (if insertpos
1308 (save-excursion
1309 (goto-char (1- insertpos))
1310 (self-insert-command (prefix-numeric-value arg)))
1311 (self-insert-command (prefix-numeric-value arg)))))
1315 ;;; Miscellaneous
1318 (defun inform-next-object (&optional arg)
1319 "Go to the next object or class declaration in the file.
1320 With a prefix arg, go forward that many declarations.
1321 With a negative prefix arg, search backwards."
1322 (interactive "P")
1323 (let ((fun 're-search-forward)
1324 (errstring "more")
1325 (n (prefix-numeric-value arg)))
1326 (cond ((< n 0)
1327 (setq fun 're-search-backward errstring "previous" n (- n)))
1328 ((looking-at inform-real-object-regexp)
1329 (setq n (1+ n))))
1330 (prog1
1331 (funcall fun inform-real-object-regexp nil 'move n)
1332 (forward-line 0))))
1334 ;; This function doubles as an `imenu-prev-name' function, so when
1335 ;; called noninteractively it must return T if it was successful and NIL
1336 ;; if not. Argument NIL must correspond to moving backwards by 1.
1338 (defun inform-prev-object (&optional arg)
1339 "Go to the previous object or class declaration in the file.
1340 With a prefix arg, go back many declarations.
1341 With a negative prefix arg, go forwards."
1342 (interactive "P")
1343 (inform-next-object (- (prefix-numeric-value arg))))
1345 (defun inform-imenu-extract-name ()
1346 (if (looking-at
1347 "^#?\\(object\\|nearby\\|class\\)\\s-+\\(->\\s-+\\)*\\(\\(\\w\\|\\s_\\)+\\)")
1348 (concat (if (string= "class" (downcase (match-string 1)))
1349 "Class ")
1350 (buffer-substring-no-properties (match-beginning 3)
1351 (match-end 3)))))
1353 (defun inform-build-project ()
1354 "Compile the current Inform project.
1355 The current project is given by `inform-project-file', or the current
1356 file if this is NIL."
1357 (interactive)
1358 (let ((project-file (file-name-nondirectory (inform-project-file))))
1359 (compile
1360 (concat inform-command
1361 (if (and inform-libraries-directory
1362 (file-directory-p inform-libraries-directory))
1363 (concat " +" inform-libraries-directory)
1365 ;; Note the use of Microsoft style errors. The
1366 ;; Archimedes-style errors don't give the correct file
1367 ;; name.
1368 " " inform-command-options " -E1 "
1369 (if (string-match "\\`[^.]+\\(\\.inf\\'\\)" project-file)
1370 (substring project-file 0 (match-beginning 1))
1371 project-file)))))
1373 (defun inform-convert-old-format ()
1374 "Undoes Inform Mode 0.4 formatting for current buffer.
1375 Early versions of Inform Mode used tab stops every 4 characters to
1376 control formatting, which was the Wrong Thing To Do. This function
1377 undoes that stupidity."
1378 (interactive)
1379 (let ((tab-width 4))
1380 (untabify (point-min) (point-max))))
1384 ;;; Spell checking
1387 (defun inform-spell-check-buffer ()
1388 "Spellcheck all strings in the buffer using ispell."
1389 (interactive)
1390 (let (start (spell-continue t))
1391 (save-excursion
1392 (goto-char (point-min))
1393 (while (and (search-forward "\"" nil t)
1394 spell-continue)
1395 (if (and (eq (car (inform-syntax-class)) 'string)
1396 ;; don't spell check include directives etc
1397 (not (save-excursion
1398 (forward-line 0)
1399 (looking-at inform-directive-regexp))))
1400 (progn
1401 (forward-char -1) ; move point to quotation mark
1402 (setq start (point))
1403 (forward-sexp)
1404 (ispell-region start (point))
1405 ;; If user quit out (eg by pressing q while in ispell)
1406 ;; don't continue looking for strings to check.
1407 (setq spell-continue
1408 (and ispell-process
1409 (eq (process-status ispell-process) 'run)))))))))
1413 (provide 'inform-mode)
1415 ;;; inform-mode.el ends here