Add arch taglines
[bpt/emacs.git] / lisp / progmodes / f90.el
CommitLineData
be010748 1;;; f90.el --- Fortran-90 mode (free format)
b578f267 2
d2d15846 3;; Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
034a9d40 4
88ea9ddc 5;; Author: Torbj\"orn Einarsson <Torbjorn.Einarsson@era.ericsson.se>
b125e643 6;; Maintainer: Glenn Morris <gmorris@ast.cam.ac.uk>
034a9d40
RS
7;; Keywords: fortran, f90, languages
8
b578f267
EN
9;; This file is part of GNU Emacs.
10
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
b578f267
EN
13;; the Free Software Foundation; either version 2, or (at your option)
14;; 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
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
034a9d40
RS
25
26;;; Commentary:
b578f267 27
87ee2359
GM
28;; Major mode for editing F90 programs in FREE FORMAT.
29;; The minor language revision F95 is also supported (with font-locking).
30
034a9d40 31;; Knows about continuation lines, named structured statements, and other
87ee2359
GM
32;; features in F90 including HPF (High Performance Fortran) structures.
33;; The basic feature provides accurate indentation of F90 programs.
034a9d40
RS
34;; In addition, there are many more features like automatic matching of all
35;; end statements, an auto-fill function to break long lines, a join-lines
87ee2359
GM
36;; function which joins continued lines, etc.
37
38;; To facilitate typing, a fairly complete list of abbreviations is provided.
39;; All abbreviations begin with the backquote character "`"
40;; (this requires modification of the syntax-table).
41;; For example, `i expands to integer (if abbrev-mode is on).
034a9d40 42
87ee2359 43;; There are two separate features for altering the appearance of code:
034a9d40 44;; 1) Upcasing or capitalizing of all keywords.
87ee2359
GM
45;; 2) Colors/fonts using font-lock-mode.
46;; Automatic upcase or downcase of keywords is controlled by the variable
47;; f90-auto-keyword-case.
034a9d40
RS
48
49;; The indentations of lines starting with ! is determined by the first of the
87ee2359 50;; following matches (values in the left column are the defaults):
ee30478d
KH
51
52;; start-string/regexp indent variable holding start-string/regexp
53;; !!! 0
54;; !hpf\\$ (re) 0 f90-directive-comment-re
55;; !!$ 0 f90-comment-region
56;; ! (re) as code f90-indented-comment-re
57;; default comment-column
58
59;; Ex: Here is the result of 3 different settings of f90-indented-comment-re
60;; f90-indented-comment-re !-indentation !!-indentation
61;; ! as code as code
62;; !! comment-column as code
63;; ![^!] as code comment-column
87ee2359
GM
64;; Trailing comments are indented to comment-column with indent-for-comment.
65;; The function f90-comment-region toggles insertion of
66;; the variable f90-comment-region in every line of the region.
034a9d40 67
a729409a 68;; One common convention for free vs. fixed format is that free format files
69658465 69;; have the ending .f90 or .f95 while fixed format files have the ending .f.
87ee2359
GM
70;; Emacs automatically loads Fortran files in the appropriate mode based
71;; on extension. You can modify this by adjusting the variable auto-mode-alist.
72;; For example:
73;; (add-to-list 'auto-mode-alist '("\\.f\\'" . f90-mode))
74
034a9d40 75;; Once you have entered f90-mode, you may get more info by using
69658465 76;; the command describe-mode (C-h m). For online help use
87ee2359
GM
77;; C-h f <Name of function you want described>, or
78;; C-h v <Name of variable you want described>.
034a9d40 79
87ee2359
GM
80;; To customize f90-mode for your taste, use, for example:
81;; (you don't have to specify values for all the parameters below)
82;;
d2d15846 83;;(add-hook 'f90-mode-hook
87ee2359 84;; ;; These are the default values.
034a9d40
RS
85;; '(lambda () (setq f90-do-indent 3
86;; f90-if-indent 3
87;; f90-type-indent 3
88;; f90-program-indent 2
89;; f90-continuation-indent 5
90;; f90-comment-region "!!$"
ee30478d
KH
91;; f90-directive-comment-re "!hpf\\$"
92;; f90-indented-comment-re "!"
87ee2359 93;; f90-break-delimiters "[-+\\*/><=,% \t]"
034a9d40
RS
94;; f90-break-before-delimiters t
95;; f90-beginning-ampersand t
96;; f90-smart-end 'blink
97;; f90-auto-keyword-case nil
87ee2359 98;; f90-leave-line-no nil
72e80cad 99;; indent-tabs-mode nil
b974df0a 100;; f90-font-lock-keywords f90-font-lock-keywords-2
72e80cad 101;; )
87ee2359 102;; ;; These are not default.
034a9d40 103;; (abbrev-mode 1) ; turn on abbreviation mode
b974df0a 104;; (f90-add-imenu-menu) ; extra menu with functions etc.
034a9d40
RS
105;; (if f90-auto-keyword-case ; change case of all keywords on startup
106;; (f90-change-keywords f90-auto-keyword-case))
107;; ))
87ee2359
GM
108;;
109;; in your .emacs file. You can also customize the lists
110;; f90-font-lock-keywords, etc.
111;;
112;; The auto-fill and abbreviation minor modes are accessible from the F90 menu,
b974df0a 113;; or by using M-x auto-fill-mode and M-x abbrev-mode, respectively.
034a9d40
RS
114
115;; Remarks
116;; 1) Line numbers are by default left-justified. If f90-leave-line-no is
117;; non-nil, the line numbers are never touched.
87ee2359 118;; 2) Multi-; statements like "do i=1,20 ; j=j+i ; end do" are not handled
034a9d40 119;; correctly, but I imagine them to be rare.
ee30478d 120;; 3) Regexps for hilit19 are no longer supported.
87ee2359 121;; 4) For FIXED FORMAT code, use fortran mode.
034a9d40 122;; 5) This mode does not work under emacs-18.x.
72e80cad
KH
123;; 6) Preprocessor directives, i.e., lines starting with # are left-justified
124;; and are untouched by all case-changing commands. There is, at present, no
125;; mechanism for treating multi-line directives (continued by \ ).
ee30478d
KH
126;; 7) f77 do-loops do 10 i=.. ; ; 10 continue are not correctly indented.
127;; You are urged to use f90-do loops (with labels if you wish).
c80718cc 128;; 8) The highlighting mode under XEmacs is not as complete as under Emacs.
034a9d40
RS
129
130;; List of user commands
131;; f90-previous-statement f90-next-statement
132;; f90-beginning-of-subprogram f90-end-of-subprogram f90-mark-subprogram
133;; f90-comment-region
134;; f90-indent-line f90-indent-new-line
135;; f90-indent-region (can be called by calling indent-region)
136;; f90-indent-subprogram
137;; f90-break-line f90-join-lines
034a9d40
RS
138;; f90-fill-region
139;; f90-insert-end
140;; f90-upcase-keywords f90-upcase-region-keywords
141;; f90-downcase-keywords f90-downcase-region-keywords
142;; f90-capitalize-keywords f90-capitalize-region-keywords
b974df0a
EN
143;; f90-add-imenu-menu
144;; f90-font-lock-1, f90-font-lock-2, f90-font-lock-3, f90-font-lock-4
034a9d40 145
87ee2359 146;; Original author's thanks
034a9d40
RS
147;; Thanks to all the people who have tested the mode. Special thanks to Jens
148;; Bloch Helmers for encouraging me to write this code, for creative
149;; suggestions as well as for the lists of hpf-commands.
150;; Also thanks to the authors of the fortran and pascal modes, on which some
151;; of this code is built.
152
a729409a
GM
153;;; Code:
154
87ee2359
GM
155;; TODO
156;; Support for hideshow, align.
157;; OpenMP, preprocessor highlighting.
158
34ba7e3d
GM
159(defvar comment-auto-fill-only-comments)
160(defvar font-lock-keywords)
0ee7f068 161
034a9d40 162;; User options
034a9d40 163
fcad5199 164(defgroup f90 nil
a729409a 165 "Major mode for editing free format Fortran 90,95 code."
d2d15846 166 :group 'languages)
034a9d40 167
fcad5199 168(defgroup f90-indent nil
a729409a 169 "Indentation in free format Fortran."
fcad5199 170 :prefix "f90-"
a729409a 171 :group 'f90)
034a9d40 172
034a9d40 173
fcad5199
RS
174(defcustom f90-do-indent 3
175 "*Extra indentation applied to DO blocks."
a729409a 176 :type 'integer
fcad5199 177 :group 'f90-indent)
034a9d40 178
fcad5199
RS
179(defcustom f90-if-indent 3
180 "*Extra indentation applied to IF, SELECT CASE, WHERE and FORALL blocks."
a729409a 181 :type 'integer
fcad5199 182 :group 'f90-indent)
034a9d40 183
fcad5199
RS
184(defcustom f90-type-indent 3
185 "*Extra indentation applied to TYPE, INTERFACE and BLOCK DATA blocks."
a729409a 186 :type 'integer
fcad5199 187 :group 'f90-indent)
034a9d40 188
fcad5199 189(defcustom f90-program-indent 2
a729409a
GM
190 "*Extra indentation applied to PROGRAM, MODULE, SUBROUTINE, FUNCTION blocks."
191 :type 'integer
fcad5199 192 :group 'f90-indent)
034a9d40 193
fcad5199 194(defcustom f90-continuation-indent 5
a729409a
GM
195 "*Extra indentation applied to continuation lines."
196 :type 'integer
fcad5199 197 :group 'f90-indent)
034a9d40 198
fcad5199 199(defcustom f90-comment-region "!!$"
87ee2359 200 "*String inserted by \\[f90-comment-region] at start of each line in region."
a729409a 201 :type 'string
fcad5199
RS
202 :group 'f90-indent)
203
204(defcustom f90-indented-comment-re "!"
a729409a
GM
205 "*Regexp matching comments to indent as code."
206 :type 'regexp
fcad5199
RS
207 :group 'f90-indent)
208
209(defcustom f90-directive-comment-re "!hpf\\$"
210 "*Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented."
a729409a 211 :type 'regexp
fcad5199
RS
212 :group 'f90-indent)
213
214(defcustom f90-beginning-ampersand t
87ee2359 215 "*Non-nil gives automatic insertion of \& at start of continuation line."
a729409a 216 :type 'boolean
fcad5199
RS
217 :group 'f90)
218
219(defcustom f90-smart-end 'blink
034a9d40
RS
220 "*From an END statement, check and fill the end using matching block start.
221Allowed values are 'blink, 'no-blink, and nil, which determine
fcad5199 222whether to blink the matching beginning."
a729409a 223 :type '(choice (const blink) (const no-blink) (const nil))
fcad5199 224 :group 'f90)
034a9d40 225
fcad5199
RS
226(defcustom f90-break-delimiters "[-+\\*/><=,% \t]"
227 "*Regexp holding list of delimiters at which lines may be broken."
a729409a 228 :type 'regexp
fcad5199 229 :group 'f90)
034a9d40 230
fcad5199
RS
231(defcustom f90-break-before-delimiters t
232 "*Non-nil causes `f90-do-auto-fill' to break lines before delimiters."
a729409a 233 :type 'boolean
fcad5199 234 :group 'f90)
034a9d40 235
fcad5199 236(defcustom f90-auto-keyword-case nil
034a9d40 237 "*Automatic case conversion of keywords.
87ee2359 238The options are 'downcase-word, 'upcase-word, 'capitalize-word and nil."
a729409a
GM
239 :type '(choice (const downcase-word) (const upcase-word)
240 (const capitalize-word) (const nil))
fcad5199
RS
241 :group 'f90)
242
243(defcustom f90-leave-line-no nil
87ee2359 244 "*If non-nil, line numbers are not left justified."
a729409a 245 :type 'boolean
fcad5199
RS
246 :group 'f90)
247
0ee7f068
GM
248(defcustom f90-mode-hook nil
249 "Hook run when entering F90 mode."
a729409a 250 :type 'hook
0ee7f068 251 :options '(f90-add-imenu-menu)
a729409a 252 :group 'f90)
0ee7f068
GM
253
254;; User options end here.
1bb3ae5c 255
ee30478d 256(defconst f90-keywords-re
84021009
SM
257 (regexp-opt '("allocatable" "allocate" "assign" "assignment" "backspace"
258 "block" "call" "case" "character" "close" "common" "complex"
259 "contains" "continue" "cycle" "data" "deallocate"
260 "dimension" "do" "double" "else" "elseif" "elsewhere" "end"
261 "enddo" "endfile" "endif" "entry" "equivalence" "exit"
262 "external" "forall" "format" "function" "goto" "if"
263 "implicit" "include" "inquire" "integer" "intent"
264 "interface" "intrinsic" "logical" "module" "namelist" "none"
265 "nullify" "only" "open" "operator" "optional" "parameter"
266 "pause" "pointer" "precision" "print" "private" "procedure"
267 "program" "public" "read" "real" "recursive" "result" "return"
268 "rewind" "save" "select" "sequence" "stop" "subroutine"
269 "target" "then" "type" "use" "where" "while" "write"
270 ;; F95 keywords.
271 "elemental" "pure") 'words)
ee30478d
KH
272 "Regexp for F90 keywords.")
273
274(defconst f90-keywords-level-3-re
84021009
SM
275 (regexp-opt
276 '("allocatable" "allocate" "assign" "assignment" "backspace"
277 "close" "deallocate" "dimension" "endfile" "entry" "equivalence"
278 "external" "inquire" "intent" "intrinsic" "nullify" "only" "open"
279 "operator" "optional" "parameter" "pause" "pointer" "print" "private"
280 "public" "read" "recursive" "result" "rewind" "save" "select"
281 "sequence" "target" "write"
282 ;; F95 keywords.
283 "elemental" "pure") 'words)
284 "Keyword-regexp for font-lock level >= 3.")
ee30478d 285
ee30478d 286(defconst f90-procedures-re
84021009 287 (concat "\\<"
69658465
GM
288 (regexp-opt
289 '("abs" "achar" "acos" "adjustl" "adjustr" "aimag" "aint"
290 "all" "allocated" "anint" "any" "asin" "associated"
291 "atan" "atan2" "bit_size" "btest" "ceiling" "char" "cmplx"
292 "conjg" "cos" "cosh" "count" "cshift" "date_and_time" "dble"
293 "digits" "dim" "dot_product" "dprod" "eoshift" "epsilon"
294 "exp" "exponent" "floor" "fraction" "huge" "iachar" "iand"
295 "ibclr" "ibits" "ibset" "ichar" "ieor" "index" "int" "ior"
296 "ishft" "ishftc" "kind" "lbound" "len" "len_trim" "lge" "lgt"
297 "lle" "llt" "log" "log10" "logical" "matmul" "max"
298 "maxexponent" "maxloc" "maxval" "merge" "min" "minexponent"
299 "minloc" "minval" "mod" "modulo" "mvbits" "nearest" "nint"
300 "not" "pack" "precision" "present" "product" "radix"
301 ;; Real is taken out here to avoid highlighting declarations.
302 "random_number" "random_seed" "range" ;; "real"
303 "repeat" "reshape" "rrspacing" "scale" "scan"
304 "selected_int_kind" "selected_real_kind" "set_exponent"
305 "shape" "sign" "sin" "sinh" "size" "spacing" "spread" "sqrt"
306 "sum" "system_clock" "tan" "tanh" "tiny" "transfer"
307 "transpose" "trim" "ubound" "unpack" "verify"
308 ;; F95 intrinsic functions.
309 "null" "cpu_time") t)
310 ;; A left parenthesis to avoid highlighting non-procedures.
311 "[ \t]*(")
ee30478d
KH
312 "Regexp whose first part matches F90 intrinsic procedures.")
313
314(defconst f90-operators-re
69658465
GM
315 (concat "\\."
316 (regexp-opt '("and" "eq" "eqv" "false" "ge" "gt" "le" "lt" "ne"
317 "neqv" "not" "or" "true") t)
318 "\\.")
ee30478d
KH
319 "Regexp matching intrinsic operators.")
320
321(defconst f90-hpf-keywords-re
84021009 322 (regexp-opt
ec2f376f 323 ;; Intrinsic procedures.
84021009
SM
324 '("all_prefix" "all_scatter" "all_suffix" "any_prefix"
325 "any_scatter" "any_suffix" "copy_prefix" "copy_scatter"
326 "copy_suffix" "count_prefix" "count_scatter" "count_suffix"
327 "grade_down" "grade_up"
328 "hpf_alignment" "hpf_distribution" "hpf_template" "iall" "iall_prefix"
329 "iall_scatter" "iall_suffix" "iany" "iany_prefix" "iany_scatter"
330 "iany_suffix" "ilen" "iparity" "iparity_prefix"
331 "iparity_scatter" "iparity_suffix" "leadz" "maxval_prefix"
332 "maxval_scatter" "maxval_suffix" "minval_prefix" "minval_scatter"
333 "minval_suffix" "number_of_processors" "parity"
334 "parity_prefix" "parity_scatter" "parity_suffix" "popcnt" "poppar"
335 "processors_shape" "product_prefix" "product_scatter"
336 "product_suffix" "sum_prefix" "sum_scatter" "sum_suffix"
ec2f376f 337 ;; Directives.
84021009 338 "align" "distribute" "dynamic" "independent" "inherit" "processors"
69658465 339 "realign" "redistribute" "template"
ec2f376f 340 ;; Keywords.
84021009 341 "block" "cyclic" "extrinsic" "new" "onto" "pure" "with") 'words)
ee30478d 342 "Regexp for all HPF keywords, procedures and directives.")
034a9d40 343
ec2f376f 344;; Highlighting patterns.
034a9d40 345
ee30478d 346(defvar f90-font-lock-keywords-1
45d1e4d4 347 (list
ec2f376f 348 ;; Special highlighting of "module procedure".
84021009 349 '("\\<\\(module[ \t]*procedure\\)\\>" (1 font-lock-keyword-face))
eb9f0295
GM
350 ;; Highlight definition of derived type.
351 '("\\<\\(\\(?:end[ \t]*\\)?type\\)\\>\\([^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
6fb453e1 352 (1 font-lock-keyword-face) (3 font-lock-function-name-face))
84021009 353 ;; Other functions and declarations.
69658465 354 '("\\<\\(\\(?:end[ \t]*\\)?\\(program\\|module\\|function\\|\
6fb453e1 355subroutine\\)\\|use\\|call\\)\\>[ \t]*\\(\\sw+\\)?"
84021009 356 (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
b974df0a 357 "\\<\\(\\(end[ \t]*\\)?\\(interface\\|block[ \t]*data\\)\\|contains\\)\\>")
ee30478d
KH
358 "This does fairly subdued highlighting of comments and function calls.")
359
360(defvar f90-font-lock-keywords-2
69658465
GM
361 (append
362 f90-font-lock-keywords-1
363 (list
ec2f376f 364 ;; Variable declarations (avoid the real function call).
69658465 365 '("^[ \t0-9]*\\(real\\|integer\\|c\\(haracter\\|omplex\\)\\|\
7aee8047
GM
366logical\\|type[ \t]*(\\sw+)\\)\\(.*::\\|[ \t]*(.*)\\)?\\([^&!\n]*\\)"
367 (1 font-lock-type-face t) (4 font-lock-variable-name-face t))
ec2f376f 368 ;; do, if, select, where, and forall constructs.
69658465
GM
369 '("\\<\\(end[ \t]*\\(do\\|if\\|select\\|forall\\|where\\)\\)\\>\
370\\([ \t]+\\(\\sw+\\)\\)?"
371 (1 font-lock-keyword-face) (3 font-lock-constant-face nil t))
372 '("^[ \t0-9]*\\(\\(\\sw+\\)[ \t]*:[ \t]*\\)?\\(\\(if\\|\
373do\\([ \t]*while\\)?\\|select[ \t]*case\\|where\\|forall\\)\\)\\>"
374 (2 font-lock-constant-face nil t) (3 font-lock-keyword-face))
ec2f376f 375 ;; Implicit declaration.
69658465 376 '("\\<\\(implicit\\)[ \t]*\\(real\\|integer\\|c\\(haracter\\|omplex\\)\
6fb453e1 377\\|logical\\|type[ \t]*(\\sw+)\\|none\\)[ \t]*"
69658465
GM
378 (1 font-lock-keyword-face) (2 font-lock-type-face))
379 '("\\<\\(namelist\\|common\\)[ \t]*\/\\(\\sw+\\)?\/"
380 (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
381 "\\<else\\([ \t]*if\\|where\\)?\\>"
7aee8047 382 '("\\(&\\)[ \t]*\\(!\\|$\\)" (1 font-lock-keyword-face))
69658465
GM
383 "\\<\\(then\\|continue\\|format\\|include\\|stop\\|return\\)\\>"
384 '("\\<\\(exit\\|cycle\\)[ \t]*\\(\\sw+\\)?\\>"
385 (1 font-lock-keyword-face) (2 font-lock-constant-face nil t))
386 '("\\<\\(case\\)[ \t]*\\(default\\|(\\)" . 1)
387 '("\\<\\(do\\|go *to\\)\\>[ \t]*\\([0-9]+\\)"
388 (1 font-lock-keyword-face) (2 font-lock-constant-face))
f14ca250 389 ;; Line numbers (lines whose first character after number is letter).
69658465 390 '("^[ \t]*\\([0-9]+\\)[ \t]*[a-z]+" (1 font-lock-constant-face t))))
87ee2359 391 "Highlights declarations, do-loops and other constructs.")
ee30478d
KH
392
393(defvar f90-font-lock-keywords-3
394 (append f90-font-lock-keywords-2
69658465
GM
395 (list
396 f90-keywords-level-3-re
397 f90-operators-re
398 (list f90-procedures-re '(1 font-lock-keyword-face keep))
ec2f376f 399 "\\<real\\>" ; avoid overwriting real defs
69658465 400 ))
ee30478d
KH
401 "Highlights all F90 keywords and intrinsic procedures.")
402
403(defvar f90-font-lock-keywords-4
404 (append f90-font-lock-keywords-3
69658465 405 (list f90-hpf-keywords-re))
ee30478d
KH
406 "Highlights all F90 and HPF keywords.")
407
408(defvar f90-font-lock-keywords
87ee2359 409 f90-font-lock-keywords-2
0ee7f068
GM
410 "*Default expressions to highlight in F90 mode.
411Can be overridden by the value of `font-lock-maximum-decoration'.")
034a9d40 412
ec2f376f 413
70186f7f
GM
414(defvar f90-mode-syntax-table
415 (let ((table (make-syntax-table)))
416 (modify-syntax-entry ?\! "<" table) ; begin comment
417 (modify-syntax-entry ?\n ">" table) ; end comment
418 (modify-syntax-entry ?_ "w" table) ; underscore in names
419 (modify-syntax-entry ?\' "\"" table) ; string quote
420 (modify-syntax-entry ?\" "\"" table) ; string quote
421 (modify-syntax-entry ?\` "w" table) ; for abbrevs
422 (modify-syntax-entry ?\r " " table) ; return is whitespace
423 (modify-syntax-entry ?+ "." table) ; punctuation
424 (modify-syntax-entry ?- "." table)
425 (modify-syntax-entry ?= "." table)
426 (modify-syntax-entry ?* "." table)
427 (modify-syntax-entry ?/ "." table)
428 (modify-syntax-entry ?\\ "\\" table) ; escape chars
429 table)
430 "Syntax table used in F90 mode.")
431
432(defvar f90-mode-map
433 (let ((map (make-sparse-keymap)))
434 (define-key map "`" 'f90-abbrev-start)
435 (define-key map "\C-c;" 'f90-comment-region)
436 (define-key map "\C-\M-a" 'f90-beginning-of-subprogram)
437 (define-key map "\C-\M-e" 'f90-end-of-subprogram)
438 (define-key map "\C-\M-h" 'f90-mark-subprogram)
6f1d50da
GM
439 (define-key map "\C-\M-n" 'f90-end-of-block)
440 (define-key map "\C-\M-p" 'f90-beginning-of-block)
70186f7f
GM
441 (define-key map "\C-\M-q" 'f90-indent-subprogram)
442 (define-key map "\C-j" 'f90-indent-new-line) ; LFD equals C-j
443 (define-key map "\r" 'newline)
444 (define-key map "\C-c\r" 'f90-break-line)
445;;; (define-key map [M-return] 'f90-break-line)
76bccf35
GM
446 (define-key map "\C-c\C-a" 'f90-previous-block)
447 (define-key map "\C-c\C-e" 'f90-next-block)
70186f7f
GM
448 (define-key map "\C-c\C-d" 'f90-join-lines)
449 (define-key map "\C-c\C-f" 'f90-fill-region)
450 (define-key map "\C-c\C-p" 'f90-previous-statement)
451 (define-key map "\C-c\C-n" 'f90-next-statement)
452 (define-key map "\C-c\C-w" 'f90-insert-end)
453 (define-key map "\t" 'f90-indent-line)
454 (define-key map "," 'f90-electric-insert)
455 (define-key map "+" 'f90-electric-insert)
456 (define-key map "-" 'f90-electric-insert)
457 (define-key map "*" 'f90-electric-insert)
458 (define-key map "/" 'f90-electric-insert)
a729409a
GM
459
460 (easy-menu-define f90-menu map "Menu for F90 mode."
461 `("F90"
462 ("Customization"
463 ,(custom-menu-create 'f90)
464 ["Set" Custom-set t]
465 ["Save" Custom-save t]
466 ["Reset to Current" Custom-reset-current t]
467 ["Reset to Saved" Custom-reset-saved t]
468 ["Reset to Standard Settings" Custom-reset-standard t]
469 )
470 "--"
471 ["Indent Subprogram" f90-indent-subprogram t]
472 ["Mark Subprogram" f90-mark-subprogram t]
473 ["Beginning of Subprogram" f90-beginning-of-subprogram t]
474 ["End of Subprogram" f90-end-of-subprogram t]
475 "--"
476 ["(Un)Comment Region" f90-comment-region mark-active]
477 ["Indent Region" f90-indent-region mark-active]
478 ["Fill Region" f90-fill-region mark-active]
479 "--"
480 ["Break Line at Point" f90-break-line t]
481 ["Join with Previous Line" f90-join-lines t]
482 ["Insert Block End" f90-insert-end t]
483 "--"
484 ("Highlighting"
485 ["Toggle font-lock-mode" font-lock-mode :selected font-lock-mode
486 :style toggle]
487 "--"
488 ["Light highlighting (level 1)" f90-font-lock-1 t]
489 ["Moderate highlighting (level 2)" f90-font-lock-2 t]
490 ["Heavy highlighting (level 3)" f90-font-lock-3 t]
491 ["Maximum highlighting (level 4)" f90-font-lock-4 t]
492 )
493 ("Change Keyword Case"
494 ["Upcase Keywords (buffer)" f90-upcase-keywords t]
495 ["Capitalize Keywords (buffer)" f90-capitalize-keywords t]
496 ["Downcase Keywords (buffer)" f90-downcase-keywords t]
497 "--"
498 ["Upcase Keywords (region)" f90-upcase-region-keywords
499 mark-active]
500 ["Capitalize Keywords (region)" f90-capitalize-region-keywords
501 mark-active]
502 ["Downcase Keywords (region)" f90-downcase-region-keywords
503 mark-active]
504 )
505 "--"
506 ["Toggle auto-fill" auto-fill-mode :selected auto-fill-function
507 :style toggle]
508 ["Toggle abbrev-mode" abbrev-mode :selected abbrev-mode
509 :style toggle]
510 ["Add imenu Menu" f90-add-imenu-menu
511 :active (not (lookup-key (current-local-map) [menu-bar index]))
512 :included (fboundp 'imenu-add-to-menubar)]))
70186f7f 513 map)
034a9d40 514 "Keymap used in F90 mode.")
ee30478d 515
b974df0a 516
1a341882
GM
517(defun f90-font-lock-1 ()
518 "Set `font-lock-keywords' to `f90-font-lock-keywords-1'."
519 (interactive)
520 (font-lock-mode 1)
521 (setq font-lock-keywords f90-font-lock-keywords-1)
522 (font-lock-fontify-buffer))
523
524(defun f90-font-lock-2 ()
525 "Set `font-lock-keywords' to `f90-font-lock-keywords-2'."
526 (interactive)
527 (font-lock-mode 1)
528 (setq font-lock-keywords f90-font-lock-keywords-2)
529 (font-lock-fontify-buffer))
530
531(defun f90-font-lock-3 ()
532 "Set `font-lock-keywords' to `f90-font-lock-keywords-3'."
533 (interactive)
534 (font-lock-mode 1)
535 (setq font-lock-keywords f90-font-lock-keywords-3)
536 (font-lock-fontify-buffer))
537
538(defun f90-font-lock-4 ()
539 "Set `font-lock-keywords' to `f90-font-lock-keywords-4'."
540 (interactive)
541 (font-lock-mode 1)
542 (setq font-lock-keywords f90-font-lock-keywords-4)
543 (font-lock-fontify-buffer))
544
545\f
ee30478d 546;; Regexps for finding program structures.
69658465 547(defconst f90-blocks-re
ec2f376f
GM
548 (concat "\\(block[ \t]*data\\|"
549 (regexp-opt '("do" "if" "interface" "function" "module" "program"
550 "select" "subroutine" "type" "where" "forall"))
551 "\\)\\>")
552 "Regexp potentially indicating a \"block\" of F90 code.")
553
69658465 554(defconst f90-program-block-re
ec2f376f
GM
555 (regexp-opt '("program" "module" "subroutine" "function") 'paren)
556 "Regexp used to locate the start/end of a \"subprogram\".")
557
69658465 558(defconst f90-else-like-re
ec2f376f
GM
559 "\\(else\\([ \t]*if\\|where\\)?\\|case[ \t]*\\(default\\|(\\)\\)"
560 "Regexp matching an ELSE IF, ELSEWHERE, CASE statement.")
561
69658465 562(defconst f90-end-if-re
ec2f376f
GM
563 (concat "end[ \t]*"
564 (regexp-opt '("if" "select" "where" "forall") 'paren)
565 "\\>")
566 "Regexp matching the end of an IF, SELECT, WHERE, FORALL block.")
567
69658465 568(defconst f90-end-type-re
ec2f376f
GM
569 "end[ \t]*\\(type\\|interface\\|block[ \t]*data\\)\\>"
570 "Regexp matching the end of a TYPE, INTERFACE, BLOCK DATA section.")
571
ee30478d 572(defconst f90-type-def-re
eb9f0295 573 "\\<\\(type\\)\\>\\(?:[^()\n]*::\\)?[ \t]*\\(\\sw+\\)"
e7272ece 574 "Regexp matching the definition of a derived type.")
ec2f376f
GM
575
576(defconst f90-no-break-re
577 (regexp-opt '("**" "//" "=>") 'paren)
578 "Regexp specifying where not to break lines when filling.")
579
580(defvar f90-cache-position nil
581 "Temporary position used to speed up region operations.")
034a9d40 582(make-variable-buffer-local 'f90-cache-position)
ec2f376f 583
b974df0a 584\f
ec2f376f 585;; Imenu support.
ee30478d 586(defvar f90-imenu-generic-expression
b974df0a
EN
587 (let ((good-char "[^!\"\&\n \t]") (not-e "[^e!\n\"\& \t]")
588 (not-n "[^n!\n\"\& \t]") (not-d "[^d!\n\"\& \t]"))
589 (list
590 '(nil "^[ \t0-9]*program[ \t]+\\(\\sw+\\)" 1)
591 '("Modules" "^[ \t0-9]*module[ \t]+\\(\\sw+\\)[ \t]*\\(!\\|$\\)" 1)
592 '("Types" "^[ \t0-9]*type[ \t]+\\(\\sw+\\)" 1)
593 (list
69658465 594 "Procedures"
b974df0a
EN
595 (concat
596 "^[ \t0-9]*"
597 "\\("
ec2f376f
GM
598 ;; At least three non-space characters before function/subroutine.
599 ;; Check that the last three non-space characters do not spell E N D.
b974df0a
EN
600 "[^!\"\&\n]*\\("
601 not-e good-char good-char "\\|"
602 good-char not-n good-char "\\|"
603 good-char good-char not-d "\\)"
604 "\\|"
ec2f376f 605 ;; Less than three non-space characters before function/subroutine.
b974df0a
EN
606 good-char "?" good-char "?"
607 "\\)"
608 "[ \t]*\\(function\\|subroutine\\)[ \t]+\\(\\sw+\\)")
69658465 609 4)))
a729409a 610 "Value for `imenu-generic-expression' in F90 mode.")
ee30478d 611
b974df0a 612(defun f90-add-imenu-menu ()
b974df0a 613 "Add an imenu menu to the menubar."
87ee2359 614 (interactive)
34ba7e3d 615 (if (lookup-key (current-local-map) [menu-bar index])
5c2a80ad
GM
616 (message "%s" "F90-imenu already exists.")
617 (imenu-add-to-menubar "F90-imenu")
901e8d1d 618 (redraw-frame (selected-frame))))
b974df0a 619
034a9d40 620\f
ec2f376f 621;; Abbrevs have generally two letters, except standard types `c, `i, `r, `t.
4f9fc702
GM
622(defvar f90-mode-abbrev-table
623 (let (abbrevs-changed)
0ee7f068
GM
624 (define-abbrev-table 'f90-mode-abbrev-table nil)
625 ;; Use the 6th arg (SYSTEM-FLAG) of define-abbrev if possible.
626 ;; A little baroque to quieten the byte-compiler.
627 (mapcar
628 (function (lambda (element)
629 (condition-case nil
630 (apply 'define-abbrev f90-mode-abbrev-table
631 (append element '(nil 0 t)))
632 (wrong-number-of-arguments
633 (apply 'define-abbrev f90-mode-abbrev-table
634 (append element '(nil 0)))))))
635 '(("`al" "allocate" )
636 ("`ab" "allocatable" )
637 ("`as" "assignment" )
638 ("`ba" "backspace" )
639 ("`bd" "block data" )
640 ("`c" "character" )
641 ("`cl" "close" )
642 ("`cm" "common" )
643 ("`cx" "complex" )
644 ("`cn" "contains" )
645 ("`cy" "cycle" )
646 ("`de" "deallocate" )
647 ("`df" "define" )
648 ("`di" "dimension" )
649 ("`dw" "do while" )
650 ("`el" "else" )
651 ("`eli" "else if" )
652 ("`elw" "elsewhere" )
653 ("`eq" "equivalence" )
654 ("`ex" "external" )
655 ("`ey" "entry" )
656 ("`fl" "forall" )
657 ("`fo" "format" )
658 ("`fu" "function" )
659 ("`fa" ".false." )
660 ("`im" "implicit none")
661 ("`in" "include" )
662 ("`i" "integer" )
663 ("`it" "intent" )
664 ("`if" "interface" )
665 ("`lo" "logical" )
666 ("`mo" "module" )
667 ("`na" "namelist" )
668 ("`nu" "nullify" )
669 ("`op" "optional" )
670 ("`pa" "parameter" )
671 ("`po" "pointer" )
672 ("`pr" "print" )
673 ("`pi" "private" )
674 ("`pm" "program" )
675 ("`pu" "public" )
676 ("`r" "real" )
677 ("`rc" "recursive" )
678 ("`rt" "return" )
679 ("`rw" "rewind" )
680 ("`se" "select" )
681 ("`sq" "sequence" )
682 ("`su" "subroutine" )
683 ("`ta" "target" )
684 ("`tr" ".true." )
685 ("`t" "type" )
686 ("`wh" "where" )
687 ("`wr" "write" )))
4f9fc702
GM
688 f90-mode-abbrev-table)
689 "Abbrev table for F90 mode.")
034a9d40 690\f
d2d15846 691
034a9d40
RS
692;;;###autoload
693(defun f90-mode ()
87ee2359 694 "Major mode for editing Fortran 90,95 code in free format.
a729409a 695For fixed format code, use `fortran-mode'.
034a9d40 696
a729409a 697\\[f90-indent-line] indents the current line.
ec2f376f 698\\[f90-indent-new-line] indents current line and creates a new\
034a9d40 699 indented line.
87ee2359 700\\[f90-indent-subprogram] indents the current subprogram.
034a9d40
RS
701
702Type `? or `\\[help-command] to display a list of built-in\
703 abbrevs for F90 keywords.
704
705Key definitions:
706\\{f90-mode-map}
707
708Variables controlling indentation style and extra features:
709
ec2f376f
GM
710`f90-do-indent'
711 Extra indentation within do blocks (default 3).
712`f90-if-indent'
713 Extra indentation within if/select case/where/forall blocks (default 3).
714`f90-type-indent'
715 Extra indentation within type/interface/block-data blocks (default 3).
716`f90-program-indent'
717 Extra indentation within program/module/subroutine/function blocks
718 (default 2).
719`f90-continuation-indent'
720 Extra indentation applied to continuation lines (default 5).
721`f90-comment-region'
e3f5ce56
GM
722 String inserted by function \\[f90-comment-region] at start of each
723 line in region (default \"!!!$\").
ec2f376f
GM
724`f90-indented-comment-re'
725 Regexp determining the type of comment to be intended like code
726 (default \"!\").
727`f90-directive-comment-re'
728 Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented
729 (default \"!hpf\\\\$\").
730`f90-break-delimiters'
731 Regexp holding list of delimiters at which lines may be broken
732 (default \"[-+*/><=,% \\t]\").
733`f90-break-before-delimiters'
734 Non-nil causes `f90-do-auto-fill' to break lines before delimiters
735 (default t).
736`f90-beginning-ampersand'
737 Automatic insertion of \& at beginning of continuation lines (default t).
738`f90-smart-end'
739 From an END statement, check and fill the end using matching block start.
740 Allowed values are 'blink, 'no-blink, and nil, which determine
741 whether to blink the matching beginning (default 'blink).
742`f90-auto-keyword-case'
743 Automatic change of case of keywords (default nil).
744 The possibilities are 'downcase-word, 'upcase-word, 'capitalize-word.
745`f90-leave-line-no'
746 Do not left-justify line numbers (default nil).
747`f90-keywords-re'
748 List of keywords used for highlighting/upcase-keywords etc.
034a9d40
RS
749
750Turning on F90 mode calls the value of the variable `f90-mode-hook'
751with no args, if that value is non-nil."
752 (interactive)
753 (kill-all-local-variables)
e3f5ce56
GM
754 (setq major-mode 'f90-mode
755 mode-name "F90"
756 local-abbrev-table f90-mode-abbrev-table)
034a9d40
RS
757 (set-syntax-table f90-mode-syntax-table)
758 (use-local-map f90-mode-map)
e3f5ce56
GM
759 (set (make-local-variable 'indent-line-function) 'f90-indent-line)
760 (set (make-local-variable 'indent-region-function) 'f90-indent-region)
761 (set (make-local-variable 'require-final-newline) t)
762 (set (make-local-variable 'comment-start) "!")
763 (set (make-local-variable 'comment-start-skip) "!+ *")
764 (set (make-local-variable 'comment-indent-function) 'f90-comment-indent)
765 (set (make-local-variable 'abbrev-all-caps) t)
766 (set (make-local-variable 'normal-auto-fill-function) 'f90-do-auto-fill)
70186f7f 767 (setq indent-tabs-mode nil) ; auto buffer local
e3f5ce56
GM
768 (set (make-local-variable 'font-lock-defaults)
769 '((f90-font-lock-keywords f90-font-lock-keywords-1
770 f90-font-lock-keywords-2
771 f90-font-lock-keywords-3
772 f90-font-lock-keywords-4)
773 nil t))
45d1e4d4 774 (set (make-local-variable 'imenu-case-fold-search) t)
e3f5ce56 775 (set (make-local-variable 'imenu-generic-expression)
a729409a
GM
776 f90-imenu-generic-expression)
777 (set (make-local-variable 'beginning-of-defun-function)
778 'f90-beginning-of-subprogram)
779 (set (make-local-variable 'end-of-defun-function) 'f90-end-of-subprogram)
d2d15846
DL
780 (set (make-local-variable 'add-log-current-defun-function)
781 #'f90-current-defun)
48548fd5 782 (run-hooks 'f90-mode-hook))
ec2f376f 783
034a9d40 784\f
ec2f376f 785;; Inline-functions.
034a9d40 786(defsubst f90-in-string ()
d14e6bbe 787 "Return non-nil if point is inside a string.
ec2f376f 788Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 789and lies before point."
034a9d40
RS
790 (let ((beg-pnt
791 (if (and f90-cache-position (> (point) f90-cache-position))
792 f90-cache-position
793 (point-min))))
794 (nth 3 (parse-partial-sexp beg-pnt (point)))))
69658465 795
034a9d40 796(defsubst f90-in-comment ()
d14e6bbe 797 "Return non-nil if point is inside a comment.
ec2f376f 798Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 799and lies before point."
034a9d40
RS
800 (let ((beg-pnt
801 (if (and f90-cache-position (> (point) f90-cache-position))
802 f90-cache-position
803 (point-min))))
804 (nth 4 (parse-partial-sexp beg-pnt (point)))))
805
806(defsubst f90-line-continued ()
d14e6bbe
GM
807 "Return t if the current line is a continued one.
808This includes comment lines embedded in continued lines, but
809not the last line of a continued statement."
034a9d40 810 (save-excursion
6734e165
GM
811 (beginning-of-line)
812 (while (and (looking-at "[ \t]*\\(!\\|$\\)") (zerop (forward-line -1))))
e3f5ce56
GM
813 (end-of-line)
814 (while (f90-in-comment)
815 (search-backward "!" (line-beginning-position))
816 (skip-chars-backward "!"))
817 (skip-chars-backward " \t")
818 (= (preceding-char) ?&)))
034a9d40 819
748dd5a8
GM
820;; GM this is not right, eg a continuation line starting with a number.
821;; Need f90-code-start-position function.
822;; And yet, things seems to work with this...
7aee8047
GM
823;; cf f90-indent-line
824;; (beginning-of-line) ; digits after & \n are not line-nos
825;; (if (not (save-excursion (and (f90-previous-statement)
826;; (f90-line-continued))))
827;; (f90-indent-line-no)
034a9d40
RS
828(defsubst f90-current-indentation ()
829 "Return indentation of current line.
830Line-numbers are considered whitespace characters."
e3f5ce56 831 (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")))
034a9d40
RS
832
833(defsubst f90-indent-to (col &optional no-line-number)
834 "Indent current line to column COL.
d14e6bbe
GM
835If optional argument NO-LINE-NUMBER is nil, jump over a possible
836line-number before indenting."
034a9d40 837 (beginning-of-line)
748dd5a8 838 (or no-line-number
034a9d40
RS
839 (skip-chars-forward " \t0-9"))
840 (delete-horizontal-space)
748dd5a8
GM
841 ;; Leave >= 1 space after line number.
842 (indent-to col (if (zerop (current-column)) 0 1)))
034a9d40 843
034a9d40 844(defsubst f90-get-present-comment-type ()
d14e6bbe
GM
845 "If point lies within a comment, return the string starting the comment.
846For example, \"!\" or \"!!\"."
034a9d40 847 (save-excursion
e3f5ce56
GM
848 (when (f90-in-comment)
849 (beginning-of-line)
89fa1ef5 850 (re-search-forward "!+" (line-end-position))
e3f5ce56 851 (while (f90-in-string)
89fa1ef5
GM
852 (re-search-forward "!+" (line-end-position)))
853 (match-string 0))))
034a9d40
RS
854
855(defsubst f90-equal-symbols (a b)
ec2f376f 856 "Compare strings A and B neglecting case and allowing for nil value."
f14ca250
GM
857 (equal (if a (downcase a) nil)
858 (if b (downcase b) nil)))
034a9d40 859
034a9d40 860(defsubst f90-looking-at-do ()
d14e6bbe
GM
861 "Return (\"do\" NAME) if a do statement starts after point.
862NAME is nil if the statement has no label."
f14ca250 863 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(do\\)\\>")
0db701f0 864 (list (match-string 3) (match-string 2))))
ee30478d
KH
865
866(defsubst f90-looking-at-select-case ()
d14e6bbe
GM
867 "Return (\"select\" NAME) if a select-case statement starts after point.
868NAME is nil if the statement has no label."
f14ca250 869 (if (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
d14e6bbe 870\\(select\\)[ \t]*case[ \t]*(")
748dd5a8 871 (list (match-string 3) (match-string 2))))
034a9d40
RS
872
873(defsubst f90-looking-at-if-then ()
d14e6bbe
GM
874 "Return (\"if\" NAME) if an if () then statement starts after point.
875NAME is nil if the statement has no label."
034a9d40 876 (save-excursion
f14ca250
GM
877 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\\(if\\)\\>")
878 (let ((struct (match-string 3))
748dd5a8 879 (label (match-string 2))
f14ca250
GM
880 (pos (scan-lists (point) 1 0)))
881 (and pos (goto-char pos))
5c2a80ad
GM
882 (skip-chars-forward " \t")
883 (if (or (looking-at "then\\>")
884 (when (f90-line-continued)
885 (f90-next-statement)
886 (skip-chars-forward " \t0-9&")
887 (looking-at "then\\>")))
888 (list struct label))))))
034a9d40 889
b32a3d99 890(defsubst f90-looking-at-where-or-forall ()
d14e6bbe
GM
891 "Return (KIND NAME) if a where or forall block starts after point.
892NAME is nil if the statement has no label."
f14ca250
GM
893 (save-excursion
894 (when (looking-at "\\(\\(\\sw+\\)[ \t]*:\\)?[ \t]*\
895\\(where\\|forall\\)\\>")
896 (let ((struct (match-string 3))
748dd5a8 897 (label (match-string 2))
f14ca250
GM
898 (pos (scan-lists (point) 1 0)))
899 (and pos (goto-char pos))
900 (skip-chars-forward " \t")
901 (if (looking-at "\\(!\\|$\\)") (list struct label))))))
034a9d40
RS
902
903(defsubst f90-looking-at-type-like ()
d14e6bbe
GM
904 "Return (KIND NAME) if a type/interface/block-data block starts after point.
905NAME is non-nil only for type."
69658465 906 (cond
ee30478d 907 ((looking-at f90-type-def-re)
e7272ece 908 (list (match-string 1) (match-string 2)))
ee30478d 909 ((looking-at "\\(interface\\|block[\t]*data\\)\\>")
6734e165 910 (list (match-string 1) nil))))
034a9d40
RS
911
912(defsubst f90-looking-at-program-block-start ()
d14e6bbe 913 "Return (KIND NAME) if a program block with name NAME starts after point."
034a9d40 914 (cond
ee30478d 915 ((looking-at "\\(program\\)[ \t]+\\(\\sw+\\)\\>")
6734e165 916 (list (match-string 1) (match-string 2)))
034a9d40 917 ((and (not (looking-at "module[ \t]*procedure\\>"))
ee30478d 918 (looking-at "\\(module\\)[ \t]+\\(\\sw+\\)\\>"))
6734e165 919 (list (match-string 1) (match-string 2)))
b974df0a 920 ((and (not (looking-at "end[ \t]*\\(function\\|subroutine\\)"))
748dd5a8
GM
921 (looking-at "[^!'\"\&\n]*\\(function\\|subroutine\\)[ \t]+\
922\\(\\sw+\\)"))
6734e165 923 (list (match-string 1) (match-string 2)))))
034a9d40
RS
924
925(defsubst f90-looking-at-program-block-end ()
d14e6bbe 926 "Return (KIND NAME) if a block with name NAME ends after point."
69658465 927 (if (looking-at (concat "end[ \t]*" f90-blocks-re
ee30478d 928 "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
6734e165 929 (list (match-string 1) (match-string 3))))
034a9d40
RS
930
931(defsubst f90-comment-indent ()
ec2f376f
GM
932 "Return the indentation to be used for a comment starting at point.
933Used for `comment-indent-function' by F90 mode.
934\"!!!\", `f90-directive-comment-re', variable `f90-comment-region' return 0.
935`f90-indented-comment-re' (if not trailing code) calls `f90-calculate-indent'.
89fa1ef5 936All others return `comment-column', leaving at least one space after code."
034a9d40 937 (cond ((looking-at "!!!") 0)
ee30478d
KH
938 ((and f90-directive-comment-re
939 (looking-at f90-directive-comment-re)) 0)
034a9d40 940 ((looking-at (regexp-quote f90-comment-region)) 0)
2c0b59e3
DL
941 ((and (looking-at f90-indented-comment-re)
942 ;; Don't attempt to indent trailing comment as code.
943 (save-excursion
944 (skip-chars-backward " \t")
945 (bolp)))
034a9d40
RS
946 (f90-calculate-indent))
947 (t (skip-chars-backward " \t")
948 (max (if (bolp) 0 (1+ (current-column))) comment-column))))
949
950(defsubst f90-present-statement-cont ()
d14e6bbe
GM
951 "Return continuation properties of present statement.
952Possible return values are:
953single - statement is not continued.
954begin - current line is the first in a continued statement.
955end - current line is the last in a continued statement
956middle - current line is neither first nor last in a continued statement.
957Comment lines embedded amongst continued lines return 'middle."
034a9d40
RS
958 (let (pcont cont)
959 (save-excursion
e3f5ce56 960 (setq pcont (if (f90-previous-statement) (f90-line-continued))))
034a9d40
RS
961 (setq cont (f90-line-continued))
962 (cond ((and (not pcont) (not cont)) 'single)
963 ((and (not pcont) cont) 'begin)
964 ((and pcont (not cont)) 'end)
965 ((and pcont cont) 'middle)
e3f5ce56 966 (t (error "The impossible occurred")))))
034a9d40
RS
967
968(defsubst f90-indent-line-no ()
d14e6bbe
GM
969 "If `f90-leave-line-no' is nil, left-justify a line number.
970Leaves point at the first non-blank character after the line number.
971Call from beginning of line."
748dd5a8
GM
972 (and (null f90-leave-line-no) (looking-at "[ \t]+[0-9]")
973 (delete-horizontal-space))
034a9d40
RS
974 (skip-chars-forward " \t0-9"))
975
976(defsubst f90-no-block-limit ()
d14e6bbe
GM
977 "Return nil if point is at the edge of a code block.
978Searches line forward for \"function\" or \"subroutine\",
979if all else fails."
748dd5a8
GM
980 (save-excursion
981 (not (or (looking-at "end")
982 (looking-at "\\(do\\|if\\|else\\(if\\|where\\)?\
7cae52cf 983\\|select[ \t]*case\\|case\\|where\\|forall\\)\\>")
748dd5a8 984 (looking-at "\\(program\\|module\\|interface\\|\
034a9d40 985block[ \t]*data\\)\\>")
748dd5a8
GM
986 (looking-at "\\(contains\\|\\sw+[ \t]*:\\)")
987 (looking-at f90-type-def-re)
988 (re-search-forward "\\(function\\|subroutine\\)"
989 (line-end-position) t)))))
034a9d40
RS
990
991(defsubst f90-update-line ()
d14e6bbe
GM
992 "Change case of current line as per `f90-auto-keyword-case'."
993 (if f90-auto-keyword-case
994 (f90-change-keywords f90-auto-keyword-case
995 (line-beginning-position) (line-end-position))))
034a9d40 996\f
7cae52cf 997(defun f90-electric-insert ()
d14e6bbe 998 "Change keyword case and auto-fill line as operators are inserted."
7cae52cf 999 (interactive)
7cae52cf 1000 (self-insert-command 1)
d14e6bbe
GM
1001 (if auto-fill-function (f90-do-auto-fill) ; also updates line
1002 (f90-update-line)))
1003
7cae52cf 1004
034a9d40
RS
1005(defun f90-get-correct-indent ()
1006 "Get correct indent for a line starting with line number.
1007Does not check type and subprogram indentation."
6734e165 1008 (let ((epnt (line-end-position)) icol cont)
034a9d40
RS
1009 (save-excursion
1010 (while (and (f90-previous-statement)
1011 (or (progn
1012 (setq cont (f90-present-statement-cont))
1013 (or (eq cont 'end) (eq cont 'middle)))
1014 (looking-at "[ \t]*[0-9]"))))
1015 (setq icol (current-indentation))
1016 (beginning-of-line)
5c2a80ad
GM
1017 (when (re-search-forward "\\(if\\|do\\|select\\|where\\|forall\\)"
1018 (line-end-position) t)
e3f5ce56
GM
1019 (beginning-of-line)
1020 (skip-chars-forward " \t")
5c2a80ad
GM
1021 (cond ((f90-looking-at-do)
1022 (setq icol (+ icol f90-do-indent)))
1023 ((or (f90-looking-at-if-then)
1024 (f90-looking-at-where-or-forall)
1025 (f90-looking-at-select-case))
1026 (setq icol (+ icol f90-if-indent))))
1027 (end-of-line))
034a9d40 1028 (while (re-search-forward
ee30478d 1029 "\\(if\\|do\\|select\\|where\\|forall\\)" epnt t)
e3f5ce56
GM
1030 (beginning-of-line)
1031 (skip-chars-forward " \t0-9")
1032 (cond ((f90-looking-at-do)
1033 (setq icol (+ icol f90-do-indent)))
1034 ((or (f90-looking-at-if-then)
1035 (f90-looking-at-where-or-forall)
1036 (f90-looking-at-select-case))
1037 (setq icol (+ icol f90-if-indent)))
1038 ((looking-at f90-end-if-re)
1039 (setq icol (- icol f90-if-indent)))
1040 ((looking-at "end[ \t]*do\\>")
1041 (setq icol (- icol f90-do-indent))))
034a9d40
RS
1042 (end-of-line))
1043 icol)))
69658465 1044
034a9d40
RS
1045(defun f90-calculate-indent ()
1046 "Calculate the indent column based on previous statements."
1047 (interactive)
1048 (let (icol cont (case-fold-search t) (pnt (point)))
1049 (save-excursion
1050 (if (not (f90-previous-statement))
1051 (setq icol 0)
1052 (setq cont (f90-present-statement-cont))
1053 (if (eq cont 'end)
1054 (while (not (eq 'begin (f90-present-statement-cont)))
1055 (f90-previous-statement)))
1056 (cond ((eq cont 'begin)
1057 (setq icol (+ (f90-current-indentation)
1058 f90-continuation-indent)))
e3f5ce56 1059 ((eq cont 'middle) (setq icol (current-indentation)))
034a9d40
RS
1060 (t (setq icol (f90-current-indentation))
1061 (skip-chars-forward " \t")
1062 (if (looking-at "[0-9]")
1063 (setq icol (f90-get-correct-indent))
1064 (cond ((or (f90-looking-at-if-then)
1065 (f90-looking-at-where-or-forall)
1066 (f90-looking-at-select-case)
69658465 1067 (looking-at f90-else-like-re))
034a9d40
RS
1068 (setq icol (+ icol f90-if-indent)))
1069 ((f90-looking-at-do)
1070 (setq icol (+ icol f90-do-indent)))
1071 ((f90-looking-at-type-like)
1072 (setq icol (+ icol f90-type-indent)))
1073 ((or (f90-looking-at-program-block-start)
1074 (looking-at "contains[ \t]*\\($\\|!\\)"))
1075 (setq icol (+ icol f90-program-indent)))))
1076 (goto-char pnt)
1077 (beginning-of-line)
1078 (cond ((looking-at "[ \t]*$"))
ec2f376f 1079 ((looking-at "[ \t]*#") ; check for cpp directive
034a9d40
RS
1080 (setq icol 0))
1081 (t
1082 (skip-chars-forward " \t0-9")
1083 (cond ((or (looking-at f90-else-like-re)
1084 (looking-at f90-end-if-re))
1085 (setq icol (- icol f90-if-indent)))
ee30478d 1086 ((looking-at "end[ \t]*do\\>")
034a9d40
RS
1087 (setq icol (- icol f90-do-indent)))
1088 ((looking-at f90-end-type-re)
1089 (setq icol (- icol f90-type-indent)))
1090 ((or (looking-at "contains[ \t]*\\(!\\|$\\)")
1091 (f90-looking-at-program-block-end))
1092 (setq icol (- icol f90-program-indent))))))
1093 ))))
1094 icol))
1095\f
034a9d40
RS
1096(defun f90-previous-statement ()
1097 "Move point to beginning of the previous F90 statement.
ec2f376f
GM
1098Return nil if no previous statement is found.
1099A statement is a line which is neither blank nor a comment."
034a9d40
RS
1100 (interactive)
1101 (let (not-first-statement)
1102 (beginning-of-line)
1103 (while (and (setq not-first-statement (zerop (forward-line -1)))
ee30478d 1104 (looking-at "[ \t0-9]*\\(!\\|$\\|#\\)")))
034a9d40
RS
1105 not-first-statement))
1106
1107(defun f90-next-statement ()
1108 "Move point to beginning of the next F90 statement.
1109Return nil if no later statement is found."
1110 (interactive)
1111 (let (not-last-statement)
1112 (beginning-of-line)
1113 (while (and (setq not-last-statement
1114 (and (zerop (forward-line 1))
1115 (not (eobp))))
1116 (looking-at "[ \t0-9]*\\(!\\|$\\)")))
1117 not-last-statement))
1118
1119(defun f90-beginning-of-subprogram ()
76bccf35 1120 "Move point to the beginning of the current subprogram.
ec2f376f 1121Return (TYPE NAME), or nil if not found."
034a9d40
RS
1122 (interactive)
1123 (let ((count 1) (case-fold-search t) matching-beg)
e3f5ce56 1124 (beginning-of-line)
76bccf35 1125 (while (and (> count 0)
034a9d40 1126 (re-search-backward f90-program-block-re nil 'move))
e3f5ce56
GM
1127 (beginning-of-line)
1128 (skip-chars-forward " \t0-9")
1129 (cond ((setq matching-beg (f90-looking-at-program-block-start))
1130 (setq count (1- count)))
1131 ((f90-looking-at-program-block-end)
1132 (setq count (1+ count)))))
034a9d40
RS
1133 (beginning-of-line)
1134 (if (zerop count)
1135 matching-beg
ec2f376f 1136 (message "No beginning found.")
034a9d40
RS
1137 nil)))
1138
1139(defun f90-end-of-subprogram ()
76bccf35 1140 "Move point to the end of the current subprogram.
ec2f376f 1141Return (TYPE NAME), or nil if not found."
034a9d40 1142 (interactive)
7aee8047
GM
1143 (let ((case-fold-search t)
1144 (count 1)
1145 matching-end)
034a9d40 1146 (end-of-line)
76bccf35 1147 (while (and (> count 0)
034a9d40 1148 (re-search-forward f90-program-block-re nil 'move))
e3f5ce56
GM
1149 (beginning-of-line)
1150 (skip-chars-forward " \t0-9")
034a9d40 1151 (cond ((f90-looking-at-program-block-start)
e3f5ce56 1152 (setq count (1+ count)))
034a9d40 1153 ((setq matching-end (f90-looking-at-program-block-end))
e3f5ce56 1154 (setq count (1- count))))
034a9d40 1155 (end-of-line))
6f1d50da
GM
1156 ;; This means f90-end-of-subprogram followed by f90-start-of-subprogram
1157 ;; has a net non-zero effect, which seems odd.
1158;;; (forward-line 1)
034a9d40
RS
1159 (if (zerop count)
1160 matching-end
1161 (message "No end found.")
1162 nil)))
1163
6f1d50da
GM
1164
1165(defun f90-end-of-block (&optional num)
1166 "Move point forward to the end of the current code block.
1167With optional argument NUM, go forward that many balanced blocks.
1168If NUM is negative, go backward to the start of a block.
1169Checks for consistency of block types and labels (if present),
1170and completes outermost block if necessary."
1171 (interactive "p")
1172 (if (and num (< num 0)) (f90-beginning-of-block (- num)))
1173 (let ((f90-smart-end nil) ; for the final `f90-match-end'
1174 (case-fold-search t)
1175 (count (or num 1))
1176 start-list start-this start-type start-label end-type end-label)
1177 (if (interactive-p) (push-mark (point) t))
1178 (end-of-line) ; probably want this
1179 (while (and (> count 0) (re-search-forward f90-blocks-re nil 'move))
1180 (beginning-of-line)
1181 (skip-chars-forward " \t0-9")
1182 (cond ((or (f90-in-string) (f90-in-comment)))
1183 ((setq start-this
1184 (or
1185 (f90-looking-at-do)
1186 (f90-looking-at-select-case)
1187 (f90-looking-at-type-like)
1188 (f90-looking-at-program-block-start)
1189 (f90-looking-at-if-then)
1190 (f90-looking-at-where-or-forall)))
1191 (setq start-list (cons start-this start-list) ; not add-to-list!
1192 count (1+ count)))
1193 ((looking-at (concat "end[ \t]*" f90-blocks-re
1194 "[ \t]*\\(\\sw+\\)?"))
1195 (setq end-type (match-string 1)
1196 end-label (match-string 2)
1197 count (1- count))
1198 ;; Check any internal blocks.
1199 (when start-list
1200 (setq start-this (car start-list)
1201 start-list (cdr start-list)
1202 start-type (car start-this)
1203 start-label (cadr start-this))
748dd5a8 1204 (or (f90-equal-symbols start-type end-type)
6f1d50da
GM
1205 (error "End type `%s' does not match start type `%s'"
1206 end-type start-type))
748dd5a8 1207 (or (f90-equal-symbols start-label end-label)
6f1d50da
GM
1208 (error "End label `%s' does not match start label `%s'"
1209 end-label start-label)))))
1210 (end-of-line))
76bccf35 1211 (if (> count 0) (error "Missing block end"))
6f1d50da
GM
1212 ;; Check outermost block.
1213 (if (interactive-p)
1214 (save-excursion
1215 (beginning-of-line)
1216 (skip-chars-forward " \t0-9")
1217 (f90-match-end)))))
1218
1219(defun f90-beginning-of-block (&optional num)
1220 "Move point backwards to the start of the current code block.
1221With optional argument NUM, go backward that many balanced blocks.
1222If NUM is negative, go forward to the end of a block.
1223Checks for consistency of block types and labels (if present).
1224Does not check the outermost block, because it may be incomplete."
1225 (interactive "p")
1226 (if (and num (< num 0)) (f90-end-of-block (- num)))
1227 (let ((case-fold-search t)
1228 (count (or num 1))
748dd5a8
GM
1229 end-list end-this end-type end-label
1230 start-this start-type start-label)
6f1d50da
GM
1231 (if (interactive-p) (push-mark (point) t))
1232 (beginning-of-line) ; probably want this
1233 (while (and (> count 0) (re-search-backward f90-blocks-re nil 'move))
1234 (beginning-of-line)
1235 (skip-chars-forward " \t0-9")
1236 (cond ((or (f90-in-string) (f90-in-comment)))
1237 ((looking-at (concat "end[ \t]*" f90-blocks-re
1238 "[ \t]*\\(\\sw+\\)?"))
1239 (setq end-list (cons (list (match-string 1) (match-string 2))
1240 end-list)
1241 count (1+ count)))
1242 ((setq start-this
1243 (or
1244 (f90-looking-at-do)
1245 (f90-looking-at-select-case)
1246 (f90-looking-at-type-like)
1247 (f90-looking-at-program-block-start)
1248 (f90-looking-at-if-then)
1249 (f90-looking-at-where-or-forall)))
1250 (setq start-type (car start-this)
1251 start-label (cadr start-this)
1252 count (1- count))
1253 ;; Check any internal blocks.
1254 (when end-list
1255 (setq end-this (car end-list)
1256 end-list (cdr end-list)
1257 end-type (car end-this)
1258 end-label (cadr end-this))
748dd5a8 1259 (or (f90-equal-symbols start-type end-type)
6f1d50da
GM
1260 (error "Start type `%s' does not match end type `%s'"
1261 start-type end-type))
748dd5a8 1262 (or (f90-equal-symbols start-label end-label)
6f1d50da
GM
1263 (error "Start label `%s' does not match end label `%s'"
1264 start-label end-label))))))
1265 (if (> count 0) (error "Missing block start"))))
1266
76bccf35
GM
1267(defun f90-next-block (&optional num)
1268 "Move point forward to the next end or start of a code block.
1269With optional argument NUM, go forward that many blocks.
1270If NUM is negative, go backwards.
1271A block is a subroutine, if-endif, etc."
6f1d50da 1272 (interactive "p")
76bccf35
GM
1273 (let ((case-fold-search t)
1274 (count (if num (abs num) 1)))
1275 (while (and (> count 0)
1276 (if (> num 0) (re-search-forward f90-blocks-re nil 'move)
1277 (re-search-backward f90-blocks-re nil 'move)))
6f1d50da
GM
1278 (beginning-of-line)
1279 (skip-chars-forward " \t0-9")
76bccf35
GM
1280 (cond ((or (f90-in-string) (f90-in-comment)))
1281 ((or
1282 (looking-at "end[ \t]*")
1283 (f90-looking-at-do)
1284 (f90-looking-at-select-case)
1285 (f90-looking-at-type-like)
1286 (f90-looking-at-program-block-start)
1287 (f90-looking-at-if-then)
1288 (f90-looking-at-where-or-forall))
1289 (setq count (1- count))))
1290 (if (> num 0) (end-of-line)
1291 (beginning-of-line)))))
1292
1293
1294(defun f90-previous-block (&optional num)
1295 "Move point backward to the previous end or start of a code block.
1296With optional argument NUM, go backward that many blocks.
1297If NUM is negative, go forwards.
1298A block is a subroutine, if-endif, etc."
6f1d50da 1299 (interactive "p")
76bccf35 1300 (f90-next-block (- (or num 1))))
6f1d50da
GM
1301
1302
034a9d40 1303(defun f90-mark-subprogram ()
a729409a 1304 "Put mark at end of F90 subprogram, point at beginning, push mark."
034a9d40
RS
1305 (interactive)
1306 (let ((pos (point)) program)
1307 (f90-end-of-subprogram)
0ee7f068 1308 (push-mark)
034a9d40
RS
1309 (goto-char pos)
1310 (setq program (f90-beginning-of-subprogram))
1a341882 1311 (if (fboundp 'zmacs-activate-region)
0ee7f068 1312 (zmacs-activate-region)
1bb3ae5c 1313 (setq mark-active t
0ee7f068 1314 deactivate-mark nil))
034a9d40
RS
1315 program))
1316
1317(defun f90-comment-region (beg-region end-region)
1318 "Comment/uncomment every line in the region.
d14e6bbe
GM
1319Insert the variable `f90-comment-region' at the start of every line
1320in the region, or, if already present, remove it."
034a9d40 1321 (interactive "*r")
748dd5a8 1322 (let ((end (copy-marker end-region)))
034a9d40
RS
1323 (goto-char beg-region)
1324 (beginning-of-line)
1325 (if (looking-at (regexp-quote f90-comment-region))
1326 (delete-region (point) (match-end 0))
1327 (insert f90-comment-region))
e3f5ce56 1328 (while (and (zerop (forward-line 1))
748dd5a8 1329 (< (point) end))
034a9d40
RS
1330 (if (looking-at (regexp-quote f90-comment-region))
1331 (delete-region (point) (match-end 0))
1332 (insert f90-comment-region)))
1333 (set-marker end nil)))
1334
1335(defun f90-indent-line (&optional no-update)
87ee2359
GM
1336 "Indent current line as F90 code.
1337Unless optional argument NO-UPDATE is non-nil, call `f90-update-line'
1338after indenting."
a729409a 1339 (interactive "*P")
748dd5a8
GM
1340 (let ((case-fold-search t)
1341 (pos (point-marker))
1342 indent no-line-number)
1343 (beginning-of-line) ; digits after & \n are not line-nos
1344 (if (not (save-excursion (and (f90-previous-statement)
1345 (f90-line-continued))))
1346 (f90-indent-line-no)
1347 (setq no-line-number t)
1348 (skip-chars-forward " \t"))
034a9d40
RS
1349 (if (looking-at "!")
1350 (setq indent (f90-comment-indent))
748dd5a8
GM
1351 (and f90-smart-end (looking-at "end")
1352 (f90-match-end))
034a9d40 1353 (setq indent (f90-calculate-indent)))
748dd5a8 1354 (or (= indent (current-column))
e3f5ce56 1355 (f90-indent-to indent no-line-number))
034a9d40
RS
1356 ;; If initial point was within line's indentation,
1357 ;; position after the indentation. Else stay at same point in text.
748dd5a8
GM
1358 (and (< (point) pos)
1359 (goto-char pos))
69658465 1360 (if auto-fill-function
7aee8047
GM
1361 ;; GM NO-UPDATE not honoured, since this calls f90-update-line.
1362 (f90-do-auto-fill)
748dd5a8 1363 (or no-update (f90-update-line)))
034a9d40
RS
1364 (set-marker pos nil)))
1365
1366(defun f90-indent-new-line ()
a729409a 1367 "Re-indent current line, insert a newline and indent the newline.
87ee2359 1368An abbrev before point is expanded if the variable `abbrev-mode' is non-nil.
034a9d40 1369If run in the middle of a line, the line is not broken."
a729409a 1370 (interactive "*")
748dd5a8
GM
1371 (if abbrev-mode (expand-abbrev))
1372 (beginning-of-line) ; reindent where likely to be needed
7aee8047 1373 (f90-indent-line) ; calls indent-line-no, update-line
748dd5a8
GM
1374 (end-of-line)
1375 (delete-horizontal-space) ; destroy trailing whitespace
1376 (let ((string (f90-in-string))
1377 (cont (f90-line-continued)))
1378 (and string (not cont) (insert "&"))
034a9d40 1379 (newline)
748dd5a8 1380 (if (or string (and cont f90-beginning-ampersand)) (insert "&")))
7aee8047 1381 (f90-indent-line 'no-update)) ; nothing to update
034a9d40
RS
1382
1383
1384(defun f90-indent-region (beg-region end-region)
1385 "Indent every line in region by forward parsing."
1386 (interactive "*r")
748dd5a8 1387 (let ((end-region-mark (copy-marker end-region))
e3f5ce56 1388 (save-point (point-marker))
748dd5a8 1389 block-list ind-lev ind-curr ind-b cont struct beg-struct end-struct)
034a9d40 1390 (goto-char beg-region)
ec2f376f 1391 ;; First find a line which is not a continuation line or comment.
034a9d40 1392 (beginning-of-line)
ee30478d 1393 (while (and (looking-at "[ \t]*[0-9]*\\(!\\|#\\|[ \t]*$\\)")
034a9d40
RS
1394 (progn (f90-indent-line 'no-update)
1395 (zerop (forward-line 1)))
1396 (< (point) end-region-mark)))
1397 (setq cont (f90-present-statement-cont))
1398 (while (and (or (eq cont 'middle) (eq cont 'end))
1399 (f90-previous-statement))
1400 (setq cont (f90-present-statement-cont)))
ec2f376f 1401 ;; Process present line for beginning of block.
034a9d40
RS
1402 (setq f90-cache-position (point))
1403 (f90-indent-line 'no-update)
e3f5ce56
GM
1404 (setq ind-lev (f90-current-indentation)
1405 ind-curr ind-lev)
1406 (beginning-of-line)
1407 (skip-chars-forward " \t0-9")
1408 (setq struct nil
1409 ind-b (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
034a9d40
RS
1410 ((or (setq struct (f90-looking-at-if-then))
1411 (setq struct (f90-looking-at-select-case))
1412 (setq struct (f90-looking-at-where-or-forall))
1413 (looking-at f90-else-like-re))
1414 f90-if-indent)
1415 ((setq struct (f90-looking-at-type-like))
1416 f90-type-indent)
34ba7e3d
GM
1417 ((or (setq struct (f90-looking-at-program-block-start))
1418 (looking-at "contains[ \t]*\\($\\|!\\)"))
034a9d40
RS
1419 f90-program-indent)))
1420 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1421 (if struct (setq block-list (cons struct block-list)))
1422 (while (and (f90-line-continued) (zerop (forward-line 1))
1423 (< (point) end-region-mark))
ec2f376f
GM
1424 (if (looking-at "[ \t]*!")
1425 (f90-indent-to (f90-comment-indent))
748dd5a8
GM
1426 (or (= (current-indentation)
1427 (+ ind-curr f90-continuation-indent))
ec2f376f
GM
1428 (f90-indent-to (+ ind-curr f90-continuation-indent) 'no-line-no))))
1429 ;; Process all following lines.
d14e6bbe 1430 (while (and (zerop (forward-line 1)) (< (point) end-region-mark))
034a9d40
RS
1431 (beginning-of-line)
1432 (f90-indent-line-no)
1433 (setq f90-cache-position (point))
1434 (cond ((looking-at "[ \t]*$") (setq ind-curr 0))
1435 ((looking-at "[ \t]*#") (setq ind-curr 0))
1436 ((looking-at "!") (setq ind-curr (f90-comment-indent)))
1437 ((f90-no-block-limit) (setq ind-curr ind-lev))
1438 ((looking-at f90-else-like-re) (setq ind-curr
1439 (- ind-lev f90-if-indent)))
1440 ((looking-at "contains[ \t]*\\($\\|!\\)")
1441 (setq ind-curr (- ind-lev f90-program-indent)))
1442 ((setq ind-b
1443 (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
1444 ((or (setq struct (f90-looking-at-if-then))
1445 (setq struct (f90-looking-at-select-case))
1446 (setq struct (f90-looking-at-where-or-forall)))
1447 f90-if-indent)
1448 ((setq struct (f90-looking-at-type-like))
1449 f90-type-indent)
1450 ((setq struct (f90-looking-at-program-block-start))
1451 f90-program-indent)))
1452 (setq ind-curr ind-lev)
1453 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1454 (setq block-list (cons struct block-list)))
1455 ((setq end-struct (f90-looking-at-program-block-end))
1456 (setq beg-struct (car block-list)
1457 block-list (cdr block-list))
69658465 1458 (if f90-smart-end
034a9d40 1459 (save-excursion
ec2f376f
GM
1460 (f90-block-match (car beg-struct) (car (cdr beg-struct))
1461 (car end-struct) (car (cdr end-struct)))))
034a9d40
RS
1462 (setq ind-b
1463 (cond ((looking-at f90-end-if-re) f90-if-indent)
1464 ((looking-at "end[ \t]*do\\>") f90-do-indent)
1465 ((looking-at f90-end-type-re) f90-type-indent)
1466 ((f90-looking-at-program-block-end)
1467 f90-program-indent)))
1468 (if ind-b (setq ind-lev (- ind-lev ind-b)))
1469 (setq ind-curr ind-lev))
034a9d40 1470 (t (setq ind-curr ind-lev)))
ec2f376f 1471 ;; Do the indentation if necessary.
748dd5a8 1472 (or (= ind-curr (current-column))
034a9d40
RS
1473 (f90-indent-to ind-curr))
1474 (while (and (f90-line-continued) (zerop (forward-line 1))
1475 (< (point) end-region-mark))
ec2f376f
GM
1476 (if (looking-at "[ \t]*!")
1477 (f90-indent-to (f90-comment-indent))
748dd5a8
GM
1478 (or (= (current-indentation)
1479 (+ ind-curr f90-continuation-indent))
ec2f376f
GM
1480 (f90-indent-to
1481 (+ ind-curr f90-continuation-indent) 'no-line-no)))))
1482 ;; Restore point, etc.
034a9d40
RS
1483 (setq f90-cache-position nil)
1484 (goto-char save-point)
1485 (set-marker end-region-mark nil)
1486 (set-marker save-point nil)
1a341882 1487 (if (fboundp 'zmacs-deactivate-region)
0ee7f068 1488 (zmacs-deactivate-region)
034a9d40
RS
1489 (deactivate-mark))))
1490
1491(defun f90-indent-subprogram ()
ec2f376f 1492 "Properly indent the subprogram containing point."
a729409a 1493 (interactive "*")
034a9d40 1494 (save-excursion
e3f5ce56 1495 (let ((program (f90-mark-subprogram)))
034a9d40
RS
1496 (if program
1497 (progn
2a74bdc1 1498 (message "Indenting %s %s..."
7f03b2b5 1499 (car program) (car (cdr program)))
9a8ba072 1500 (indent-region (point) (mark) nil)
2a74bdc1 1501 (message "Indenting %s %s...done"
7f03b2b5 1502 (car program) (car (cdr program))))
2a74bdc1 1503 (message "Indenting the whole file...")
9a8ba072 1504 (indent-region (point) (mark) nil)
2a74bdc1 1505 (message "Indenting the whole file...done")))))
034a9d40 1506
034a9d40 1507(defun f90-break-line (&optional no-update)
87ee2359
GM
1508 "Break line at point, insert continuation marker(s) and indent.
1509Unless in a string or comment, or if the optional argument NO-UPDATE
1510is non-nil, call `f90-update-line' after inserting the continuation marker."
a729409a 1511 (interactive "*P")
89fa1ef5
GM
1512 (cond ((f90-in-string)
1513 (insert "&\n&"))
1514 ((f90-in-comment)
1515 (insert "\n" (f90-get-present-comment-type)))
1516 (t (insert "&")
1517 (or no-update (f90-update-line))
1518 (newline 1)
1519 (if f90-beginning-ampersand (insert "&"))))
84021009 1520 (indent-according-to-mode))
69658465 1521
034a9d40 1522(defun f90-find-breakpoint ()
87ee2359 1523 "From `fill-column', search backward for break-delimiter."
748dd5a8
GM
1524 (re-search-backward f90-break-delimiters (line-beginning-position))
1525 (if (not f90-break-before-delimiters)
1526 (forward-char (if (looking-at f90-no-break-re) 2 1))
1527 (backward-char)
1528 (or (looking-at f90-no-break-re)
0db701f0 1529 (forward-char))))
034a9d40 1530
034a9d40 1531(defun f90-do-auto-fill ()
d14e6bbe
GM
1532 "Break line if non-white characters beyond `fill-column'.
1533Update keyword case first."
a729409a 1534 (interactive "*")
ec2f376f 1535 ;; Break line before or after last delimiter (non-word char) if
b974df0a 1536 ;; position is beyond fill-column.
ec2f376f 1537 ;; Will not break **, //, or => (as specified by f90-no-break-re).
7cae52cf 1538 (f90-update-line)
d595e95d
GM
1539 ;; Need this for `f90-electric-insert' and other f90- callers.
1540 (unless (and (boundp 'comment-auto-fill-only-comments)
1541 comment-auto-fill-only-comments
1542 (not (f90-in-comment)))
1543 (while (> (current-column) fill-column)
1544 (let ((pos-mark (point-marker)))
1545 (move-to-column fill-column)
1546 (or (f90-in-string) (f90-find-breakpoint))
1547 (f90-break-line)
1548 (goto-char pos-mark)
1549 (set-marker pos-mark nil)))))
b974df0a 1550
a259425b
GM
1551(defun f90-join-lines (&optional arg)
1552 "Join current line to previous, fix whitespace, continuation, comments.
a729409a 1553With optional argument ARG, join current line to following line.
a259425b
GM
1554Like `join-line', but handles F90 syntax."
1555 (interactive "*P")
1556 (beginning-of-line)
1557 (if arg (forward-line 1))
1558 (when (eq (preceding-char) ?\n)
1559 (skip-chars-forward " \t")
1560 (if (looking-at "\&") (delete-char 1))
1561 (beginning-of-line)
1562 (delete-region (point) (1- (point)))
034a9d40 1563 (skip-chars-backward " \t")
a259425b
GM
1564 (and (eq (preceding-char) ?&) (delete-char -1))
1565 (and (f90-in-comment)
1566 (looking-at "[ \t]*!+")
1567 (replace-match ""))
1568 (or (f90-in-string)
1569 (fixup-whitespace))))
034a9d40
RS
1570
1571(defun f90-fill-region (beg-region end-region)
d14e6bbe 1572 "Fill every line in region by forward parsing. Join lines if possible."
034a9d40 1573 (interactive "*r")
748dd5a8 1574 (let ((end-region-mark (copy-marker end-region))
e3f5ce56
GM
1575 (go-on t)
1576 f90-smart-end f90-auto-keyword-case auto-fill-function)
034a9d40
RS
1577 (goto-char beg-region)
1578 (while go-on
ec2f376f 1579 ;; Join as much as possible.
a729409a 1580 (while (progn
a259425b
GM
1581 (end-of-line)
1582 (skip-chars-backward " \t")
1583 (eq (preceding-char) ?&))
1584 (f90-join-lines 'forward))
ec2f376f 1585 ;; Chop the line if necessary.
034a9d40
RS
1586 (while (> (save-excursion (end-of-line) (current-column))
1587 fill-column)
1588 (move-to-column fill-column)
7cae52cf
RS
1589 (f90-find-breakpoint)
1590 (f90-break-line 'no-update))
748dd5a8 1591 (setq go-on (and (< (point) end-region-mark)
e3f5ce56
GM
1592 (zerop (forward-line 1)))
1593 f90-cache-position (point)))
034a9d40 1594 (setq f90-cache-position nil)
748dd5a8 1595 (set-marker end-region-mark nil)
1a341882 1596 (if (fboundp 'zmacs-deactivate-region)
0ee7f068 1597 (zmacs-deactivate-region)
034a9d40
RS
1598 (deactivate-mark))))
1599\f
1600(defun f90-block-match (beg-block beg-name end-block end-name)
1601 "Match end-struct with beg-struct and complete end-block if possible.
ec2f376f
GM
1602BEG-BLOCK is the type of block as indicated at the start (e.g., do).
1603BEG-NAME is the block start name (may be nil).
1604END-BLOCK is the type of block as indicated at the end (may be nil).
1605END-NAME is the block end name (may be nil).
034a9d40 1606Leave point at the end of line."
6734e165 1607 (search-forward "end" (line-end-position))
034a9d40 1608 (catch 'no-match
748dd5a8
GM
1609 (if (f90-equal-symbols beg-block end-block)
1610 (search-forward end-block)
1611 (if end-block
1612 (progn
1613 (message "END %s does not match %s." end-block beg-block)
1614 (end-of-line)
1615 (throw 'no-match nil))
1616 (message "Inserting %s." beg-block)
1617 (insert (concat " " beg-block))))
1618 (if (f90-equal-symbols beg-name end-name)
1619 (and end-name (search-forward end-name))
1620 (cond ((and beg-name (not end-name))
1621 (message "Inserting %s." beg-name)
1622 (insert (concat " " beg-name)))
1623 ((and beg-name end-name)
1624 (message "Replacing %s with %s." end-name beg-name)
1625 (search-forward end-name)
1626 (replace-match beg-name))
1627 ((and (not beg-name) end-name)
1628 (message "Deleting %s." end-name)
1629 (search-forward end-name)
1630 (replace-match ""))))
1631 (or (looking-at "[ \t]*!") (delete-horizontal-space))))
034a9d40
RS
1632
1633(defun f90-match-end ()
ec2f376f 1634 "From an end block statement, find the corresponding block and name."
034a9d40 1635 (interactive)
748dd5a8
GM
1636 (let ((count 1)
1637 (top-of-window (window-start))
1638 (end-point (point))
1639 (case-fold-search t)
e3f5ce56 1640 matching-beg beg-name end-name beg-block end-block end-struct)
5c2a80ad
GM
1641 (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")
1642 (setq end-struct (f90-looking-at-program-block-end)))
e3f5ce56
GM
1643 (setq end-block (car end-struct)
1644 end-name (car (cdr end-struct)))
5c2a80ad
GM
1645 (save-excursion
1646 (beginning-of-line)
6dd52caf 1647 (while (and (> count 0) (re-search-backward f90-blocks-re nil t))
e3f5ce56 1648 (beginning-of-line)
748dd5a8
GM
1649 ;; GM not a line number if continued line.
1650;;; (skip-chars-forward " \t")
1651;;; (skip-chars-forward "0-9")
e3f5ce56 1652 (skip-chars-forward " \t0-9")
6dd52caf
GM
1653 (cond ((or (f90-in-string) (f90-in-comment)))
1654 ((setq matching-beg
e3f5ce56
GM
1655 (or
1656 (f90-looking-at-do)
1657 (f90-looking-at-if-then)
1658 (f90-looking-at-where-or-forall)
1659 (f90-looking-at-select-case)
1660 (f90-looking-at-type-like)
1661 (f90-looking-at-program-block-start)))
1662 (setq count (1- count)))
6dd52caf 1663 ((looking-at (concat "end[ \t]*" f90-blocks-re))
e3f5ce56 1664 (setq count (1+ count)))))
6dd52caf 1665 (if (> count 0)
5c2a80ad
GM
1666 (message "No matching beginning.")
1667 (f90-update-line)
1668 (if (eq f90-smart-end 'blink)
1669 (if (< (point) top-of-window)
1670 (message "Matches %s: %s"
1671 (what-line)
1672 (buffer-substring
1673 (line-beginning-position)
1674 (line-end-position)))
1675 (sit-for 1)))
e3f5ce56
GM
1676 (setq beg-block (car matching-beg)
1677 beg-name (car (cdr matching-beg)))
5c2a80ad
GM
1678 (goto-char end-point)
1679 (beginning-of-line)
1680 (f90-block-match beg-block beg-name end-block end-name))))))
034a9d40
RS
1681
1682(defun f90-insert-end ()
87ee2359 1683 "Insert a complete end statement matching beginning of present block."
a729409a 1684 (interactive "*")
e3f5ce56 1685 (let ((f90-smart-end (or f90-smart-end 'blink)))
034a9d40
RS
1686 (insert "end")
1687 (f90-indent-new-line)))
1688\f
ec2f376f 1689;; Abbrevs and keywords.
034a9d40
RS
1690
1691(defun f90-abbrev-start ()
69658465 1692 "Typing `\\[help-command] or `? lists all the F90 abbrevs.
034a9d40 1693Any other key combination is executed normally."
7aee8047
GM
1694 (interactive "*")
1695 (insert last-command-char)
1696 (let (char event)
1697 (if (fboundp 'next-command-event) ; XEmacs
1698 (setq event (next-command-event)
1699 char (event-to-character event))
1700 (setq event (read-event)
1701 char event))
be550ccc 1702 ;; Insert char if not equal to `?', or if abbrev-mode is off.
7aee8047 1703 (if (and abbrev-mode (or (eq char ??) (eq char help-char)))
034a9d40 1704 (f90-abbrev-help)
7aee8047 1705 (setq unread-command-events (list event)))))
034a9d40
RS
1706
1707(defun f90-abbrev-help ()
1708 "List the currently defined abbrevs in F90 mode."
1709 (interactive)
1710 (message "Listing abbrev table...")
1711 (display-buffer (f90-prepare-abbrev-list-buffer))
1712 (message "Listing abbrev table...done"))
1713
1714(defun f90-prepare-abbrev-list-buffer ()
ec2f376f 1715 "Create a buffer listing the F90 mode abbreviations."
034a9d40
RS
1716 (save-excursion
1717 (set-buffer (get-buffer-create "*Abbrevs*"))
1718 (erase-buffer)
1719 (insert-abbrev-table-description 'f90-mode-abbrev-table t)
1720 (goto-char (point-min))
1721 (set-buffer-modified-p nil)
1722 (edit-abbrevs-mode))
1723 (get-buffer-create "*Abbrevs*"))
1724
1725(defun f90-upcase-keywords ()
1726 "Upcase all F90 keywords in the buffer."
a729409a 1727 (interactive "*")
034a9d40
RS
1728 (f90-change-keywords 'upcase-word))
1729
1730(defun f90-capitalize-keywords ()
1731 "Capitalize all F90 keywords in the buffer."
a729409a 1732 (interactive "*")
034a9d40
RS
1733 (f90-change-keywords 'capitalize-word))
1734
1735(defun f90-downcase-keywords ()
1736 "Downcase all F90 keywords in the buffer."
a729409a 1737 (interactive "*")
034a9d40
RS
1738 (f90-change-keywords 'downcase-word))
1739
1740(defun f90-upcase-region-keywords (beg end)
1741 "Upcase all F90 keywords in the region."
1742 (interactive "*r")
1743 (f90-change-keywords 'upcase-word beg end))
1744
1745(defun f90-capitalize-region-keywords (beg end)
1746 "Capitalize all F90 keywords in the region."
1747 (interactive "*r")
1748 (f90-change-keywords 'capitalize-word beg end))
1749
1750(defun f90-downcase-region-keywords (beg end)
1751 "Downcase all F90 keywords in the region."
1752 (interactive "*r")
1753 (f90-change-keywords 'downcase-word beg end))
1754
1755;; Change the keywords according to argument.
1756(defun f90-change-keywords (change-word &optional beg end)
ec2f376f 1757 "Change the case of F90 keywords in the region (if specified) or buffer.
02f85cba 1758CHANGE-WORD should be one of 'upcase-word, 'downcase-word, 'capitalize-word."
034a9d40 1759 (save-excursion
e3f5ce56
GM
1760 (setq beg (or beg (point-min))
1761 end (or end (point-max)))
69658465 1762 (let ((keyword-re
ee30478d
KH
1763 (concat "\\("
1764 f90-keywords-re "\\|" f90-procedures-re "\\|"
1765 f90-hpf-keywords-re "\\|" f90-operators-re "\\)"))
a1506d29 1766 (ref-point (point-min))
e3f5ce56
GM
1767 (modified (buffer-modified-p))
1768 state saveword back-point)
034a9d40 1769 (goto-char beg)
ee30478d
KH
1770 (unwind-protect
1771 (while (re-search-forward keyword-re end t)
5c2a80ad
GM
1772 (unless (progn
1773 (setq state (parse-partial-sexp ref-point (point)))
1774 (or (nth 3 state) (nth 4 state)
748dd5a8 1775 ;; GM f90-directive-comment-re?
ec2f376f 1776 (save-excursion ; check for cpp directive
5c2a80ad
GM
1777 (beginning-of-line)
1778 (skip-chars-forward " \t0-9")
1779 (looking-at "#"))))
ee30478d 1780 (setq ref-point (point)
e3f5ce56
GM
1781 back-point (save-excursion (backward-word 1) (point))
1782 saveword (buffer-substring back-point ref-point))
ee30478d
KH
1783 (funcall change-word -1)
1784 (or (string= saveword (buffer-substring back-point ref-point))
1785 (setq modified t))))
1786 (or modified (set-buffer-modified-p nil))))))
034a9d40 1787
d2d15846
DL
1788
1789(defun f90-current-defun ()
1790 "Function to use for `add-log-current-defun-function' in F90 mode."
1791 (save-excursion
1792 (nth 1 (f90-beginning-of-subprogram))))
1793
034a9d40 1794(provide 'f90)
db97b872 1795
ab5796a9 1796;;; arch-tag: fceac97c-c147-44bd-aec0-172d4b560ef8
034a9d40 1797;;; f90.el ends here