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