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