Bind "C-c ]" to block-closing commands in several modes.
[bpt/emacs.git] / lisp / progmodes / f90.el
index 2c152d9..b6c42d2 100644 (file)
@@ -1,7 +1,7 @@
 ;;; f90.el --- Fortran-90 mode (free format)
 
 ;; Copyright (C) 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2005,
-;;   2006, 2007, 2008  Free Software Foundation, Inc.
+;;   2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
 
 ;; Author: Torbj\"orn Einarsson <Torbjorn.Einarsson@era.ericsson.se>
 ;; Maintainer: Glenn Morris <rgm@gnu.org>
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; 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, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -39,7 +37,6 @@
 
 ;; To facilitate typing, a fairly complete list of abbreviations is provided.
 ;; All abbreviations begin with the backquote character "`"
-;; (this requires modification of the syntax-table).
 ;; For example, `i expands to integer (if abbrev-mode is on).
 
 ;; There are two separate features for altering the appearance of code:
 ;; 3. Support for align.
 ;; Font-locking:
 ;; 1. OpenMP, OpenMPI?, preprocessor highlighting.
-;; 2. interface blah - Highlight "blah" in function-name face?
-;; Need to avoid "interface operator (+)" etc.
-;; 3. integer_name = 1
-;; 4. Labels for "else" statements (F2003)?
+;; 2. integer_name = 1
+;; 3. Labels for "else" statements (F2003)?
 
 (defvar comment-auto-fill-only-comments)
 (defvar font-lock-keywords)
 (defcustom f90-do-indent 3
   "Extra indentation applied to DO blocks."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent)
-(put 'f90-do-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-if-indent 3
   "Extra indentation applied to IF, SELECT CASE, WHERE and FORALL blocks."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent)
-(put 'f90-if-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-type-indent 3
   "Extra indentation applied to TYPE, ENUM, INTERFACE and BLOCK DATA blocks."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent)
-(put 'f90-type-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-program-indent 2
   "Extra indentation applied to PROGRAM, MODULE, SUBROUTINE, FUNCTION blocks."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent)
-(put 'f90-program-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-associate-indent 2
   "Extra indentation applied to ASSOCIATE blocks."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent
   :version "23.1")
-(put 'f90-associate-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-continuation-indent 5
   "Extra indentation applied to continuation lines."
   :type  'integer
+  :safe  'integerp
   :group 'f90-indent)
-(put 'f90-continuation-indent 'safe-local-variable 'integerp)
 
 (defcustom f90-comment-region "!!$"
   "String inserted by \\[f90-comment-region] at start of each line in region."
   :type  'string
+  :safe  'stringp
   :group 'f90-indent)
-(put 'f90-comment-region 'safe-local-variable 'stringp)
 
 (defcustom f90-indented-comment-re "!"
   "Regexp matching comments to indent as code."
   :type  'regexp
+  :safe  'stringp
   :group 'f90-indent)
-(put 'f90-indented-comment-re 'safe-local-variable 'stringp)
 
 (defcustom f90-directive-comment-re "!hpf\\$"
   "Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented."
   :type  'regexp
+  :safe  'stringp
   :group 'f90-indent)
-(put 'f90-directive-comment-re 'safe-local-variable 'stringp)
 
 (defcustom f90-beginning-ampersand t
   "Non-nil gives automatic insertion of \& at start of continuation line."
   :type  'boolean
+  :safe  'booleanp
   :group 'f90)
-(put 'f90-beginning-ampersand 'safe-local-variable 'booleanp)
 
 (defcustom f90-smart-end 'blink
   "Qualification of END statements according to the matching block start.
@@ -251,48 +246,47 @@ values are 'blink, 'no-blink, and nil.  If nil, nothing is done.
 The other two settings have the same effect, but 'blink
 additionally blinks the cursor to the start of the block."
   :type  '(choice (const blink) (const no-blink) (const nil))
+  :safe  (lambda (value) (memq value '(blink no-blink nil)))
   :group 'f90)
-(put 'f90-smart-end 'safe-local-variable
-     (lambda (value) (memq value '(blink no-blink nil))))
 
 (defcustom f90-break-delimiters "[-+\\*/><=,% \t]"
   "Regexp matching delimiter characters at which lines may be broken.
-There are certain tokens comprised entirely of characters
-matching this regexp that should not be split, and these are
-specified by the constant `f90-no-break-re'."
-  :type  'regexp
+There are some common two-character tokens where one or more of
+the members matches this regexp.  Although Fortran allows breaks
+within lexical tokens (provided the next line has a beginning ampersand),
+the constant `f90-no-break-re' ensures that such tokens are not split."
+  :type 'regexp
+  :safe 'stringp
   :group 'f90)
-(put 'f90-break-delimiters 'safe-local-variable 'stringp)
 
 (defcustom f90-break-before-delimiters t
   "Non-nil causes `f90-do-auto-fill' to break lines before delimiters."
-  :type  'boolean
+  :type 'boolean
+  :safe 'booleanp
   :group 'f90)
-(put 'f90-break-before-delimiters 'safe-local-variable 'booleanp)
 
 (defcustom f90-auto-keyword-case nil
   "Automatic case conversion of keywords.
 The options are 'downcase-word, 'upcase-word, 'capitalize-word and nil."
   :type  '(choice (const downcase-word) (const upcase-word)
                   (const capitalize-word) (const nil))
+  :safe (lambda (value) (memq value '(downcase-word
+                                      capitalize-word upcase-word nil)))
   :group 'f90)
-(put 'f90-auto-keyword-case 'safe-local-variable
-     (lambda (value) (memq value '(downcase-word
-                                   capitalize-word upcase-word nil))))
 
 (defcustom f90-leave-line-no nil
   "If non-nil, line numbers are not left justified."
   :type  'boolean
+  :safe  'booleanp
   :group 'f90)
-(put 'f90-leave-line-no 'safe-local-variable 'booleanp)
 
 (defcustom f90-mode-hook nil
   "Hook run when entering F90 mode."
   :type    'hook
+  ;; Not the only safe options, but some common ones.
+  :safe    (lambda (value) (member value '((f90-add-imenu-menu) nil)))
   :options '(f90-add-imenu-menu)
   :group   'f90)
-(put 'f90-mode-hook 'safe-local-variable
-     (lambda (value) (member value '((f90-add-imenu-menu) nil))))
 
 ;; User options end here.
 
@@ -443,7 +437,7 @@ The options are 'downcase-word, 'upcase-word, 'capitalize-word and nil."
 Set the match data so that subexpression 1,2 are the TYPE, and
 type-name parts, respectively."
   (let (found l)
-    (while (and (re-search-forward "\\<\\(\\(?:end[ \t]*\\)?type\\)[ \t]*"
+    (while (and (re-search-forward "\\<\\(\\(?:end[ \t]*\\)?type\\)\\>[ \t]*"
                                    limit t)
                 (not (setq found
                            (progn
@@ -471,22 +465,21 @@ type-name parts, respectively."
 ;;;      (1 font-lock-keyword-face) (3 font-lock-function-name-face))
    '(f90-typedef-matcher
      (1 font-lock-keyword-face) (2 font-lock-function-name-face))
-   ;; Other functions and declarations.
+    ;; F2003.  Prevent operators being highlighted as functions.
+    '("\\<\\(\\(?:end[ \t]*\\)?interface[ \t]*\\(?:assignment\\|operator\\|\
+read\\|write\\)\\)[ \t]*(" (1 font-lock-keyword-face t))
+   ;; Other functions and declarations.  Named interfaces = F2003.
    '("\\<\\(\\(?:end[ \t]*\\)?\\(program\\|module\\|function\\|associate\\|\
-subroutine\\)\\|use\\|call\\)\\>[ \t]*\\(\\sw+\\)?"
+subroutine\\|interface\\)\\|use\\|call\\)\\>[ \t]*\\(\\sw+\\)?"
      (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
    ;; F2003.
    '("\\<\\(use\\)[ \t]*,[ \t]*\\(\\(?:non_\\)?intrinsic\\)[ \t]*::[ \t]*\
 \\(\\sw+\\)"
      (1 font-lock-keyword-face) (2 font-lock-keyword-face)
      (3 font-lock-function-name-face))
-   "\\<\\(\\(end[ \t]*\\)?block[ \t]*data\\|contains\\|\
-end[ \t]*interface\\)\\>"
-   ;; "abstract interface" is F2003. Must come after previous entry.
-   '("\\<\\(\\(?:abstract[ \t]*\\)?interface\\)\\>"
-     ;; [ \t]*\\(\\(\\sw+\\)[ \t]*[^(]\\)?"
-     ;; (2) messes up "interface operator ()", etc.
-     (1 font-lock-keyword-face))) ;(2 font-lock-function-name-face nil t)))
+   "\\<\\(\\(end[ \t]*\\)?block[ \t]*data\\|contains\\)\\>"
+   ;; "abstract interface" is F2003.
+   '("\\<abstract[ \t]*interface\\>" (0 font-lock-keyword-face t)))
   "This does fairly subdued highlighting of comments and function calls.")
 
 ;; NB not explicitly handling this, yet it seems to work.
@@ -562,9 +555,8 @@ logical\\|double[ \t]*precision\\|\
 \\(function\\)\\>[ \t]*\\(\\sw+\\)[ \t]*\\(([^&!\n]*)\\)"
       (1 font-lock-type-face t) (4 font-lock-keyword-face t)
       (5 font-lock-function-name-face t) (6 'default t))
-    ;; enum (F2003; cf type in -1).
-    '("\\<\\(enum\\)\\>\\([^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
-      (1 font-lock-keyword-face) (3 font-lock-function-name-face))
+    ;; enum (F2003; must be followed by ", bind(C)").
+    '("\\<\\(enum\\)[ \t]*," (1 font-lock-keyword-face))
     ;; end do, enum (F2003), if, select, where, and forall constructs.
     '("\\<\\(end[ \t]*\\(do\\|if\\|enum\\|select\\|forall\\|where\\)\\)\\>\
 \\([ \t]+\\(\\sw+\\)\\)?"
@@ -601,11 +593,12 @@ logical\\|double[ \t]*precision\\|type[ \t]*(\\sw+)\\|none\\)[ \t]*"
           (list
            f90-keywords-level-3-re
            f90-operators-re
+           ;; FIXME why isn't this font-lock-builtin-face, which
+           ;; otherwise we hardly use, as in fortran.el?
            (list f90-procedures-re '(1 font-lock-keyword-face keep))
            "\\<real\\>"                 ; avoid overwriting real defs
            ;; As an attribute, but not as an optional argument.
-           '("\\<\\(asynchronous\\)[ \t]*[^=]" . 1)
-           ))
+           '("\\<\\(asynchronous\\)[ \t]*[^=]" . 1)))
   "Highlights all F90 keywords and intrinsic procedures.")
 
 (defvar f90-font-lock-keywords-4
@@ -624,17 +617,20 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
   (let ((table (make-syntax-table)))
     (modify-syntax-entry ?\! "<"  table) ; begin comment
     (modify-syntax-entry ?\n ">"  table) ; end comment
+    ;; FIXME: This goes against the convention: it should be "_".
     (modify-syntax-entry ?_  "w"  table) ; underscore in names
     (modify-syntax-entry ?\' "\"" table) ; string quote
     (modify-syntax-entry ?\" "\"" table) ; string quote
-    (modify-syntax-entry ?\` "w"  table) ; for abbrevs
+    ;; FIXME: We used to set ` to word syntax for the benefit of abbrevs, but
+    ;; we do not need it any more.  Not sure if it should be "_" or "." now.
+    (modify-syntax-entry ?\` "_"  table)
     (modify-syntax-entry ?\r " "  table) ; return is whitespace
     (modify-syntax-entry ?+  "."  table) ; punctuation
     (modify-syntax-entry ?-  "."  table)
     (modify-syntax-entry ?=  "."  table)
     (modify-syntax-entry ?*  "."  table)
     (modify-syntax-entry ?/  "."  table)
-    ;; I think that the f95 standard leaves the behaviour of \
+    ;; I think that the f95 standard leaves the behavior of \
     ;; unspecified, but that f2k will require it to be non-special.
     ;; Use `f90-backslash-not-special' to change.
     (modify-syntax-entry ?\\ "\\" table) ; escape chars
@@ -652,7 +648,7 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
     (define-key map "\C-\M-p"  'f90-beginning-of-block)
     (define-key map "\C-\M-q"  'f90-indent-subprogram)
     (define-key map "\C-j"     'f90-indent-new-line) ; LFD equals C-j
-    (define-key map "\r"       'newline)
+;;;    (define-key map "\r"       'newline)
     (define-key map "\C-c\r"   'f90-break-line)
 ;;;  (define-key map [M-return] 'f90-break-line)
     (define-key map "\C-c\C-a" 'f90-previous-block)
@@ -661,8 +657,10 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
     (define-key map "\C-c\C-f" 'f90-fill-region)
     (define-key map "\C-c\C-p" 'f90-previous-statement)
     (define-key map "\C-c\C-n" 'f90-next-statement)
+    (define-key map "\C-c]"    'f90-insert-end)
     (define-key map "\C-c\C-w" 'f90-insert-end)
-    (define-key map "\t"       'f90-indent-line)
+    ;; Standard tab binding will call this, and also handle regions.
+;;;    (define-key map "\t"       'f90-indent-line)
     (define-key map ","        'f90-electric-insert)
     (define-key map "+"        'f90-electric-insert)
     (define-key map "-"        'f90-electric-insert)
@@ -673,29 +671,44 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
       `("F90"
         ("Customization"
          ,(custom-menu-create 'f90)
-         ["Set"  Custom-set t]
-         ["Save" Custom-save t]
-         ["Reset to Current" Custom-reset-current t]
-         ["Reset to Saved"   Custom-reset-saved t]
-         ["Reset to Standard Settings" Custom-reset-standard t]
+         ;; FIXME useless?
+         ["Set"  Custom-set :active t
+          :help "Set current value of all edited settings in the buffer"]
+         ["Save" Custom-save :active t
+          :help "Set and save all edited settings"]
+         ["Reset to Current" Custom-reset-current :active t
+          :help "Reset all edited settings to current"]
+         ["Reset to Saved" Custom-reset-saved :active t
+          :help "Reset all edited or set settings to saved"]
+         ["Reset to Standard Settings" Custom-reset-standard :active t
+          :help "Erase all cusomizations in buffer"]
          )
         "--"
-        ["Indent Subprogram"       f90-indent-subprogram       t]
-        ["Mark Subprogram"         f90-mark-subprogram         t]
-        ["Beginning of Subprogram" f90-beginning-of-subprogram t]
-        ["End of Subprogram"       f90-end-of-subprogram       t]
+        ["Indent Subprogram" f90-indent-subprogram t]
+        ["Mark Subprogram" f90-mark-subprogram :active t :help
+         "Mark the end of the current subprogram, move point to the start"]
+        ["Beginning of Subprogram" f90-beginning-of-subprogram :active t
+         :help "Move point to the start of the current subprogram"]
+        ["End of Subprogram" f90-end-of-subprogram :active t
+         :help "Move point to the end of the current subprogram"]
         "--"
-        ["(Un)Comment Region" f90-comment-region mark-active]
-        ["Indent Region"      f90-indent-region  mark-active]
-        ["Fill Region"        f90-fill-region    mark-active]
+        ["(Un)Comment Region" f90-comment-region :active mark-active
+         :help "Comment or uncomment the region"]
+        ["Indent Region" f90-indent-region :active mark-active]
+        ["Fill Region" f90-fill-region :active mark-active
+         :help "Fill long lines in the region"]
         "--"
-        ["Break Line at Point"     f90-break-line t]
-        ["Join with Previous Line" f90-join-lines t]
-        ["Insert Block End"        f90-insert-end t]
+        ["Break Line at Point" f90-break-line :active t
+         :help "Break the current line at point"]
+        ["Join with Previous Line" f90-join-lines :active t
+         :help "Join the current line to the previous one"]
+        ["Insert Block End" f90-insert-end :active t
+         :help "Insert an end statement for the current code block"]
         "--"
         ("Highlighting"
+         :help "Fontify this buffer to varying degrees"
          ["Toggle font-lock-mode" font-lock-mode :selected font-lock-mode
-          :style toggle]
+          :style toggle :help "Fontify text in this buffer"]
          "--"
          ["Light highlighting (level 1)"    f90-font-lock-1 t]
          ["Moderate highlighting (level 2)" f90-font-lock-2 t]
@@ -703,6 +716,7 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
          ["Maximum highlighting (level 4)"  f90-font-lock-4 t]
          )
         ("Change Keyword Case"
+         :help "Change the case of keywords in the buffer or region"
          ["Upcase Keywords (buffer)"     f90-upcase-keywords     t]
          ["Capitalize Keywords (buffer)" f90-capitalize-keywords t]
          ["Downcase Keywords (buffer)"   f90-downcase-keywords   t]
@@ -715,45 +729,46 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
           mark-active]
          )
         "--"
-        ["Toggle auto-fill"   auto-fill-mode :selected auto-fill-function
-         :style toggle]
-        ["Toggle abbrev-mode" abbrev-mode    :selected abbrev-mode
-         :style toggle]
-        ["Add imenu Menu" f90-add-imenu-menu
+        ["Toggle Auto Fill" auto-fill-mode :selected auto-fill-function
+         :style toggle
+         :help "Automatically fill text while typing in this buffer"]
+        ["Toggle Abbrev Mode" abbrev-mode :selected abbrev-mode
+         :style toggle :help "Expand abbreviations while typing in this buffer"]
+        ["Add Imenu Menu" f90-add-imenu-menu
          :active   (not (lookup-key (current-local-map) [menu-bar index]))
-         :included (fboundp 'imenu-add-to-menubar)]))
+         :included (fboundp 'imenu-add-to-menubar)
+         :help "Add an index menu to the menu-bar"
+         ]))
     map)
   "Keymap used in F90 mode.")
 
 
+(defun f90-font-lock-n (n)
+  "Set `font-lock-keywords' to F90 level N keywords."
+  (font-lock-mode 1)
+  (setq font-lock-keywords
+        (symbol-value (intern-soft (format "f90-font-lock-keywords-%d" n))))
+  (font-lock-fontify-buffer))
+
 (defun f90-font-lock-1 ()
   "Set `font-lock-keywords' to `f90-font-lock-keywords-1'."
   (interactive)
-  (font-lock-mode 1)
-  (setq font-lock-keywords f90-font-lock-keywords-1)
-  (font-lock-fontify-buffer))
+  (f90-font-lock-n 1))
 
 (defun f90-font-lock-2 ()
   "Set `font-lock-keywords' to `f90-font-lock-keywords-2'."
   (interactive)
-  (font-lock-mode 1)
-  (setq font-lock-keywords f90-font-lock-keywords-2)
-  (font-lock-fontify-buffer))
+  (f90-font-lock-n 2))
 
 (defun f90-font-lock-3 ()
   "Set `font-lock-keywords' to `f90-font-lock-keywords-3'."
   (interactive)
-  (font-lock-mode 1)
-  (setq font-lock-keywords f90-font-lock-keywords-3)
-  (font-lock-fontify-buffer))
+  (f90-font-lock-n 3))
 
 (defun f90-font-lock-4 ()
   "Set `font-lock-keywords' to `f90-font-lock-keywords-4'."
   (interactive)
-  (font-lock-mode 1)
-  (setq font-lock-keywords f90-font-lock-keywords-4)
-  (font-lock-fontify-buffer))
-
+  (f90-font-lock-n 4))
 \f
 ;; Regexps for finding program structures.
 (defconst f90-blocks-re
@@ -804,12 +819,13 @@ Can be overridden by the value of `font-lock-maximum-decoration'.")
   "Regexp matching a CLASS/TYPE IS statement.")
 
 (defconst f90-no-break-re
-  (regexp-opt '("**" "//" "=>" ">=" "<=" "==" "/=") 'paren)
-  "Regexp specifying where not to break lines when filling.
-This regexp matches certain tokens comprised entirely of
-characters matching the regexp `f90-break-delimiters' that should
-not be split by filling.  Each element is assumed to be two
-characters long.")
+  (regexp-opt '("**" "//" "=>" ">=" "<=" "==" "/=" "(/" "/)") 'paren)
+  "Regexp specifying two-character tokens not to split when breaking lines.
+Each token has one or more of the characters from `f90-break-delimiters'.
+Note that if only one of the characters is from that variable,
+then the presence of the token here allows a line-break before or
+after the other character, where a break would not normally be
+allowed.  This minor issue currently only affects \"(/\" and \"/)\".")
 
 (defvar f90-cache-position nil
   "Temporary position used to speed up region operations.")
@@ -869,7 +885,7 @@ Used in the F90 entry in `hs-special-modes-alist'.")
 (defun f90-imenu-type-matcher ()
   "Search backward for the start of a derived type.
 Set subexpression 1 in the match-data to the name of the type."
-  (let (found l)
+  (let (found)
     (while (and (re-search-backward "^[ \t0-9]*type[ \t]*" nil t)
                 (not (setq found
                            (save-excursion
@@ -884,7 +900,8 @@ Set subexpression 1 in the match-data to the name of the type."
 (defvar f90-imenu-generic-expression
   (let ((good-char "[^!\"\&\n \t]") (not-e "[^e!\n\"\& \t]")
         (not-n "[^n!\n\"\& \t]") (not-d "[^d!\n\"\& \t]")
-        (not-ib "[^i(!\n\"\& \t]") (not-s "[^s!\n\"\& \t]"))
+        ;; (not-ib "[^i(!\n\"\& \t]") (not-s "[^s!\n\"\& \t]")
+        )
     (list
      '(nil "^[ \t0-9]*program[ \t]+\\(\\sw+\\)" 1)
      '("Modules" "^[ \t0-9]*module[ \t]+\\(\\sw+\\)[ \t]*\\(!\\|$\\)" 1)
@@ -925,87 +942,74 @@ Set subexpression 1 in the match-data to the name of the type."
 
 \f
 ;; Abbrevs have generally two letters, except standard types `c, `i, `r, `t.
-(defvar f90-mode-abbrev-table
-  (progn
-    (define-abbrev-table 'f90-mode-abbrev-table nil)
-    f90-mode-abbrev-table)
-  "Abbrev table for F90 mode.")
-
-(let (abbrevs-changed)
-  ;; Use the 6th arg (SYSTEM-FLAG) of define-abbrev if possible.
-  ;; A little baroque to quieten the byte-compiler.
-  (mapc
-   (function (lambda (element)
-               (condition-case nil
-                   (apply 'define-abbrev f90-mode-abbrev-table
-                          (append element '(nil 0 t)))
-                 (wrong-number-of-arguments
-                  (apply 'define-abbrev f90-mode-abbrev-table
-                         (append element '(nil 0)))))))
-   '(("`al"  "allocate"     )
-     ("`ab"  "allocatable"  )
-     ("`ai"  "abstract interface")
-     ("`as"  "assignment"   )
-     ("`asy" "asynchronous" )
-     ("`ba"  "backspace"    )
-     ("`bd"  "block data"   )
-     ("`c"   "character"    )
-     ("`cl"  "close"        )
-     ("`cm"  "common"       )
-     ("`cx"  "complex"      )
-     ("`cn"  "contains"     )
-     ("`cy"  "cycle"        )
-     ("`de"  "deallocate"   )
-     ("`df"  "define"       )
-     ("`di"  "dimension"    )
-     ("`dp"  "double precision")
-     ("`dw"  "do while"     )
-     ("`el"  "else"         )
-     ("`eli" "else if"      )
-     ("`elw" "elsewhere"    )
-     ("`em"  "elemental"    )
-     ("`e"   "enumerator"   )
-     ("`eq"  "equivalence"  )
-     ("`ex"  "external"     )
-     ("`ey"  "entry"        )
-     ("`fl"  "forall"       )
-     ("`fo"  "format"       )
-     ("`fu"  "function"     )
-     ("`fa"  ".false."      )
-     ("`im"  "implicit none")
-     ("`in"  "include"      )
-     ("`i"   "integer"      )
-     ("`it"  "intent"       )
-     ("`if"  "interface"    )
-     ("`lo"  "logical"      )
-     ("`mo"  "module"       )
-     ("`na"  "namelist"     )
-     ("`nu"  "nullify"      )
-     ("`op"  "optional"     )
-     ("`pa"  "parameter"    )
-     ("`po"  "pointer"      )
-     ("`pr"  "print"        )
-     ("`pi"  "private"      )
-     ("`pm"  "program"      )
-     ("`pr"  "protected"    )
-     ("`pu"  "public"       )
-     ("`r"   "real"         )
-     ("`rc"  "recursive"    )
-     ("`rt"  "return"       )
-     ("`rw"  "rewind"       )
-     ("`se"  "select"       )
-     ("`sq"  "sequence"     )
-     ("`su"  "subroutine"   )
-     ("`ta"  "target"       )
-     ("`tr"  ".true."       )
-     ("`t"   "type"         )
-     ("`vo"  "volatile"     )
-     ("`wh"  "where"        )
-     ("`wr"  "write"        ))))
-
+(define-abbrev-table 'f90-mode-abbrev-table
+  (mapcar (lambda (e) (list (car e) (cdr e) nil :system t))
+          '(("`al"  . "allocate"     )
+            ("`ab"  . "allocatable"  )
+            ("`ai"  . "abstract interface")
+            ("`as"  . "assignment"   )
+            ("`asy" . "asynchronous" )
+            ("`ba"  . "backspace"    )
+            ("`bd"  . "block data"   )
+            ("`c"   . "character"    )
+            ("`cl"  . "close"        )
+            ("`cm"  . "common"       )
+            ("`cx"  . "complex"      )
+            ("`cn"  . "contains"     )
+            ("`cy"  . "cycle"        )
+            ("`de"  . "deallocate"   )
+            ("`df"  . "define"       )
+            ("`di"  . "dimension"    )
+            ("`dp"  . "double precision")
+            ("`dw"  . "do while"     )
+            ("`el"  . "else"         )
+            ("`eli" . "else if"      )
+            ("`elw" . "elsewhere"    )
+            ("`em"  . "elemental"    )
+            ("`e"   . "enumerator"   )
+            ("`eq"  . "equivalence"  )
+            ("`ex"  . "external"     )
+            ("`ey"  . "entry"        )
+            ("`fl"  . "forall"       )
+            ("`fo"  . "format"       )
+            ("`fu"  . "function"     )
+            ("`fa"  . ".false."      )
+            ("`im"  . "implicit none")
+            ("`in"  . "include"      )
+            ("`i"   . "integer"      )
+            ("`it"  . "intent"       )
+            ("`if"  . "interface"    )
+            ("`lo"  . "logical"      )
+            ("`mo"  . "module"       )
+            ("`na"  . "namelist"     )
+            ("`nu"  . "nullify"      )
+            ("`op"  . "optional"     )
+            ("`pa"  . "parameter"    )
+            ("`po"  . "pointer"      )
+            ("`pr"  . "print"        )
+            ("`pi"  . "private"      )
+            ("`pm"  . "program"      )
+            ("`pr"  . "protected"    )
+            ("`pu"  . "public"       )
+            ("`r"   . "real"         )
+            ("`rc"  . "recursive"    )
+            ("`rt"  . "return"       )
+            ("`rw"  . "rewind"       )
+            ("`se"  . "select"       )
+            ("`sq"  . "sequence"     )
+            ("`su"  . "subroutine"   )
+            ("`ta"  . "target"       )
+            ("`tr"  . ".true."       )
+            ("`t"   . "type"         )
+            ("`vo"  . "volatile"     )
+            ("`wh"  . "where"        )
+            ("`wr"  . "write"        )))
+  "Abbrev table for F90 mode."
+  ;; Accept ` as the first char of an abbrev.  Also allow _ in abbrevs.
+  :regexp "\\(?:[^[:word:]_`]\\|^\\)\\(`?[[:word:]_]+\\)[^[:word:]_]*")
 \f
 ;;;###autoload
-(defun f90-mode ()
+(define-derived-mode f90-mode prog-mode "F90"
   "Major mode for editing Fortran 90,95 code in free format.
 For fixed format code, use `fortran-mode'.
 
@@ -1062,13 +1066,9 @@ Variables controlling indentation style and extra features:
 
 Turning on F90 mode calls the value of the variable `f90-mode-hook'
 with no args, if that value is non-nil."
-  (interactive)
-  (kill-all-local-variables)
-  (setq major-mode 'f90-mode
-        mode-name "F90"
-        local-abbrev-table f90-mode-abbrev-table)
-  (set-syntax-table f90-mode-syntax-table)
-  (use-local-map f90-mode-map)
+  :group 'f90
+  :syntax-table f90-mode-syntax-table
+  :abbrev-table f90-mode-abbrev-table
   (set (make-local-variable 'indent-line-function) 'f90-indent-line)
   (set (make-local-variable 'indent-region-function) 'f90-indent-region)
   (set (make-local-variable 'require-final-newline) mode-require-final-newline)
@@ -1091,8 +1091,7 @@ with no args, if that value is non-nil."
        'f90-beginning-of-subprogram)
   (set (make-local-variable 'end-of-defun-function) 'f90-end-of-subprogram)
   (set (make-local-variable 'add-log-current-defun-function)
-       #'f90-current-defun)
-  (run-mode-hooks 'f90-mode-hook))
+       #'f90-current-defun))
 
 \f
 ;; Inline-functions.
@@ -1223,10 +1222,10 @@ NAME is nil if the statement has no label."
 
 (defsubst f90-looking-at-type-like ()
   "Return (KIND NAME) if a type/enum/interface/block-data starts after point.
-NAME is non-nil only for type."
+NAME is non-nil only for type and certain interfaces."
   (cond
    ((save-excursion
-      (and (looking-at "\\<type[ \t]*")
+      (and (looking-at "\\<type\\>[ \t]*")
            (goto-char (match-end 0))
            (not (looking-at "\\(is\\>\\|(\\)"))
            (or (looking-at "\\(\\sw+\\)")
@@ -1236,7 +1235,15 @@ NAME is non-nil only for type."
 ;;;    ((and (not (looking-at f90-typeis-re))
 ;;;          (looking-at f90-type-def-re))
 ;;;     (list (match-string 1) (match-string 2)))
-   ((looking-at "\\(enum\\|interface\\|block[ \t]*data\\)\\>")
+   ((looking-at "\\<\\(interface\\)\\>[ \t]*")
+    (list (match-string 1)
+          (save-excursion
+            (goto-char (match-end 0))
+            (if (or (looking-at "\\(operator\\|assignment\\|read\\|\
+write\\)[ \t]*([^)\n]*)")
+                    (looking-at "\\sw+"))
+                (match-string 0)))))
+   ((looking-at "\\(enum\\|block[ \t]*data\\)\\>")
     (list (match-string 1) nil))
    ((looking-at "abstract[ \t]*\\(interface\\)\\>")
     (list (match-string 1) nil))))
@@ -1264,9 +1271,12 @@ NAME is non-nil only for type."
 
 (defsubst f90-looking-at-program-block-end ()
   "Return (KIND NAME) if a block with name NAME ends after point."
-  (if (looking-at (concat "end[ \t]*" f90-blocks-re
-                          "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
-      (list (match-string 1) (match-string 3))))
+  (cond ((looking-at "end[ \t]*\\(interface\\)[ \t]*\\(\
+\\(?:assignment\\|operator\\|read\\|write\\)[ \t]*([^)\n]*)\\)")
+         (list (match-string 1) (match-string 2)))
+        ((looking-at (concat "end[ \t]*" f90-blocks-re
+                             "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
+        (list (match-string 1) (match-string 3)))))
 
 (defsubst f90-comment-indent ()
   "Return the indentation to be used for a comment starting at point.
@@ -1342,6 +1352,8 @@ if all else fails."
   (if auto-fill-function (f90-do-auto-fill) ; also updates line
     (f90-update-line)))
 
+;; Behave like self-insert-command for delete-selection-mode (bug#5593).
+(put 'f90-electric-insert 'delete-selection t)
 
 (defun f90-get-correct-indent ()
   "Get correct indent for a line starting with line number.
@@ -1349,9 +1361,8 @@ Does not check type and subprogram indentation."
   (let ((epnt (line-end-position)) icol cont)
     (save-excursion
       (while (and (f90-previous-statement)
-                  (or (progn
-                        (setq cont (f90-present-statement-cont))
-                        (or (eq cont 'end) (eq cont 'middle)))
+                  (or (memq (setq cont (f90-present-statement-cont))
+                            '(middle end))
                       (looking-at "[ \t]*[0-9]"))))
       (setq icol (current-indentation))
       (beginning-of-line)
@@ -1452,8 +1463,7 @@ Does not check type and subprogram indentation."
                                (setq icol (- icol f90-associate-indent)))
                               ((or (looking-at "contains[ \t]*\\(!\\|$\\)")
                                    (f90-looking-at-program-block-end))
-                               (setq icol (- icol f90-program-indent))))))
-                 ))))
+                               (setq icol (- icol f90-program-indent))))))))))
     icol))
 \f
 (defun f90-previous-statement ()
@@ -1500,7 +1510,8 @@ Return (TYPE NAME), or nil if not found."
         matching-beg
       ;; Note this includes the case of an un-named main program,
       ;; in which case we go to (point-min).
-      (message "No beginning found.")
+      (if (called-interactively-p 'interactive)
+         (message "No beginning found"))
       nil)))
 
 (defun f90-end-of-subprogram ()
@@ -1508,7 +1519,7 @@ Return (TYPE NAME), or nil if not found."
 Return (TYPE NAME), or nil if not found."
   (interactive)
   (let ((case-fold-search t)
-        (count 1) 
+        (count 1)
         matching-end)
     (end-of-line)
     (while (and (> count 0)
@@ -1525,7 +1536,8 @@ Return (TYPE NAME), or nil if not found."
 ;;;    (forward-line 1)
     (if (zerop count)
         matching-end
-      (message "No end found.")
+      (if (called-interactively-p 'interactive)
+         (message "No end found"))
       nil)))
 
 
@@ -1537,7 +1549,8 @@ for consistency of block types and labels (if present), and
 completes outermost block if `f90-smart-end' is non-nil.
 Interactively, pushes mark before moving point."
   (interactive "p")
-  (if (interactive-p) (push-mark (point) t)) ; can move some distance
+  ;; Can move some distance.
+  (if (called-interactively-p 'any) (push-mark (point) t))
   (and num (< num 0) (f90-beginning-of-block (- num)))
   (let ((f90-smart-end (if f90-smart-end 'no-blink)) ; for final match-end
         (case-fold-search t)
@@ -1593,7 +1606,7 @@ Checks for consistency of block types and labels (if present).
 Does not check the outermost block, because it may be incomplete.
 Interactively, pushes mark before moving point."
   (interactive "p")
-  (if (interactive-p) (push-mark (point) t))
+  (if (called-interactively-p 'any) (push-mark (point) t))
   (and num (< num 0) (f90-end-of-block (- num)))
   (let ((case-fold-search t)
         (count (or num 1))
@@ -1771,7 +1784,7 @@ If run in the middle of a line, the line is not broken."
                        (zerop (forward-line 1)))
                 (< (point) end-region-mark)))
     (setq cont (f90-present-statement-cont))
-    (while (and (or (eq cont 'middle) (eq cont 'end))
+    (while (and (memq cont '(middle end))
                 (f90-previous-statement))
       (setq cont (f90-present-statement-cont)))
     ;; Process present line for beginning of block.
@@ -1837,8 +1850,8 @@ If run in the middle of a line, the line is not broken."
                    block-list (cdr block-list))
              (if f90-smart-end
                  (save-excursion
-                   (f90-block-match (car beg-struct) (car (cdr beg-struct))
-                                    (car end-struct) (car (cdr end-struct)))))
+                   (f90-block-match (car beg-struct) (cadr beg-struct)
+                                    (car end-struct) (cadr end-struct))))
              (setq ind-b
                    (cond ((looking-at f90-end-if-re) f90-if-indent)
                          ((looking-at "end[ \t]*do\\>")  f90-do-indent)
@@ -1878,10 +1891,10 @@ If run in the middle of a line, the line is not broken."
       (if program
           (progn
             (message "Indenting %s %s..."
-                     (car program) (car (cdr program)))
+                     (car program) (cadr program))
             (indent-region (point) (mark) nil)
             (message "Indenting %s %s...done"
-                     (car program) (car (cdr program))))
+                     (car program) (cadr program)))
         (message "Indenting the whole file...")
         (indent-region (point) (mark) nil)
         (message "Indenting the whole file...done")))))
@@ -1899,6 +1912,8 @@ is non-nil, call `f90-update-line' after inserting the continuation marker."
         (t (insert "&")
            (or no-update (f90-update-line))
            (newline 1)
+           ;; FIXME also need leading ampersand if split lexical token (eg ==).
+           ;; Or respect f90-no-break-re.
            (if f90-beginning-ampersand (insert "&"))))
   (indent-according-to-mode))
 
@@ -2028,7 +2043,7 @@ Leave point at the end of line."
     (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")
                           (setq end-struct (f90-looking-at-program-block-end)))
       (setq end-block (car end-struct)
-            end-name  (car (cdr end-struct)))
+            end-name  (cadr end-struct))
       (save-excursion
         (beginning-of-line)
         (while (and (> count 0)
@@ -2069,7 +2084,7 @@ Leave point at the end of line."
                             (line-end-position)))
                 (sit-for blink-matching-delay)))
           (setq beg-block (car matching-beg)
-                beg-name (car (cdr matching-beg)))
+                beg-name (cadr matching-beg))
           (goto-char end-point)
           (beginning-of-line)
           (f90-block-match beg-block beg-name end-block end-name))))))
@@ -2087,7 +2102,7 @@ Leave point at the end of line."
   "Typing `\\[help-command] or `? lists all the F90 abbrevs.
 Any other key combination is executed normally."
   (interactive "*")
-  (insert last-command-char)
+  (insert last-command-event)
   (let (char event)
     (if (fboundp 'next-command-event) ; XEmacs
         (setq event (next-command-event)
@@ -2096,7 +2111,7 @@ Any other key combination is executed normally."
       (setq event (read-event)
             char event))
     ;; Insert char if not equal to `?', or if abbrev-mode is off.
-    (if (and abbrev-mode (or (eq char ??) (eq char help-char)))
+    (if (and abbrev-mode (memq char (list ?? help-char)))
         (f90-abbrev-help)
       (setq unread-command-events (list event)))))
 
@@ -2109,8 +2124,7 @@ Any other key combination is executed normally."
 
 (defun f90-prepare-abbrev-list-buffer ()
   "Create a buffer listing the F90 mode abbreviations."
-  (save-excursion
-    (set-buffer (get-buffer-create "*Abbrevs*"))
+  (with-current-buffer (get-buffer-create "*Abbrevs*")
     (erase-buffer)
     (insert-abbrev-table-description 'f90-mode-abbrev-table t)
     (goto-char (point-min))
@@ -2179,7 +2193,7 @@ CHANGE-WORD should be one of 'upcase-word, 'downcase-word, 'capitalize-word."
               (funcall change-word -1)
               (or (string= saveword (buffer-substring back-point ref-point))
                   (setq modified t))))
-        (or modified (set-buffer-modified-p nil))))))
+        (or modified (restore-buffer-modified-p nil))))))
 
 
 (defun f90-current-defun ()
@@ -2202,5 +2216,5 @@ escape character."
 
 (provide 'f90)
 
-;;; arch-tag: fceac97c-c147-44bd-aec0-172d4b560ef8
+;; arch-tag: fceac97c-c147-44bd-aec0-172d4b560ef8
 ;;; f90.el ends here