X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/7dcc0df26071beed5b3f6723a9f8868843d2fa63..b336bfcdf39f1e4d35bff4a7bd01d3b4bca8f516:/lisp/progmodes/fortran.el diff --git a/lisp/progmodes/fortran.el b/lisp/progmodes/fortran.el index 0e7fd0ee05..3784ba787c 100644 --- a/lisp/progmodes/fortran.el +++ b/lisp/progmodes/fortran.el @@ -1,7 +1,8 @@ ;;; 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. +;; 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +;; Free Software Foundation, Inc. ;; Author: Michael D. Prange ;; Maintainer: Glenn Morris @@ -9,10 +10,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 +21,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: @@ -55,11 +54,11 @@ ;; silence compiler (defvar dabbrev-case-fold-search) -(defvar font-lock-syntactic-keywords) (defvar gud-find-expr-function) (defvar imenu-case-fold-search) (defvar imenu-syntax-alist) - +(defvar comment-region-function) +(defvar uncomment-region-function) (defgroup fortran nil "Major mode for editing fixed format Fortran code." @@ -78,48 +77,57 @@ :group 'fortran) -;;;###autoload (defcustom fortran-tab-mode-default nil - "*Default tabbing/carriage control style for empty files in Fortran mode. + "Default tabbing/carriage control style for empty files in Fortran mode. 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) -(defcustom fortran-tab-mode-string "/t" - "*String to appear in mode line in TAB format buffers." +;; TODO add more detail of what tab mode is to doc string. +(defcustom fortran-tab-mode-string + (propertize "/t" 'help-echo "This buffer is in Fortran TAB mode" + 'mouse-face 'mode-line-highlight + 'local-map + (make-mode-line-mouse-map 'mouse-1 + (lambda () + (interactive) + (describe-variable + 'fortran-tab-mode-string)))) + "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 'safe-local-variable 'stringp) (defcustom fortran-do-indent 3 - "*Extra indentation applied to DO blocks." + "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." + "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." + "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." + "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. + "How to indent comments. nil forces comment lines not to be touched; `fixed' indents to `fortran-comment-line-extra-indent' columns beyond `fortran-minimum-statement-indent-fixed' (if `indent-tabs-mode' nil), or @@ -127,116 +135,113 @@ 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." + "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." + "Delimiter inserted to start new full-line comment. +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. (defcustom fortran-comment-line-start-skip "^[CcDd*!]\\(\\([^ \t\n]\\)\\2+\\)?[ \t]*" - "*Regexp to match the start of a full-line comment." + "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]*#.*" - "*Regexp to match a directive line. + "Regexp to match a directive line. 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." + "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." + "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. ;; The code in this file accepts either format for compatibility. (defcustom fortran-comment-indent-char " " - "*Single-character string inserted for Fortran comment 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 (char-valid-p value) - (and (stringp value) - (= (length value) 1))))) (defcustom fortran-line-number-indent 1 - "*Maximum indentation for Fortran line numbers. + "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." + "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. + "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. + "Single-character string used for Fortran continuation lines. 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 \ + "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." + "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 "0 4 6 10 20 30 40 5\ 0 60 70\n\ @@ -246,9 +251,10 @@ 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 "0 810 20 30 40 5\ 0 60 70\n\ @@ -258,22 +264,49 @@ 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. + "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) + +;; TODO 0 as no-limit, as per g77. +(defcustom fortran-line-length 72 + "Maximum number of characters in a line of fixed-form Fortran code. +Characters beyond this point are treated as comments. Setting +this variable directly (after fortran mode is loaded) does not +take effect. Use either \\[customize] (which affects all Fortran +buffers and the default) or the function +`fortran-line-length' (which can also operate on just the current +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. + (fortran-line-length value t)) + :version "23.1" + :group 'fortran) + +(make-variable-buffer-local 'fortran-line-length) + +(defcustom fortran-mode-hook nil + "Hook run when entering Fortran mode." + :type 'hook :group 'fortran) -(put 'fortran-break-before-delimiters 'safe-local-variable 'booleanp) + (defconst fortran-break-delimiters-re "[-+*/><=, \t]" "Regexp matching delimiter characters at which lines may be broken. There are certain tokens comprised entirely of characters @@ -289,22 +322,23 @@ characters matching the regexp `fortran-break-delimiters-re' that should not be split by filling. Each element is assumed to be two characters long.") -(defcustom fortran-mode-hook nil - "Hook run when entering Fortran mode." - :type 'hook - :group 'fortran) - - -(defvar fortran-if-start-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(" +(defconst fortran-if-start-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(" "Regexp matching the start of an IF statement.") -(defvar fortran-end-prog-re1 +;; 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\\)\\>\ \\([ \t]*\\(\\sw\\|\\s_\\)+\\)?\\)?" "Regexp possibly matching the end of a subprogram.") -(defvar fortran-end-prog-re +(defconst fortran-end-prog-re (concat "^[ \t0-9]*" fortran-end-prog-re1) "Regexp possibly matching the end of a subprogram, from the line start. See also `fortran-end-prog-re1'.") @@ -359,8 +393,8 @@ program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?" 'paren) "\\>") ;; Builtin operators. (concat "\\." (regexp-opt - '("and" "or" "not" "lt" "le" "eq" "ge" - "gt" "ne" "eqv" "neqv" "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]+\\)?" @@ -369,6 +403,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 @@ -378,7 +434,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 @@ -432,11 +488,13 @@ 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. -(defvar fortran-font-lock-syntactic-keywords - '(("^[cd\\*]" 0 (11)) - ("^[^cd\\*\t\n].\\{71\\}\\([^\n]+\\)" 1 (11))) - "`font-lock-syntactic-keywords' for Fortran. -These get fixed-format comments fontified.") +(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'. +This is used to fontify fixed-format Fortran comments." + `(("^[cd\\*]" 0 (11)) + (,(format "^[^cd\\*\t\n].\\{%d\\}\\([^\n]+\\)" (1- fortran-line-length)) + 1 (11)))) (defvar fortran-font-lock-keywords fortran-font-lock-keywords-1 "Default expressions to highlight in Fortran mode.") @@ -520,15 +578,15 @@ tries to strike a compromise between complexity and flexibility. Used in the Fortran entry in `hs-special-modes-alist'.") (add-to-list 'hs-special-modes-alist - `(fortran-mode ,fortran-start-block-re ,fortran-end-block-re + `(fortran-mode ,fortran-start-block-re ,fortran-end-block-re "^[cC*!]" fortran-end-of-block nil)) (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) @@ -560,7 +618,8 @@ Used in the Fortran entry in `hs-special-modes-alist'.") (let ((map (make-sparse-keymap))) (define-key map ";" 'fortran-abbrev-start) (define-key map "\C-c;" 'fortran-comment-region) - (define-key map "\M-;" 'fortran-indent-comment) + ;; The default comment-dwim does at least as much as this. +;;; (define-key map "\M-;" 'fortran-indent-comment) (define-key map "\M-\n" 'fortran-split-line) (define-key map "\M-\C-n" 'fortran-end-of-block) (define-key map "\M-\C-p" 'fortran-beginning-of-block) @@ -584,14 +643,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] @@ -601,9 +667,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] @@ -611,103 +680,101 @@ 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] - ["72-column window" fortran-window-create 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 :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.") - -(let (abbrevs-changed) - ;; Use the 6th arg (SYSTEM-FLAG) of define-abbrev if possible. - ;; Only use `apply' to quieten the byte-compiler. - (mapcar - (function (lambda (element) - (condition-case nil - (apply 'define-abbrev fortran-mode-abbrev-table - (append element '(nil 0 t))) - (wrong-number-of-arguments - (apply 'define-abbrev fortran-mode-abbrev-table - (append element '(nil 0))))))) - '((";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 @@ -748,7 +815,7 @@ Variables controlling indentation style and extra features: `fortran-minimum-statement-indent-tab' (TAB format), depending on the continuation format in use. relative indent to `fortran-comment-line-extra-indent' beyond the - indentation for a line of code. + indentation for a line of code. (default 'fixed) `fortran-comment-indent-char' Single-character string to be inserted instead of space for @@ -806,11 +873,16 @@ with no args, if that value is non-nil." ;; (concat "\\(\\)\\(![ \t]*\\|" fortran-comment-line-start-skip "\\)") "\\(\\)\\(?:^[CcDd*]\\|!\\)\\(?:\\([^ \t\n]\\)\\2+\\)?[ \t]*") (set (make-local-variable 'comment-indent-function) 'fortran-comment-indent) + (set (make-local-variable 'comment-region-function) 'fortran-comment-region) + (set (make-local-variable 'uncomment-region-function) + 'fortran-uncomment-region) + (set (make-local-variable 'comment-insert-comment-function) + 'fortran-indent-comment) (set (make-local-variable 'abbrev-all-caps) t) (set (make-local-variable 'normal-auto-fill-function) 'fortran-auto-fill) (set (make-local-variable 'indent-tabs-mode) (fortran-analyze-file-format)) (setq mode-line-process '(indent-tabs-mode fortran-tab-mode-string)) - (set (make-local-variable 'fill-column) 72) + (set (make-local-variable 'fill-column) fortran-line-length) (set (make-local-variable 'fill-paragraph-function) 'fortran-fill-paragraph) (set (make-local-variable 'font-lock-defaults) '((fortran-font-lock-keywords @@ -819,9 +891,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)) - (set (make-local-variable 'font-lock-syntactic-keywords) - fortran-font-lock-syntactic-keywords) + fortran-beginning-of-subprogram + (font-lock-syntactic-keywords + . fortran-font-lock-syntactic-keywords))) (set (make-local-variable 'imenu-case-fold-search) t) (set (make-local-variable 'imenu-generic-expression) fortran-imenu-generic-expression) @@ -834,9 +906,40 @@ 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)) +(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)))))) + (if global + (buffer-list) + (list (current-buffer)))) + (if global + (setq-default fortran-line-length nchars)))) + +(defun fortran-hack-local-variables () + "Fortran mode adds this to `hack-local-variables-hook'." + (fortran-line-length fortran-line-length)) + +(declare-function gud-find-c-expr "gud.el" nil) + (defun fortran-gud-find-expr () ;; Consider \n as punctuation (end of expression). (with-syntax-table fortran-gud-syntax-table @@ -862,33 +965,33 @@ or on a new line inserted before this line if this line is not blank." (beginning-of-line) ;; Recognize existing comments of either kind. (cond ((fortran-find-comment-start-skip 'all) - (goto-char (match-beginning 0)) - (if (bolp) - (fortran-indent-line) - (unless (= (current-column) (fortran-comment-indent)) + (goto-char (match-beginning 0)) + (if (bolp) + (fortran-indent-line) + (unless (= (current-column) (fortran-comment-indent)) (delete-horizontal-space) (indent-to (fortran-comment-indent))))) - ;; No existing comment. - ;; If side-by-side comments are defined, insert one, - ;; unless line is now blank. - ((and comment-start (not (looking-at "[ \t]*$")) - (string-match comment-start-skip (concat " " comment-start))) - (end-of-line) - (delete-horizontal-space) - (indent-to (fortran-comment-indent)) - (insert comment-start)) - ;; Else insert separate-line comment, making a new line if nec. - (t - (if (looking-at "^[ \t]*$") - (delete-horizontal-space) - (beginning-of-line) - (insert ?\n) - (forward-char -1)) - (insert fortran-comment-line-start) - (insert-char (if (stringp fortran-comment-indent-char) - (aref fortran-comment-indent-char 0) - fortran-comment-indent-char) - (- (fortran-calculate-indent) (current-column)))))) + ;; No existing comment. + ;; If side-by-side comments are defined, insert one, + ;; unless line is now blank. + ((and comment-start (not (looking-at "[ \t]*$")) + (string-match comment-start-skip (concat " " comment-start))) + (end-of-line) + (delete-horizontal-space) + (indent-to (fortran-comment-indent)) + (insert comment-start)) + ;; Else insert separate-line comment, making a new line if nec. + (t + (if (looking-at "^[ \t]*$") + (delete-horizontal-space) + (beginning-of-line) + (insert ?\n) + (forward-char -1)) + (insert fortran-comment-line-start) + (insert-char (if (stringp fortran-comment-indent-char) + (aref fortran-comment-indent-char 0) + fortran-comment-indent-char) + (- (fortran-calculate-indent) (current-column)))))) (defun fortran-comment-region (beg-region end-region arg) "Comment every line in the region. @@ -898,7 +1001,7 @@ BEG-REGION and END-REGION specify the region boundaries. With non-nil ARG, uncomments the region." (interactive "*r\nP") (let ((end-region-mark (copy-marker end-region)) - (save-point (point-marker))) + (save-point (point-marker))) (goto-char beg-region) (beginning-of-line) (if arg @@ -917,12 +1020,17 @@ With non-nil ARG, uncomments the region." (set-marker end-region-mark nil) (set-marker save-point nil))) +;; uncomment-region calls this with 3 args. +(defun fortran-uncomment-region (start end &optional ignored) + "Uncomment every line in the region." + (fortran-comment-region start end t)) + (defun fortran-abbrev-start () "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))) @@ -931,7 +1039,7 @@ Any other key combination is executed normally." ;; Insert char if not equal to `?', or if abbrev-mode is off. (if (and abbrev-mode (or (eq char ??) (eq char help-char) (memq event help-event-list))) - (fortran-abbrev-help) + (fortran-abbrev-help) (push event unread-command-events)))) (defun fortran-abbrev-help () @@ -964,44 +1072,44 @@ The next key typed is executed unless it is SPC." (save-excursion (beginning-of-line) (if (eq (window-start (selected-window)) - (window-point (selected-window))) - (line-beginning-position 2) + (window-point (selected-window))) + (line-beginning-position 2) (point))) nil "Type SPC or any command to erase ruler.")) (defun fortran-window-create () - "Make the window 72 columns wide. + "Make the window `fortran-line-length' (default 72) columns wide. See also `fortran-window-create-momentarily'." (interactive) (let ((window-min-width 2)) (unless (window-full-width-p) - (enlarge-window-horizontally (- (frame-width) - (window-width) 1))) + (enlarge-window-horizontally (- (frame-width) + (window-width) 1))) (let* ((window-edges (window-edges)) - (scroll-bar-width (- (nth 2 window-edges) - (car window-edges) - (window-width)))) - (split-window-horizontally (+ 72 scroll-bar-width))) + (scroll-bar-width (- (nth 2 window-edges) + (car window-edges) + (window-width)))) + (split-window-horizontally (+ fortran-line-length scroll-bar-width))) (other-window 1) (switch-to-buffer " fortran-window-extra" t) (select-window (previous-window)))) (defun fortran-window-create-momentarily (&optional arg) - "Momentarily make the window 72 columns wide. + "Momentarily make the window `fortran-line-length' (default 72) columns wide. Optional ARG non-nil and non-unity disables the momentary feature. See also `fortran-window-create'." (interactive "p") (if (or (not arg) - (= arg 1)) + (= arg 1)) (save-window-excursion - (progn - (condition-case nil - (fortran-window-create) - (error (error "No room for Fortran window"))) - (message "Type SPC to continue editing.") - (let ((char (read-event))) - (or (equal char ?\s) - (setq unread-command-events (list char)))))) + (progn + (condition-case nil + (fortran-window-create) + (error (error "No room for Fortran window"))) + (message "Type SPC to continue editing.") + (let ((char (read-event))) + (or (equal char ?\s) + (setq unread-command-events (list char)))))) (fortran-window-create))) (defun fortran-split-line () @@ -1009,13 +1117,13 @@ See also `fortran-window-create'." (interactive "*") (delete-horizontal-space) (if (save-excursion - (let ((pos (point))) - (beginning-of-line) - (and (fortran-find-comment-start-skip 'all) - (< (match-beginning 0) pos)))) + (let ((pos (point))) + (beginning-of-line) + (and (fortran-find-comment-start-skip 'all) + (< (match-beginning 0) pos)))) (insert ?\n (match-string 0)) (if indent-tabs-mode - (insert ?\n ?\t (fortran-numerical-continuation-char)) + (insert ?\n ?\t (fortran-numerical-continuation-char)) (insert "\n " fortran-continuation-string))) ; space after \n important (fortran-indent-line)) ; when cont string is C, c or * @@ -1051,7 +1159,7 @@ plus one, otherwise return 1. Zero not allowed." (save-excursion (forward-line -1) (if (looking-at "\t[1-9]") - (+ ?1 (% (- (char-after (1+ (point))) ?0) 9)) + (+ ?1 (% (- (char-after (1+ (point))) ?0) 9)) ?1))) (put 'fortran-electric-line-number 'delete-selection t) @@ -1061,29 +1169,29 @@ Auto-indent does not happen if a numeric ARG is used." (interactive "*P") (if (or arg (not fortran-electric-line-number)) (if arg - (self-insert-command (prefix-numeric-value arg)) - (self-insert-command 1)) + (self-insert-command (prefix-numeric-value arg)) + (self-insert-command 1)) (if (or (and (= 5 (current-column)) - (save-excursion - (beginning-of-line) + (save-excursion + (beginning-of-line) ;; In col 5 with only spaces to the left. - (looking-at " \\{5\\}"))) - (and (= (if indent-tabs-mode - fortran-minimum-statement-indent-tab - fortran-minimum-statement-indent-fixed) (current-column)) + (looking-at " \\{5\\}"))) + (and (= (if indent-tabs-mode + fortran-minimum-statement-indent-tab + fortran-minimum-statement-indent-fixed) (current-column)) ;; In col 8 with a single tab to the left. - (eq ?\t (char-after (line-beginning-position))) - (not (or (eq last-command 'fortran-indent-line) - (eq last-command - 'fortran-indent-new-line)))) - (save-excursion - (re-search-backward "[^ \t0-9]" - (line-beginning-position) - t)) ; not a line number - (looking-at "[0-9]")) ; within a line number - (self-insert-command (prefix-numeric-value arg)) + (eq ?\t (char-after (line-beginning-position))) + (not (or (eq last-command 'fortran-indent-line) + (eq last-command + 'fortran-indent-new-line)))) + (save-excursion + (re-search-backward "[^ \t0-9]" + (line-beginning-position) + t)) ; not a line number + (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)))) @@ -1093,41 +1201,52 @@ Auto-indent does not happen if a numeric ARG is used." ;; match of whitespace, avoiding possible column 73+ stuff. (save-match-data (string-match "^\\s-*\\(\\'\\|\\s<\\)" - (buffer-substring (match-end 0) - (min (line-end-position) - (+ 72 (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. + (buffer-substring (match-end 0) + (min (line-end-position) + (+ fortran-line-length + (line-beginning-position))))))) + +;; 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. @@ -1138,28 +1257,28 @@ Directive lines are treated as comments." (let (not-first-statement continue-test) (beginning-of-line) (setq continue-test - (and - (not (looking-at fortran-comment-line-start-skip)) + (and + (not (looking-at fortran-comment-line-start-skip)) (not (looking-at fortran-directive-re)) - (or (looking-at - (concat "[ \t]*" - (regexp-quote fortran-continuation-string))) - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")))) + (or (looking-at + (concat "[ \t]*" + (regexp-quote fortran-continuation-string))) + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")))) (while (and (setq not-first-statement (zerop (forward-line -1))) - (or (looking-at fortran-comment-line-start-skip) + (or (looking-at fortran-comment-line-start-skip) (looking-at fortran-directive-re) (looking-at (concat "[ \t]*" (regexp-quote fortran-continuation-string))) - (looking-at "[ \t]*$\\| \\{5\\}[^ 0\n]\\|\t[1-9]") - (looking-at (concat "[ \t]*" comment-start-skip))))) + (looking-at "[ \t]*$\\| \\{5\\}[^ 0\n]\\|\t[1-9]") + (looking-at (concat "[ \t]*" comment-start-skip))))) (cond ((and continue-test - (not not-first-statement)) - (message "Incomplete continuation statement.")) - (continue-test - (fortran-previous-statement)) - ((not not-first-statement) - 'first-statement)))) + (not not-first-statement)) + (message "Incomplete continuation statement.")) + (continue-test + (fortran-previous-statement)) + ((not not-first-statement) + 'first-statement)))) (defun fortran-next-statement () "Move point to beginning of the next Fortran statement. @@ -1170,14 +1289,14 @@ Directive lines are treated as comments." (let (not-last-statement) (beginning-of-line) (while (and (setq not-last-statement - (and (zerop (forward-line 1)) - (not (eobp)))) - (or (looking-at fortran-comment-line-start-skip) + (and (zerop (forward-line 1)) + (not (eobp)))) + (or (looking-at fortran-comment-line-start-skip) (looking-at fortran-directive-re) - (looking-at "[ \t]*$\\| [^ 0\n]\\|\t[1-9]") - (looking-at (concat "[ \t]*" comment-start-skip))))) + (looking-at "[ \t]*$\\| [^ 0\n]\\|\t[1-9]") + (looking-at (concat "[ \t]*" comment-start-skip))))) (if (not not-last-statement) - 'last-statement))) + 'last-statement))) (defun fortran-looking-at-if-then () "Return non-nil if at the start of a line with an IF ... THEN statement." @@ -1198,7 +1317,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))) @@ -1231,7 +1350,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))) @@ -1262,10 +1381,10 @@ pushes mark before moving point." "From a line matching REGEX, blink matching KEYWORD statement line. Use function FIND-BEGIN to match it." (let ((top-of-window (window-start)) - (end-point (point)) - (case-fold-search t) - matching - message) + (end-point (point)) + (case-fold-search t) + matching + message) (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9") @@ -1289,7 +1408,7 @@ Use function FIND-BEGIN to match it." (defun fortran-blink-matching-if () "From an ENDIF or ELSE statement, blink the matching IF statement." (fortran-blink-match "e\\(nd[ \t]*if\\|lse\\([ \t]*if\\)?\\)\\b" - "if" #'fortran-beginning-if)) + "if" #'fortran-beginning-if)) (defun fortran-blink-matching-do () "From an ENDDO statement, blink the matching DO or DO WHILE statement." @@ -1312,27 +1431,27 @@ The marks are pushed." Return point or nil." (let ((case-fold-search t)) (if (save-excursion (beginning-of-line) - (skip-chars-forward " \t0-9") - (looking-at "end[ \t]*do\\b")) - ;; Sitting on one. - (match-beginning 0) + (skip-chars-forward " \t0-9") + (looking-at "end[ \t]*do\\b")) + ;; Sitting on one. + (match-beginning 0) ;; Search for one. (save-excursion - (let ((count 1)) - (while (and (not (zerop count)) - (not (eq (fortran-next-statement) 'last-statement)) - ;; Keep local to subprogram. - (not (and (looking-at fortran-end-prog-re) - (fortran-check-end-prog-re)))) - (skip-chars-forward " \t0-9") - (cond ((looking-at "end[ \t]*do\\b") - (setq count (1- count))) - ((looking-at - "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+[^0-9]") - (setq count (1+ count))))) - (and (zerop count) - ;; All pairs accounted for. - (point))))))) + (let ((count 1)) + (while (and (not (zerop count)) + (not (eq (fortran-next-statement) 'last-statement)) + ;; Keep local to subprogram. + (not (and (looking-at fortran-end-prog-re) + (fortran-check-end-prog-re)))) + (skip-chars-forward " \t0-9") + (cond ((looking-at "end[ \t]*do\\b") + (setq count (1- count))) + ((looking-at + "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+[^0-9]") + (setq count (1+ count))))) + (and (zerop count) + ;; All pairs accounted for. + (point))))))) (defun fortran-beginning-do () "Search backwards for first unmatched DO [WHILE]. @@ -1340,28 +1459,28 @@ Return point or nil. Ignores labelled DO loops (ie DO 10 ... 10 CONTINUE)." (let ((case-fold-search t) (dostart-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+[^0-9]")) (if (save-excursion - (beginning-of-line) - (skip-chars-forward " \t0-9") - (looking-at dostart-re)) - ;; Sitting on one. - (match-beginning 0) + (beginning-of-line) + (skip-chars-forward " \t0-9") + (looking-at dostart-re)) + ;; Sitting on one. + (match-beginning 0) ;; Search for one. (save-excursion - (let ((count 1)) - (while (and (not (zerop count)) - (not (eq (fortran-previous-statement) 'first-statement)) - ;; Keep local to subprogram. - (not (and (looking-at fortran-end-prog-re) - (fortran-check-end-prog-re)))) - (skip-chars-forward " \t0-9") - (cond ((looking-at dostart-re) - (setq count (1- count))) + (let ((count 1)) + (while (and (not (zerop count)) + (not (eq (fortran-previous-statement) 'first-statement)) + ;; Keep local to subprogram. + (not (and (looking-at fortran-end-prog-re) + (fortran-check-end-prog-re)))) + (skip-chars-forward " \t0-9") + (cond ((looking-at dostart-re) + (setq count (1- count))) ;; Note labelled loop ends not considered. - ((looking-at "end[ \t]*do\\b") - (setq count (1+ count))))) - (and (zerop count) - ;; All pairs accounted for. - (point))))))) + ((looking-at "end[ \t]*do\\b") + (setq count (1+ count))))) + (and (zerop count) + ;; All pairs accounted for. + (point))))))) (defun fortran-mark-if () "Put mark at end of Fortran IF-ENDIF construct, point at beginning. @@ -1381,103 +1500,103 @@ The marks are pushed." Return point or nil." (let ((case-fold-search t)) (if (save-excursion (beginning-of-line) - (skip-chars-forward " \t0-9") - (looking-at "end[ \t]*if\\b")) - ;; Sitting on one. - (match-beginning 0) + (skip-chars-forward " \t0-9") + (looking-at "end[ \t]*if\\b")) + ;; Sitting on one. + (match-beginning 0) ;; Search for one. The point has been already been moved to first ;; letter on line but this should not cause troubles. (save-excursion - (let ((count 1)) - (while (and (not (zerop count)) - (not (eq (fortran-next-statement) 'last-statement)) - ;; Keep local to subprogram. - (not (and (looking-at fortran-end-prog-re) - (fortran-check-end-prog-re)))) - (skip-chars-forward " \t0-9") - (cond ((looking-at "end[ \t]*if\\b") - (setq count (1- count))) - ((looking-at fortran-if-start-re) - (save-excursion - (if (or - (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") - (let (then-test) ; multi-line if-then - (while - (and - (zerop (forward-line 1)) - ;; Search forward for then. - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") - (not - (setq then-test - (looking-at - ".*then\\b[ \t]*[^ \t(=a-z0-9]"))))) - then-test)) - (setq count (1+ count))))))) - (and (zerop count) - ;; All pairs accounted for. - (point))))))) + (let ((count 1)) + (while (and (not (zerop count)) + (not (eq (fortran-next-statement) 'last-statement)) + ;; Keep local to subprogram. + (not (and (looking-at fortran-end-prog-re) + (fortran-check-end-prog-re)))) + (skip-chars-forward " \t0-9") + (cond ((looking-at "end[ \t]*if\\b") + (setq count (1- count))) + ((looking-at fortran-if-start-re) + (save-excursion + (if (or + (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") + (let (then-test) ; multi-line if-then + (while + (and + (zerop (forward-line 1)) + ;; Search forward for then. + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") + (not + (setq then-test + (looking-at + ".*then\\b[ \t]*[^ \t(=a-z0-9]"))))) + then-test)) + (setq count (1+ count))))))) + (and (zerop count) + ;; All pairs accounted for. + (point))))))) (defun fortran-beginning-if () "Search backwards for first unmatched IF-THEN. Return point or nil." (let ((case-fold-search t)) (if (save-excursion - ;; May be sitting on multi-line if-then statement, first - ;; move to beginning of current statement. Note: - ;; `fortran-previous-statement' moves to previous statement - ;; *unless* current statement is first one. Only move - ;; forward if not first-statement. - (if (not (eq (fortran-previous-statement) 'first-statement)) - (fortran-next-statement)) - (skip-chars-forward " \t0-9") - (and - (looking-at fortran-if-start-re) - (save-match-data - (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") - ;; Multi-line if-then. - (let (then-test) - (while + ;; May be sitting on multi-line if-then statement, first + ;; move to beginning of current statement. Note: + ;; `fortran-previous-statement' moves to previous statement + ;; *unless* current statement is first one. Only move + ;; forward if not first-statement. + (if (not (eq (fortran-previous-statement) 'first-statement)) + (fortran-next-statement)) + (skip-chars-forward " \t0-9") + (and + (looking-at fortran-if-start-re) + (save-match-data + (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") + ;; Multi-line if-then. + (let (then-test) + (while (and (zerop (forward-line 1)) - ;; Search forward for then. - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") - (not - (setq then-test - (looking-at - ".*then\\b[ \t]*[^ \t(=a-z0-9]"))))) - then-test))))) - ;; Sitting on one. - (match-beginning 0) + ;; Search forward for then. + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") + (not + (setq then-test + (looking-at + ".*then\\b[ \t]*[^ \t(=a-z0-9]"))))) + then-test))))) + ;; Sitting on one. + (match-beginning 0) ;; Search for one. (save-excursion - (let ((count 1)) - (while (and (not (zerop count)) - (not (eq (fortran-previous-statement) 'first-statement)) - ;; Keep local to subprogram. - (not (and (looking-at fortran-end-prog-re) - (fortran-check-end-prog-re)))) - (skip-chars-forward " \t0-9") - (cond ((looking-at fortran-if-start-re) - (save-excursion - (if (or - (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") - (let (then-test) ; multi-line if-then - (while - (and - (zerop (forward-line 1)) - ;; Search forward for then. - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") - (not - (setq then-test - (looking-at - (concat ".*then\\b[ \t]*" - "[^ \t(=a-z0-9]")))))) - then-test)) - (setq count (1- count))))) - ((looking-at "end[ \t]*if\\b") - (setq count (1+ count))))) - (and (zerop count) - ;; All pairs accounted for. - (point))))))) + (let ((count 1)) + (while (and (not (zerop count)) + (not (eq (fortran-previous-statement) 'first-statement)) + ;; Keep local to subprogram. + (not (and (looking-at fortran-end-prog-re) + (fortran-check-end-prog-re)))) + (skip-chars-forward " \t0-9") + (cond ((looking-at fortran-if-start-re) + (save-excursion + (if (or + (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]") + (let (then-test) ; multi-line if-then + (while + (and + (zerop (forward-line 1)) + ;; Search forward for then. + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") + (not + (setq then-test + (looking-at + (concat ".*then\\b[ \t]*" + "[^ \t(=a-z0-9]")))))) + then-test)) + (setq count (1- count))))) + ((looking-at "end[ \t]*if\\b") + (setq count (1+ count))))) + (and (zerop count) + ;; All pairs accounted for. + (point))))))) (defun fortran-indent-line () @@ -1487,15 +1606,15 @@ Return point or nil." (save-excursion (beginning-of-line) (if (or (not (= cfi (fortran-current-line-indentation))) - (and (re-search-forward "^[ \t]*[0-9]+" (+ (point) 4) t) - (not (fortran-line-number-indented-correctly-p)))) - (fortran-indent-to-column cfi) - (beginning-of-line) - (if (fortran-find-comment-start-skip) - (fortran-indent-comment)))) + (and (re-search-forward "^[ \t]*[0-9]+" (+ (point) 4) t) + (not (fortran-line-number-indented-correctly-p)))) + (fortran-indent-to-column cfi) + (beginning-of-line) + (if (fortran-find-comment-start-skip) + (fortran-indent-comment)))) ;; Never leave point in left margin. (if (< (current-column) cfi) - (move-to-column cfi)) + (move-to-column cfi)) (and auto-fill-function (> (save-excursion (end-of-line) (current-column)) fill-column) @@ -1510,20 +1629,20 @@ Return point or nil." "Function to use for `normal-auto-fill-function' in Fortran mode." (if (> (current-column) (current-fill-column)) (let ((cfi (fortran-calculate-indent))) - (save-excursion - (beginning-of-line) - (if (or (not (= cfi (fortran-current-line-indentation))) - (and (re-search-forward "^[ \t]*[0-9]+" - (+ (point) 4) t) - (not (fortran-line-number-indented-correctly-p)))) - (fortran-indent-to-column cfi) - (beginning-of-line) - (if (fortran-find-comment-start-skip) - (fortran-indent-comment)))) - (fortran-fill) - ;; Never leave point in left margin. - (if (< (current-column) cfi) - (move-to-column cfi))))) + (save-excursion + (beginning-of-line) + (if (or (not (= cfi (fortran-current-line-indentation))) + (and (re-search-forward "^[ \t]*[0-9]+" + (+ (point) 4) t) + (not (fortran-line-number-indented-correctly-p)))) + (fortran-indent-to-column cfi) + (beginning-of-line) + (if (fortran-find-comment-start-skip) + (fortran-indent-comment)))) + (fortran-fill) + ;; Never leave point in left margin. + (if (< (current-column) cfi) + (move-to-column cfi))))) ;; Historically this was a separate function which advertised itself ;; as reindenting but only did so where `most likely to be necessary'. @@ -1541,21 +1660,21 @@ Return point or nil." (defun fortran-calculate-indent () "Calculates the Fortran indent column based on previous lines." (let (icol first-statement (case-fold-search t) - (fortran-minimum-statement-indent - (if indent-tabs-mode - fortran-minimum-statement-indent-tab - fortran-minimum-statement-indent-fixed))) + (fortran-minimum-statement-indent + (if indent-tabs-mode + fortran-minimum-statement-indent-tab + fortran-minimum-statement-indent-fixed))) (save-excursion (setq first-statement (fortran-previous-statement)) (if first-statement - (setq icol fortran-minimum-statement-indent) + (setq icol fortran-minimum-statement-indent) (if (= (point) (point-min)) (setq icol fortran-minimum-statement-indent) (setq icol (fortran-current-line-indentation))) (skip-chars-forward " \t0-9") (cond ((looking-at "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(") (if (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t_$(=a-z0-9]") - (let (then-test) ; multi-line if-then + (let (then-test) ; multi-line if-then (while (and (zerop (forward-line 1)) ;; Search forward for then. (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]") @@ -1586,57 +1705,71 @@ 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]*$")) ;; Check for directive before comment, so as not to indent. - ((looking-at fortran-directive-re) - (setq fortran-minimum-statement-indent 0 icol 0)) - ((looking-at fortran-comment-line-start-skip) - (cond ((eq fortran-comment-indent-style 'relative) - (setq icol (+ icol fortran-comment-line-extra-indent))) - ((eq fortran-comment-indent-style 'fixed) - (setq icol (+ fortran-minimum-statement-indent - fortran-comment-line-extra-indent)))) - (setq fortran-minimum-statement-indent 0)) - ((or (looking-at (concat "[ \t]*" - (regexp-quote - fortran-continuation-string))) - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) + ((looking-at fortran-directive-re) + (setq fortran-minimum-statement-indent 0 icol 0)) + ((looking-at fortran-comment-line-start-skip) + (cond ((eq fortran-comment-indent-style 'relative) + (setq icol (+ icol fortran-comment-line-extra-indent))) + ((eq fortran-comment-indent-style 'fixed) + (setq icol (+ fortran-minimum-statement-indent + fortran-comment-line-extra-indent)))) + (setq fortran-minimum-statement-indent 0)) + ((or (looking-at (concat "[ \t]*" + (regexp-quote + fortran-continuation-string))) + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) (skip-chars-forward " \t") ;; Do not introduce extra whitespace into a broken string. (setq icol (if (fortran-is-in-string-p (point)) 6 (+ icol fortran-continuation-indent)))) - (first-statement) - ((and fortran-check-all-num-for-matching-do - (looking-at "[ \t]*[0-9]+") - (fortran-check-for-matching-do)) - (setq icol (- icol fortran-do-indent))) - (t - (skip-chars-forward " \t0-9") - (cond ((looking-at "end[ \t]*\\(if\\|select\\|where\\)\\b") - (setq icol (- icol fortran-if-indent))) - ((looking-at "else\\(if\\)?\\b") - (setq icol (- icol fortran-if-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]+[ \t]*\ +\\(continue\\|end[ \t]*do\\)\\>") + (fortran-check-for-matching-do)) + (setq icol (- icol fortran-do-indent))) + (t + (skip-chars-forward " \t0-9") + (cond ((looking-at "end[ \t]*\\(if\\|select\\|where\\)\\b") + (setq icol (- icol fortran-if-indent))) + ((looking-at "else\\(if\\)?\\b") + (setq icol (- icol fortran-if-indent))) ((looking-at "case[ \t]*\\((.*)\\|default\\>\\)") - (setq icol (- icol fortran-if-indent))) - ((looking-at "\\(otherwise\\|else[ \t]*where\\)\\b") - (setq icol (- icol fortran-if-indent))) - ((and (looking-at "continue\\b") - (fortran-check-for-matching-do)) - (setq icol (- icol fortran-do-indent))) - ((looking-at "end[ \t]*do\\b") - (setq icol (- icol fortran-do-indent))) - ((looking-at "end[ \t]*\ + (setq icol (- icol fortran-if-indent))) + ((looking-at "\\(otherwise\\|else[ \t]*where\\)\\b") + (setq icol (- icol fortran-if-indent))) + ((and (looking-at "continue\\b") + (fortran-check-for-matching-do)) + (setq icol (- icol fortran-do-indent))) + ((looking-at "end[ \t]*do\\b") + (setq icol (- icol fortran-do-indent))) + ((looking-at "end[ \t]*\ \\(structure\\|union\\|map\\|interface\\)\\b[ \t]*[^ \t=(a-z]") - (setq icol (- icol fortran-structure-indent))) - ((and (looking-at fortran-end-prog-re1) - (fortran-check-end-prog-re) - (not (= icol fortran-minimum-statement-indent))) - (message "Warning: `end' not in column %d. Probably\ + (setq icol (- icol fortran-structure-indent))) + ((and (looking-at fortran-end-prog-re1) + (fortran-check-end-prog-re) + (not (= icol fortran-minimum-statement-indent))) + (message "Warning: `end' not in column %d. Probably\ an unclosed block." fortran-minimum-statement-indent)))))) (max fortran-minimum-statement-indent icol))) @@ -1650,16 +1783,16 @@ non-indentation text within the comment." (save-excursion (beginning-of-line) (cond ((looking-at fortran-comment-line-start-skip) - (goto-char (match-end 0)) - (skip-chars-forward - (if (stringp fortran-comment-indent-char) - fortran-comment-indent-char - (char-to-string fortran-comment-indent-char)))) - ((or (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) - (goto-char (match-end 0))) - (t - ;; Move past line number. - (skip-chars-forward "[ \t0-9]"))) + (goto-char (match-end 0)) + (skip-chars-forward + (if (stringp fortran-comment-indent-char) + fortran-comment-indent-char + (char-to-string fortran-comment-indent-char)))) + ((or (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) + (goto-char (match-end 0))) + (t + ;; Move past line number. + (skip-chars-forward "[ \t0-9]"))) ;; Move past whitespace. (skip-chars-forward " \t") (current-column))) @@ -1676,48 +1809,48 @@ notes: 1) A non-zero/non-blank character in column 5 indicates a continuation (save-excursion (beginning-of-line) (if (looking-at fortran-comment-line-start-skip) - (if fortran-comment-indent-style - (let* ((char (if (stringp fortran-comment-indent-char) - (aref fortran-comment-indent-char 0) - fortran-comment-indent-char)) - (chars (string ?\s ?\t char))) - (goto-char (match-end 0)) - (skip-chars-backward chars) - (delete-region (point) (progn (skip-chars-forward chars) - (point))) - (insert-char char (- col (current-column))))) + (if fortran-comment-indent-style + (let* ((char (if (stringp fortran-comment-indent-char) + (aref fortran-comment-indent-char 0) + fortran-comment-indent-char)) + (chars (string ?\s ?\t char))) + (goto-char (match-end 0)) + (skip-chars-backward chars) + (delete-region (point) (progn (skip-chars-forward chars) + (point))) + (insert-char char (- col (current-column))))) (if (looking-at "\t[1-9]") - (if indent-tabs-mode - (goto-char (match-end 0)) - (delete-char 2) - (insert-char ?\s 5) - (insert fortran-continuation-string)) - (if (looking-at " \\{5\\}[^ 0\n]") - (if indent-tabs-mode - (progn (delete-char 6) - (insert ?\t (fortran-numerical-continuation-char) 1)) - (forward-char 6)) - (delete-horizontal-space) - ;; Put line number in columns 0-4, or + (if indent-tabs-mode + (goto-char (match-end 0)) + (delete-char 2) + (insert-char ?\s 5) + (insert fortran-continuation-string)) + (if (looking-at " \\{5\\}[^ 0\n]") + (if indent-tabs-mode + (progn (delete-char 6) + (insert ?\t (fortran-numerical-continuation-char) 1)) + (forward-char 6)) + (delete-horizontal-space) + ;; Put line number in columns 0-4, or ;; continuation character in column 5. - (cond ((eobp)) - ((looking-at (regexp-quote fortran-continuation-string)) - (if indent-tabs-mode - (progn - (indent-to - (if indent-tabs-mode - fortran-minimum-statement-indent-tab - fortran-minimum-statement-indent-fixed)) - (delete-char 1) - (insert-char (fortran-numerical-continuation-char) 1)) - (indent-to 5) - (forward-char 1))) - ((looking-at "[0-9]+") - (let ((extra-space (- 5 (- (match-end 0) (point))))) - (if (< extra-space 0) - (message "Warning: line number exceeds 5-digit limit.") - (indent-to (min fortran-line-number-indent extra-space)))) - (skip-chars-forward "0-9"))))) + (cond ((eobp)) + ((looking-at (regexp-quote fortran-continuation-string)) + (if indent-tabs-mode + (progn + (indent-to + (if indent-tabs-mode + fortran-minimum-statement-indent-tab + fortran-minimum-statement-indent-fixed)) + (delete-char 1) + (insert-char (fortran-numerical-continuation-char) 1)) + (indent-to 5) + (forward-char 1))) + ((looking-at "[0-9]+") + (let ((extra-space (- 5 (- (match-end 0) (point))))) + (if (< extra-space 0) + (message "Warning: line number exceeds 5-digit limit.") + (indent-to (min fortran-line-number-indent extra-space)))) + (skip-chars-forward "0-9"))))) ;; Point is now after any continuation character or line number. ;; Put body of statement where specified. (delete-horizontal-space) @@ -1736,20 +1869,20 @@ Do not call if there is no line number." (beginning-of-line) (skip-chars-forward " \t") (and (<= (current-column) fortran-line-number-indent) - (or (= (current-column) fortran-line-number-indent) - (progn (skip-chars-forward "0-9") - (= (current-column) 5)))))) + (or (= (current-column) fortran-line-number-indent) + (progn (skip-chars-forward "0-9") + (= (current-column) 5)))))) (defun fortran-check-for-matching-do () "When called from a numbered statement, return t if matching DO is found. Otherwise return nil." (let ((case-fold-search t) - charnum) + charnum) (save-excursion (beginning-of-line) (when (looking-at "[ \t]*[0-9]+") (skip-chars-forward " \t") - (skip-chars-forward "0") ; skip past leading zeros + (skip-chars-forward "0") ; skip past leading zeros (setq charnum (buffer-substring (point) (progn (skip-chars-forward "0-9") @@ -1776,19 +1909,19 @@ If ALL is nil, only match comments that start in column > 0." ;; (comment-search-forward (line-end-position) t)) (when (or all comment-start-skip) (let ((pos (point)) - (css (if comment-start-skip - (concat fortran-comment-line-start-skip - "\\|" comment-start-skip) - fortran-comment-line-start-skip))) + (css (if comment-start-skip + (concat fortran-comment-line-start-skip + "\\|" comment-start-skip) + fortran-comment-line-start-skip))) (when (re-search-forward css (line-end-position) t) - (if (and (or all (> (match-beginning 0) (line-beginning-position))) - (or (save-match-data - (not (fortran-is-in-string-p (match-beginning 0)))) - ;; Recurse for rest of line. - (fortran-find-comment-start-skip all))) - (point) - (goto-char pos) - nil))))) + (if (and (or all (> (match-beginning 0) (line-beginning-position))) + (or (save-match-data + (not (fortran-is-in-string-p (match-beginning 0)))) + ;; Recurse for rest of line. + (fortran-find-comment-start-skip all))) + (point) + (goto-char pos) + nil))))) ;; From: ralf@up3aud1.gwdg.de (Ralf Fassel) ;; Test if TAB format continuation lines work. @@ -1797,57 +1930,57 @@ If ALL is nil, only match comments that start in column > 0." (save-excursion (goto-char where) (cond - ((bolp) nil) ; bol is never inside a string - ((save-excursion ; comment lines too - (beginning-of-line) - (looking-at fortran-comment-line-start-skip)) nil) + ((bolp) nil) ; bol is never inside a string + ((save-excursion ; comment lines too + (beginning-of-line) + (looking-at fortran-comment-line-start-skip)) nil) (t (let ((parse-state '(0 nil nil nil nil nil 0)) - (quoted-comment-start (if comment-start - (regexp-quote comment-start))) - (not-done t) - parse-limit end-of-line) - ;; Move to start of current statement. - (fortran-next-statement) - (fortran-previous-statement) - ;; Now parse up to WHERE. - (while not-done - (if (or ;; Skip to next line if: - ;; - comment line? - (looking-at fortran-comment-line-start-skip) - ;; - at end of line? - (eolp) - ;; - not in a string and after comment-start? - (and (not (nth 3 parse-state)) - comment-start - (equal comment-start - (char-to-string (preceding-char))))) - (if (> (forward-line) 0) - (setq not-done nil)) - ;; else: - ;; If we are at beginning of code line, skip any - ;; whitespace, labels and tab continuation markers. - (if (bolp) (skip-chars-forward " \t0-9")) - ;; If we are in column <= 5 now, check for continuation char. - (cond ((= 5 (current-column)) (forward-char 1)) - ((and (< (current-column) 5) - (equal fortran-continuation-string - (char-to-string (following-char))) - (forward-char 1)))) - ;; Find out parse-limit from here. - (setq end-of-line (line-end-position)) - (setq parse-limit (min where end-of-line)) - ;; Parse max up to comment-start, if non-nil and in current line. - (if comment-start - (save-excursion - (if (re-search-forward quoted-comment-start end-of-line t) - (setq parse-limit (min (point) parse-limit))))) - ;; Now parse if still in limits. - (if (< (point) where) - (setq parse-state (parse-partial-sexp - (point) parse-limit nil nil parse-state)) - (setq not-done nil)))) - ;; Result. - (nth 3 parse-state)))))) + (quoted-comment-start (if comment-start + (regexp-quote comment-start))) + (not-done t) + parse-limit end-of-line) + ;; Move to start of current statement. + (fortran-next-statement) + (fortran-previous-statement) + ;; Now parse up to WHERE. + (while not-done + (if (or ;; Skip to next line if: + ;; - comment line? + (looking-at fortran-comment-line-start-skip) + ;; - at end of line? + (eolp) + ;; - not in a string and after comment-start? + (and (not (nth 3 parse-state)) + comment-start + (equal comment-start + (char-to-string (preceding-char))))) + (if (> (forward-line) 0) + (setq not-done nil)) + ;; else: + ;; If we are at beginning of code line, skip any + ;; whitespace, labels and tab continuation markers. + (if (bolp) (skip-chars-forward " \t0-9")) + ;; If we are in column <= 5 now, check for continuation char. + (cond ((= 5 (current-column)) (forward-char 1)) + ((and (< (current-column) 5) + (equal fortran-continuation-string + (char-to-string (following-char))) + (forward-char 1)))) + ;; Find out parse-limit from here. + (setq end-of-line (line-end-position)) + (setq parse-limit (min where end-of-line)) + ;; Parse max up to comment-start, if non-nil and in current line. + (if comment-start + (save-excursion + (if (re-search-forward quoted-comment-start end-of-line t) + (setq parse-limit (min (point) parse-limit))))) + ;; Now parse if still in limits. + (if (< (point) where) + (setq parse-state (parse-partial-sexp + (point) parse-limit nil nil parse-state)) + (setq not-done nil)))) + ;; Result. + (nth 3 parse-state)))))) ;; From old version. (defalias 'fortran-auto-fill-mode 'auto-fill-mode) @@ -1855,17 +1988,17 @@ If ALL is nil, only match comments that start in column > 0." (defun fortran-fill () "Fill the current line at an appropriate point(s)." (let* ((auto-fill-function #'fortran-auto-fill) - (opoint (point)) - (bol (line-beginning-position)) - (eol (line-end-position)) - (bos (min eol (+ bol (fortran-current-line-indentation)))) + (opoint (point)) + (bol (line-beginning-position)) + (eol (line-end-position)) + (bos (min eol (+ bol (fortran-current-line-indentation)))) ;; If in a string at fill-column, break it either before the ;; initial quote, or at fill-col (if string is too long). - (quote - (save-excursion - (goto-char bol) - ;; OK to break quotes on comment lines. - (unless (looking-at fortran-comment-line-start-skip) + (quote + (save-excursion + (goto-char bol) + ;; OK to break quotes on comment lines. + (unless (looking-at fortran-comment-line-start-skip) (let (fcpoint start) (move-to-column fill-column) (when (fortran-is-in-string-p (setq fcpoint (point))) @@ -1884,12 +2017,12 @@ If ALL is nil, only match comments that start in column > 0." (- fill-column 6 fortran-continuation-indent)) fcpoint start)))))) - ;; Decide where to split the line. If a position for a quoted - ;; string was found above then use that, else break the line - ;; before/after the last delimiter. - (fill-point - (or quote - (save-excursion + ;; Decide where to split the line. If a position for a quoted + ;; string was found above then use that, else break the line + ;; before/after the last delimiter. + (fill-point + (or quote + (save-excursion ;; If f-b-b-d is t, have an extra column to play with, ;; since delimiter gets shifted to new line. (move-to-column (if fortran-break-before-delimiters @@ -1913,13 +2046,13 @@ If ALL is nil, only match comments that start in column > 0." (or (looking-at fortran-no-break-re) (forward-char))))) ;; Line indented beyond fill-column? - (when (<= (point) bos) + (when (<= (point) bos) (move-to-column (1+ fill-column)) ;; What is this doing??? (or (re-search-forward "[\t\n,'+-/*)=]" eol t) (goto-char bol))) - (if (bolp) - (re-search-forward "[ \t]" opoint t)) + (if (bolp) + (re-search-forward "[ \t]" opoint t)) (point))))) ;; If we are in an in-line comment, don't break unless the ;; line of code is longer than it should be. Otherwise @@ -1928,20 +2061,20 @@ If ALL is nil, only match comments that start in column > 0." ;; Need to use fortran-find-comment-start-skip to make sure that ;; quoted !'s don't prevent a break. (when (and (save-excursion - (beginning-of-line) - (if (not (fortran-find-comment-start-skip)) + (beginning-of-line) + (if (not (fortran-find-comment-start-skip)) t - (goto-char (match-beginning 0)) - (>= (point) fill-point))) - (save-excursion - (goto-char fill-point) - (not (bolp))) - (> (save-excursion - (goto-char opoint) - (current-column)) - (min (1+ fill-column) - (+ (fortran-calculate-indent) - fortran-continuation-indent)))) + (goto-char (match-beginning 0)) + (>= (point) fill-point))) + (save-excursion + (goto-char fill-point) + (not (bolp))) + (> (save-excursion + (goto-char opoint) + (current-column)) + (min (1+ fill-column) + (+ (fortran-calculate-indent) + fortran-continuation-indent)))) (goto-char fill-point) (fortran-break-line) (end-of-line)))) @@ -1949,27 +2082,27 @@ If ALL is nil, only match comments that start in column > 0." (defun fortran-break-line () "Call `fortran-split-line'. Joins continuation lines first, then refills." (let ((bol (line-beginning-position)) - (comment-string - (save-excursion - (if (fortran-find-comment-start-skip) - (delete-and-extract-region - (match-beginning 0) (line-end-position)))))) + (comment-string + (save-excursion + (if (fortran-find-comment-start-skip) + (delete-and-extract-region + (match-beginning 0) (line-end-position)))))) ;; Forward line 1 really needs to go to next non white line. (if (save-excursion (forward-line) - (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) - (progn - (end-of-line) - (delete-region (point) (match-end 0)) - (delete-horizontal-space) - (fortran-fill)) + (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")) + (progn + (end-of-line) + (delete-region (point) (match-end 0)) + (delete-horizontal-space) + (fortran-fill)) (fortran-split-line)) (if comment-string - (save-excursion - (goto-char bol) - (end-of-line) - (delete-horizontal-space) - (indent-to (fortran-comment-indent)) - (insert comment-string))))) + (save-excursion + (goto-char bol) + (end-of-line) + (delete-horizontal-space) + (indent-to (fortran-comment-indent)) + (insert comment-string))))) (defun fortran-analyze-file-format () "Return nil if fixed format is used, t if TAB formatting is used. @@ -1979,12 +2112,12 @@ before the end or in the first `fortran-analyze-depth' lines." (save-excursion (goto-char (point-min)) (while (not (or - (eobp) - (eq (char-after) ?\t) - (looking-at " \\{6\\}") - (> i fortran-analyze-depth))) - (forward-line) - (setq i (1+ i))) + (eobp) + (eq (char-after) ?\t) + (looking-at " \\{6\\}") + (> i fortran-analyze-depth))) + (forward-line) + (setq i (1+ i))) (cond ((eq (char-after) ?\t) t) ((looking-at " \\{6\\}") nil) @@ -2026,29 +2159,28 @@ Always returns non-nil (to prevent `fill-paragraph' being called)." (fortran-indent-line))) (defun fortran-strip-sequence-nos (&optional do-space) - "Delete all text in column 72 and up (assumed to be sequence numbers). -Normally also deletes trailing whitespace after stripping such text. -Supplying prefix arg DO-SPACE prevents stripping the whitespace." + "Delete all text in column `fortran-line-length' (default 72) and up. +This is assumed to be sequence numbers. Normally also deletes +trailing whitespace after stripping such text. Supplying prefix +arg DO-SPACE prevents stripping the whitespace." (interactive "*p") (save-excursion (goto-char (point-min)) - (while (re-search-forward "^.\\{72\\}\\(.*\\)" nil t) + (while (re-search-forward (format "^.\\{%d\\}\\(.*\\)" fortran-line-length) + nil t) (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)