;; This Emacs Lisp code will adapt Martin Schwenke's eiffel-mode to ;; conform to the Gobo standard. ;; tested with version 2.67 (require 'eiffel) ;; the following extension are recognized as Eiffel files (add-to-list 'auto-mode-alist '("\\.e\\'" . eiffel-mode)) (add-to-list 'auto-mode-alist '("\\.ge\\'" . eiffel-mode)) ;; in case all your Lex/Yacc is done with Eiffel, define this (add-to-list 'auto-mode-alist '("\\.y\\'" . eiffel-mode)) (add-to-list 'auto-mode-alist '("\\.l\\'" . eiffel-mode)) ;; assumes package whitespace is available ;; tested with version 1.9 ;; I downloaded it from: ;; ftp://ftp.splode.com/pub/users/friedman/emacs-lisp/whitespace.el (require 'whitespace) ;; remove white space at end of lines in Eiffel mode (add-to-list 'nuke-trailing-whitespace-always-major-modes 'eiffel-mode) (defun gobonize () "Makes sure current class obeys Gobo standards. Be careful, not foolproof." (interactive) (save-excursion (my-eiffel-tabify) ;; remove semi-colon at end of lines ;; this will remove semi-colons in method parameters lists as well! (goto-char (point-min)) (while (re-search-forward";[\t ]*$" nil t) (replace-match "" nil nil)) ;; replace semi-colon with end comment (goto-char (point-min)) (while (re-search-forward"\;[\t ]*--[\t ]*\\(.*\\)$" nil t) (replace-match " -- \\1" nil nil)) ;; remove comments after end ;; should remove end comments of functions, and class end comment (goto-char (point-min)) (while (re-search-forward"\\bend\\b[\t ]*--[\t ]*.*$" nil t) (replace-match "end" nil nil)) ;; convert !! to create (goto-char (point-min)) (while (re-search-forward"^[\t]*\\(!![ \t]*\\)" nil t) (replace-match "create " nil nil nil 1)) ;; reindent everything (indent-region (point-min) (point-max) nil) ;; single space after feature name and colon (goto-char (point-min)) (while (re-search-forward"\\([A-Za-z0-9_]+\\)[\t ]*:[\t ]*\\([A-Za-z0-9_]+\\)" nil t) (replace-match "\\1: \\2" nil nil)) ;; single space between feature name and ( (goto-char (point-min)) (while (re-search-forward"[\t]+[a-zA-Z0-9_.:= ]*[a-zA-Z0-9_]+\\([\t ]*\\)(" nil t) (replace-match " " nil nil nil 1)) ;; single space between ARRAY [INTEGER] (goto-char (point-min)) (while (re-search-forward":[\t ]+\\([A-Z0-9_]+\\)[\t ]*\\[\\([A-Z0-9_]+\\)\\]" nil t) (replace-match ": \\1 [\\2]")) ;; { FOO } should become {FOO} (goto-char (point-min)) (while (re-search-forward"{[\t ]*\\([A-Z0-9_]+\\)[\t ]*}" nil t) (replace-match "{\\1}")) ;; remove {ANY} in feature {ANY} clauses (goto-char (point-min)) (while (re-search-forward"^feature[\t ]*\\({ANY}\\)[\t ]*" nil t) (replace-match "feature ")) ) ) (defun my-eiffel-tabify () "Tabify the entire buffer. This is meant to be added to local-write-file-hooks when needed." (interactive) (save-excursion ;; convert lines with spaces to as many tabs as possible ;; can leave some space behind ;; tabify causes undo to loose information some how (tabify (point-min) (point-max)) ;; convert lines which have both tabs and spaces to tabs only (goto-char (point-min)) (while (re-search-forward "^\\(\t +\\| +\t\\)" nil t) (replace-match "\t")) ;; this converts tabs which are not at the beginning of a line to ;; spaces only ;; I didn't get this to work somehow, so don't use tabs... ; (goto-char (point-min)) ; (while (re-search-forward "[^ \t]+\\(\t+\\)" nil t) ; (replace-match " " nil nil nil 1)) ) ) ;; when editing SmallEiffel source, execute this function ;; perhaps should become a minor mode? (defun smalleiffel-source-mode () "Makes sure SmallEiffel source can be edited without making major changes to the style. Execute this function after you've loaded a SmallEiffel source file." (interactive) (set-variable 'tab-width 8) (setq indent-tabs-mode nil) (remove-hook 'local-write-file-hooks 'my-eiffel-tabify) ) ;; useful to enter a precondition for a string variable. (define-skeleton eif-string-not-empty "Generate a precondition that a string may not be empty." (completing-read "name of STRING variable: " nil) str "_not_empty: " str " /= Void and then not " str ".is_empty" ) ;; Settings I find useful (defun my-eiffel-mode-hook () ;; do not autofill with Eiffel (auto-fill-mode -1) ;; you might have enabled tabs or not, but in Eiffel mode you have no choice. (setq indent-tabs-mode t) ;; only tabify for white space at beginning of line (setq tabify-regexp "^[ \t]+") ; ;; only needed for eiffel-mode 2.15 or earlier ; (setq tab-width eif-indent-increment) ;; turn this hook off if saving is too slow on your machine ;; I also experienced with this that undo after save doesn't work any more. (add-hook 'local-write-file-hooks 'my-eiffel-tabify) ) (setq eiffel-mode-hook 'my-eiffel-mode-hook) ;; global Eiffel options, no need to set them per buffer ;; and as these are Gobo required settings, no need to customize them (setq eif-inherit-level-kw-indent 2) (setq eif-check-keyword-indent 1) (setq eif-extra-then-indent 0) (setq eif-string-continuation-indent 1) ;; assertion continuations are on next line with one additional tab ;; under the assertion tag ;; (defun eif-indent-assertion-continuation (id-colon) ;; "Generally, are we in line that is a continuation of an assertion? ;; More precisely, are we inside a pre or a post condition clause on a ;; line that is a continuation of a multi-line assertion beginning with a ;; tag? If so, return the indentation of the continuation line. The ;; argument ID-COLON is t if the line we are indenting begins with ;; \" :\", and nil otherwise." ;; (let ((limit (point))) ;; (if (save-excursion ;; (if (re-search-backward (concat eif-feature-level-keywords "\\|" ;; eif-end-keyword-regexp) nil t) ;; (if (looking-at "ensure\\|require") ;; (setq limit (point))))) ;; (save-excursion ;; (end-of-line) ;; (if (and (not id-colon) (re-search-backward ": *" limit t)) ;; (progn ;; (+ (eif-current-line-indent) eif-indent-increment))) ;; ) ;; )))