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