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