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