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