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