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