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