Add a comment in case people complain about the h flag for Tar.
[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))
380 ;; line numbers (lines whose first character after number is letter)
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
034a9d40
RS
404(defvar f90-mode-syntax-table nil
405 "Syntax table in use in F90 mode buffers.")
406
d14e6bbe 407(unless f90-mode-syntax-table
034a9d40 408 (setq f90-mode-syntax-table (make-syntax-table))
ec2f376f
GM
409 (modify-syntax-entry ?\! "<" f90-mode-syntax-table) ; begin comment
410 (modify-syntax-entry ?\n ">" f90-mode-syntax-table) ; end comment
411 (modify-syntax-entry ?_ "w" f90-mode-syntax-table) ; underscore in names
034a9d40
RS
412 (modify-syntax-entry ?\' "\"" f90-mode-syntax-table) ; string quote
413 (modify-syntax-entry ?\" "\"" f90-mode-syntax-table) ; string quote
ec2f376f
GM
414 (modify-syntax-entry ?\` "w" f90-mode-syntax-table) ; for abbrevs
415 (modify-syntax-entry ?\r " " f90-mode-syntax-table) ; return is whitespace
416 (modify-syntax-entry ?+ "." f90-mode-syntax-table) ; punctuation
417 (modify-syntax-entry ?- "." f90-mode-syntax-table)
418 (modify-syntax-entry ?= "." f90-mode-syntax-table)
419 (modify-syntax-entry ?* "." f90-mode-syntax-table)
420 (modify-syntax-entry ?/ "." f90-mode-syntax-table)
029ec30f 421 (modify-syntax-entry ?\\ "\\" f90-mode-syntax-table)) ; escape chars
034a9d40 422
ec2f376f 423
034a9d40
RS
424(defvar f90-mode-map ()
425 "Keymap used in F90 mode.")
ee30478d 426
d14e6bbe 427(unless f90-mode-map
034a9d40
RS
428 (setq f90-mode-map (make-sparse-keymap))
429 (define-key f90-mode-map "`" 'f90-abbrev-start)
430 (define-key f90-mode-map "\C-c;" 'f90-comment-region)
431 (define-key f90-mode-map "\C-\M-a" 'f90-beginning-of-subprogram)
432 (define-key f90-mode-map "\C-\M-e" 'f90-end-of-subprogram)
433 (define-key f90-mode-map "\C-\M-h" 'f90-mark-subprogram)
434 (define-key f90-mode-map "\C-\M-q" 'f90-indent-subprogram)
435 (define-key f90-mode-map "\C-j" 'f90-indent-new-line) ; LFD equals C-j
436 (define-key f90-mode-map "\r" 'newline)
437 (define-key f90-mode-map "\C-c\r" 'f90-break-line)
ec2f376f 438;;; (define-key f90-mode-map [M-return] 'f90-break-line)
034a9d40
RS
439 (define-key f90-mode-map "\C-c\C-d" 'f90-join-lines)
440 (define-key f90-mode-map "\C-c\C-f" 'f90-fill-region)
441 (define-key f90-mode-map "\C-c\C-p" 'f90-previous-statement)
442 (define-key f90-mode-map "\C-c\C-n" 'f90-next-statement)
443 (define-key f90-mode-map "\C-c\C-w" 'f90-insert-end)
7cae52cf
RS
444 (define-key f90-mode-map "\t" 'f90-indent-line)
445 (define-key f90-mode-map "," 'f90-electric-insert)
446 (define-key f90-mode-map "+" 'f90-electric-insert)
447 (define-key f90-mode-map "-" 'f90-electric-insert)
448 (define-key f90-mode-map "*" 'f90-electric-insert)
449 (define-key f90-mode-map "/" 'f90-electric-insert))
ee30478d 450
69658465 451
1bb3ae5c 452(if f90-xemacs-flag
ee30478d
KH
453 (defvar f90-xemacs-menu
454 '("F90"
455 ["Indent Subprogram" f90-indent-subprogram t]
456 ["Mark Subprogram" f90-mark-subprogram t]
457 ["Beginning of Subprogram" f90-beginning-of-subprogram t]
458 ["End of Subprogram" f90-end-of-subprogram t]
459 "-----"
460 ["(Un)Comment Region" f90-comment-region t]
461 ["Indent Region" indent-region t]
462 ["Fill Region" f90-fill-region t]
463 "-----"
464 ["Break Line at Point" f90-break-line t]
465 ["Join with Next Line" f90-join-lines t]
466 ["Insert Newline" newline t]
b974df0a 467 ["Insert Block End" f90-insert-end t]
ee30478d
KH
468 "-----"
469 ["Upcase Keywords (buffer)" f90-upcase-keywords t]
ec2f376f 470 ["Upcase Keywords (region)" f90-upcase-region-keywords t]
ee30478d 471 ["Capitalize Keywords (buffer)" f90-capitalize-keywords t]
ec2f376f 472 ["Capitalize Keywords (region)" f90-capitalize-region-keywords t]
ee30478d 473 ["Downcase Keywords (buffer)" f90-downcase-keywords t]
ec2f376f 474 ["Downcase Keywords (region)" f90-downcase-region-keywords t]
ee30478d 475 "-----"
ec2f376f
GM
476 ["Toggle abbrev-mode" abbrev-mode t]
477 ["Toggle auto-fill" auto-fill-mode t])
ee30478d 478 "XEmacs menu for F90 mode.")
b974df0a 479
ec2f376f 480 ;; Emacs.
b974df0a
EN
481 (defvar f90-change-case-menu
482 (let ((map (make-sparse-keymap "Change Keyword Case")))
ec2f376f
GM
483 (define-key map [dkr]
484 (cons "Downcase Keywords (region)" 'f90-downcase-region-keywords))
b974df0a 485 (put 'f90-downcase-region-keywords 'menu-enable 'mark-active)
ec2f376f
GM
486 (define-key map [ckr]
487 (cons "Capitalize Keywords (region)" 'f90-capitalize-region-keywords))
b974df0a 488 (put 'f90-capitalize-region-keywords 'menu-enable 'mark-active)
ec2f376f
GM
489 (define-key map [ukr]
490 (cons "Upcase Keywords (region)" 'f90-upcase-region-keywords))
b974df0a 491 (put 'f90-upcase-region-keywords 'menu-enable 'mark-active)
b974df0a 492 (define-key map [line] (list "-----------------"))
ec2f376f
GM
493 (define-key map [dkb]
494 (cons "Downcase Keywords (buffer)" 'f90-downcase-keywords))
495 (define-key map [ckb]
496 (cons "Capitalize Keywords (buffer)" 'f90-capitalize-keywords))
497 (define-key map [ukb]
498 (cons "Upcase Keywords (buffer)" 'f90-upcase-keywords))
b974df0a
EN
499 map)
500 "Submenu for change of case.")
501 (defalias 'f90-change-case-menu f90-change-case-menu)
502
ec2f376f 503 ;; Font-lock-menu and function calls.
b974df0a
EN
504 (defalias 'f90-font-lock-on 'font-lock-mode)
505 (defalias 'f90-font-lock-off 'font-lock-mode)
506 (put 'f90-font-lock-on 'menu-enable 'font-lock-mode)
507 (put 'f90-font-lock-off 'menu-enable '(not font-lock-mode))
69658465 508
b974df0a
EN
509 (defun f90-font-lock-1 ()
510 (interactive)
511 "Set font-lock-keywords to f90-font-lock-keywords-1."
512 (font-lock-mode 1)
513 (setq font-lock-keywords f90-font-lock-keywords-1)
514 (font-lock-fontify-buffer))
69658465 515
b974df0a
EN
516 (defun f90-font-lock-2 ()
517 (interactive)
518 "Set font-lock-keywords to f90-font-lock-keywords-2."
519 (font-lock-mode 1)
520 (setq font-lock-keywords f90-font-lock-keywords-2)
521 (font-lock-fontify-buffer))
69658465 522
b974df0a
EN
523 (defun f90-font-lock-3 ()
524 (interactive)
525 "Set font-lock-keywords to f90-font-lock-keywords-3."
526 (font-lock-mode 1)
527 (setq font-lock-keywords f90-font-lock-keywords-3)
528 (font-lock-fontify-buffer))
69658465 529
b974df0a
EN
530 (defun f90-font-lock-4 ()
531 (interactive)
532 "Set font-lock-keywords to f90-font-lock-keywords-4."
533 (font-lock-mode 1)
534 (setq font-lock-keywords f90-font-lock-keywords-4)
535 (font-lock-fontify-buffer))
69658465 536
b974df0a
EN
537 (defvar f90-font-lock-menu
538 (let ((map (make-sparse-keymap "f90-font-lock-menu")))
ec2f376f
GM
539 (define-key map [h4]
540 (cons "Maximum highlighting (level 4)" 'f90-font-lock-4))
541 (define-key map [h3]
542 (cons "Heavy highlighting (level 3)" 'f90-font-lock-3))
543 (define-key map [h2]
544 (cons "Default highlighting (level 2)" 'f90-font-lock-2))
545 (define-key map [h1]
546 (cons "Light highlighting (level 1)" 'f90-font-lock-1))
b974df0a 547 (define-key map [line] (list "-----------------"))
ec2f376f
GM
548 (define-key map [floff]
549 (cons "Turn off font-lock-mode" 'f90-font-lock-on))
550 (define-key map [flon]
551 (cons "Turn on font-lock-mode" 'f90-font-lock-off))
b974df0a
EN
552 map)
553 "Submenu for highlighting using font-lock-mode.")
ec2f376f 554
b974df0a
EN
555 (defalias 'f90-font-lock-menu f90-font-lock-menu)
556
034a9d40 557 (define-key f90-mode-map [menu-bar] (make-sparse-keymap))
69658465
GM
558 (define-key f90-mode-map [menu-bar f90]
559 (cons "F90" (make-sparse-keymap "f90")))
b974df0a
EN
560 (define-key f90-mode-map [menu-bar f90 f90-imenu-menu]
561 '("Add imenu Menu" . f90-add-imenu-menu))
034a9d40
RS
562 (define-key f90-mode-map [menu-bar f90 abbrev-mode]
563 '("Toggle abbrev-mode" . abbrev-mode))
b974df0a
EN
564 (define-key f90-mode-map [menu-bar f90 auto-fill-mode]
565 '("Toggle auto-fill" . auto-fill-mode))
ec2f376f 566 (define-key f90-mode-map [menu-bar f90 line1] '("----"))
b974df0a
EN
567 (define-key f90-mode-map [menu-bar f90 f90-change-case-menu]
568 (cons "Change Keyword Case" 'f90-change-case-menu))
569 (define-key f90-mode-map [menu-bar f90 f90-font-lock-menu]
570 (cons "Highlighting" 'f90-font-lock-menu))
ec2f376f 571 (define-key f90-mode-map [menu-bar f90 line2] '("----"))
034a9d40 572 (define-key f90-mode-map [menu-bar f90 f90-insert-end]
b974df0a 573 '("Insert Block End" . f90-insert-end))
034a9d40 574 (define-key f90-mode-map [menu-bar f90 f90-join-lines]
658e4ee5 575 '("Join with Next Line" . f90-join-lines))
034a9d40 576 (define-key f90-mode-map [menu-bar f90 f90-break-line]
658e4ee5 577 '("Break Line at Point" . f90-break-line))
ec2f376f 578 (define-key f90-mode-map [menu-bar f90 line3] '("----"))
034a9d40
RS
579 (define-key f90-mode-map [menu-bar f90 f90-fill-region]
580 '("Fill Region" . f90-fill-region))
b974df0a 581 (put 'f90-fill-region 'menu-enable 'mark-active)
034a9d40
RS
582 (define-key f90-mode-map [menu-bar f90 indent-region]
583 '("Indent Region" . indent-region))
584 (define-key f90-mode-map [menu-bar f90 f90-comment-region]
585 '("(Un)Comment Region" . f90-comment-region))
b974df0a 586 (put 'f90-comment-region 'menu-enable 'mark-active)
ec2f376f 587 (define-key f90-mode-map [menu-bar f90 line4] '("----"))
034a9d40
RS
588 (define-key f90-mode-map [menu-bar f90 f90-end-of-subprogram]
589 '("End of Subprogram" . f90-end-of-subprogram))
590 (define-key f90-mode-map [menu-bar f90 f90-beginning-of-subprogram]
591 '("Beginning of Subprogram" . f90-beginning-of-subprogram))
592 (define-key f90-mode-map [menu-bar f90 f90-mark-subprogram]
593 '("Mark Subprogram" . f90-mark-subprogram))
594 (define-key f90-mode-map [menu-bar f90 f90-indent-subprogram]
b974df0a
EN
595 '("Indent Subprogram" . f90-indent-subprogram))
596 )
597
ee30478d 598;; Regexps for finding program structures.
69658465 599(defconst f90-blocks-re
ec2f376f
GM
600 (concat "\\(block[ \t]*data\\|"
601 (regexp-opt '("do" "if" "interface" "function" "module" "program"
602 "select" "subroutine" "type" "where" "forall"))
603 "\\)\\>")
604 "Regexp potentially indicating a \"block\" of F90 code.")
605
69658465 606(defconst f90-program-block-re
ec2f376f
GM
607 (regexp-opt '("program" "module" "subroutine" "function") 'paren)
608 "Regexp used to locate the start/end of a \"subprogram\".")
609
69658465 610(defconst f90-else-like-re
ec2f376f
GM
611 "\\(else\\([ \t]*if\\|where\\)?\\|case[ \t]*\\(default\\|(\\)\\)"
612 "Regexp matching an ELSE IF, ELSEWHERE, CASE statement.")
613
69658465 614(defconst f90-end-if-re
ec2f376f
GM
615 (concat "end[ \t]*"
616 (regexp-opt '("if" "select" "where" "forall") 'paren)
617 "\\>")
618 "Regexp matching the end of an IF, SELECT, WHERE, FORALL block.")
619
69658465 620(defconst f90-end-type-re
ec2f376f
GM
621 "end[ \t]*\\(type\\|interface\\|block[ \t]*data\\)\\>"
622 "Regexp matching the end of a TYPE, INTERFACE, BLOCK DATA section.")
623
ee30478d 624(defconst f90-type-def-re
ec2f376f
GM
625 "\\<\\(type\\)\\([^(\n]*\\)\\(::\\)?[ \t]*\\b\\(\\sw+\\)"
626 "Regexp matching the declaration of a variable of derived type.")
627
628(defconst f90-no-break-re
629 (regexp-opt '("**" "//" "=>") 'paren)
630 "Regexp specifying where not to break lines when filling.")
631
632(defvar f90-cache-position nil
633 "Temporary position used to speed up region operations.")
034a9d40 634(make-variable-buffer-local 'f90-cache-position)
ec2f376f
GM
635
636(defvar f90-imenu-flag nil
637 "Non-nil means this buffer already has an imenu.")
638(make-variable-buffer-local 'f90-imenu-flag)
ee30478d 639
b974df0a 640\f
ec2f376f 641;; Imenu support.
ee30478d 642(defvar f90-imenu-generic-expression
b974df0a
EN
643 (let ((good-char "[^!\"\&\n \t]") (not-e "[^e!\n\"\& \t]")
644 (not-n "[^n!\n\"\& \t]") (not-d "[^d!\n\"\& \t]"))
645 (list
646 '(nil "^[ \t0-9]*program[ \t]+\\(\\sw+\\)" 1)
647 '("Modules" "^[ \t0-9]*module[ \t]+\\(\\sw+\\)[ \t]*\\(!\\|$\\)" 1)
648 '("Types" "^[ \t0-9]*type[ \t]+\\(\\sw+\\)" 1)
649 (list
69658465 650 "Procedures"
b974df0a
EN
651 (concat
652 "^[ \t0-9]*"
653 "\\("
ec2f376f
GM
654 ;; At least three non-space characters before function/subroutine.
655 ;; Check that the last three non-space characters do not spell E N D.
b974df0a
EN
656 "[^!\"\&\n]*\\("
657 not-e good-char good-char "\\|"
658 good-char not-n good-char "\\|"
659 good-char good-char not-d "\\)"
660 "\\|"
ec2f376f 661 ;; Less than three non-space characters before function/subroutine.
b974df0a
EN
662 good-char "?" good-char "?"
663 "\\)"
664 "[ \t]*\\(function\\|subroutine\\)[ \t]+\\(\\sw+\\)")
69658465 665 4)))
87ee2359 666 "Generic imenu expression for F90 mode.")
ee30478d 667
b974df0a 668(defun f90-add-imenu-menu ()
b974df0a 669 "Add an imenu menu to the menubar."
87ee2359 670 (interactive)
ec2f376f 671 (if f90-imenu-flag
5c2a80ad
GM
672 (message "%s" "F90-imenu already exists.")
673 (imenu-add-to-menubar "F90-imenu")
674 (redraw-frame (selected-frame))
ec2f376f 675 (setq f90-imenu-flag t)))
5c2a80ad 676
ec2f376f 677(put 'f90-add-imenu-menu 'menu-enable '(not f90-imenu-flag))
b974df0a
EN
678
679
ec2f376f
GM
680;; When compiling under GNU Emacs, load imenu during compilation.
681;; If you have 19.22 or earlier, comment this out, or get imenu.
1bb3ae5c
GM
682(or f90-xemacs-flag (eval-when-compile (require 'imenu)))
683
034a9d40 684\f
ec2f376f 685;; Abbrevs have generally two letters, except standard types `c, `i, `r, `t.
034a9d40 686(defvar f90-mode-abbrev-table nil)
d14e6bbe 687(unless f90-mode-abbrev-table
034a9d40
RS
688 (let ((ac abbrevs-changed))
689 (define-abbrev-table 'f90-mode-abbrev-table ())
e3f5ce56
GM
690 (define-abbrev f90-mode-abbrev-table "`al" "allocate" nil 0 t)
691 (define-abbrev f90-mode-abbrev-table "`ab" "allocatable" nil 0 t)
692 (define-abbrev f90-mode-abbrev-table "`as" "assignment" nil 0 t)
693 (define-abbrev f90-mode-abbrev-table "`ba" "backspace" nil 0 t)
694 (define-abbrev f90-mode-abbrev-table "`bd" "block data" nil 0 t)
695 (define-abbrev f90-mode-abbrev-table "`c" "character" nil 0 t)
696 (define-abbrev f90-mode-abbrev-table "`cl" "close" nil 0 t)
697 (define-abbrev f90-mode-abbrev-table "`cm" "common" nil 0 t)
698 (define-abbrev f90-mode-abbrev-table "`cx" "complex" nil 0 t)
699 (define-abbrev f90-mode-abbrev-table "`cn" "contains" nil 0 t)
700 (define-abbrev f90-mode-abbrev-table "`cy" "cycle" nil 0 t)
701 (define-abbrev f90-mode-abbrev-table "`de" "deallocate" nil 0 t)
702 (define-abbrev f90-mode-abbrev-table "`df" "define" nil 0 t)
703 (define-abbrev f90-mode-abbrev-table "`di" "dimension" nil 0 t)
704 (define-abbrev f90-mode-abbrev-table "`dw" "do while" nil 0 t)
705 (define-abbrev f90-mode-abbrev-table "`el" "else" nil 0 t)
706 (define-abbrev f90-mode-abbrev-table "`eli" "else if" nil 0 t)
707 (define-abbrev f90-mode-abbrev-table "`elw" "elsewhere" nil 0 t)
708 (define-abbrev f90-mode-abbrev-table "`eq" "equivalence" nil 0 t)
709 (define-abbrev f90-mode-abbrev-table "`ex" "external" nil 0 t)
710 (define-abbrev f90-mode-abbrev-table "`ey" "entry" nil 0 t)
711 (define-abbrev f90-mode-abbrev-table "`fl" "forall" nil 0 t)
712 (define-abbrev f90-mode-abbrev-table "`fo" "format" nil 0 t)
713 (define-abbrev f90-mode-abbrev-table "`fu" "function" nil 0 t)
714 (define-abbrev f90-mode-abbrev-table "`fa" ".false." nil 0 t)
5cc39d76 715 (define-abbrev f90-mode-abbrev-table "`im" "implicit none" nil 0 t)
e3f5ce56
GM
716 (define-abbrev f90-mode-abbrev-table "`in " "include" nil 0 t)
717 (define-abbrev f90-mode-abbrev-table "`i" "integer" nil 0 t)
718 (define-abbrev f90-mode-abbrev-table "`it" "intent" nil 0 t)
719 (define-abbrev f90-mode-abbrev-table "`if" "interface" nil 0 t)
720 (define-abbrev f90-mode-abbrev-table "`lo" "logical" nil 0 t)
721 (define-abbrev f90-mode-abbrev-table "`mo" "module" nil 0 t)
722 (define-abbrev f90-mode-abbrev-table "`na" "namelist" nil 0 t)
723 (define-abbrev f90-mode-abbrev-table "`nu" "nullify" nil 0 t)
724 (define-abbrev f90-mode-abbrev-table "`op" "optional" nil 0 t)
725 (define-abbrev f90-mode-abbrev-table "`pa" "parameter" nil 0 t)
726 (define-abbrev f90-mode-abbrev-table "`po" "pointer" nil 0 t)
727 (define-abbrev f90-mode-abbrev-table "`pr" "print" nil 0 t)
728 (define-abbrev f90-mode-abbrev-table "`pi" "private" nil 0 t)
729 (define-abbrev f90-mode-abbrev-table "`pm" "program" nil 0 t)
730 (define-abbrev f90-mode-abbrev-table "`pu" "public" nil 0 t)
731 (define-abbrev f90-mode-abbrev-table "`r" "real" nil 0 t)
732 (define-abbrev f90-mode-abbrev-table "`rc" "recursive" nil 0 t)
733 (define-abbrev f90-mode-abbrev-table "`rt" "return" nil 0 t)
734 (define-abbrev f90-mode-abbrev-table "`rw" "rewind" nil 0 t)
735 (define-abbrev f90-mode-abbrev-table "`se" "select" nil 0 t)
736 (define-abbrev f90-mode-abbrev-table "`sq" "sequence" nil 0 t)
737 (define-abbrev f90-mode-abbrev-table "`su" "subroutine" nil 0 t)
738 (define-abbrev f90-mode-abbrev-table "`ta" "target" nil 0 t)
739 (define-abbrev f90-mode-abbrev-table "`tr" ".true." nil 0 t)
740 (define-abbrev f90-mode-abbrev-table "`t" "type" nil 0 t)
741 (define-abbrev f90-mode-abbrev-table "`wh" "where" nil 0 t)
742 (define-abbrev f90-mode-abbrev-table "`wr" "write" nil 0 t)
034a9d40
RS
743 (setq abbrevs-changed ac)))
744\f
d2d15846 745(defcustom f90-mode-hook nil
ec2f376f 746 "Hook run when entering F90 mode."
d2d15846
DL
747 :type 'hook
748 :options '(f90-add-imenu-menu)
749 :group 'f90)
750
034a9d40
RS
751;;;###autoload
752(defun f90-mode ()
87ee2359 753 "Major mode for editing Fortran 90,95 code in free format.
034a9d40 754
ec2f376f 755\\[f90-indent-new-line] indents current line and creates a new\
034a9d40 756 indented line.
ec2f376f 757\\[f90-indent-line] indents the current line.
87ee2359 758\\[f90-indent-subprogram] indents the current subprogram.
034a9d40
RS
759
760Type `? or `\\[help-command] to display a list of built-in\
761 abbrevs for F90 keywords.
762
763Key definitions:
764\\{f90-mode-map}
765
766Variables controlling indentation style and extra features:
767
ec2f376f
GM
768`f90-do-indent'
769 Extra indentation within do blocks (default 3).
770`f90-if-indent'
771 Extra indentation within if/select case/where/forall blocks (default 3).
772`f90-type-indent'
773 Extra indentation within type/interface/block-data blocks (default 3).
774`f90-program-indent'
775 Extra indentation within program/module/subroutine/function blocks
776 (default 2).
777`f90-continuation-indent'
778 Extra indentation applied to continuation lines (default 5).
779`f90-comment-region'
e3f5ce56
GM
780 String inserted by function \\[f90-comment-region] at start of each
781 line in region (default \"!!!$\").
ec2f376f
GM
782`f90-indented-comment-re'
783 Regexp determining the type of comment to be intended like code
784 (default \"!\").
785`f90-directive-comment-re'
786 Regexp of comment-like directive like \"!HPF\\\\$\", not to be indented
787 (default \"!hpf\\\\$\").
788`f90-break-delimiters'
789 Regexp holding list of delimiters at which lines may be broken
790 (default \"[-+*/><=,% \\t]\").
791`f90-break-before-delimiters'
792 Non-nil causes `f90-do-auto-fill' to break lines before delimiters
793 (default t).
794`f90-beginning-ampersand'
795 Automatic insertion of \& at beginning of continuation lines (default t).
796`f90-smart-end'
797 From an END statement, check and fill the end using matching block start.
798 Allowed values are 'blink, 'no-blink, and nil, which determine
799 whether to blink the matching beginning (default 'blink).
800`f90-auto-keyword-case'
801 Automatic change of case of keywords (default nil).
802 The possibilities are 'downcase-word, 'upcase-word, 'capitalize-word.
803`f90-leave-line-no'
804 Do not left-justify line numbers (default nil).
805`f90-keywords-re'
806 List of keywords used for highlighting/upcase-keywords etc.
034a9d40
RS
807
808Turning on F90 mode calls the value of the variable `f90-mode-hook'
809with no args, if that value is non-nil."
810 (interactive)
811 (kill-all-local-variables)
e3f5ce56
GM
812 (setq major-mode 'f90-mode
813 mode-name "F90"
814 local-abbrev-table f90-mode-abbrev-table)
034a9d40
RS
815 (set-syntax-table f90-mode-syntax-table)
816 (use-local-map f90-mode-map)
e3f5ce56
GM
817 (set (make-local-variable 'indent-line-function) 'f90-indent-line)
818 (set (make-local-variable 'indent-region-function) 'f90-indent-region)
819 (set (make-local-variable 'require-final-newline) t)
820 (set (make-local-variable 'comment-start) "!")
821 (set (make-local-variable 'comment-start-skip) "!+ *")
822 (set (make-local-variable 'comment-indent-function) 'f90-comment-indent)
823 (set (make-local-variable 'abbrev-all-caps) t)
824 (set (make-local-variable 'normal-auto-fill-function) 'f90-do-auto-fill)
72e80cad 825 (setq indent-tabs-mode nil)
ec2f376f 826 ;; Setting up things for font-lock.
1bb3ae5c
GM
827 (when f90-xemacs-flag
828 (put 'f90-mode 'font-lock-keywords-case-fold-search t)
829 (when (and (featurep 'menubar)
69658465
GM
830 current-menubar
831 (not (assoc "F90" current-menubar)))
832 (set-buffer-menubar (copy-sequence current-menubar))
833 (add-submenu nil f90-xemacs-menu)))
ec2f376f 834 ;; XEmacs: Does not need a special case, since both emacsen work alike -sb.
e3f5ce56
GM
835 (set (make-local-variable 'font-lock-defaults)
836 '((f90-font-lock-keywords f90-font-lock-keywords-1
837 f90-font-lock-keywords-2
838 f90-font-lock-keywords-3
839 f90-font-lock-keywords-4)
840 nil t))
45d1e4d4
DL
841 ;; Tell imenu how to handle f90.
842 (set (make-local-variable 'imenu-case-fold-search) t)
e3f5ce56
GM
843 (set (make-local-variable 'imenu-generic-expression)
844 f90-imenu-generic-expression)
d2d15846
DL
845 (set (make-local-variable 'add-log-current-defun-function)
846 #'f90-current-defun)
48548fd5 847 (run-hooks 'f90-mode-hook))
ec2f376f 848
034a9d40 849\f
ec2f376f 850;; Inline-functions.
034a9d40 851(defsubst f90-in-string ()
d14e6bbe 852 "Return non-nil if point is inside a string.
ec2f376f 853Checks from `point-min', or `f90-cache-position', if that is non-nil
d14e6bbe 854and lies before point."
034a9d40
RS
855 (let ((beg-pnt
856 (if (and f90-cache-position (> (point) f90-cache-position))
857 f90-cache-position
858 (point-min))))
859 (nth 3 (parse-partial-sexp beg-pnt (point)))))
69658465 860
034a9d40 861(defsubst f90-in-comment ()
d14e6bbe 862 "Return non-nil if point is inside a comment.
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 4 (parse-partial-sexp beg-pnt (point)))))
870
871(defsubst f90-line-continued ()
d14e6bbe
GM
872 "Return t if the current line is a continued one.
873This includes comment lines embedded in continued lines, but
874not the last line of a continued statement."
034a9d40 875 (save-excursion
6734e165
GM
876 (beginning-of-line)
877 (while (and (looking-at "[ \t]*\\(!\\|$\\)") (zerop (forward-line -1))))
e3f5ce56
GM
878 (end-of-line)
879 (while (f90-in-comment)
880 (search-backward "!" (line-beginning-position))
881 (skip-chars-backward "!"))
882 (skip-chars-backward " \t")
883 (= (preceding-char) ?&)))
034a9d40
RS
884
885(defsubst f90-current-indentation ()
886 "Return indentation of current line.
887Line-numbers are considered whitespace characters."
e3f5ce56 888 (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")))
034a9d40
RS
889
890(defsubst f90-indent-to (col &optional no-line-number)
891 "Indent current line to column COL.
d14e6bbe
GM
892If optional argument NO-LINE-NUMBER is nil, jump over a possible
893line-number before indenting."
034a9d40
RS
894 (beginning-of-line)
895 (if (not no-line-number)
896 (skip-chars-forward " \t0-9"))
897 (delete-horizontal-space)
898 (if (zerop (current-column))
899 (indent-to col)
d14e6bbe 900 (indent-to col 1))) ; leave >= 1 space after line number
034a9d40 901
034a9d40 902(defsubst f90-get-present-comment-type ()
d14e6bbe
GM
903 "If point lies within a comment, return the string starting the comment.
904For example, \"!\" or \"!!\"."
034a9d40 905 (save-excursion
e3f5ce56
GM
906 (when (f90-in-comment)
907 (beginning-of-line)
908 (re-search-forward "[!]+" (line-end-position))
909 (while (f90-in-string)
910 (re-search-forward "[!]+" (line-end-position))
911 (match-string 0)))))
034a9d40
RS
912
913(defsubst f90-equal-symbols (a b)
ec2f376f 914 "Compare strings A and B neglecting case and allowing for nil value."
034a9d40
RS
915 (let ((a-local (if a (downcase a) nil))
916 (b-local (if b (downcase b) nil)))
917 (equal a-local b-local)))
918
ec2f376f
GM
919;; XEmacs 19.11 & 19.12 return a single char when matching an empty regexp.
920;; The next 2 functions are therefore longer than necessary.
034a9d40 921(defsubst f90-looking-at-do ()
d14e6bbe
GM
922 "Return (\"do\" NAME) if a do statement starts after point.
923NAME is nil if the statement has no label."
ee30478d
KH
924 (if (looking-at "\\(\\(\\sw+\\)[ \t]*\:\\)?[ \t]*\\(do\\)\\>")
925 (let (label
6734e165 926 (struct (match-string 3)))
ee30478d 927 (if (looking-at "\\(\\sw+\\)[ \t]*\:")
6734e165 928 (setq label (match-string 1)))
ee30478d
KH
929 (list struct label))))
930
931(defsubst f90-looking-at-select-case ()
d14e6bbe
GM
932 "Return (\"select\" NAME) if a select-case statement starts after point.
933NAME is nil if the statement has no label."
934 (if (looking-at "\\(\\(\\sw+\\)[ \t]*\:\\)?[ \t]*\
935\\(select\\)[ \t]*case[ \t]*(")
ee30478d 936 (let (label
6734e165 937 (struct (match-string 3)))
ee30478d 938 (if (looking-at "\\(\\sw+\\)[ \t]*\:")
6734e165 939 (setq label (match-string 1)))
ee30478d 940 (list struct label))))
034a9d40
RS
941
942(defsubst f90-looking-at-if-then ()
d14e6bbe
GM
943 "Return (\"if\" NAME) if an if () then statement starts after point.
944NAME is nil if the statement has no label."
034a9d40 945 (save-excursion
e3f5ce56
GM
946 (when (looking-at "\\(\\(\\sw+\\)[ \t]*\:\\)?[ \t]*\\(if\\)\\>")
947 (let (label
948 (struct (match-string 3)))
5c2a80ad
GM
949 (if (looking-at "\\(\\sw+\\)[ \t]*\:")
950 (setq label (match-string 1)))
951 (let ((pos (scan-lists (point) 1 0)))
952 (and pos (goto-char pos)))
953 (skip-chars-forward " \t")
954 (if (or (looking-at "then\\>")
955 (when (f90-line-continued)
956 (f90-next-statement)
957 (skip-chars-forward " \t0-9&")
958 (looking-at "then\\>")))
959 (list struct label))))))
034a9d40 960
034a9d40 961(defsubst f90-looking-at-where-or-forall ()
d14e6bbe
GM
962 "Return (KIND NAME) if a where or forall block starts after point.
963NAME is nil if the statement has no label."
964 (if (looking-at "\\(\\(\\sw+\\)[ \t]*\:\\)?[ \t]*\
965\\(where\\|forall\\)[ \t]*(.*)[ \t]*\\(!\\|$\\)")
7cae52cf 966 (let (label
6734e165 967 (struct (match-string 3)))
7cae52cf 968 (if (looking-at "\\(\\sw+\\)[ \t]*\:")
6734e165 969 (setq label (match-string 1)))
7cae52cf 970 (list struct label))))
034a9d40
RS
971
972(defsubst f90-looking-at-type-like ()
d14e6bbe
GM
973 "Return (KIND NAME) if a type/interface/block-data block starts after point.
974NAME is non-nil only for type."
69658465 975 (cond
ee30478d 976 ((looking-at f90-type-def-re)
6734e165 977 (list (match-string 1) (match-string 4)))
ee30478d 978 ((looking-at "\\(interface\\|block[\t]*data\\)\\>")
6734e165 979 (list (match-string 1) nil))))
034a9d40
RS
980
981(defsubst f90-looking-at-program-block-start ()
d14e6bbe 982 "Return (KIND NAME) if a program block with name NAME starts after point."
034a9d40 983 (cond
ee30478d 984 ((looking-at "\\(program\\)[ \t]+\\(\\sw+\\)\\>")
6734e165 985 (list (match-string 1) (match-string 2)))
034a9d40 986 ((and (not (looking-at "module[ \t]*procedure\\>"))
ee30478d 987 (looking-at "\\(module\\)[ \t]+\\(\\sw+\\)\\>"))
6734e165 988 (list (match-string 1) (match-string 2)))
b974df0a 989 ((and (not (looking-at "end[ \t]*\\(function\\|subroutine\\)"))
d14e6bbe
GM
990 (looking-at "[^!'\"\&\n]*\\(function\\|subroutine\\)\
991[ \t]+\\(\\sw+\\)"))
6734e165 992 (list (match-string 1) (match-string 2)))))
034a9d40
RS
993
994(defsubst f90-looking-at-program-block-end ()
d14e6bbe 995 "Return (KIND NAME) if a block with name NAME ends after point."
69658465 996 (if (looking-at (concat "end[ \t]*" f90-blocks-re
ee30478d 997 "?\\([ \t]+\\(\\sw+\\)\\)?\\>"))
6734e165 998 (list (match-string 1) (match-string 3))))
034a9d40
RS
999
1000(defsubst f90-comment-indent ()
ec2f376f
GM
1001 "Return the indentation to be used for a comment starting at point.
1002Used for `comment-indent-function' by F90 mode.
1003\"!!!\", `f90-directive-comment-re', variable `f90-comment-region' return 0.
1004`f90-indented-comment-re' (if not trailing code) calls `f90-calculate-indent'.
1005Any other type return `comment-column', leaving at least one space after code."
034a9d40 1006 (cond ((looking-at "!!!") 0)
ee30478d
KH
1007 ((and f90-directive-comment-re
1008 (looking-at f90-directive-comment-re)) 0)
034a9d40 1009 ((looking-at (regexp-quote f90-comment-region)) 0)
2c0b59e3
DL
1010 ((and (looking-at f90-indented-comment-re)
1011 ;; Don't attempt to indent trailing comment as code.
1012 (save-excursion
1013 (skip-chars-backward " \t")
1014 (bolp)))
034a9d40
RS
1015 (f90-calculate-indent))
1016 (t (skip-chars-backward " \t")
1017 (max (if (bolp) 0 (1+ (current-column))) comment-column))))
1018
1019(defsubst f90-present-statement-cont ()
d14e6bbe
GM
1020 "Return continuation properties of present statement.
1021Possible return values are:
1022single - statement is not continued.
1023begin - current line is the first in a continued statement.
1024end - current line is the last in a continued statement
1025middle - current line is neither first nor last in a continued statement.
1026Comment lines embedded amongst continued lines return 'middle."
034a9d40
RS
1027 (let (pcont cont)
1028 (save-excursion
e3f5ce56 1029 (setq pcont (if (f90-previous-statement) (f90-line-continued))))
034a9d40
RS
1030 (setq cont (f90-line-continued))
1031 (cond ((and (not pcont) (not cont)) 'single)
1032 ((and (not pcont) cont) 'begin)
1033 ((and pcont (not cont)) 'end)
1034 ((and pcont cont) 'middle)
e3f5ce56 1035 (t (error "The impossible occurred")))))
034a9d40
RS
1036
1037(defsubst f90-indent-line-no ()
d14e6bbe
GM
1038 "If `f90-leave-line-no' is nil, left-justify a line number.
1039Leaves point at the first non-blank character after the line number.
1040Call from beginning of line."
1041 (if (and (null f90-leave-line-no) (looking-at "[ \t]+[0-9]"))
1042 (delete-horizontal-space))
034a9d40
RS
1043 (skip-chars-forward " \t0-9"))
1044
1045(defsubst f90-no-block-limit ()
d14e6bbe
GM
1046 "Return nil if point is at the edge of a code block.
1047Searches line forward for \"function\" or \"subroutine\",
1048if all else fails."
6734e165 1049 (let ((eol (line-end-position)))
034a9d40
RS
1050 (save-excursion
1051 (not (or (looking-at "end")
7cae52cf
RS
1052 (looking-at "\\(do\\|if\\|else\\(if\\|where\\)?\
1053\\|select[ \t]*case\\|case\\|where\\|forall\\)\\>")
034a9d40
RS
1054 (looking-at "\\(program\\|module\\|interface\\|\
1055block[ \t]*data\\)\\>")
ee30478d
KH
1056 (looking-at "\\(contains\\|\\sw+[ \t]*:\\)")
1057 (looking-at f90-type-def-re)
034a9d40
RS
1058 (re-search-forward "\\(function\\|subroutine\\)" eol t))))))
1059
1060(defsubst f90-update-line ()
d14e6bbe
GM
1061 "Change case of current line as per `f90-auto-keyword-case'."
1062 (if f90-auto-keyword-case
1063 (f90-change-keywords f90-auto-keyword-case
1064 (line-beginning-position) (line-end-position))))
034a9d40 1065\f
7cae52cf 1066(defun f90-electric-insert ()
d14e6bbe 1067 "Change keyword case and auto-fill line as operators are inserted."
7cae52cf 1068 (interactive)
7cae52cf 1069 (self-insert-command 1)
d14e6bbe
GM
1070 (if auto-fill-function (f90-do-auto-fill) ; also updates line
1071 (f90-update-line)))
1072
7cae52cf 1073
034a9d40
RS
1074(defun f90-get-correct-indent ()
1075 "Get correct indent for a line starting with line number.
1076Does not check type and subprogram indentation."
6734e165 1077 (let ((epnt (line-end-position)) icol cont)
034a9d40
RS
1078 (save-excursion
1079 (while (and (f90-previous-statement)
1080 (or (progn
1081 (setq cont (f90-present-statement-cont))
1082 (or (eq cont 'end) (eq cont 'middle)))
1083 (looking-at "[ \t]*[0-9]"))))
1084 (setq icol (current-indentation))
1085 (beginning-of-line)
5c2a80ad
GM
1086 (when (re-search-forward "\\(if\\|do\\|select\\|where\\|forall\\)"
1087 (line-end-position) t)
e3f5ce56
GM
1088 (beginning-of-line)
1089 (skip-chars-forward " \t")
5c2a80ad
GM
1090 (cond ((f90-looking-at-do)
1091 (setq icol (+ icol f90-do-indent)))
1092 ((or (f90-looking-at-if-then)
1093 (f90-looking-at-where-or-forall)
1094 (f90-looking-at-select-case))
1095 (setq icol (+ icol f90-if-indent))))
1096 (end-of-line))
034a9d40 1097 (while (re-search-forward
ee30478d 1098 "\\(if\\|do\\|select\\|where\\|forall\\)" epnt t)
e3f5ce56
GM
1099 (beginning-of-line)
1100 (skip-chars-forward " \t0-9")
1101 (cond ((f90-looking-at-do)
1102 (setq icol (+ icol f90-do-indent)))
1103 ((or (f90-looking-at-if-then)
1104 (f90-looking-at-where-or-forall)
1105 (f90-looking-at-select-case))
1106 (setq icol (+ icol f90-if-indent)))
1107 ((looking-at f90-end-if-re)
1108 (setq icol (- icol f90-if-indent)))
1109 ((looking-at "end[ \t]*do\\>")
1110 (setq icol (- icol f90-do-indent))))
034a9d40
RS
1111 (end-of-line))
1112 icol)))
69658465 1113
034a9d40
RS
1114(defun f90-calculate-indent ()
1115 "Calculate the indent column based on previous statements."
1116 (interactive)
1117 (let (icol cont (case-fold-search t) (pnt (point)))
1118 (save-excursion
1119 (if (not (f90-previous-statement))
1120 (setq icol 0)
1121 (setq cont (f90-present-statement-cont))
1122 (if (eq cont 'end)
1123 (while (not (eq 'begin (f90-present-statement-cont)))
1124 (f90-previous-statement)))
1125 (cond ((eq cont 'begin)
1126 (setq icol (+ (f90-current-indentation)
1127 f90-continuation-indent)))
e3f5ce56 1128 ((eq cont 'middle) (setq icol (current-indentation)))
034a9d40
RS
1129 (t (setq icol (f90-current-indentation))
1130 (skip-chars-forward " \t")
1131 (if (looking-at "[0-9]")
1132 (setq icol (f90-get-correct-indent))
1133 (cond ((or (f90-looking-at-if-then)
1134 (f90-looking-at-where-or-forall)
1135 (f90-looking-at-select-case)
69658465 1136 (looking-at f90-else-like-re))
034a9d40
RS
1137 (setq icol (+ icol f90-if-indent)))
1138 ((f90-looking-at-do)
1139 (setq icol (+ icol f90-do-indent)))
1140 ((f90-looking-at-type-like)
1141 (setq icol (+ icol f90-type-indent)))
1142 ((or (f90-looking-at-program-block-start)
1143 (looking-at "contains[ \t]*\\($\\|!\\)"))
1144 (setq icol (+ icol f90-program-indent)))))
1145 (goto-char pnt)
1146 (beginning-of-line)
1147 (cond ((looking-at "[ \t]*$"))
ec2f376f 1148 ((looking-at "[ \t]*#") ; check for cpp directive
034a9d40
RS
1149 (setq icol 0))
1150 (t
1151 (skip-chars-forward " \t0-9")
1152 (cond ((or (looking-at f90-else-like-re)
1153 (looking-at f90-end-if-re))
1154 (setq icol (- icol f90-if-indent)))
ee30478d 1155 ((looking-at "end[ \t]*do\\>")
034a9d40
RS
1156 (setq icol (- icol f90-do-indent)))
1157 ((looking-at f90-end-type-re)
1158 (setq icol (- icol f90-type-indent)))
1159 ((or (looking-at "contains[ \t]*\\(!\\|$\\)")
1160 (f90-looking-at-program-block-end))
1161 (setq icol (- icol f90-program-indent))))))
1162 ))))
1163 icol))
1164\f
034a9d40
RS
1165(defun f90-previous-statement ()
1166 "Move point to beginning of the previous F90 statement.
ec2f376f
GM
1167Return nil if no previous statement is found.
1168A statement is a line which is neither blank nor a comment."
034a9d40
RS
1169 (interactive)
1170 (let (not-first-statement)
1171 (beginning-of-line)
1172 (while (and (setq not-first-statement (zerop (forward-line -1)))
ee30478d 1173 (looking-at "[ \t0-9]*\\(!\\|$\\|#\\)")))
034a9d40
RS
1174 not-first-statement))
1175
1176(defun f90-next-statement ()
1177 "Move point to beginning of the next F90 statement.
1178Return nil if no later statement is found."
1179 (interactive)
1180 (let (not-last-statement)
1181 (beginning-of-line)
1182 (while (and (setq not-last-statement
1183 (and (zerop (forward-line 1))
1184 (not (eobp))))
1185 (looking-at "[ \t0-9]*\\(!\\|$\\)")))
1186 not-last-statement))
1187
1188(defun f90-beginning-of-subprogram ()
1189 "Move point to the beginning of subprogram.
ec2f376f 1190Return (TYPE NAME), or nil if not found."
034a9d40
RS
1191 (interactive)
1192 (let ((count 1) (case-fold-search t) matching-beg)
e3f5ce56
GM
1193 (beginning-of-line)
1194 (skip-chars-forward " \t0-9")
69658465 1195 (if (setq matching-beg (f90-looking-at-program-block-start))
e3f5ce56 1196 (setq count (1- count)))
034a9d40
RS
1197 (while (and (not (zerop count))
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 ()
1212 "Move point to the end of subprogram.
ec2f376f 1213Return (TYPE NAME), or nil if not found."
034a9d40
RS
1214 (interactive)
1215 (let ((count 1) (case-fold-search t) matching-end)
e3f5ce56
GM
1216 (beginning-of-line)
1217 (skip-chars-forward " \t0-9")
034a9d40
RS
1218 (if (setq matching-end (f90-looking-at-program-block-end))
1219 (setq count (1- count)))
1220 (end-of-line)
1221 (while (and (not (zerop count))
1222 (re-search-forward f90-program-block-re nil 'move))
e3f5ce56
GM
1223 (beginning-of-line)
1224 (skip-chars-forward " \t0-9")
034a9d40 1225 (cond ((f90-looking-at-program-block-start)
e3f5ce56 1226 (setq count (1+ count)))
034a9d40 1227 ((setq matching-end (f90-looking-at-program-block-end))
e3f5ce56 1228 (setq count (1- count))))
034a9d40
RS
1229 (end-of-line))
1230 (forward-line 1)
1231 (if (zerop count)
1232 matching-end
1233 (message "No end found.")
1234 nil)))
1235
82b4fc4a
GM
1236(defvar f90-mark-subprogram-overlay nil
1237 "Used internally by `f90-mark-subprogram' to highlight the subprogram.")
1238(make-variable-buffer-local 'f90-mark-subprogram-overlay)
1239
034a9d40 1240(defun f90-mark-subprogram ()
82b4fc4a
GM
1241 "Put mark at end of F90 subprogram, point at beginning, push marks.
1242If called interactively, highlight the subprogram with the face `highlight'.
1243Call again to remove the highlighting."
034a9d40
RS
1244 (interactive)
1245 (let ((pos (point)) program)
1246 (f90-end-of-subprogram)
1247 (push-mark (point) t)
1248 (goto-char pos)
1249 (setq program (f90-beginning-of-subprogram))
1250 ;; The keywords in the preceding lists assume case-insensitivity.
1bb3ae5c 1251 (if f90-xemacs-flag
034a9d40 1252 (zmacs-activate-region)
1bb3ae5c 1253 (setq mark-active t
82b4fc4a
GM
1254 deactivate-mark nil)
1255 (if (interactive-p)
1256 (if (overlayp f90-mark-subprogram-overlay)
1257 (if (overlay-buffer f90-mark-subprogram-overlay)
1258 (delete-overlay f90-mark-subprogram-overlay)
1259 (move-overlay f90-mark-subprogram-overlay (point) (mark)))
1260 (setq f90-mark-subprogram-overlay (make-overlay (point) (mark)))
1261 (overlay-put f90-mark-subprogram-overlay 'face 'highlight))))
034a9d40
RS
1262 program))
1263
1264(defun f90-comment-region (beg-region end-region)
1265 "Comment/uncomment every line in the region.
d14e6bbe
GM
1266Insert the variable `f90-comment-region' at the start of every line
1267in the region, or, if already present, remove it."
034a9d40
RS
1268 (interactive "*r")
1269 (let ((end (make-marker)))
1270 (set-marker end end-region)
1271 (goto-char beg-region)
1272 (beginning-of-line)
1273 (if (looking-at (regexp-quote f90-comment-region))
1274 (delete-region (point) (match-end 0))
1275 (insert f90-comment-region))
e3f5ce56
GM
1276 (while (and (zerop (forward-line 1))
1277 (< (point) (marker-position end)))
034a9d40
RS
1278 (if (looking-at (regexp-quote f90-comment-region))
1279 (delete-region (point) (match-end 0))
1280 (insert f90-comment-region)))
1281 (set-marker end nil)))
1282
1283(defun f90-indent-line (&optional no-update)
87ee2359
GM
1284 "Indent current line as F90 code.
1285Unless optional argument NO-UPDATE is non-nil, call `f90-update-line'
1286after indenting."
034a9d40 1287 (interactive)
e3f5ce56 1288 (let (indent no-line-number (pos (make-marker)) (case-fold-search t))
034a9d40 1289 (set-marker pos (point))
ec2f376f 1290 (beginning-of-line) ; digits after & \n are not line-nos
034a9d40
RS
1291 (if (save-excursion (and (f90-previous-statement) (f90-line-continued)))
1292 (progn (setq no-line-number t) (skip-chars-forward " \t"))
1293 (f90-indent-line-no))
1294 (if (looking-at "!")
1295 (setq indent (f90-comment-indent))
ee30478d 1296 (if (and (looking-at "end") f90-smart-end)
69658465 1297 (f90-match-end))
034a9d40 1298 (setq indent (f90-calculate-indent)))
e3f5ce56
GM
1299 (if (not (zerop (- indent (current-column))))
1300 (f90-indent-to indent no-line-number))
034a9d40
RS
1301 ;; If initial point was within line's indentation,
1302 ;; position after the indentation. Else stay at same point in text.
1303 (if (< (point) (marker-position pos))
1304 (goto-char (marker-position pos)))
69658465 1305 (if auto-fill-function
d14e6bbe
GM
1306 (f90-do-auto-fill) ; also updates line
1307 (if (not no-update) (f90-update-line)))
034a9d40
RS
1308 (set-marker pos nil)))
1309
1310(defun f90-indent-new-line ()
87ee2359
GM
1311 "Reindent current line, insert a newline and indent the newline.
1312An abbrev before point is expanded if the variable `abbrev-mode' is non-nil.
034a9d40
RS
1313If run in the middle of a line, the line is not broken."
1314 (interactive)
1315 (let (string cont (case-fold-search t))
1316 (if abbrev-mode (expand-abbrev))
ec2f376f 1317 (beginning-of-line) ; reindent where likely to be needed
034a9d40 1318 (f90-indent-line-no)
ec2f376f 1319 (f90-indent-line 'no-update)
034a9d40 1320 (end-of-line)
ec2f376f 1321 (delete-horizontal-space) ; destroy trailing whitespace
e3f5ce56
GM
1322 (setq string (f90-in-string)
1323 cont (f90-line-continued))
034a9d40
RS
1324 (if (and string (not cont)) (insert "&"))
1325 (f90-update-line)
1326 (newline)
1327 (if (or string (and cont f90-beginning-ampersand)) (insert "&"))
1328 (f90-indent-line 'no-update)))
1329
1330
1331(defun f90-indent-region (beg-region end-region)
1332 "Indent every line in region by forward parsing."
1333 (interactive "*r")
e3f5ce56
GM
1334 (let ((end-region-mark (make-marker))
1335 (save-point (point-marker))
1336 block-list ind-lev ind-curr ind-b cont
034a9d40
RS
1337 struct beg-struct end-struct)
1338 (set-marker end-region-mark end-region)
1339 (goto-char beg-region)
ec2f376f 1340 ;; First find a line which is not a continuation line or comment.
034a9d40 1341 (beginning-of-line)
ee30478d 1342 (while (and (looking-at "[ \t]*[0-9]*\\(!\\|#\\|[ \t]*$\\)")
034a9d40
RS
1343 (progn (f90-indent-line 'no-update)
1344 (zerop (forward-line 1)))
1345 (< (point) end-region-mark)))
1346 (setq cont (f90-present-statement-cont))
1347 (while (and (or (eq cont 'middle) (eq cont 'end))
1348 (f90-previous-statement))
1349 (setq cont (f90-present-statement-cont)))
ec2f376f 1350 ;; Process present line for beginning of block.
034a9d40
RS
1351 (setq f90-cache-position (point))
1352 (f90-indent-line 'no-update)
e3f5ce56
GM
1353 (setq ind-lev (f90-current-indentation)
1354 ind-curr ind-lev)
1355 (beginning-of-line)
1356 (skip-chars-forward " \t0-9")
1357 (setq struct nil
1358 ind-b (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
034a9d40
RS
1359 ((or (setq struct (f90-looking-at-if-then))
1360 (setq struct (f90-looking-at-select-case))
1361 (setq struct (f90-looking-at-where-or-forall))
1362 (looking-at f90-else-like-re))
1363 f90-if-indent)
1364 ((setq struct (f90-looking-at-type-like))
1365 f90-type-indent)
1366 ((or(setq struct (f90-looking-at-program-block-start))
1367 (looking-at "contains[ \t]*\\($\\|!\\)"))
1368 f90-program-indent)))
1369 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1370 (if struct (setq block-list (cons struct block-list)))
1371 (while (and (f90-line-continued) (zerop (forward-line 1))
1372 (< (point) end-region-mark))
ec2f376f
GM
1373 (if (looking-at "[ \t]*!")
1374 (f90-indent-to (f90-comment-indent))
1375 (if (not (zerop (- (current-indentation)
1376 (+ ind-curr f90-continuation-indent))))
1377 (f90-indent-to (+ ind-curr f90-continuation-indent) 'no-line-no))))
1378 ;; Process all following lines.
d14e6bbe 1379 (while (and (zerop (forward-line 1)) (< (point) end-region-mark))
034a9d40
RS
1380 (beginning-of-line)
1381 (f90-indent-line-no)
1382 (setq f90-cache-position (point))
1383 (cond ((looking-at "[ \t]*$") (setq ind-curr 0))
1384 ((looking-at "[ \t]*#") (setq ind-curr 0))
1385 ((looking-at "!") (setq ind-curr (f90-comment-indent)))
1386 ((f90-no-block-limit) (setq ind-curr ind-lev))
1387 ((looking-at f90-else-like-re) (setq ind-curr
1388 (- ind-lev f90-if-indent)))
1389 ((looking-at "contains[ \t]*\\($\\|!\\)")
1390 (setq ind-curr (- ind-lev f90-program-indent)))
1391 ((setq ind-b
1392 (cond ((setq struct (f90-looking-at-do)) f90-do-indent)
1393 ((or (setq struct (f90-looking-at-if-then))
1394 (setq struct (f90-looking-at-select-case))
1395 (setq struct (f90-looking-at-where-or-forall)))
1396 f90-if-indent)
1397 ((setq struct (f90-looking-at-type-like))
1398 f90-type-indent)
1399 ((setq struct (f90-looking-at-program-block-start))
1400 f90-program-indent)))
1401 (setq ind-curr ind-lev)
1402 (if ind-b (setq ind-lev (+ ind-lev ind-b)))
1403 (setq block-list (cons struct block-list)))
1404 ((setq end-struct (f90-looking-at-program-block-end))
1405 (setq beg-struct (car block-list)
1406 block-list (cdr block-list))
69658465 1407 (if f90-smart-end
034a9d40 1408 (save-excursion
ec2f376f
GM
1409 (f90-block-match (car beg-struct) (car (cdr beg-struct))
1410 (car end-struct) (car (cdr end-struct)))))
034a9d40
RS
1411 (setq ind-b
1412 (cond ((looking-at f90-end-if-re) f90-if-indent)
1413 ((looking-at "end[ \t]*do\\>") f90-do-indent)
1414 ((looking-at f90-end-type-re) f90-type-indent)
1415 ((f90-looking-at-program-block-end)
1416 f90-program-indent)))
1417 (if ind-b (setq ind-lev (- ind-lev ind-b)))
1418 (setq ind-curr ind-lev))
034a9d40 1419 (t (setq ind-curr ind-lev)))
ec2f376f 1420 ;; Do the indentation if necessary.
034a9d40
RS
1421 (if (not (zerop (- ind-curr (current-column))))
1422 (f90-indent-to ind-curr))
1423 (while (and (f90-line-continued) (zerop (forward-line 1))
1424 (< (point) end-region-mark))
ec2f376f
GM
1425 (if (looking-at "[ \t]*!")
1426 (f90-indent-to (f90-comment-indent))
1427 (if (not (zerop (- (current-indentation)
1428 (+ ind-curr f90-continuation-indent))))
1429 (f90-indent-to
1430 (+ ind-curr f90-continuation-indent) 'no-line-no)))))
1431 ;; Restore point, etc.
034a9d40
RS
1432 (setq f90-cache-position nil)
1433 (goto-char save-point)
1434 (set-marker end-region-mark nil)
1435 (set-marker save-point nil)
1bb3ae5c 1436 (if f90-xemacs-flag
034a9d40
RS
1437 (zmacs-deactivate-region)
1438 (deactivate-mark))))
1439
1440(defun f90-indent-subprogram ()
ec2f376f 1441 "Properly indent the subprogram containing point."
034a9d40
RS
1442 (interactive)
1443 (save-excursion
e3f5ce56 1444 (let ((program (f90-mark-subprogram)))
034a9d40
RS
1445 (if program
1446 (progn
2a74bdc1 1447 (message "Indenting %s %s..."
7f03b2b5 1448 (car program) (car (cdr program)))
9a8ba072 1449 (indent-region (point) (mark) nil)
2a74bdc1 1450 (message "Indenting %s %s...done"
7f03b2b5 1451 (car program) (car (cdr program))))
2a74bdc1 1452 (message "Indenting the whole file...")
9a8ba072 1453 (indent-region (point) (mark) nil)
2a74bdc1 1454 (message "Indenting the whole file...done")))))
034a9d40 1455
034a9d40 1456(defun f90-break-line (&optional no-update)
87ee2359
GM
1457 "Break line at point, insert continuation marker(s) and indent.
1458Unless in a string or comment, or if the optional argument NO-UPDATE
1459is non-nil, call `f90-update-line' after inserting the continuation marker."
034a9d40
RS
1460 (interactive)
1461 (let (ctype)
1462 (cond ((f90-in-string)
f00d3304 1463 (insert "&") (newline 1) (insert "&"))
034a9d40 1464 ((f90-in-comment)
034a9d40 1465 (setq ctype (f90-get-present-comment-type))
f00d3304 1466 (newline 1)
b974df0a
EN
1467 (insert ctype))
1468 (t (insert "&")
034a9d40 1469 (if (not no-update) (f90-update-line))
f00d3304 1470 (newline 1)
b974df0a 1471 (if f90-beginning-ampersand (insert "&")))))
84021009 1472 (indent-according-to-mode))
69658465 1473
034a9d40 1474(defun f90-find-breakpoint ()
87ee2359 1475 "From `fill-column', search backward for break-delimiter."
6734e165 1476 (let ((bol (line-beginning-position)))
034a9d40 1477 (re-search-backward f90-break-delimiters bol)
5c2a80ad
GM
1478 (if (not f90-break-before-delimiters)
1479 (if (looking-at f90-no-break-re)
1480 (forward-char 2)
1481 (forward-char))
1482 (backward-char)
1483 (if (not (looking-at f90-no-break-re))
1484 (forward-char)))))
034a9d40 1485
034a9d40 1486(defun f90-do-auto-fill ()
d14e6bbe
GM
1487 "Break line if non-white characters beyond `fill-column'.
1488Update keyword case first."
034a9d40 1489 (interactive)
ec2f376f 1490 ;; Break line before or after last delimiter (non-word char) if
b974df0a 1491 ;; position is beyond fill-column.
ec2f376f 1492 ;; Will not break **, //, or => (as specified by f90-no-break-re).
7cae52cf 1493 (f90-update-line)
b974df0a 1494 (while (> (current-column) fill-column)
69658465
GM
1495 (let ((pos-mark (point-marker)))
1496 (move-to-column fill-column)
e3f5ce56 1497 (or (f90-in-string) (f90-find-breakpoint))
69658465
GM
1498 (f90-break-line)
1499 (goto-char pos-mark)
1500 (set-marker pos-mark nil))))
b974df0a 1501
034a9d40
RS
1502
1503(defun f90-join-lines ()
1504 "Join present line with next line, if this line ends with \&."
1505 (interactive)
1506 (let (pos (oldpos (point)))
1507 (end-of-line)
1508 (skip-chars-backward " \t")
e3f5ce56
GM
1509 (when (= (preceding-char) ?&)
1510 (delete-char -1)
1511 (setq pos (point))
1512 (forward-line 1)
1513 (skip-chars-forward " \t")
1514 (if (looking-at "\&") (delete-char 1))
1515 (delete-region pos (point))
1516 (unless (f90-in-string)
1517 (delete-horizontal-space)
1518 (insert " "))
1519 (if (and auto-fill-function
1520 (> (line-end-position) fill-column))
1521 (f90-do-auto-fill))
1522 (goto-char oldpos)
1523 t))) ; return t if joined something
034a9d40
RS
1524
1525(defun f90-fill-region (beg-region end-region)
d14e6bbe 1526 "Fill every line in region by forward parsing. Join lines if possible."
034a9d40
RS
1527 (interactive "*r")
1528 (let ((end-region-mark (make-marker))
e3f5ce56
GM
1529 (go-on t)
1530 f90-smart-end f90-auto-keyword-case auto-fill-function)
034a9d40
RS
1531 (set-marker end-region-mark end-region)
1532 (goto-char beg-region)
1533 (while go-on
ec2f376f 1534 ;; Join as much as possible.
7cae52cf 1535 (while (f90-join-lines))
ec2f376f 1536 ;; Chop the line if necessary.
034a9d40
RS
1537 (while (> (save-excursion (end-of-line) (current-column))
1538 fill-column)
1539 (move-to-column fill-column)
7cae52cf
RS
1540 (f90-find-breakpoint)
1541 (f90-break-line 'no-update))
e3f5ce56
GM
1542 (setq go-on (and (< (point) (marker-position end-region-mark))
1543 (zerop (forward-line 1)))
1544 f90-cache-position (point)))
034a9d40 1545 (setq f90-cache-position nil)
1bb3ae5c 1546 (if f90-xemacs-flag
034a9d40
RS
1547 (zmacs-deactivate-region)
1548 (deactivate-mark))))
1549\f
1550(defun f90-block-match (beg-block beg-name end-block end-name)
1551 "Match end-struct with beg-struct and complete end-block if possible.
ec2f376f
GM
1552BEG-BLOCK is the type of block as indicated at the start (e.g., do).
1553BEG-NAME is the block start name (may be nil).
1554END-BLOCK is the type of block as indicated at the end (may be nil).
1555END-NAME is the block end name (may be nil).
034a9d40 1556Leave point at the end of line."
6734e165 1557 (search-forward "end" (line-end-position))
034a9d40
RS
1558 (catch 'no-match
1559 (if (not (f90-equal-symbols beg-block end-block))
1560 (if end-block
1561 (progn
1562 (message "END %s does not match %s." end-block beg-block)
69658465 1563 (end-of-line)
034a9d40
RS
1564 (throw 'no-match nil))
1565 (message "Inserting %s." beg-block)
1566 (insert (concat " " beg-block)))
1567 (search-forward end-block))
1568 (if (not (f90-equal-symbols beg-name end-name))
69658465 1569 (cond ((and beg-name (not end-name))
034a9d40
RS
1570 (message "Inserting %s." beg-name)
1571 (insert (concat " " beg-name)))
69658465 1572 ((and beg-name end-name)
034a9d40
RS
1573 (message "Replacing %s with %s." end-name beg-name)
1574 (search-forward end-name)
1575 (replace-match beg-name))
69658465 1576 ((and (not beg-name) end-name)
034a9d40
RS
1577 (message "Deleting %s." end-name)
1578 (search-forward end-name)
1579 (replace-match "")))
1580 (if end-name (search-forward end-name)))
ee30478d 1581 (if (not (looking-at "[ \t]*!")) (delete-horizontal-space))))
034a9d40
RS
1582
1583(defun f90-match-end ()
ec2f376f 1584 "From an end block statement, find the corresponding block and name."
034a9d40 1585 (interactive)
e3f5ce56 1586 (let ((count 1) (top-of-window (window-start))
034a9d40 1587 (end-point (point)) (case-fold-search t)
e3f5ce56 1588 matching-beg beg-name end-name beg-block end-block end-struct)
5c2a80ad
GM
1589 (when (save-excursion (beginning-of-line) (skip-chars-forward " \t0-9")
1590 (setq end-struct (f90-looking-at-program-block-end)))
e3f5ce56
GM
1591 (setq end-block (car end-struct)
1592 end-name (car (cdr end-struct)))
5c2a80ad
GM
1593 (save-excursion
1594 (beginning-of-line)
69658465 1595 (while
5c2a80ad
GM
1596 (and (not (zerop count))
1597 (let ((stop nil) notexist)
1598 (while (not stop)
1599 (setq notexist
69658465 1600 (not (re-search-backward
5c2a80ad
GM
1601 (concat "\\(" f90-blocks-re "\\)") nil t)))
1602 (if notexist
1603 (setq stop t)
1604 (setq stop
1605 (not (or (f90-in-string)
1606 (f90-in-comment))))))
1607 (not notexist)))
e3f5ce56
GM
1608 (beginning-of-line)
1609 (skip-chars-forward " \t0-9")
5c2a80ad 1610 (cond ((setq matching-beg
e3f5ce56
GM
1611 (or
1612 (f90-looking-at-do)
1613 (f90-looking-at-if-then)
1614 (f90-looking-at-where-or-forall)
1615 (f90-looking-at-select-case)
1616 (f90-looking-at-type-like)
1617 (f90-looking-at-program-block-start)))
1618 (setq count (1- count)))
5c2a80ad 1619 ((looking-at (concat "end[ \t]*" f90-blocks-re "\\b"))
e3f5ce56 1620 (setq count (1+ count)))))
5c2a80ad
GM
1621 (if (not (zerop count))
1622 (message "No matching beginning.")
1623 (f90-update-line)
1624 (if (eq f90-smart-end 'blink)
1625 (if (< (point) top-of-window)
1626 (message "Matches %s: %s"
1627 (what-line)
1628 (buffer-substring
1629 (line-beginning-position)
1630 (line-end-position)))
1631 (sit-for 1)))
e3f5ce56
GM
1632 (setq beg-block (car matching-beg)
1633 beg-name (car (cdr matching-beg)))
5c2a80ad
GM
1634 (goto-char end-point)
1635 (beginning-of-line)
1636 (f90-block-match beg-block beg-name end-block end-name))))))
034a9d40
RS
1637
1638(defun f90-insert-end ()
87ee2359 1639 "Insert a complete end statement matching beginning of present block."
034a9d40 1640 (interactive)
e3f5ce56 1641 (let ((f90-smart-end (or f90-smart-end 'blink)))
034a9d40
RS
1642 (insert "end")
1643 (f90-indent-new-line)))
1644\f
ec2f376f 1645;; Abbrevs and keywords.
034a9d40
RS
1646
1647(defun f90-abbrev-start ()
69658465 1648 "Typing `\\[help-command] or `? lists all the F90 abbrevs.
034a9d40
RS
1649Any other key combination is executed normally."
1650 (interactive)
ee30478d 1651 (let (e c)
034a9d40 1652 (insert last-command-char)
1bb3ae5c
GM
1653 (if (not f90-xemacs-flag)
1654 (setq c (read-event))
1655 (setq e (next-command-event)
1656 c (event-to-character e)))
ec2f376f 1657 ;; Insert char if not equal to `?'.
0cf5bb50 1658 (if (or (eq c ??) (eq c help-char))
034a9d40 1659 (f90-abbrev-help)
1bb3ae5c 1660 (if f90-xemacs-flag
ee30478d 1661 (setq unread-command-event e)
034a9d40
RS
1662 (setq unread-command-events (list c))))))
1663
1664(defun f90-abbrev-help ()
1665 "List the currently defined abbrevs in F90 mode."
1666 (interactive)
1667 (message "Listing abbrev table...")
1668 (display-buffer (f90-prepare-abbrev-list-buffer))
1669 (message "Listing abbrev table...done"))
1670
1671(defun f90-prepare-abbrev-list-buffer ()
ec2f376f 1672 "Create a buffer listing the F90 mode abbreviations."
034a9d40
RS
1673 (save-excursion
1674 (set-buffer (get-buffer-create "*Abbrevs*"))
1675 (erase-buffer)
1676 (insert-abbrev-table-description 'f90-mode-abbrev-table t)
1677 (goto-char (point-min))
1678 (set-buffer-modified-p nil)
1679 (edit-abbrevs-mode))
1680 (get-buffer-create "*Abbrevs*"))
1681
1682(defun f90-upcase-keywords ()
1683 "Upcase all F90 keywords in the buffer."
1684 (interactive)
1685 (f90-change-keywords 'upcase-word))
1686
1687(defun f90-capitalize-keywords ()
1688 "Capitalize all F90 keywords in the buffer."
1689 (interactive)
1690 (f90-change-keywords 'capitalize-word))
1691
1692(defun f90-downcase-keywords ()
1693 "Downcase all F90 keywords in the buffer."
1694 (interactive)
1695 (f90-change-keywords 'downcase-word))
1696
1697(defun f90-upcase-region-keywords (beg end)
1698 "Upcase all F90 keywords in the region."
1699 (interactive "*r")
1700 (f90-change-keywords 'upcase-word beg end))
1701
1702(defun f90-capitalize-region-keywords (beg end)
1703 "Capitalize all F90 keywords in the region."
1704 (interactive "*r")
1705 (f90-change-keywords 'capitalize-word beg end))
1706
1707(defun f90-downcase-region-keywords (beg end)
1708 "Downcase all F90 keywords in the region."
1709 (interactive "*r")
1710 (f90-change-keywords 'downcase-word beg end))
1711
1712;; Change the keywords according to argument.
1713(defun f90-change-keywords (change-word &optional beg end)
ec2f376f
GM
1714 "Change the case of F90 keywords in the region (if specified) or buffer.
1715CHANGE-WORD should be one of 'upcase-word, 'downcase-word, capitalize-word."
034a9d40 1716 (save-excursion
e3f5ce56
GM
1717 (setq beg (or beg (point-min))
1718 end (or end (point-max)))
69658465 1719 (let ((keyword-re
ee30478d
KH
1720 (concat "\\("
1721 f90-keywords-re "\\|" f90-procedures-re "\\|"
1722 f90-hpf-keywords-re "\\|" f90-operators-re "\\)"))
e3f5ce56
GM
1723 (ref-point (point-min))
1724 (modified (buffer-modified-p))
1725 state saveword back-point)
034a9d40 1726 (goto-char beg)
ee30478d
KH
1727 (unwind-protect
1728 (while (re-search-forward keyword-re end t)
5c2a80ad
GM
1729 (unless (progn
1730 (setq state (parse-partial-sexp ref-point (point)))
1731 (or (nth 3 state) (nth 4 state)
ec2f376f 1732 (save-excursion ; check for cpp directive
5c2a80ad
GM
1733 (beginning-of-line)
1734 (skip-chars-forward " \t0-9")
1735 (looking-at "#"))))
ee30478d 1736 (setq ref-point (point)
e3f5ce56
GM
1737 back-point (save-excursion (backward-word 1) (point))
1738 saveword (buffer-substring back-point ref-point))
ee30478d
KH
1739 (funcall change-word -1)
1740 (or (string= saveword (buffer-substring back-point ref-point))
1741 (setq modified t))))
1742 (or modified (set-buffer-modified-p nil))))))
034a9d40 1743
d2d15846
DL
1744
1745(defun f90-current-defun ()
1746 "Function to use for `add-log-current-defun-function' in F90 mode."
1747 (save-excursion
1748 (nth 1 (f90-beginning-of-subprogram))))
1749
034a9d40 1750(provide 'f90)
db97b872 1751
034a9d40 1752;;; f90.el ends here