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