inform-mode.el 59 KB

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