;;; fortran.el --- Fortran mode for GNU Emacs
;; Copyright (C) 1986, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
-;; 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;; 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Author: Michael D. Prange <prange@erl.mit.edu>
A value of nil specifies that continuation lines are marked
with a character in column 6."
:type 'boolean
+ :safe 'booleanp
:group 'fortran-indent)
-(put 'fortran-tab-mode-default 'safe-local-variable 'booleanp)
;; TODO add more detail of what tab mode is to doc string.
(defcustom fortran-tab-mode-string
"String to appear in mode line in TAB format buffers.
See Info node `(emacs)ForIndent Cont'."
:type 'string
+ :risky t
:group 'fortran-indent)
-(put 'fortran-tab-mode-string 'risky-local-variable t)
(defcustom fortran-do-indent 3
"Extra indentation applied to DO blocks."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-do-indent 'safe-local-variable 'integerp)
(defcustom fortran-if-indent 3
"Extra indentation applied to IF, SELECT CASE and WHERE blocks."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-if-indent 'safe-local-variable 'integerp)
(defcustom fortran-structure-indent 3
"Extra indentation applied to STRUCTURE, UNION, MAP and INTERFACE blocks."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-structure-indent 'safe-local-variable 'integerp)
(defcustom fortran-continuation-indent 5
"Extra indentation applied to continuation lines."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-continuation-indent 'safe-local-variable 'integerp)
(defcustom fortran-comment-indent-style 'fixed
"How to indent comments.
`relative' indents to current Fortran indentation plus
`fortran-comment-line-extra-indent'."
:type '(radio (const :tag "Untouched" nil) (const fixed) (const relative))
+ :safe (lambda (value) (memq value '(nil fixed relative)))
:group 'fortran-indent)
-(put 'fortran-comment-indent 'safe-local-variable
- (lambda (value) (memq value '(nil fixed relative))))
(defcustom fortran-comment-line-extra-indent 0
"Amount of extra indentation for text within full-line comments."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent
:group 'fortran-comment)
-(put 'fortran-comment-line-extra-indent 'safe-local-variable 'integerp)
(defcustom fortran-comment-line-start "C"
"Delimiter inserted to start new full-line comment.
allow trailing comments on a line."
:version "21.1"
:type 'string
+ :safe 'stringp
:group 'fortran-comment)
-(put 'fortran-comment-line-start 'safe-local-variable 'stringp)
;; This used to match preprocessor lines too, but that messes up
;; filling and doesn't seem to be necessary.
"Regexp to match the start of a full-line comment."
:version "21.1"
:type 'regexp
+ :safe 'stringp
:group 'fortran-comment)
-(put 'fortran-comment-line-start-skip 'safe-local-variable 'stringp)
(defcustom fortran-directive-re
"^[ \t]*#.*"
The matching line will be given zero indentation."
:version "22.1"
:type 'regexp
+ :safe 'stringp
:group 'fortran-indent)
-(put 'fortran-directive-re 'safe-local-variable 'stringp)
(defcustom fortran-minimum-statement-indent-fixed 6
"Minimum statement indentation for fixed format continuation style."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-minimum-statement-indent-fixed 'safe-local-variable 'integerp)
(defcustom fortran-minimum-statement-indent-tab (max tab-width 6)
"Minimum statement indentation for TAB format continuation style."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-minimum-statement-indent-tab 'safe-local-variable 'integerp)
;; Note that this is documented in the v18 manuals as being a string
;; of length one rather than a single character.
"Single-character string inserted for Fortran comment indentation.
Normally a space."
:type 'string
+ :safe (lambda (value) (or (characterp value)
+ (and (stringp value) (= (length value) 1))))
:group 'fortran-comment)
-(put 'fortran-comment-indent-char 'safe-local-variable
- (lambda (value) (or (characterp value)
- (and (stringp value)
- (= (length value) 1)))))
(defcustom fortran-line-number-indent 1
"Maximum indentation for Fortran line numbers.
5 means right-justify them within their five-column field."
:type 'integer
+ :safe 'integerp
:group 'fortran-indent)
-(put 'fortran-line-number-indent 'safe-local-variable 'integerp)
(defcustom fortran-check-all-num-for-matching-do nil
"Non-nil causes all numbered lines to be treated as possible DO loop ends."
:type 'boolean
+ :safe 'booleanp
:group 'fortran)
-(put 'fortran-check-all-num-for-matching-do 'safe-local-variable 'booleanp)
(defcustom fortran-blink-matching-if nil
"Non-nil causes \\[fortran-indent-line] on ENDIF to blink on matching IF.
Also, from an ENDDO statement blink on matching DO [WHILE] statement."
:type 'boolean
+ :safe 'booleanp
:group 'fortran)
-(put 'fortran-blink-matching-if 'safe-local-variable 'booleanp)
(defcustom fortran-continuation-string "$"
"Single-character string used for Fortran continuation lines.
line, it will convert the line into a continuation line of the
appropriate style. Normally \"$\"."
:type 'string
+ :safe (lambda (value) (and (stringp value) (= (length value) 1)))
:group 'fortran)
-(put 'fortran-continuation-string 'safe-local-variable
- (lambda (value) (and (stringp value)
- (= (length value) 1))))
(defcustom fortran-comment-region "c$$$"
"String inserted by \\[fortran-comment-region] at start of each \
line in region."
:type 'string
+ :safe 'stringp
:group 'fortran-comment)
-(put 'fortran-comment-region 'safe-local-variable 'stringp)
(defcustom fortran-electric-line-number t
"Non-nil causes line numbers to be moved to the correct column as typed."
:type 'boolean
+ :safe 'booleanp
:group 'fortran)
-(put 'fortran-electric-line-number 'safe-local-variable 'booleanp)
;; TODO use fortran-line-length, somehow.
(defcustom fortran-column-ruler-fixed
This variable is used in fixed format mode.
See the variable `fortran-column-ruler-tab' for TAB format mode."
:type 'string
+ :safe 'stringp
:group 'fortran)
-(put 'fortran-column-ruler-fixed 'safe-local-variable 'stringp)
;; TODO use fortran-line-length, somehow.
(defcustom fortran-column-ruler-tab
This variable is used in TAB format mode.
See the variable `fortran-column-ruler-fixed' for fixed format mode."
:type 'string
+ :safe 'stringp
:group 'fortran)
-(put 'fortran-column-ruler-tab 'safe-local-variable 'stringp)
(defcustom fortran-analyze-depth 100
"Number of lines to scan to identify fixed or TAB format style."
:type 'integer
+ :safe 'integerp
:group 'fortran)
-(put 'fortran-analyze-depth 'safe-local-variable 'integerp)
(defcustom fortran-break-before-delimiters t
"Non-nil causes filling to break lines before delimiters.
Delimiters are characters matching the regexp `fortran-break-delimiters-re'."
:type 'boolean
+ :safe 'booleanp
:group 'fortran)
-(put 'fortran-break-before-delimiters 'safe-local-variable 'booleanp)
;; TODO 0 as no-limit, as per g77.
(defcustom fortran-line-length 72
buffer). This corresponds to the g77 compiler option
`-ffixed-line-length-N'."
:type 'integer
+ :safe 'integerp
:initialize 'custom-initialize-default
:set (lambda (symbol value)
;; Do all fortran buffers, and the default.
:version "23.1"
:group 'fortran)
-(put 'fortran-line-length 'safe-local-variable 'integerp)
(make-variable-buffer-local 'fortran-line-length)
(defcustom fortran-mode-hook nil
'("^ *\\([0-9]+\\)" . font-lock-constant-face)))
"Medium level highlighting for Fortran mode.")
+;; See bug#1385. Never really looked into _why_ this matters...
+(defun fortran-match-and-skip-declaration (limit)
+ "Like `font-lock-match-c-style-declaration-item-and-skip-to-next'.
+The only difference is, it returns t in a case when the default returns nil."
+ (when (looking-at "[ \n\t*]*\\(\\sw+\\)[ \t\n]*\\(((?\\)?")
+ (when (and (match-end 2) (> (- (match-end 2) (match-beginning 2)) 1))
+ (let ((pos (point)))
+ (skip-chars-backward " \t\n")
+ (skip-syntax-backward "w")
+ (unless (looking-at "\\(\\sw+\\)[ \t\n]*\\sw+[ \t\n]*\\(((?\\)?")
+ (goto-char pos)
+ (looking-at "[ \n\t*]*\\(\\sw+\\)[ \t\n]*\\(((?\\)?"))))
+ (save-match-data
+ (condition-case nil
+ (save-restriction
+ (narrow-to-region (point-min) limit)
+ (goto-char (match-end 1))
+ (while (not (looking-at "[ \t\n]*\\(\\(,\\)\\|;\\|\\'\\)"))
+ (goto-char (or (scan-sexps (point) 1) (point-max))))
+ (goto-char (match-end 2)))
+ (error t)))))
+
(defvar fortran-font-lock-keywords-3
(append
fortran-font-lock-keywords-1
;; Type specifier.
'(1 font-lock-type-face)
;; Declaration item (or just /.../ block name).
- `(font-lock-match-c-style-declaration-item-and-skip-to-next
+ `(fortran-match-and-skip-declaration
;; Start after any *(...) expression.
(condition-case nil
(and (match-beginning ,(1+ (regexp-opt-depth
;; (We can do so for F90-style). Therefore an unmatched quote in a
;; standard comment will throw fontification off on the wrong track.
;; So we do syntactic fontification with regexps.
-(defun fortran-font-lock-syntactic-keywords ()
- "Return a value for `font-lock-syntactic-keywords' in Fortran mode.
-This varies according to the value of `fortran-line-length'.
+(defun fortran-make-syntax-propertize-function (line-length)
+ "Return a value for `syntax-propertize-function' in Fortran mode.
+This varies according to the value of LINE-LENGTH.
This is used to fontify fixed-format Fortran comments."
- `(("^[cd\\*]" 0 (11))
- (,(format "^[^cd\\*\t\n].\\{%d\\}\\([^\n]+\\)" (1- fortran-line-length))
- 1 (11))))
+ ;; This results in a non-byte-compiled function. We could pass it through
+ ;; `byte-compile', but simple benchmarks indicate that it's probably not
+ ;; worth the trouble (about ½% of slow down).
+ (eval ;I hate `eval', but it's hard to avoid it here.
+ `(syntax-propertize-rules
+ ("^[cd\\*]" (0 "<"))
+ ;; We mark all chars after line-length as "comment-start", rather than
+ ;; just the first one. This is so that a closing ' that's past the
+ ;; line-length will indeed be ignored (and will result in a string that
+ ;; leaks into subsequent lines).
+ ((format "^[^cd\\*\t\n].\\{%d\\}\\(.+\\)" (1- line-length))
+ (1 "<")))))
(defvar fortran-font-lock-keywords fortran-font-lock-keywords-1
"Default expressions to highlight in Fortran mode.")
\f
;;;###autoload
-(defun fortran-mode ()
+(define-derived-mode fortran-mode prog-mode "Fortran"
"Major mode for editing Fortran code in fixed format.
For free format code, use `f90-mode'.
Turning on Fortran mode calls the value of the variable `fortran-mode-hook'
with no args, if that value is non-nil."
- (interactive)
- (kill-all-local-variables)
- (setq major-mode 'fortran-mode
- mode-name "Fortran"
- local-abbrev-table fortran-mode-abbrev-table)
- (set-syntax-table fortran-mode-syntax-table)
- (use-local-map fortran-mode-map)
+ :group 'fortran
+ :syntax-table fortran-mode-syntax-table
+ :abbrev-table fortran-mode-abbrev-table
(set (make-local-variable 'indent-line-function) 'fortran-indent-line)
(set (make-local-variable 'indent-region-function)
(lambda (start end)
fortran-font-lock-keywords-3
fortran-font-lock-keywords-4)
nil t ((?/ . "$/") ("_$" . "w"))
- fortran-beginning-of-subprogram
- (font-lock-syntactic-keywords
- . fortran-font-lock-syntactic-keywords)))
+ fortran-beginning-of-subprogram))
+ (set (make-local-variable 'syntax-propertize-function)
+ (fortran-make-syntax-propertize-function fortran-line-length))
(set (make-local-variable 'imenu-case-fold-search) t)
(set (make-local-variable 'imenu-generic-expression)
fortran-imenu-generic-expression)
#'fortran-current-defun)
(set (make-local-variable 'dabbrev-case-fold-search) 'case-fold-search)
(set (make-local-variable 'gud-find-expr-function) 'fortran-gud-find-expr)
- (add-hook 'hack-local-variables-hook 'fortran-hack-local-variables nil t)
- (run-mode-hooks 'fortran-mode-hook))
+ (add-hook 'hack-local-variables-hook 'fortran-hack-local-variables nil t))
\f
(defun fortran-line-length (nchars &optional global)
"Set the length of fixed-form Fortran lines to NCHARS.
This normally only affects the current buffer, which must be in
Fortran mode. If the optional argument GLOBAL is non-nil, it
-affects all Fortran buffers, and also the default."
- (interactive "p")
- (let (new)
- (mapc (lambda (buff)
- (with-current-buffer buff
- (when (eq major-mode 'fortran-mode)
- (setq fortran-line-length nchars
- fill-column fortran-line-length
- new (fortran-font-lock-syntactic-keywords))
- ;; Refontify only if necessary.
- (unless (equal new font-lock-syntactic-keywords)
- (setq font-lock-syntactic-keywords
- (fortran-font-lock-syntactic-keywords))
- (if font-lock-mode (font-lock-mode 1))))))
+affects all Fortran buffers, and also the default.
+If a numeric prefix argument is specified, it will be used as NCHARS,
+otherwise is a non-numeric prefix arg is specified, the length will be
+provided via the minibuffer, and otherwise the current column is used."
+ (interactive
+ (list (cond
+ ((numberp current-prefix-arg) current-prefix-arg)
+ (current-prefix-arg
+ (read-number "Line length: " (default-value 'fortran-line-length)))
+ (t (current-column)))))
+ (dolist (buff (if global
+ (buffer-list)
+ (list (current-buffer))))
+ (with-current-buffer buff
+ (when (derived-mode-p 'fortran-mode)
+ (unless (eq fortran-line-length nchars)
+ (setq fortran-line-length nchars
+ fill-column fortran-line-length
+ syntax-propertize-function
+ (fortran-make-syntax-propertize-function nchars))
+ (syntax-ppss-flush-cache (point-min))
+ (if font-lock-mode (font-lock-mode 1))))))
(if global
- (buffer-list)
- (list (current-buffer))))
- (if global
- (setq-default fortran-line-length nchars))))
+ (setq-default fortran-line-length nchars)))
(defun fortran-hack-local-variables ()
"Fortran mode adds this to `hack-local-variables-hook'."
not check for consistency of block types. Interactively, pushes
mark before moving point."
(interactive "p")
- (if (interactive-p) (push-mark (point) t))
+ (if (called-interactively-p 'any) (push-mark (point) t))
(and num (< num 0) (fortran-beginning-of-block (- num)))
(let ((case-fold-search t)
(count (or num 1)))
Does not check for consistency of block types. Interactively,
pushes mark before moving point."
(interactive "p")
- (if (interactive-p) (push-mark (point) t))
+ (if (called-interactively-p 'any) (push-mark (point) t))
(and num (< num 0) (fortran-end-of-block (- num)))
(let ((case-fold-search t)
(count (or num 1)))