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