(cperl-set-style-back): Fix spelling in docstrings.
[bpt/emacs.git] / lisp / progmodes / simula.el
index 0f8f5a0..b909753 100644 (file)
@@ -1,10 +1,9 @@
 ;;; simula.el --- SIMULA 87 code editing commands for Emacs
 
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1994, 1996 Free Software Foundation, Inc.
 
 ;; Author: Hans Henrik Eriksen <hhe@ifi.uio.no>
 ;; Maintainer: simula-mode@ifi.uio.no
-;; Version: 0.992
 ;; Adapted-By: ESR
 ;; Keywords: languages
 
@@ -21,8 +20,9 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
 
 ;;; Code:
 
-(provide 'simula-mode)
+\f
+(defgroup simula nil
+  "Major mode for editing Simula code."
+  :prefix "simula-"
+  :group 'languages)
 
-(defconst simula-tab-always-indent nil
-  "*Non-nil means TAB in SIMULA mode should always reindent the current line.
+(defconst simula-tab-always-indent-default nil
+  "Non-nil means TAB in SIMULA mode should always reindent the current line.
 Otherwise TAB indents only when point is within
 the run of whitespace at the beginning of the line.")
 
-(defconst simula-indent-level 3
-  "*Indentation of SIMULA statements with respect to containing block.")
+(defcustom simula-tab-always-indent simula-tab-always-indent-default
+  "*Non-nil means TAB in SIMULA mode should always reindent the current line.
+Otherwise TAB indents only when point is within
+the run of whitespace at the beginning of the line."
+  :type 'boolean
+  :group 'simula)
 
-(defconst simula-substatement-offset 3
-  "*Extra indentation after DO, THEN, ELSE, WHEN and OTHERWISE.")
+(defconst simula-indent-level-default 3
+  "Indentation of SIMULA statements with respect to containing block.")
 
-(defconst simula-continued-statement-offset 3
-  "*Extra indentation for lines not starting a statement or substatement.
+(defcustom simula-indent-level simula-indent-level-default
+  "*Indentation of SIMULA statements with respect to containing block."
+  :type 'integer
+  :group 'simula)
+
+
+(defconst simula-substatement-offset-default 3
+  "Extra indentation after DO, THEN, ELSE, WHEN and OTHERWISE.")
+
+(defcustom simula-substatement-offset simula-substatement-offset-default
+  "*Extra indentation after DO, THEN, ELSE, WHEN and OTHERWISE."
+  :type 'integer
+  :group 'simula)
+
+(defconst simula-continued-statement-offset-default 3
+  "Extra indentation for lines not starting a statement or substatement.
 If value is a list, each line in a multipleline continued statement
 will have the car of the list extra indentation with respect to
 the previous line of the statement.")
 
-(defconst simula-label-offset -4711
-  "*Offset of SIMULA label lines relative to usual indentation.")
+(defcustom simula-continued-statement-offset
+  simula-continued-statement-offset-default
+  "*Extra indentation for lines not starting a statement or substatement.
+If value is a list, each line in a multipleline continued statement
+will have the car of the list extra indentation with respect to
+the previous line of the statement."
+  :type 'integer
+  :group 'simula)
 
-(defconst simula-if-indent '(0 . 0)
-  "*Extra indentation of THEN and ELSE with respect to the starting IF.
+(defconst simula-label-offset-default -4711
+  "Offset of SIMULA label lines relative to usual indentation.")
+
+(defcustom simula-label-offset simula-label-offset-default
+  "*Offset of SIMULA label lines relative to usual indentation."
+  :type 'integer
+  :group 'simula)
+
+(defconst simula-if-indent-default '(0 . 0)
+  "Extra indentation of THEN and ELSE with respect to the starting IF.
 Value is a cons cell, the car is extra THEN indentation and the cdr
 extra ELSE indentation.  IF after ELSE is indented as the starting IF.")
 
-(defconst simula-inspect-indent '(0 . 0)
-  "*Extra indentation of WHEN and OTHERWISE with respect to the INSPECT.
+(defcustom simula-if-indent simula-if-indent-default
+  "*Extra indentation of THEN and ELSE with respect to the starting IF.
+Value is a cons cell, the car is extra THEN indentation and the cdr
+extra ELSE indentation.  IF after ELSE is indented as the starting IF."
+  :type '(cons integer integer)
+  :group 'simula)
+
+(defconst simula-inspect-indent-default '(0 . 0)
+  "Extra indentation of WHEN and OTHERWISE with respect to the INSPECT.
 Value is a cons cell, the car is extra WHEN indentation
 and the cdr extra OTHERWISE indentation.")
 
-(defconst simula-electric-indent nil
-  "*Non-nil means `simula-indent-line' function may reindent previous line.")
+(defcustom simula-inspect-indent simula-inspect-indent-default
+  "*Extra indentation of WHEN and OTHERWISE with respect to the INSPECT.
+Value is a cons cell, the car is extra WHEN indentation
+and the cdr extra OTHERWISE indentation."
+  :type '(cons integer integer)
+  :group 'simula)
+
+(defconst simula-electric-indent-default nil
+  "Non-nil means `simula-indent-line' function may reindent previous line.")
 
-(defconst simula-abbrev-keyword 'upcase
-  "*Specify how to convert case for SIMULA keywords.
+(defcustom simula-electric-indent simula-electric-indent-default
+  "*Non-nil means `simula-indent-line' function may reindent previous line."
+  :type 'boolean
+  :group 'simula)
+
+(defconst simula-abbrev-keyword-default 'upcase
+  "Specify how to convert case for SIMULA keywords.
 Value is one of the symbols `upcase', `downcase', `capitalize',
 \(as in) `abbrev-table' or nil if they should not be changed.")
 
-(defconst simula-abbrev-stdproc 'abbrev-table
-  "*Specify how to convert case for standard SIMULA procedure and class names.
+(defcustom simula-abbrev-keyword simula-abbrev-keyword-default
+  "*Specify how to convert case for SIMULA keywords.
+Value is one of the symbols `upcase', `downcase', `capitalize',
+\(as in) `abbrev-table' or nil if they should not be changed."
+  :type '(choice (const upcase) (const downcase) (const capitalize)(const nil))
+  :group 'simula)
+
+(defconst simula-abbrev-stdproc-default 'abbrev-table
+  "Specify how to convert case for standard SIMULA procedure and class names.
 Value is one of the symbols `upcase', `downcase', `capitalize',
 \(as in) `abbrev-table', or nil if they should not be changed.")
 
-(defvar simula-abbrev-file nil
+(defcustom simula-abbrev-stdproc simula-abbrev-stdproc-default
+  "*Specify how to convert case for standard SIMULA procedure and class names.
+Value is one of the symbols `upcase', `downcase', `capitalize',
+\(as in) `abbrev-table', or nil if they should not be changed."
+  :type '(choice (const upcase) (const downcase) (const capitalize)
+         (const abbrev-table) (const nil))
+  :group 'simula)
+
+(defcustom simula-abbrev-file nil
   "*File with extra abbrev definitions for use in SIMULA mode.
 These are used together with the standard abbrev definitions for SIMULA.
 Please note that the standard definitions are required
-for SIMULA mode to function correctly.")
+for SIMULA mode to function correctly."
+  :type '(choice file (const nil))
+  :group 'simula)
 
 (defvar simula-mode-syntax-table nil
   "Syntax table in SIMULA mode buffers.")
 
+(defconst simula-font-lock-syntactic-keywords
+  `(;; `comment' directive.
+    ("\\<\\(c\\)omment\\>" 1 "<")
+    ;; end comments
+    (,(concat "\\<end\\>\\([^;\n]\\).*?\\(\n\\|\\(.\\)\\(;\\|"
+             (regexp-opt '("end" "else" "when" "otherwise"))
+             "\\)\\)")
+     (1 "< b")
+     (3 "> b" nil t))
+    ;; non-quoted single-quote char.
+    ("'\\('\\)'" 1 ".")))
+
+;; Regexps written with help from Alf-Ivar Holm <alfh@ifi.uio.no>.
+(defconst simula-font-lock-keywords-1
+  '(;;
+    ;; Compiler directives.
+    ("^%\\([^ \t\n].*\\)" 1 font-lock-constant-face t)
+    ;;
+    ;; Class and procedure names.
+    ("\\<\\(class\\|procedure\\)\\>[ \t]*\\(\\sw+\\)?"
+     (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
+  "Subdued level highlighting for Simula mode.")
+
+(defconst simula-font-lock-keywords-2
+  (append simula-font-lock-keywords-1
+   (list
+    ;;
+    ;; Constants.
+    '("\\<\\(false\\|none\\|notext\\|true\\)\\>" . font-lock-constant-face)
+    ;;
+    ;; Keywords.
+    (regexp-opt
+     '("activate" "after" "and" "at" "before" "begin" "delay" "do"
+       "else" "end" "eq" "eqv" "external" "for" "ge" "go" "goto" "gt"
+       "hidden" "if" "imp" "in" "inner" "inspect" "is" "label" "le"
+       "lt" "ne" "new" "not" "or" "otherwise" "prior" "protected"
+       "qua" "reactivate" "step" "switch" "then" "this" "to" "until"
+       "virtual" "when" "while") 'words)
+    ;;
+    ;; Types.
+    (cons (regexp-opt
+          '("array" "boolean" "character" "integer"
+            "long" "name" "real" "short" "text" "value" "ref") 'words)
+         'font-lock-type-face)))
+  "Medium level highlighting for Simula mode.")
+
+(defconst simula-font-lock-keywords-3
+  (append simula-font-lock-keywords-2
+   (list
+    ;;
+    ;; Super-class names and super-slow.
+    '("\\<\\(\\sw+\\)[ \t]+class\\>" 1 font-lock-function-name-face)
+    ;;
+    ;; Types and their declarations.
+    (list (concat "\\<\\(array\\|boolean\\|character\\|integer\\|"
+                 "long\\|name\\|real\\|short\\|text\\|value\\)\\>"
+                 "\\([ \t]+\\sw+\\>\\)*")
+         '(font-lock-match-c-style-declaration-item-and-skip-to-next
+           ;; Start with point after all type specifiers.
+           (goto-char (or (match-beginning 2) (match-end 1)))
+           ;; Finish with point after first type specifier.
+           (goto-char (match-end 1))
+           ;; Fontify as a variable name.
+           (1 font-lock-variable-name-face)))
+    ;;
+    ;; Object references and their declarations.
+    '("\\<\\(ref\\)\\>[ \t]*\\((\\(\\sw+\\))\\)?"
+      (3 font-lock-function-name-face nil t)
+      (font-lock-match-c-style-declaration-item-and-skip-to-next nil nil
+       (1 font-lock-variable-name-face)))
+    ))
+  "Gaudy level highlighting for Simula mode.")
+
+(defvar simula-font-lock-keywords simula-font-lock-keywords-1
+  "Default expressions to highlight in Simula mode.")
+
+; The following function is taken from cc-mode.el,
+; it determines the flavor of the Emacs running
+
+(defvar simula-mode-menu
+  '(["Report Bug"            simula-submit-bug-report t]
+    ["Indent Line"           simula-indent-line t]
+    ["Backward Statement"     simula-previous-statement t]
+    ["Forward Statement"      simula-next-statement t]
+    ["Backward Up Level"      simula-backward-up-level t]
+    ["Forward Down Statement" simula-forward-down-level t])
+  "Lucid Emacs menu for SIMULA mode.")
+
 (if simula-mode-syntax-table
     ()
   (setq simula-mode-syntax-table (copy-syntax-table (standard-syntax-table)))
   (modify-syntax-entry ?!  "<"    simula-mode-syntax-table)
   (modify-syntax-entry ?$  "."    simula-mode-syntax-table)
-  (modify-syntax-entry ?%  "."    simula-mode-syntax-table)
+  (modify-syntax-entry ?%  "< b"  simula-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b"  simula-mode-syntax-table)
   (modify-syntax-entry ?'  "\""   simula-mode-syntax-table)
   (modify-syntax-entry ?\( "()"   simula-mode-syntax-table)
   (modify-syntax-entry ?\) ")("   simula-mode-syntax-table)
@@ -103,85 +264,115 @@ for SIMULA mode to function correctly.")
   (modify-syntax-entry ?\[ "."    simula-mode-syntax-table)
   (modify-syntax-entry ?\\ "."    simula-mode-syntax-table)
   (modify-syntax-entry ?\] "."    simula-mode-syntax-table)
-  (modify-syntax-entry ?_  "w"    simula-mode-syntax-table)
+  (modify-syntax-entry ?_  "_"    simula-mode-syntax-table)
   (modify-syntax-entry ?\| "."    simula-mode-syntax-table)
   (modify-syntax-entry ?\{ "."    simula-mode-syntax-table)
   (modify-syntax-entry ?\} "."    simula-mode-syntax-table))
 
-(defvar simula-mode-map ()
-  "Keymap used in SIMULA mode.")
-
-(if simula-mode-map
-    ()
-  (setq simula-mode-map (make-sparse-keymap))
-  (define-key simula-mode-map "\C-c\C-u"   'simula-backward-up-level)
-  (define-key simula-mode-map "\C-c\C-p"   'simula-previous-statement)
-  (define-key simula-mode-map "\C-c\C-d"   'simula-forward-down-level)
-  (define-key simula-mode-map "\C-c\C-n"   'simula-next-statement)
-  ;(define-key simula-mode-map "\C-c\C-g"   'simula-goto-definition)
-  ;(define-key simula-mode-map "\C-c\C-h"   'simula-standard-help)
-  (define-key simula-mode-map "\177"       'backward-delete-char-untabify)
-  (define-key simula-mode-map ":"          'simula-electric-label)
-  (define-key simula-mode-map "\t"         'simula-indent-command))
-
-(defvar simula-mode-abbrev-table nil
-  "Abbrev table in SIMULA mode buffers")
-
-
-(defun simula-mode ()
+(defvar simula-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\C-u"   'simula-backward-up-level)
+    (define-key map "\C-c\C-p"   'simula-previous-statement)
+    (define-key map "\C-c\C-d"   'simula-forward-down-level)
+    (define-key map "\C-c\C-n"   'simula-next-statement)
+    ;; (define-key map "\C-c\C-g"   'simula-goto-definition)
+    ;; (define-key map "\C-c\C-h"   'simula-standard-help)
+    (define-key map "\177"       'backward-delete-char-untabify)
+    (define-key map ":"          'simula-electric-label)
+    (define-key map "\e\C-q"     'simula-indent-exp)
+    (define-key map "\t"         'simula-indent-command)
+    ;; Emacs 19 defines menus in the mode map
+    (define-key map [menu-bar simula]
+      (cons "SIMULA" (make-sparse-keymap "SIMULA")))
+    (define-key map [menu-bar simula bug-report]
+      '("Submit Bug Report" . simula-submit-bug-report))
+    (define-key map [menu-bar simula separator-indent]
+      '("--"))
+    (define-key map [menu-bar simula indent-exp]
+      '("Indent Expression" . simula-indent-exp))
+    (define-key map [menu-bar simula indent-line]
+      '("Indent Line" . simula-indent-command))
+    (define-key map [menu-bar simula separator-navigate]
+      '("--"))
+    (define-key map [menu-bar simula backward-stmt]
+      '("Previous Statement" . simula-previous-statement))
+    (define-key map [menu-bar simula forward-stmt]
+      '("Next Statement" . simula-next-statement))
+    (define-key map [menu-bar simula backward-up]
+      '("Backward Up Level" . simula-backward-up-level))
+    (define-key map [menu-bar simula forward-down]
+      '("Forward Down Statement" . simula-forward-down-level))
+
+    (put 'simula-next-statement 'menu-enable '(not (eobp)))
+    (put 'simula-previous-statement 'menu-enable '(not (bobp)))
+    (put 'simula-forward-down-level 'menu-enable '(not (eobp)))
+    (put 'simula-backward-up-level 'menu-enable '(not (bobp)))
+    (put 'simula-indent-command 'menu-enable '(not buffer-read-only))
+    (put 'simula-indent-exp 'menu-enable '(not buffer-read-only))
+
+    ;; RMS: mouse-3 should not select this menu.  mouse-3's global
+    ;; definition is useful in SIMULA mode and we should not interfere
+    ;; with that.  The menu is mainly for beginners, and for them,
+    ;; the menubar requires less memory than a special click.
+    ;; in Lucid Emacs, we want the menu to popup when the 3rd button is
+    ;; hit.  In 19.10 and beyond this is done automatically if we put
+    ;; the menu on mode-popup-menu variable, see c-common-init [cc-mode.el]
+    ;;(if (not (boundp 'mode-popup-menu))
+    ;; (define-key simula-mode-map 'button3 'simula-popup-menu))
+    map)
+  "Keymap used in `simula-mode'.")
+
+;; menus for Lucid
+(defun simula-popup-menu (e)
+  "Pops up the SIMULA menu."
+  (interactive "@e")
+  (popup-menu (cons (concat mode-name " Mode Commands") simula-mode-menu)))
+
+;;;###autoload
+(define-derived-mode simula-mode nil "Simula"
   "Major mode for editing SIMULA code.
 \\{simula-mode-map}
 Variables controlling indentation style:
- simula-tab-always-indent
+ `simula-tab-always-indent'
     Non-nil means TAB in SIMULA mode should always reindent the current line,
     regardless of where in the line point is when the TAB command is used.
- simula-indent-level
+ `simula-indent-level'
     Indentation of SIMULA statements with respect to containing block.
- simula-substatement-offset
+ `simula-substatement-offset'
     Extra indentation after DO, THEN, ELSE, WHEN and OTHERWISE.
simula-continued-statement-offset 3
`simula-continued-statement-offset' 3
     Extra indentation for lines not starting a statement or substatement,
     e.g. a nested FOR-loop.  If value is a list, each line in a multiple-
     line continued statement will have the car of the list extra indentation
     with respect to the previous line of the statement.
simula-label-offset -4711
`simula-label-offset' -4711
     Offset of SIMULA label lines relative to usual indentation.
simula-if-indent '(0 . 0)
`simula-if-indent' '(0 . 0)
     Extra indentation of THEN and ELSE with respect to the starting IF.
     Value is a cons cell, the car is extra THEN indentation and the cdr
     extra ELSE indentation.  IF after ELSE is indented as the starting IF.
simula-inspect-indent '(0 . 0)
`simula-inspect-indent' '(0 . 0)
     Extra indentation of WHEN and OTHERWISE with respect to the
     corresponding INSPECT.  Value is a cons cell, the car is
     extra WHEN indentation and the cdr extra OTHERWISE indentation.
simula-electric-indent nil
`simula-electric-indent' nil
     If this variable is non-nil, `simula-indent-line'
     will check the previous line to see if it has to be reindented.
simula-abbrev-keyword 'upcase
`simula-abbrev-keyword' 'upcase
     Determine how SIMULA keywords will be expanded.  Value is one of
     the symbols `upcase', `downcase', `capitalize', (as in) `abbrev-table',
     or nil if they should not be changed.
simula-abbrev-stdproc 'abbrev-table
`simula-abbrev-stdproc' 'abbrev-table
     Determine how standard SIMULA procedure and class names will be
     expanded.  Value is one of the symbols `upcase', `downcase', `capitalize',
     (as in) `abbrev-table', or nil if they should not be changed.
 
 Turning on SIMULA mode calls the value of the variable simula-mode-hook
-with no arguments, if that value is non-nil
-
-Warning: simula-mode-hook should not read in an abbrev file without calling
-the function simula-install-standard-abbrevs afterwards, preferably not
-at all."
-  (interactive)
-  (kill-all-local-variables)
-  (use-local-map simula-mode-map)
-  (setq major-mode 'simula-mode)
-  (setq mode-name "SIMULA")
+with no arguments, if that value is non-nil."
   (make-local-variable 'comment-column)
   (setq comment-column 40)
-  (make-local-variable 'end-comment-column)
-  (setq end-comment-column 75)
-  (set-syntax-table simula-mode-syntax-table)
+;  (make-local-variable 'end-comment-column)
+;  (setq end-comment-column 75)
   (make-local-variable 'paragraph-start)
   (setq paragraph-start "[ \t]*$\\|\\f")
   (make-local-variable 'paragraph-separate)
@@ -189,7 +380,7 @@ at all."
   (make-local-variable 'indent-line-function)
   (setq indent-line-function 'simula-indent-line)
   (make-local-variable 'require-final-newline)
-  (setq require-final-newline t)
+  (setq require-final-newline mode-require-final-newline)
   (make-local-variable 'comment-start)
   (setq comment-start "! ")
   (make-local-variable 'comment-end)
@@ -200,17 +391,34 @@ at all."
   (setq parse-sexp-ignore-comments nil)
   (make-local-variable 'comment-multi-line)
   (setq comment-multi-line t)
-  (if simula-mode-abbrev-table
-      ()
-    (if simula-abbrev-file
-       (read-abbrev-file simula-abbrev-file)
-      (define-abbrev-table 'simula-mode-abbrev-table ()))
-    (let (abbrevs-changed)
-      (simula-install-standard-abbrevs)))
-  (setq local-abbrev-table simula-mode-abbrev-table)
-  (abbrev-mode 1)
-  (run-hooks 'simula-mode-hook))
-
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults
+       '((simula-font-lock-keywords simula-font-lock-keywords-1
+          simula-font-lock-keywords-2 simula-font-lock-keywords-3)
+         nil t ((?_ . "w")) nil
+         (font-lock-syntactic-keywords . simula-font-lock-syntactic-keywords)))
+  (abbrev-mode 1))
+
+(defun simula-indent-exp ()
+  "Indent SIMULA expression following point."
+  (interactive)
+  (let ((here (point))
+       (simula-electric-indent nil)
+       end)
+    (simula-skip-comment-forward)
+    (if (eobp)
+       (goto-char here)
+      (unwind-protect
+         (progn
+           (simula-next-statement 1)
+           (setq end (point-marker))
+           (simula-previous-statement 1)
+           (beginning-of-line)
+           (while (< (point) end)
+             (if (not (looking-at "[ \t]*$"))
+                 (simula-indent-line))
+             (forward-line 1)))
+       (and end (set-marker end nil))))))
 
 
 (defun simula-indent-line ()
@@ -220,27 +428,26 @@ If `simula-electric-indent' is non-nil, indent previous line if necessary."
        (indent (simula-calculate-indent))
        (case-fold-search t))
     (unwind-protect
-       (progn
-         ;;
-         ;; manually expand abbrev on last line, if any
-         ;;
-         (end-of-line 0)
-         (expand-abbrev)
-         ;; now maybe we should reindent that line
-         (if simula-electric-indent
-             (progn
-               (beginning-of-line)
-               (skip-chars-forward " \t\f")
-               (if (and
-                    (looking-at
-                     "\\(end\\|if\\|then\\|else\\|when\\|otherwise\\)\\>")
-                    (not (simula-context)))
-                   ;; yes - reindent
-                   (let ((post-indent (simula-calculate-indent)))
-                     (if (eq (current-indentation) post-indent)
-                         ()
-                       (delete-horizontal-space)
-                       (indent-to post-indent)))))))
+       (if simula-electric-indent
+           (progn
+             ;;
+             ;; manually expand abbrev on last line, if any
+             ;;
+             (end-of-line 0)
+             (expand-abbrev)
+             ;; now maybe we should reindent that line
+             (beginning-of-line)
+             (skip-chars-forward " \t\f")
+             (if (and
+                  (looking-at
+                   "\\(end\\|if\\|then\\|else\\|when\\|otherwise\\)\\>")
+                  (not (simula-context)))
+                 ;; yes - reindent
+                 (let ((post-indent (simula-calculate-indent)))
+                   (if (eq (current-indentation) post-indent)
+                       ()
+                     (delete-horizontal-space)
+                     (indent-to post-indent))))))
       (goto-char (- (point-max) origin))
       (if (eq (current-indentation) indent)
          (back-to-indentation)
@@ -299,7 +506,7 @@ The relative indentation among the lines of the statement are preserved."
 
 
 (defun simula-context ()
-  "Returns value according to syntactic SIMULA context of point.
+  "Return value according to syntactic SIMULA context of point.
     0    point inside COMMENT comment
     1    point on SIMULA-compiler directive line
     2    point inside END comment
@@ -363,14 +570,22 @@ The relative indentation among the lines of the statement are preserved."
          (cond
           ((memq (preceding-char) '(?d ?D))
            (setq return-value 2)
-           (while (and (memq (preceding-char) '(?d ?D)) (not return-value))
-             (while (and (re-search-forward
-                          ";\\|\\<end\\>\\|\\<else\\>\\|\\<otherwise\\>\\|\\<when\\>\\|^%"
-                          origin 'move)
-                         (eq (preceding-char) ?%))
-               (beginning-of-line 2)))
-           (if (looking-at "[ \t\n\f]*\\(;\\|\\<end\\>\\|\\<else\\>\\|\\<otherwise\\>\\|\\<when\\>\\)")
-               (setq return-value nil)))
+           (while (and (re-search-forward
+                        ";\\|\\<end\\>\\|\\<else\\>\\|\\<otherwise\\>\\|\\<when\\>\\|^%"
+                        origin 'move)
+                       ;; found another END?
+                       (or (memq (preceding-char) '(?d ?D))
+                           ;; if directive, skip line
+                           (and (eq (preceding-char) ?%)
+                                (beginning-of-line 2))
+                           ;; found other keyword, out of END comment
+                           (setq return-value nil))))
+           (if (and (eq (char-syntax (preceding-char)) ?w)
+                    (eq (char-syntax (following-char)) ?w))
+               (save-excursion
+                 (backward-word 1)
+                 (if (looking-at "end\\>\\|else\\>\\|otherwise\\>\\|when\\>")
+                     (setq return-value nil)))))
           ((memq (preceding-char) '(?! ?t ?T))
            ; skip comment
            (setq return-value 0)
@@ -405,10 +620,11 @@ The relative indentation among the lines of the statement are preserved."
   (let ((origin (- (point-max) (point)))
        (case-fold-search t)
        ;; don't mix a label with an assignment operator := :-
-       ;; therefore look at next typed character...
-       (next-char (setq unread-command-events (list (read-event))))
-       (com-char last-command-char))
+       ;; therefore take a peek at next typed character...
+       (next-char (read-event)))
     (unwind-protect
+       (setq unread-command-events (append unread-command-events
+                                           (list next-char)))
        ;; Problem: find out if character just read is a command char
        ;; that would insert something after ':' making it a label.
        ;; At least \n, \r (and maybe \t) falls into this category.
@@ -435,7 +651,7 @@ The relative indentation among the lines of the statement are preserved."
              (delete-horizontal-space)
              (indent-to amount)))
       (goto-char (- (point-max) origin)))))
-       
+
 
 (defun simula-backward-up-level (count)
   "Move backward up COUNT block levels.
@@ -504,7 +720,7 @@ If COUNT is negative, move backward down block level instead."
              (goto-char origin)
              (signal 'quit nil))))))
 
-     
+
 (defun simula-previous-statement (count)
   "Move backward COUNT statements.
 If COUNT is negative, move forward instead."
@@ -515,6 +731,7 @@ If COUNT is negative, move forward instead."
          (case-fold-search t)
          (origin (point)))
       (condition-case ()
+         ;;
          (progn
            (simula-skip-comment-backward)
            (if (memq (preceding-char) '(?n ?N))
@@ -523,7 +740,8 @@ If COUNT is negative, move forward instead."
                  (if (not (looking-at "\\<begin\\>"))
                      (backward-word -1)))
              (if (eq (preceding-char) ?\;)
-                 (backward-char 1)))
+                 (backward-char 1))
+             )
            (while (and (natnump (setq count (1- count)))
                        (setq status (simula-search-backward
                                      ";\\|\\<begin\\>" nil 'move))))
@@ -563,7 +781,7 @@ If COUNT is negative, move backward instead."
        (quit (progn (goto-char origin) (signal 'quit nil)))))))
 
 
-(defun simula-skip-comment-backward ()
+(defun simula-skip-comment-backward (&optional stop-at-end)
   "Search towards bob to find first char that is outside a comment."
   (interactive)
   (catch 'simula-out
@@ -573,7 +791,9 @@ If COUNT is negative, move backward instead."
        (if (eq (preceding-char) ?\;)
            (save-excursion
              (backward-char 1)
-             (setq context (simula-context)))
+             (setq context (simula-context))
+             (if (and stop-at-end (eq context 2))
+                 (setq context nil)))
          (setq context (simula-context)))
        (cond
         ((memq context '(nil 3 4))
@@ -590,9 +810,10 @@ If COUNT is negative, move backward instead."
          (while (and (re-search-backward "!\\|\\<comment\\>")
                      (memq (simula-context) '(0 1)))))
         ((eq context 1)
-         (end-of-line 0)
+         (beginning-of-line)
          (if (bobp)
-             (throw 'simula-out nil)))
+             (throw 'simula-out nil)
+           (backward-char)))
         ((eq context 2)
          ;; an END-comment must belong to an END
          (re-search-backward "\\<end\\>")
@@ -609,6 +830,8 @@ If COUNT is negative, move backward instead."
   (catch 'simula-out
     (while t
       (skip-chars-forward " \t\n\f")
+      ;; BUG: the following (0 2) branches don't take into account intermixing
+      ;; directive lines
       (cond
        ((looking-at "!\\|\\<comment\\>")
        (search-forward ";" nil 'move))
@@ -665,6 +888,11 @@ If COUNT is negative, move backward instead."
        (prog1
            (current-column)
          (goto-char origin)))
+       ((eq where 1)
+       ;;
+       ;; Directive. Always 0.
+       ;;
+       0)
        ;;
        ;; Detect missing string delimiters
        ;;
@@ -721,7 +949,7 @@ If COUNT is negative, move backward instead."
               (looking-at "[a-z0-9_]*[ \t\f]*:[^-=]"))
          (setq indent simula-label-offset)))
        ;; find line with non-comment text
-       (simula-skip-comment-backward)
+       (simula-skip-comment-backward 'dont-skip-end)
        (if (and found-end
                 (not (eq (preceding-char) ?\;))
                 (if (memq (preceding-char) '(?N ?n))
@@ -799,7 +1027,7 @@ If COUNT is negative, move backward instead."
            ;; (at or before comment or label)
            ;; temp = t means finished
            (setq temp
-                 (and (not (simula-context))                   
+                 (and (not (simula-context))
                       (save-excursion
                         (skip-chars-forward " \t\f")
                         (or (looking-at "virtual")
@@ -886,7 +1114,7 @@ If COUNT is negative, move backward instead."
 
 
 (defun simula-find-do-match ()
-  "Find keyword matching DO: FOR, WHILE, INSPECT or WHEN"
+  "Find keyword matching DO: FOR, WHILE, INSPECT or WHEN."
   (while (and (re-search-backward
               "\\<\\(do\\|for\\|while\\|inspect\\|when\\|end\\|begin\\)\\>\\|;"
               nil 'move)
@@ -932,7 +1160,14 @@ If COUNT is negative, move backward instead."
     (cond
      ((eq simula-abbrev-stdproc 'upcase) (upcase-word -1))
      ((eq simula-abbrev-stdproc 'downcase) (downcase-word -1))
-     ((eq simula-abbrev-stdproc 'capitalize) (capitalize-word -1)))))
+     ((eq simula-abbrev-stdproc 'capitalize) (capitalize-word -1))
+     ((eq simula-abbrev-stdproc 'abbrev-table)
+      ;; If not in lowercase, expansions are always capitalized.
+      ;; We then want to replace with the exact expansion.
+      (if (equal (symbol-name last-abbrev) last-abbrev-text)
+         ()
+       (downcase-word -1)
+       (expand-abbrev))))))
 
 
 (defun simula-expand-keyword ()
@@ -941,7 +1176,12 @@ If COUNT is negative, move backward instead."
     (cond
      ((eq simula-abbrev-keyword 'upcase) (upcase-word -1))
      ((eq simula-abbrev-keyword 'downcase) (downcase-word -1))
-     ((eq simula-abbrev-keyword 'capitalize) (capitalize-word -1)))))
+     ((eq simula-abbrev-keyword 'capitalize) (capitalize-word -1))
+     ((eq simula-abbrev-stdproc 'abbrev-table)
+      (if (equal (symbol-name last-abbrev) last-abbrev-text)
+         ()
+       (downcase-word -1)
+       (expand-abbrev))))))
 
 
 (defun simula-electric-keyword ()
@@ -986,11 +1226,10 @@ If COUNT is negative, move backward instead."
                               (simula-backward-up-level 1)
                               (if (pos-visible-in-window-p)
                                   (sit-for 1)
-                                (message
-                                 (concat "Matches "
+                                (message "Matches %s"
                                          (buffer-substring
                                           (point)
-                                          (+ (point) (window-width))))))))
+                                          (+ (point) (window-width)))))))
                         (skip-chars-backward " \t\f")
                         (bolp)))
                  (let ((indent (simula-calculate-indent)))
@@ -1007,56 +1246,134 @@ If COUNT is negative, move backward instead."
          (quit (goto-char (- (point-max) pos))))))))
 
 
-(defun simula-search-backward (string &optional limit move)
-  (setq string (concat string "\\|\\<end\\>"))
-  (let (level)
-    (catch 'simula-out
-      (while (re-search-backward string limit move)
-       (if (simula-context)
-           ()
-         (if (looking-at "\\<end\\>")
-              (progn
-                (setq level 0)
-                (while (natnump level)
-                  (re-search-backward "\\<begin\\>\\|\\<end\\>")
-                  (if (simula-context)
-                      ()
-                    (setq level (if (memq (following-char) '(?b ?B))
-                                    (1- level)
-                                  (1+ level))))))
-            (throw 'simula-out t)))))))
-
-
-(defun simula-search-forward (string &optional limit move)
-  (setq string (concat string "\\|\\<begin\\>"))
-  (let (level)
-    (catch 'exit
-      (while (re-search-forward string limit move)
-       (goto-char (match-beginning 0))
-       (if (simula-context)
-           (goto-char (1- (match-end 0)))
-         (if (looking-at "\\<begin\\>")
-             (progn
-               (goto-char (1- (match-end 0)))
-               (setq level 0)
-               (while (natnump level)
-                 (re-search-forward "\\<begin\\>\\|\\<end\\>")
-                 (backward-word 1)
-                 (if (not (simula-context))
-                     (setq level (if (memq (following-char) '(?e ?E))
-                                     (1- level)
-                                   (1+ level))))
-                 (backward-word -1)))
-           (goto-char (1- (match-end 0)))
-           (throw 'exit t)))))))
-
-  
+(defun simula-search-backward (regexp &optional bound noerror)
+  "Search backward from point for regular expression REGEXP,
+ignoring matches found inside SIMULA comments, string literals,
+and BEGIN..END blocks.
+Set point to the end of the occurrence found, and return point.
+An optional second argument BOUND bounds the search, it is a buffer position.
+The match found must not extend after that position.  Optional third argument
+NOERROR, if t, means if fail just return nil (no error).
+If not nil and not t, move to limit of search and return nil."
+  (let (begin end context (comb-regexp (concat regexp "\\|\\<end\\>"))
+             match (start-point (point)))
+    (catch 'simula-backward
+      (while (re-search-backward comb-regexp bound 1)
+       ;; We have a match, check SIMULA context at match-beginning
+       ;; to see if we are outside comments etc.
+       ;; Set MATCH to t if we found a true match,
+       ;; set MATCH to 'BLOCK if we found a BEGIN..END block,
+       ;; else set MATCH to nil.
+       (save-match-data
+         (setq context (simula-context))
+         (cond
+          ((eq context nil)
+           (setq match (if (looking-at regexp) t 'BLOCK)))
+          ;; A comment-ending `;' is part of the comment, and shouldn't match.
+          ;; ((eq context 0)
+          ;;  (setq match (if (eq (following-char) ?\;) t nil)))
+          ((eq context 2)
+           (setq match (if (and (looking-at regexp)
+                                (looking-at ";\\|\\<end\\>\\|\\<else\\>\\|\\<otherwise\\>\\|\\<when\\>"))
+                           t
+                         (if (looking-at "\\<end\\>") 'BLOCK nil))))
+          (t (setq match nil))))
+       ;; Exit if true match
+       (if (eq match t) (throw 'simula-backward (point)))
+       (if (eq match 'BLOCK)
+       ;; We found the END of a block
+           (let ((level 0))
+             (while (natnump level)
+                 (if (re-search-backward "\\<begin\\>\\|\\<end\\>" bound 1)
+                     (let ((context (simula-context)))
+                       ;;    We found a BEGIN -> decrease level count
+                       (cond ((and (eq context nil)
+                                  (memq (following-char) '(?b ?B)))
+                              (setq level (1- level)))
+                             ;; END -> increase level count
+                             ((and (memq context '(nil 2))
+                                   (memq (following-char) '(?e ?E)))
+                              (setq level (1+ level)))))
+                   ;; Block search failed.  Action depends on noerror.
+                   (if (or (not noerror) (eq noerror t))
+                       (goto-char start-point))
+                   (if (not noerror)
+                       (signal 'search-failed (list regexp)))
+                   (throw 'simula-backward nil))))))
+      ;; Search failed.  Action depends on noerror.
+      (if (or (not noerror) (eq noerror t))
+         (goto-char start-point))
+      (if noerror
+         nil
+       (signal 'search-failed (list regexp))))))
+
+
+(defun simula-search-forward (regexp &optional bound noerror)
+  "Search forward from point for regular expression REGEXP,
+ignoring matches found inside SIMULA comments, string literals,
+and BEGIN..END blocks.
+Set point to the end of the occurrence found, and return point.
+An optional second argument BOUND bounds the search, it is a buffer position.
+The match found must not extend after that position.  Optional third argument
+NOERROR, if t, means if fail just return nil (no error).
+If not nil and not t, move to limit of search and return nil."
+  (let (begin end context (comb-regexp (concat regexp "\\|\\<begin\\>"))
+             match (start-point (point)))
+    (catch 'simula-forward
+      (while (re-search-forward comb-regexp bound 1)
+       ;; We have a match, check SIMULA context at match-beginning
+       ;; to see if we are outside comments.
+       ;; Set MATCH to t if we found a true match,
+       ;; set MATCH to 'BLOCK if we found a BEGIN..END block,
+       ;; else set MATCH to nil.
+       (save-match-data
+         (save-excursion
+           (goto-char (match-beginning 0))
+           (setq context (simula-context))
+           (cond
+            ((not context)
+             (setq match (if (looking-at regexp) t 'BLOCK)))
+            ;; Comment-ending `;' is part of the comment, and shouldn't match.
+            ;; ((eq context 0)
+            ;;  (setq match (if (eq (following-char) ?\;) t nil)))
+            ((eq context 2)
+             (setq match (if (and (looking-at regexp)
+                                  (looking-at ";\\|\\<end\\>\\|\\<else\\>\\|\\<otherwise\\>\\|\\<when\\>")) t nil)))
+            (t (setq match nil)))))
+       ;; Exit if true match
+       (if (eq match t) (throw 'simula-forward (point)))
+       (if (eq match 'BLOCK)
+       ;; We found the BEGINning of a block
+           (let ((level 0))
+             (while (natnump level)
+                 (if (re-search-forward "\\<begin\\>\\|\\<end\\>" bound 1)
+                     (let ((context (simula-context)))
+                       ;;    We found a BEGIN -> increase level count
+                       (cond ((eq context nil) (setq level (1+ level)))
+                             ;; END -> decrease level count
+                             ((and (eq context 2)
+                                   ;; Don't match BEGIN inside END comment
+                                   (memq (preceding-char) '(?d ?D)))
+                              (setq level (1- level)))))
+                   ;; Block search failed.  Action depends on noerror.
+                   (if (or (not noerror) (eq noerror t))
+                       (goto-char start-point))
+                   (if (not noerror)
+                       (signal 'search-failed (list regexp)))
+                   (throw 'simula-forward nil))))))
+      ;; Search failed.  Action depends on noerror.
+      (if (or (not noerror) (eq noerror t))
+         (goto-char start-point))
+      (if noerror
+         nil
+       (signal 'search-failed (list regexp))))))
+
+
 (defun simula-install-standard-abbrevs ()
   "Define Simula keywords, procedures and classes in local abbrev table."
   ;; procedure and class names are as of the SIMULA 87 standard.
   (interactive)
-  (mapcar (function (lambda (args)
-                     (apply 'define-abbrev simula-mode-abbrev-table args)))
+  (dolist (args
          '(("abs" "Abs" simula-expand-stdproc)
            ("accum" "Accum" simula-expand-stdproc)
            ("activate" "ACTIVATE" simula-expand-keyword)
@@ -1286,6 +1603,58 @@ If COUNT is negative, move backward instead."
            ("virtual" "VIRTUAL" simula-expand-keyword)
            ("wait" "Wait" simula-expand-stdproc)
            ("when" "WHEN" simula-electric-keyword)
-           ("while" "WHILE" simula-expand-keyword))))
-
+           ("while" "WHILE" simula-expand-keyword)))
+    (define-abbrev simula-mode-abbrev-table
+      (nth 0 args) (nth 1 args) (nth 2 args) nil 'system)))
+
+(if simula-abbrev-file
+    (read-abbrev-file simula-abbrev-file))
+(let (abbrevs-changed)
+  (simula-install-standard-abbrevs))
+
+;; Hilit mode support.
+(if (and (fboundp 'hilit-set-mode-patterns)
+        (boundp 'hilit-patterns-alist)
+        (not (assoc 'simula-mode hilit-patterns-alist)))
+    (hilit-set-mode-patterns
+     'simula-mode
+     '(
+       ("^%\\([ \t\f].*\\)?$" nil comment)
+       ("^%include\\>" nil include)
+       ("\"[^\"\n]*\"\\|'.'\\|'![0-9]+!'" nil string)
+       ("\\<\\(ACTIVATE\\|AFTER\\|AND\\|ARRAY\\|AT\\|BEFORE\\|BEGIN\\|BOOLEAN\\|CHARACTER\\|CLASS\\|DELAY\\|DO\\|ELSE\\|END\\|EQ\\|EQV\\|EXTERNAL\\|FALSE\\|FOR\\|GE\\|GO\\|GOTO\\|GT\\|HIDDEN\\|IF\\|IMP\\|IN\\|INNER\\|INSPECT\\|INTEGER\\|IS\\|LABEL\\|LE\\|LONG\\|LT\\|NAME\\|NE\\|NEW\\|NONE\\|NOT\\|NOTEXT\\|OR\\|OTHERWISE\\|PRIOR\\|PROCEDURE\\|PROTECTED\\|QUA\\|REACTIVATE\\|REAL\\|REF\\|SHORT\\|STEP\\|SWITCH\\|TEXT\\|THEN\\|THIS\\|TO\\|TRUE\\|UNTIL\\|VALUE\\|VIRTUAL\\|WHEN\\|WHILE\\)\\>" nil keyword)
+       ("!\\|\\<COMMENT\\>" ";" comment))
+     nil 'case-insensitive))
+\f
+;; defuns for submitting bug reports
+
+(defconst simula-mode-help-address "simula-mode@ifi.uio.no"
+  "Address accepting submission of `simula-mode' bug reports.")
+
+(defun simula-submit-bug-report ()
+  "Submit via mail a bug report on `simula-mode'."
+  (interactive)
+  (and
+   (y-or-n-p "Do you want to submit a report on simula-mode? ")
+   (reporter-submit-bug-report
+    simula-mode-help-address
+    (concat "simula-mode from Emacs " emacs-version)
+    (list
+     ;; report only the vars that affect indentation
+     'simula-indent-level
+     'simula-substatement-offset
+     'simula-continued-statement-offset
+     'simula-label-offset
+     'simula-if-indent
+     'simula-inspect-indent
+     'simula-electric-indent
+     'simula-abbrev-keyword
+     'simula-abbrev-stdproc
+     'simula-abbrev-file
+     'simula-tab-always-indent
+     ))))
+
+(provide 'simula)
+
+;;; arch-tag: 488c1bb0-eebf-4f06-93df-1df603f06255
 ;;; simula.el ends here