Use push, with-current-buffer, dolist, ...
[bpt/emacs.git] / lisp / progmodes / f90.el
CommitLineData
be010748 1;;; f90.el --- Fortran-90 mode (free format)
b578f267 2
d2d15846 3;; Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
034a9d40 4
88ea9ddc 5;; Author: Torbj\"orn Einarsson <Torbjorn.Einarsson@era.ericsson.se>
b125e643 6;; Maintainer: Glenn Morris <gmorris@ast.cam.ac.uk>
034a9d40
RS
7;; Keywords: fortran, f90, languages
8
b578f267
EN
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
034a9d40 12;; it under the terms of the GNU General Public License as published by
b578f267
EN
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
034a9d40 15
b578f267 16;; GNU Emacs is distributed in the hope that it will be useful,
034a9d40
RS
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.
034a9d40
RS
25
26;;; Commentary:
b578f267 27
87ee2359
GM
28;; Major mode for editing F90 programs in FREE FORMAT.
29;; The minor language revision F95 is also supported (with font-locking).
30
034a9d40 31;; Knows about continuation lines, named structured statements, and other
87ee2359
GM
32;; features in F90 including HPF (High Performance Fortran) structures.
33;; The basic feature provides accurate indentation of F90 programs.
034a9d40
RS
34;; In addition, there are many more features like automatic matching of all
35;; end statements, an auto-fill function to break long lines, a join-lines
87ee2359
GM
36;; function which joins continued lines, etc.
37
38;; To facilitate typing, a fairly complete list of abbreviations is provided.
39;; All abbreviations begin with the backquote character "`"
40;; (this requires modification of the syntax-table).
41;; For example, `i expands to integer (if abbrev-mode is on).
034a9d40 42
87ee2359 43;; There are two separate features for altering the appearance of code:
034a9d40 44;; 1) Upcasing or capitalizing of all keywords.
87ee2359
GM
45;; 2) Colors/fonts using font-lock-mode.
46;; Automatic upcase or downcase of keywords is controlled by the variable
47;; f90-auto-keyword-case.
034a9d40
RS
48
49;; The indentations of lines starting with ! is determined by the first of the
87ee2359 50;; following matches (values in the left column are the defaults):
ee30478d
KH
51
52;; start-string/regexp indent variable holding start-string/regexp
53;; !!! 0
54;; !hpf\\$ (re) 0 f90-directive-comment-re
55;; !!$ 0 f90-comment-region
56;; ! (re) as code f90-indented-comment-re
57;; default comment-column
58
59;; Ex: Here is the result of 3 different settings of f90-indented-comment-re
60;; f90-indented-comment-re !-indentation !!-indentation
61;; ! as code as code
62;; !! comment-column as code
63;; ![^!] as code comment-column
87ee2359
GM
64;; Trailing comments are indented to comment-column with indent-for-comment.
65;; The function f90-comment-region toggles insertion of
66;; the variable f90-comment-region in every line of the region.
034a9d40
RS
67
68;; One common convention for free vs. fixed format is that free-format files
69658465 69;; have the ending .f90 or .f95 while fixed format files have the ending .f.
87ee2359
GM
70;; Emacs automatically loads Fortran files in the appropriate mode based
71;; on extension. You can modify this by adjusting the variable auto-mode-alist.
72;; For example:
73;; (add-to-list 'auto-mode-alist '("\\.f\\'" . f90-mode))
74
034a9d40 75;; Once you have entered f90-mode, you may get more info by using
69658465 76;; the command describe-mode (C-h m). For online help use
87ee2359
GM
77;; C-h f <Name of function you want described>, or
78;; C-h v <Name of variable you want described>.
034a9d40 79
87ee2359
GM
80;; To customize f90-mode for your taste, use, for example:
81;; (you don't have to specify values for all the parameters below)
82;;
d2d15846 83;;(add-hook 'f90-mode-hook
87ee2359 84;; ;; These are the default values.
034a9d40
RS
85;; '(lambda () (setq f90-do-indent 3
86;; f90-if-indent 3
87;; f90-type-indent 3
88;; f90-program-indent 2
89;; f90-continuation-indent 5
90;; f90-comment-region "!!$"
ee30478d
KH
91;; f90-directive-comment-re "!hpf\\$"
92;; f90-indented-comment-re "!"
87ee2359 93;; f90-break-delimiters "[-+\\*/><=,% \t]"
034a9d40
RS
94;; f90-break-before-delimiters t
95;; f90-beginning-ampersand t
96;; f90-smart-end 'blink
97;; f90-auto-keyword-case nil
87ee2359 98;; f90-leave-line-no nil
72e80cad 99;; indent-tabs-mode nil
b974df0a 100;; f90-font-lock-keywords f90-font-lock-keywords-2
72e80cad 101;; )
87ee2359 102;; ;; These are not default.
034a9d40 103;; (abbrev-mode 1) ; turn on abbreviation mode
b974df0a 104;; (f90-add-imenu-menu) ; extra menu with functions etc.
034a9d40
RS
105;; (if f90-auto-keyword-case ; change case of all keywords on startup
106;; (f90-change-keywords f90-auto-keyword-case))
107;; ))
87ee2359
GM
108;;
109;; in your .emacs file. You can also customize the lists
110;; f90-font-lock-keywords, etc.
111;;
112;; The auto-fill and abbreviation minor modes are accessible from the F90 menu,
b974df0a 113;; or by using M-x auto-fill-mode and M-x abbrev-mode, respectively.
034a9d40
RS
114
115;; Remarks
116;; 1) Line numbers are by default left-justified. If f90-leave-line-no is
117;; non-nil, the line numbers are never touched.
87ee2359 118;; 2) Multi-; statements like "do i=1,20 ; j=j+i ; end do" are not handled
034a9d40 119;; correctly, but I imagine them to be rare.
ee30478d 120;; 3) Regexps for hilit19 are no longer supported.
87ee2359 121;; 4) For FIXED FORMAT code, use fortran mode.
034a9d40 122;; 5) This mode does not work under emacs-18.x.
72e80cad
KH
123;; 6) Preprocessor directives, i.e., lines starting with # are left-justified
124;; and are untouched by all case-changing commands. There is, at present, no
125;; mechanism for treating multi-line directives (continued by \ ).
ee30478d
KH
126;; 7) f77 do-loops do 10 i=.. ; ; 10 continue are not correctly indented.
127;; You are urged to use f90-do loops (with labels if you wish).
c80718cc 128;; 8) The highlighting mode under XEmacs is not as complete as under Emacs.
034a9d40
RS
129
130;; List of user commands
131;; f90-previous-statement f90-next-statement
132;; f90-beginning-of-subprogram f90-end-of-subprogram f90-mark-subprogram
133;; f90-comment-region
134;; f90-indent-line f90-indent-new-line
135;; f90-indent-region (can be called by calling indent-region)
136;; f90-indent-subprogram
137;; f90-break-line f90-join-lines
034a9d40
RS
138;; f90-fill-region
139;; f90-insert-end
140;; f90-upcase-keywords f90-upcase-region-keywords
141;; f90-downcase-keywords f90-downcase-region-keywords
142;; f90-capitalize-keywords f90-capitalize-region-keywords
b974df0a
EN
143;; f90-add-imenu-menu
144;; f90-font-lock-1, f90-font-lock-2, f90-font-lock-3, f90-font-lock-4
034a9d40 145
87ee2359 146;; Original author's thanks
034a9d40
RS
147;; Thanks to all the people who have tested the mode. Special thanks to Jens
148;; Bloch Helmers for encouraging me to write this code, for creative
149;; suggestions as well as for the lists of hpf-commands.
150;; Also thanks to the authors of the fortran and pascal modes, on which some
151;; of this code is built.
152
87ee2359
GM
153;; TODO
154;; Support for hideshow, align.
155;; OpenMP, preprocessor highlighting.
156
034a9d40 157;;; Code:
b578f267 158
34ba7e3d
GM
159(defvar comment-auto-fill-only-comments)
160(defvar font-lock-keywords)
0ee7f068 161
034a9d40 162;; User options
034a9d40 163
fcad5199 164(defgroup f90 nil
87ee2359 165 "Major mode for editing Fortran 90,95 code."
d2d15846 166 :group 'languages)
034a9d40 167
fcad5199 168(defgroup f90-indent nil
87ee2359 169 "Indentation in free-format Fortran."
fcad5199
RS
170 :prefix "f90-"
171 :group 'f90)
034a9d40 172
034a9d40 173
fcad5199
RS
174(defcustom f90-do-indent 3
175 "*Extra indentation applied to DO blocks."
176 :type 'integer
177 :group 'f90-indent)
034a9d40 178
fcad5199
RS
179(defcustom f90-if-indent 3
180 "*Extra indentation applied to IF, SELECT CASE, WHERE and FORALL blocks."
181 :type 'integer
182 :group 'f90-indent)
034a9d40 183
fcad5199
RS
184(defcustom f90-type-indent 3
185 "*Extra indentation applied to TYPE, INTERFACE and BLOCK DATA blocks."
186 :type 'integer
187 :group 'f90-indent)
034a9d40 188
fcad5199
RS
189(defcustom f90-program-indent 2
190 "*Extra indentation applied to PROGRAM/MODULE/SUBROUTINE/FUNCTION blocks."
191 :type 'integer
192 :group 'f90-indent)
034a9d40 193
fcad5199
RS
194(defcustom f90-continuation-indent 5
195 "*Extra indentation applied to F90 continuation lines."
196 :type 'integer
197 :group 'f90-indent)
034a9d40 198
fcad5199 199(defcustom f90-comment-region "!!$"
87ee2359 200 "*String inserted by \\[f90-comment-region] at start of each line in region."
fcad5199
RS
201 :type 'string
202 :group 'f90-indent)
203
204(defcustom f90-indented-comment-re "!"
87ee2359 205 "*Regexp saying which comments to indent like code."
fcad5199
RS
206 :type 'regexp
207 :group 'f90-indent)
208
209(defcustom f90-directive-comment-re "!hpf\\$"
210 "*Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented."
211 :type 'regexp
212 :group 'f90-indent)
213
214(defcustom f90-beginning-ampersand t
87ee2359 215 "*Non-nil gives automatic insertion of \& at start of continuation line."
fcad5199
RS
216 :type 'boolean
217 :group 'f90)
218
219(defcustom f90-smart-end 'blink
034a9d40
RS
220 "*From an END statement, check and fill the end using matching block start.
221Allowed values are 'blink, 'no-blink, and nil, which determine
fcad5199
RS
222whether to blink the matching beginning."
223 :type '(choice (const blink) (const no-blink) (const nil))
224 :group 'f90)
034a9d40 225
fcad5199
RS
226(defcustom f90-break-delimiters "[-+\\*/><=,% \t]"
227 "*Regexp holding list of delimiters at which lines may be broken."
228 :type 'regexp
229 :group 'f90)
034a9d40 230
fcad5199
RS
231(defcustom f90-break-before-delimiters t
232 "*Non-nil causes `f90-do-auto-fill' to break lines before delimiters."
d9d41ec6 233 :type 'boolean
fcad5199 234 :group 'f90)
034a9d40 235
fcad5199 236(defcustom f90-auto-keyword-case nil
034a9d40 237 "*Automatic case conversion of keywords.
87ee2359 238The options are 'downcase-word, 'upcase-word, 'capitalize-word and nil."
fcad5199
RS
239 :type '(choice (const downcase-word) (const upcase-word)
240 (const capitalize-word) (const nil))
241 :group 'f90)
242
243(defcustom f90-leave-line-no nil
87ee2359 244 "*If non-nil, line numbers are not left justified."
fcad5199
RS
245 :type 'boolean
246 :group 'f90)
247
0ee7f068
GM
248(defcustom f90-mode-hook nil
249 "Hook run when entering F90 mode."
250 :type 'hook
251 :options '(f90-add-imenu-menu)
252 :group 'f90)
253
254;; User options end here.
1bb3ae5c 255
ee30478d 256(defconst f90-keywords-re
84021009
SM
257 (regexp-opt '("allocatable" "allocate" "assign" "assignment" "backspace"
258 "block" "call" "case" "character" "close" "common" "complex"
259 "contains" "continue" "cycle" "data" "deallocate"
260 "dimension" "do" "double" "else" "elseif" "elsewhere" "end"
261 "enddo" "endfile" "endif" "entry" "equivalence" "exit"
262 "external" "forall" "format" "function" "goto" "if"
263 "implicit" "include" "inquire" "integer" "intent"
264 "interface" "intrinsic" "logical" "module" "namelist" "none"
265 "nullify" "only" "open" "operator" "optional" "parameter"
266 "pause" "pointer" "precision" "print" "private" "procedure"
267 "program" "public" "read" "real" "recursive" "result" "return"
268 "rewind" "save" "select" "sequence" "stop" "subroutine"
269 "target" "then" "type" "use" "where" "while" "write"
270 ;; F95 keywords.
271 "elemental" "pure") 'words)
ee30478d
KH
272 "Regexp for F90 keywords.")
273
274(defconst f90-keywords-level-3-re
84021009
SM
275 (regexp-opt
276 '("allocatable" "allocate" "assign" "assignment" "backspace"
277 "close" "deallocate" "dimension" "endfile" "entry" "equivalence"
278 "external" "inquire" "intent" "intrinsic" "nullify" "only" "open"
279 "operator" "optional" "parameter" "pause" "pointer" "print" "private"
280 "public" "read" "recursive" "result" "rewind" "save" "select"
281 "sequence" "target" "write"
282 ;; F95 keywords.
283 "elemental" "pure") 'words)
284 "Keyword-regexp for font-lock level >= 3.")
ee30478d 285
ee30478d 286(defconst f90-procedures-re
84021009 287 (concat "\\<"
69658465
GM
288 (regexp-opt
289 '("abs" "achar" "acos" "adjustl" "adjustr" "aimag" "aint"
290 "all" "allocated" "anint" "any" "asin" "associated"
291 "atan" "atan2" "bit_size" "btest" "ceiling" "char" "cmplx"
292 "conjg" "cos" "cosh" "count" "cshift" "date_and_time" "dble"
293 "digits" "dim" "dot_product" "dprod" "eoshift" "epsilon"
294 "exp" "exponent" "floor" "fraction" "huge" "iachar" "iand"
295 "ibclr" "ibits" "ibset" "ichar" "ieor" "index" "int" "ior"
296 "ishft" "ishftc" "kind" "lbound" "len" "len_trim" "lge" "lgt"
297 "lle" "llt" "log" "log10" "logical" "matmul" "max"
298 "maxexponent" "maxloc" "maxval" "merge" "min" "minexponent"
299 "minloc" "minval" "mod" "modulo" "mvbits" "nearest" "nint"
300 "not" "pack" "precision" "present" "product" "radix"
301 ;; Real is taken out here to avoid highlighting declarations.
302 "random_number" "random_seed" "range" ;; "real"
303 "repeat" "reshape" "rrspacing" "scale" "scan"
304 "selected_int_kind" "selected_real_kind" "set_exponent"
305 "shape" "sign" "sin" "sinh" "size" "spacing" "spread" "sqrt"
306 "sum" "system_clock" "tan" "tanh" "tiny" "transfer"
307 "transpose" "trim" "ubound" "unpack" "verify"
308 ;; F95 intrinsic functions.
309 "null" "cpu_time") t)
310 ;; A left parenthesis to avoid highlighting non-procedures.
311 "[ \t]*(")
ee30478d
KH
312 "Regexp whose first part matches F90 intrinsic procedures.")
313
314(defconst f90-operators-re
69658465
GM
315 (concat "\\."
316 (regexp-opt '("and" "eq" "eqv" "false" "ge" "gt" "le" "lt" "ne"
317 "neqv" "not" "or" "true") t)
318 "\\.")
ee30478d
KH
319 "Regexp matching intrinsic operators.")
320
321(defconst f90-hpf-keywords-re
84021009 322 (regexp-opt
ec2f376f 323 ;; Intrinsic procedures.
84021009
SM
324 '("all_prefix" "all_scatter" "all_suffix" "any_prefix"
325 "any_scatter" "any_suffix" "copy_prefix" "copy_scatter"
326 "copy_suffix" "count_prefix" "count_scatter" "count_suffix"
327 "grade_down" "grade_up"
328 "hpf_alignment" "hpf_distribution" "hpf_template" "iall" "iall_prefix"
329 "iall_scatter" "iall_suffix" "iany" "iany_prefix" "iany_scatter"
330 "iany_suffix" "ilen" "iparity" "iparity_prefix"
331 "iparity_scatter" "iparity_suffix" "leadz" "maxval_prefix"
332 "maxval_scatter" "maxval_suffix" "minval_prefix" "minval_scatter"
333 "minval_suffix" "number_of_processors" "parity"
334 "parity_prefix" "parity_scatter" "parity_suffix" "popcnt" "poppar"
335 "processors_shape" "product_prefix" "product_scatter"
336 "product_suffix" "sum_prefix" "sum_scatter" "sum_suffix"
ec2f376f 337 ;; Directives.
84021009 338 "align" "distribute" "dynamic" "independent" "inherit" "processors"
69658465 339 "realign" "redistribute" "template"
ec2f376f 340 ;; Keywords.
84021009 341 "block" "cyclic" "extrinsic" "new" "onto" "pure" "with") 'words)
ee30478d 342 "Regexp for all HPF keywords, procedures and directives.")
034a9d40 343
ec2f376f 344;; Highlighting patterns.
034a9d40 345
ee30478d 346(defvar f90-font-lock-keywords-1
45d1e4d4 347 (list
ec2f376f 348 ;; Special highlighting of "module procedure".
84021009 349 '("\\<\\(module[ \t]*procedure\\)\\>" (1 font-lock-keyword-face))
eb9f0295
GM
350 ;; Highlight definition of derived type.
351 '("\\<\\(\\(?:end[ \t]*\\)?type\\)\\>\\([^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
6fb453e1 352 (1 font-lock-keyword-face) (3 font-lock-function-name-face))
84021009 353 ;; Other functions and declarations.
69658465 354 '("\\<\\(\\(?:end[ \t]*\\)?\\(program\\|module\\|function\\|\
6fb453e1 355subroutine\\)\\|use\\|call\\)\\>[ \t]*\\(\\sw+\\)?"
84021009 356 (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
b974df0a 357 "\\<\\(\\(end[ \t]*\\)?\\(interface\\|block[ \t]*data\\)\\|contains\\)\\>")
ee30478d
KH
358 "This does fairly subdued highlighting of comments and function calls.")
359
360(defvar f90-font-lock-keywords-2
69658465
GM
361 (append
362 f90-font-lock-keywords-1
363 (list
ec2f376f 364 ;; Variable declarations (avoid the real function call).
69658465
GM
365 '("^[ \t0-9]*\\(real\\|integer\\|c\\(haracter\\|omplex\\)\\|\
366logical\\|type[ \t]*(\\sw+)\\)\\(.*::\\|[ \t]*(.*)\\)?\\([^!\n]*\\)"
367 (1 font-lock-type-face t) (4 font-lock-variable-name-face))
ec2f376f 368 ;; do, if, select, where, and forall constructs.
69658465
GM
369 '("\\<\\(end[ \t]*\\(do\\|if\\|select\\|forall\\|where\\)\\)\\>\
370\\([ \t]+\\(\\sw+\\)\\)?"
371 (1 font-lock-keyword-face) (3 font-lock-constant-face nil t))
372 '("^[ \t0-9]*\\(\\(\\sw+\\)[ \t]*:[ \t]*\\)?\\(\\(if\\|\
373do\\([ \t]*while\\)?\\|select[ \t]*case\\|where\\|forall\\)\\)\\>"
374 (2 font-lock-constant-face nil t) (3 font-lock-keyword-face))
ec2f376f 375 ;; Implicit declaration.
69658465 376 '("\\<\\(implicit\\)[ \t]*\\(real\\|integer\\|c\\(haracter\\|omplex\\)\
6fb453e1 377\\|logical\\|type[ \t]*(\\sw+)\\|none\\)[ \t]*"
69658465
GM
378 (1 font-lock-keyword-face) (2 font-lock-type-face))
379 '("\\<\\(namelist\\|common\\)[ \t]*\/\\(\\sw+\\)?\/"
380 (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
381 "\\<else\\([ \t]*if\\|where\\)?\\>"
382 "\\<\\(then\\|continue\\|format\\|include\\|stop\\|return\\)\\>"
383 '("\\<\\(exit\\|cycle\\)[ \t]*\\(\\sw+\\)?\\>"
384 (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
385 '("\\<\\(case\\)[ \t]*\\(default\\|(\\)" . 1)
386 '("\\<\\(do\\|go *to\\)\\>[ \t]*\\([0-9]+\\)"
387 (1 font-lock-keyword-face) (2 font-lock-constant-face))
f14ca250 388 ;; Line numbers (lines whose first character after number is letter).
69658465 389 '("^[ \t]*\\([0-9]+\\)[ \t]*[a-z]+" (1 font-lock-constant-face t))))
87ee2359 390 "Highlights declarations, do-loops and other constructs.")
ee30478d
KH
391
392(defvar f90-font-lock-keywords-3
393 (append f90-font-lock-keywords-2
69658465
GM
394 (list
395 f90-keywords-level-3-re
396 f90-operators-re
397 (list f90-procedures-re '(1 font-lock-keyword-face keep))
ec2f376f 398 "\\<real\\>" ; avoid overwriting real defs
69658465 399 ))
ee30478d
KH
400 "Highlights all F90 keywords and intrinsic procedures.")
401
402(defvar f90-font-lock-keywords-4
403 (append f90-font-lock-keywords-3
69658465 404 (list f90-hpf-keywords-re))
ee30478d
KH
405 "Highlights all F90 and HPF keywords.")
406
407(defvar f90-font-lock-keywords
87ee2359 408 f90-font-lock-keywords-2
0ee7f068
GM
409 "*Default expressions to highlight in F90 mode.
410Can be overridden by the value of `font-lock-maximum-decoration'.")
034a9d40 411
ec2f376f 412
70186f7f
GM
413(defvar f90-mode-syntax-table
414 (let ((table (make-syntax-table)))
415 (modify-syntax-entry ?\! "<" table) ; begin comment
416 (modify-syntax-entry ?\n ">" table) ; end comment
417 (modify-syntax-entry ?_ "w" table) ; underscore in names
418 (modify-syntax-entry ?\' "\"" table) ; string quote
419 (modify-syntax-entry ?\" "\"" table) ; string quote
420 (modify-syntax-entry ?\` "w" table) ; for abbrevs
421 (modify-syntax-entry ?\r " " table) ; return is whitespace
422 (modify-syntax-entry ?+ "." table) ; punctuation
423 (modify-syntax-entry ?- "." table)
424 (modify-syntax-entry ?= "." table)
425 (modify-syntax-entry ?* "." table)
426 (modify-syntax-entry ?/ "." table)
427 (modify-syntax-entry ?\\ "\\" table) ; escape chars
428 table)
429 "Syntax table used in F90 mode.")
430
431(defvar f90-mode-map
432 (let ((map (make-sparse-keymap)))
433 (define-key map "`" 'f90-abbrev-start)
434 (define-key map "\C-c;" 'f90-comment-region)
435 (define-key map "\C-\M-a" 'f90-beginning-of-subprogram)
436 (define-key map "\C-\M-e" 'f90-end-of-subprogram)
437 (define-key map "\C-\M-h" 'f90-mark-subprogram)
6f1d50da
GM
438 (define-key map "\C-\M-n" 'f90-end-of-block)
439 (define-key map "\C-\M-p" 'f90-beginning-of-block)
70186f7f
GM
440 (define-key map "\C-\M-q" 'f90-indent-subprogram)
441 (define-key map "\C-j" 'f90-indent-new-line) ; LFD equals C-j
442 (define-key map "\r" 'newline)
443 (define-key map "\C-c\r" 'f90-break-line)
444;;; (define-key map [M-return] 'f90-break-line)
76bccf35
GM
445 (define-key map "\C-c\C-a" 'f90-previous-block)
446 (define-key map "\C-c\C-e" 'f90-next-block)
70186f7f
GM
447 (define-key map "\C-c\C-d" 'f90-join-lines)
448 (define-key map "\C-c\C-f" 'f90-fill-region)
449 (define-key map "\C-c\C-p" 'f90-previous-statement)
450 (define-key map "\C-c\C-n" 'f90-next-statement)
451 (define-key map "\C-c\C-w" 'f90-insert-end)
452 (define-key map "\t" 'f90-indent-line)
453 (define-key map "," 'f90-electric-insert)
454 (define-key map "+" 'f90-electric-insert)
455 (define-key map "-" 'f90-electric-insert)
456 (define-key map "*" 'f90-electric-insert)
457 (define-key map "/" 'f90-electric-insert)
458 map)
034a9d40 459 "Keymap used in F90 mode.")
ee30478d 460
b974df0a 461
1a341882
GM
462(easy-menu-define f90-menu f90-mode-map "Menu for F90 mode."
463 '("F90"
464 ["Indent Subprogram" f90-indent-subprogram t]
465 ["Mark Subprogram" f90-mark-subprogram t]
466 ["Beginning of Subprogram" f90-beginning-of-subprogram t]
467 ["End of Subprogram" f90-end-of-subprogram t]
468 "--"
469 ["(Un)Comment Region" f90-comment-region mark-active]
470 ["Indent Region" f90-indent-region mark-active]
471 ["Fill Region" f90-fill-region mark-active]
472 "--"
473 ["Break Line at Point" f90-break-line t]
474 ["Join with Next Line" f90-join-lines t]
475 ["Insert Block End" f90-insert-end t]
476 "--"
477 ("Highlighting"
34ba7e3d
GM
478 ["Toggle font-lock-mode" font-lock-mode :selected font-lock-mode
479 :style toggle]
1a341882
GM
480 "--"
481 ["Light highlighting (level 1)" f90-font-lock-1 t]
482 ["Moderate highlighting (level 2)" f90-font-lock-2 t]
483 ["Heavy highlighting (level 3)" f90-font-lock-3 t]
484 ["Maximum highlighting (level 4)" f90-font-lock-4 t]
485 )
486 ("Change Keyword Case"
487 ["Upcase Keywords (buffer)" f90-upcase-keywords t]
488 ["Capitalize Keywords (buffer)" f90-capitalize-keywords t]
489 ["Downcase Keywords (buffer)" f90-downcase-keywords t]
490 "--"
491 ["Upcase Keywords (region)" f90-upcase-region-keywords mark-active]
492 ["Capitalize Keywords (region)" f90-capitalize-region-keywords
493 mark-active]
494 ["Downcase Keywords (region)" f90-downcase-region-keywords mark-active]
495 )
496 "--"
34ba7e3d 497 ["Toggle auto-fill" auto-fill-mode :selected auto-fill-function
1a341882 498 :style toggle]
34ba7e3d 499 ["Toggle abbrev-mode" abbrev-mode :selected abbrev-mode :style toggle]
901e8d1d 500 ["Add imenu Menu" f90-add-imenu-menu
34ba7e3d
GM
501 :active (not (lookup-key (current-local-map) [menu-bar index]))
502 :included (fboundp 'imenu-add-to-menubar)]
1a341882
GM
503 ))
504
505(defun f90-font-lock-1 ()
506 "Set `font-lock-keywords' to `f90-font-lock-keywords-1'."
507 (interactive)
508 (font-lock-mode 1)
509 (setq font-lock-keywords f90-font-lock-keywords-1)
510 (font-lock-fontify-buffer))
511
512(defun f90-font-lock-2 ()
513 "Set `font-lock-keywords' to `f90-font-lock-keywords-2'."
514 (interactive)
515 (font-lock-mode 1)
516 (setq font-lock-keywords f90-font-lock-keywords-2)
517 (font-lock-fontify-buffer))
518
519(defun f90-font-lock-3 ()
520 "Set `font-lock-keywords' to `f90-font-lock-keywords-3'."
521 (interactive)
522 (font-lock-mode 1)
523 (setq font-lock-keywords f90-font-lock-keywords-3)
524 (font-lock-fontify-buffer))
525
526(defun f90-font-lock-4 ()
527 "Set `font-lock-keywords' to `f90-font-lock-keywords-4'."
528 (interactive)
529 (font-lock-mode 1)
530 (setq font-lock-keywords f90-font-lock-keywords-4)
531 (font-lock-fontify-buffer))
532
533\f
ee30478d 534;; Regexps for finding program structures.
69658465 535(defconst f90-blocks-re
ec2f376f
GM
536 (concat "\\(block[ \t]*data\\|"
537 (regexp-opt '("do" "if" "interface" "function" "module" "program"
538 "select" "subroutine" "type" "where" "forall"))
539 "\\)\\>")
540 "Regexp potentially indicating a \"block\" of F90 code.")
541
69658465 542(defconst f90-program-block-re
ec2f376f
GM
543 (regexp-opt '("program" "module" "subroutine" "function") 'paren)
544 "Regexp used to locate the start/end of a \"subprogram\".")
545
69658465 546(defconst f90-else-like-re
ec2f376f
GM
547 "\\(else\\([ \t]*if\\|where\\)?\\|case[ \t]*\\(default\\|(\\)\\)"
548 "Regexp matching an ELSE IF, ELSEWHERE, CASE statement.")
549
69658465 550(defconst f90-end-if-re
ec2f376f
GM
551 (concat "end[ \t]*"
552 (regexp-opt '("if" "select" "where" "forall") 'paren)
553 "\\>")
554 "Regexp matching the end of an IF, SELECT, WHERE, FORALL block.")
555
69658465 556(defconst f90-end-type-re
ec2f376f
GM
557 "end[ \t]*\\(type\\|interface\\|block[ \t]*data\\)\\>"
558 "Regexp matching the end of a TYPE, INTERFACE, BLOCK DATA section.")
559
ee30478d 560(defconst f90-type-def-re
eb9f0295 561 "\\<\\(type\\)\\>\\(?:[^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
e7272ece 562 "Regexp matching the definition of a derived type.")
ec2f376f
GM
563
564(defconst f90-no-break-re
565 (regexp-opt '("**" "//" "=>") 'paren)
566 "Regexp specifying where not to break lines when filling.")
567
568(defvar f90-cache-position nil
569 "Temporary position used to speed up region operations.")
034a9d40 570(make-variable-buffer-local 'f90-cache-position)
ec2f376f 571
b974df0a 572\f
ec2f376f 573;; Imenu support.
ee30478d 574(defvar f90-imenu-generic-expression
b974df0a
EN
575 (let ((good-char "[^!\"\&\n \t]") (not-e "[^e!\n\"\& \t]")
576 (not-n "[^n!\n\"\& \t]") (not-d "[^d!\n\"\& \t]"))
577 (list
578 '(nil "^[ \t0-9]*program[ \t]+\\(\\sw+\\)" 1)
579 '("Modules" "^[ \t0-9]*module[ \t]+\\(\\sw+\\)[ \t]*\\(!\\|$\\)" 1)
580 '("Types" "^[ \t0-9]*type[ \t]+\\(\\sw+\\)" 1)
581 (list
69658465 582 "Procedures"
b974df0a
EN
583 (concat
584 "^[ \t0-9]*"
585 "\\("
ec2f376f
GM
586 ;; At least three non-space characters before function/subroutine.
587 ;; Check that the last three non-space characters do not spell E N D.
b974df0a
EN
588 "[^!\"\&\n]*\\("
589 not-e good-char good-char "\\|"
590 good-char not-n good-char "\\|"
591 good-char good-char not-d "\\)"
592 "\\|"
ec2f376f 593 ;; Less than three non-space characters before function/subroutine.
b974df0a
EN
594 good-char "?" good-char "?"
595 "\\)"
596 "[ \t]*\\(function\\|subroutine\\)[ \t]+\\(\\sw+\\)")
69658465 597 4)))
87ee2359 598 "Generic imenu expression for F90 mode.")
ee30478d 599
b974df0a 600(defun f90-add-imenu-menu ()
b974df0a 601 "Add an imenu menu to the menubar."
87ee2359 602 (interactive)
34ba7e3d 603 (if (lookup-key (current-local-map) [menu-bar index])
5c2a80ad
GM
604 (message "%s" "F90-imenu already exists.")
605 (imenu-add-to-menubar "F90-imenu")
901e8d1d 606 (redraw-frame (selected-frame))))
b974df0a 607
034a9d40 608\f
ec2f376f 609;; Abbrevs have generally two letters, except standard types `c, `i, `r, `t.
4f9fc702
GM
610(defvar f90-mode-abbrev-table
611 (let (abbrevs-changed)
0ee7f068
GM
612 (define-abbrev-table 'f90-mode-abbrev-table nil)
613 ;; Use the 6th arg (SYSTEM-FLAG) of define-abbrev if possible.
614 ;; A little baroque to quieten the byte-compiler.
615 (mapcar
616 (function (lambda (element)
617 (condition-case nil
618 (apply 'define-abbrev f90-mode-abbrev-table
619 (append element '(nil 0 t)))
620 (wrong-number-of-arguments
621 (apply 'define-abbrev f90-mode-abbrev-table
622 (append element '(nil 0)))))))
623 '(("`al" "allocate" )
624 ("`ab" "allocatable" )
625 ("`as" "assignment" )
626 ("`ba" "backspace" )
627 ("`bd" "block data" )
628 ("`c" "character" )
629 ("`cl" "close" )
630 ("`cm" "common" )
631 ("`cx" "complex" )
632 ("`cn" "contains" )
633 ("`cy" "cycle" )
634 ("`de" "deallocate" )
635 ("`df" "define" )
636 ("`di" "dimension" )
637 ("`dw" "do while" )
638 ("`el" "else" )
639 ("`eli" "else if" )
640 ("`elw" "elsewhere" )
641 ("`eq" "equivalence" )
642 ("`ex" "external" )
643 ("`ey" "entry" )
644 ("`fl" "forall" )
645 ("`fo" "format" )
646 ("`fu" "function" )
647 ("`fa" ".false." )
648 ("`im" "implicit none")
649 ("`in" "include" )
650 ("`i" "integer" )
651 ("`it" "intent" )
652 ("`if" "interface" )
653 ("`lo" "logical" )
654 ("`mo" "module" )
655 ("`na" "namelist" )
656 ("`nu" "nullify" )
657 ("`op" "optional" )
658 ("`pa" "parameter" )
659 ("`po" "pointer" )
660 ("`pr" "print" )
661 ("`pi" "private" )
662 ("`pm" "program" )
663 ("`pu" "public" )
664 ("`r" "real" )
665 ("`rc" "recursive" )
666 ("`rt" "return" )
667 ("`rw" "rewind" )
668 ("`se" "select" )
669 ("`sq" "sequence" )
670 ("`su" "subroutine" )
671 ("`ta" "target" )
672 ("`tr" ".true." )
673 ("`t" "type" )
674 ("`wh" "where" )
675 ("`wr" "write" )))
4f9fc702
GM
676 f90-mode-abbrev-table)
677 "Abbrev table for F90 mode.")
034a9d40 678\f
d2d15846 679
034a9d40
RS
680;;;###autoload
681(defun f90-mode ()
87ee2359 682 "Major mode for editing Fortran 90,95 code in free format.
034a9d40 683
ec2f376f 684\\[f90-indent-new-line] indents current line and creates a new\
034a9d40 685 indented line.
ec2f376f 686\\[f90-indent-line] indents the current line.
87ee2359 687\\[f90-indent-subprogram] indents the current subprogram.
034a9d40
RS
688
689Type `? or `\\[help-command] to display a list of built-in\
690 abbrevs for F90 keywords.
691
692Key definitions:
693\\{f90-mode-map}
694
695Variables controlling indentation style and extra features:
696
ec2f376f
GM
697`f90-do-indent'
698 Extra indentation within do blocks (default 3).
699`f90-if-indent'
700 Extra indentation within if/select case/where/forall blocks (default 3).
701`f90-type-indent'
702 Extra indentation within type/interface/block-data blocks (default 3).
703`f90-program-indent'
704 Extra indentation within program/module/subroutine/function blocks
705 (default 2).
706`f90-continuation-indent'
707 Extra indentation applied to continuation lines (default 5).
708`f90-comment-region'
e3f5ce56
GM
709 String inserted by function \\[f90-comment-region] at start of each
710 line in region (default \"!!!$\").
ec2f376f
GM
711`f90-indented-comment-re'
712 Regexp determining the type of comment to be intended like code
713 (default \"!\").
714`f90-directive-comment-re'
715 Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented
716 (default \"!hpf\\\\$\").
717`f90-break-delimiters'
718 Regexp holding list of delimiters at which lines may be broken
719 (default \"[-+*/><=,% \\t]\").
720`f90-break-before-delimiters'
721 Non-nil causes `f90-do-auto-fill' to break lines before delimiters
722 (default t).
723`f90-beginning-ampersand'
724 Automatic insertion of \& at beginning of continuation lines (default t).
725`f90-smart-end'
726 From an END statement, check and fill the end using matching block start.
727 Allowed values are 'blink, 'no-blink, and nil, which determine
728 whether to blink the matching beginning (default 'blink).
729`f90-auto-keyword-case'
730 Automatic change of case of keywords (default nil).
731 The possibilities are 'downcase-word, 'upcase-word, 'capitalize-word.
732`f90-leave-line-no'
733 Do not left-justify line numbers (default nil).
734`f90-keywords-re'
735 List of keywords used for highlighting/upcase-keywords etc.
034a9d40
RS
736
737Turning on F90 mode calls the value of the variable `f90-mode-hook'
738with no args, if that value is non-nil."
739 (interactive)
740 (kill-all-local-variables)
e3f5ce56
GM
741 (setq major-mode 'f90-mode
742 mode-name "F90"
743 local-abbrev-table f90-mode-abbrev-table)
034a9d40
RS
744 (set-syntax-table f90-mode-syntax-table)
745 (use-local-map f90-mode-map)
e3f5ce56
GM
746 (set (make-local-variable 'indent-line-function) 'f90-indent-line)
747 (set (make-local-variable 'indent-region-function) 'f90-indent-region)
748 (set (make-local-variable 'require-final-newline) t)
749 (set (make-local-variable 'comment-start) "!")
750 (set (make-local-variable 'comment-start-skip) "!+ *")
751 (set (make-local-variable 'comment-indent-function) 'f90-comment-indent)
752 (set (make-local-variable 'abbrev-all-caps) t)
753 (set (make-local-variable 'normal-auto-fill-function) 'f90-do-auto-fill)
70186f7f 754 (setq indent-tabs-mode nil) ; auto buffer local
1a341882 755 (easy-menu-add f90-menu)
ec2f376f 756 ;; Setting up things for font-lock.
e3f5ce56
GM
757 (set (make-local-variable 'font-lock-defaults)
758 '((f90-font-lock-keywords f90-font-lock-keywords-1
759 f90-font-lock-keywords-2
760 f90-font-lock-keywords-3
761 f90-font-lock-keywords-4)
762 nil t))
45d1e4d4
DL
763 ;; Tell imenu how to handle f90.
764 (set (make-local-variable 'imenu-case-fold-search) t)
e3f5ce56
GM
765 (set (make-local-variable 'imenu-generic-expression)
766 f90-imenu-generic-expression)
d2d15846
DL
767 (set (make-local-variable 'add-log-current-defun-function)
768 #'f90-current-defun)
48548fd5 769 (run-hooks 'f90-mode-hook))
ec2f376f 770
034a9d40 771\f
ec2f376f 772;; Inline-functions.
034a9d40 773(defsubst f90-in-string ()
d14e6bbe 774 "Return non-nil if point is inside a string.
ec2f376f 775Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 776and lies before point."
034a9d40
RS
777 (let ((beg-pnt
778 (if (and f90-cache-position (> (point) f90-cache-position))
779 f90-cache-position
780 (point-min))))
781 (nth 3 (parse-partial-sexp beg-pnt (point)))))
69658465 782
034a9d40 783(defsubst f90-in-comment ()
d14e6bbe 784 "Return non-nil if point is inside a comment.
ec2f376f 785Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 786and lies before point."
034a9d40
RS
787 (let ((beg-pnt
788 (if (and f90-cache-position (> (point) f90-cache-position))
789 f90-cache-position
790 (point-min))))
791 (nth 4 (parse-partial-sexp beg-pnt (point)))))
792
793(defsubst f90-line-continued ()
d14e6bbe
GM
794 "Return t if the current line is a continued one.
795This includes comment lines embedded in continued lines, but
796not the last line of a continued statement."
034a9d40 797 (save-excursion
6734e165
GM
798 (beginning-of-line)
799 (while (and (looking-at "[ \t]*\\(!\\|$\\)") (zerop (forward-line -1))))
e3f5ce56
GM
800 (end-of-line)
801 (while (f90-in-comment)
802 (search-backward "!" (line-beginning-position))
803 (skip-chars-backward "!"))
804 (skip-chars-backward " \t")
805 (= (preceding-char) ?&)))
034a9d40
RS
806
807(defsubst f90-current-indentation ()
808 "Return indentation of current line.
809Line-numbers are considered whitespace characters."
e3f5ce56 810 (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")))
034a9d40
RS
811
812(defsubst f90-indent-to (col &optional no-line-number)
813 "Indent current line to column COL.
d14e6bbe
GM
814If optional argument NO-LINE-NUMBER is nil, jump over a possible
815line-number before indenting."
034a9d40
RS
816 (beginning-of-line)
817 (if (not no-line-number)
818 (skip-chars-forward " \t0-9"))
819 (delete-horizontal-space)
820 (if (zerop (current-column))
821 (indent-to col)
d14e6bbe 822 (indent-to col 1))) ; leave >= 1 space after line number
034a9d40 823
034a9d40 824(defsubst f90-get-present-comment-type ()
d14e6bbe
GM
825 "If point lies within a comment, return the string starting the comment.
826For example, \"!\" or \"!!\"."
034a9d40 827 (save-excursion
e3f5ce56
GM
828 (when (f90-in-comment)
829 (beginning-of-line)
89fa1ef5 830 (re-search-forward "!+" (line-end-position))
e3f5ce56 831 (while (f90-in-string)
89fa1ef5
GM
832 (re-search-forward "!+" (line-end-position)))
833 (match-string 0))))
034a9d40
RS
834
835(defsubst f90-equal-symbols (a b)
ec2f376f 836 "Compare strings A and B neglecting case and allowing for nil value."
f14ca250
GM
837 (equal (if a (downcase a) nil)
838 (if b (downcase b) nil)))
034a9d40 839
ec2f376f
GM
840;; XEmacs 19.11 & 19.12 return a single char when matching an empty regexp.
841;; The next 2 functions are therefore longer than necessary.
034a9d40 842(defsubst f90-looking-at-do ()
d14e6bbe
GM
843 "Return (\"do\" NAME) if a do statement starts after point.
844NAME is nil if the statement has no label."
f14ca250
GM
845 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(do\\)\\>")
846 (list (match-string 3)
847 (if (looking-at "\\(\\sw+\\)[ \t]*:") (match-string 1)))))
ee30478d
KH
848
849(defsubst f90-looking-at-select-case ()
d14e6bbe
GM
850 "Return (\"select\" NAME) if a select-case statement starts after point.
851NAME is nil if the statement has no label."
f14ca250 852 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
d14e6bbe 853\\(select\\)[ \t]*case[ \t]*(")
f14ca250
GM
854 (list (match-string 3)
855 (if (looking-at "\\(\\sw+\\)[ \t]*:") (match-string 1)))))
034a9d40
RS
856
857(defsubst f90-looking-at-if-then ()
d14e6bbe
GM
858 "Return (\"if\" NAME) if an if () then statement starts after point.
859NAME is nil if the statement has no label."
034a9d40 860 (save-excursion
f14ca250
GM
861 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(if\\)\\>")
862 (let ((struct (match-string 3))
863 (label (if (looking-at "\\(\\sw+\\)[ \t]*:") (match-string 1)))
864 (pos (scan-lists (point) 1 0)))
865 (and pos (goto-char pos))
5c2a80ad
GM
866 (skip-chars-forward " \t")
867 (if (or (looking-at "then\\>")
868 (when (f90-line-continued)
869 (f90-next-statement)
870 (skip-chars-forward " \t0-9&")
871 (looking-at "then\\>")))
872 (list struct label))))))
034a9d40 873
b32a3d99 874(defsubst f90-looking-at-where-or-forall ()
d14e6bbe
GM
875 "Return (KIND NAME) if a where or forall block starts after point.
876NAME is nil if the statement has no label."
f14ca250
GM
877 (save-excursion
878 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
879\\(where\\|forall\\)\\>")
880 (let ((struct (match-string 3))
881 (label (if (looking-at "\\(\\sw+\\)[ \t]*:") (match-string 1)))
882 (pos (scan-lists (point) 1 0)))
883 (and pos (goto-char pos))
884 (skip-chars-forward " \t")
885 (if (looking-at "\\(!\\|$\\)") (list struct label))))))
034a9d40
RS
886
887(defsubst f90-looking-at-type-like ()
d14e6bbe
GM
888 "Return (KIND NAME) if a type/interface/block-data block starts after point.
889NAME is non-nil only for type."
69658465 890 (cond
ee30478d 891 ((looking-at f90-type-def-re)
e7272ece 892 (list (match-string 1) (match-string 2)))
ee30478d 893 ((looking-at "\\(interface\\|block[\t]*data\\)\\>")
6734e165 894 (list (match-string 1) nil))))
034a9d40
RS
895
896(defsubst f90-looking-at-program-block-start ()
d14e6bbe 897 "Return (KIND NAME) if a program block with name NAME starts after point."
034a9d40 898 (cond
ee30478d 899 ((looking-at "\\(program\\)[ \t]+\\(\\sw+\\)\\>")
6734e165 900 (list (match-string 1) (match-string 2)))
034a9d40 901 ((and (not (looking-at "module[ \t]*procedure\\>"))
ee30478d 902 (looking-at "\\(module\\)[ \t]+\\(\\sw+\\)\\>"))
6734e165 903 (list (match-string 1) (match-string 2)))
b974df0a 904 ((and (not (looking-at "end[ \t]*\\(function\\|subroutine\\)"))
d14e6bbe
GM
905 (looking-at "[^!'\"\&\n]*\\(function\\|subroutine\\)\
906[ \t]+\\(\\sw+\\)"))
6734e165 907 (list (match-string 1) (match-string 2)))))
034a9d40
RS
908
909(defsubst f90-looking-at-program-block-end ()
d14e6bbe 910 "Return (KIND NAME) if a block with name NAME ends after point."
69658465 911 (if (looking-at (concat "end[ \t]*" f90-blocks-re
ee30478d 912 "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
6734e165 913 (list (match-string 1) (match-string 3))))
034a9d40
RS
914
915(defsubst f90-comment-indent ()
ec2f376f
GM
916 "Return the indentation to be used for a comment starting at point.
917Used for `comment-indent-function' by F90 mode.
918\"!!!\", `f90-directive-comment-re', variable `f90-comment-region' return 0.
919`f90-indented-comment-re' (if not trailing code) calls `f90-calculate-indent'.
89fa1ef5 920All others return `comment-column', leaving at least one space after code."
034a9d40 921 (cond ((looking-at "!!!") 0)
ee30478d
KH
922 ((and f90-directive-comment-re
923 (looking-at f90-directive-comment-re)) 0)
034a9d40 924 ((looking-at (regexp-quote f90-comment-region)) 0)
2c0b59e3
DL
925 ((and (looking-at f90-indented-comment-re)
926 ;; Don't attempt to indent trailing comment as code.
927 (save-excursion
928 (skip-chars-backward " \t")
929 (bolp)))
034a9d40
RS
930 (f90-calculate-indent))
931 (t (skip-chars-backward " \t")
932 (max (if (bolp) 0 (1+ (current-column))) comment-column))))
933
934(defsubst f90-present-statement-cont ()
d14e6bbe
GM
935 "Return continuation properties of present statement.
936Possible return values are:
937single - statement is not continued.
938begin - current line is the first in a continued statement.
939end - current line is the last in a continued statement
940middle - current line is neither first nor last in a continued statement.
941Comment lines embedded amongst continued lines return 'middle."
034a9d40
RS
942 (let (pcont cont)
943 (save-excursion
e3f5ce56 944 (setq pcont (if (f90-previous-statement) (f90-line-continued))))
034a9d40
RS
945 (setq cont (f90-line-continued))
946 (cond ((and (not pcont) (not cont)) 'single)
947 ((and (not pcont) cont) 'begin)
948 ((and pcont (not cont)) 'end)
949 ((and pcont cont) 'middle)
e3f5ce56 950 (t (error "The impossible occurred")))))
034a9d40
RS
951
952(defsubst f90-indent-line-no ()
d14e6bbe
GM
953 "If `f90-leave-line-no' is nil, left-justify a line number.
954Leaves point at the first non-blank character after the line number.
955Call from beginning of line."
956 (if (and (null f90-leave-line-no) (looking-at "[ \t]+[0-9]"))
957 (delete-horizontal-space))
034a9d40
RS
958 (skip-chars-forward " \t0-9"))
959
960(defsubst f90-no-block-limit ()
d14e6bbe
GM
961 "Return nil if point is at the edge of a code block.
962Searches line forward for \"function\" or \"subroutine\",
963if all else fails."
6734e165 964 (let ((eol (line-end-position)))
034a9d40
RS
965 (save-excursion
966 (not (or (looking-at "end")
7cae52cf
RS
967 (looking-at "\\(do\\|if\\|else\\(if\\|where\\)?\
968\\|select[ \t]*case\\|case\\|where\\|forall\\)\\>")
034a9d40
RS
969 (looking-at "\\(program\\|module\\|interface\\|\
970block[ \t]*data\\)\\>")
ee30478d
KH
971 (looking-at "\\(contains\\|\\sw+[ \t]*:\\)")
972 (looking-at f90-type-def-re)
034a9d40
RS
973 (re-search-forward "\\(function\\|subroutine\\)" eol t))))))
974
975(defsubst f90-update-line ()
d14e6bbe
GM
976 "Change case of current line as per `f90-auto-keyword-case'."
977 (if f90-auto-keyword-case
978 (f90-change-keywords f90-auto-keyword-case
979 (line-beginning-position) (line-end-position))))
034a9d40 980\f
7cae52cf 981(defun f90-electric-insert ()
d14e6bbe 982 "Change keyword case and auto-fill line as operators are inserted."
7cae52cf 983 (interactive)
7cae52cf 984 (self-insert-command 1)
d14e6bbe
GM
985 (if auto-fill-function (f90-do-auto-fill) ; also updates line
986 (f90-update-line)))
987
7cae52cf 988
034a9d40
RS
989(defun f90-get-correct-indent ()
990 "Get correct indent for a line starting with line number.
991Does not check type and subprogram indentation."
6734e165 992 (let ((epnt (line-end-position)) icol cont)
034a9d40
RS
993 (save-excursion
994 (while (and (f90-previous-statement)
995 (or (progn
996 (setq cont (f90-present-statement-cont))
997 (or (eq cont 'end) (eq cont 'middle)))
998 (looking-at "[ \t]*[0-9]"))))
999 (setq icol (current-indentation))
1000 (beginning-of-line)
5c2a80ad
GM
1001 (when (re-search-forward "\\(if\\|do\\|select\\|where\\|forall\\)"
1002 (line-end-position) t)
e3f5ce56
GM
1003 (beginning-of-line)
1004 (skip-chars-forward " \t")
5c2a80ad
GM
1005 (cond ((f90-looking-at-do)
1006 (setq icol (+ icol f90-do-indent)))
1007 ((or (f90-looking-at-if-then)
1008 (f90-looking-at-where-or-forall)
1009 (f90-looking-at-select-case))
1010 (setq icol (+ icol f90-if-indent))))
1011 (end-of-line))
034a9d40 1012 (while (re-search-forward
ee30478d 1013 "\\(if\\|do\\|select\\|where\\|forall\\)" epnt t)
e3f5ce56
GM
1014 (beginning-of-line)
1015 (skip-chars-forward " \t0-9")
1016 (cond ((f90-looking-at-do)
1017 (setq icol (+ icol f90-do-indent)))
1018 ((or (f90-looking-at-if-then)
1019 (f90-looking-at-where-or-forall)
1020 (f90-looking-at-select-case))
1021 (setq icol (+ icol f90-if-indent)))
1022 ((looking-at f90-end-if-re)
1023 (setq icol (- icol f90-if-indent)))
1024 ((looking-at "end[ \t]*do\\>")
1025 (setq icol (- icol f90-do-indent))))
034a9d40
RS
1026 (end-of-line))
1027 icol)))
69658465 1028
034a9d40
RS
1029(defun f90-calculate-indent ()
1030 "Calculate the indent column based on previous statements."
1031 (interactive)
1032 (let (icol cont (case-fold-search t) (pnt (point)))
1033 (save-excursion
1034 (if (not (f90-previous-statement))
1035 (setq icol 0)
1036 (setq cont (f90-present-statement-cont))
1037 (if (eq cont 'end)
1038 (while (not (eq 'begin (f90-present-statement-cont)))
1039 (f90-previous-statement)))
1040 (cond ((eq cont 'begin)
1041 (setq icol (+ (f90-current-indentation)
1042 f90-continuation-indent)))
e3f5ce56 1043 ((eq cont 'middle) (setq icol (current-indentation)))
034a9d40
RS
1044 (t (setq icol (f90-current-indentation))
1045 (skip-chars-forward " \t")
1046 (if (looking-at "[0-9]")
1047 (setq icol (f90-get-correct-indent))
1048 (cond ((or (f90-looking-at-if-then)
1049 (f90-looking-at-where-or-forall)
1050 (f90-looking-at-select-case)
69658465 1051 (looking-at f90-else-like-re))
034a9d40
RS
1052 (setq icol (+ icol f90-if-indent)))
1053 ((f90-looking-at-do)
1054 (setq icol (+ icol f90-do-indent)))
1055 ((f90-looking-at-type-like)
1056 (setq icol (+ icol f90-type-indent)))
1057 ((or (f90-looking-at-program-block-start)
1058 (looking-at "contains[ \t]*\\($\\|!\\)"))
1059 (setq icol (+ icol f90-program-indent)))))
1060 (goto-char pnt)
1061 (beginning-of-line)
1062 (cond ((looking-at "[ \t]*$"))
ec2f376f 1063 ((looking-at "[ \t]*#") ; check for cpp directive
034a9d40
RS
1064 (setq icol 0))
1065 (t
1066 (skip-chars-forward " \t0-9")
1067 (cond ((or (looking-at f90-else-like-re)
1068 (looking-at f90-end-if-re))
1069 (setq icol (- icol f90-if-indent)))
ee30478d 1070 ((looking-at "end[ \t]*do\\>")
034a9d40
RS
1071 (setq icol (- icol f90-do-indent)))
1072 ((looking-at f90-end-type-re)
1073 (setq icol (- icol f90-type-indent)))
1074 ((or (looking-at "contains[ \t]*\\(!\\|$\\)")
1075 (f90-looking-at-program-block-end))
1076 (setq icol (- icol f90-program-indent))))))
1077 ))))
1078 icol))
1079\f
034a9d40
RS
1080(defun f90-previous-statement ()
1081 "Move point to beginning of the previous F90 statement.
ec2f376f
GM
1082Return nil if no previous statement is found.
1083A statement is a line which is neither blank nor a comment."
034a9d40
RS
1084 (interactive)
1085 (let (not-first-statement)
1086 (beginning-of-line)
1087 (while (and (setq not-first-statement (zerop (forward-line -1)))
ee30478d 1088 (looking-at "[ \t0-9]*\\(!\\|$\\|#\\)")))
034a9d40
RS
1089 not-first-statement))
1090
1091(defun f90-next-statement ()
1092 "Move point to beginning of the next F90 statement.
1093Return nil if no later statement is found."
1094 (interactive)
1095 (let (not-last-statement)
1096 (beginning-of-line)
1097 (while (and (setq not-last-statement
1098 (and (zerop (forward-line 1))
1099 (not (eobp))))
1100 (looking-at "[ \t0-9]*\\(!\\|$\\)")))
1101 not-last-statement))
1102
1103(defun f90-beginning-of-subprogram ()
76bccf35 1104 "Move point to the beginning of the current subprogram.
ec2f376f 1105Return (TYPE NAME), or nil if not found."
034a9d40
RS
1106 (interactive)
1107 (let ((count 1) (case-fold-search t) matching-beg)
e3f5ce56 1108 (beginning-of-line)
76bccf35 1109 (while (and (> count 0)
034a9d40 1110 (re-search-backward f90-program-block-re nil 'move))
e3f5ce56
GM
1111 (beginning-of-line)
1112 (skip-chars-forward " \t0-9")
1113 (cond ((setq matching-beg (f90-looking-at-program-block-start))
1114 (setq count (1- count)))
1115 ((f90-looking-at-program-block-end)
1116 (setq count (1+ count)))))
034a9d40
RS
1117 (beginning-of-line)
1118 (if (zerop count)
1119 matching-beg
ec2f376f 1120 (message "No beginning found.")
034a9d40
RS
1121 nil)))
1122
1123(defun f90-end-of-subprogram ()
76bccf35 1124 "Move point to the end of the current subprogram.
ec2f376f 1125Return (TYPE NAME), or nil if not found."
034a9d40
RS
1126 (interactive)
1127 (let ((count 1) (case-fold-search t) matching-end)
034a9d40 1128 (end-of-line)
76bccf35 1129 (while (and (> count 0)
034a9d40 1130 (re-search-forward f90-program-block-re nil 'move))
e3f5ce56
GM
1131 (beginning-of-line)
1132 (skip-chars-forward " \t0-9")
034a9d40 1133 (cond ((f90-looking-at-program-block-start)
e3f5ce56 1134 (setq count (1+ count)))
034a9d40 1135 ((setq matching-end (f90-looking-at-program-block-end))
e3f5ce56 1136 (setq count (1- count))))
034a9d40 1137 (end-of-line))
6f1d50da
GM
1138 ;; This means f90-end-of-subprogram followed by f90-start-of-subprogram
1139 ;; has a net non-zero effect, which seems odd.
1140;;; (forward-line 1)
034a9d40
RS
1141 (if (zerop count)
1142 matching-end
1143 (message "No end found.")
1144 nil)))
1145
6f1d50da
GM
1146
1147(defun f90-end-of-block (&optional num)
1148 "Move point forward to the end of the current code block.
1149With optional argument NUM, go forward that many balanced blocks.
1150If NUM is negative, go backward to the start of a block.
1151Checks for consistency of block types and labels (if present),
1152and completes outermost block if necessary."
1153 (interactive "p")
1154 (if (and num (< num 0)) (f90-beginning-of-block (- num)))
1155 (let ((f90-smart-end nil) ; for the final `f90-match-end'
1156 (case-fold-search t)
1157 (count (or num 1))
1158 start-list start-this start-type start-label end-type end-label)
1159 (if (interactive-p) (push-mark (point) t))
1160 (end-of-line) ; probably want this
1161 (while (and (> count 0) (re-search-forward f90-blocks-re nil 'move))
1162 (beginning-of-line)
1163 (skip-chars-forward " \t0-9")
1164 (cond ((or (f90-in-string) (f90-in-comment)))
1165 ((setq start-this
1166 (or
1167 (f90-looking-at-do)
1168 (f90-looking-at-select-case)
1169 (f90-looking-at-type-like)
1170 (f90-looking-at-program-block-start)
1171 (f90-looking-at-if-then)
1172 (f90-looking-at-where-or-forall)))
1173 (setq start-list (cons start-this start-list) ; not add-to-list!
1174 count (1+ count)))
1175 ((looking-at (concat "end[ \t]*" f90-blocks-re
1176 "[ \t]*\\(\\sw+\\)?"))
1177 (setq end-type (match-string 1)
1178 end-label (match-string 2)
1179 count (1- count))
1180 ;; Check any internal blocks.
1181 (when start-list
1182 (setq start-this (car start-list)
1183 start-list (cdr start-list)
1184 start-type (car start-this)
1185 start-label (cadr start-this))
1186 (if (not (f90-equal-symbols start-type end-type))
1187 (error "End type `%s' does not match start type `%s'"
1188 end-type start-type))
1189 (if (not (f90-equal-symbols start-label end-label))
1190 (error "End label `%s' does not match start label `%s'"
1191 end-label start-label)))))
1192 (end-of-line))
76bccf35 1193 (if (> count 0) (error "Missing block end"))
6f1d50da
GM
1194 ;; Check outermost block.
1195 (if (interactive-p)
1196 (save-excursion
1197 (beginning-of-line)
1198 (skip-chars-forward " \t0-9")
1199 (f90-match-end)))))
1200
1201(defun f90-beginning-of-block (&optional num)
1202 "Move point backwards to the start of the current code block.
1203With optional argument NUM, go backward that many balanced blocks.
1204If NUM is negative, go forward to the end of a block.
1205Checks for consistency of block types and labels (if present).
1206Does not check the outermost block, because it may be incomplete."
1207 (interactive "p")
1208 (if (and num (< num 0)) (f90-end-of-block (- num)))
1209 (let ((case-fold-search t)
1210 (count (or num 1))
1211 end-list end-this end-type end-label start-this start-type start-label)
1212 (if (interactive-p) (push-mark (point) t))
1213 (beginning-of-line) ; probably want this
1214 (while (and (> count 0) (re-search-backward f90-blocks-re nil 'move))
1215 (beginning-of-line)
1216 (skip-chars-forward " \t0-9")
1217 (cond ((or (f90-in-string) (f90-in-comment)))
1218 ((looking-at (concat "end[ \t]*" f90-blocks-re
1219 "[ \t]*\\(\\sw+\\)?"))
1220 (setq end-list (cons (list (match-string 1) (match-string 2))
1221 end-list)
1222 count (1+ count)))
1223 ((setq start-this
1224 (or
1225 (f90-looking-at-do)
1226 (f90-looking-at-select-case)
1227 (f90-looking-at-type-like)
1228 (f90-looking-at-program-block-start)
1229 (f90-looking-at-if-then)
1230 (f90-looking-at-where-or-forall)))
1231 (setq start-type (car start-this)
1232 start-label (cadr start-this)
1233 count (1- count))
1234 ;; Check any internal blocks.
1235 (when end-list
1236 (setq end-this (car end-list)
1237 end-list (cdr end-list)
1238 end-type (car end-this)
1239 end-label (cadr end-this))
1240 (if (not (f90-equal-symbols start-type end-type))
1241 (error "Start type `%s' does not match end type `%s'"
1242 start-type end-type))
1243 (if (not (f90-equal-symbols start-label end-label))
1244 (error "Start label `%s' does not match end label `%s'"
1245 start-label end-label))))))
1246 (if (> count 0) (error "Missing block start"))))
1247
76bccf35
GM
1248(defun f90-next-block (&optional num)
1249 "Move point forward to the next end or start of a code block.
1250With optional argument NUM, go forward that many blocks.
1251If NUM is negative, go backwards.
1252A block is a subroutine, if-endif, etc."
6f1d50da 1253 (interactive "p")
76bccf35
GM
1254 (let ((case-fold-search t)
1255 (count (if num (abs num) 1)))
1256 (while (and (> count 0)
1257 (if (> num 0) (re-search-forward f90-blocks-re nil 'move)
1258 (re-search-backward f90-blocks-re nil 'move)))
6f1d50da
GM
1259 (beginning-of-line)
1260 (skip-chars-forward " \t0-9")
76bccf35
GM
1261 (cond ((or (f90-in-string) (f90-in-comment)))
1262 ((or
1263 (looking-at "end[ \t]*")
1264 (f90-looking-at-do)
1265 (f90-looking-at-select-case)
1266 (f90-looking-at-type-like)
1267 (f90-looking-at-program-block-start)
1268 (f90-looking-at-if-then)
1269 (f90-looking-at-where-or-forall))
1270 (setq count (1- count))))
1271 (if (> num 0) (end-of-line)
1272 (beginning-of-line)))))
1273
1274
1275(defun f90-previous-block (&optional num)
1276 "Move point backward to the previous end or start of a code block.
1277With optional argument NUM, go backward that many blocks.
1278If NUM is negative, go forwards.
1279A block is a subroutine, if-endif, etc."
6f1d50da 1280 (interactive "p")
76bccf35 1281 (f90-next-block (- (or num 1))))
6f1d50da
GM
1282
1283
034a9d40 1284(defun f90-mark-subprogram ()
0ee7f068 1285 "Put mark at end of F90 subprogram, point at beginning, push marks."
034a9d40
RS
1286 (interactive)
1287 (let ((pos (point)) program)
1288 (f90-end-of-subprogram)
0ee7f068 1289 (push-mark)
034a9d40
RS
1290 (goto-char pos)
1291 (setq program (f90-beginning-of-subprogram))
1a341882 1292 (if (fboundp 'zmacs-activate-region)
0ee7f068 1293 (zmacs-activate-region)
1bb3ae5c 1294 (setq mark-active t
0ee7f068 1295 deactivate-mark nil))
034a9d40
RS
1296 program))
1297
1298(defun f90-comment-region (beg-region end-region)
1299 "Comment/uncomment every line in the region.
d14e6bbe
GM
1300Insert the variable `f90-comment-region' at the start of every line
1301in the region, or, if already present, remove it."
034a9d40
RS
1302 (interactive "*r")
1303 (let ((end (make-marker)))
1304 (set-marker end end-region)
1305 (goto-char beg-region)
1306 (beginning-of-line)
1307 (if (looking-at (regexp-quote f90-comment-region))
1308 (delete-region (point) (match-end 0))
1309 (insert f90-comment-region))
e3f5ce56
GM
1310 (while (and (zerop (forward-line 1))
1311 (< (point) (marker-position end)))
034a9d40
RS
1312 (if (looking-at (regexp-quote f90-comment-region))
1313 (delete-region (point) (match-end 0))
1314 (insert f90-comment-region)))
1315 (set-marker end nil)))
1316
1317(defun f90-indent-line (&optional no-update)
87ee2359
GM
1318 "Indent current line as F90 code.
1319Unless optional argument NO-UPDATE is non-nil, call `f90-update-line'
1320after indenting."
034a9d40 1321 (interactive)
e3f5ce56 1322 (let (indent no-line-number (pos (make-marker)) (case-fold-search t))
034a9d40 1323 (set-marker pos (point))
34ba7e3d 1324 (beginning-of-line) ; digits after & \n are not line-nos
034a9d40
RS
1325 (if (save-excursion (and (f90-previous-statement) (f90-line-continued)))
1326 (progn (setq no-line-number t) (skip-chars-forward " \t"))
1327 (f90-indent-line-no))
1328 (if (looking-at "!")
1329 (setq indent (f90-comment-indent))
ee30478d 1330 (if (and (looking-at "end") f90-smart-end)
69658465 1331 (f90-match-end))
034a9d40 1332 (setq indent (f90-calculate-indent)))
e3f5ce56
GM
1333 (if (not (zerop (- indent (current-column))))
1334 (f90-indent-to indent no-line-number))
034a9d40
RS
1335 ;; If initial point was within line's indentation,
1336 ;; position after the indentation. Else stay at same point in text.
1337 (if (< (point) (marker-position pos))
1338 (goto-char (marker-position pos)))
69658465 1339 (if auto-fill-function
d14e6bbe
GM
1340 (f90-do-auto-fill) ; also updates line
1341 (if (not no-update) (f90-update-line)))
034a9d40
RS
1342 (set-marker pos nil)))
1343
1344(defun f90-indent-new-line ()
87ee2359
GM
1345 "Reindent current line, insert a newline and indent the newline.
1346An abbrev before point is expanded if the variable `abbrev-mode' is non-nil.
034a9d40
RS
1347If run in the middle of a line, the line is not broken."
1348 (interactive)
1349 (let (string cont (case-fold-search t))
1350 (if abbrev-mode (expand-abbrev))
34ba7e3d 1351 (beginning-of-line) ; reindent where likely to be needed
034a9d40 1352 (f90-indent-line-no)
ec2f376f 1353 (f90-indent-line 'no-update)
034a9d40 1354 (end-of-line)
ec2f376f 1355 (delete-horizontal-space) ; destroy trailing whitespace
e3f5ce56
GM
1356 (setq string (f90-in-string)
1357 cont (f90-line-continued))
034a9d40
RS
1358 (if (and string (not cont)) (insert "&"))
1359 (f90-update-line)
1360 (newline)
1361 (if (or string (and cont f90-beginning-ampersand)) (insert "&"))
1362 (f90-indent-line 'no-update)))
1363
1364
1365(defun f90-indent-region (beg-region end-region)
1366 "Indent every line in region by forward parsing."
1367 (interactive "*r")
e3f5ce56
GM
1368 (let ((end-region-mark (make-marker))
1369 (save-point (point-marker))
1370 block-list ind-lev ind-curr ind-b cont
034a9d40
RS
1371 struct beg-struct end-struct)
1372 (set-marker end-region-mark end-region)
1373 (goto-char beg-region)
ec2f376f 1374 ;; First find a line which is not a continuation line or comment.
034a9d40 1375 (beginning-of-line)
ee30478d 1376 (while (and (looking-at "[ \t]*[0-9]*\\(!\\|#\\|[ \t]*$\\)")
034a9d40
RS
1377 (progn (f90-indent-line 'no-update)
1378 (zerop (forward-line 1)))
1379 (< (point) end-region-mark)))
1380 (setq cont (f90-present-statement-cont))
1381 (while (and (or (eq cont 'middle) (eq cont 'end))
1382 (f90-previous-statement))
1383 (setq cont (f90-present-statement-cont)))
ec2f376f 1384 ;; Process present line for beginning of block.
034a9d40
RS
1385 (setq f90-cache-position (point))
1386 (f90-indent-line 'no-update)
e3f5ce56
GM
1387 (setq ind-lev (f90-current-indentation)
1388 ind-curr ind-lev)
1389 (beginning-of-line)
1390 (skip-chars-forward " \t0-9")
1391 (setq struct nil
1392 ind-b (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
034a9d40
RS
1393 ((or (setq struct (f90-looking-at-if-then))
1394 (setq struct (f90-looking-at-select-case))
1395 (setq struct (f90-looking-at-where-or-forall))
1396 (looking-at f90-else-like-re))
1397 f90-if-indent)
1398 ((setq struct (f90-looking-at-type-like))
1399 f90-type-indent)
34ba7e3d
GM
1400 ((or (setq struct (f90-looking-at-program-block-start))
1401 (looking-at "contains[ \t]*\\($\\|!\\)"))
034a9d40
RS
1402 f90-program-indent)))
1403 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1404 (if struct (setq block-list (cons struct block-list)))
1405 (while (and (f90-line-continued) (zerop (forward-line 1))
1406 (< (point) end-region-mark))
ec2f376f
GM
1407 (if (looking-at "[ \t]*!")
1408 (f90-indent-to (f90-comment-indent))
1409 (if (not (zerop (- (current-indentation)
1410 (+ ind-curr f90-continuation-indent))))
1411 (f90-indent-to (+ ind-curr f90-continuation-indent) 'no-line-no))))
1412 ;; Process all following lines.
d14e6bbe 1413 (while (and (zerop (forward-line 1)) (< (point) end-region-mark))
034a9d40
RS
1414 (beginning-of-line)
1415 (f90-indent-line-no)
1416 (setq f90-cache-position (point))
1417 (cond ((looking-at "[ \t]*$") (setq ind-curr 0))
1418 ((looking-at "[ \t]*#") (setq ind-curr 0))
1419 ((looking-at "!") (setq ind-curr (f90-comment-indent)))
1420 ((f90-no-block-limit) (setq ind-curr ind-lev))
1421 ((looking-at f90-else-like-re) (setq ind-curr
1422 (- ind-lev f90-if-indent)))
1423 ((looking-at "contains[ \t]*\\($\\|!\\)")
1424 (setq ind-curr (- ind-lev f90-program-indent)))
1425 ((setq ind-b
1426 (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
1427 ((or (setq struct (f90-looking-at-if-then))
1428 (setq struct (f90-looking-at-select-case))
1429 (setq struct (f90-looking-at-where-or-forall)))
1430 f90-if-indent)
1431 ((setq struct (f90-looking-at-type-like))
1432 f90-type-indent)
1433 ((setq struct (f90-looking-at-program-block-start))
1434 f90-program-indent)))
1435 (setq ind-curr ind-lev)
1436 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1437 (setq block-list (cons struct block-list)))
1438 ((setq end-struct (f90-looking-at-program-block-end))
1439 (setq beg-struct (car block-list)
1440 block-list (cdr block-list))
69658465 1441 (if f90-smart-end
034a9d40 1442 (save-excursion
ec2f376f
GM
1443 (f90-block-match (car beg-struct) (car (cdr beg-struct))
1444 (car end-struct) (car (cdr end-struct)))))
034a9d40
RS
1445 (setq ind-b
1446 (cond ((looking-at f90-end-if-re) f90-if-indent)
1447 ((looking-at "end[ \t]*do\\>") f90-do-indent)
1448 ((looking-at f90-end-type-re) f90-type-indent)
1449 ((f90-looking-at-program-block-end)
1450 f90-program-indent)))
1451 (if ind-b (setq ind-lev (- ind-lev ind-b)))
1452 (setq ind-curr ind-lev))
034a9d40 1453 (t (setq ind-curr ind-lev)))
ec2f376f 1454 ;; Do the indentation if necessary.
034a9d40
RS
1455 (if (not (zerop (- ind-curr (current-column))))
1456 (f90-indent-to ind-curr))
1457 (while (and (f90-line-continued) (zerop (forward-line 1))
1458 (< (point) end-region-mark))
ec2f376f
GM
1459 (if (looking-at "[ \t]*!")
1460 (f90-indent-to (f90-comment-indent))
1461 (if (not (zerop (- (current-indentation)
1462 (+ ind-curr f90-continuation-indent))))
1463 (f90-indent-to
1464 (+ ind-curr f90-continuation-indent) 'no-line-no)))))
1465 ;; Restore point, etc.
034a9d40
RS
1466 (setq f90-cache-position nil)
1467 (goto-char save-point)
1468 (set-marker end-region-mark nil)
1469 (set-marker save-point nil)
1a341882 1470 (if (fboundp 'zmacs-deactivate-region)
0ee7f068 1471 (zmacs-deactivate-region)
034a9d40
RS
1472 (deactivate-mark))))
1473
1474(defun f90-indent-subprogram ()
ec2f376f 1475 "Properly indent the subprogram containing point."
034a9d40
RS
1476 (interactive)
1477 (save-excursion
e3f5ce56 1478 (let ((program (f90-mark-subprogram)))
034a9d40
RS
1479 (if program
1480 (progn
2a74bdc1 1481 (message "Indenting %s %s..."
7f03b2b5 1482 (car program) (car (cdr program)))
9a8ba072 1483 (indent-region (point) (mark) nil)
2a74bdc1 1484 (message "Indenting %s %s...done"
7f03b2b5 1485 (car program) (car (cdr program))))
2a74bdc1 1486 (message "Indenting the whole file...")
9a8ba072 1487 (indent-region (point) (mark) nil)
2a74bdc1 1488 (message "Indenting the whole file...done")))))
034a9d40 1489
034a9d40 1490(defun f90-break-line (&optional no-update)
87ee2359
GM
1491 "Break line at point, insert continuation marker(s) and indent.
1492Unless in a string or comment, or if the optional argument NO-UPDATE
1493is non-nil, call `f90-update-line' after inserting the continuation marker."
034a9d40 1494 (interactive)
89fa1ef5
GM
1495 (cond ((f90-in-string)
1496 (insert "&\n&"))
1497 ((f90-in-comment)
1498 (insert "\n" (f90-get-present-comment-type)))
1499 (t (insert "&")
1500 (or no-update (f90-update-line))
1501 (newline 1)
1502 (if f90-beginning-ampersand (insert "&"))))
84021009 1503 (indent-according-to-mode))
69658465 1504
034a9d40 1505(defun f90-find-breakpoint ()
87ee2359 1506 "From `fill-column', search backward for break-delimiter."
6734e165 1507 (let ((bol (line-beginning-position)))
034a9d40 1508 (re-search-backward f90-break-delimiters bol)
5c2a80ad
GM
1509 (if (not f90-break-before-delimiters)
1510 (if (looking-at f90-no-break-re)
1511 (forward-char 2)
1512 (forward-char))
1513 (backward-char)
1514 (if (not (looking-at f90-no-break-re))
1515 (forward-char)))))
034a9d40 1516
034a9d40 1517(defun f90-do-auto-fill ()
d14e6bbe
GM
1518 "Break line if non-white characters beyond `fill-column'.
1519Update keyword case first."
034a9d40 1520 (interactive)
ec2f376f 1521 ;; Break line before or after last delimiter (non-word char) if
b974df0a 1522 ;; position is beyond fill-column.
ec2f376f 1523 ;; Will not break **, //, or => (as specified by f90-no-break-re).
7cae52cf 1524 (f90-update-line)
d595e95d
GM
1525 ;; Need this for `f90-electric-insert' and other f90- callers.
1526 (unless (and (boundp 'comment-auto-fill-only-comments)
1527 comment-auto-fill-only-comments
1528 (not (f90-in-comment)))
1529 (while (> (current-column) fill-column)
1530 (let ((pos-mark (point-marker)))
1531 (move-to-column fill-column)
1532 (or (f90-in-string) (f90-find-breakpoint))
1533 (f90-break-line)
1534 (goto-char pos-mark)
1535 (set-marker pos-mark nil)))))
b974df0a 1536
034a9d40
RS
1537
1538(defun f90-join-lines ()
1539 "Join present line with next line, if this line ends with \&."
1540 (interactive)
1541 (let (pos (oldpos (point)))
1542 (end-of-line)
1543 (skip-chars-backward " \t")
e3f5ce56
GM
1544 (when (= (preceding-char) ?&)
1545 (delete-char -1)
1546 (setq pos (point))
1547 (forward-line 1)
1548 (skip-chars-forward " \t")
1549 (if (looking-at "\&") (delete-char 1))
1550 (delete-region pos (point))
1551 (unless (f90-in-string)
1552 (delete-horizontal-space)
1553 (insert " "))
1554 (if (and auto-fill-function
1555 (> (line-end-position) fill-column))
1556 (f90-do-auto-fill))
1557 (goto-char oldpos)
1558 t))) ; return t if joined something
034a9d40
RS
1559
1560(defun f90-fill-region (beg-region end-region)
d14e6bbe 1561 "Fill every line in region by forward parsing. Join lines if possible."
034a9d40
RS
1562 (interactive "*r")
1563 (let ((end-region-mark (make-marker))
e3f5ce56
GM
1564 (go-on t)
1565 f90-smart-end f90-auto-keyword-case auto-fill-function)
034a9d40
RS
1566 (set-marker end-region-mark end-region)
1567 (goto-char beg-region)
1568 (while go-on
ec2f376f 1569 ;; Join as much as possible.
7cae52cf 1570 (while (f90-join-lines))
ec2f376f 1571 ;; Chop the line if necessary.
034a9d40
RS
1572 (while (> (save-excursion (end-of-line) (current-column))
1573 fill-column)
1574 (move-to-column fill-column)
7cae52cf
RS
1575 (f90-find-breakpoint)
1576 (f90-break-line 'no-update))
e3f5ce56
GM
1577 (setq go-on (and (< (point) (marker-position end-region-mark))
1578 (zerop (forward-line 1)))
1579 f90-cache-position (point)))
034a9d40 1580 (setq f90-cache-position nil)
1a341882 1581 (if (fboundp 'zmacs-deactivate-region)
0ee7f068 1582 (zmacs-deactivate-region)
034a9d40
RS
1583 (deactivate-mark))))
1584\f
1585(defun f90-block-match (beg-block beg-name end-block end-name)
1586 "Match end-struct with beg-struct and complete end-block if possible.
ec2f376f
GM
1587BEG-BLOCK is the type of block as indicated at the start (e.g., do).
1588BEG-NAME is the block start name (may be nil).
1589END-BLOCK is the type of block as indicated at the end (may be nil).
1590END-NAME is the block end name (may be nil).
034a9d40 1591Leave point at the end of line."
6734e165 1592 (search-forward "end" (line-end-position))
034a9d40
RS
1593 (catch 'no-match
1594 (if (not (f90-equal-symbols beg-block end-block))
1595 (if end-block
1596 (progn
1597 (message "END %s does not match %s." end-block beg-block)
69658465 1598 (end-of-line)
034a9d40
RS
1599 (throw 'no-match nil))
1600 (message "Inserting %s." beg-block)
1601 (insert (concat " " beg-block)))
1602 (search-forward end-block))
1603 (if (not (f90-equal-symbols beg-name end-name))
69658465 1604 (cond ((and beg-name (not end-name))
034a9d40
RS
1605 (message "Inserting %s." beg-name)
1606 (insert (concat " " beg-name)))
69658465 1607 ((and beg-name end-name)
034a9d40
RS
1608 (message "Replacing %s with %s." end-name beg-name)
1609 (search-forward end-name)
1610 (replace-match beg-name))
69658465 1611 ((and (not beg-name) end-name)
034a9d40
RS
1612 (message "Deleting %s." end-name)
1613 (search-forward end-name)
1614 (replace-match "")))
1615 (if end-name (search-forward end-name)))
ee30478d 1616 (if (not (looking-at "[ \t]*!")) (delete-horizontal-space))))
034a9d40
RS
1617
1618(defun f90-match-end ()
ec2f376f 1619 "From an end block statement, find the corresponding block and name."
034a9d40 1620 (interactive)
e3f5ce56 1621 (let ((count 1) (top-of-window (window-start))
034a9d40 1622 (end-point (point)) (case-fold-search t)
e3f5ce56 1623 matching-beg beg-name end-name beg-block end-block end-struct)
5c2a80ad
GM
1624 (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")
1625 (setq end-struct (f90-looking-at-program-block-end)))
e3f5ce56
GM
1626 (setq end-block (car end-struct)
1627 end-name (car (cdr end-struct)))
5c2a80ad
GM
1628 (save-excursion
1629 (beginning-of-line)
6dd52caf 1630 (while (and (> count 0) (re-search-backward f90-blocks-re nil t))
e3f5ce56
GM
1631 (beginning-of-line)
1632 (skip-chars-forward " \t0-9")
6dd52caf
GM
1633 (cond ((or (f90-in-string) (f90-in-comment)))
1634 ((setq matching-beg
e3f5ce56
GM
1635 (or
1636 (f90-looking-at-do)
1637 (f90-looking-at-if-then)
1638 (f90-looking-at-where-or-forall)
1639 (f90-looking-at-select-case)
1640 (f90-looking-at-type-like)
1641 (f90-looking-at-program-block-start)))
1642 (setq count (1- count)))
6dd52caf 1643 ((looking-at (concat "end[ \t]*" f90-blocks-re))
e3f5ce56 1644 (setq count (1+ count)))))
6dd52caf 1645 (if (> count 0)
5c2a80ad
GM
1646 (message "No matching beginning.")
1647 (f90-update-line)
1648 (if (eq f90-smart-end 'blink)
1649 (if (< (point) top-of-window)
1650 (message "Matches %s: %s"
1651 (what-line)
1652 (buffer-substring
1653 (line-beginning-position)
1654 (line-end-position)))
1655 (sit-for 1)))
e3f5ce56
GM
1656 (setq beg-block (car matching-beg)
1657 beg-name (car (cdr matching-beg)))
5c2a80ad
GM
1658 (goto-char end-point)
1659 (beginning-of-line)
1660 (f90-block-match beg-block beg-name end-block end-name))))))
034a9d40
RS
1661
1662(defun f90-insert-end ()
87ee2359 1663 "Insert a complete end statement matching beginning of present block."
034a9d40 1664 (interactive)
e3f5ce56 1665 (let ((f90-smart-end (or f90-smart-end 'blink)))
034a9d40
RS
1666 (insert "end")
1667 (f90-indent-new-line)))
1668\f
ec2f376f 1669;; Abbrevs and keywords.
034a9d40
RS
1670
1671(defun f90-abbrev-start ()
69658465 1672 "Typing `\\[help-command] or `? lists all the F90 abbrevs.
034a9d40
RS
1673Any other key combination is executed normally."
1674 (interactive)
0ee7f068 1675 (let (c)
034a9d40 1676 (insert last-command-char)
1a341882
GM
1677 (setq c (if (fboundp 'next-command-event) ; XEmacs
1678 (event-to-character (next-command-event))
1679 (read-event)))
be550ccc
GM
1680 ;; Insert char if not equal to `?', or if abbrev-mode is off.
1681 (if (and abbrev-mode (or (eq c ??) (eq c help-char)))
034a9d40 1682 (f90-abbrev-help)
0ee7f068 1683 (setq unread-command-events (list c)))))
034a9d40
RS
1684
1685(defun f90-abbrev-help ()
1686 "List the currently defined abbrevs in F90 mode."
1687 (interactive)
1688 (message "Listing abbrev table...")
1689 (display-buffer (f90-prepare-abbrev-list-buffer))
1690 (message "Listing abbrev table...done"))
1691
1692(defun f90-prepare-abbrev-list-buffer ()
ec2f376f 1693 "Create a buffer listing the F90 mode abbreviations."
034a9d40
RS
1694 (save-excursion
1695 (set-buffer (get-buffer-create "*Abbrevs*"))
1696 (erase-buffer)
1697 (insert-abbrev-table-description 'f90-mode-abbrev-table t)
1698 (goto-char (point-min))
1699 (set-buffer-modified-p nil)
1700 (edit-abbrevs-mode))
1701 (get-buffer-create "*Abbrevs*"))
1702
1703(defun f90-upcase-keywords ()
1704 "Upcase all F90 keywords in the buffer."
1705 (interactive)
1706 (f90-change-keywords 'upcase-word))
1707
1708(defun f90-capitalize-keywords ()
1709 "Capitalize all F90 keywords in the buffer."
1710 (interactive)
1711 (f90-change-keywords 'capitalize-word))
1712
1713(defun f90-downcase-keywords ()
1714 "Downcase all F90 keywords in the buffer."
1715 (interactive)
1716 (f90-change-keywords 'downcase-word))
1717
1718(defun f90-upcase-region-keywords (beg end)
1719 "Upcase all F90 keywords in the region."
1720 (interactive "*r")
1721 (f90-change-keywords 'upcase-word beg end))
1722
1723(defun f90-capitalize-region-keywords (beg end)
1724 "Capitalize all F90 keywords in the region."
1725 (interactive "*r")
1726 (f90-change-keywords 'capitalize-word beg end))
1727
1728(defun f90-downcase-region-keywords (beg end)
1729 "Downcase all F90 keywords in the region."
1730 (interactive "*r")
1731 (f90-change-keywords 'downcase-word beg end))
1732
1733;; Change the keywords according to argument.
1734(defun f90-change-keywords (change-word &optional beg end)
ec2f376f 1735 "Change the case of F90 keywords in the region (if specified) or buffer.
02f85cba 1736CHANGE-WORD should be one of 'upcase-word, 'downcase-word, 'capitalize-word."
034a9d40 1737 (save-excursion
e3f5ce56
GM
1738 (setq beg (or beg (point-min))
1739 end (or end (point-max)))
69658465 1740 (let ((keyword-re
ee30478d
KH
1741 (concat "\\("
1742 f90-keywords-re "\\|" f90-procedures-re "\\|"
1743 f90-hpf-keywords-re "\\|" f90-operators-re "\\)"))
a1506d29 1744 (ref-point (point-min))
e3f5ce56
GM
1745 (modified (buffer-modified-p))
1746 state saveword back-point)
034a9d40 1747 (goto-char beg)
ee30478d
KH
1748 (unwind-protect
1749 (while (re-search-forward keyword-re end t)
5c2a80ad
GM
1750 (unless (progn
1751 (setq state (parse-partial-sexp ref-point (point)))
1752 (or (nth 3 state) (nth 4 state)
ec2f376f 1753 (save-excursion ; check for cpp directive
5c2a80ad
GM
1754 (beginning-of-line)
1755 (skip-chars-forward " \t0-9")
1756 (looking-at "#"))))
ee30478d 1757 (setq ref-point (point)
e3f5ce56
GM
1758 back-point (save-excursion (backward-word 1) (point))
1759 saveword (buffer-substring back-point ref-point))
ee30478d
KH
1760 (funcall change-word -1)
1761 (or (string= saveword (buffer-substring back-point ref-point))
1762 (setq modified t))))
1763 (or modified (set-buffer-modified-p nil))))))
034a9d40 1764
d2d15846
DL
1765
1766(defun f90-current-defun ()
1767 "Function to use for `add-log-current-defun-function' in F90 mode."
1768 (save-excursion
1769 (nth 1 (f90-beginning-of-subprogram))))
1770
034a9d40 1771(provide 'f90)
db97b872 1772
034a9d40 1773;;; f90.el ends here