Replace last-command-char with last-command-event.
[bpt/emacs.git] / lisp / progmodes / f90.el
CommitLineData
be010748 1;;; f90.el --- Fortran-90 mode (free format)
b578f267 2
a20b3848 3;; Copyright (C) 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2005,
ae940284 4;; 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
034a9d40 5
88ea9ddc 6;; Author: Torbj\"orn Einarsson <Torbjorn.Einarsson@era.ericsson.se>
9877fcf1 7;; Maintainer: Glenn Morris <rgm@gnu.org>
034a9d40
RS
8;; Keywords: fortran, f90, languages
9
b578f267
EN
10;; This file is part of GNU Emacs.
11
b1fc2b50 12;; GNU Emacs is free software: you can redistribute it and/or modify
034a9d40 13;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
034a9d40 16
b578f267 17;; GNU Emacs is distributed in the hope that it will be useful,
034a9d40
RS
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b1fc2b50 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
034a9d40
RS
24
25;;; Commentary:
b578f267 26
87ee2359
GM
27;; Major mode for editing F90 programs in FREE FORMAT.
28;; The minor language revision F95 is also supported (with font-locking).
b1ca7740 29;; Some/many (?) aspects of F2003 are supported.
87ee2359 30
034a9d40 31;; Knows about continuation lines, named structured statements, and other
87ee2359
GM
32;; features in F90 including HPF (High Performance Fortran) structures.
33;; The basic feature provides accurate indentation of F90 programs.
034a9d40
RS
34;; In addition, there are many more features like automatic matching of all
35;; end statements, an auto-fill function to break long lines, a join-lines
87ee2359
GM
36;; function which joins continued lines, etc.
37
38;; To facilitate typing, a fairly complete list of abbreviations is provided.
39;; All abbreviations begin with the backquote character "`"
87ee2359 40;; For example, `i expands to integer (if abbrev-mode is on).
034a9d40 41
87ee2359 42;; There are two separate features for altering the appearance of code:
034a9d40 43;; 1) Upcasing or capitalizing of all keywords.
87ee2359
GM
44;; 2) Colors/fonts using font-lock-mode.
45;; Automatic upcase or downcase of keywords is controlled by the variable
46;; f90-auto-keyword-case.
034a9d40
RS
47
48;; The indentations of lines starting with ! is determined by the first of the
87ee2359 49;; following matches (values in the left column are the defaults):
ee30478d
KH
50
51;; start-string/regexp indent variable holding start-string/regexp
52;; !!! 0
53;; !hpf\\$ (re) 0 f90-directive-comment-re
54;; !!$ 0 f90-comment-region
55;; ! (re) as code f90-indented-comment-re
56;; default comment-column
57
58;; Ex: Here is the result of 3 different settings of f90-indented-comment-re
59;; f90-indented-comment-re !-indentation !!-indentation
60;; ! as code as code
61;; !! comment-column as code
62;; ![^!] as code comment-column
87ee2359
GM
63;; Trailing comments are indented to comment-column with indent-for-comment.
64;; The function f90-comment-region toggles insertion of
65;; the variable f90-comment-region in every line of the region.
034a9d40 66
a729409a 67;; One common convention for free vs. fixed format is that free format files
69658465 68;; have the ending .f90 or .f95 while fixed format files have the ending .f.
87ee2359
GM
69;; Emacs automatically loads Fortran files in the appropriate mode based
70;; on extension. You can modify this by adjusting the variable auto-mode-alist.
71;; For example:
72;; (add-to-list 'auto-mode-alist '("\\.f\\'" . f90-mode))
73
034a9d40 74;; Once you have entered f90-mode, you may get more info by using
69658465 75;; the command describe-mode (C-h m). For online help use
87ee2359
GM
76;; C-h f <Name of function you want described>, or
77;; C-h v <Name of variable you want described>.
034a9d40 78
87ee2359
GM
79;; To customize f90-mode for your taste, use, for example:
80;; (you don't have to specify values for all the parameters below)
81;;
d2d15846 82;;(add-hook 'f90-mode-hook
87ee2359 83;; ;; These are the default values.
034a9d40
RS
84;; '(lambda () (setq f90-do-indent 3
85;; f90-if-indent 3
86;; f90-type-indent 3
87;; f90-program-indent 2
88;; f90-continuation-indent 5
89;; f90-comment-region "!!$"
ee30478d
KH
90;; f90-directive-comment-re "!hpf\\$"
91;; f90-indented-comment-re "!"
87ee2359 92;; f90-break-delimiters "[-+\\*/><=,% \t]"
034a9d40
RS
93;; f90-break-before-delimiters t
94;; f90-beginning-ampersand t
95;; f90-smart-end 'blink
96;; f90-auto-keyword-case nil
87ee2359 97;; f90-leave-line-no nil
72e80cad 98;; indent-tabs-mode nil
b974df0a 99;; f90-font-lock-keywords f90-font-lock-keywords-2
72e80cad 100;; )
87ee2359 101;; ;; These are not default.
034a9d40 102;; (abbrev-mode 1) ; turn on abbreviation mode
b974df0a 103;; (f90-add-imenu-menu) ; extra menu with functions etc.
034a9d40
RS
104;; (if f90-auto-keyword-case ; change case of all keywords on startup
105;; (f90-change-keywords f90-auto-keyword-case))
640f9e26 106;; ))
87ee2359
GM
107;;
108;; in your .emacs file. You can also customize the lists
109;; f90-font-lock-keywords, etc.
110;;
111;; The auto-fill and abbreviation minor modes are accessible from the F90 menu,
b974df0a 112;; or by using M-x auto-fill-mode and M-x abbrev-mode, respectively.
034a9d40
RS
113
114;; Remarks
115;; 1) Line numbers are by default left-justified. If f90-leave-line-no is
116;; non-nil, the line numbers are never touched.
87ee2359 117;; 2) Multi-; statements like "do i=1,20 ; j=j+i ; end do" are not handled
034a9d40 118;; correctly, but I imagine them to be rare.
ee30478d 119;; 3) Regexps for hilit19 are no longer supported.
87ee2359 120;; 4) For FIXED FORMAT code, use fortran mode.
034a9d40 121;; 5) This mode does not work under emacs-18.x.
72e80cad
KH
122;; 6) Preprocessor directives, i.e., lines starting with # are left-justified
123;; and are untouched by all case-changing commands. There is, at present, no
124;; mechanism for treating multi-line directives (continued by \ ).
ee30478d
KH
125;; 7) f77 do-loops do 10 i=.. ; ; 10 continue are not correctly indented.
126;; You are urged to use f90-do loops (with labels if you wish).
c80718cc 127;; 8) The highlighting mode under XEmacs is not as complete as under Emacs.
034a9d40
RS
128
129;; List of user commands
130;; f90-previous-statement f90-next-statement
131;; f90-beginning-of-subprogram f90-end-of-subprogram f90-mark-subprogram
132;; f90-comment-region
133;; f90-indent-line f90-indent-new-line
134;; f90-indent-region (can be called by calling indent-region)
135;; f90-indent-subprogram
136;; f90-break-line f90-join-lines
034a9d40
RS
137;; f90-fill-region
138;; f90-insert-end
139;; f90-upcase-keywords f90-upcase-region-keywords
140;; f90-downcase-keywords f90-downcase-region-keywords
141;; f90-capitalize-keywords f90-capitalize-region-keywords
b974df0a
EN
142;; f90-add-imenu-menu
143;; f90-font-lock-1, f90-font-lock-2, f90-font-lock-3, f90-font-lock-4
034a9d40 144
87ee2359 145;; Original author's thanks
034a9d40
RS
146;; Thanks to all the people who have tested the mode. Special thanks to Jens
147;; Bloch Helmers for encouraging me to write this code, for creative
148;; suggestions as well as for the lists of hpf-commands.
149;; Also thanks to the authors of the fortran and pascal modes, on which some
150;; of this code is built.
151
a729409a
GM
152;;; Code:
153
87ee2359 154;; TODO
b1ca7740
GM
155;; 1. Any missing F2003 syntax?
156;; 2. Have "f90-mode" just recognize F90 syntax, then derived modes
5ab33946 157;; "f95-mode", "f2003-mode" for the language revisions.
b1ca7740
GM
158;; 3. Support for align.
159;; Font-locking:
160;; 1. OpenMP, OpenMPI?, preprocessor highlighting.
161;; 2. interface blah - Highlight "blah" in function-name face?
162;; Need to avoid "interface operator (+)" etc.
163;; 3. integer_name = 1
164;; 4. Labels for "else" statements (F2003)?
87ee2359 165
34ba7e3d
GM
166(defvar comment-auto-fill-only-comments)
167(defvar font-lock-keywords)
0ee7f068 168
034a9d40 169;; User options
034a9d40 170
fcad5199 171(defgroup f90 nil
a729409a 172 "Major mode for editing free format Fortran 90,95 code."
8ec3bce0 173 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
d2d15846 174 :group 'languages)
034a9d40 175
fcad5199 176(defgroup f90-indent nil
a729409a 177 "Indentation in free format Fortran."
fcad5199 178 :prefix "f90-"
a729409a 179 :group 'f90)
034a9d40 180
034a9d40 181
fcad5199 182(defcustom f90-do-indent 3
d4654cff 183 "Extra indentation applied to DO blocks."
a729409a 184 :type 'integer
fcad5199 185 :group 'f90-indent)
b2752d93 186(put 'f90-do-indent 'safe-local-variable 'integerp)
034a9d40 187
fcad5199 188(defcustom f90-if-indent 3
d4654cff 189 "Extra indentation applied to IF, SELECT CASE, WHERE and FORALL blocks."
a729409a 190 :type 'integer
fcad5199 191 :group 'f90-indent)
b2752d93 192(put 'f90-if-indent 'safe-local-variable 'integerp)
034a9d40 193
fcad5199 194(defcustom f90-type-indent 3
5ab33946 195 "Extra indentation applied to TYPE, ENUM, INTERFACE and BLOCK DATA blocks."
a729409a 196 :type 'integer
fcad5199 197 :group 'f90-indent)
b2752d93 198(put 'f90-type-indent 'safe-local-variable 'integerp)
034a9d40 199
fcad5199 200(defcustom f90-program-indent 2
d4654cff 201 "Extra indentation applied to PROGRAM, MODULE, SUBROUTINE, FUNCTION blocks."
a729409a 202 :type 'integer
fcad5199 203 :group 'f90-indent)
b2752d93 204(put 'f90-program-indent 'safe-local-variable 'integerp)
034a9d40 205
5ab33946
GM
206(defcustom f90-associate-indent 2
207 "Extra indentation applied to ASSOCIATE blocks."
208 :type 'integer
209 :group 'f90-indent
210 :version "23.1")
b2752d93 211(put 'f90-associate-indent 'safe-local-variable 'integerp)
5ab33946 212
fcad5199 213(defcustom f90-continuation-indent 5
d4654cff 214 "Extra indentation applied to continuation lines."
a729409a 215 :type 'integer
fcad5199 216 :group 'f90-indent)
b2752d93 217(put 'f90-continuation-indent 'safe-local-variable 'integerp)
034a9d40 218
fcad5199 219(defcustom f90-comment-region "!!$"
d4654cff 220 "String inserted by \\[f90-comment-region] at start of each line in region."
a729409a 221 :type 'string
fcad5199 222 :group 'f90-indent)
b2752d93 223(put 'f90-comment-region 'safe-local-variable 'stringp)
fcad5199
RS
224
225(defcustom f90-indented-comment-re "!"
d4654cff 226 "Regexp matching comments to indent as code."
a729409a 227 :type 'regexp
fcad5199 228 :group 'f90-indent)
f28af2ae 229(put 'f90-indented-comment-re 'safe-local-variable 'stringp)
fcad5199
RS
230
231(defcustom f90-directive-comment-re "!hpf\\$"
d4654cff 232 "Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented."
a729409a 233 :type 'regexp
fcad5199 234 :group 'f90-indent)
f28af2ae 235(put 'f90-directive-comment-re 'safe-local-variable 'stringp)
fcad5199
RS
236
237(defcustom f90-beginning-ampersand t
d4654cff 238 "Non-nil gives automatic insertion of \& at start of continuation line."
a729409a 239 :type 'boolean
fcad5199 240 :group 'f90)
b2752d93 241(put 'f90-beginning-ampersand 'safe-local-variable 'booleanp)
fcad5199
RS
242
243(defcustom f90-smart-end 'blink
d4654cff 244 "Qualification of END statements according to the matching block start.
784d007b
GM
245For example, the END that closes an IF block is changed to END
246IF. If the block has a label, this is added as well. Allowed
247values are 'blink, 'no-blink, and nil. If nil, nothing is done.
248The other two settings have the same effect, but 'blink
249additionally blinks the cursor to the start of the block."
a729409a 250 :type '(choice (const blink) (const no-blink) (const nil))
fcad5199 251 :group 'f90)
b2752d93
GM
252(put 'f90-smart-end 'safe-local-variable
253 (lambda (value) (memq value '(blink no-blink nil))))
034a9d40 254
fcad5199 255(defcustom f90-break-delimiters "[-+\\*/><=,% \t]"
d4654cff 256 "Regexp matching delimiter characters at which lines may be broken.
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)
fffa137c 638 ;; I think that the f95 standard leaves the behavior of \
784d007b
GM
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
83596348 656;;; (define-key map "\r" 'newline)
70186f7f
GM
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)
83596348
GM
666 ;; Standard tab binding will call this, and also handle regions.
667;;; (define-key map "\t" 'f90-indent-line)
70186f7f
GM
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)
672 (define-key map "/" 'f90-electric-insert)
a729409a
GM
673
674 (easy-menu-define f90-menu map "Menu for F90 mode."
675 `("F90"
676 ("Customization"
677 ,(custom-menu-create 'f90)
68fcc3ec
GM
678 ;; FIXME useless?
679 ["Set" Custom-set :active t
680 :help "Set current value of all edited settings in the buffer"]
681 ["Save" Custom-save :active t
682 :help "Set and save all edited settings"]
683 ["Reset to Current" Custom-reset-current :active t
684 :help "Reset all edited settings to current"]
685 ["Reset to Saved" Custom-reset-saved :active t
686 :help "Reset all edited or set settings to saved"]
687 ["Reset to Standard Settings" Custom-reset-standard :active t
688 :help "Erase all cusomizations in buffer"]
a729409a
GM
689 )
690 "--"
68fcc3ec
GM
691 ["Indent Subprogram" f90-indent-subprogram t]
692 ["Mark Subprogram" f90-mark-subprogram :active t :help
693 "Mark the end of the current subprogram, move point to the start"]
694 ["Beginning of Subprogram" f90-beginning-of-subprogram :active t
695 :help "Move point to the start of the current subprogram"]
696 ["End of Subprogram" f90-end-of-subprogram :active t
697 :help "Move point to the end of the current subprogram"]
a729409a 698 "--"
68fcc3ec
GM
699 ["(Un)Comment Region" f90-comment-region :active mark-active
700 :help "Comment or uncomment the region"]
701 ["Indent Region" f90-indent-region :active mark-active]
702 ["Fill Region" f90-fill-region :active mark-active
703 :help "Fill long lines in the region"]
a729409a 704 "--"
68fcc3ec
GM
705 ["Break Line at Point" f90-break-line :active t
706 :help "Break the current line at point"]
707 ["Join with Previous Line" f90-join-lines :active t
708 :help "Join the current line to the previous one"]
709 ["Insert Block End" f90-insert-end :active t
710 :help "Insert an end statement for the current code block"]
a729409a
GM
711 "--"
712 ("Highlighting"
68fcc3ec 713 :help "Fontify this buffer to varying degrees"
a729409a 714 ["Toggle font-lock-mode" font-lock-mode :selected font-lock-mode
68fcc3ec 715 :style toggle :help "Fontify text in this buffer"]
a729409a
GM
716 "--"
717 ["Light highlighting (level 1)" f90-font-lock-1 t]
718 ["Moderate highlighting (level 2)" f90-font-lock-2 t]
719 ["Heavy highlighting (level 3)" f90-font-lock-3 t]
720 ["Maximum highlighting (level 4)" f90-font-lock-4 t]
721 )
722 ("Change Keyword Case"
68fcc3ec 723 :help "Change the case of keywords in the buffer or region"
a729409a
GM
724 ["Upcase Keywords (buffer)" f90-upcase-keywords t]
725 ["Capitalize Keywords (buffer)" f90-capitalize-keywords t]
726 ["Downcase Keywords (buffer)" f90-downcase-keywords t]
727 "--"
728 ["Upcase Keywords (region)" f90-upcase-region-keywords
729 mark-active]
730 ["Capitalize Keywords (region)" f90-capitalize-region-keywords
731 mark-active]
732 ["Downcase Keywords (region)" f90-downcase-region-keywords
733 mark-active]
734 )
735 "--"
68fcc3ec
GM
736 ["Toggle Auto Fill" auto-fill-mode :selected auto-fill-function
737 :style toggle
738 :help "Automatically fill text while typing in this buffer"]
739 ["Toggle Abbrev Mode" abbrev-mode :selected abbrev-mode
740 :style toggle :help "Expand abbreviations while typing in this buffer"]
741 ["Add Imenu Menu" f90-add-imenu-menu
a729409a 742 :active (not (lookup-key (current-local-map) [menu-bar index]))
68fcc3ec
GM
743 :included (fboundp 'imenu-add-to-menubar)
744 :help "Add an index menu to the menu-bar"
745 ]))
70186f7f 746 map)
034a9d40 747 "Keymap used in F90 mode.")
ee30478d 748
b974df0a 749
599aeab9
GM
750(defun f90-font-lock-n (n)
751 "Set `font-lock-keywords' to F90 level N keywords."
752 (font-lock-mode 1)
753 (setq font-lock-keywords
754 (symbol-value (intern-soft (format "f90-font-lock-keywords-%d" n))))
755 (font-lock-fontify-buffer))
756
1a341882
GM
757(defun f90-font-lock-1 ()
758 "Set `font-lock-keywords' to `f90-font-lock-keywords-1'."
759 (interactive)
599aeab9 760 (f90-font-lock-n 1))
1a341882
GM
761
762(defun f90-font-lock-2 ()
763 "Set `font-lock-keywords' to `f90-font-lock-keywords-2'."
764 (interactive)
599aeab9 765 (f90-font-lock-n 2))
1a341882
GM
766
767(defun f90-font-lock-3 ()
768 "Set `font-lock-keywords' to `f90-font-lock-keywords-3'."
769 (interactive)
599aeab9 770 (f90-font-lock-n 3))
1a341882
GM
771
772(defun f90-font-lock-4 ()
773 "Set `font-lock-keywords' to `f90-font-lock-keywords-4'."
774 (interactive)
599aeab9 775 (f90-font-lock-n 4))
1a341882 776\f
ee30478d 777;; Regexps for finding program structures.
69658465 778(defconst f90-blocks-re
ec2f376f
GM
779 (concat "\\(block[ \t]*data\\|"
780 (regexp-opt '("do" "if" "interface" "function" "module" "program"
5ab33946
GM
781 "select" "subroutine" "type" "where" "forall"
782 ;; F2003.
783 "enum" "associate"))
ec2f376f
GM
784 "\\)\\>")
785 "Regexp potentially indicating a \"block\" of F90 code.")
786
69658465 787(defconst f90-program-block-re
ec2f376f
GM
788 (regexp-opt '("program" "module" "subroutine" "function") 'paren)
789 "Regexp used to locate the start/end of a \"subprogram\".")
790
5ab33946 791;; "class is" is F2003.
69658465 792(defconst f90-else-like-re
5ab33946
GM
793 "\\(else\\([ \t]*if\\|where\\)?\\|case[ \t]*\\(default\\|(\\)\\|\
794\\(class\\|type\\)[ \t]*is[ \t]*(\\|class[ \t]*default\\)"
795 "Regexp matching an ELSE IF, ELSEWHERE, CASE, CLASS/TYPE IS statement.")
ec2f376f 796
69658465 797(defconst f90-end-if-re
ec2f376f
GM
798 (concat "end[ \t]*"
799 (regexp-opt '("if" "select" "where" "forall") 'paren)
800 "\\>")
801 "Regexp matching the end of an IF, SELECT, WHERE, FORALL block.")
802
69658465 803(defconst f90-end-type-re
5ab33946
GM
804 "end[ \t]*\\(type\\|enum\\|interface\\|block[ \t]*data\\)\\>"
805 "Regexp matching the end of a TYPE, ENUM, INTERFACE, BLOCK DATA section.")
806
807(defconst f90-end-associate-re
808 "end[ \t]*associate\\>"
809 "Regexp matching the end of an ASSOCIATE block.")
ec2f376f 810
5ab33946
GM
811;; This is for a TYPE block, not a variable of derived TYPE.
812;; Hence no need to add CLASS for F2003.
ee30478d 813(defconst f90-type-def-re
5ab33946
GM
814 ;; type word
815 ;; type :: word
816 ;; type, stuff :: word
817 ;; NOT "type ("
eb9f0295 818 "\\<\\(type\\)\\>\\(?:[^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
e7272ece 819 "Regexp matching the definition of a derived type.")
ec2f376f 820
5ab33946
GM
821(defconst f90-typeis-re
822 "\\<\\(class\\|type\\)[ \t]*is[ \t]*("
823 "Regexp matching a CLASS/TYPE IS statement.")
824
ec2f376f 825(defconst f90-no-break-re
6f43f690
GM
826 (regexp-opt '("**" "//" "=>" ">=" "<=" "==" "/=") 'paren)
827 "Regexp specifying where not to break lines when filling.
828This regexp matches certain tokens comprised entirely of
829characters matching the regexp `f90-break-delimiters' that should
830not be split by filling. Each element is assumed to be two
831characters long.")
ec2f376f
GM
832
833(defvar f90-cache-position nil
834 "Temporary position used to speed up region operations.")
034a9d40 835(make-variable-buffer-local 'f90-cache-position)
ec2f376f 836
b974df0a 837\f
799dee7a
GM
838;; Hideshow support.
839(defconst f90-end-block-re
fcca5273 840 (concat "^[ \t0-9]*\\<end[ \t]*"
799dee7a 841 (regexp-opt '("do" "if" "forall" "function" "interface"
fcca5273 842 "module" "program" "select" "subroutine"
5ab33946
GM
843 "type" "where" "enum" "associate") t)
844 "\\>")
fcca5273 845 "Regexp matching the end of an F90 \"block\", from the line start.
799dee7a
GM
846Used in the F90 entry in `hs-special-modes-alist'.")
847
848;; Ignore the fact that FUNCTION, SUBROUTINE, WHERE, FORALL have a
fcca5273 849;; following "(". DO, CASE, IF can have labels.
799dee7a
GM
850(defconst f90-start-block-re
851 (concat
852 "^[ \t0-9]*" ; statement number
853 "\\(\\("
854 "\\(\\sw+[ \t]*:[ \t]*\\)?" ; structure label
5ab33946 855 "\\(do\\|select[ \t]*\\(case\\|type\\)\\|"
fcca5273
GM
856 ;; See comments in fortran-start-block-re for the problems of IF.
857 "if[ \t]*(\\(.*\\|"
858 ".*\n\\([^if]*\\([^i].\\|.[^f]\\|.\\>\\)\\)\\)\\<then\\|"
799dee7a
GM
859 ;; Distinguish WHERE block from isolated WHERE.
860 "\\(where\\|forall\\)[ \t]*(.*)[ \t]*\\(!\\|$\\)\\)\\)"
861 "\\|"
5ab33946
GM
862 ;; Avoid F2003 "type is" in "select type",
863 ;; and also variables of derived type "type (foo)".
864 ;; "type, foo" must be a block (?).
b1ca7740
GM
865 "type[ \t,]\\("
866 "[^i(!\n\"\& \t]\\|" ; not-i(
867 "i[^s!\n\"\& \t]\\|" ; i not-s
868 "is\\sw\\)\\|"
5ab33946 869 ;; "abstract interface" is F2003.
b1ca7740 870 "program\\|\\(?:abstract[ \t]*\\)?interface\\|module\\|"
5ab33946
GM
871 ;; "enum", but not "enumerator".
872 "function\\|subroutine\\|enum[^e]\\|associate"
799dee7a
GM
873 "\\)"
874 "[ \t]*")
fcca5273 875 "Regexp matching the start of an F90 \"block\", from the line start.
799dee7a
GM
876A simple regexp cannot do this in fully correct fashion, so this
877tries to strike a compromise between complexity and flexibility.
878Used in the F90 entry in `hs-special-modes-alist'.")
879
880;; hs-special-modes-alist is autoloaded.
881(add-to-list 'hs-special-modes-alist
882 `(f90-mode ,f90-start-block-re ,f90-end-block-re
883 "!" f90-end-of-block nil))
884
885\f
ec2f376f 886;; Imenu support.
b1ca7740
GM
887;; FIXME trivial to extend this to enum. Worth it?
888(defun f90-imenu-type-matcher ()
889 "Search backward for the start of a derived type.
890Set subexpression 1 in the match-data to the name of the type."
5d16fdd7 891 (let (found)
b1ca7740
GM
892 (while (and (re-search-backward "^[ \t0-9]*type[ \t]*" nil t)
893 (not (setq found
894 (save-excursion
895 (goto-char (match-end 0))
896 (unless (looking-at "\\(is\\>\\|(\\)")
897 (or (looking-at "\\(\\sw+\\)")
898 (re-search-forward
899 "[ \t]*::[ \t]*\\(\\sw+\\)"
900 (line-end-position) t))))))))
901 found))
902
ee30478d 903(defvar f90-imenu-generic-expression
b974df0a 904 (let ((good-char "[^!\"\&\n \t]") (not-e "[^e!\n\"\& \t]")
b1ca7740 905 (not-n "[^n!\n\"\& \t]") (not-d "[^d!\n\"\& \t]")
5d16fdd7
SM
906 ;; (not-ib "[^i(!\n\"\& \t]") (not-s "[^s!\n\"\& \t]")
907 )
b974df0a
EN
908 (list
909 '(nil "^[ \t0-9]*program[ \t]+\\(\\sw+\\)" 1)
910 '("Modules" "^[ \t0-9]*module[ \t]+\\(\\sw+\\)[ \t]*\\(!\\|$\\)" 1)
b1ca7740
GM
911 (list "Types" 'f90-imenu-type-matcher 1)
912 ;; Does not handle: "type[, stuff] :: foo".
913;;; (format "^[ \t0-9]*type[ \t]+\\(\\(%s\\|i%s\\|is\\sw\\)\\sw*\\)"
914;;; not-ib not-s)
915;;; 1)
916 ;; Can't get the subexpression numbers to match in the two branches.
917;;; (format "^[ \t0-9]*type\\([ \t]*,.*\\(::\\)[ \t]*\\(\\sw+\\)\\|[ \t]+\\(\\(%s\\|i%s\\|is\\sw\\)\\sw*\\)\\)" not-ib not-s)
918;;; 3)
b974df0a 919 (list
69658465 920 "Procedures"
b974df0a
EN
921 (concat
922 "^[ \t0-9]*"
923 "\\("
ec2f376f
GM
924 ;; At least three non-space characters before function/subroutine.
925 ;; Check that the last three non-space characters do not spell E N D.
b974df0a
EN
926 "[^!\"\&\n]*\\("
927 not-e good-char good-char "\\|"
928 good-char not-n good-char "\\|"
929 good-char good-char not-d "\\)"
930 "\\|"
ec2f376f 931 ;; Less than three non-space characters before function/subroutine.
b974df0a
EN
932 good-char "?" good-char "?"
933 "\\)"
934 "[ \t]*\\(function\\|subroutine\\)[ \t]+\\(\\sw+\\)")
69658465 935 4)))
a729409a 936 "Value for `imenu-generic-expression' in F90 mode.")
ee30478d 937
b974df0a 938(defun f90-add-imenu-menu ()
b974df0a 939 "Add an imenu menu to the menubar."
87ee2359 940 (interactive)
34ba7e3d 941 (if (lookup-key (current-local-map) [menu-bar index])
5c2a80ad
GM
942 (message "%s" "F90-imenu already exists.")
943 (imenu-add-to-menubar "F90-imenu")
901e8d1d 944 (redraw-frame (selected-frame))))
b974df0a 945
034a9d40 946\f
ec2f376f 947;; Abbrevs have generally two letters, except standard types `c, `i, `r, `t.
5d16fdd7
SM
948(define-abbrev-table 'f90-mode-abbrev-table
949 (mapcar (lambda (e) (list (car e) (cdr e) nil :system t))
950 '(("`al" . "allocate" )
951 ("`ab" . "allocatable" )
952 ("`ai" . "abstract interface")
953 ("`as" . "assignment" )
954 ("`asy" . "asynchronous" )
955 ("`ba" . "backspace" )
956 ("`bd" . "block data" )
957 ("`c" . "character" )
958 ("`cl" . "close" )
959 ("`cm" . "common" )
960 ("`cx" . "complex" )
961 ("`cn" . "contains" )
962 ("`cy" . "cycle" )
963 ("`de" . "deallocate" )
964 ("`df" . "define" )
965 ("`di" . "dimension" )
966 ("`dp" . "double precision")
967 ("`dw" . "do while" )
968 ("`el" . "else" )
969 ("`eli" . "else if" )
970 ("`elw" . "elsewhere" )
971 ("`em" . "elemental" )
972 ("`e" . "enumerator" )
973 ("`eq" . "equivalence" )
974 ("`ex" . "external" )
975 ("`ey" . "entry" )
976 ("`fl" . "forall" )
977 ("`fo" . "format" )
978 ("`fu" . "function" )
979 ("`fa" . ".false." )
980 ("`im" . "implicit none")
981 ("`in" . "include" )
982 ("`i" . "integer" )
983 ("`it" . "intent" )
984 ("`if" . "interface" )
985 ("`lo" . "logical" )
986 ("`mo" . "module" )
987 ("`na" . "namelist" )
988 ("`nu" . "nullify" )
989 ("`op" . "optional" )
990 ("`pa" . "parameter" )
991 ("`po" . "pointer" )
992 ("`pr" . "print" )
993 ("`pi" . "private" )
994 ("`pm" . "program" )
995 ("`pr" . "protected" )
996 ("`pu" . "public" )
997 ("`r" . "real" )
998 ("`rc" . "recursive" )
999 ("`rt" . "return" )
1000 ("`rw" . "rewind" )
1001 ("`se" . "select" )
1002 ("`sq" . "sequence" )
1003 ("`su" . "subroutine" )
1004 ("`ta" . "target" )
1005 ("`tr" . ".true." )
1006 ("`t" . "type" )
1007 ("`vo" . "volatile" )
1008 ("`wh" . "where" )
1009 ("`wr" . "write" )))
1010 "Abbrev table for F90 mode."
1011 ;; Accept ` as the first char of an abbrev. Also allow _ in abbrevs.
1012 :regexp "\\(?:[^[:word:]_`]\\|^\\)\\(`?[[:word:]_]+\\)[^[:word:]_]*")
0761b294 1013\f
034a9d40
RS
1014;;;###autoload
1015(defun f90-mode ()
87ee2359 1016 "Major mode for editing Fortran 90,95 code in free format.
a729409a 1017For fixed format code, use `fortran-mode'.
034a9d40 1018
a729409a 1019\\[f90-indent-line] indents the current line.
ec2f376f 1020\\[f90-indent-new-line] indents current line and creates a new\
034a9d40 1021 indented line.
87ee2359 1022\\[f90-indent-subprogram] indents the current subprogram.
034a9d40
RS
1023
1024Type `? or `\\[help-command] to display a list of built-in\
1025 abbrevs for F90 keywords.
1026
1027Key definitions:
1028\\{f90-mode-map}
1029
1030Variables controlling indentation style and extra features:
1031
ec2f376f
GM
1032`f90-do-indent'
1033 Extra indentation within do blocks (default 3).
1034`f90-if-indent'
5ab33946 1035 Extra indentation within if/select/where/forall blocks (default 3).
ec2f376f 1036`f90-type-indent'
5ab33946 1037 Extra indentation within type/enum/interface/block-data blocks (default 3).
ec2f376f
GM
1038`f90-program-indent'
1039 Extra indentation within program/module/subroutine/function blocks
1040 (default 2).
1041`f90-continuation-indent'
1042 Extra indentation applied to continuation lines (default 5).
1043`f90-comment-region'
e3f5ce56
GM
1044 String inserted by function \\[f90-comment-region] at start of each
1045 line in region (default \"!!!$\").
ec2f376f
GM
1046`f90-indented-comment-re'
1047 Regexp determining the type of comment to be intended like code
1048 (default \"!\").
1049`f90-directive-comment-re'
1050 Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented
1051 (default \"!hpf\\\\$\").
1052`f90-break-delimiters'
1053 Regexp holding list of delimiters at which lines may be broken
1054 (default \"[-+*/><=,% \\t]\").
1055`f90-break-before-delimiters'
1056 Non-nil causes `f90-do-auto-fill' to break lines before delimiters
1057 (default t).
1058`f90-beginning-ampersand'
1059 Automatic insertion of \& at beginning of continuation lines (default t).
1060`f90-smart-end'
1061 From an END statement, check and fill the end using matching block start.
1062 Allowed values are 'blink, 'no-blink, and nil, which determine
1063 whether to blink the matching beginning (default 'blink).
1064`f90-auto-keyword-case'
1065 Automatic change of case of keywords (default nil).
1066 The possibilities are 'downcase-word, 'upcase-word, 'capitalize-word.
1067`f90-leave-line-no'
1068 Do not left-justify line numbers (default nil).
034a9d40
RS
1069
1070Turning on F90 mode calls the value of the variable `f90-mode-hook'
1071with no args, if that value is non-nil."
1072 (interactive)
1073 (kill-all-local-variables)
e3f5ce56
GM
1074 (setq major-mode 'f90-mode
1075 mode-name "F90"
1076 local-abbrev-table f90-mode-abbrev-table)
034a9d40
RS
1077 (set-syntax-table f90-mode-syntax-table)
1078 (use-local-map f90-mode-map)
e3f5ce56
GM
1079 (set (make-local-variable 'indent-line-function) 'f90-indent-line)
1080 (set (make-local-variable 'indent-region-function) 'f90-indent-region)
722d3132 1081 (set (make-local-variable 'require-final-newline) mode-require-final-newline)
e3f5ce56
GM
1082 (set (make-local-variable 'comment-start) "!")
1083 (set (make-local-variable 'comment-start-skip) "!+ *")
1084 (set (make-local-variable 'comment-indent-function) 'f90-comment-indent)
1085 (set (make-local-variable 'abbrev-all-caps) t)
1086 (set (make-local-variable 'normal-auto-fill-function) 'f90-do-auto-fill)
70186f7f 1087 (setq indent-tabs-mode nil) ; auto buffer local
e3f5ce56
GM
1088 (set (make-local-variable 'font-lock-defaults)
1089 '((f90-font-lock-keywords f90-font-lock-keywords-1
1090 f90-font-lock-keywords-2
1091 f90-font-lock-keywords-3
1092 f90-font-lock-keywords-4)
1093 nil t))
45d1e4d4 1094 (set (make-local-variable 'imenu-case-fold-search) t)
e3f5ce56 1095 (set (make-local-variable 'imenu-generic-expression)
a729409a
GM
1096 f90-imenu-generic-expression)
1097 (set (make-local-variable 'beginning-of-defun-function)
1098 'f90-beginning-of-subprogram)
1099 (set (make-local-variable 'end-of-defun-function) 'f90-end-of-subprogram)
d2d15846
DL
1100 (set (make-local-variable 'add-log-current-defun-function)
1101 #'f90-current-defun)
9a969196 1102 (run-mode-hooks 'f90-mode-hook))
ec2f376f 1103
034a9d40 1104\f
ec2f376f 1105;; Inline-functions.
034a9d40 1106(defsubst f90-in-string ()
d14e6bbe 1107 "Return non-nil if point is inside a string.
ec2f376f 1108Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 1109and lies before point."
034a9d40 1110 (let ((beg-pnt
640f9e26
GM
1111 (if (and f90-cache-position (> (point) f90-cache-position))
1112 f90-cache-position
1113 (point-min))))
034a9d40 1114 (nth 3 (parse-partial-sexp beg-pnt (point)))))
69658465 1115
034a9d40 1116(defsubst f90-in-comment ()
d14e6bbe 1117 "Return non-nil if point is inside a comment.
ec2f376f 1118Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 1119and lies before point."
034a9d40 1120 (let ((beg-pnt
640f9e26
GM
1121 (if (and f90-cache-position (> (point) f90-cache-position))
1122 f90-cache-position
1123 (point-min))))
034a9d40
RS
1124 (nth 4 (parse-partial-sexp beg-pnt (point)))))
1125
1126(defsubst f90-line-continued ()
d14e6bbe
GM
1127 "Return t if the current line is a continued one.
1128This includes comment lines embedded in continued lines, but
1129not the last line of a continued statement."
034a9d40 1130 (save-excursion
6734e165
GM
1131 (beginning-of-line)
1132 (while (and (looking-at "[ \t]*\\(!\\|$\\)") (zerop (forward-line -1))))
e3f5ce56
GM
1133 (end-of-line)
1134 (while (f90-in-comment)
1135 (search-backward "!" (line-beginning-position))
1136 (skip-chars-backward "!"))
1137 (skip-chars-backward " \t")
1138 (= (preceding-char) ?&)))
034a9d40 1139
748dd5a8
GM
1140;; GM this is not right, eg a continuation line starting with a number.
1141;; Need f90-code-start-position function.
1142;; And yet, things seems to work with this...
7aee8047
GM
1143;; cf f90-indent-line
1144;; (beginning-of-line) ; digits after & \n are not line-nos
1145;; (if (not (save-excursion (and (f90-previous-statement)
1146;; (f90-line-continued))))
1147;; (f90-indent-line-no)
034a9d40
RS
1148(defsubst f90-current-indentation ()
1149 "Return indentation of current line.
1150Line-numbers are considered whitespace characters."
e3f5ce56 1151 (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")))
034a9d40
RS
1152
1153(defsubst f90-indent-to (col &optional no-line-number)
1154 "Indent current line to column COL.
d14e6bbe
GM
1155If optional argument NO-LINE-NUMBER is nil, jump over a possible
1156line-number before indenting."
034a9d40 1157 (beginning-of-line)
748dd5a8 1158 (or no-line-number
034a9d40
RS
1159 (skip-chars-forward " \t0-9"))
1160 (delete-horizontal-space)
748dd5a8
GM
1161 ;; Leave >= 1 space after line number.
1162 (indent-to col (if (zerop (current-column)) 0 1)))
034a9d40 1163
034a9d40 1164(defsubst f90-get-present-comment-type ()
d14e6bbe 1165 "If point lies within a comment, return the string starting the comment.
718d0706
GM
1166For example, \"!\" or \"!!\", followed by the appropriate amount of
1167whitespace, if any."
1168 ;; Include the whitespace for consistent auto-filling of comment blocks.
034a9d40 1169 (save-excursion
e3f5ce56
GM
1170 (when (f90-in-comment)
1171 (beginning-of-line)
718d0706 1172 (re-search-forward "!+[ \t]*" (line-end-position))
e3f5ce56 1173 (while (f90-in-string)
718d0706
GM
1174 (re-search-forward "!+[ \t]*" (line-end-position)))
1175 (match-string-no-properties 0))))
034a9d40
RS
1176
1177(defsubst f90-equal-symbols (a b)
ec2f376f 1178 "Compare strings A and B neglecting case and allowing for nil value."
f14ca250
GM
1179 (equal (if a (downcase a) nil)
1180 (if b (downcase b) nil)))
034a9d40 1181
034a9d40 1182(defsubst f90-looking-at-do ()
d14e6bbe
GM
1183 "Return (\"do\" NAME) if a do statement starts after point.
1184NAME is nil if the statement has no label."
f14ca250 1185 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(do\\)\\>")
0db701f0 1186 (list (match-string 3) (match-string 2))))
ee30478d
KH
1187
1188(defsubst f90-looking-at-select-case ()
5ab33946 1189 "Return (\"select\" NAME) if a select statement starts after point.
d14e6bbe 1190NAME is nil if the statement has no label."
f14ca250 1191 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
5ab33946 1192\\(select\\)[ \t]*\\(case\\|type\\)[ \t]*(")
748dd5a8 1193 (list (match-string 3) (match-string 2))))
034a9d40
RS
1194
1195(defsubst f90-looking-at-if-then ()
d14e6bbe
GM
1196 "Return (\"if\" NAME) if an if () then statement starts after point.
1197NAME is nil if the statement has no label."
034a9d40 1198 (save-excursion
f14ca250
GM
1199 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(if\\)\\>")
1200 (let ((struct (match-string 3))
748dd5a8 1201 (label (match-string 2))
f14ca250
GM
1202 (pos (scan-lists (point) 1 0)))
1203 (and pos (goto-char pos))
5c2a80ad
GM
1204 (skip-chars-forward " \t")
1205 (if (or (looking-at "then\\>")
1206 (when (f90-line-continued)
1207 (f90-next-statement)
1208 (skip-chars-forward " \t0-9&")
1209 (looking-at "then\\>")))
1210 (list struct label))))))
034a9d40 1211
5ab33946
GM
1212;; FIXME label?
1213(defsubst f90-looking-at-associate ()
1214 "Return (\"associate\") if an associate block starts after point."
1215 (if (looking-at "\\<\\(associate\\)[ \t]*(")
1216 (list (match-string 1))))
1217
b32a3d99 1218(defsubst f90-looking-at-where-or-forall ()
d14e6bbe
GM
1219 "Return (KIND NAME) if a where or forall block starts after point.
1220NAME is nil if the statement has no label."
f14ca250
GM
1221 (save-excursion
1222 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
1223\\(where\\|forall\\)\\>")
1224 (let ((struct (match-string 3))
748dd5a8 1225 (label (match-string 2))
f14ca250
GM
1226 (pos (scan-lists (point) 1 0)))
1227 (and pos (goto-char pos))
1228 (skip-chars-forward " \t")
1229 (if (looking-at "\\(!\\|$\\)") (list struct label))))))
034a9d40
RS
1230
1231(defsubst f90-looking-at-type-like ()
5ab33946 1232 "Return (KIND NAME) if a type/enum/interface/block-data starts after point.
d14e6bbe 1233NAME is non-nil only for type."
69658465 1234 (cond
5ab33946 1235 ((save-excursion
5fe2902f 1236 (and (looking-at "\\<type\\>[ \t]*")
5ab33946
GM
1237 (goto-char (match-end 0))
1238 (not (looking-at "\\(is\\>\\|(\\)"))
1239 (or (looking-at "\\(\\sw+\\)")
1240 (re-search-forward "[ \t]*::[ \t]*\\(\\sw+\\)"
1241 (line-end-position) t))))
1242 (list "type" (match-string 1)))
1243;;; ((and (not (looking-at f90-typeis-re))
1244;;; (looking-at f90-type-def-re))
1245;;; (list (match-string 1) (match-string 2)))
1246 ((looking-at "\\(enum\\|interface\\|block[ \t]*data\\)\\>")
1247 (list (match-string 1) nil))
1248 ((looking-at "abstract[ \t]*\\(interface\\)\\>")
6734e165 1249 (list (match-string 1) nil))))
034a9d40
RS
1250
1251(defsubst f90-looking-at-program-block-start ()
d14e6bbe 1252 "Return (KIND NAME) if a program block with name NAME starts after point."
784d007b 1253;;;NAME is nil for an un-named main PROGRAM block."
034a9d40 1254 (cond
ee30478d 1255 ((looking-at "\\(program\\)[ \t]+\\(\\sw+\\)\\>")
6734e165 1256 (list (match-string 1) (match-string 2)))
034a9d40 1257 ((and (not (looking-at "module[ \t]*procedure\\>"))
640f9e26 1258 (looking-at "\\(module\\)[ \t]+\\(\\sw+\\)\\>"))
6734e165 1259 (list (match-string 1) (match-string 2)))
b974df0a 1260 ((and (not (looking-at "end[ \t]*\\(function\\|subroutine\\)"))
640f9e26 1261 (looking-at "[^!'\"\&\n]*\\(function\\|subroutine\\)[ \t]+\
748dd5a8 1262\\(\\sw+\\)"))
6734e165 1263 (list (match-string 1) (match-string 2)))))
784d007b
GM
1264;; Following will match an un-named main program block; however
1265;; one needs to check if there is an actual PROGRAM statement after
1266;; point (and before any END program). Adding this will require
1267;; change to eg f90-calculate-indent.
1268;;; ((save-excursion
1269;;; (not (f90-previous-statement)))
1270;;; '("program" nil))))
034a9d40
RS
1271
1272(defsubst f90-looking-at-program-block-end ()
d14e6bbe 1273 "Return (KIND NAME) if a block with name NAME ends after point."
69658465 1274 (if (looking-at (concat "end[ \t]*" f90-blocks-re
640f9e26 1275 "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
6734e165 1276 (list (match-string 1) (match-string 3))))
034a9d40
RS
1277
1278(defsubst f90-comment-indent ()
ec2f376f
GM
1279 "Return the indentation to be used for a comment starting at point.
1280Used for `comment-indent-function' by F90 mode.
1281\"!!!\", `f90-directive-comment-re', variable `f90-comment-region' return 0.
1282`f90-indented-comment-re' (if not trailing code) calls `f90-calculate-indent'.
89fa1ef5 1283All others return `comment-column', leaving at least one space after code."
034a9d40 1284 (cond ((looking-at "!!!") 0)
640f9e26
GM
1285 ((and f90-directive-comment-re
1286 (looking-at f90-directive-comment-re)) 0)
1287 ((looking-at (regexp-quote f90-comment-region)) 0)
1288 ((and (looking-at f90-indented-comment-re)
1289 ;; Don't attempt to indent trailing comment as code.
1290 (save-excursion
1291 (skip-chars-backward " \t")
1292 (bolp)))
1293 (f90-calculate-indent))
1294 (t (save-excursion
b464e0ee
GM
1295 (skip-chars-backward " \t")
1296 (max (if (bolp) 0 (1+ (current-column))) comment-column)))))
034a9d40
RS
1297
1298(defsubst f90-present-statement-cont ()
d14e6bbe
GM
1299 "Return continuation properties of present statement.
1300Possible return values are:
1301single - statement is not continued.
1302begin - current line is the first in a continued statement.
1303end - current line is the last in a continued statement
1304middle - current line is neither first nor last in a continued statement.
1305Comment lines embedded amongst continued lines return 'middle."
034a9d40
RS
1306 (let (pcont cont)
1307 (save-excursion
e3f5ce56 1308 (setq pcont (if (f90-previous-statement) (f90-line-continued))))
034a9d40
RS
1309 (setq cont (f90-line-continued))
1310 (cond ((and (not pcont) (not cont)) 'single)
640f9e26
GM
1311 ((and (not pcont) cont) 'begin)
1312 ((and pcont (not cont)) 'end)
1313 ((and pcont cont) 'middle)
1314 (t (error "The impossible occurred")))))
034a9d40
RS
1315
1316(defsubst f90-indent-line-no ()
d14e6bbe
GM
1317 "If `f90-leave-line-no' is nil, left-justify a line number.
1318Leaves point at the first non-blank character after the line number.
1319Call from beginning of line."
748dd5a8
GM
1320 (and (null f90-leave-line-no) (looking-at "[ \t]+[0-9]")
1321 (delete-horizontal-space))
034a9d40
RS
1322 (skip-chars-forward " \t0-9"))
1323
1324(defsubst f90-no-block-limit ()
d14e6bbe
GM
1325 "Return nil if point is at the edge of a code block.
1326Searches line forward for \"function\" or \"subroutine\",
1327if all else fails."
748dd5a8
GM
1328 (save-excursion
1329 (not (or (looking-at "end")
1330 (looking-at "\\(do\\|if\\|else\\(if\\|where\\)?\
5ab33946
GM
1331\\|select[ \t]*\\(case\\|type\\)\\|case\\|where\\|forall\\)\\>")
1332 (looking-at "\\(program\\|module\\|\
b1ca7740 1333\\(?:abstract[ \t]*\\)?interface\\|block[ \t]*data\\)\\>")
748dd5a8
GM
1334 (looking-at "\\(contains\\|\\sw+[ \t]*:\\)")
1335 (looking-at f90-type-def-re)
1336 (re-search-forward "\\(function\\|subroutine\\)"
1337 (line-end-position) t)))))
034a9d40
RS
1338
1339(defsubst f90-update-line ()
d14e6bbe
GM
1340 "Change case of current line as per `f90-auto-keyword-case'."
1341 (if f90-auto-keyword-case
1342 (f90-change-keywords f90-auto-keyword-case
1343 (line-beginning-position) (line-end-position))))
034a9d40 1344\f
d694ccd7 1345(defun f90-electric-insert (&optional arg)
d14e6bbe 1346 "Change keyword case and auto-fill line as operators are inserted."
d694ccd7
GM
1347 (interactive "*p")
1348 (self-insert-command arg)
d14e6bbe
GM
1349 (if auto-fill-function (f90-do-auto-fill) ; also updates line
1350 (f90-update-line)))
1351
7cae52cf 1352
034a9d40
RS
1353(defun f90-get-correct-indent ()
1354 "Get correct indent for a line starting with line number.
1355Does not check type and subprogram indentation."
6734e165 1356 (let ((epnt (line-end-position)) icol cont)
034a9d40
RS
1357 (save-excursion
1358 (while (and (f90-previous-statement)
640f9e26
GM
1359 (or (progn
1360 (setq cont (f90-present-statement-cont))
1361 (or (eq cont 'end) (eq cont 'middle)))
1362 (looking-at "[ \t]*[0-9]"))))
034a9d40
RS
1363 (setq icol (current-indentation))
1364 (beginning-of-line)
5c2a80ad
GM
1365 (when (re-search-forward "\\(if\\|do\\|select\\|where\\|forall\\)"
1366 (line-end-position) t)
e3f5ce56
GM
1367 (beginning-of-line)
1368 (skip-chars-forward " \t")
5c2a80ad
GM
1369 (cond ((f90-looking-at-do)
1370 (setq icol (+ icol f90-do-indent)))
1371 ((or (f90-looking-at-if-then)
1372 (f90-looking-at-where-or-forall)
1373 (f90-looking-at-select-case))
5ab33946
GM
1374 (setq icol (+ icol f90-if-indent)))
1375 ((f90-looking-at-associate)
1376 (setq icol (+ icol f90-associate-indent))))
5c2a80ad 1377 (end-of-line))
034a9d40 1378 (while (re-search-forward
640f9e26
GM
1379 "\\(if\\|do\\|select\\|where\\|forall\\)" epnt t)
1380 (beginning-of-line)
e3f5ce56 1381 (skip-chars-forward " \t0-9")
640f9e26 1382 (cond ((f90-looking-at-do)
e3f5ce56
GM
1383 (setq icol (+ icol f90-do-indent)))
1384 ((or (f90-looking-at-if-then)
1385 (f90-looking-at-where-or-forall)
1386 (f90-looking-at-select-case))
1387 (setq icol (+ icol f90-if-indent)))
5ab33946
GM
1388 ((f90-looking-at-associate)
1389 (setq icol (+ icol f90-associate-indent)))
e3f5ce56
GM
1390 ((looking-at f90-end-if-re)
1391 (setq icol (- icol f90-if-indent)))
5ab33946
GM
1392 ((looking-at f90-end-associate-re)
1393 (setq icol (- icol f90-associate-indent)))
e3f5ce56
GM
1394 ((looking-at "end[ \t]*do\\>")
1395 (setq icol (- icol f90-do-indent))))
640f9e26 1396 (end-of-line))
034a9d40 1397 icol)))
69658465 1398
034a9d40
RS
1399(defun f90-calculate-indent ()
1400 "Calculate the indent column based on previous statements."
1401 (interactive)
1402 (let (icol cont (case-fold-search t) (pnt (point)))
1403 (save-excursion
1404 (if (not (f90-previous-statement))
b78cbdf7
GM
1405 ;; If f90-previous-statement returns nil, we must have been
1406 ;; called from on or before the first line of the first statement.
640f9e26 1407 (setq icol (if (save-excursion
b78cbdf7
GM
1408 ;; f90-previous-statement has moved us over
1409 ;; comment/blank lines, so we need to get
1410 ;; back to the first code statement.
1411 (when (looking-at "[ \t]*\\([!#]\\|$\\)")
1412 (f90-next-statement))
1413 (skip-chars-forward " \t0-9")
784d007b
GM
1414 (f90-looking-at-program-block-start))
1415 0
1416 ;; No explicit PROGRAM start statement.
1417 f90-program-indent))
640f9e26
GM
1418 (setq cont (f90-present-statement-cont))
1419 (if (eq cont 'end)
1420 (while (not (eq 'begin (f90-present-statement-cont)))
1421 (f90-previous-statement)))
1422 (cond ((eq cont 'begin)
1423 (setq icol (+ (f90-current-indentation)
1424 f90-continuation-indent)))
1425 ((eq cont 'middle) (setq icol (current-indentation)))
1426 (t (setq icol (f90-current-indentation))
1427 (skip-chars-forward " \t")
1428 (if (looking-at "[0-9]")
1429 (setq icol (f90-get-correct-indent))
1430 (cond ((or (f90-looking-at-if-then)
1431 (f90-looking-at-where-or-forall)
1432 (f90-looking-at-select-case)
1433 (looking-at f90-else-like-re))
1434 (setq icol (+ icol f90-if-indent)))
1435 ((f90-looking-at-do)
1436 (setq icol (+ icol f90-do-indent)))
1437 ((f90-looking-at-type-like)
1438 (setq icol (+ icol f90-type-indent)))
5ab33946
GM
1439 ((f90-looking-at-associate)
1440 (setq icol (+ icol f90-associate-indent)))
640f9e26
GM
1441 ((or (f90-looking-at-program-block-start)
1442 (looking-at "contains[ \t]*\\($\\|!\\)"))
1443 (setq icol (+ icol f90-program-indent)))))
1444 (goto-char pnt)
1445 (beginning-of-line)
1446 (cond ((looking-at "[ \t]*$"))
1447 ((looking-at "[ \t]*#") ; check for cpp directive
1448 (setq icol 0))
1449 (t
1450 (skip-chars-forward " \t0-9")
1451 (cond ((or (looking-at f90-else-like-re)
1452 (looking-at f90-end-if-re))
1453 (setq icol (- icol f90-if-indent)))
1454 ((looking-at "end[ \t]*do\\>")
1455 (setq icol (- icol f90-do-indent)))
1456 ((looking-at f90-end-type-re)
1457 (setq icol (- icol f90-type-indent)))
5ab33946
GM
1458 ((looking-at f90-end-associate-re)
1459 (setq icol (- icol f90-associate-indent)))
640f9e26
GM
1460 ((or (looking-at "contains[ \t]*\\(!\\|$\\)")
1461 (f90-looking-at-program-block-end))
599aeab9 1462 (setq icol (- icol f90-program-indent))))))))))
034a9d40
RS
1463 icol))
1464\f
034a9d40
RS
1465(defun f90-previous-statement ()
1466 "Move point to beginning of the previous F90 statement.
784d007b
GM
1467If no previous statement is found (i.e. if called from the first
1468statement in the buffer), move to the start of the buffer and
1469return nil. A statement is a line which is neither blank nor a
1470comment."
034a9d40
RS
1471 (interactive)
1472 (let (not-first-statement)
1473 (beginning-of-line)
1474 (while (and (setq not-first-statement (zerop (forward-line -1)))
640f9e26 1475 (looking-at "[ \t0-9]*\\(!\\|$\\|#\\)")))
034a9d40
RS
1476 not-first-statement))
1477
1478(defun f90-next-statement ()
1479 "Move point to beginning of the next F90 statement.
1480Return nil if no later statement is found."
1481 (interactive)
1482 (let (not-last-statement)
1483 (beginning-of-line)
1484 (while (and (setq not-last-statement
640f9e26
GM
1485 (and (zerop (forward-line 1))
1486 (not (eobp))))
1487 (looking-at "[ \t0-9]*\\(!\\|$\\)")))
034a9d40
RS
1488 not-last-statement))
1489
1490(defun f90-beginning-of-subprogram ()
76bccf35 1491 "Move point to the beginning of the current subprogram.
ec2f376f 1492Return (TYPE NAME), or nil if not found."
034a9d40
RS
1493 (interactive)
1494 (let ((count 1) (case-fold-search t) matching-beg)
e3f5ce56 1495 (beginning-of-line)
76bccf35 1496 (while (and (> count 0)
640f9e26 1497 (re-search-backward f90-program-block-re nil 'move))
e3f5ce56
GM
1498 (beginning-of-line)
1499 (skip-chars-forward " \t0-9")
1500 (cond ((setq matching-beg (f90-looking-at-program-block-start))
1501 (setq count (1- count)))
1502 ((f90-looking-at-program-block-end)
1503 (setq count (1+ count)))))
034a9d40
RS
1504 (beginning-of-line)
1505 (if (zerop count)
640f9e26 1506 matching-beg
784d007b
GM
1507 ;; Note this includes the case of an un-named main program,
1508 ;; in which case we go to (point-min).
575b6746 1509 (if (interactive-p) (message "No beginning found"))
034a9d40
RS
1510 nil)))
1511
1512(defun f90-end-of-subprogram ()
76bccf35 1513 "Move point to the end of the current subprogram.
ec2f376f 1514Return (TYPE NAME), or nil if not found."
034a9d40 1515 (interactive)
7aee8047 1516 (let ((case-fold-search t)
575b6746 1517 (count 1)
7aee8047 1518 matching-end)
034a9d40 1519 (end-of-line)
76bccf35 1520 (while (and (> count 0)
640f9e26 1521 (re-search-forward f90-program-block-re nil 'move))
e3f5ce56
GM
1522 (beginning-of-line)
1523 (skip-chars-forward " \t0-9")
034a9d40 1524 (cond ((f90-looking-at-program-block-start)
640f9e26
GM
1525 (setq count (1+ count)))
1526 ((setq matching-end (f90-looking-at-program-block-end))
1527 (setq count (1- count))))
034a9d40 1528 (end-of-line))
6f1d50da
GM
1529 ;; This means f90-end-of-subprogram followed by f90-start-of-subprogram
1530 ;; has a net non-zero effect, which seems odd.
1531;;; (forward-line 1)
034a9d40 1532 (if (zerop count)
640f9e26 1533 matching-end
575b6746 1534 (if (interactive-p) (message "No end found"))
034a9d40
RS
1535 nil)))
1536
6f1d50da
GM
1537
1538(defun f90-end-of-block (&optional num)
1539 "Move point forward to the end of the current code block.
1540With optional argument NUM, go forward that many balanced blocks.
784d007b
GM
1541If NUM is negative, go backward to the start of a block. Checks
1542for consistency of block types and labels (if present), and
1543completes outermost block if `f90-smart-end' is non-nil.
1544Interactively, pushes mark before moving point."
6f1d50da 1545 (interactive "p")
784d007b
GM
1546 (if (interactive-p) (push-mark (point) t)) ; can move some distance
1547 (and num (< num 0) (f90-beginning-of-block (- num)))
1548 (let ((f90-smart-end (if f90-smart-end 'no-blink)) ; for final match-end
6f1d50da
GM
1549 (case-fold-search t)
1550 (count (or num 1))
1551 start-list start-this start-type start-label end-type end-label)
6f1d50da
GM
1552 (end-of-line) ; probably want this
1553 (while (and (> count 0) (re-search-forward f90-blocks-re nil 'move))
1554 (beginning-of-line)
1555 (skip-chars-forward " \t0-9")
1556 (cond ((or (f90-in-string) (f90-in-comment)))
1557 ((setq start-this
1558 (or
1559 (f90-looking-at-do)
1560 (f90-looking-at-select-case)
1561 (f90-looking-at-type-like)
5ab33946 1562 (f90-looking-at-associate)
6f1d50da
GM
1563 (f90-looking-at-program-block-start)
1564 (f90-looking-at-if-then)
1565 (f90-looking-at-where-or-forall)))
1566 (setq start-list (cons start-this start-list) ; not add-to-list!
1567 count (1+ count)))
1568 ((looking-at (concat "end[ \t]*" f90-blocks-re
1569 "[ \t]*\\(\\sw+\\)?"))
1570 (setq end-type (match-string 1)
1571 end-label (match-string 2)
1572 count (1- count))
1573 ;; Check any internal blocks.
1574 (when start-list
1575 (setq start-this (car start-list)
1576 start-list (cdr start-list)
1577 start-type (car start-this)
1578 start-label (cadr start-this))
748dd5a8 1579 (or (f90-equal-symbols start-type end-type)
6f1d50da
GM
1580 (error "End type `%s' does not match start type `%s'"
1581 end-type start-type))
748dd5a8 1582 (or (f90-equal-symbols start-label end-label)
6f1d50da
GM
1583 (error "End label `%s' does not match start label `%s'"
1584 end-label start-label)))))
1585 (end-of-line))
76bccf35 1586 (if (> count 0) (error "Missing block end"))
6f1d50da 1587 ;; Check outermost block.
784d007b
GM
1588 (when f90-smart-end
1589 (save-excursion
1590 (beginning-of-line)
1591 (skip-chars-forward " \t0-9")
1592 (f90-match-end)))))
6f1d50da
GM
1593
1594(defun f90-beginning-of-block (&optional num)
1595 "Move point backwards to the start of the current code block.
1596With optional argument NUM, go backward that many balanced blocks.
1597If NUM is negative, go forward to the end of a block.
1598Checks for consistency of block types and labels (if present).
784d007b
GM
1599Does not check the outermost block, because it may be incomplete.
1600Interactively, pushes mark before moving point."
6f1d50da 1601 (interactive "p")
fcca5273 1602 (if (interactive-p) (push-mark (point) t))
784d007b 1603 (and num (< num 0) (f90-end-of-block (- num)))
6f1d50da
GM
1604 (let ((case-fold-search t)
1605 (count (or num 1))
748dd5a8
GM
1606 end-list end-this end-type end-label
1607 start-this start-type start-label)
6f1d50da
GM
1608 (beginning-of-line) ; probably want this
1609 (while (and (> count 0) (re-search-backward f90-blocks-re nil 'move))
1610 (beginning-of-line)
1611 (skip-chars-forward " \t0-9")
1612 (cond ((or (f90-in-string) (f90-in-comment)))
1613 ((looking-at (concat "end[ \t]*" f90-blocks-re
1614 "[ \t]*\\(\\sw+\\)?"))
1615 (setq end-list (cons (list (match-string 1) (match-string 2))
1616 end-list)
1617 count (1+ count)))
1618 ((setq start-this
1619 (or
1620 (f90-looking-at-do)
1621 (f90-looking-at-select-case)
1622 (f90-looking-at-type-like)
5ab33946 1623 (f90-looking-at-associate)
6f1d50da
GM
1624 (f90-looking-at-program-block-start)
1625 (f90-looking-at-if-then)
1626 (f90-looking-at-where-or-forall)))
1627 (setq start-type (car start-this)
1628 start-label (cadr start-this)
1629 count (1- count))
1630 ;; Check any internal blocks.
1631 (when end-list
1632 (setq end-this (car end-list)
1633 end-list (cdr end-list)
1634 end-type (car end-this)
1635 end-label (cadr end-this))
748dd5a8 1636 (or (f90-equal-symbols start-type end-type)
6f1d50da
GM
1637 (error "Start type `%s' does not match end type `%s'"
1638 start-type end-type))
748dd5a8 1639 (or (f90-equal-symbols start-label end-label)
6f1d50da
GM
1640 (error "Start label `%s' does not match end label `%s'"
1641 start-label end-label))))))
784d007b
GM
1642 ;; Includes an un-named main program block.
1643 (if (> count 0) (error "Missing block start"))))
6f1d50da 1644
76bccf35
GM
1645(defun f90-next-block (&optional num)
1646 "Move point forward to the next end or start of a code block.
1647With optional argument NUM, go forward that many blocks.
1648If NUM is negative, go backwards.
1649A block is a subroutine, if-endif, etc."
6f1d50da 1650 (interactive "p")
76bccf35
GM
1651 (let ((case-fold-search t)
1652 (count (if num (abs num) 1)))
1653 (while (and (> count 0)
1654 (if (> num 0) (re-search-forward f90-blocks-re nil 'move)
1655 (re-search-backward f90-blocks-re nil 'move)))
6f1d50da
GM
1656 (beginning-of-line)
1657 (skip-chars-forward " \t0-9")
76bccf35
GM
1658 (cond ((or (f90-in-string) (f90-in-comment)))
1659 ((or
1660 (looking-at "end[ \t]*")
1661 (f90-looking-at-do)
1662 (f90-looking-at-select-case)
1663 (f90-looking-at-type-like)
5ab33946 1664 (f90-looking-at-associate)
76bccf35
GM
1665 (f90-looking-at-program-block-start)
1666 (f90-looking-at-if-then)
1667 (f90-looking-at-where-or-forall))
1668 (setq count (1- count))))
1669 (if (> num 0) (end-of-line)
1670 (beginning-of-line)))))
1671
1672
1673(defun f90-previous-block (&optional num)
1674 "Move point backward to the previous end or start of a code block.
1675With optional argument NUM, go backward that many blocks.
1676If NUM is negative, go forwards.
1677A block is a subroutine, if-endif, etc."
6f1d50da 1678 (interactive "p")
76bccf35 1679 (f90-next-block (- (or num 1))))
6f1d50da
GM
1680
1681
034a9d40 1682(defun f90-mark-subprogram ()
a729409a 1683 "Put mark at end of F90 subprogram, point at beginning, push mark."
034a9d40
RS
1684 (interactive)
1685 (let ((pos (point)) program)
1686 (f90-end-of-subprogram)
0ee7f068 1687 (push-mark)
034a9d40
RS
1688 (goto-char pos)
1689 (setq program (f90-beginning-of-subprogram))
a445370f 1690 (if (featurep 'xemacs)
0ee7f068 1691 (zmacs-activate-region)
1bb3ae5c 1692 (setq mark-active t
0ee7f068 1693 deactivate-mark nil))
034a9d40
RS
1694 program))
1695
1696(defun f90-comment-region (beg-region end-region)
1697 "Comment/uncomment every line in the region.
d14e6bbe
GM
1698Insert the variable `f90-comment-region' at the start of every line
1699in the region, or, if already present, remove it."
034a9d40 1700 (interactive "*r")
748dd5a8 1701 (let ((end (copy-marker end-region)))
034a9d40
RS
1702 (goto-char beg-region)
1703 (beginning-of-line)
1704 (if (looking-at (regexp-quote f90-comment-region))
640f9e26 1705 (delete-region (point) (match-end 0))
034a9d40 1706 (insert f90-comment-region))
e3f5ce56 1707 (while (and (zerop (forward-line 1))
640f9e26 1708 (< (point) end))
034a9d40 1709 (if (looking-at (regexp-quote f90-comment-region))
640f9e26
GM
1710 (delete-region (point) (match-end 0))
1711 (insert f90-comment-region)))
034a9d40
RS
1712 (set-marker end nil)))
1713
1714(defun f90-indent-line (&optional no-update)
87ee2359
GM
1715 "Indent current line as F90 code.
1716Unless optional argument NO-UPDATE is non-nil, call `f90-update-line'
1717after indenting."
a729409a 1718 (interactive "*P")
748dd5a8
GM
1719 (let ((case-fold-search t)
1720 (pos (point-marker))
1721 indent no-line-number)
1722 (beginning-of-line) ; digits after & \n are not line-nos
1723 (if (not (save-excursion (and (f90-previous-statement)
1724 (f90-line-continued))))
1725 (f90-indent-line-no)
1726 (setq no-line-number t)
1727 (skip-chars-forward " \t"))
034a9d40 1728 (if (looking-at "!")
640f9e26 1729 (setq indent (f90-comment-indent))
ab09adac 1730 (and f90-smart-end (looking-at "end")
748dd5a8 1731 (f90-match-end))
034a9d40 1732 (setq indent (f90-calculate-indent)))
748dd5a8 1733 (or (= indent (current-column))
e3f5ce56 1734 (f90-indent-to indent no-line-number))
034a9d40
RS
1735 ;; If initial point was within line's indentation,
1736 ;; position after the indentation. Else stay at same point in text.
748dd5a8
GM
1737 (and (< (point) pos)
1738 (goto-char pos))
69658465 1739 (if auto-fill-function
7aee8047
GM
1740 ;; GM NO-UPDATE not honoured, since this calls f90-update-line.
1741 (f90-do-auto-fill)
748dd5a8 1742 (or no-update (f90-update-line)))
034a9d40
RS
1743 (set-marker pos nil)))
1744
1745(defun f90-indent-new-line ()
a729409a 1746 "Re-indent current line, insert a newline and indent the newline.
87ee2359 1747An abbrev before point is expanded if the variable `abbrev-mode' is non-nil.
034a9d40 1748If run in the middle of a line, the line is not broken."
a729409a 1749 (interactive "*")
748dd5a8
GM
1750 (if abbrev-mode (expand-abbrev))
1751 (beginning-of-line) ; reindent where likely to be needed
7aee8047 1752 (f90-indent-line) ; calls indent-line-no, update-line
748dd5a8 1753 (end-of-line)
640f9e26 1754 (delete-horizontal-space) ; destroy trailing whitespace
748dd5a8
GM
1755 (let ((string (f90-in-string))
1756 (cont (f90-line-continued)))
1757 (and string (not cont) (insert "&"))
034a9d40 1758 (newline)
748dd5a8 1759 (if (or string (and cont f90-beginning-ampersand)) (insert "&")))
7aee8047 1760 (f90-indent-line 'no-update)) ; nothing to update
034a9d40
RS
1761
1762
784d007b
GM
1763;; TODO not add spaces to empty lines at the start.
1764;; Why is second line getting extra indent over first?
034a9d40
RS
1765(defun f90-indent-region (beg-region end-region)
1766 "Indent every line in region by forward parsing."
1767 (interactive "*r")
748dd5a8 1768 (let ((end-region-mark (copy-marker end-region))
e3f5ce56 1769 (save-point (point-marker))
cbc121c7 1770 (case-fold-search t)
640f9e26 1771 block-list ind-lev ind-curr ind-b cont struct beg-struct end-struct)
034a9d40 1772 (goto-char beg-region)
ec2f376f 1773 ;; First find a line which is not a continuation line or comment.
034a9d40 1774 (beginning-of-line)
ee30478d 1775 (while (and (looking-at "[ \t]*[0-9]*\\(!\\|#\\|[ \t]*$\\)")
640f9e26
GM
1776 (progn (f90-indent-line 'no-update)
1777 (zerop (forward-line 1)))
1778 (< (point) end-region-mark)))
034a9d40
RS
1779 (setq cont (f90-present-statement-cont))
1780 (while (and (or (eq cont 'middle) (eq cont 'end))
640f9e26 1781 (f90-previous-statement))
034a9d40 1782 (setq cont (f90-present-statement-cont)))
ec2f376f 1783 ;; Process present line for beginning of block.
034a9d40
RS
1784 (setq f90-cache-position (point))
1785 (f90-indent-line 'no-update)
e3f5ce56
GM
1786 (setq ind-lev (f90-current-indentation)
1787 ind-curr ind-lev)
1788 (beginning-of-line)
1789 (skip-chars-forward " \t0-9")
1790 (setq struct nil
1791 ind-b (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
640f9e26
GM
1792 ((or (setq struct (f90-looking-at-if-then))
1793 (setq struct (f90-looking-at-select-case))
1794 (setq struct (f90-looking-at-where-or-forall))
1795 (looking-at f90-else-like-re))
1796 f90-if-indent)
1797 ((setq struct (f90-looking-at-type-like))
1798 f90-type-indent)
5ab33946
GM
1799 ((setq struct (f90-looking-at-associate))
1800 f90-associate-indent)
640f9e26 1801 ((or (setq struct (f90-looking-at-program-block-start))
34ba7e3d 1802 (looking-at "contains[ \t]*\\($\\|!\\)"))
640f9e26 1803 f90-program-indent)))
034a9d40
RS
1804 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1805 (if struct (setq block-list (cons struct block-list)))
1806 (while (and (f90-line-continued) (zerop (forward-line 1))
640f9e26 1807 (< (point) end-region-mark))
ec2f376f
GM
1808 (if (looking-at "[ \t]*!")
1809 (f90-indent-to (f90-comment-indent))
748dd5a8
GM
1810 (or (= (current-indentation)
1811 (+ ind-curr f90-continuation-indent))
ec2f376f
GM
1812 (f90-indent-to (+ ind-curr f90-continuation-indent) 'no-line-no))))
1813 ;; Process all following lines.
d14e6bbe 1814 (while (and (zerop (forward-line 1)) (< (point) end-region-mark))
034a9d40
RS
1815 (beginning-of-line)
1816 (f90-indent-line-no)
1817 (setq f90-cache-position (point))
1818 (cond ((looking-at "[ \t]*$") (setq ind-curr 0))
640f9e26
GM
1819 ((looking-at "[ \t]*#") (setq ind-curr 0))
1820 ((looking-at "!") (setq ind-curr (f90-comment-indent)))
1821 ((f90-no-block-limit) (setq ind-curr ind-lev))
1822 ((looking-at f90-else-like-re) (setq ind-curr
1823 (- ind-lev f90-if-indent)))
1824 ((looking-at "contains[ \t]*\\($\\|!\\)")
1825 (setq ind-curr (- ind-lev f90-program-indent)))
1826 ((setq ind-b
1827 (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
1828 ((or (setq struct (f90-looking-at-if-then))
1829 (setq struct (f90-looking-at-select-case))
1830 (setq struct (f90-looking-at-where-or-forall)))
1831 f90-if-indent)
1832 ((setq struct (f90-looking-at-type-like))
1833 f90-type-indent)
5ab33946
GM
1834 ((setq struct (f90-looking-at-associate))
1835 f90-associate-indent)
640f9e26
GM
1836 ((setq struct (f90-looking-at-program-block-start))
1837 f90-program-indent)))
1838 (setq ind-curr ind-lev)
1839 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1840 (setq block-list (cons struct block-list)))
1841 ((setq end-struct (f90-looking-at-program-block-end))
1842 (setq beg-struct (car block-list)
1843 block-list (cdr block-list))
1844 (if f90-smart-end
1845 (save-excursion
599aeab9
GM
1846 (f90-block-match (car beg-struct) (cadr beg-struct)
1847 (car end-struct) (cadr end-struct))))
640f9e26
GM
1848 (setq ind-b
1849 (cond ((looking-at f90-end-if-re) f90-if-indent)
1850 ((looking-at "end[ \t]*do\\>") f90-do-indent)
1851 ((looking-at f90-end-type-re) f90-type-indent)
5ab33946
GM
1852 ((looking-at f90-end-associate-re)
1853 f90-associate-indent)
640f9e26
GM
1854 ((f90-looking-at-program-block-end)
1855 f90-program-indent)))
1856 (if ind-b (setq ind-lev (- ind-lev ind-b)))
1857 (setq ind-curr ind-lev))
1858 (t (setq ind-curr ind-lev)))
ec2f376f 1859 ;; Do the indentation if necessary.
748dd5a8 1860 (or (= ind-curr (current-column))
640f9e26 1861 (f90-indent-to ind-curr))
034a9d40 1862 (while (and (f90-line-continued) (zerop (forward-line 1))
640f9e26 1863 (< (point) end-region-mark))
ec2f376f
GM
1864 (if (looking-at "[ \t]*!")
1865 (f90-indent-to (f90-comment-indent))
748dd5a8
GM
1866 (or (= (current-indentation)
1867 (+ ind-curr f90-continuation-indent))
ec2f376f
GM
1868 (f90-indent-to
1869 (+ ind-curr f90-continuation-indent) 'no-line-no)))))
1870 ;; Restore point, etc.
034a9d40
RS
1871 (setq f90-cache-position nil)
1872 (goto-char save-point)
1873 (set-marker end-region-mark nil)
1874 (set-marker save-point nil)
a445370f 1875 (if (featurep 'xemacs)
640f9e26 1876 (zmacs-deactivate-region)
034a9d40
RS
1877 (deactivate-mark))))
1878
1879(defun f90-indent-subprogram ()
ec2f376f 1880 "Properly indent the subprogram containing point."
a729409a 1881 (interactive "*")
034a9d40 1882 (save-excursion
e3f5ce56 1883 (let ((program (f90-mark-subprogram)))
034a9d40 1884 (if program
640f9e26
GM
1885 (progn
1886 (message "Indenting %s %s..."
599aeab9 1887 (car program) (cadr program))
640f9e26
GM
1888 (indent-region (point) (mark) nil)
1889 (message "Indenting %s %s...done"
599aeab9 1890 (car program) (cadr program)))
640f9e26
GM
1891 (message "Indenting the whole file...")
1892 (indent-region (point) (mark) nil)
1893 (message "Indenting the whole file...done")))))
034a9d40 1894
034a9d40 1895(defun f90-break-line (&optional no-update)
87ee2359
GM
1896 "Break line at point, insert continuation marker(s) and indent.
1897Unless in a string or comment, or if the optional argument NO-UPDATE
1898is non-nil, call `f90-update-line' after inserting the continuation marker."
a729409a 1899 (interactive "*P")
89fa1ef5
GM
1900 (cond ((f90-in-string)
1901 (insert "&\n&"))
1902 ((f90-in-comment)
718d0706 1903 (delete-horizontal-space 'backwards) ; remove trailing whitespace
89fa1ef5
GM
1904 (insert "\n" (f90-get-present-comment-type)))
1905 (t (insert "&")
1906 (or no-update (f90-update-line))
1907 (newline 1)
1908 (if f90-beginning-ampersand (insert "&"))))
84021009 1909 (indent-according-to-mode))
69658465 1910
034a9d40 1911(defun f90-find-breakpoint ()
87ee2359 1912 "From `fill-column', search backward for break-delimiter."
748dd5a8
GM
1913 (re-search-backward f90-break-delimiters (line-beginning-position))
1914 (if (not f90-break-before-delimiters)
1915 (forward-char (if (looking-at f90-no-break-re) 2 1))
1916 (backward-char)
1917 (or (looking-at f90-no-break-re)
0db701f0 1918 (forward-char))))
034a9d40 1919
034a9d40 1920(defun f90-do-auto-fill ()
d14e6bbe
GM
1921 "Break line if non-white characters beyond `fill-column'.
1922Update keyword case first."
a729409a 1923 (interactive "*")
ec2f376f 1924 ;; Break line before or after last delimiter (non-word char) if
b974df0a 1925 ;; position is beyond fill-column.
ec2f376f 1926 ;; Will not break **, //, or => (as specified by f90-no-break-re).
7cae52cf 1927 (f90-update-line)
d595e95d
GM
1928 ;; Need this for `f90-electric-insert' and other f90- callers.
1929 (unless (and (boundp 'comment-auto-fill-only-comments)
1930 comment-auto-fill-only-comments
1931 (not (f90-in-comment)))
1932 (while (> (current-column) fill-column)
1933 (let ((pos-mark (point-marker)))
1934 (move-to-column fill-column)
1935 (or (f90-in-string) (f90-find-breakpoint))
1936 (f90-break-line)
1937 (goto-char pos-mark)
1938 (set-marker pos-mark nil)))))
b974df0a 1939
a259425b
GM
1940(defun f90-join-lines (&optional arg)
1941 "Join current line to previous, fix whitespace, continuation, comments.
a729409a 1942With optional argument ARG, join current line to following line.
a259425b
GM
1943Like `join-line', but handles F90 syntax."
1944 (interactive "*P")
1945 (beginning-of-line)
1946 (if arg (forward-line 1))
1947 (when (eq (preceding-char) ?\n)
1948 (skip-chars-forward " \t")
1949 (if (looking-at "\&") (delete-char 1))
1950 (beginning-of-line)
1951 (delete-region (point) (1- (point)))
034a9d40 1952 (skip-chars-backward " \t")
a259425b
GM
1953 (and (eq (preceding-char) ?&) (delete-char -1))
1954 (and (f90-in-comment)
1955 (looking-at "[ \t]*!+")
1956 (replace-match ""))
1957 (or (f90-in-string)
1958 (fixup-whitespace))))
034a9d40
RS
1959
1960(defun f90-fill-region (beg-region end-region)
d14e6bbe 1961 "Fill every line in region by forward parsing. Join lines if possible."
034a9d40 1962 (interactive "*r")
748dd5a8 1963 (let ((end-region-mark (copy-marker end-region))
e3f5ce56 1964 (go-on t)
640f9e26 1965 f90-smart-end f90-auto-keyword-case auto-fill-function)
034a9d40
RS
1966 (goto-char beg-region)
1967 (while go-on
ec2f376f 1968 ;; Join as much as possible.
a729409a 1969 (while (progn
a259425b
GM
1970 (end-of-line)
1971 (skip-chars-backward " \t")
1972 (eq (preceding-char) ?&))
1973 (f90-join-lines 'forward))
ec2f376f 1974 ;; Chop the line if necessary.
034a9d40 1975 (while (> (save-excursion (end-of-line) (current-column))
640f9e26
GM
1976 fill-column)
1977 (move-to-column fill-column)
1978 (f90-find-breakpoint)
1979 (f90-break-line 'no-update))
748dd5a8 1980 (setq go-on (and (< (point) end-region-mark)
e3f5ce56
GM
1981 (zerop (forward-line 1)))
1982 f90-cache-position (point)))
034a9d40 1983 (setq f90-cache-position nil)
748dd5a8 1984 (set-marker end-region-mark nil)
a445370f 1985 (if (featurep 'xemacs)
640f9e26 1986 (zmacs-deactivate-region)
034a9d40
RS
1987 (deactivate-mark))))
1988\f
1989(defun f90-block-match (beg-block beg-name end-block end-name)
1990 "Match end-struct with beg-struct and complete end-block if possible.
ec2f376f
GM
1991BEG-BLOCK is the type of block as indicated at the start (e.g., do).
1992BEG-NAME is the block start name (may be nil).
1993END-BLOCK is the type of block as indicated at the end (may be nil).
1994END-NAME is the block end name (may be nil).
034a9d40 1995Leave point at the end of line."
784d007b
GM
1996 ;; Hack to deal with the case when this is called from
1997 ;; f90-indent-region on a program block without an explicit PROGRAM
1998 ;; statement at the start. Should really be an error (?).
1999 (or beg-block (setq beg-block "program"))
6734e165 2000 (search-forward "end" (line-end-position))
034a9d40 2001 (catch 'no-match
784d007b 2002 (if (and end-block (f90-equal-symbols beg-block end-block))
748dd5a8
GM
2003 (search-forward end-block)
2004 (if end-block
2005 (progn
2006 (message "END %s does not match %s." end-block beg-block)
2007 (end-of-line)
2008 (throw 'no-match nil))
2009 (message "Inserting %s." beg-block)
2010 (insert (concat " " beg-block))))
2011 (if (f90-equal-symbols beg-name end-name)
2012 (and end-name (search-forward end-name))
2013 (cond ((and beg-name (not end-name))
2014 (message "Inserting %s." beg-name)
2015 (insert (concat " " beg-name)))
2016 ((and beg-name end-name)
2017 (message "Replacing %s with %s." end-name beg-name)
2018 (search-forward end-name)
2019 (replace-match beg-name))
2020 ((and (not beg-name) end-name)
2021 (message "Deleting %s." end-name)
2022 (search-forward end-name)
2023 (replace-match ""))))
2024 (or (looking-at "[ \t]*!") (delete-horizontal-space))))
034a9d40
RS
2025
2026(defun f90-match-end ()
ec2f376f 2027 "From an end block statement, find the corresponding block and name."
034a9d40 2028 (interactive)
748dd5a8
GM
2029 (let ((count 1)
2030 (top-of-window (window-start))
640f9e26 2031 (end-point (point))
748dd5a8 2032 (case-fold-search t)
640f9e26 2033 matching-beg beg-name end-name beg-block end-block end-struct)
5c2a80ad 2034 (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")
ab09adac
GM
2035 (setq end-struct (f90-looking-at-program-block-end)))
2036 (setq end-block (car end-struct)
599aeab9 2037 end-name (cadr end-struct))
5c2a80ad
GM
2038 (save-excursion
2039 (beginning-of-line)
784d007b
GM
2040 (while (and (> count 0)
2041 (not (= (line-beginning-position) (point-min))))
2042 (re-search-backward f90-blocks-re nil 'move)
e3f5ce56 2043 (beginning-of-line)
748dd5a8
GM
2044 ;; GM not a line number if continued line.
2045;;; (skip-chars-forward " \t")
2046;;; (skip-chars-forward "0-9")
e3f5ce56 2047 (skip-chars-forward " \t0-9")
6dd52caf
GM
2048 (cond ((or (f90-in-string) (f90-in-comment)))
2049 ((setq matching-beg
e3f5ce56
GM
2050 (or
2051 (f90-looking-at-do)
2052 (f90-looking-at-if-then)
2053 (f90-looking-at-where-or-forall)
2054 (f90-looking-at-select-case)
2055 (f90-looking-at-type-like)
5ab33946 2056 (f90-looking-at-associate)
784d007b
GM
2057 (f90-looking-at-program-block-start)
2058 ;; Interpret a single END without a block
2059 ;; start to be the END of a program block
2060 ;; without an initial PROGRAM line.
2061 (if (= (line-beginning-position) (point-min))
2062 '("program" nil))))
e3f5ce56 2063 (setq count (1- count)))
6dd52caf 2064 ((looking-at (concat "end[ \t]*" f90-blocks-re))
e3f5ce56 2065 (setq count (1+ count)))))
6dd52caf 2066 (if (> count 0)
5c2a80ad
GM
2067 (message "No matching beginning.")
2068 (f90-update-line)
2069 (if (eq f90-smart-end 'blink)
2070 (if (< (point) top-of-window)
2071 (message "Matches %s: %s"
2072 (what-line)
2073 (buffer-substring
2074 (line-beginning-position)
2075 (line-end-position)))
c046af95 2076 (sit-for blink-matching-delay)))
e3f5ce56 2077 (setq beg-block (car matching-beg)
599aeab9 2078 beg-name (cadr matching-beg))
5c2a80ad
GM
2079 (goto-char end-point)
2080 (beginning-of-line)
ab09adac 2081 (f90-block-match beg-block beg-name end-block end-name))))))
034a9d40
RS
2082
2083(defun f90-insert-end ()
87ee2359 2084 "Insert a complete end statement matching beginning of present block."
a729409a 2085 (interactive "*")
e3f5ce56 2086 (let ((f90-smart-end (or f90-smart-end 'blink)))
034a9d40
RS
2087 (insert "end")
2088 (f90-indent-new-line)))
2089\f
ec2f376f 2090;; Abbrevs and keywords.
034a9d40
RS
2091
2092(defun f90-abbrev-start ()
69658465 2093 "Typing `\\[help-command] or `? lists all the F90 abbrevs.
034a9d40 2094Any other key combination is executed normally."
7aee8047 2095 (interactive "*")
1ba983e8 2096 (insert last-command-event)
7aee8047
GM
2097 (let (char event)
2098 (if (fboundp 'next-command-event) ; XEmacs
2099 (setq event (next-command-event)
2c948571 2100 char (and (fboundp 'event-to-character)
640f9e26 2101 (event-to-character event)))
7aee8047
GM
2102 (setq event (read-event)
2103 char event))
be550ccc 2104 ;; Insert char if not equal to `?', or if abbrev-mode is off.
7aee8047 2105 (if (and abbrev-mode (or (eq char ??) (eq char help-char)))
640f9e26 2106 (f90-abbrev-help)
7aee8047 2107 (setq unread-command-events (list event)))))
034a9d40
RS
2108
2109(defun f90-abbrev-help ()
2110 "List the currently defined abbrevs in F90 mode."
2111 (interactive)
2112 (message "Listing abbrev table...")
2113 (display-buffer (f90-prepare-abbrev-list-buffer))
2114 (message "Listing abbrev table...done"))
2115
2116(defun f90-prepare-abbrev-list-buffer ()
ec2f376f 2117 "Create a buffer listing the F90 mode abbreviations."
5d16fdd7 2118 (with-current-buffer (get-buffer-create "*Abbrevs*")
034a9d40
RS
2119 (erase-buffer)
2120 (insert-abbrev-table-description 'f90-mode-abbrev-table t)
2121 (goto-char (point-min))
2122 (set-buffer-modified-p nil)
2123 (edit-abbrevs-mode))
2124 (get-buffer-create "*Abbrevs*"))
2125
2126(defun f90-upcase-keywords ()
2127 "Upcase all F90 keywords in the buffer."
a729409a 2128 (interactive "*")
034a9d40
RS
2129 (f90-change-keywords 'upcase-word))
2130
2131(defun f90-capitalize-keywords ()
2132 "Capitalize all F90 keywords in the buffer."
a729409a 2133 (interactive "*")
034a9d40
RS
2134 (f90-change-keywords 'capitalize-word))
2135
2136(defun f90-downcase-keywords ()
2137 "Downcase all F90 keywords in the buffer."
a729409a 2138 (interactive "*")
034a9d40
RS
2139 (f90-change-keywords 'downcase-word))
2140
2141(defun f90-upcase-region-keywords (beg end)
2142 "Upcase all F90 keywords in the region."
2143 (interactive "*r")
2144 (f90-change-keywords 'upcase-word beg end))
2145
2146(defun f90-capitalize-region-keywords (beg end)
2147 "Capitalize all F90 keywords in the region."
2148 (interactive "*r")
2149 (f90-change-keywords 'capitalize-word beg end))
2150
2151(defun f90-downcase-region-keywords (beg end)
2152 "Downcase all F90 keywords in the region."
2153 (interactive "*r")
2154 (f90-change-keywords 'downcase-word beg end))
2155
2156;; Change the keywords according to argument.
2157(defun f90-change-keywords (change-word &optional beg end)
ec2f376f 2158 "Change the case of F90 keywords in the region (if specified) or buffer.
02f85cba 2159CHANGE-WORD should be one of 'upcase-word, 'downcase-word, 'capitalize-word."
034a9d40 2160 (save-excursion
e3f5ce56
GM
2161 (setq beg (or beg (point-min))
2162 end (or end (point-max)))
69658465 2163 (let ((keyword-re
640f9e26
GM
2164 (concat "\\("
2165 f90-keywords-re "\\|" f90-procedures-re "\\|"
2166 f90-hpf-keywords-re "\\|" f90-operators-re "\\)"))
2167 (ref-point (point-min))
2168 (modified (buffer-modified-p))
e3f5ce56 2169 state saveword back-point)
034a9d40 2170 (goto-char beg)
ee30478d 2171 (unwind-protect
640f9e26
GM
2172 (while (re-search-forward keyword-re end t)
2173 (unless (progn
5c2a80ad
GM
2174 (setq state (parse-partial-sexp ref-point (point)))
2175 (or (nth 3 state) (nth 4 state)
748dd5a8 2176 ;; GM f90-directive-comment-re?
ec2f376f 2177 (save-excursion ; check for cpp directive
5c2a80ad
GM
2178 (beginning-of-line)
2179 (skip-chars-forward " \t0-9")
2180 (looking-at "#"))))
640f9e26
GM
2181 (setq ref-point (point)
2182 back-point (save-excursion (backward-word 1) (point))
e3f5ce56 2183 saveword (buffer-substring back-point ref-point))
640f9e26
GM
2184 (funcall change-word -1)
2185 (or (string= saveword (buffer-substring back-point ref-point))
2186 (setq modified t))))
5d16fdd7 2187 (or modified (restore-buffer-modified-p nil))))))
034a9d40 2188
d2d15846
DL
2189
2190(defun f90-current-defun ()
2191 "Function to use for `add-log-current-defun-function' in F90 mode."
2192 (save-excursion
2193 (nth 1 (f90-beginning-of-subprogram))))
2194
784d007b
GM
2195
2196(defun f90-backslash-not-special (&optional all)
2197 "Make the backslash character (\\) be non-special in the current buffer.
2198With optional argument ALL, change the default for all present
2199and future F90 buffers. F90 mode normally treats backslash as an
2200escape character."
2201 (or (eq major-mode 'f90-mode)
2202 (error "This function should only be used in F90 buffers"))
2203 (when (equal (char-syntax ?\\ ) ?\\ )
2204 (or all (set-syntax-table (copy-syntax-table (syntax-table))))
2205 (modify-syntax-entry ?\\ ".")))
2206
2207
034a9d40 2208(provide 'f90)
db97b872 2209
cbee283d 2210;; arch-tag: fceac97c-c147-44bd-aec0-172d4b560ef8
034a9d40 2211;;; f90.el ends here