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