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