X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/a113b3ca322fd73d97d0d9d69c9f48dc13fb326a..73b0cd50031a714347109169ceb8bacae338612a:/lisp/progmodes/fortran.el diff --git a/lisp/progmodes/fortran.el b/lisp/progmodes/fortran.el index fdd8ff75a2..28928b440c 100644 --- a/lisp/progmodes/fortran.el +++ b/lisp/progmodes/fortran.el @@ -1,8 +1,7 @@ ;;; fortran.el --- Fortran mode for GNU Emacs -;; Copyright (C) 1986, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, -;; 2002, 2003, 2004, 2005, 2006, 2007, 2008 -;; Free Software Foundation, Inc. +;; Copyright (C) 1986, 1993-1995, 1997-2011 +;; Free Software Foundation, Inc. ;; Author: Michael D. Prange ;; Maintainer: Glenn Morris @@ -10,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 @@ -21,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 . ;;; Commentary: @@ -85,8 +82,8 @@ A non-nil value specifies tab-digit style of continuation control. A value of nil specifies that continuation lines are marked with a character in column 6." :type 'boolean + :safe 'booleanp :group 'fortran-indent) -(put 'fortran-tab-mode-default 'safe-local-variable 'booleanp) ;; TODO add more detail of what tab mode is to doc string. (defcustom fortran-tab-mode-string @@ -98,34 +95,35 @@ with a character in column 6." (interactive) (describe-variable 'fortran-tab-mode-string)))) - "String to appear in mode line in TAB format buffers." + "String to appear in mode line in TAB format buffers. +See Info node `(emacs)ForIndent Cont'." :type 'string + :risky t :group 'fortran-indent) -(put 'fortran-tab-mode-string 'risky-local-variable t) (defcustom fortran-do-indent 3 "Extra indentation applied to DO blocks." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-do-indent 'safe-local-variable 'integerp) (defcustom fortran-if-indent 3 "Extra indentation applied to IF, SELECT CASE and WHERE blocks." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-if-indent 'safe-local-variable 'integerp) (defcustom fortran-structure-indent 3 "Extra indentation applied to STRUCTURE, UNION, MAP and INTERFACE blocks." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-structure-indent 'safe-local-variable 'integerp) (defcustom fortran-continuation-indent 5 "Extra indentation applied to continuation lines." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-continuation-indent 'safe-local-variable 'integerp) (defcustom fortran-comment-indent-style 'fixed "How to indent comments. @@ -136,24 +134,24 @@ nil forces comment lines not to be touched; `relative' indents to current Fortran indentation plus `fortran-comment-line-extra-indent'." :type '(radio (const :tag "Untouched" nil) (const fixed) (const relative)) + :safe (lambda (value) (memq value '(nil fixed relative))) :group 'fortran-indent) -(put 'fortran-comment-indent 'safe-local-variable - (lambda (value) (memq value '(nil fixed relative)))) (defcustom fortran-comment-line-extra-indent 0 "Amount of extra indentation for text within full-line comments." :type 'integer + :safe 'integerp :group 'fortran-indent :group 'fortran-comment) -(put 'fortran-comment-line-extra-indent 'safe-local-variable 'integerp) (defcustom fortran-comment-line-start "C" "Delimiter inserted to start new full-line comment. -You might want to change this to \"*\", for instance." +You might want to change this to \"*\", for instance; or \"!\" to +allow trailing comments on a line." :version "21.1" :type 'string + :safe 'stringp :group 'fortran-comment) -(put 'fortran-comment-line-start 'safe-local-variable 'stringp) ;; This used to match preprocessor lines too, but that messes up ;; filling and doesn't seem to be necessary. @@ -162,8 +160,8 @@ You might want to change this to \"*\", for instance." "Regexp to match the start of a full-line comment." :version "21.1" :type 'regexp + :safe 'stringp :group 'fortran-comment) -(put 'fortran-comment-line-start-skip 'safe-local-variable 'stringp) (defcustom fortran-directive-re "^[ \t]*#.*" @@ -172,20 +170,20 @@ The matching text will be fontified with `font-lock-keyword-face'. The matching line will be given zero indentation." :version "22.1" :type 'regexp + :safe 'stringp :group 'fortran-indent) -(put 'fortran-directive-re 'safe-local-variable 'stringp) (defcustom fortran-minimum-statement-indent-fixed 6 "Minimum statement indentation for fixed format continuation style." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-minimum-statement-indent-fixed 'safe-local-variable 'integerp) (defcustom fortran-minimum-statement-indent-tab (max tab-width 6) "Minimum statement indentation for TAB format continuation style." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-minimum-statement-indent-tab 'safe-local-variable 'integerp) ;; Note that this is documented in the v18 manuals as being a string ;; of length one rather than a single character. @@ -194,31 +192,29 @@ The matching line will be given zero indentation." "Single-character string inserted for Fortran comment indentation. Normally a space." :type 'string + :safe (lambda (value) (or (characterp value) + (and (stringp value) (= (length value) 1)))) :group 'fortran-comment) -(put 'fortran-comment-indent-char 'safe-local-variable - (lambda (value) (or (characterp value) - (and (stringp value) - (= (length value) 1))))) (defcustom fortran-line-number-indent 1 "Maximum indentation for Fortran line numbers. 5 means right-justify them within their five-column field." :type 'integer + :safe 'integerp :group 'fortran-indent) -(put 'fortran-line-number-indent 'safe-local-variable 'integerp) (defcustom fortran-check-all-num-for-matching-do nil "Non-nil causes all numbered lines to be treated as possible DO loop ends." :type 'boolean + :safe 'booleanp :group 'fortran) -(put 'fortran-check-all-num-for-matching-do 'safe-local-variable 'booleanp) (defcustom fortran-blink-matching-if nil "Non-nil causes \\[fortran-indent-line] on ENDIF to blink on matching IF. Also, from an ENDDO statement blink on matching DO [WHILE] statement." :type 'boolean + :safe 'booleanp :group 'fortran) -(put 'fortran-blink-matching-if 'safe-local-variable 'booleanp) (defcustom fortran-continuation-string "$" "Single-character string used for Fortran continuation lines. @@ -226,25 +222,23 @@ In fixed format continuation style, this character is inserted in column 6 by \\[fortran-split-line] to begin a continuation line. Also, if \\[fortran-indent-line] finds this at the beginning of a line, it will convert the line into a continuation line of the -appropriate style. Normally $." +appropriate style. Normally \"$\"." :type 'string + :safe (lambda (value) (and (stringp value) (= (length value) 1))) :group 'fortran) -(put 'fortran-continuation-string 'safe-local-variable - (lambda (value) (and (stringp value) - (= (length value) 1)))) (defcustom fortran-comment-region "c$$$" "String inserted by \\[fortran-comment-region] at start of each \ line in region." :type 'string + :safe 'stringp :group 'fortran-comment) -(put 'fortran-comment-region 'safe-local-variable 'stringp) (defcustom fortran-electric-line-number t "Non-nil causes line numbers to be moved to the correct column as typed." :type 'boolean + :safe 'booleanp :group 'fortran) -(put 'fortran-electric-line-number 'safe-local-variable 'booleanp) ;; TODO use fortran-line-length, somehow. (defcustom fortran-column-ruler-fixed @@ -256,8 +250,8 @@ line in region." This variable is used in fixed format mode. See the variable `fortran-column-ruler-tab' for TAB format mode." :type 'string + :safe 'stringp :group 'fortran) -(put 'fortran-column-ruler-fixed 'safe-local-variable 'stringp) ;; TODO use fortran-line-length, somehow. (defcustom fortran-column-ruler-tab @@ -269,21 +263,21 @@ See the variable `fortran-column-ruler-tab' for TAB format mode." This variable is used in TAB format mode. See the variable `fortran-column-ruler-fixed' for fixed format mode." :type 'string + :safe 'stringp :group 'fortran) -(put 'fortran-column-ruler-tab 'safe-local-variable 'stringp) (defcustom fortran-analyze-depth 100 "Number of lines to scan to identify fixed or TAB format style." :type 'integer + :safe 'integerp :group 'fortran) -(put 'fortran-analyze-depth 'safe-local-variable 'integerp) (defcustom fortran-break-before-delimiters t "Non-nil causes filling to break lines before delimiters. Delimiters are characters matching the regexp `fortran-break-delimiters-re'." :type 'boolean + :safe 'booleanp :group 'fortran) -(put 'fortran-break-before-delimiters 'safe-local-variable 'booleanp) ;; TODO 0 as no-limit, as per g77. (defcustom fortran-line-length 72 @@ -296,6 +290,7 @@ buffers and the default) or the function buffer). This corresponds to the g77 compiler option `-ffixed-line-length-N'." :type 'integer + :safe 'integerp :initialize 'custom-initialize-default :set (lambda (symbol value) ;; Do all fortran buffers, and the default. @@ -303,7 +298,6 @@ buffer). This corresponds to the g77 compiler option :version "23.1" :group 'fortran) -(put 'fortran-line-length 'safe-local-variable 'integerp) (make-variable-buffer-local 'fortran-line-length) (defcustom fortran-mode-hook nil @@ -330,6 +324,13 @@ characters long.") (defconst fortran-if-start-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(" "Regexp matching the start of an IF statement.") +;; Note fortran-current-defun uses the subgroups. +(defconst fortran-start-prog-re + "^[ \t]*\\(program\\|subroutine\\|function\ +\\|[ \ta-z0-9*()]*[ \t]+function\\|\ +\\(block[ \t]*data\\)\\)" + "Regexp matching the start of a subprogram, from the line start.") + (defconst fortran-end-prog-re1 "end\ \\([ \t]*\\(program\\|subroutine\\|function\\|block[ \t]*data\\)\\>\ @@ -391,8 +392,8 @@ program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?" 'paren) "\\>") ;; Builtin operators. (concat "\\." (regexp-opt - '("and" "or" "not" "lt" "le" "eq" "ge" - "gt" "ne" "true" "false") + '("and" "eq" "eqv" "false" "ge" "gt" "le" "lt" "ne" + "neqv" "not" "or" "true") 'paren) "\\.") ;; do/goto keywords and targets, and goto tags. '("\\<\\(do\\|go *to\\)\\>[ \t]*\\([0-9]+\\)?" @@ -401,6 +402,28 @@ program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?" '("^ *\\([0-9]+\\)" . font-lock-constant-face))) "Medium level highlighting for Fortran mode.") +;; See bug#1385. Never really looked into _why_ this matters... +(defun fortran-match-and-skip-declaration (limit) + "Like `font-lock-match-c-style-declaration-item-and-skip-to-next'. +The only difference is, it returns t in a case when the default returns nil." + (when (looking-at "[ \n\t*]*\\(\\sw+\\)[ \t\n]*\\(((?\\)?") + (when (and (match-end 2) (> (- (match-end 2) (match-beginning 2)) 1)) + (let ((pos (point))) + (skip-chars-backward " \t\n") + (skip-syntax-backward "w") + (unless (looking-at "\\(\\sw+\\)[ \t\n]*\\sw+[ \t\n]*\\(((?\\)?") + (goto-char pos) + (looking-at "[ \n\t*]*\\(\\sw+\\)[ \t\n]*\\(((?\\)?")))) + (save-match-data + (condition-case nil + (save-restriction + (narrow-to-region (point-min) limit) + (goto-char (match-end 1)) + (while (not (looking-at "[ \t\n]*\\(\\(,\\)\\|;\\|\\'\\)")) + (goto-char (or (scan-sexps (point) 1) (point-max)))) + (goto-char (match-end 2))) + (error t))))) + (defvar fortran-font-lock-keywords-3 (append fortran-font-lock-keywords-1 @@ -410,7 +433,7 @@ program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?" ;; Type specifier. '(1 font-lock-type-face) ;; Declaration item (or just /.../ block name). - `(font-lock-match-c-style-declaration-item-and-skip-to-next + `(fortran-match-and-skip-declaration ;; Start after any *(...) expression. (condition-case nil (and (match-beginning ,(1+ (regexp-opt-depth @@ -464,13 +487,22 @@ Consists of level 3 plus all other intrinsics not already highlighted.") ;; (We can do so for F90-style). Therefore an unmatched quote in a ;; standard comment will throw fontification off on the wrong track. ;; So we do syntactic fontification with regexps. -(defun fortran-font-lock-syntactic-keywords () - "Return a value for `font-lock-syntactic-keywords' in Fortran mode. -This varies according to the value of `fortran-line-length'. +(defun fortran-make-syntax-propertize-function (line-length) + "Return a value for `syntax-propertize-function' in Fortran mode. +This varies according to the value of LINE-LENGTH. This is used to fontify fixed-format Fortran comments." - `(("^[cd\\*]" 0 (11)) - (,(format "^[^cd\\*\t\n].\\{%d\\}\\([^\n]+\\)" (1- fortran-line-length)) - 1 (11)))) + ;; This results in a non-byte-compiled function. We could pass it through + ;; `byte-compile', but simple benchmarks indicate that it's probably not + ;; worth the trouble (about ½% of slow down). + (eval ;I hate `eval', but it's hard to avoid it here. + `(syntax-propertize-rules + ("^[cd\\*]" (0 "<")) + ;; We mark all chars after line-length as "comment-start", rather than + ;; just the first one. This is so that a closing ' that's past the + ;; line-length will indeed be ignored (and will result in a string that + ;; leaks into subsequent lines). + ((format "^[^cd\\*\t\n].\\{%d\\}\\(.+\\)" (1- line-length)) + (1 "<"))))) (defvar fortran-font-lock-keywords fortran-font-lock-keywords-1 "Default expressions to highlight in Fortran mode.") @@ -560,9 +592,9 @@ Used in the Fortran entry in `hs-special-modes-alist'.") (defvar fortran-mode-syntax-table (let ((table (make-syntax-table))) - ;; We might like `;' to be punctuation (g77 multi-statement - ;; lines), but that screws abbrevs. - (modify-syntax-entry ?\; "w" table) + ;; Was a word-constituent (for abbrevs), now punctuation (g77 + ;; multi-statement lines). + (modify-syntax-entry ?\; "." table) (modify-syntax-entry ?\r " " table) (modify-syntax-entry ?+ "." table) (modify-syntax-entry ?- "." table) @@ -619,14 +651,21 @@ Used in the Fortran entry in `hs-special-modes-alist'.") (easy-menu-define fortran-menu map "Menu for Fortran mode." `("Fortran" - ["Manual" (info "(emacs)Fortran")] + ["Manual" (info "(emacs)Fortran") :active t + :help "Read the Emacs manual chapter on Fortran mode"] ("Customization" ,(custom-menu-create 'fortran) - ["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"] ) "--" ["Comment Region" fortran-comment-region mark-active] @@ -636,9 +675,12 @@ Used in the Fortran entry in `hs-special-modes-alist'.") ["Indent Region" indent-region mark-active] ["Indent Subprogram" fortran-indent-subprogram t] "--" - ["Beginning of Subprogram" fortran-beginning-of-subprogram t] - ["End of Subprogram" fortran-end-of-subprogram t] + ["Beginning of Subprogram" fortran-beginning-of-subprogram :active t + :help "Move point to the start of the current subprogram"] + ["End of Subprogram" fortran-end-of-subprogram :active t + :help "Move point to the end of the current subprogram"] ("Mark" + :help "Mark a region of code" ["Subprogram" mark-defun t] ["IF Block" fortran-mark-if t] ["DO Block" fortran-mark-do t] @@ -646,106 +688,105 @@ Used in the Fortran entry in `hs-special-modes-alist'.") ["Narrow to Subprogram" narrow-to-defun t] ["Widen" widen t] "--" - ["Temporary column ruler" fortran-column-ruler t] + ["Temporary Column Ruler" fortran-column-ruler :active t + :help "Briefly display Fortran column numbers"] ;; May not be '72', depending on fortran-line-length, but this ;; seems ok for a menu item. - ["72-column window" fortran-window-create t] + ["72-column Window" fortran-window-create :active t + :help "Set window width to Fortran line length"] ["Full Width Window" (enlarge-window-horizontally (- (frame-width) (window-width))) - (not (window-full-width-p))] - ["Momentary 72-column window" fortran-window-create-momentarily t] + :active (not (window-full-width-p)) + :help "Make window full width"] + ["Momentary 72-Column Window" fortran-window-create-momentarily + :active t :help "Briefly set window width to Fortran line length"] "--" - ["Break Line at Point" fortran-split-line t] - ["Join Line" fortran-join-line t] - ["Fill Statement/Comment" fill-paragraph t] + ["Break Line at Point" fortran-split-line :active t + :help "Break the current line at point"] + ["Join Line" fortran-join-line :active t + :help "Join the current line to the previous one"] + ["Fill Statement/Comment" fill-paragraph t] "--" - ["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" imenu-add-menubar-index + ["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" imenu-add-menubar-index :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 Fortran mode.") -(defvar fortran-mode-abbrev-table - (progn - (define-abbrev-table 'fortran-mode-abbrev-table nil) - fortran-mode-abbrev-table) - "Abbrev table for Fortran mode.") - -;; Not in defvar because user abbrevs may be restored before this file loads. -(mapc - (lambda (e) - (condition-case nil - (define-abbrev fortran-mode-abbrev-table (car e) (cdr e) nil :count 0 - :system t) - (wrong-number-of-arguments ; Emacs 22 - (define-abbrev fortran-mode-abbrev-table (car e) (cdr e) nil 0 t)))) - '((";au" . "automatic" ) - (";b" . "byte" ) - (";bd" . "block data" ) - (";ch" . "character" ) - (";cl" . "close" ) - (";c" . "continue" ) - (";cm" . "common" ) - (";cx" . "complex" ) - (";df" . "define" ) - (";di" . "dimension" ) - (";do" . "double" ) - (";dc" . "double complex" ) - (";dp" . "double precision" ) - (";dw" . "do while" ) - (";e" . "else" ) - (";ed" . "enddo" ) - (";el" . "elseif" ) - (";en" . "endif" ) - (";eq" . "equivalence" ) - (";ew" . "endwhere" ) - (";ex" . "external" ) - (";ey" . "entry" ) - (";f" . "format" ) - (";fa" . ".false." ) - (";fu" . "function" ) - (";g" . "goto" ) - (";im" . "implicit" ) - (";ib" . "implicit byte" ) - (";ic" . "implicit complex" ) - (";ich" . "implicit character") - (";ii" . "implicit integer" ) - (";il" . "implicit logical" ) - (";ir" . "implicit real" ) - (";inc" . "include" ) - (";in" . "integer" ) - (";intr" . "intrinsic" ) - (";l" . "logical" ) - (";n" . "namelist" ) - (";o" . "open" ) ; was ;op - (";pa" . "parameter" ) - (";pr" . "program" ) - (";ps" . "pause" ) - (";p" . "print" ) - (";rc" . "record" ) - (";re" . "real" ) - (";r" . "read" ) - (";rt" . "return" ) - (";rw" . "rewind" ) - (";s" . "stop" ) - (";sa" . "save" ) - (";st" . "structure" ) - (";sc" . "static" ) - (";su" . "subroutine" ) - (";tr" . ".true." ) - (";ty" . "type" ) - (";vo" . "volatile" ) - (";w" . "write" ) - (";wh" . "where" ))) +(define-abbrev-table 'fortran-mode-abbrev-table + (mapcar (lambda (e) (list (car e) (cdr e) nil :system t)) + '((";au" . "automatic" ) + (";b" . "byte" ) + (";bd" . "block data" ) + (";ch" . "character" ) + (";cl" . "close" ) + (";c" . "continue" ) + (";cm" . "common" ) + (";cx" . "complex" ) + (";df" . "define" ) + (";di" . "dimension" ) + (";do" . "double" ) + (";dc" . "double complex" ) + (";dp" . "double precision" ) + (";dw" . "do while" ) + (";e" . "else" ) + (";ed" . "enddo" ) + (";el" . "elseif" ) + (";en" . "endif" ) + (";eq" . "equivalence" ) + (";ew" . "endwhere" ) + (";ex" . "external" ) + (";ey" . "entry" ) + (";f" . "format" ) + (";fa" . ".false." ) + (";fu" . "function" ) + (";g" . "goto" ) + (";im" . "implicit" ) + (";ib" . "implicit byte" ) + (";ic" . "implicit complex" ) + (";ich" . "implicit character") + (";ii" . "implicit integer" ) + (";il" . "implicit logical" ) + (";ir" . "implicit real" ) + (";inc" . "include" ) + (";in" . "integer" ) + (";intr" . "intrinsic" ) + (";l" . "logical" ) + (";n" . "namelist" ) + (";o" . "open" ) ; was ;op + (";pa" . "parameter" ) + (";pr" . "program" ) + (";ps" . "pause" ) + (";p" . "print" ) + (";rc" . "record" ) + (";re" . "real" ) + (";r" . "read" ) + (";rt" . "return" ) + (";rw" . "rewind" ) + (";s" . "stop" ) + (";sa" . "save" ) + (";st" . "structure" ) + (";sc" . "static" ) + (";su" . "subroutine" ) + (";tr" . ".true." ) + (";ty" . "type" ) + (";vo" . "volatile" ) + (";w" . "write" ) + (";wh" . "where" ))) + "Abbrev table for Fortran mode." + ;; Accept ; as the first char of an abbrev. Also allow _ in abbrevs. + :regexp "\\(?:[^[:word:]_;]\\|^\\)\\(;?[[:word:]_]+\\)[^[:word:]_]*") ;;;###autoload -(defun fortran-mode () +(define-derived-mode fortran-mode prog-mode "Fortran" "Major mode for editing Fortran code in fixed format. For free format code, use `f90-mode'. @@ -815,13 +856,9 @@ Variables controlling indentation style and extra features: Turning on Fortran mode calls the value of the variable `fortran-mode-hook' with no args, if that value is non-nil." - (interactive) - (kill-all-local-variables) - (setq major-mode 'fortran-mode - mode-name "Fortran" - local-abbrev-table fortran-mode-abbrev-table) - (set-syntax-table fortran-mode-syntax-table) - (use-local-map fortran-mode-map) + :group 'fortran + :syntax-table fortran-mode-syntax-table + :abbrev-table fortran-mode-abbrev-table (set (make-local-variable 'indent-line-function) 'fortran-indent-line) (set (make-local-variable 'indent-region-function) (lambda (start end) @@ -858,9 +895,9 @@ with no args, if that value is non-nil." fortran-font-lock-keywords-3 fortran-font-lock-keywords-4) nil t ((?/ . "$/") ("_$" . "w")) - fortran-beginning-of-subprogram - (font-lock-syntactic-keywords - . (fortran-font-lock-syntactic-keywords)))) + fortran-beginning-of-subprogram)) + (set (make-local-variable 'syntax-propertize-function) + (fortran-make-syntax-propertize-function fortran-line-length)) (set (make-local-variable 'imenu-case-fold-search) t) (set (make-local-variable 'imenu-generic-expression) fortran-imenu-generic-expression) @@ -873,33 +910,37 @@ with no args, if that value is non-nil." #'fortran-current-defun) (set (make-local-variable 'dabbrev-case-fold-search) 'case-fold-search) (set (make-local-variable 'gud-find-expr-function) 'fortran-gud-find-expr) - (add-hook 'hack-local-variables-hook 'fortran-hack-local-variables nil t) - (run-mode-hooks 'fortran-mode-hook)) + (add-hook 'hack-local-variables-hook 'fortran-hack-local-variables nil t)) (defun fortran-line-length (nchars &optional global) "Set the length of fixed-form Fortran lines to NCHARS. This normally only affects the current buffer, which must be in Fortran mode. If the optional argument GLOBAL is non-nil, it -affects all Fortran buffers, and also the default." - (interactive "p") - (let (new) - (mapc (lambda (buff) - (with-current-buffer buff - (when (eq major-mode 'fortran-mode) - (setq fortran-line-length nchars - fill-column fortran-line-length - new (fortran-font-lock-syntactic-keywords)) - ;; Refontify only if necessary. - (unless (equal new font-lock-syntactic-keywords) - (setq font-lock-syntactic-keywords - (fortran-font-lock-syntactic-keywords)) - (if font-lock-mode (font-lock-mode 1)))))) +affects all Fortran buffers, and also the default. +If a numeric prefix argument is specified, it will be used as NCHARS, +otherwise is a non-numeric prefix arg is specified, the length will be +provided via the minibuffer, and otherwise the current column is used." + (interactive + (list (cond + ((numberp current-prefix-arg) current-prefix-arg) + (current-prefix-arg + (read-number "Line length: " (default-value 'fortran-line-length))) + (t (current-column))))) + (dolist (buff (if global + (buffer-list) + (list (current-buffer)))) + (with-current-buffer buff + (when (derived-mode-p 'fortran-mode) + (unless (eq fortran-line-length nchars) + (setq fortran-line-length nchars + fill-column fortran-line-length + syntax-propertize-function + (fortran-make-syntax-propertize-function nchars)) + (syntax-ppss-flush-cache (point-min)) + (if font-lock-mode (font-lock-mode 1)))))) (if global - (buffer-list) - (list (current-buffer)))) - (if global - (setq-default fortran-line-length nchars)))) + (setq-default fortran-line-length nchars))) (defun fortran-hack-local-variables () "Fortran mode adds this to `hack-local-variables-hook'." @@ -997,7 +1038,7 @@ With non-nil ARG, uncomments the region." "Typing ;\\[help-command] or ;? lists all the Fortran abbrevs. Any other key combination is executed normally." (interactive "*") - (insert last-command-char) + (insert last-command-event) (let* ((event (if (fboundp 'next-command-event) ; XEmacs (next-command-event) (read-event))) @@ -1158,7 +1199,7 @@ Auto-indent does not happen if a numeric ARG is used." (looking-at "[0-9]")) ; within a line number (self-insert-command (prefix-numeric-value arg)) (skip-chars-backward " \t") - (insert last-command-char) + (insert last-command-event) (fortran-indent-line)))) @@ -1173,37 +1214,47 @@ Auto-indent does not happen if a numeric ARG is used." (+ fortran-line-length (line-beginning-position))))))) -;; Note that you can't just check backwards for `subroutine' &c in -;; case of un-marked main programs not at the start of the file. +;; This is more complex than first expected because the beginning of a +;; main program may be implicit (ie not marked by a PROGRAM statement). +;; This would be fine (we could just go to bob in the absence of a match), +;; except it need not even be the first subprogram in the file (eg it +;; could follow a subroutine). Hence we have to search for END +;; statements instead. +;; cf fortran-beginning-of-block, f90-beginning-of-subprogram +;; Note that unlike the latter, we don't have to worry about nested +;; subprograms (?). +;; FIXME push-mark? (defun fortran-beginning-of-subprogram () "Move point to the beginning of the current Fortran subprogram." (interactive) - (save-match-data - (let ((case-fold-search t)) - (beginning-of-line -1) - (if (catch 'ok - (while (re-search-backward fortran-end-prog-re nil 'move) - (if (fortran-check-end-prog-re) - (throw 'ok t)))) - (forward-line))))) - + (let ((case-fold-search t)) + ;; If called already at the start of subprogram, go to the previous. + (beginning-of-line (if (bolp) 0 1)) + (save-match-data + (or (looking-at fortran-start-prog-re) + ;; This leaves us at bob if before the first subprogram. + (eq (fortran-previous-statement) 'first-statement) + (if (or (catch 'ok + (while (re-search-backward fortran-end-prog-re nil 'move) + (if (fortran-check-end-prog-re) (throw 'ok t)))) + ;; If the search failed, must be at bob. + ;; First code line is the start of the subprogram. + ;; FIXME use a more rigorous test, cf fortran-next-statement? + ;; Though that needs to handle continuations too. + (not (looking-at "^\\([ \t]*[0-9]\\|[ \t]+[^!#]\\)"))) + (fortran-next-statement)))))) + +;; This is simpler than f-beginning-of-s because the end of a +;; subprogram is never implicit. (defun fortran-end-of-subprogram () "Move point to the end of the current Fortran subprogram." (interactive) - (save-match-data - (let ((case-fold-search t)) - (if (save-excursion ; on END - (beginning-of-line) - (and (looking-at fortran-end-prog-re) - (fortran-check-end-prog-re))) - (forward-line) - (beginning-of-line 2) - (catch 'ok - (while (re-search-forward fortran-end-prog-re nil 'move) - (if (fortran-check-end-prog-re) - (throw 'ok t)))) - (goto-char (match-beginning 0)) - (forward-line))))) + (let ((case-fold-search t)) + (beginning-of-line) + (save-match-data + (while (and (re-search-forward fortran-end-prog-re nil 'move) + (not (fortran-check-end-prog-re)))) + (forward-line)))) (defun fortran-previous-statement () "Move point to beginning of the previous Fortran statement. @@ -1263,8 +1314,7 @@ Directive lines are treated as comments." (if i (save-excursion (goto-char i) - (beginning-of-line) - (= (point) p))))) + (= (line-beginning-position) p))))) ;; Used in hs-special-modes-alist. (defun fortran-end-of-block (&optional num) @@ -1274,7 +1324,7 @@ If NUM is negative, go backward to the start of a block. Does not check for consistency of block types. 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) (fortran-beginning-of-block (- num))) (let ((case-fold-search t) (count (or num 1))) @@ -1307,7 +1357,7 @@ blocks. If NUM is negative, go forward to the end of a block. Does not check for consistency of block types. 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) (fortran-end-of-block (- num))) (let ((case-fold-search t) (count (or num 1))) @@ -1662,7 +1712,17 @@ Return point or nil." ((and (looking-at fortran-end-prog-re1) (fortran-check-end-prog-re)) ;; Previous END resets indent to minimum. - (setq icol fortran-minimum-statement-indent))))) + (setq icol fortran-minimum-statement-indent)) + ;; Previous statement was a numbered DO loop without a + ;; closing CONTINUE or END DO, so we indented the + ;; terminator like the loop body. + ((and fortran-check-all-num-for-matching-do + (not (looking-at "\\(continue\\|end[ \t]*do\\)\\>")) + (progn + (beginning-of-line) + (and (looking-at "[ \t]*[0-9]+") + (fortran-check-for-matching-do)))) + (setq icol (- icol fortran-do-indent)))))) (save-excursion (beginning-of-line) (cond ((looking-at "[ \t]*$")) @@ -1687,8 +1747,12 @@ Return point or nil." 6 (+ icol fortran-continuation-indent)))) (first-statement) + ;; The terminating statement is actually part of the + ;; loop body, so unless it is a CONTINUE or END DO, we + ;; indent it like the loop body (see above). ((and fortran-check-all-num-for-matching-do - (looking-at "[ \t]*[0-9]+") + (looking-at "[ \t]*[0-9]+[ \t]*\ +\\(continue\\|end[ \t]*do\\)\\>") (fortran-check-for-matching-do)) (setq icol (- icol fortran-do-indent))) (t @@ -2114,19 +2178,16 @@ arg DO-SPACE prevents stripping the whitespace." (replace-match "" nil nil nil 1) (unless do-space (delete-horizontal-space))))) -;; This code used to live in add-log.el, but this is a better place -;; for it. +;; This code used to live in add-log.el, but this is a better place for it. (defun fortran-current-defun () "Function to use for `add-log-current-defun-function' in Fortran mode." (save-excursion ;; We must be inside function body for this to work. (fortran-beginning-of-subprogram) - (let ((case-fold-search t)) ; case-insensitive + (let ((case-fold-search t)) ;; Search for fortran subprogram start. (if (re-search-forward - (concat "^[ \t]*\\(program\\|subroutine\\|function" - "\\|[ \ta-z0-9*()]*[ \t]+function\\|" - "\\(block[ \t]*data\\)\\)") + fortran-start-prog-re (save-excursion (fortran-end-of-subprogram) (point)) t) @@ -2144,5 +2205,4 @@ arg DO-SPACE prevents stripping the whitespace." (provide 'fortran) -;; arch-tag: 74935096-21c4-4cab-8ee5-6ef16090dc04 ;;; fortran.el ends here