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