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