Some fixes to follow coding conventions.
[bpt/emacs.git] / lisp / progmodes / fortran.el
CommitLineData
e5167999
ER
1;;; fortran.el --- Fortran mode for GNU Emacs
2
315aa1de
DL
3;; Copyright (c) 1986, 93, 94, 95, 97, 98, 99, 2000
4;; Free Software Foundation, Inc.
9750e079 5
e5167999 6;; Author: Michael D. Prange <prange@erl.mit.edu>
7977773d 7;; Maintainer: Dave Love <fx@gnu.org>
fd7fa35a 8;; Keywords: languages
1a06eabd 9
e5167999
ER
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
e5167999
ER
26
27;;; Commentary:
28
7977773d
DL
29;; This mode is documented in the Emacs manual.
30;;
31;; Note that it is for editing Fortran77 or Fortran90 fixed source
45cf60ae 32;; form. For editing Fortran 90 free format source, use `f90-mode'
7977773d
DL
33;; (f90.el).
34
35;;; History:
36
37;; Fortran mode was upgraded by Stephen A. Wood (saw@cebaf.gov).
23029d77
JB
38
39;; We acknowledge many contributions and valuable suggestions by
b8cbdf43 40;; Lawrence R. Dodd, Ralf Fassel, Ralph Finch, Stephen Gildea,
60db3594 41;; Dr. Anil Gokhale, Ulrich Mueller, Mark Neale, Eric Prestemon,
b8cbdf43
RS
42;; Gary Sabot and Richard Stallman.
43
e5167999
ER
44;;; Code:
45
45cf60ae 46;; Todo:
1eb6bf70 47
315aa1de 48;; * Tidy it all up (more)!
1eb6bf70
DL
49;; * Implement insertion and removal of statement continuations in
50;; mixed f77/f90 style, with the first `&' past column 72 and the
51;; second in column 6.
45cf60ae 52;; * Support any other extensions to f77 grokked by GNU Fortran.
1eb6bf70 53
fcad5199
RS
54(defgroup fortran nil
55 "Fortran mode for Emacs"
e04196d3 56 :link '(custom-manual "(emacs)Fortran")
fcad5199
RS
57 :group 'languages)
58
59(defgroup fortran-indent nil
60 "Indentation variables in Fortran mode"
61 :prefix "fortran-"
62 :group 'fortran)
63
64(defgroup fortran-comment nil
65 "Comment-handling variables in Fortran mode"
66 :prefix "fortran-"
67 :group 'fortran)
68
69
3dd63760 70;;;###autoload
fcad5199 71(defcustom fortran-tab-mode-default nil
e80f2147
RS
72 "*Default tabbing/carriage control style for empty files in Fortran mode.
73A value of t specifies tab-digit style of continuation control.
74A value of nil specifies that continuation lines are marked
fcad5199
RS
75with a character in column 6."
76 :type 'boolean
77 :group 'fortran-indent)
e80f2147 78
e80f2147 79;; Buffer local, used to display mode line.
fcad5199
RS
80(defcustom fortran-tab-mode-string nil
81 "String to appear in mode line when TAB format mode is on."
82 :type '(choice (const nil) string)
83 :group 'fortran-indent)
1eb6bf70 84(make-variable-buffer-local 'fortran-tab-mode-string)
fcad5199
RS
85
86(defcustom fortran-do-indent 3
87 "*Extra indentation applied to DO blocks."
88 :type 'integer
89 :group 'fortran-indent)
90
91(defcustom fortran-if-indent 3
92 "*Extra indentation applied to IF blocks."
93 :type 'integer
94 :group 'fortran-indent)
95
96(defcustom fortran-structure-indent 3
97 "*Extra indentation applied to STRUCTURE, UNION, MAP and INTERFACE blocks."
98 :type 'integer
99 :group 'fortran-indent)
100
101(defcustom fortran-continuation-indent 5
102 "*Extra indentation applied to Fortran continuation lines."
103 :type 'integer
104 :group 'fortran-indent)
105
106(defcustom fortran-comment-indent-style 'fixed
7977773d
DL
107 "*How to indent comments.
108nil forces comment lines not to be touched,
23029d77
JB
109'fixed makes fixed comment indentation to `fortran-comment-line-extra-indent'
110columns beyond `fortran-minimum-statement-indent-fixed' (for
111`indent-tabs-mode' of nil) or `fortran-minimum-statement-indent-tab' (for
112`indent-tabs-mode' of t), and 'relative indents to current
fcad5199 113Fortran indentation plus `fortran-comment-line-extra-indent'."
1eb6bf70 114 :type '(radio (const :tag "Untouched" nil) (const fixed) (const relative))
fcad5199
RS
115 :group 'fortran-indent)
116
117(defcustom fortran-comment-line-extra-indent 0
118 "*Amount of extra indentation for text within full-line comments."
119 :type 'integer
120 :group 'fortran-indent
121 :group 'fortran-comment)
122
68ca306c
DL
123(defcustom fortran-comment-line-start "C"
124 "*Delimiter inserted to start new full-line comment.
125You might want to change this to \"*\", for instance."
7dae727d 126 :version "21.1"
68ca306c 127 :type 'string
fcad5199
RS
128 :group 'fortran-comment)
129
68ca306c
DL
130;; This used to match preprocessor lines too, but that messes up
131;; filling and doesn't seem to be necessary.
132(defcustom fortran-comment-line-start-skip
8b31236d 133 "^[CcDd*!]\\(\\([^ \t\n]\\)\\2+\\)?[ \t]*"
fcad5199 134 "*Regexp to match the start of a full-line comment."
7dae727d 135 :version "21.1"
68ca306c 136 :type 'regexp
fcad5199
RS
137 :group 'fortran-comment)
138
139(defcustom fortran-minimum-statement-indent-fixed 6
140 "*Minimum statement indentation for fixed format continuation style."
141 :type 'integer
142 :group 'fortran-indent)
143
144(defcustom fortran-minimum-statement-indent-tab (max tab-width 6)
145 "*Minimum statement indentation for TAB format continuation style."
146 :type 'integer
147 :group 'fortran-indent)
3dd63760
JB
148
149;; Note that this is documented in the v18 manuals as being a string
150;; of length one rather than a single character.
151;; The code in this file accepts either format for compatibility.
fcad5199 152(defcustom fortran-comment-indent-char " "
3dd63760 153 "*Single-character string inserted for Fortran comment indentation.
fcad5199
RS
154Normally a space."
155 :type 'string
156 :group 'fortran-comment)
3dd63760 157
fcad5199 158(defcustom fortran-line-number-indent 1
3dd63760 159 "*Maximum indentation for Fortran line numbers.
fcad5199
RS
1605 means right-justify them within their five-column field."
161 :type 'integer
162 :group 'fortran-indent)
3dd63760 163
fcad5199
RS
164(defcustom fortran-check-all-num-for-matching-do nil
165 "*Non-nil causes all numbered lines to be treated as possible DO loop ends."
166 :type 'boolean
167 :group 'fortran)
3dd63760 168
fcad5199 169(defcustom fortran-blink-matching-if nil
c5af0a18 170 "*Non-nil causes \\[fortran-indent-line] on ENDIF statement to blink on matching IF.
fcad5199
RS
171Also, from an ENDDO statement blink on matching DO [WHILE] statement."
172 :type 'boolean
173 :group 'fortran)
3dd63760 174
fcad5199 175(defcustom fortran-continuation-string "$"
b8cbdf43 176 "*Single-character string used for Fortran continuation lines.
3dd63760
JB
177In fixed format continuation style, this character is inserted in
178column 6 by \\[fortran-split-line] to begin a continuation line.
179Also, if \\[fortran-indent-line] finds this at the beginning of a line, it will
180convert the line into a continuation line of the appropriate style.
fcad5199
RS
181Normally $."
182 :type 'string
183 :group 'fortran)
3dd63760 184
fcad5199 185(defcustom fortran-comment-region "c$$$"
1eb6bf70
DL
186 "*String inserted by \\[fortran-comment-region] at start of each \
187line in region."
fcad5199
RS
188 :type 'string
189 :group 'fortran-comment)
3dd63760 190
fcad5199 191(defcustom fortran-electric-line-number t
1eb6bf70
DL
192 "*Non-nil causes line number digits to be moved to the correct \
193column as typed."
fcad5199
RS
194 :type 'boolean
195 :group 'fortran)
3dd63760 196
23029d77
JB
197(defvar fortran-column-ruler-fixed
198 "0 4 6 10 20 30 40 5\
7977773d 1990 60 70\n\
f022dd89
SM
200\[ ]|{ | | | | | | | | \
201\| | | | |}\n"
dc6579ac 202 "String displayed above current line by \\[fortran-column-ruler].
5a8d870b 203This variable used in fixed format mode.")
23029d77
JB
204
205(defvar fortran-column-ruler-tab
206 "0 810 20 30 40 5\
7977773d 2070 60 70\n\
f022dd89
SM
208\[ ]| { | | | | | | | | \
209\| | | | |}\n"
dc6579ac 210 "String displayed above current line by \\[fortran-column-ruler].
5a8d870b 211This variable used in TAB format mode.")
3dd63760 212
3dd63760 213(defvar fortran-analyze-depth 100
1eb6bf70
DL
214 "Number of lines to scan to determine whether to use fixed or TAB \
215format style.")
3dd63760 216
fcad5199 217(defcustom fortran-break-before-delimiters t
1eb6bf70 218 "*Non-nil causes filling to break lines before delimiters."
fcad5199
RS
219 :type 'boolean
220 :group 'fortran)
b8cbdf43 221
315aa1de
DL
222(defvar fortran-mode-syntax-table
223 (let ((table (make-syntax-table)))
224 ;; We might like `;' to be punctuation (g77 multi-statement
225 ;; lines), but that screws abbrevs.
226 (modify-syntax-entry ?\; "w" table)
227 (modify-syntax-entry ?\r " " table)
228 (modify-syntax-entry ?+ "." table)
229 (modify-syntax-entry ?- "." table)
230 (modify-syntax-entry ?= "." table)
231 (modify-syntax-entry ?* "." table)
232 (modify-syntax-entry ?/ "." table)
233 (modify-syntax-entry ?\' "\"" table)
234 (modify-syntax-entry ?\" "\"" table)
235 (modify-syntax-entry ?\\ "\\" table)
236 ;; This might be better as punctuation, as for C, but this way you
237 ;; can treat floating-point numbers as symbols.
238 (modify-syntax-entry ?. "_" table) ; e.g. `a.ne.b'
239 (modify-syntax-entry ?_ "_" table)
240 (modify-syntax-entry ?$ "_" table) ; esp. VMSisms
241 (modify-syntax-entry ?\! "<" table)
242 (modify-syntax-entry ?\n ">" table)
243 table)
244 "Syntax table in use in Fortran mode buffers.")
3dd63760 245
f687a879
DL
246;; Comments are real pain in Fortran because there is no way to
247;; represent the standard comment syntax in an Emacs syntax table.
248;; (We can do so for F90-style). Therefore an unmatched quote in a
249;; standard comment will throw fontification off on the wrong track.
250;; So we do syntactic fontification with regexps.
563f09b5 251
315aa1de 252(defvar fortran-font-lock-keywords-1 nil
f3d4eb7b 253 "Subdued level highlighting for Fortran mode.")
563f09b5 254
315aa1de 255(defvar fortran-font-lock-keywords-2 nil
f408b027
SM
256 "Medium level highlighting for Fortran mode.")
257
315aa1de 258(defvar fortran-font-lock-keywords-3 nil
f408b027
SM
259 "Gaudy level highlighting for Fortran mode.")
260
315aa1de 261(defvar fortran-font-lock-syntactic-keywords nil
f687a879
DL
262 "`font-lock-syntactic-keywords' for Fortran.
263These get fixed-format comments fontified.")
0f1057e9 264
68ca306c 265(let ((comment-chars "cd\\*") ; `d' for `debugging' comments
37c3edc4 266 (fortran-type-types
6fd849f6
DL
267 (eval-when-compile
268 (let ((re (regexp-opt
269 (let ((simple-types
270 '("character" "byte" "integer" "logical"
271 "none" "real" "complex"
272 "double precision" "double complex"))
273 (structured-types '("structure" "union" "map"))
274 (other-types '("record" "dimension"
275 "parameter" "common" "save"
276 "external" "intrinsic" "data"
277 "equivalence")))
278 (append
279 (mapcar (lambda (x) (concat "implicit " x))
280 simple-types)
281 simple-types
282 (mapcar (lambda (x) (concat "end " x))
283 structured-types)
284 structured-types
285 other-types)))))
315aa1de
DL
286 ;; In the optimized regexp above, replace spaces by a
287 ;; regexp for optional whitespace; regexp-opt would have
288 ;; escaped that.
6fd849f6 289 (mapconcat #'identity (split-string re) "[ \t]*"))))
37c3edc4
DL
290 (fortran-keywords
291 (eval-when-compile
1eb6bf70
DL
292 (regexp-opt '("continue" "format" "end" "enddo" "if" "then"
293 "else" "endif" "elseif" "while" "inquire" "stop"
294 "return" "include" "open" "close" "read" "write"
46d4d7bf 295 "format" "print" "select" "case" "cycle" "exit"))))
37c3edc4
DL
296 (fortran-logicals
297 (eval-when-compile
1eb6bf70 298 (regexp-opt '("and" "or" "not" "lt" "le" "eq" "ge" "gt" "ne"
37c3edc4 299 "true" "false")))))
f687a879
DL
300 (setq fortran-font-lock-syntactic-keywords
301 ;; Fixed format comments. (!-style handled normally.)
302 (list
303 (list (concat "^[" comment-chars "]") 0 '(11))
315aa1de 304 (list (concat "^[^" comment-chars "\t\n]" ".\\{71\\}"
f687a879
DL
305 "\\([^\n]+\\)")
306 1 '(11))))
37c3edc4
DL
307 (setq fortran-font-lock-keywords-1
308 (list
37c3edc4
DL
309 ;; Program, subroutine and function declarations, plus calls.
310 (list (concat "\\<\\(block[ \t]*data\\|call\\|entry\\|function\\|"
311 "program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?")
312 '(1 font-lock-keyword-face)
313 '(2 font-lock-function-name-face nil t))))
37c3edc4
DL
314 (setq fortran-font-lock-keywords-2
315 (append fortran-font-lock-keywords-1
316 (list
37c3edc4 317 ;; Fontify all type specifiers (must be first; see below).
45cf60ae 318 (cons (concat "\\<\\(" fortran-type-types "\\)\\>")
37c3edc4 319 'font-lock-type-face)
37c3edc4
DL
320 ;; Fontify all builtin keywords (except logical, do
321 ;; and goto; see below).
322 (concat "\\<\\(" fortran-keywords "\\)\\>")
37c3edc4
DL
323 ;; Fontify all builtin operators.
324 (concat "\\.\\(" fortran-logicals "\\)\\.")
37c3edc4
DL
325 ;; Fontify do/goto keywords and targets, and goto tags.
326 (list "\\<\\(do\\|go *to\\)\\>[ \t]*\\([0-9]+\\)?"
327 '(1 font-lock-keyword-face)
328 '(2 font-lock-constant-face nil t))
329 (cons "^ *\\([0-9]+\\)" 'font-lock-constant-face))))
37c3edc4
DL
330 (setq fortran-font-lock-keywords-3
331 (append
37c3edc4
DL
332 ;; The list `fortran-font-lock-keywords-1'.
333 fortran-font-lock-keywords-1
37c3edc4
DL
334 ;; Fontify all type specifiers plus their declared items.
335 (list
336 (list (concat "\\<\\(" fortran-type-types "\\)\\>[ \t(/]*\\(*\\)?")
337 ;; Fontify the type specifier.
338 '(1 font-lock-type-face)
339 ;; Fontify each declaration item (or just the /.../ block name).
6fd849f6 340 `(font-lock-match-c-style-declaration-item-and-skip-to-next
37c3edc4 341 ;; Start after any *(...) expression.
6fd849f6
DL
342 (condition-case nil
343 (and (and (match-beginning ,(+ 2 (regexp-opt-depth
344 fortran-type-types)))
345 (forward-sexp))
346 (forward-sexp))
347 (error nil))
37c3edc4
DL
348 ;; No need to clean up.
349 nil
350 ;; Fontify as a variable name, functions are
351 ;; fontified elsewhere.
352 (1 font-lock-variable-name-face nil t))))
37c3edc4
DL
353 ;; Things extra to `fortran-font-lock-keywords-3'
354 ;; (must be done first).
355 (list
37c3edc4
DL
356 ;; Fontify goto-like `err=label'/`end=label' in read/write
357 ;; statements.
358 '(", *\\(e\\(nd\\|rr\\)\\)\\> *\\(= *\\([0-9]+\\)\\)?"
359 (1 font-lock-keyword-face) (4 font-lock-constant-face nil t))
37c3edc4
DL
360 ;; Highlight standard continuation character and in a
361 ;; TAB-formatted line.
362 '("^ \\([^ 0]\\)" 1 font-lock-string-face)
363 '("^\t\\([1-9]\\)" 1 font-lock-string-face))
68ca306c
DL
364 (list
365 ;; cpp stuff (ugh)
366 '("^# *[a-z]+" . font-lock-keyword-face))
37c3edc4
DL
367 ;; The list `fortran-font-lock-keywords-2' less that for types
368 ;; (see above).
369 (cdr (nthcdr (length fortran-font-lock-keywords-1)
370 fortran-font-lock-keywords-2)))))
f022dd89 371
f3d4eb7b
SM
372(defvar fortran-font-lock-keywords fortran-font-lock-keywords-1
373 "Default expressions to highlight in Fortran mode.")
60db3594 374\f
9645c179 375(defvar fortran-imenu-generic-expression
240e9cda
DL
376 ;; These patterns could be confused by sequence nos. in cols 72+ and
377 ;; don't allow continuations everywhere.
7977773d
DL
378 (list
379 (list
380 nil
a8189dfe
DL
381 ;; [This will be fooled by `end function' allowed by G77. Also,
382 ;; it assumes sensible whitespace is employed.]
383 (concat
384 ;; leading whitespace:
385 "^\\s-+\\("
386 ;; function declaration with optional type, e.g. `real',
387 ;; `real*4', character(*), `double precision':
388 "\\(\\sw\\|\\s-\\|[*()+]\\)*"
389 "\\<function\\|subroutine\\|entry\\|block\\s-*data\\|program\\)"
390 ;; Possible statement continuation:
391 "[ \t" fortran-continuation-string "]+"
392 ;; Variable to index:
393 "\\(\\sw+\\)")
7977773d
DL
394 3)
395 ;; Un-named block data
396 (list nil "^\\s-+\\(block\\s-*data\\)\\s-*$" 1))
45cf60ae 397 "Imenu generic expression for `imenu-default-create-index-function'.")
f408b027 398
7a7db8e5 399(defvar fortran-mode-map
315aa1de
DL
400 (let ((map (make-sparse-keymap)))
401 (define-key map ";" 'fortran-abbrev-start)
402 (define-key map "\C-c;" 'fortran-comment-region)
403 (define-key map "\M-;" 'fortran-indent-comment)
404 (define-key map "\M-\n" 'fortran-split-line)
405 (define-key map "\M-\C-q" 'fortran-indent-subprogram)
406 (define-key map "\C-c\C-w" 'fortran-window-create-momentarily)
407 (define-key map "\C-c\C-r" 'fortran-column-ruler)
408 (define-key map "\C-c\C-p" 'fortran-previous-statement)
409 (define-key map "\C-c\C-n" 'fortran-next-statement)
410 (define-key map "\C-c\C-d" 'fortran-join-line) ; like f90
411 (define-key map "\M-^" 'fortran-join-line) ; subvert delete-indentation
412 (define-key map "0" 'fortran-electric-line-number)
413 (define-key map "1" 'fortran-electric-line-number)
414 (define-key map "2" 'fortran-electric-line-number)
415 (define-key map "3" 'fortran-electric-line-number)
416 (define-key map "4" 'fortran-electric-line-number)
417 (define-key map "5" 'fortran-electric-line-number)
418 (define-key map "6" 'fortran-electric-line-number)
419 (define-key map "7" 'fortran-electric-line-number)
420 (define-key map "8" 'fortran-electric-line-number)
421 (define-key map "9" 'fortran-electric-line-number)
7a7db8e5
DL
422
423 ;; Menu
562e00da
DL
424 (easy-menu-define
425 fortran-mode-menu map ""
426 `("Fortran"
427 ["Manual" (info "(emacs)Fortran")]
428 ("Customization"
429 ,(custom-menu-create 'fortran)
430 ["Set" Custom-set t]
431 ["Save" Custom-save t]
432 ["Reset to Current" Custom-reset-current t]
433 ["Reset to Saved" Custom-reset-saved t]
434 ["Reset to Standard Settings" Custom-reset-standard t])
435 "----"
436 ["Toggle Auto-fill" auto-fill-mode :style toggle
437 :selected auto-fill-function]
438 ["Toggle abbrev-mode" abbrev-mode :style toggle :selected abbrev-mode]
439 "----"
440 ["Comment-out Region" fortran-comment-region mark-active]
441 ["Uncomment-out region"
442 (fortran-comment-region (region-beginning) (region-end) 1)
443 mark-active]
444 ["Indent Region" indent-region mark-active]
445 ["Indent Subprogram" fortran-indent-subprogram t]
446 "----"
447 ["Beginning of Subprogram" fortran-beginning-of-subprogram t]
448 ["End of Subprogram" fortran-end-of-subprogram t]
449 ("Mark"
450 ["Subprogram" mark-defun t]
451 ["IF Block" fortran-mark-if t]
452 ["DO Block" fortran-mark-do t])
453 ["Narrow to Subprogram" narrow-to-defun t]
454 ["Widen" widen t]
455 "----"
456 ["Temporary column ruler" fortran-column-ruler t]
457 ["72-column window" fortran-window-create t]
458 ["Full Width Window"
459 (enlarge-window-horizontally (- (frame-width) (window-width)))
460 (< (window-width) (frame-width))]
461 ["Momentary 72-column window" fortran-window-create-momentarily t]
462 "----"
463 ["Break Line at Point" fortran-split-line t]
464 ["Join Line" fortran-join-line t]
465 ["Fill Statement/Comment" fill-paragraph t]
466 "----"
467 ["Add imenu menu"
468 imenu-add-menubar-index (not (and (boundp 'imenu--index-alist)
469 imenu--index-alist))]))
315aa1de 470 map)
b8cbdf43 471 "Keymap used in Fortran mode.")
3dd63760 472\f
7a7db8e5 473(defvar fortran-mode-abbrev-table
3dd63760
JB
474 (let ((ac abbrevs-changed))
475 (define-abbrev-table 'fortran-mode-abbrev-table ())
476 (define-abbrev fortran-mode-abbrev-table ";au" "automatic" nil)
477 (define-abbrev fortran-mode-abbrev-table ";b" "byte" nil)
b8cbdf43 478 (define-abbrev fortran-mode-abbrev-table ";bd" "block data" nil)
3dd63760
JB
479 (define-abbrev fortran-mode-abbrev-table ";ch" "character" nil)
480 (define-abbrev fortran-mode-abbrev-table ";cl" "close" nil)
481 (define-abbrev fortran-mode-abbrev-table ";c" "continue" nil)
482 (define-abbrev fortran-mode-abbrev-table ";cm" "common" nil)
483 (define-abbrev fortran-mode-abbrev-table ";cx" "complex" nil)
484 (define-abbrev fortran-mode-abbrev-table ";df" "define" nil)
485 (define-abbrev fortran-mode-abbrev-table ";di" "dimension" nil)
486 (define-abbrev fortran-mode-abbrev-table ";do" "double" nil)
487 (define-abbrev fortran-mode-abbrev-table ";dc" "double complex" nil)
488 (define-abbrev fortran-mode-abbrev-table ";dp" "double precision" nil)
489 (define-abbrev fortran-mode-abbrev-table ";dw" "do while" nil)
490 (define-abbrev fortran-mode-abbrev-table ";e" "else" nil)
491 (define-abbrev fortran-mode-abbrev-table ";ed" "enddo" nil)
492 (define-abbrev fortran-mode-abbrev-table ";el" "elseif" nil)
493 (define-abbrev fortran-mode-abbrev-table ";en" "endif" nil)
494 (define-abbrev fortran-mode-abbrev-table ";eq" "equivalence" nil)
b8cbdf43 495 (define-abbrev fortran-mode-abbrev-table ";ew" "endwhere" nil)
3dd63760
JB
496 (define-abbrev fortran-mode-abbrev-table ";ex" "external" nil)
497 (define-abbrev fortran-mode-abbrev-table ";ey" "entry" nil)
498 (define-abbrev fortran-mode-abbrev-table ";f" "format" nil)
499 (define-abbrev fortran-mode-abbrev-table ";fa" ".false." nil)
500 (define-abbrev fortran-mode-abbrev-table ";fu" "function" nil)
501 (define-abbrev fortran-mode-abbrev-table ";g" "goto" nil)
502 (define-abbrev fortran-mode-abbrev-table ";im" "implicit" nil)
503 (define-abbrev fortran-mode-abbrev-table ";ib" "implicit byte" nil)
504 (define-abbrev fortran-mode-abbrev-table ";ic" "implicit complex" nil)
505 (define-abbrev fortran-mode-abbrev-table ";ich" "implicit character" nil)
506 (define-abbrev fortran-mode-abbrev-table ";ii" "implicit integer" nil)
507 (define-abbrev fortran-mode-abbrev-table ";il" "implicit logical" nil)
508 (define-abbrev fortran-mode-abbrev-table ";ir" "implicit real" nil)
509 (define-abbrev fortran-mode-abbrev-table ";inc" "include" nil)
510 (define-abbrev fortran-mode-abbrev-table ";in" "integer" nil)
511 (define-abbrev fortran-mode-abbrev-table ";intr" "intrinsic" nil)
512 (define-abbrev fortran-mode-abbrev-table ";l" "logical" nil)
513 (define-abbrev fortran-mode-abbrev-table ";n" "namelist" nil)
514 (define-abbrev fortran-mode-abbrev-table ";o" "open" nil) ; was ;op
515 (define-abbrev fortran-mode-abbrev-table ";pa" "parameter" nil)
516 (define-abbrev fortran-mode-abbrev-table ";pr" "program" nil)
517 (define-abbrev fortran-mode-abbrev-table ";ps" "pause" nil)
518 (define-abbrev fortran-mode-abbrev-table ";p" "print" nil)
519 (define-abbrev fortran-mode-abbrev-table ";rc" "record" nil)
520 (define-abbrev fortran-mode-abbrev-table ";re" "real" nil)
521 (define-abbrev fortran-mode-abbrev-table ";r" "read" nil)
522 (define-abbrev fortran-mode-abbrev-table ";rt" "return" nil)
523 (define-abbrev fortran-mode-abbrev-table ";rw" "rewind" nil)
524 (define-abbrev fortran-mode-abbrev-table ";s" "stop" nil)
525 (define-abbrev fortran-mode-abbrev-table ";sa" "save" nil)
526 (define-abbrev fortran-mode-abbrev-table ";st" "structure" nil)
527 (define-abbrev fortran-mode-abbrev-table ";sc" "static" nil)
528 (define-abbrev fortran-mode-abbrev-table ";su" "subroutine" nil)
529 (define-abbrev fortran-mode-abbrev-table ";tr" ".true." nil)
530 (define-abbrev fortran-mode-abbrev-table ";ty" "type" nil)
531 (define-abbrev fortran-mode-abbrev-table ";vo" "volatile" nil)
532 (define-abbrev fortran-mode-abbrev-table ";w" "write" nil)
b8cbdf43 533 (define-abbrev fortran-mode-abbrev-table ";wh" "where" nil)
7a7db8e5
DL
534 (setq abbrevs-changed ac)
535 fortran-mode-abbrev-table))
3dd63760 536\f
1eb6bf70
DL
537(eval-when-compile ; silence compiler
538 (defvar imenu-case-fold-search)
539 (defvar imenu-syntax-alist))
7977773d 540
7dae727d
DL
541(defcustom fortran-mode-hook nil
542 "Hook run by Fortran mode."
543 :type 'hook
544 :group 'fortran)
545
3dd63760
JB
546;;;###autoload
547(defun fortran-mode ()
b8cbdf43 548 "Major mode for editing Fortran code.
60db3594 549\\[fortran-indent-line] indents the current Fortran line correctly.
b8cbdf43 550DO statements must not share a common CONTINUE.
3dd63760 551
1eb6bf70
DL
552Type ;? or ;\\[help-command] to display a list of built-in abbrevs for
553Fortran keywords.
3dd63760
JB
554
555Key definitions:
556\\{fortran-mode-map}
557
558Variables controlling indentation style and extra features:
559
45cf60ae 560 `comment-start'
315aa1de
DL
561 If you want to use comments starting with `!',
562 set this to the string \"!\".
45cf60ae 563 `fortran-do-indent'
3dd63760 564 Extra indentation within do blocks. (default 3)
45cf60ae 565 `fortran-if-indent'
3dd63760 566 Extra indentation within if blocks. (default 3)
45cf60ae 567 `fortran-structure-indent'
5a8d870b
RS
568 Extra indentation within structure, union, map and interface blocks.
569 (default 3)
45cf60ae 570 `fortran-continuation-indent'
b8cbdf43 571 Extra indentation applied to continuation statements. (default 5)
45cf60ae
DL
572 `fortran-comment-line-extra-indent'
573 Amount of extra indentation for text within full-line comments. (default 0)
574 `fortran-comment-indent-style'
3dd63760 575 nil means don't change indentation of text in full-line comments,
23029d77
JB
576 fixed means indent that text at `fortran-comment-line-extra-indent' beyond
577 the value of `fortran-minimum-statement-indent-fixed' (for fixed
578 format continuation style) or `fortran-minimum-statement-indent-tab'
579 (for TAB format continuation style).
580 relative means indent at `fortran-comment-line-extra-indent' beyond the
3dd63760
JB
581 indentation for a line of code.
582 (default 'fixed)
45cf60ae 583 `fortran-comment-indent-char'
b8cbdf43 584 Single-character string to be inserted instead of space for
3dd63760 585 full-line comment indentation. (default \" \")
45cf60ae
DL
586 `fortran-minimum-statement-indent-fixed'
587 Minimum indentation for Fortran statements in fixed format mode. (def.6)
588 `fortran-minimum-statement-indent-tab'
589 Minimum indentation for Fortran statements in TAB format mode. (default 9)
590 `fortran-line-number-indent'
3dd63760
JB
591 Maximum indentation for line numbers. A line number will get
592 less than this much indentation if necessary to avoid reaching
593 column 5. (default 1)
45cf60ae 594 `fortran-check-all-num-for-matching-do'
b8cbdf43 595 Non-nil causes all numbered lines to be treated as possible \"continue\"
3dd63760 596 statements. (default nil)
45cf60ae 597 `fortran-blink-matching-if'
c5af0a18
RS
598 Non-nil causes \\[fortran-indent-line] on an ENDIF statement to blink on
599 matching IF. Also, from an ENDDO statement, blink on matching DO [WHILE]
600 statement. (default nil)
45cf60ae 601 `fortran-continuation-string'
3dd63760
JB
602 Single-character string to be inserted in column 5 of a continuation
603 line. (default \"$\")
45cf60ae 604 `fortran-comment-region'
60db3594 605 String inserted by \\[fortran-comment-region] at start of each line in
3dd63760 606 region. (default \"c$$$\")
45cf60ae 607 `fortran-electric-line-number'
60db3594 608 Non-nil causes line number digits to be moved to the correct column
3dd63760 609 as typed. (default t)
45cf60ae 610 `fortran-break-before-delimiters'
7dae727d 611 Non-nil causes lines to be broken before delimiters.
b8cbdf43 612 (default t)
3dd63760 613
b8cbdf43 614Turning on Fortran mode calls the value of the variable `fortran-mode-hook'
3dd63760
JB
615with no args, if that value is non-nil."
616 (interactive)
617 (kill-all-local-variables)
3dd63760
JB
618 (setq local-abbrev-table fortran-mode-abbrev-table)
619 (set-syntax-table fortran-mode-syntax-table)
f3d4eb7b 620 ;; Font Lock mode support.
f408b027
SM
621 (make-local-variable 'font-lock-defaults)
622 (setq font-lock-defaults '((fortran-font-lock-keywords
623 fortran-font-lock-keywords-1
624 fortran-font-lock-keywords-2
625 fortran-font-lock-keywords-3)
f687a879 626 nil t ((?/ . "$/") ("_$" . "w"))
7dae727d 627 fortran-beginning-of-subprogram))
f687a879
DL
628 (set (make-local-variable 'font-lock-syntactic-keywords)
629 fortran-font-lock-syntactic-keywords)
b8cbdf43
RS
630 (make-local-variable 'fortran-break-before-delimiters)
631 (setq fortran-break-before-delimiters t)
3dd63760
JB
632 (make-local-variable 'indent-line-function)
633 (setq indent-line-function 'fortran-indent-line)
e41b2db1 634 (make-local-variable 'comment-indent-function)
315aa1de 635 (setq comment-indent-function 'fortran-comment-indent)
3dd63760
JB
636 (make-local-variable 'comment-start-skip)
637 (setq comment-start-skip "![ \t]*")
638 (make-local-variable 'comment-start)
8b31236d 639 (setq comment-start fortran-comment-line-start)
3dd63760
JB
640 (make-local-variable 'require-final-newline)
641 (setq require-final-newline t)
642 (make-local-variable 'abbrev-all-caps)
643 (setq abbrev-all-caps t)
644 (make-local-variable 'indent-tabs-mode)
645 (setq indent-tabs-mode nil)
23029d77 646;;;(setq abbrev-mode t) ; ?? (abbrev-mode 1) instead??
1eb6bf70 647 (set (make-local-variable 'fill-column) 72)
3dd63760
JB
648 (use-local-map fortran-mode-map)
649 (setq mode-name "Fortran")
650 (setq major-mode 'fortran-mode)
23029d77
JB
651 (make-local-variable 'fortran-comment-line-extra-indent)
652 (make-local-variable 'fortran-minimum-statement-indent-fixed)
653 (make-local-variable 'fortran-minimum-statement-indent-tab)
654 (make-local-variable 'fortran-column-ruler-fixed)
45cf60ae 655 (make-local-variable 'fortran-column-ruler-tab)
23029d77
JB
656 (setq fortran-tab-mode-string " TAB-format")
657 (setq indent-tabs-mode (fortran-analyze-file-format))
7977773d 658 (setq imenu-case-fold-search t)
9645c179 659 (setq imenu-generic-expression fortran-imenu-generic-expression)
7977773d 660 (setq imenu-syntax-alist '(("_$" . "w")))
1eb6bf70 661 (set (make-local-variable 'fill-paragraph-function) 'fortran-fill-paragraph)
7aabd23e 662 (set (make-local-variable 'normal-auto-fill-function) 'fortran-auto-fill)
1eb6bf70
DL
663 (set (make-local-variable 'indent-line-function) 'fortran-indent-line)
664 (set (make-local-variable 'indent-region-function)
665 (lambda (start end)
666 (let (fortran-blink-matching-if ; avoid blinking delay
667 indent-region-function)
668 (indent-region start end nil))))
26ef1c87
DL
669 (set (make-local-variable 'beginning-of-defun-function)
670 #'fortran-beginning-of-subprogram)
671 (set (make-local-variable 'end-of-defun-function)
672 #'fortran-end-of-subprogram)
68ca306c
DL
673 (set (make-local-variable 'add-log-current-defun-function)
674 #'fortran-current-defun)
315aa1de 675 (set (make-local-variable 'dabbrev-case-fold-search) 'case-fold-search)
3dd63760
JB
676 (run-hooks 'fortran-mode-hook))
677\f
315aa1de 678(defsubst fortran-comment-indent ()
3dd63760
JB
679 (save-excursion
680 (skip-chars-backward " \t")
681 (max (+ 1 (current-column))
682 comment-column)))
683
684(defun fortran-indent-comment ()
685 "Align or create comment on current line.
686Existing comments of all types are recognized and aligned.
687If the line has no comment, a side-by-side comment is inserted and aligned
7977773d 688if the value of `comment-start' is not nil.
3dd63760
JB
689Otherwise, a separate-line comment is inserted, on this line
690or on a new line inserted before this line if this line is not blank."
691 (interactive)
692 (beginning-of-line)
693 ;; Recognize existing comments of either kind.
7dae727d 694 (cond ((looking-at fortran-comment-line-start-skip)
3dd63760 695 (fortran-indent-line))
23029d77 696 ((fortran-find-comment-start-skip) ; catches any inline comment and
b8cbdf43
RS
697 ; leaves point after comment-start-skip
698 (if comment-start-skip
699 (progn (goto-char (match-beginning 0))
315aa1de
DL
700 (if (not (= (current-column)
701 (fortran-comment-indent)))
b8cbdf43 702 (progn (delete-horizontal-space)
315aa1de 703 (indent-to (fortran-comment-indent)))))
b8cbdf43 704 (end-of-line))) ; otherwise goto end of line or sth else?
3dd63760
JB
705 ;; No existing comment.
706 ;; If side-by-side comments are defined, insert one,
707 ;; unless line is now blank.
708 ((and comment-start (not (looking-at "^[ \t]*$")))
709 (end-of-line)
710 (delete-horizontal-space)
315aa1de 711 (indent-to (fortran-comment-indent))
3dd63760
JB
712 (insert comment-start))
713 ;; Else insert separate-line comment, making a new line if nec.
714 (t
715 (if (looking-at "^[ \t]*$")
716 (delete-horizontal-space)
717 (beginning-of-line)
7dae727d 718 (insert ?\n)
3dd63760 719 (forward-char -1))
7dae727d 720 (insert fortran-comment-line-start)
3dd63760
JB
721 (insert-char (if (stringp fortran-comment-indent-char)
722 (aref fortran-comment-indent-char 0)
b8cbdf43 723 fortran-comment-indent-char)
1eb6bf70 724 (- (fortran-calculate-indent) (current-column))))))
3dd63760
JB
725
726(defun fortran-comment-region (beg-region end-region arg)
727 "Comments every line in the region.
7977773d 728Puts `fortran-comment-region' at the beginning of every line in the region.
60db3594 729BEG-REGION and END-REGION are args which specify the region boundaries.
3dd63760
JB
730With non-nil ARG, uncomments the region."
731 (interactive "*r\nP")
b937fd1e 732 (let ((end-region-mark (copy-marker end-region))
315aa1de 733 (save-point (point-marker)))
3dd63760
JB
734 (goto-char beg-region)
735 (beginning-of-line)
736 (if (not arg) ;comment the region
737 (progn (insert fortran-comment-region)
738 (while (and (= (forward-line 1) 0)
739 (< (point) end-region-mark))
740 (insert fortran-comment-region)))
741 (let ((com (regexp-quote fortran-comment-region))) ;uncomment the region
742 (if (looking-at com)
743 (delete-region (point) (match-end 0)))
744 (while (and (= (forward-line 1) 0)
745 (< (point) end-region-mark))
746 (if (looking-at com)
747 (delete-region (point) (match-end 0))))))
748 (goto-char save-point)
749 (set-marker end-region-mark nil)
750 (set-marker save-point nil)))
751\f
752(defun fortran-abbrev-start ()
60db3594 753 "Typing ;\\[help-command] or ;? lists all the Fortran abbrevs.
3dd63760
JB
754Any other key combination is executed normally."
755 (interactive)
756 (let (c)
757 (insert last-command-char)
d654e8ce
RS
758 (if (or (eq (setq c (read-event)) ??) ;insert char if not equal to `?'
759 (eq c help-char))
3dd63760 760 (fortran-abbrev-help)
dbc4e1c1 761 (setq unread-command-events (list c)))))
3dd63760
JB
762
763(defun fortran-abbrev-help ()
764 "List the currently defined abbrevs in Fortran mode."
765 (interactive)
766 (message "Listing abbrev table...")
e80f2147 767 (display-buffer (fortran-prepare-abbrev-list-buffer))
3dd63760
JB
768 (message "Listing abbrev table...done"))
769
e80f2147
RS
770(defun fortran-prepare-abbrev-list-buffer ()
771 (save-excursion
772 (set-buffer (get-buffer-create "*Abbrevs*"))
773 (erase-buffer)
4632a893 774 (insert-abbrev-table-description 'fortran-mode-abbrev-table t)
e80f2147
RS
775 (goto-char (point-min))
776 (set-buffer-modified-p nil)
777 (edit-abbrevs-mode))
778 (get-buffer-create "*Abbrevs*"))
779
3dd63760 780(defun fortran-column-ruler ()
7977773d 781 "Insert a column ruler momentarily above current line, till next keystroke.
5a8d870b
RS
782The ruler is defined by the value of `fortran-column-ruler-fixed' when in fixed
783format mode, and `fortran-column-ruler-tab' when in TAB format mode.
3dd63760
JB
784The key typed is executed unless it is SPC."
785 (interactive)
60db3594 786 (momentary-string-display
23029d77
JB
787 (if indent-tabs-mode
788 fortran-column-ruler-tab
789 fortran-column-ruler-fixed)
790 (save-excursion
60db3594 791 (beginning-of-line)
23029d77
JB
792 (if (eq (window-start (selected-window))
793 (window-point (selected-window)))
7a7db8e5 794 (line-beginning-position 2)
23029d77 795 (point)))
3dd63760
JB
796 nil "Type SPC or any command to erase ruler."))
797
798(defun fortran-window-create ()
7977773d 799 "Make the window 72 columns wide.
fe668515 800See also `fortran-window-create-momentarily'."
3dd63760
JB
801 (interactive)
802 (condition-case error
803 (progn
804 (let ((window-min-width 2))
23029d77
JB
805 (if (< (window-width) (frame-width))
806 (enlarge-window-horizontally (- (frame-width)
3dd63760 807 (window-width) 1)))
aa9305c9
DL
808 (let* ((window-edges (window-edges))
809 (scroll-bar-width (- (nth 2 window-edges)
810 (car window-edges)
811 (window-width))))
812 (split-window-horizontally (+ 72 scroll-bar-width)))
3dd63760
JB
813 (other-window 1)
814 (switch-to-buffer " fortran-window-extra" t)
815 (select-window (previous-window))))
e8af40ee 816 (error (message "No room for Fortran window")
3dd63760
JB
817 'error)))
818
819(defun fortran-window-create-momentarily (&optional arg)
7977773d 820 "Momentarily make the window 72 columns wide.
3dd63760 821Optional ARG non-nil and non-unity disables the momentary feature.
fe668515 822See also `fortran-window-create'."
3dd63760
JB
823 (interactive "p")
824 (if (or (not arg)
825 (= arg 1))
826 (save-window-excursion
827 (if (not (equal (fortran-window-create) 'error))
828 (progn (message "Type SPC to continue editing.")
d654e8ce 829 (let ((char (read-event)))
3dd63760 830 (or (equal char (string-to-char " "))
dbc4e1c1 831 (setq unread-command-events (list char)))))))
3dd63760
JB
832 (fortran-window-create)))
833
834(defun fortran-split-line ()
835 "Break line at point and insert continuation marker and alignment."
836 (interactive)
837 (delete-horizontal-space)
7dae727d
DL
838 (if (save-excursion
839 (beginning-of-line)
840 (looking-at fortran-comment-line-start-skip))
841 (insert ?\n fortran-comment-line-start ? )
23029d77 842 (if indent-tabs-mode
7dae727d 843 (insert ?\n ?\t (fortran-numerical-continuation-char))
1eb6bf70
DL
844 (insert "\n " fortran-continuation-string))) ; Space after \n important
845 (fortran-indent-line)) ; when the cont string is C, c or *.
846
847(defun fortran-remove-continuation ()
848 (if (looking-at "\\( [^ 0\n]\\|\t[1-9]\\|&\\)")
849 (progn (replace-match "")
850 (delete-indentation)
851 t)))
3dd63760 852
46d4d7bf
DL
853(defun fortran-join-line (arg)
854 "Join current line to the previous one and re-indent.
855With a prefix argument, repeat this operation that many times.
856If the prefix argument ARG is negative, join the next -ARG lines.
857Continuation lines are correctly handled."
858 (interactive "*p")
7977773d 859 (save-excursion
46d4d7bf
DL
860 (when (> 0 arg)
861 (setq arg (- arg))
862 (forward-line arg))
863 (while (not (zerop arg))
864 (beginning-of-line)
865 (or (fortran-remove-continuation)
866 (delete-indentation))
867 (setq arg (1- arg)))
7977773d
DL
868 (fortran-indent-line)))
869
3dd63760 870(defun fortran-numerical-continuation-char ()
b56eb7c9 871 "Return a digit for tab-digit style of continuation lines.
3dd63760
JB
872If, previous line is a tab-digit continuation line, returns that digit
873plus one. Otherwise return 1. Zero not allowed."
874 (save-excursion
875 (forward-line -1)
876 (if (looking-at "\t[1-9]")
877 (+ ?1 (% (- (char-after (+ (point) 1)) ?0) 9))
878 ?1)))
879
556dd629 880(put 'fortran-electric-line-number 'delete-selection t)
3dd63760
JB
881(defun fortran-electric-line-number (arg)
882 "Self insert, but if part of a Fortran line number indent it automatically.
7977773d 883Auto-indent does not happen if a numeric ARG is used."
3dd63760
JB
884 (interactive "P")
885 (if (or arg (not fortran-electric-line-number))
60db3594 886 (if arg
b8cbdf43 887 (self-insert-command (prefix-numeric-value arg))
3dd63760
JB
888 (self-insert-command 1))
889 (if (or (and (= 5 (current-column))
890 (save-excursion
891 (beginning-of-line)
315aa1de 892 (looking-at " \\{5\\}"))) ;In col 5 with only spaces to left.
23029d77 893 (and (= (if indent-tabs-mode
1eb6bf70
DL
894 fortran-minimum-statement-indent-tab
895 fortran-minimum-statement-indent-fixed) (current-column))
7dae727d
DL
896 (eq ?\t (char-after (line-beginning-position))) ;In col 8
897 ; with a single tab to the left.
3dd63760
JB
898 (not (or (eq last-command 'fortran-indent-line)
899 (eq last-command
aa4ed68c 900 'fortran-indent-new-line))))
3dd63760
JB
901 (save-excursion
902 (re-search-backward "[^ \t0-9]"
7dae727d 903 (line-beginning-position)
1eb6bf70
DL
904 t)) ;not a line number
905 (looking-at "[0-9]")) ;within a line number
b8cbdf43 906 (self-insert-command (prefix-numeric-value arg))
3dd63760
JB
907 (skip-chars-backward " \t")
908 (insert last-command-char)
909 (fortran-indent-line))))
910\f
1eb6bf70 911(defvar fortran-end-prog-re1
7b84ff90 912 "end\
823ab5da
DL
913\\([ \t]*\\(program\\|subroutine\\|function\\|block[ \t]*data\\)\\>\
914\\([ \t]*\\(\\sw\\|\\s_\\)+\\)?\\)?")
1eb6bf70 915(defvar fortran-end-prog-re
0a3a1c19
DL
916 (concat "^[ \t0-9]*" fortran-end-prog-re1)
917 "Regexp possibly marking subprogram end.")
1eb6bf70 918
823ab5da
DL
919(defun fortran-check-end-prog-re ()
920 "Check a preliminary match against `fortran-end-prog-re'."
921 ;; Having got a possible match for the subprogram end, we need a
922 ;; match of whitespace, avoiding possible column 73+ stuff.
923 (save-match-data
bd6cabcf 924 (string-match "^\\s-*\\(\\'\\|\\s<\\)"
823ab5da
DL
925 (buffer-substring (match-end 0)
926 (min (line-end-position)
927 (+ 72 (line-beginning-position)))))))
928
929;; Note that you can't just check backwards for `subroutine' &c in
930;; case of un-marked main programs not at the start of the file.
7dae727d 931(defun fortran-beginning-of-subprogram ()
b8cbdf43 932 "Moves point to the beginning of the current Fortran subprogram."
3dd63760 933 (interactive)
f687a879
DL
934 (save-match-data
935 (let ((case-fold-search t))
936 (beginning-of-line -1)
937 (if (catch 'ok
938 (while (re-search-backward fortran-end-prog-re nil 'move)
939 (if (fortran-check-end-prog-re)
940 (throw 'ok t))))
941 (forward-line)))))
3dd63760 942
7dae727d 943(defun fortran-end-of-subprogram ()
b8cbdf43 944 "Moves point to the end of the current Fortran subprogram."
3dd63760 945 (interactive)
f687a879
DL
946 (save-match-data
947 (let ((case-fold-search t))
948 (if (save-excursion ; on END
949 (beginning-of-line)
950 (and (looking-at fortran-end-prog-re)
951 (fortran-check-end-prog-re)))
952 (forward-line)
953 (beginning-of-line 2)
954 (catch 'ok
955 (while (re-search-forward fortran-end-prog-re nil 'move)
956 (if (fortran-check-end-prog-re)
957 (throw 'ok t))))
958 (goto-char (match-beginning 0))
959 (forward-line)))))
3dd63760 960
3dd63760 961(defun fortran-previous-statement ()
b8cbdf43
RS
962 "Moves point to beginning of the previous Fortran statement.
963Returns `first-statement' if that statement is the first
3dd63760
JB
964non-comment Fortran statement in the file, and nil otherwise."
965 (interactive)
966 (let (not-first-statement continue-test)
967 (beginning-of-line)
968 (setq continue-test
b8cbdf43 969 (and
7dae727d 970 (not (looking-at fortran-comment-line-start-skip))
b8cbdf43 971 (or (looking-at
315aa1de
DL
972 (concat "[ \t]*"
973 (regexp-quote fortran-continuation-string)))
974 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]"))))
3dd63760 975 (while (and (setq not-first-statement (= (forward-line -1) 0))
7dae727d 976 (or (looking-at fortran-comment-line-start-skip)
315aa1de
DL
977 (looking-at "[ \t]*$\\| \\{5\\}[^ 0\n]\\|\t[1-9]")
978 (looking-at (concat "[ \t]*" comment-start-skip)))))
3dd63760
JB
979 (cond ((and continue-test
980 (not not-first-statement))
981 (message "Incomplete continuation statement."))
60db3594 982 (continue-test
3dd63760
JB
983 (fortran-previous-statement))
984 ((not not-first-statement)
985 'first-statement))))
986
987(defun fortran-next-statement ()
b8cbdf43 988 "Moves point to beginning of the next Fortran statement.
3dd63760
JB
989Returns `last-statement' if that statement is the last
990non-comment Fortran statement in the file, and nil otherwise."
991 (interactive)
992 (let (not-last-statement)
993 (beginning-of-line)
b8cbdf43
RS
994 (while (and (setq not-last-statement
995 (and (= (forward-line 1) 0)
996 (not (eobp))))
7dae727d
DL
997 (or (looking-at fortran-comment-line-start-skip)
998 (looking-at "[ \t]*$\\| [^ 0\n]\\|\t[1-9]")
315aa1de 999 (looking-at (concat "[ \t]*" comment-start-skip)))))
3dd63760
JB
1000 (if (not not-last-statement)
1001 'last-statement)))
1002\f
7dae727d
DL
1003(defun fortran-blink-match (regex keyword find-begin)
1004 "From a line matching REGEX, blink matching KEYWORD statement line.
1005Use function FIND-BEGIN to match it."
1eb6bf70 1006 (let ((top-of-window (window-start))
7dae727d 1007 (end-point (point))
1eb6bf70 1008 (case-fold-search t)
7dae727d 1009 matching
1eb6bf70 1010 message)
7dae727d
DL
1011 (if (save-excursion
1012 (beginning-of-line)
1013 (skip-chars-forward " \t0-9")
1014 (looking-at regex))
3dd63760 1015 (progn
7dae727d
DL
1016 (if (not (setq matching (funcall find-begin)))
1017 (setq message (concat "No matching " keyword "."))
1018 (if (< matching top-of-window)
c5af0a18 1019 (save-excursion
7dae727d 1020 (goto-char matching)
c5af0a18
RS
1021 (beginning-of-line)
1022 (setq message
1023 (concat "Matches "
7dae727d
DL
1024 (buffer-substring (point)
1025 (line-end-position)))))))
3dd63760 1026 (if message
b8cbdf43 1027 (message "%s" message)
7dae727d 1028 (goto-char matching)
3dd63760 1029 (sit-for 1)
7dae727d
DL
1030 (goto-char end-point))))))
1031
1032(defun fortran-blink-matching-if ()
1033 "From an ENDIF or ELSE statement, blink the matching IF statement."
1034 (fortran-blink-match "e\\(nd[ \t]*if\\|lse\\([ \t]*if\\)?\\)\\b"
1035 "if" #'fortran-beginning-if))
947388af
RS
1036
1037(defun fortran-blink-matching-do ()
45cf60ae 1038 "From an ENDDO statement, blink the matching DO or DO WHILE statement."
7dae727d 1039 (fortran-blink-match "end[ \t]*do\\b" "do" #'fortran-beginning-do))
c5af0a18
RS
1040
1041(defun fortran-mark-do ()
60db3594 1042 "Put mark at end of Fortran DO [WHILE]-ENDDO construct, point at beginning.
c5af0a18
RS
1043The marks are pushed."
1044 (interactive)
1045 (let (enddo-point do-point)
1046 (if (setq enddo-point (fortran-end-do))
1047 (if (not (setq do-point (fortran-beginning-do)))
1048 (message "No matching do.")
c5af0a18
RS
1049 (goto-char enddo-point)
1050 (push-mark)
1051 (goto-char do-point)))))
1052
1053(defun fortran-end-do ()
45cf60ae
DL
1054 "Search forward for first unmatched ENDDO.
1055Return point or nil."
1eb6bf70
DL
1056 (let ((case-fold-search t))
1057 (if (save-excursion (beginning-of-line)
1058 (skip-chars-forward " \t0-9")
1059 (looking-at "end[ \t]*do\\b"))
1060 ;; Sitting on one.
1061 (match-beginning 0)
1062 ;; Search for one.
1063 (save-excursion
1064 (let ((count 1))
7dae727d 1065 (while (and (not (= count 0))
1eb6bf70
DL
1066 (not (eq (fortran-next-statement) 'last-statement))
1067 ;; Keep local to subprogram
45cf60ae
DL
1068 (not (and (looking-at fortran-end-prog-re)
1069 (fortran-check-end-prog-re))))
1eb6bf70
DL
1070 (skip-chars-forward " \t0-9")
1071 (cond ((looking-at "end[ \t]*do\\b")
1072 (setq count (1- count)))
7dae727d
DL
1073 ((looking-at
1074 "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+[^0-9]")
1075 (setq count (+ count 1)))))
1076 (and (= count 0)
1eb6bf70
DL
1077 ;; All pairs accounted for.
1078 (point)))))))
c5af0a18
RS
1079
1080(defun fortran-beginning-do ()
45cf60ae
DL
1081 "Search backwards for first unmatched DO [WHILE].
1082Return point or nil."
1eb6bf70 1083 (let ((case-fold-search t))
7dae727d
DL
1084 (if (save-excursion
1085 (beginning-of-line)
1086 (skip-chars-forward " \t0-9")
1087 (looking-at "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+"))
1eb6bf70
DL
1088 ;; Sitting on one.
1089 (match-beginning 0)
1090 ;; Search for one.
1091 (save-excursion
1092 (let ((count 1))
7dae727d 1093 (while (and (not (= count 0))
1eb6bf70
DL
1094 (not (eq (fortran-previous-statement) 'first-statement))
1095 ;; Keep local to subprogram
45cf60ae
DL
1096 (not (and (looking-at fortran-end-prog-re)
1097 (fortran-check-end-prog-re))))
1eb6bf70 1098 (skip-chars-forward " \t0-9")
7dae727d
DL
1099 (cond ((looking-at
1100 "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?do[ \t]+[^0-9]")
1eb6bf70
DL
1101 (setq count (1- count)))
1102 ((looking-at "end[ \t]*do\\b")
1103 (setq count (1+ count)))))
7dae727d 1104 (and (= count 0)
1eb6bf70
DL
1105 ;; All pairs accounted for.
1106 (point)))))))
c5af0a18
RS
1107
1108(defun fortran-mark-if ()
1109 "Put mark at end of Fortran IF-ENDIF construct, point at beginning.
1110The marks are pushed."
1111 (interactive)
1112 (let (endif-point if-point)
1113 (if (setq endif-point (fortran-end-if))
1114 (if (not (setq if-point (fortran-beginning-if)))
1115 (message "No matching if.")
1116 ;; Set mark, move point.
1117 (goto-char endif-point)
1118 (push-mark)
1119 (goto-char if-point)))))
1120
1eb6bf70
DL
1121(defvar fortran-if-start-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(")
1122
c5af0a18 1123(defun fortran-end-if ()
45cf60ae
DL
1124 "Search forwards for first unmatched ENDIF.
1125Return point or nil."
1eb6bf70
DL
1126 (let ((case-fold-search t))
1127 (if (save-excursion (beginning-of-line)
1128 (skip-chars-forward " \t0-9")
1129 (looking-at "end[ \t]*if\\b"))
1130 ;; Sitting on one.
1131 (match-beginning 0)
1132 ;; Search for one. The point has been already been moved to first
1133 ;; letter on line but this should not cause troubles.
1134 (save-excursion
1135 (let ((count 1))
7dae727d 1136 (while (and (not (= count 0))
1eb6bf70
DL
1137 (not (eq (fortran-next-statement) 'last-statement))
1138 ;; Keep local to subprogram.
45cf60ae
DL
1139 (not (and (looking-at fortran-end-prog-re)
1140 (fortran-check-end-prog-re))))
1eb6bf70
DL
1141 (skip-chars-forward " \t0-9")
1142 (cond ((looking-at "end[ \t]*if\\b")
7dae727d 1143 (setq count (- count 1)))
1eb6bf70
DL
1144 ((looking-at fortran-if-start-re)
1145 (save-excursion
1146 (if (or
1147 (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]")
1148 (let (then-test) ; Multi-line if-then.
1149 (while
315aa1de
DL
1150 (and
1151 (= (forward-line 1) 0)
1152 ;; Search forward for then.
1153 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")
1154 (not
1155 (setq then-test
1156 (looking-at
1157 ".*then\\b[ \t]*[^ \t(=a-z0-9]")))))
1eb6bf70 1158 then-test))
7dae727d
DL
1159 (setq count (+ count 1)))))))
1160 (and (= count 0)
1eb6bf70
DL
1161 ;; All pairs accounted for.
1162 (point)))))))
c5af0a18
RS
1163
1164(defun fortran-beginning-if ()
45cf60ae
DL
1165 "Search backwards for first unmatched IF-THEN.
1166Return point or nil."
1eb6bf70
DL
1167 (let ((case-fold-search t))
1168 (if (save-excursion
7dae727d
DL
1169 ;; May be sitting on multi-line if-then statement, first
1170 ;; move to beginning of current statement. Note:
1171 ;; `fortran-previous-statement' moves to previous statement
1172 ;; *unless* current statement is first one. Only move
1173 ;; forward if not first-statement.
1eb6bf70
DL
1174 (if (not (eq (fortran-previous-statement) 'first-statement))
1175 (fortran-next-statement))
1176 (skip-chars-forward " \t0-9")
1177 (and
1178 (looking-at fortran-if-start-re)
1179 (save-match-data
1180 (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]")
1181 ;; Multi-line if-then.
1182 (let (then-test)
1183 (while
c5af0a18 1184 (and (= (forward-line 1) 0)
1eb6bf70 1185 ;; Search forward for then.
315aa1de 1186 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")
1eb6bf70
DL
1187 (not
1188 (setq then-test
1189 (looking-at
1190 ".*then\\b[ \t]*[^ \t(=a-z0-9]")))))
1191 then-test)))))
1192 ;; Sitting on one.
1193 (match-beginning 0)
1194 ;; Search for one.
1195 (save-excursion
1196 (let ((count 1))
7dae727d 1197 (while (and (not (= count 0))
1eb6bf70
DL
1198 (not (eq (fortran-previous-statement) 'first-statement))
1199 ;; Keep local to subprogram.
45cf60ae
DL
1200 (not (and (looking-at fortran-end-prog-re)
1201 (fortran-check-end-prog-re))))
1eb6bf70
DL
1202 (skip-chars-forward " \t0-9")
1203 (cond ((looking-at fortran-if-start-re)
1204 (save-excursion
1205 (if (or
1206 (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]")
1207 (let (then-test) ; Multi-line if-then.
1208 (while
315aa1de
DL
1209 (and
1210 (= (forward-line 1) 0)
1211 ;; Search forward for then.
1212 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")
1213 (not
1214 (setq then-test
1215 (looking-at
1216 (concat ".*then\\b[ \t]*"
1217 "[^ \t(=a-z[0-9]]"))))))
1eb6bf70 1218 then-test))
7dae727d 1219 (setq count (- count 1)))))
1eb6bf70 1220 ((looking-at "end[ \t]*if\\b")
7dae727d
DL
1221 (setq count (+ count 1)))))
1222 (and (= count 0)
1eb6bf70
DL
1223 ;; All pairs accounted for.
1224 (point)))))))
3dd63760
JB
1225\f
1226(defun fortran-indent-line ()
7977773d 1227 "Indent current Fortran line based on its contents and on previous lines."
3dd63760 1228 (interactive)
1eb6bf70 1229 (let ((cfi (fortran-calculate-indent)))
3dd63760
JB
1230 (save-excursion
1231 (beginning-of-line)
1232 (if (or (not (= cfi (fortran-current-line-indentation)))
1233 (and (re-search-forward "^[ \t]*[0-9]+" (+ (point) 4) t)
1234 (not (fortran-line-number-indented-correctly-p))))
1235 (fortran-indent-to-column cfi)
1236 (beginning-of-line)
7dae727d 1237 (if (and (not (looking-at fortran-comment-line-start-skip))
23029d77 1238 (fortran-find-comment-start-skip))
3dd63760
JB
1239 (fortran-indent-comment))))
1240 ;; Never leave point in left margin.
1241 (if (< (current-column) cfi)
1242 (move-to-column cfi))
23029d77 1243 (if (and auto-fill-function
7dae727d
DL
1244 (> (save-excursion (end-of-line) (current-column))
1245 fill-column))
b8cbdf43
RS
1246 (save-excursion
1247 (end-of-line)
4254fe58 1248 (fortran-fill)))
3dd63760 1249 (if fortran-blink-matching-if
7977773d 1250 (progn
947388af
RS
1251 (fortran-blink-matching-if)
1252 (fortran-blink-matching-do)))))
3dd63760 1253
315aa1de
DL
1254(defun fortran-auto-fill ()
1255 (if (> (current-column) (current-fill-column))
1256 (let ((cfi (fortran-calculate-indent)))
1257 (save-excursion
1258 (beginning-of-line)
1259 (if (or (not (= cfi (fortran-current-line-indentation)))
1260 (and (re-search-forward "^[ \t]*[0-9]+"
1261 (+ (point) 4) t)
1262 (not (fortran-line-number-indented-correctly-p))))
1263 (fortran-indent-to-column cfi)
1264 (beginning-of-line)
1265 (if (and (not (looking-at fortran-comment-line-start-skip))
1266 (fortran-find-comment-start-skip))
1267 (fortran-indent-comment))))
1268 (fortran-fill)
1269 ;; Never leave point in left margin.
1270 (if (< (current-column) cfi)
1271 (move-to-column cfi)))))
1272
5aca2648
DL
1273;; Historically this was a separate function which advertised itself
1274;; as reindenting but only did so where `most likely to be necessary'.
1275(defalias 'fortran-indent-new-line 'reindent-then-newline-and-indent)
b8cbdf43 1276
3dd63760 1277(defun fortran-indent-subprogram ()
7977773d 1278 "Properly indent the Fortran subprogram which contains point."
3dd63760
JB
1279 (interactive)
1280 (save-excursion
7a7db8e5 1281 (mark-defun)
c4c42b2e
DL
1282 (message "Indenting subprogram...")
1283 (indent-region (point) (mark) nil))
3dd63760
JB
1284 (message "Indenting subprogram...done."))
1285
1eb6bf70 1286(defun fortran-calculate-indent ()
b8cbdf43 1287 "Calculates the Fortran indent column based on previous lines."
3dd63760
JB
1288 (let (icol first-statement (case-fold-search t)
1289 (fortran-minimum-statement-indent
23029d77
JB
1290 (if indent-tabs-mode
1291 fortran-minimum-statement-indent-tab
1292 fortran-minimum-statement-indent-fixed)))
3dd63760
JB
1293 (save-excursion
1294 (setq first-statement (fortran-previous-statement))
1295 (if first-statement
1296 (setq icol fortran-minimum-statement-indent)
1297 (progn
1298 (if (= (point) (point-min))
1299 (setq icol fortran-minimum-statement-indent)
1300 (setq icol (fortran-current-line-indentation)))
1301 (skip-chars-forward " \t0-9")
1eb6bf70 1302 (cond ((looking-at "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*(")
b8cbdf43 1303 (if (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t_$(=a-z0-9]")
3dd63760
JB
1304 (let (then-test) ;multi-line if-then
1305 (while (and (= (forward-line 1) 0)
b8cbdf43 1306 ;;search forward for then
315aa1de 1307 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")
7dae727d
DL
1308 (not (setq then-test
1309 (looking-at
1310 ".*then\\b[ \t]\
b8cbdf43 1311*[^ \t_$(=a-z0-9]")))))
3dd63760
JB
1312 then-test))
1313 (setq icol (+ icol fortran-if-indent))))
1eb6bf70 1314 ((looking-at "else\\(if\\)?\\b")
3dd63760 1315 (setq icol (+ icol fortran-if-indent)))
1eb6bf70 1316 ((looking-at "select[ \t]*case[ \t](.*)")
5a8d870b 1317 (setq icol (+ icol fortran-if-indent)))
1eb6bf70 1318 ((looking-at "case[ \t]*(.*)")
5a8d870b
RS
1319 (setq icol (+ icol fortran-if-indent)))
1320 ((looking-at "case[ \t]*default\\b")
1321 (setq icol (+ icol fortran-if-indent)))
b8cbdf43
RS
1322 ((looking-at "\\(otherwise\\|else[ \t]*where\\)\\b")
1323 (setq icol (+ icol fortran-if-indent)))
f93f92f1 1324 ((looking-at "where[ \t]*(.*)[ \t]*\n")
b8cbdf43 1325 (setq icol (+ icol fortran-if-indent)))
3dd63760
JB
1326 ((looking-at "do\\b")
1327 (setq icol (+ icol fortran-do-indent)))
b8cbdf43 1328 ((looking-at
7dae727d
DL
1329 "\\(structure\\|union\\|map\\|interface\\)\
1330\\b[ \t]*[^ \t=(a-z]")
b8cbdf43 1331 (setq icol (+ icol fortran-structure-indent)))
45cf60ae
DL
1332 ((and (looking-at fortran-end-prog-re1)
1333 (fortran-check-end-prog-re))
b8cbdf43 1334 ;; Previous END resets indent to minimum
3dd63760
JB
1335 (setq icol fortran-minimum-statement-indent))))))
1336 (save-excursion
1337 (beginning-of-line)
1338 (cond ((looking-at "[ \t]*$"))
7dae727d 1339 ((looking-at fortran-comment-line-start-skip)
3dd63760 1340 (cond ((eq fortran-comment-indent-style 'relative)
23029d77 1341 (setq icol (+ icol fortran-comment-line-extra-indent)))
3dd63760 1342 ((eq fortran-comment-indent-style 'fixed)
b8cbdf43 1343 (setq icol (+ fortran-minimum-statement-indent
23029d77 1344 fortran-comment-line-extra-indent))))
b8cbdf43 1345 (setq fortran-minimum-statement-indent 0))
3dd63760 1346 ((or (looking-at (concat "[ \t]*"
b8cbdf43
RS
1347 (regexp-quote
1348 fortran-continuation-string)))
315aa1de 1349 (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]"))
3dd63760 1350 (setq icol (+ icol fortran-continuation-indent)))
b56eb7c9
RS
1351 ((looking-at "[ \t]*#") ; Check for cpp directive.
1352 (setq fortran-minimum-statement-indent 0 icol 0))
3dd63760
JB
1353 (first-statement)
1354 ((and fortran-check-all-num-for-matching-do
1355 (looking-at "[ \t]*[0-9]+")
1356 (fortran-check-for-matching-do))
1357 (setq icol (- icol fortran-do-indent)))
1358 (t
1359 (skip-chars-forward " \t0-9")
1eb6bf70 1360 (cond ((looking-at "end[ \t]*\\(if\\|select\\|where\\)\\b")
3dd63760 1361 (setq icol (- icol fortran-if-indent)))
1eb6bf70 1362 ((looking-at "else\\(if\\)?\\b")
5a8d870b 1363 (setq icol (- icol fortran-if-indent)))
1eb6bf70 1364 ((looking-at "case[ \t]*\\((.*)\\|default\\>\\)")
5a8d870b 1365 (setq icol (- icol fortran-if-indent)))
b8cbdf43
RS
1366 ((looking-at "\\(otherwise\\|else[ \t]*where\\)\\b")
1367 (setq icol (- icol fortran-if-indent)))
3dd63760
JB
1368 ((and (looking-at "continue\\b")
1369 (fortran-check-for-matching-do))
1370 (setq icol (- icol fortran-do-indent)))
1371 ((looking-at "end[ \t]*do\\b")
1372 (setq icol (- icol fortran-do-indent)))
1eb6bf70 1373 ((looking-at "end[ \t]*\
5a8d870b 1374\\(structure\\|union\\|map\\|interface\\)\\b[ \t]*[^ \t=(a-z]")
b8cbdf43 1375 (setq icol (- icol fortran-structure-indent)))
1eb6bf70 1376 ((and (looking-at fortran-end-prog-re1)
45cf60ae 1377 (fortran-check-end-prog-re)
3dd63760
JB
1378 (not (= icol fortran-minimum-statement-indent)))
1379 (message "Warning: `end' not in column %d. Probably\
1380 an unclosed block." fortran-minimum-statement-indent))))))
1381 (max fortran-minimum-statement-indent icol)))
1382\f
1383(defun fortran-current-line-indentation ()
1384 "Indentation of current line, ignoring Fortran line number or continuation.
1385This is the column position of the first non-whitespace character
1386aside from the line number and/or column 5/8 line-continuation character.
1387For comment lines, returns indentation of the first
1388non-indentation text within the comment."
1389 (save-excursion
1390 (beginning-of-line)
7dae727d 1391 (cond ((looking-at fortran-comment-line-start-skip)
3dd63760
JB
1392 (goto-char (match-end 0))
1393 (skip-chars-forward
b8cbdf43
RS
1394 (if (stringp fortran-comment-indent-char)
1395 fortran-comment-indent-char
1396 (char-to-string fortran-comment-indent-char))))
315aa1de 1397 ((or (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]"))
3dd63760
JB
1398 (goto-char (match-end 0)))
1399 (t
1400 ;; Move past line number.
7dae727d 1401 (skip-chars-forward "[ \t0-9]")))
3dd63760
JB
1402 ;; Move past whitespace.
1403 (skip-chars-forward " \t")
1404 (current-column)))
1405
1406(defun fortran-indent-to-column (col)
7977773d 1407 "Indent current line with spaces to column COL.
3dd63760
JB
1408notes: 1) A non-zero/non-blank character in column 5 indicates a continuation
1409 line, and this continuation character is retained on indentation;
b8cbdf43
RS
1410 2) If `fortran-continuation-string' is the first non-whitespace
1411 character, this is a continuation line;
3dd63760
JB
1412 3) A non-continuation line which has a number as the first
1413 non-whitespace character is a numbered line.
b8cbdf43 1414 4) A TAB followed by a digit indicates a continuation line."
3dd63760
JB
1415 (save-excursion
1416 (beginning-of-line)
7dae727d 1417 (if (looking-at fortran-comment-line-start-skip)
3dd63760 1418 (if fortran-comment-indent-style
7dae727d
DL
1419 (let* ((char (if (stringp fortran-comment-indent-char)
1420 (aref fortran-comment-indent-char 0)
1421 fortran-comment-indent-char))
1422 (chars (string ? ?\t char)))
3dd63760 1423 (goto-char (match-end 0))
7dae727d
DL
1424 (skip-chars-backward chars)
1425 (delete-region (point) (progn (skip-chars-forward chars)
1426 (point)))
3dd63760
JB
1427 (insert-char char (- col (current-column)))))
1428 (if (looking-at "\t[1-9]")
23029d77 1429 (if indent-tabs-mode
3dd63760
JB
1430 (goto-char (match-end 0))
1431 (delete-char 2)
7dae727d 1432 (insert-char ? 5)
3dd63760 1433 (insert fortran-continuation-string))
315aa1de 1434 (if (looking-at " \\{5\\}[^ 0\n]")
23029d77 1435 (if indent-tabs-mode
3dd63760 1436 (progn (delete-char 6)
7dae727d 1437 (insert ?\t (fortran-numerical-continuation-char) 1))
3dd63760
JB
1438 (forward-char 6))
1439 (delete-horizontal-space)
b8cbdf43
RS
1440 ;; Put line number in columns 0-4
1441 ;; or put continuation character in column 5.
3dd63760
JB
1442 (cond ((eobp))
1443 ((looking-at (regexp-quote fortran-continuation-string))
23029d77 1444 (if indent-tabs-mode
3dd63760 1445 (progn
60db3594 1446 (indent-to
23029d77
JB
1447 (if indent-tabs-mode
1448 fortran-minimum-statement-indent-tab
1449 fortran-minimum-statement-indent-fixed))
3dd63760
JB
1450 (delete-char 1)
1451 (insert-char (fortran-numerical-continuation-char) 1))
b8cbdf43
RS
1452 (indent-to 5)
1453 (forward-char 1)))
3dd63760
JB
1454 ((looking-at "[0-9]+")
1455 (let ((extra-space (- 5 (- (match-end 0) (point)))))
1456 (if (< extra-space 0)
1457 (message "Warning: line number exceeds 5-digit limit.")
1458 (indent-to (min fortran-line-number-indent extra-space))))
1459 (skip-chars-forward "0-9")))))
1460 ;; Point is now after any continuation character or line number.
1461 ;; Put body of statement where specified.
1462 (delete-horizontal-space)
1463 (indent-to col)
1464 ;; Indent any comment following code on the same line.
1465 (if (and comment-start-skip
23029d77 1466 (fortran-find-comment-start-skip))
3dd63760 1467 (progn (goto-char (match-beginning 0))
7dae727d 1468 (if (not (= (current-column)
315aa1de 1469 (fortran-comment-indent)))
3dd63760 1470 (progn (delete-horizontal-space)
315aa1de 1471 (indent-to (fortran-comment-indent)))))))))
3dd63760
JB
1472
1473(defun fortran-line-number-indented-correctly-p ()
1474 "Return t if current line's line number is correctly indented.
1475Do not call if there is no line number."
1476 (save-excursion
1477 (beginning-of-line)
1478 (skip-chars-forward " \t")
1479 (and (<= (current-column) fortran-line-number-indent)
1480 (or (= (current-column) fortran-line-number-indent)
1481 (progn (skip-chars-forward "0-9")
1482 (= (current-column) 5))))))
1483
1484(defun fortran-check-for-matching-do ()
7977773d
DL
1485 "When called from a numbered statement, return t if matching DO is found.
1486Otherwise return nil."
7dae727d
DL
1487 (let ((case-fold-search t)
1488 charnum)
3dd63760
JB
1489 (save-excursion
1490 (beginning-of-line)
1491 (if (looking-at "[ \t]*[0-9]+")
1492 (progn
1493 (skip-chars-forward " \t")
7dae727d
DL
1494 (skip-chars-forward "0") ;skip past leading zeros
1495 (setq charnum
1496 (buffer-substring (point) (progn
1497 (skip-chars-forward "0-9")
1498 (point))))
3dd63760 1499 (beginning-of-line)
7a7db8e5
DL
1500 (save-restriction
1501 (save-excursion
1502 (narrow-to-defun)
1503 (and (re-search-backward
315aa1de
DL
1504 (concat
1505 "\\(^[ \t0-9]*do[ \t]*0*"
1506 charnum "\\b\\)\\|" "\\(^[ \t]*0*"
1507 charnum "\\b\\)")
7a7db8e5 1508 nil t)
315aa1de
DL
1509 (looking-at
1510 (concat "^[ \t0-9]*do[ \t]*0*"
1511 charnum))))))))))
3dd63760 1512
23029d77 1513(defun fortran-find-comment-start-skip ()
b8cbdf43
RS
1514 "Move to past `comment-start-skip' found on current line.
1515Return t if `comment-start-skip' found, nil if not."
1eb6bf70
DL
1516 ;; In order to move point only if comment-start-skip is found, this
1517 ;; one uses a lot of save-excursions. Note that re-search-forward
1518 ;; moves point even if comment-start-skip is inside a string-constant.
315aa1de 1519 ;; Some code expects certain values for match-beginning and end.
b8cbdf43 1520 (interactive)
315aa1de
DL
1521 (if (and comment-start-skip
1522 (save-excursion
1523 (re-search-forward comment-start-skip (line-end-position) t)))
f022dd89
SM
1524 (let ((save-match-beginning (match-beginning 0))
1525 (save-match-end (match-end 0)))
1526 (if (fortran-is-in-string-p (match-beginning 0))
1527 (save-excursion
1528 (goto-char save-match-end)
1529 (fortran-find-comment-start-skip)) ; recurse for rest of line
1530 (goto-char save-match-beginning)
7dae727d 1531 (re-search-forward comment-start-skip (line-end-position) t)
f022dd89 1532 (goto-char (match-end 0))
315aa1de 1533 t))))
f022dd89 1534
1eb6bf70
DL
1535;;From: ralf@up3aud1.gwdg.de (Ralf Fassel)
1536;; Test if TAB format continuation lines work.
b56eb7c9 1537(defun fortran-is-in-string-p (where)
7977773d 1538 "Return non-nil iff WHERE (a buffer position) is inside a Fortran string."
b56eb7c9
RS
1539 (save-excursion
1540 (goto-char where)
1541 (cond
1542 ((bolp) nil) ; bol is never inside a string
1543 ((save-excursion ; comment lines too
1eb6bf70 1544 (beginning-of-line)
7dae727d 1545 (looking-at fortran-comment-line-start-skip)) nil)
b56eb7c9
RS
1546 (t (let (;; ok, serious now. Init some local vars:
1547 (parse-state '(0 nil nil nil nil nil 0))
1548 (quoted-comment-start (if comment-start
1549 (regexp-quote comment-start)))
1550 (not-done t)
7dae727d 1551 parse-limit end-of-line)
b56eb7c9
RS
1552 ;; move to start of current statement
1553 (fortran-next-statement)
1554 (fortran-previous-statement)
1555 ;; now parse up to WHERE
1556 (while not-done
1557 (if (or ;; skip to next line if:
1558 ;; - comment line?
7dae727d 1559 (looking-at fortran-comment-line-start-skip)
b56eb7c9
RS
1560 ;; - at end of line?
1561 (eolp)
1562 ;; - not in a string and after comment-start?
1563 (and (not (nth 3 parse-state))
1564 comment-start
1565 (equal comment-start
1566 (char-to-string (preceding-char)))))
1eb6bf70 1567 (if (> (forward-line) 0)
b56eb7c9
RS
1568 (setq not-done nil))
1569 ;; else:
1570 ;; if we are at beginning of code line, skip any
1571 ;; whitespace, labels and tab continuation markers.
1572 (if (bolp) (skip-chars-forward " \t0-9"))
1573 ;; if we are in column <= 5 now, check for continuation char
1574 (cond ((= 5 (current-column)) (forward-char 1))
1575 ((and (< (current-column) 5)
1576 (equal fortran-continuation-string
1577 (char-to-string (following-char)))
1578 (forward-char 1))))
1579 ;; find out parse-limit from here
7dae727d 1580 (setq end-of-line (line-end-position))
b56eb7c9
RS
1581 (setq parse-limit (min where end-of-line))
1582 ;; parse max up to comment-start, if non-nil and in current line
1583 (if comment-start
1584 (save-excursion
1585 (if (re-search-forward quoted-comment-start end-of-line t)
1586 (setq parse-limit (min (point) parse-limit)))))
1587 ;; now parse if still in limits
1588 (if (< (point) where)
1589 (setq parse-state (parse-partial-sexp
1590 (point) parse-limit nil nil parse-state))
7dae727d 1591 (setq not-done nil))))
b56eb7c9
RS
1592 ;; result is
1593 (nth 3 parse-state))))))
b8cbdf43 1594
7aabd23e
DL
1595;; From old version.
1596(defalias 'fortran-auto-fill-mode 'auto-fill-mode)
b8cbdf43 1597
4254fe58 1598(defun fortran-fill ()
315aa1de 1599 (let* ((auto-fill-function #'fortran-auto-fill)
e04196d3 1600 (opoint (point))
7dae727d
DL
1601 (bol (line-beginning-position))
1602 (eol (line-end-position))
b8cbdf43
RS
1603 (bos (min eol (+ bol (fortran-current-line-indentation))))
1604 (quote
1605 (save-excursion
1606 (goto-char bol)
7dae727d 1607 (if (looking-at fortran-comment-line-start-skip)
b8cbdf43
RS
1608 nil ; OK to break quotes on comment lines.
1609 (move-to-column fill-column)
1eb6bf70
DL
1610 (if (fortran-is-in-string-p (point))
1611 (save-excursion (re-search-backward "\\S\"\\s\"\\S\"" bol t)
1612 (if fortran-break-before-delimiters
1613 (point)
1614 (1+ (point))))))))
b8cbdf43
RS
1615 ;; decide where to split the line. If a position for a quoted
1616 ;; string was found above then use that, else break the line
1617 ;; before the last delimiter.
eb8c3be9 1618 ;; Delimiters are whitespace, commas, and operators.
b8cbdf43 1619 ;; Will break before a pair of *'s.
b8cbdf43
RS
1620 (fill-point
1621 (or quote
1622 (save-excursion
1623 (move-to-column (1+ fill-column))
1624 (skip-chars-backward "^ \t\n,'+-/*=)"
1625;;; (if fortran-break-before-delimiters
1626;;; "^ \t\n,'+-/*=" "^ \t\n,'+-/*=)")
1627 )
1628 (if (<= (point) (1+ bos))
1629 (progn
1630 (move-to-column (1+ fill-column))
1eb6bf70 1631 ;;what is this doing???
b8cbdf43
RS
1632 (if (not (re-search-forward "[\t\n,'+-/*)=]" eol t))
1633 (goto-char bol))))
1634 (if (bolp)
1635 (re-search-forward "[ \t]" opoint t)
1eb6bf70
DL
1636 (backward-char)
1637 (if (looking-at "\\s\"")
1638 (forward-char)
b8cbdf43
RS
1639 (skip-chars-backward " \t\*")))
1640 (if fortran-break-before-delimiters
1641 (point)
1eb6bf70 1642 (1+ (point)))))))
b8cbdf43
RS
1643 ;; if we are in an in-line comment, don't break unless the
1644 ;; line of code is longer than it should be. Otherwise
1645 ;; break the line at the column computed above.
1646 ;;
23029d77 1647 ;; Need to use fortran-find-comment-start-skip to make sure that quoted !'s
b8cbdf43
RS
1648 ;; don't prevent a break.
1649 (if (not (or (save-excursion
315aa1de
DL
1650 (if (and comment-start-skip
1651 (re-search-backward comment-start-skip bol t)
23029d77 1652 (not (fortran-is-in-string-p (point))))
b8cbdf43
RS
1653 (progn
1654 (skip-chars-backward " \t")
1655 (< (current-column) (1+ fill-column)))))
1656 (save-excursion
1657 (goto-char fill-point)
1658 (bolp))))
315aa1de
DL
1659 (when (> (save-excursion
1660 (goto-char opoint)
1661 (current-column))
1662 (min (1+ fill-column)
1663 (+ (fortran-calculate-indent)
1664 fortran-continuation-indent)))
1665 (goto-char fill-point)
1666 (fortran-break-line)
1667 (end-of-line)))))
7dae727d 1668
b8cbdf43
RS
1669(defun fortran-break-line ()
1670 (let ((opoint (point))
7dae727d
DL
1671 (bol (line-beginning-position))
1672 (eol (line-end-position))
b8cbdf43 1673 (comment-string nil))
b8cbdf43 1674 (save-excursion
23029d77 1675 (if (and comment-start-skip (fortran-find-comment-start-skip))
b8cbdf43
RS
1676 (progn
1677 (re-search-backward comment-start-skip bol t)
1678 (setq comment-string (buffer-substring (point) eol))
1679 (delete-region (point) eol))))
1eb6bf70
DL
1680 ;; Forward line 1 really needs to go to next non white line
1681 (if (save-excursion (forward-line)
315aa1de 1682 (or (looking-at " \\{5\\}[^ 0\n]\\|\t[1-9]")))
b8cbdf43 1683 (progn
b8313955
RS
1684 (end-of-line)
1685 (delete-region (point) (match-end 0))
b8cbdf43 1686 (delete-horizontal-space)
4254fe58 1687 (fortran-fill))
b8cbdf43
RS
1688 (fortran-split-line))
1689 (if comment-string
1690 (save-excursion
1691 (goto-char bol)
1692 (end-of-line)
1693 (delete-horizontal-space)
315aa1de 1694 (indent-to (fortran-comment-indent))
b8cbdf43
RS
1695 (insert comment-string)))))
1696
23029d77 1697(defun fortran-analyze-file-format ()
7977773d 1698 "Return nil if fixed format is used, t if TAB formatting is used.
23029d77
JB
1699Use `fortran-tab-mode-default' if no non-comment statements are found in the
1700file before the end or the first `fortran-analyze-depth' lines."
1701 (let ((i 0))
1702 (save-excursion
1703 (goto-char (point-min))
1704 (setq i 0)
1705 (while (not (or
1706 (eobp)
7dae727d 1707 (eq (char-after) ?\t)
315aa1de 1708 (looking-at " \\{6\\}")
23029d77
JB
1709 (> i fortran-analyze-depth)))
1710 (forward-line)
1711 (setq i (1+ i)))
1712 (cond
7dae727d 1713 ((eq (char-after) ?\t) t)
315aa1de 1714 ((looking-at " \\{6\\}") nil)
23029d77
JB
1715 (fortran-tab-mode-default t)
1716 (t nil)))))
1717
1718(or (assq 'fortran-tab-mode-string minor-mode-alist)
1719 (setq minor-mode-alist (cons
1720 '(fortran-tab-mode-string
1721 (indent-tabs-mode fortran-tab-mode-string))
1722 minor-mode-alist)))
1723
1eb6bf70
DL
1724(defun fortran-fill-paragraph (&optional justify)
1725 "Fill surrounding comment block as paragraphs, else fill statement.
1eb6bf70
DL
1726Intended as the value of `fill-paragraph-function'."
1727 (interactive "P")
1728 (save-excursion
1729 (beginning-of-line)
68ca306c 1730 (if (not (looking-at fortran-comment-line-start-skip))
1eb6bf70 1731 (fortran-fill-statement)
68ca306c
DL
1732 ;; We're in a comment block. Find the start and end of a
1733 ;; paragraph, delimited either by non-comment lines or empty
1734 ;; comments. (Get positions as markers, since the
1735 ;; `indent-region' below can shift the block's end).
1736 (let* ((non-empty-comment
8b31236d 1737 (concat fortran-comment-line-start-skip "[^ \t\n]"))
68ca306c
DL
1738 (start (save-excursion
1739 ;; Find (start of) first line.
1740 (while (and (zerop (forward-line -1))
1741 (looking-at non-empty-comment)))
1742 (or (looking-at non-empty-comment)
1743 (forward-line)) ; overshot
1744 (point-marker)))
1745 (end (save-excursion
1746 ;; Find start of first line past region to fill.
1747 (while (progn
1748 (forward-line)
1749 (looking-at non-empty-comment)))
1750 (point-marker))))
1751 ;; Indent the block, find the string comprising the effective
1752 ;; comment start skip and use that as a fill-prefix for
1753 ;; filling the region.
1754 (indent-region start end nil)
1755 (let ((paragraph-ignore-fill-prefix nil)
1756 (fill-prefix (progn
1757 (beginning-of-line)
1758 (looking-at fortran-comment-line-start-skip)
1759 (match-string 0))))
1760 (let (fill-paragraph-function)
1761 (fill-region start end justify))) ; with normal `fill-paragraph'
1762 (set-marker start nil)
1763 (set-marker end nil))))
2d060beb 1764 t)
1eb6bf70
DL
1765
1766(defun fortran-fill-statement ()
1767 "Fill a fortran statement up to `fill-column'."
1768 (interactive)
315aa1de 1769 (let ((auto-fill-function #'fortran-auto-fill))
e04196d3
DL
1770 (if (not (save-excursion
1771 (beginning-of-line)
1772 (or (looking-at "[ \t]*$")
7dae727d 1773 (looking-at fortran-comment-line-start-skip)
e04196d3 1774 (and comment-start-skip
315aa1de
DL
1775 (looking-at (concat "[ \t]*"
1776 comment-start-skip))))))
e04196d3
DL
1777 (save-excursion
1778 ;; Find beginning of statement.
1779 (fortran-next-statement)
1780 (fortran-previous-statement)
1781 ;; Re-indent initially.
1782 (fortran-indent-line)
1783 ;; Replace newline plus continuation field plus indentation with
1784 ;; single space.
1785 (while (progn
1786 (forward-line)
1787 (fortran-remove-continuation)))
1788 (fortran-previous-statement)))
1789 (fortran-indent-line)))
1eb6bf70 1790
562e00da 1791(defun fortran-strip-sqeuence-nos (&optional do-space)
b9fe3dc8
RS
1792 "Delete all text in column 72 and up (assumed to be sequence numbers).
1793Normally also deletes trailing whitespace after stripping such text.
1794Supplying prefix arg DO-SPACE prevents stripping the whitespace."
3b4613a4
DL
1795 (interactive "p")
1796 (save-excursion
1797 (goto-char (point-min))
562e00da 1798 (while (re-search-forward "^.\\{72\\}\\(.*\\)" nil t)
3b4613a4
DL
1799 (replace-match "" nil nil nil 1)
1800 (unless do-space (delete-horizontal-space)))))
1801
68ca306c
DL
1802;; This code used to live in add-log.el, but this is a better place
1803;; for it.
1804(defun fortran-current-defun ()
1805 "Function to use for `add-log-current-defun-function' in Fortran mode."
1806 ;; We must be inside function body for this to work.
1807 (fortran-beginning-of-subprogram)
1808 (let ((case-fold-search t)) ; case-insensitive
1809 ;; search for fortran subprogram start
1810 (if (re-search-forward
315aa1de
DL
1811 (concat "^[ \t]*\\(program\\|subroutine\\|function"
1812 "\\|[ \ta-z0-9*()]*[ \t]+function\\|"
1813 "\\(block[ \t]*data\\)\\)")
68ca306c
DL
1814 (save-excursion (fortran-end-of-subprogram)
1815 (point))
1816 t)
1817 (or (match-string-no-properties 2)
1818 (progn
1819 ;; move to EOL or before first left paren
1820 (if (re-search-forward "[(\n]" nil t)
1821 (progn (backward-char)
1822 (skip-chars-backward " \t"))
1823 (end-of-line))
1824 ;; Use the name preceding that.
1825 (buffer-substring-no-properties (point) (progn (backward-sexp)
1826 (point)))))
1827 "main")))
1828
49116ac0
JB
1829(provide 'fortran)
1830
1a06eabd 1831;;; fortran.el ends here