/work/eiffel.el

http://github.com/tybor/Liberty · Emacs Lisp · 2776 lines · 2096 code · 310 blank · 370 comment · 78 complexity · f93f00beb9e185edba6515dd772b3527 MD5 · raw file

Large files are truncated click here to view the full file

  1. ;;; eiffel.el --- major mode for editing Eiffel files.
  2. ;; Copyright (C) 1989, 1990, 93, 94, 95, 96, 99, 2000, 01, 02, 03
  3. ;; Tower Technology Corporation,
  4. ;; Free Software Foundation,
  5. ;; Bob Weiner,
  6. ;; C. Adrian
  7. ;; Authors: 1989-1990 Stephen Omohundro, ISE and Bob Weiner
  8. ;; 1993-1996 Tower Technology Corporation
  9. ;; 1999-2003 Martin Schwenke <martin@meltin.net>
  10. ;; Maintainer: martin@meltin.net
  11. ;; Keywords: eiffel languages oop
  12. ;; Requires: font-lock, compile, easymenu, imenu
  13. ;; This file is derived from eiffel4.el from Tower Technology Corporation.
  14. ;;
  15. ;; Known bugs:
  16. ;;
  17. ;; * eif-short buffer doesn't get font locked under GNU Emacs 19.34.
  18. ;;
  19. ;; * eif-debug can hang under (at least) XEmacs 21.4.[89] in the wait
  20. ;; loop if there is input pending (that is, if the user hits return
  21. ;; an extra time). Not yet tested under XEmacs 21.5.
  22. ;;
  23. ;; This file is distributed under the same terms as GNU Emacs.
  24. ;; GNU Emacs is free software; you can redistribute it and/or modify
  25. ;; it under the terms of the GNU General Public License as published by
  26. ;; the Free Software Foundation; either version 2, or (at your option)
  27. ;; any later version.
  28. ;; GNU Emacs is distributed in the hope that it will be useful,
  29. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  31. ;; GNU General Public License for more details.
  32. ;; You should have received a copy of the GNU General Public License
  33. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  34. ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  35. ;; Boston, MA 02110-1301, USA
  36. ;;; Commentary:
  37. ;; NEW VERSIONS
  38. ;; The latest version of this mode is always available via:
  39. ;; http://meltin.net/hacks/emacs/
  40. ;; INSTALLATION
  41. ;; To install, simply copy this file into a directory in your
  42. ;; load-path and add the following two commands in your .emacs file:
  43. ;;
  44. ;; (add-to-list 'auto-mode-alist '("\\.e\\'" . eiffel-mode))
  45. ;; (autoload 'eiffel-mode "eiffel" "Major mode for Eiffel programs" t)
  46. ;;
  47. ;; Note: to be sure to have always a fresh eiffel.el, you can add the
  48. ;; "SmartEiffel/misc" directory in the emacs load-path by adding:
  49. ;;
  50. ;; (setq load-path (cons ".../SmartEiffel/misc" load-path))
  51. ;;; History:
  52. ;;
  53. ;; Add history stuff here!!!
  54. ;;; Code:
  55. (require 'font-lock)
  56. (require 'compile)
  57. (require 'easymenu)
  58. (require 'imenu)
  59. (defconst eiffel-version-string
  60. "$Id: eiffel.el,v 2.67 2003/06/14 10:41:01 martins Exp $"
  61. "Version string to make reporting bugs more meaningful.
  62. Note that if this file becomes part of GNU Emacs then the file might
  63. be changed by the Emacs maintainers without this version number
  64. changing. This means that if you are reporting a bug for a version
  65. that was shipped with Emacs, you should report the Emacs version!")
  66. (defgroup eiffel nil
  67. "Eiffel mode for Emacs"
  68. :group 'oop)
  69. (defgroup eiffel-indent nil
  70. "Indentation variables in Eiffel mode"
  71. :prefix "eif-"
  72. :group 'eiffel)
  73. (defgroup eiffel-compile nil
  74. "Compilation support variables in Eiffel mode"
  75. :prefix "eif-"
  76. :group 'eiffel)
  77. (defun eif-customize ()
  78. "Run \\[customize-group] for the `eiffel' group."
  79. (interactive)
  80. (customize-group 'eiffel))
  81. ;; Indentation amount variables.
  82. ;;
  83. ;; The default values correspond to style used in ``Eiffel: The
  84. ;; Language''.
  85. (defcustom eif-indent-increment 3
  86. "*Default indentation interval (in spaces)."
  87. :type 'integer
  88. :group 'eiffel-indent)
  89. (defcustom eif-class-level-kw-indent 0
  90. "*Indentation for Class level keywords.
  91. Specified as number of `eif-indent-increments'. See the variable
  92. `eif-class-level-keywords-regexp'."
  93. :type 'integer
  94. :group 'eiffel-indent)
  95. (defcustom eif-extra-class-level-kw-indent 0
  96. "*Number of extra spaces to add to `eif-class-level-kw-indent'.
  97. This results in the actual indentation of a class level keyword. Can
  98. be negative."
  99. :type 'integer
  100. :group 'eiffel-indent)
  101. (defcustom eif-class-level-comment-indent 0
  102. "*Indentation of comments at the beginning of a class.
  103. Specified as number of `eif-indent-increments'."
  104. :type 'integer
  105. :group 'eiffel-indent)
  106. (defcustom eif-extra-class-level-comment-indent 0
  107. "*Number of spaces to add to `eif-class-level-comment-indent'.
  108. This results in the actual indentation of a class level comment. Can
  109. be negative."
  110. :type 'integer
  111. :group 'eiffel-indent)
  112. (defcustom eif-inherit-level-kw-indent 2
  113. "*Indentation of keywords falling under the Inherit clause.
  114. Specified as number of `eif-indent-increments'. See the variable
  115. `eif-inherit-level-keywords'."
  116. :type 'integer
  117. :group 'eiffel-indent)
  118. (defcustom eif-extra-inherit-level-kw-indent 0
  119. "*Number of spaces to add to `eif-inherit-level-kw-indent'.
  120. This results in the actual indentation of an inherit level keyword.
  121. Can be negative."
  122. :type 'integer
  123. :group 'eiffel-indent)
  124. (defcustom eif-feature-level-indent 1
  125. "*Indentation amount of features.
  126. Specified as number of `eif-indent-increments'."
  127. :type 'integer
  128. :group 'eiffel-indent)
  129. (defcustom eif-extra-feature-level-indent 0
  130. "*Number of spaces to add to `eif-feature-level-indent'.
  131. This results in the indentation of a feature. Can be negative."
  132. :type 'integer
  133. :group 'eiffel-indent)
  134. (defcustom eif-feature-level-kw-indent 2
  135. "*Indentation of keywords belonging to individual features.
  136. Specified as number of `eif-indent-increments'. See the variable
  137. `eif-feature-level-keywords-regexp'."
  138. :type 'integer
  139. :group 'eiffel-indent)
  140. (defcustom eif-extra-feature-level-kw-indent 0
  141. "*Number of spaces to add to `eif-feature-level-kw-indent'.
  142. This results in the actual indentation of a feature level keyword.
  143. Can be negative."
  144. :type 'integer
  145. :group 'eiffel-indent)
  146. (defcustom eif-feature-level-comment-indent 3
  147. "*Indentation of comments at the beginning of a feature.
  148. Specified as number of `eif-indent-increments'."
  149. :type 'integer
  150. :group 'eiffel-indent)
  151. (defcustom eif-extra-feature-level-comment-indent 0
  152. "*Number of spaces to add to `eif-feature-level-comment-indent'.
  153. This results in the actual indentation of a feature level comment.
  154. Can be negative."
  155. :type 'integer
  156. :group 'eiffel-indent)
  157. (defcustom eif-body-comment-indent 0
  158. "*Indentation of comments in the body of a routine.
  159. Specified as number of `eif-indent-increments')"
  160. :type 'integer
  161. :group 'eiffel-indent)
  162. (defcustom eif-extra-body-comment-indent 0
  163. "*Number of spaces to add to `eif-body-comment-indent'.
  164. This results in the actual indentation of a routine body comment. Can
  165. be negative."
  166. :type 'integer
  167. :group 'eiffel-indent)
  168. (defcustom eif-check-keyword-indent 0
  169. "*Extra indentation for the check clause as described in ETL.
  170. Specified as number of `eif-indent-increments'. Default is 0, which
  171. is different than in ETL's 1."
  172. :type 'integer
  173. :group 'eiffel-indent)
  174. (defcustom eif-extra-check-keyword-indent 0
  175. "*Number of spaces to add to `eif-check-keyword-indent'.
  176. This results in the actual indentation of a check keyword. Can be
  177. negative."
  178. :type 'integer
  179. :group 'eiffel-indent)
  180. (defcustom eif-rescue-keyword-indent -1
  181. "*Extra indentation for the rescue clause as described in ETL.
  182. Specified as number of `eif-indent-increments'. Default is -1."
  183. :type 'integer
  184. :group 'eiffel-indent)
  185. (defcustom eif-extra-rescue-keyword-indent 0
  186. "*Number of spaces to add to `eif-rescue-keyword-indent'.
  187. This results in the actual indentation of a rescue keyword. Can be
  188. negative."
  189. :type 'integer
  190. :group 'eiffel-indent)
  191. (defcustom eif-then-indent 0
  192. "*Indentation for a `then' appearing on a line by itself.
  193. This is as opposed to a `then' on the same line as an `if'. Specified
  194. as number of `eif-indent-increments'."
  195. :type 'integer
  196. :group 'eiffel-indent)
  197. (defcustom eif-extra-then-indent 0
  198. "*Number of spaces to add to `eif-then-indent'.
  199. This results in the actual indentation of a `then' appearing on a line
  200. by itself. Can be negative."
  201. :type 'integer
  202. :group 'eiffel-indent)
  203. (defcustom eif-continuation-indent 1
  204. "*Extra indentation for a continued statement line.
  205. Specified as number of `eif-indent-increments'."
  206. :type 'integer
  207. :group 'eiffel-indent)
  208. (defcustom eif-extra-continuation-indent 0
  209. "*Number of spaces to add to `eif-continuation-indent'.
  210. This results in the actual indentation of a continued statement
  211. line. Can be negative."
  212. :type 'integer
  213. :group 'eiffel-indent)
  214. (defcustom eif-string-continuation-indent 0
  215. "*Extra indentation for a continued string.
  216. Specified as number of `eif-indent-increments'."
  217. :type 'integer
  218. :group 'eiffel-indent)
  219. (defcustom eif-extra-string-continuation-indent -1
  220. "*Number of spaces to add to `eif-string-continuation-indent'.
  221. This results in the actual indentation of a continued string. Can be
  222. negative."
  223. :type 'integer
  224. :group 'eiffel-indent)
  225. (defcustom eif-indent-string-continuations-relatively-flag t
  226. "*Non-nil means string continuations are indented relative to 1st character.
  227. That is, `eif-string-continuation-indent' and
  228. `eif-extra-string-continuation-indent' are added to position of first
  229. character of string. If nil, string continuations are indented
  230. relative to indent of previous line."
  231. :type 'boolean
  232. :group 'eiffel-indent)
  233. (defcustom eif-set-tab-width-flag t
  234. "*Non-nil means `tab-width' is set to `eif-indent-increment' in `eiffel-mode'."
  235. :type 'boolean
  236. :group 'eiffel-indent)
  237. (defcustom eif-preprocessor-indent 0
  238. "*Indentation for lines GOBO preprocessor directives.
  239. Specified as number of `eif-indent-increments' from left margin."
  240. :type 'integer
  241. :group 'eiffel-indent)
  242. (defcustom eif-fill-max-save 4096
  243. "*Maximum size of a paragraph to save before filling.
  244. Normally \\[eif-fill-paragraph] will mark a buffer as modified even if
  245. the fill operation does not make any changes. If the paragraph being
  246. filled is smaller than the value of this variable then the contents of
  247. the paragraph will be saved for comparison with the paragraph after
  248. the fill operation. If they are the same, the buffer modification
  249. state is restored. Set this to 0 to disable this feature, or a very
  250. big number to enable it for all paragraphs."
  251. :type 'integer
  252. :group 'eiffel-indent)
  253. (defcustom eif-use-gnu-eiffel t
  254. "*If t include support for compilation using GNU Liberty Eiffel."
  255. :type 'boolean
  256. :group 'eiffel-compile)
  257. (defcustom eif-se-command
  258. "se"
  259. "*Program to use for compiling Eiffel programs.
  260. The default is \"se\"."
  261. :type 'string
  262. :group 'eiffel-compile)
  263. (defcustom eif-compile-options ""
  264. "*Options to use for compiling Eiffel programs."
  265. :type 'string
  266. :group 'eiffel-compile)
  267. ;;
  268. ;; No user-customizable definitions below this point.
  269. ;;
  270. ;;
  271. ;; Indentation macros.
  272. ;;
  273. (defmacro eif-class-level-kw-indent-m ()
  274. "Indentation amount for Class level keywords (in number of spaces)."
  275. '(+ (* eif-class-level-kw-indent eif-indent-increment)
  276. eif-extra-class-level-kw-indent))
  277. (defmacro eif-class-level-comment-indent-m ()
  278. "Indentation amount for Class level comments (in number of spaces)."
  279. '(+ (* eif-class-level-comment-indent eif-indent-increment)
  280. eif-extra-class-level-comment-indent))
  281. (defmacro eif-inherit-level-kw-indent-m ()
  282. "Indentation amount for Inherit level keywords (in number of spaces)."
  283. '(+ (* eif-inherit-level-kw-indent eif-indent-increment)
  284. eif-extra-inherit-level-kw-indent))
  285. (defmacro eif-feature-level-indent-m ()
  286. "Indentation amount for features (in number of spaces)."
  287. '(+ (* eif-feature-level-indent eif-indent-increment)
  288. eif-extra-feature-level-indent))
  289. (defmacro eif-feature-level-kw-indent-m ()
  290. "Indentation amount for Feature level keywords (in number of spaces)."
  291. '(+ (* eif-feature-level-kw-indent eif-indent-increment)
  292. eif-extra-feature-level-kw-indent))
  293. (defmacro eif-body-comment-indent-m ()
  294. "Indentation amount for comments in routine bodies (in number of spaces)."
  295. '(+ (* eif-body-comment-indent eif-indent-increment)
  296. eif-extra-body-comment-indent))
  297. (defmacro eif-feature-level-comment-indent-m ()
  298. "Indentation amount for Feature level comments (in number of spaces)."
  299. '(+ (* eif-feature-level-comment-indent eif-indent-increment)
  300. eif-extra-feature-level-comment-indent))
  301. (defmacro eif-check-keyword-indent-m ()
  302. "Indentation amount for Check keyword (in number of spaces)."
  303. '(+ (* eif-check-keyword-indent eif-indent-increment)
  304. eif-extra-check-keyword-indent))
  305. (defmacro eif-rescue-keyword-indent-m ()
  306. "Indentation amount for Rescue keyword (in number of spaces)."
  307. '(+ (* eif-rescue-keyword-indent eif-indent-increment)
  308. eif-extra-rescue-keyword-indent))
  309. (defmacro eif-then-indent-m ()
  310. "Indentation amount for `then' appearing on a line by itself (in number of spaces)."
  311. '(+ (* eif-then-indent eif-indent-increment)
  312. eif-extra-then-indent))
  313. (defmacro eif-continuation-indent-m ()
  314. "Indentation amount for a statement continuation line (in number of spaces)."
  315. '(+ (* eif-continuation-indent eif-indent-increment)
  316. eif-extra-continuation-indent))
  317. (defmacro eif-string-continuation-indent-m ()
  318. "Indentation amount for a statement continuation line (in number of spaces)."
  319. '(+ (* eif-string-continuation-indent eif-indent-increment)
  320. eif-extra-string-continuation-indent))
  321. (defmacro eif-preprocessor-indent-m ()
  322. "Indentation amount for a preprocessor statement (in number of spaces)."
  323. '(* eif-preprocessor-indent eif-indent-increment))
  324. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  325. ;; Keyword Regular Expression Constants. ;;
  326. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  327. (defconst eif-non-id-char-regexp "\\S_" ;; "[^a-z0-9_]"
  328. "The characters that are not part of identifiers.")
  329. (defun eif-post-anchor (regexp)
  330. "Anchor given REGEXP with end-word delimiter and `eif-non-id-char-regexp'."
  331. (concat "\\(" regexp "\\)\\>" eif-non-id-char-regexp))
  332. (defun eif-word-anchor (regexp)
  333. "Anchor given REGEXP with word delimiters and `eif-non-id-char-regexp'."
  334. (concat "\\<\\(" regexp "\\)\\>" eif-non-id-char-regexp))
  335. (defun eif-anchor (regexp)
  336. "Anchor given REGEXP front and back to match line break or non-symbol char."
  337. (concat "\\(^\\|\\S_\\<\\)\\(" regexp "\\)\\($\\|\\>\\S_\\)"))
  338. ;; Note invariant is handled as a special case since it is both a
  339. ;; class-level and a from-level keyword
  340. ;; Note obsolete is handled as a special case since it is both a
  341. ;; class-level and a feature-level keyword
  342. ;; Note create, note, and indexing are also handled as special cases
  343. (defconst eif-class-level-keywords
  344. (concat
  345. "\\(?:"
  346. (regexp-opt '("deferred" "expanded" "reference" "separate"))
  347. "[ \t]+\\)?class"
  348. "\\|"
  349. (regexp-opt '("inherit" "insert" "convert" "creation" "feature")))
  350. "Keywords introducing class-level clauses.
  351. Note that `invariant', `obsolete', `indexing', `note', and `create' are not included here since can
  352. function as more than one type of keyword.")
  353. (defconst eif-class-level-keywords-regexp
  354. (eif-word-anchor eif-class-level-keywords)
  355. "Regexp of keywords introducing class level clauses, with some context.
  356. See `eif-class-level-keywords'.")
  357. (defconst eif-inherit-level-keywords
  358. (regexp-opt '("rename" "redefine" "undefine" "select" "export"))
  359. "Those keywords which introduce subclauses of the inherit clause.")
  360. (defconst eif-feature-level-keywords
  361. (regexp-opt '("require" "local" "deferred" "separate" "do" "once" "ensure" "alias" "external" "attribute"))
  362. "Those keywords which are internal to features (in particular, routines).")
  363. (defconst eif-feature-level-keywords-regexp
  364. (eif-word-anchor eif-feature-level-keywords)
  365. "Regexp of keywords internal to features (usually routines).
  366. See `eif-feature-level-keywords'.")
  367. (defconst eif-end-keyword "end" "The `end' keyword.")
  368. (defconst eif-end-on-current-line ".*[ \t]end[ \t]*;?[ \t]*\\(--.*\\)?$"
  369. "Regular expression to identify lines ending with the `end' keyword.")
  370. (defconst eif-control-flow-keywords
  371. (regexp-opt '("if" "inspect" "from" "debug"))
  372. "Keywords which introduce control-flow constructs.")
  373. (defconst eif-control-flow-matching-keywords
  374. (concat (regexp-opt '("deferred" "do" "once")) "\\|" eif-control-flow-keywords)
  375. "Keywords that may cause the indentation of an `eif-control-flow-keyword'.
  376. If these occur prior to an `eif-control-flow-keyword' then the
  377. `eif-control-flow-keyword' is indented. Note that technically, `end'
  378. is part of this list but it is handled separately in the function
  379. \[eif-matching-kw\].")
  380. (defconst eif-control-flow-matching-keywords-regexp
  381. (eif-word-anchor eif-control-flow-matching-keywords)
  382. "Regexp of keywords maybe causing indentation of `eif-control-flow-keyword'.
  383. See `eif-control-flow-keywords'.")
  384. (defconst eif-check-keyword "check"
  385. "The `check' keyword.")
  386. (defconst eif-check-keywords-regexp
  387. (eif-word-anchor eif-check-keyword)
  388. "The `check' keyword (with trailing context).")
  389. ;; FIXME: Doesn't work if once keyword is followed by a string on next
  390. ;; line, but didn't get broken by this attempt at factoring.
  391. (defconst eif-check-matching-keywords-regexp
  392. eif-control-flow-matching-keywords-regexp
  393. "Keywords that may cause the indentation of an `eif-check-keyword'.
  394. If these occur prior to an `eif-check-keyword' then the
  395. `eif-check-keyword' is indented. Note that technically, `end' is part
  396. of this list but it is handled separately in the function
  397. \[eif-matching-kw\]. See also `eif-control-flow-matching-keywords-regexp'.")
  398. ;; FIXME: This could be fixed or removed.
  399. (defconst eif-end-keyword-regexp "\\<end\\>"
  400. "The `end' keyword with context.")
  401. (defconst eif-end-matching-keywords
  402. (concat (regexp-opt '("attribute" "check" "class" "feature" "rename" "redefine" "undefine"
  403. "select" "export" "separate" "external" "alias")) "\\|"
  404. eif-control-flow-matching-keywords)
  405. "Those keywords whose clause is terminated by an `end' keyword.")
  406. (defconst eif-end-matching-keywords-regexp
  407. (eif-word-anchor eif-end-matching-keywords)
  408. "Regexp of keywords whose clause is terminated by an `end' keyword.
  409. See `eif-end-matching-keywords'.")
  410. (defconst eif-rescue-keyword "rescue" "The `rescue' keyword.")
  411. (defconst eif-obsolete-keyword "obsolete" "The `obsolete' keyword.")
  412. (defconst eif-indexing-keyword
  413. (regexp-opt '("note" "indexing"))
  414. "The `indexing' and `note' keywords.")
  415. (defconst eif-indexing-keyword-regexp
  416. (eif-post-anchor eif-indexing-keyword)
  417. "Regexp matching `indexing' and `note' keywords, with trailing context.")
  418. (defconst eif-rescue-keywords-regexp
  419. (eif-word-anchor eif-rescue-keyword)
  420. "The `rescue' keyword (with trailing context).")
  421. (defconst eif-rescue-matching-keywords-regexp
  422. (eif-word-anchor (regexp-opt '("deferred" "do" "once")))
  423. "Keywords that may cause the indentation of an `eif-rescue-keyword'.
  424. If these occur prior to an `eif-rescue-keyword' then the
  425. `eif-rescue-keyword' is indented. Note that technically, `end' is
  426. part of this list but it is handled separately in the function
  427. \[eif-matching-kw\]. See also `eif-control-flow-matching-keywords-regexp'.")
  428. (defconst eif-from-level-keywords
  429. (regexp-opt '("until" "variant" "loop"))
  430. "Keywords occuring inside of a from clause.")
  431. (defconst eif-from-level-keywords-regexp
  432. (eif-word-anchor eif-from-level-keywords)
  433. "Regexp of keywords occuring inside of a from clause.
  434. See `eif-from-level-keywords'.")
  435. (defconst eif-from-keyword "from" "The keyword `from'.")
  436. (defconst eif-if-or-inspect-level-keywords
  437. (regexp-opt '("elseif" "else" "when"))
  438. "Keywords occuring inside of an if or inspect clause.")
  439. (defconst eif-if-or-inspect-level-keywords-regexp
  440. (eif-word-anchor eif-if-or-inspect-level-keywords)
  441. "Regexp of keywords occuring inside of an if or inspect clause.
  442. See eif-if-or-inspect-level-keywords.")
  443. (defconst eif-if-or-inspect-keyword-regexp
  444. (eif-word-anchor (regexp-opt '("if" "inspect")))
  445. "Regexp matching the `if' or `inspect' keywords.")
  446. (defconst eif-then-keyword ".*[ \t)]then[ \t]*$"
  447. "The keyword `then' with possible leading text.")
  448. (defconst eif-solitary-then-keyword "then" "The keyword `then'.")
  449. (defconst eif-then-matching-keywords
  450. (regexp-opt '("if" "elseif" "when") t)
  451. "Keywords that may alter the indentation of an `eif-then-keyword'.
  452. If one of these occur prior to an `eif-then-keyword' then this sets
  453. the indentation of the `eif-then-keyword'. Note that technically,
  454. `end' is part of this list but it is handled separately in the
  455. function \[eif-matching-kw\]. See also
  456. `eif-control-flow-matching-keywords-regexp'.")
  457. (defconst eif-invariant-keyword "invariant" "The `invariant' keyword.")
  458. (defconst eif-invariant-matching-keywords
  459. (regexp-opt '("from" "feature"))
  460. "Keywords that may cause the indentation of an `eif-invarient-keyword'.
  461. If one of these occurs prior to an `eif-invariant-keyword' then the
  462. `eif-invariant-keyword' is indented. Note that technically, `end' is
  463. part of this list but it is handled separately in the function
  464. \[eif-matching-kw\]. See also `eif-control-flow-matching-keywords-regexp'.")
  465. (defconst eif-obsolete-matching-keywords
  466. (regexp-opt '("is" "class") t)
  467. "Keywords that may cause the indentation of an `eif-obsolete-keyword'.
  468. If one of these occurs prior to an `eif-obsolete-keyword' then the
  469. `eif-obsolete-keyword' is indented.")
  470. (defconst eif-create-keyword
  471. "create"
  472. "Eiffel create keyword. Can be used at class or minor level.")
  473. (defconst eif-create-keyword-regexp
  474. (eif-post-anchor eif-create-keyword)
  475. "Regexp matching `create' keyword, with trailing context.")
  476. (defconst eif-indentation-keywords
  477. (concat (regexp-opt '("note" "indexing" "rescue" "inherit" "insert" "convert" "create" "creation"
  478. "invariant" "require" "local" "ensure" "obsolete")) "\\|"
  479. eif-from-level-keywords "\\|"
  480. eif-if-or-inspect-level-keywords "\\|"
  481. eif-end-matching-keywords)
  482. "Keywords that match any eiffel keyword triggering indentation.")
  483. (defconst eif-indentation-keywords-regexp
  484. (eif-word-anchor eif-indentation-keywords)
  485. "Regexp of keywords that match any eiffel keyword triggering indentation.
  486. See `eif-indentation-keywords'.")
  487. (defconst eif-once-non-indent-regexp
  488. "\\s-*once\\(\\s-\\|\n\\)+\""
  489. "Regexp of Eiffel once keyword in context not affecting indentation.")
  490. (defconst eif-feature-indentation-keywords-regexp
  491. (eif-word-anchor (regexp-opt '("convert" "creation" "feature")))
  492. "Keywords which denote the presence of features following them.")
  493. (defconst eif-is-keyword-regexp "\\(.*[ \t)]\\)?is[ \t]*\\(--.*\\)?$"
  494. "The `is' keyword (with some context).")
  495. (defconst eif-multiline-routine-is-keyword-regexp
  496. ".*([^)]*)\\([ \t\n]*\\|[ \t\n]*:[][ \t\nA-Za-x0-9_,]*\\)is[ \t]*\\(--.*\\)?$"
  497. "The `is' keyword (with some context).")
  498. (defconst eif-operator-keywords
  499. (regexp-opt '("and" "or" "implies"))
  500. "Eiffel operator keywords.")
  501. (defconst eif-operator-regexp
  502. (concat "[ \t]*\\([@*/+]\\|-[^-]\\|\\<\\("
  503. eif-operator-keywords
  504. "\\)[ \t(]\\)")
  505. "Eiffel operators - used to identify continuation lines.
  506. See `eif-operator-keywords'.")
  507. (defconst eif-operator-eol-regexp
  508. (concat ".*\\([@*/+-]\\|\\<\\(" eif-operator-keywords
  509. "\\)\\|:=\\)[ \t]*\\(--.*\\)?$")
  510. "Eiffel operators - used to identify continuation lines.")
  511. (defconst eif-all-keywords
  512. (concat eif-indentation-keywords "\\|"
  513. eif-solitary-then-keyword "\\|"
  514. eif-create-keyword "\\|"
  515. eif-end-keyword)
  516. "Regexp matching (nearly) any eiffel keyword in a line.
  517. Does not include `is'.")
  518. (defconst eif-all-keywords-regexp
  519. (concat "\\("
  520. (eif-word-anchor eif-all-keywords) "\\)")
  521. "Anchored regexp matching (nearly) any eiffel keyword in a line.
  522. Does not include `is'. See `eif-all-keywords'.")
  523. (defconst eiffel-comment-start-skip
  524. "--+|?[ \t]*"
  525. "Regexp matching the beginning of an Eiffel comment.")
  526. (defconst eif-non-source-line
  527. (concat "[ \t]*\\(\\(" "--" "\\).*\\)?$")
  528. "RE matching line with only whitespace and comment or preprocessor keyword.")
  529. (defconst eif-variable-or-const-regexp "[^()\n]*:[^=].*"
  530. "RE to match a variable or constant declaration.")
  531. ;; Factor out some important important regexps for use in
  532. ;; eif-{beginning,end}-of-feature.
  533. (defun eiffel-feature-re ()
  534. "Liberty Eiffel feature declarations"
  535. (let* ((argument-name "\\(?:[A-Za-z]*[a-z0-9_]+[A-Za-z0-9_]*\\)")
  536. (feature-name (concat "\\(?:\\(infix\\|prefix\\)\\s-+\".+?\"\\|" argument-name "\\(?:\\s-+alias\\s-+\".+?\"\\)?\\)"))
  537. (type-name "\\(?:like\\s-+\\sw+\\|[A-Z]\\sw*\\(?:\\[.+?\\]\\)?\\)"))
  538. (concat
  539. "\\(?:"
  540. "\\(?:\\<frozen\\s-\\)?"
  541. "\\s-*" feature-name "\\s-*,?"
  542. "\\)+"
  543. "\\(?:(" ; no \\s-* because it is matched above, if there is no trailing coma
  544. "\\(?:"
  545. "\\(?:\\s-*" argument-name "\\s-*,?\\)+?"
  546. ":" type-name "\\s-*;?"
  547. "\\)*?"
  548. ")\\)?" ; no \\s-* because it is matched above, if there is no trailing semi-colon
  549. "\\(?:\\s-*:\\s-*" type-name "\\)?"
  550. "\\(?:\\s-*assign\\s-*\\sw+\\)?"
  551. "\\(?:\\s-*is\\>\\)?")))
  552. (defconst eif-routine-begin-regexp
  553. ;"\\([a-z][a-zA-Z_0-9]*\\)\\s-*\\(([^)]*)\\)?\\s-*\\(:\\s-*[A-Z][A-Z0-9_]*\\(\\s-*\\[[^\\]]*\\]\\)?\\)?\\s-*\\(assign\\s-*[a-zA-Z0-9_]+\\)?\\s-*\\<is\\>\\s-*\\(--.*\\)?$"
  554. (eiffel-feature-re)
  555. "Regexp matching the beginning of an Eiffel routine declaration.")
  556. (message (concat "Liberty Eiffel features: " eif-routine-begin-regexp))
  557. (defconst eif-attribute-regexp
  558. (concat "[a-z_][^-:\n]*:\\s-*"
  559. "\\(like\\s-*[a-zA-Z][a-z_0-9]*\\|"
  560. "\\(\\(expanded\\|reference\\)\\s-*\\)?[A-Z][A-Z_0-9]*"
  561. "\\(\\s-*\\[[^-\n]*\\]\\)?\\)"
  562. "\\s-*\\($\\|[;)].*\\|--.*\\)")
  563. "Regexp matching an Eiffel attribute, parameter or local variable.")
  564. (defconst eif-constant-regexp
  565. "[a-z_][^-:\n]*:[^-\n]*\\<is\\>\\s-*[^ \t\n]"
  566. "Regexp matching an Eiffel constant declaration.")
  567. (defconst eif-probably-feature-regexp
  568. (concat "\\(" eif-routine-begin-regexp
  569. "\\|" eif-attribute-regexp
  570. "\\|" eif-constant-regexp "\\)")
  571. "Regexp probably matching an Eiffel feature.
  572. This will also match local variable and parameter declarations.")
  573. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  574. (defvar eif-matching-indent -1
  575. "Indentation of the keyword found on the last call to \[eif-matching-kw\].
  576. -1 if no match was found.")
  577. (defvar eif-matching-kw-for-end nil)
  578. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  579. ;;
  580. ;; Font-lock support.
  581. ;;
  582. ;; Rewritten from scratch by Cyril Adrian <cyril.adrian@gmail.com>
  583. ;; Specific to Liberty Eiffel
  584. ;;
  585. (defconst eiffel-keywords-feature
  586. '("agent" "all" "and" "as" "assign" "attribute" "check" "class"
  587. "convert" "create" "debug" "deferred" "do" "else" "elseif" "end" "ensure"
  588. "expanded" "export" "external" "feature" "from" "if" "implies"
  589. "indexing" "inherit" "insert" "inspect" "invariant" "is" "like"
  590. "local" "loop" "not" "note" "obsolete" "old" "once" "only" "or"
  591. "redefine" "rename" "require" "rescue" "retry" "select" "separate" "then"
  592. "undefine" "until" "variant" "when" "xor"))
  593. (defconst eiffel-keywords
  594. '("agent" "alias" "all" "and" "as" "assign" "attribute" "check" "class"
  595. "convert" "create" "debug" "deferred" "do" "else" "elseif" "end" "ensure"
  596. "expanded" "export" "external" "feature" "from" "frozen" "if" "implies"
  597. "indexing" "infix" "inherit" "insert" "inspect" "invariant" "is" "like"
  598. "local" "loop" "not" "note" "obsolete" "old" "once" "only" "or" "prefix"
  599. "redefine" "rename" "require" "rescue" "retry" "select" "separate" "then"
  600. "undefine" "until" "variant" "when" "xor"))
  601. (defconst eiffel-constants
  602. '("Current" "False" "Precursor" "Result" "True" "Void"))
  603. (defun eiffel-string-re ()
  604. "Liberty Eiffel strings"
  605. (concat "U?\"\\(?:"
  606. "\\(?:\\(?:[^%\"\n]\\|%[^\n]\\)*?\\)" ; single-line strings
  607. "\\|"
  608. "\\(?:\\(?:[^%\"\n]\\|%[^\n]\\)*?%[ \t]*\n\\(?:[ \t]*%\\(?:[^%\"\n]\\|%[^\n]\\)*?%[ \t]*\n\\)*[ \t]*%\\(?:[^%\"\n]\\|%[^\n]\\)*?\\)" ; older multiline strings
  609. "\\|"
  610. "\\(?:[[{][ \t]*\n\\(?:.*?\n\\)*?[ \t]*[]}]\"\\)" ; newer multiline strings
  611. "\\)\""))
  612. (defun eiffel-wordstart-re ()
  613. "start of words"
  614. "\\<\\(")
  615. (defun eiffel-wordend-re ()
  616. "end of words"
  617. (concat "\\)\\>"))
  618. (defun eiffel-keywords-re ()
  619. (concat
  620. (eiffel-wordstart-re)
  621. (regexp-opt eiffel-keywords)
  622. (eiffel-wordend-re)))
  623. (defun eiffel-constants-re ()
  624. (concat
  625. (eiffel-wordstart-re)
  626. (regexp-opt eiffel-constants)
  627. (eiffel-wordend-re)))
  628. (defun eiffel-preprocessor-re ()
  629. (concat
  630. (eiffel-wordstart-re)
  631. (regexp-opt '("c_inline_c" "c_inline_h" "not_yet_implemented" "se_breakpoint" "breakpoint" "to_pointer"
  632. "is_expanded_type" "is_basic_expanded_type"
  633. "object_size" "object_id_memory"
  634. "se_guru01" "se_guru02" "se_guru03"))
  635. (eiffel-wordend-re)))
  636. (defvar eiffel-font-lock-defaults
  637. (append
  638. `(
  639. ("--|\\(.*\\)\n" . font-lock-comment-face)
  640. ("--\\(.*\\)\n" . font-lock-doc-face)
  641. (,(eiffel-string-re) . font-lock-string-face)
  642. ("'\\(?:[^'%]\\|%.\\)'" . font-lock-string-face)
  643. ("\\<\\([A-Z][A-Z0-9_]*\\)\\>" . font-lock-type-face)
  644. (,(eiffel-keywords-re) 1 font-lock-keyword-face)
  645. (,(eiffel-constants-re) 1 font-lock-builtin-face)
  646. (,(eiffel-preprocessor-re) 1 font-lock-preprocessor-face)))
  647. "Default highlighting expressions for Liberty Eiffel mode")
  648. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  649. ;;
  650. ;; Compilation support for GNU SmartEiffel.
  651. ;;
  652. (defvar eif-compile-dir nil
  653. "Current directory where Eiffel compilations are taking place.
  654. Possibly used for error location.")
  655. (defvar eif-root-class nil
  656. "Current Eiffel root class being compiled/debugged.")
  657. (defvar eif-compile-target nil
  658. "Current Eiffel compilation target.")
  659. (defvar eif-debug-target nil
  660. "Current Eiffel debug target.")
  661. (defvar eif-root-proc nil
  662. "Current Eiffel root procedure.")
  663. (defvar eif-run-command nil
  664. "Current command to run after Eiffel compile.")
  665. (defvar eif-debug-command nil
  666. "Current debug command to run after Eiffel debug compile.")
  667. (defun eif-compilation-mode-hook ()
  668. "Hook function to set local value for `compilation-error-screen-columns'.
  669. This should be nil for SmartEiffel compiles, because column positions are
  670. returned as character positions rather than screen columns."
  671. ;; In Emacs > 20.7 compilation-error-screen-columns is buffer local.
  672. (or (assq 'compilation-error-screen-columns (buffer-local-variables))
  673. (make-local-variable 'compilation-error-screen-columns))
  674. (setq compilation-error-screen-columns nil))
  675. (defun eif-compile ()
  676. "Compile an Eiffel root class."
  677. (interactive)
  678. (eif-compile-prompt)
  679. (eif-compile-internal))
  680. (defun eif-set-compile-options ()
  681. "Set Eiffel compiler options."
  682. (interactive)
  683. (setq eif-compile-options
  684. (read-string "Eiffel compiler options: " eif-compile-options)))
  685. ;; Taken from Emacs 20.3 subr.el (just in case we're running under Emacs 19).
  686. (defun eif-split-string (string &optional separators)
  687. "Split STRING into substrings separated by SEPARATORS.
  688. Each match for SEPARATORS is a splitting point. The substrings
  689. between the splitting points are made into a list which is returned.
  690. If SEPARATORS is absent, it defaults to \"[ \\f\\t\\n\\r\\v]+\".
  691. If there is match for SEPARATORS at the beginning of STRING, we do not
  692. include a null substring for that. Likewise, if there is a match
  693. at the end of STRING, we do not include a null substring for that."
  694. (let ((rexp (or separators "[ \f\t\n\r\v]+"))
  695. (start 0)
  696. notfirst
  697. (list nil))
  698. (while (and (string-match rexp string
  699. (if (and notfirst
  700. (= start (match-beginning 0))
  701. (< start (length string)))
  702. (1+ start) start))
  703. (< (match-beginning 0) (length string)))
  704. (setq notfirst t)
  705. (or (eq (match-beginning 0) 0)
  706. (and (eq (match-beginning 0) (match-end 0))
  707. (eq (match-beginning 0) start))
  708. (setq list
  709. (cons (substring string start (match-beginning 0))
  710. list)))
  711. (setq start (match-end 0)))
  712. (or (eq start (length string))
  713. (setq list
  714. (cons (substring string start)
  715. list)))
  716. (nreverse list)))
  717. (defun eif-run ()
  718. "Run a compiled Eiffel program."
  719. (interactive)
  720. (setq eif-run-command
  721. (read-string "Command to run: "
  722. (or eif-run-command
  723. eif-compile-target
  724. (file-name-sans-extension
  725. (if (or (eq system-type 'windows-nt) (eq system-type 'cygwin))
  726. buffer-file-name
  727. (file-name-nondirectory (buffer-file-name)))))))
  728. (eif-run-internal))
  729. (defun eif-debug ()
  730. "Run the SmartEiffel debugger."
  731. (interactive)
  732. (eif-compile-prompt)
  733. (setq eif-debug-target
  734. (file-name-sans-extension
  735. (read-string "Debug target name: "
  736. (or eif-debug-target
  737. (concat eif-compile-target "_debug")))))
  738. (let* ((eif-compile-options (concat "-sedb " eif-compile-options))
  739. (eif-compile-target eif-debug-target)
  740. (buff (eif-compile-internal))
  741. (proc (get-buffer-process buff)))
  742. ;; This works under GNU Emacs, but hangs under at least some
  743. ;; versions of XEmacs if there is input pending.
  744. (while (eq (process-status proc) 'run)
  745. (sit-for 1))
  746. (if (= (process-exit-status proc) 0)
  747. (progn
  748. (setq eif-debug-command
  749. (read-string "Debugger command to run: "
  750. (or eif-debug-command
  751. eif-debug-target
  752. (file-name-sans-extension
  753. (if (eq system-type 'windows-nt)
  754. buffer-file-name
  755. (file-name-nondirectory
  756. (buffer-file-name)))))))
  757. (let ((eif-run-command eif-debug-command))
  758. (eif-run-internal))))))
  759. (defun eif-compile-prompt ()
  760. "Prompt for information required to compile an Eiffel root class."
  761. ;; Do the save first, since the user might still have their hand on
  762. ;; the mouse.
  763. (save-some-buffers (not compilation-ask-about-save) nil)
  764. (setq eif-compile-dir (file-name-directory (buffer-file-name)))
  765. (setq eif-root-class
  766. (file-name-sans-extension
  767. (read-string "Name of root class: "
  768. (or eif-compile-target
  769. (file-name-sans-extension
  770. (file-name-nondirectory (buffer-file-name)))))))
  771. (setq eif-compile-target eif-root-class)
  772. (setq eif-root-proc
  773. (read-string "Name of root procedure: "
  774. eif-root-proc)))
  775. (defun eif-compile-internal ()
  776. "Compile an Eiffel root class. Internal version.
  777. Returns the same thing as \\[compilation-start] - the compilation buffer."
  778. (let ((cmd (concat eif-se-command
  779. " compile -flymake_mode "
  780. eif-compile-options
  781. " -o " eif-compile-target
  782. (if (eq system-type 'windows-nt) ".exe")
  783. " " eif-root-class
  784. " " eif-root-proc))
  785. (buf-name "*Liberty Eiffel Compilation*")
  786. (compilation-mode-hook (cons 'eif-compilation-mode-hook
  787. compilation-mode-hook)))
  788. (if (fboundp 'compilation-start) ; Emacs 22 and above
  789. (compilation-start cmd nil #'(lambda (mode-name) buf-name))
  790. (compile-internal cmd "No more errors" buf-name))))
  791. (defun eif-run-internal ()
  792. "Run a compiled Eiffel program. Internal version."
  793. (let* ((tmp-buf (current-buffer))
  794. (words (eif-split-string eif-run-command))
  795. (cmd (expand-file-name (car words))))
  796. (apply 'make-comint cmd cmd nil (cdr words))
  797. (switch-to-buffer tmp-buf)
  798. (switch-to-buffer-other-window (concat "*" cmd "*"))))
  799. ;; This has been loosened up to spot parts of messages that contain
  800. ;; references to multiple locations. Thanks to Andreas
  801. ;; <nozone@sbox.tu-graz.ac.at>. Also, the column number is a character
  802. ;; count rather than a screen column, so we need to make sure that
  803. ;; compilation-error-screen-columns is nil. Note that in XEmacs this
  804. ;; variable doesn't exist, so we end up in the wrong column. Hey, at
  805. ;; least we're on the correct line!
  806. (add-to-list 'compilation-error-regexp-alist
  807. '("^Line \\([0-9]+\\) column \\([0-9]+\\) in [^ ]+ (\\([^)]+\\.[Ee]\\))" 3 1 2))
  808. (defun eif-find-file (&optional userclass)
  809. "Find and open an Eiffel file given by a class name"
  810. (interactive)
  811. (let* ((cn (or userclass (current-word))))
  812. (if (string-match "[A-Z][0-9A-Z_]*" cn)
  813. (let ((classname (substring cn (match-beginning 0) (match-end 0))))
  814. (message "Searching %s..." classname)
  815. (let* ((shellres (shell-command-to-string (concat "se find --raw " classname)))
  816. (filename (substring shellres 0 (string-match "\n" shellres))))
  817. (find-file filename)
  818. )))
  819. (message nil)))
  820. (defun eif-short ()
  821. "Display the short form of an Eiffel class."
  822. (interactive)
  823. (let* ((class (read-string
  824. "Class or file: "
  825. (if (buffer-file-name)
  826. (file-name-nondirectory (buffer-file-name)))))
  827. (buf (get-buffer-create (concat "*Eiffel - short " class "*"))))
  828. (shell-command (concat eif-se-command " short " class) buf)
  829. (with-current-buffer buf
  830. (let ((font-lock-defaults eiffel-font-lock-defaults))
  831. (font-lock-fontify-buffer))
  832. (read-only-mode 1))))
  833. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  834. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  835. ;; Utility Functions. ;;
  836. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  837. (defun eif-feature-quote ()
  838. "Put a `' around the current feature name."
  839. (interactive)
  840. (save-excursion
  841. ;; Only try to go back to the beginning of the feature if we're
  842. ;; not already there.
  843. (if (/= (point)
  844. (save-excursion
  845. (forward-sexp)
  846. (backward-sexp)
  847. (point)))
  848. (backward-sexp))
  849. (insert "`")
  850. (forward-sexp)
  851. (insert "'"))
  852. (if (looking-at "'")
  853. (forward-char 1)))
  854. (defun eif-peeking-backwards-at (regexp)
  855. "Return non-nil is previous character exists and is matched by REGEXP.
  856. The match is actually an unbounded match starting at the previous character."
  857. (save-excursion
  858. (save-match-data
  859. (and (not (bobp))
  860. (or (backward-char) t)
  861. (looking-at regexp)))))
  862. (defsubst eif-in-comment-p ()
  863. "Return t if point is in a comment."
  864. (interactive)
  865. (save-excursion
  866. (nth 4 (parse-partial-sexp
  867. (save-excursion (beginning-of-line) (point))
  868. (point)))))
  869. (defun eif-in-comment-or-quoted-string-p ()
  870. "Return t if point is in a comment or quoted string."
  871. (or (eif-in-comment-p)
  872. (eif-in-quoted-string-p)))
  873. (defun eif-not-in-comment-or-quoted-string-p ()
  874. "Return t if point is not in a comment or quoted string."
  875. (not (eif-in-comment-or-quoted-string-p)))
  876. (defun eif-near-comment-p ()
  877. "Return t if point is close enough to a comment for filling purposes."
  878. (or (eif-in-comment-p)
  879. (and (or (looking-at comment-start-skip)
  880. (eif-peeking-backwards-at comment-start-skip))
  881. (not (eif-in-quoted-string-p)))
  882. (looking-at (concat "[ \t]*" comment-start-skip))))
  883. (defun eif-re-search-forward (regexp &optional limit noerror)
  884. "Search forward from point for REGEXP not in comment or string.
  885. `case-fold-search' is set to nil when searching. For details on other
  886. arguments see \\[re-search-forward]."
  887. (interactive "sRE search: ")
  888. (let ((start (point))
  889. found case-fold-search)
  890. (while (and (setq found (re-search-forward regexp limit noerror))
  891. (eif-in-comment-or-quoted-string-p)))
  892. (if (and found
  893. (eif-not-in-comment-or-quoted-string-p))
  894. found
  895. (if (eq noerror t)
  896. (goto-char start))
  897. nil)))
  898. (defun eif-re-search-backward (regexp &optional limit noerror)
  899. "Search backward from point for REGEXP not in comment or string.
  900. `case-fold-search' is set to nil when searching. For details on other
  901. arguments see \\[re-search-forward]."
  902. (interactive "sRE search: ")
  903. (let ((start (point))
  904. found case-fold-search)
  905. (while (and (setq found (re-search-backward regexp limit noerror))
  906. (eif-in-comment-or-quoted-string-p)))
  907. (if (and found
  908. (eif-not-in-comment-or-quoted-string-p))
  909. found
  910. (if (eq noerror t)
  911. (goto-char start))
  912. nil)))
  913. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  914. ;; Indentation Functions. ;;
  915. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  916. (defun eif-skip-leading-whitespace ()
  917. "Move to the first non-whitespace character on the current line."
  918. (end-of-line)
  919. (let ((line-end (point)))
  920. (beginning-of-line)
  921. (skip-syntax-forward "-" line-end)))
  922. (defun eif-calc-indent ()
  923. "Calculate the indentation of the current line of eiffel code.
  924. This function handles the case where there is a keyword that affects
  925. indentation at the beginning of the current line. For lines that
  926. don't start with a relevant keyword, the calculation is handed off to
  927. \\[eif-calc-non-keyword-indent]."
  928. (let ((indent 0)
  929. kw-match)
  930. (save-excursion
  931. (eif-skip-leading-whitespace)
  932. ;; Look for a keyword on the current line.
  933. (if (looking-at eif-all-keywords-regexp)
  934. (cond ((looking-at eif-create-keyword-regexp)
  935. ;; Class-level or minor occurence?
  936. (if (save-excursion (eif-find-beginning-of-feature))
  937. ;; Minor.
  938. (setq indent (eif-calc-indent-non-keyword))
  939. ;; Class-level.
  940. (setq indent (eif-class-level-kw-indent-m))))
  941. ;; There's possibly a better way of coding this exception.
  942. ((looking-at eif-once-non-indent-regexp)
  943. (setq indent (eif-calc-indent-non-keyword)))
  944. ((looking-at eif-indexing-keyword-regexp)
  945. ;; Class-level or minor occurence?
  946. (if (string-match eif-check-keyword (eif-matching-kw eif-check-keywords-regexp))
  947. ;; In check.
  948. (setq indent (+ eif-matching-indent (eif-check-keyword-indent-m)))
  949. (if (save-excursion (eif-find-beginning-of-feature))
  950. ;; Minor. (BUG: "note" can also be at the END of the class!!!)
  951. (setq indent (eif-calc-indent-non-keyword))
  952. ;; Class-level.
  953. (setq indent (eif-class-level-kw-indent-m)))))
  954. ((looking-at eif-class-level-keywords-regexp)
  955. ;; File level keywords (indent defaults to 0)
  956. (setq indent (eif-class-level-kw-indent-m)))
  957. ((looking-at eif-inherit-level-keywords)
  958. ;; Inherit level keywords (indent defaults to
  959. ;; 2*eif-indent-increment)
  960. (setq indent (eif-inherit-level-kw-indent-m)))
  961. ((looking-at eif-feature-level-keywords-regexp)
  962. ;; Feature level keywords (indent defaults to
  963. ;; (eif-feature-level-indent-m) + eif-indent-increment)
  964. (setq indent (eif-feature-level-kw-indent-m)))
  965. ((looking-at eif-end-keyword)
  966. ;; End keyword (indent to level of matching keyword)
  967. (if (string-match "end"
  968. (eif-matching-kw
  969. eif-end-matching-keywords-regexp))
  970. ;; Then
  971. (if (= eif-matching-indent
  972. (eif-feature-level-kw-indent-m))
  973. ;; Then
  974. (setq indent (eif-class-level-kw-indent-m))
  975. ;; Else
  976. (setq indent
  977. (- eif-matching-indent eif-indent-increment)))
  978. ;; Else
  979. (setq indent eif-matching-indent))
  980. ;; FIXME: This is broken!!!
  981. (if (<= indent (eif-feature-level-indent-m))
  982. (save-excursion
  983. (end-of-line)
  984. (while (and (< (point) (point-max))
  985. (or (forward-char 1) t)
  986. (looking-at eif-non-source-line))
  987. (end-of-line))
  988. (if (not (looking-at eif-non-source-line))
  989. (setq indent (eif-inherit-level-kw-indent-m))
  990. (setq indent (eif-class-level-kw-indent-m))))))
  991. ((looking-at eif-control-flow-keywords)
  992. ;; Control flow keywords
  993. ;; Indent to same level as a preceding "end" or
  994. ;; if no preceding "end" is found, indent to the level
  995. ;; of the preceding "do" plus the value of
  996. ;; eif-indent-increment
  997. (setq kw-match
  998. (eif-matching-kw
  999. eif-control-flow-matching-keywords-regexp))
  1000. (cond ((string-match "end" kw-match)
  1001. (setq indent eif-matching-indent))
  1002. (t
  1003. (setq indent
  1004. (+ eif-matching-indent eif-indent-increment)))))
  1005. ((looking-at eif-check-keywords-regexp)
  1006. ;; Check keyword
  1007. ;; Indent to level of preceding "end"+eif-indent-increment or
  1008. ;; if no preceding "end" is found, indent to the level of
  1009. ;; the preceding eif-check-matching-keywords-regexp plus the
  1010. ;; value (eif-indent-increment + eif-check-keyword-indent).
  1011. (setq kw-match (eif-matching-kw
  1012. eif-check-matching-keywords-regexp))
  1013. (cond ((string-match "end" kw-match)
  1014. (setq indent (+ eif-matching-indent
  1015. (eif-check-keyword-indent-m))))
  1016. (t
  1017. (setq indent
  1018. (+ eif-matching-indent
  1019. (+ eif-indent-increment
  1020. (eif-check-keyword-indent-m)))))))
  1021. ((looking-at eif-rescue-keywords-regexp)
  1022. ;; Rescue keyword
  1023. ;; Indent to level of preceding "end"+eif-indent-increment or
  1024. ;; if no preceding "end" is found, indent to the level of
  1025. ;; the preceding eif-rescue-matching-keywords-regexp plus the
  1026. ;; value (eif-indent-increment + eif-rescue-keyword-indent).
  1027. (setq kw-match (eif-matching-kw
  1028. eif-rescue-matching-keywords-regexp))
  1029. (cond ((string-match "end" kw-match)
  1030. (setq indent (+ eif-matching-indent
  1031. (eif-rescue-keyword-indent-m))))
  1032. (t
  1033. (setq indent eif-matching-indent))))
  1034. ((looking-at eif-from-level-keywords-regexp)
  1035. ;; From level keywords (indent to level of matching "From")
  1036. (if (string-match "end" (eif-matching-kw eif-from-keyword))
  1037. ;; Closest matching KW is `end'.
  1038. (setq indent (- eif-matching-indent eif-indent-increment))
  1039. ;; Closest matching KW is one of `eif-from-keyword'.
  1040. (setq indent eif-matching-indent)))
  1041. ((looking-at eif-if-or-inspect-level-keywords-regexp)
  1042. ;; If level keywords (indent to level of matching
  1043. ;; "If" or "Inspect")
  1044. (if (string-match "end"
  1045. (eif-matching-kw
  1046. eif-if-or-inspect-keyword-regexp))
  1047. ;; Closest matching KW is `end'.
  1048. (setq indent (- eif-matching-indent eif-indent-increment))
  1049. ;; Closest matching KW is one of `eif-if-or-inspect-keyword-regexp'.
  1050. (setq indent eif-matching-indent)))
  1051. ((looking-at eif-solitary-then-keyword)
  1052. ;; Handles case where "then" appears on a line by itself
  1053. (if (eif-matching-kw eif-then-matching-keywords t)
  1054. ;; (Indented to level of the matching if, elseif or when)
  1055. (setq indent (+ eif-matching-indent (eif-then-indent-m)))
  1056. (if (save-excursion (eif-find-beginning-of-feature))
  1057. ;; (Feature-level "then")
  1058. (setq indent (eif-feature-level-kw-indent-m))
  1059. (message "Non-matching 'then'")
  1060. (setq indent (eif-calc-indent-non-keyword)))))
  1061. ((looking-at eif-invariant-keyword)
  1062. ;; Invariant keyword
  1063. ;; (Indented to level of the matching from or feature)
  1064. (if (string-match "from"
  1065. (eif-matching-kw eif-invariant-matchi