home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2 / Openstep-4.2-Intel-User.iso / usr / lib / emacs / lisp / fortran.el < prev    next >
Lisp/Scheme  |  1991-01-09  |  27KB  |  655 lines

  1. ;;; Fortran mode for GNU Emacs  (beta test version 1.21, Oct. 1, 1985)
  2. ;;; Copyright (c) 1986 Free Software Foundation, Inc.
  3. ;;; Written by Michael D. Prange (prange@erl.mit.edu)
  4.  
  5. ;; This file is part of GNU Emacs.
  6.  
  7. ;; GNU Emacs is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation; either version 1, or (at your option)
  10. ;; any later version.
  11.  
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. ;; GNU General Public License for more details.
  16.  
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  19. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  
  21. ;;; Author acknowledges help from Stephen Gildea <gildea@erl.mit.edu>
  22.  
  23. ;;; Bugs to bug-fortran-mode@erl.mit.edu.
  24.  
  25. (defvar fortran-do-indent 3
  26.   "*Extra indentation applied to `do' blocks.")
  27.  
  28. (defvar fortran-if-indent 3
  29.   "*Extra indentation applied to `if' blocks.")
  30.  
  31. (defvar fortran-continuation-indent 5
  32.   "*Extra indentation applied to `continuation' lines.")
  33.  
  34. (defvar fortran-comment-indent-style 'fixed
  35.   "*nil forces comment lines not to be touched,
  36. 'fixed produces fixed comment indentation to comment-column,
  37. and 'relative indents to current fortran indentation plus comment-column.")
  38.  
  39. (defvar fortran-comment-line-column 6
  40.   "*Indentation for text in comment lines.")
  41.  
  42. (defvar comment-line-start nil
  43.   "*Delimiter inserted to start new full-line comment.")
  44.  
  45. (defvar comment-line-start-skip nil
  46.   "*Regexp to match the start of a full-line comment.")
  47.  
  48. (defvar fortran-minimum-statement-indent 6
  49.   "*Minimum indentation for fortran statements.")
  50.  
  51. ;; Note that this is documented in the v18 manuals as being a string
  52. ;; of length one rather than a single character.
  53. ;; The code in this file accepts either format for compatibility.
  54. (defvar fortran-comment-indent-char ? 
  55.   "*Character to be inserted for Fortran comment indentation.
  56. Normally a space.")
  57.  
  58. (defvar fortran-line-number-indent 1
  59.   "*Maximum indentation for Fortran line numbers.
  60. 5 means right-justify them within their five-column field.")
  61.  
  62. (defvar fortran-check-all-num-for-matching-do nil
  63.   "*Non-nil causes all numbered lines to be treated as possible do-loop ends.")
  64.  
  65. (defvar fortran-continuation-char ?$
  66.   "*Character which is inserted in column 5 by \\[fortran-split-line]
  67. to begin a continuation line.  Normally $.")
  68.  
  69. (defvar fortran-comment-region "c$$$"
  70.   "*String inserted by \\[fortran-comment-region] at start of each line in region.")
  71.  
  72. (defvar fortran-electric-line-number t
  73.   "*Non-nil causes line number digits to be moved to the correct column as typed.")
  74.  
  75. (defvar fortran-startup-message t
  76.   "*Non-nil displays a startup message when fortran-mode is first called.")
  77.  
  78. (defvar fortran-column-ruler
  79.   (concat "0   4 6  10        20        30        40        50        60        70\n"
  80.       "[   ]|{   |    |    |    |    |    |    |    |    |    |    |    |    |}\n")
  81.   "*String displayed above current line by \\[fortran-column-ruler].")
  82.  
  83. (defconst fortran-mode-version "1.21")
  84.  
  85. (defvar fortran-mode-syntax-table nil
  86.   "Syntax table in use in fortran-mode buffers.")
  87.  
  88. (if fortran-mode-syntax-table
  89.     ()
  90.   (setq fortran-mode-syntax-table (make-syntax-table))
  91.   (modify-syntax-entry ?\; "w" fortran-mode-syntax-table)
  92.   (modify-syntax-entry ?+ "." fortran-mode-syntax-table)
  93.   (modify-syntax-entry ?- "." fortran-mode-syntax-table)
  94.   (modify-syntax-entry ?* "." fortran-mode-syntax-table)
  95.   (modify-syntax-entry ?/ "." fortran-mode-syntax-table)
  96.   (modify-syntax-entry ?\' "\"" fortran-mode-syntax-table)
  97.   (modify-syntax-entry ?\" "\"" fortran-mode-syntax-table)
  98.   (modify-syntax-entry ?\\ "/" fortran-mode-syntax-table)
  99.   (modify-syntax-entry ?. "w" fortran-mode-syntax-table)
  100.   (modify-syntax-entry ?\n ">" fortran-mode-syntax-table))
  101.  
  102. (defvar fortran-mode-map () 
  103.   "Keymap used in fortran mode.")
  104.  
  105. (if fortran-mode-map
  106.     ()
  107.   (setq fortran-mode-map (make-sparse-keymap))
  108.   (define-key fortran-mode-map ";" 'fortran-abbrev-start)
  109.   (define-key fortran-mode-map "\C-c;" 'fortran-comment-region)
  110.   (define-key fortran-mode-map "\e\C-a" 'beginning-of-fortran-subprogram)
  111.   (define-key fortran-mode-map "\e\C-e" 'end-of-fortran-subprogram)
  112.   (define-key fortran-mode-map "\e;" 'fortran-indent-comment)
  113.   (define-key fortran-mode-map "\e\C-h" 'mark-fortran-subprogram)
  114.   (define-key fortran-mode-map "\e\n" 'fortran-split-line)
  115.   (define-key fortran-mode-map "\e\C-q" 'fortran-indent-subprogram)
  116.   (define-key fortran-mode-map "\C-c\C-w" 'fortran-window-create)
  117.   (define-key fortran-mode-map "\C-c\C-r" 'fortran-column-ruler)
  118.   (define-key fortran-mode-map "\C-c\C-p" 'fortran-previous-statement)
  119.   (define-key fortran-mode-map "\C-c\C-n" 'fortran-next-statement)
  120.   (define-key fortran-mode-map "\t" 'fortran-indent-line)
  121.   (define-key fortran-mode-map "0" 'fortran-electric-line-number)
  122.   (define-key fortran-mode-map "1" 'fortran-electric-line-number)
  123.   (define-key fortran-mode-map "2" 'fortran-electric-line-number)
  124.   (define-key fortran-mode-map "3" 'fortran-electric-line-number)
  125.   (define-key fortran-mode-map "4" 'fortran-electric-line-number)
  126.   (define-key fortran-mode-map "5" 'fortran-electric-line-number)
  127.   (define-key fortran-mode-map "6" 'fortran-electric-line-number)
  128.   (define-key fortran-mode-map "7" 'fortran-electric-line-number)
  129.   (define-key fortran-mode-map "8" 'fortran-electric-line-number)
  130.   (define-key fortran-mode-map "9" 'fortran-electric-line-number))
  131.  
  132. (defvar fortran-mode-abbrev-table nil)
  133. (if fortran-mode-abbrev-table
  134.     ()
  135.   (define-abbrev-table 'fortran-mode-abbrev-table ())
  136.   (let ((abbrevs-changed nil))
  137.     (define-abbrev fortran-mode-abbrev-table  ";b"   "byte" nil)
  138.     (define-abbrev fortran-mode-abbrev-table  ";ch"  "character" nil)
  139.     (define-abbrev fortran-mode-abbrev-table  ";cl"  "close" nil)
  140.     (define-abbrev fortran-mode-abbrev-table  ";c"   "continue" nil)
  141.     (define-abbrev fortran-mode-abbrev-table  ";cm"  "common" nil)
  142.     (define-abbrev fortran-mode-abbrev-table  ";cx"  "complex" nil)
  143.     (define-abbrev fortran-mode-abbrev-table  ";di"  "dimension" nil)
  144.     (define-abbrev fortran-mode-abbrev-table  ";do"  "double" nil)
  145.     (define-abbrev fortran-mode-abbrev-table  ";dc"  "double complex" nil)
  146.     (define-abbrev fortran-mode-abbrev-table  ";dp"  "double precision" nil)
  147.     (define-abbrev fortran-mode-abbrev-table  ";dw"  "do while" nil)
  148.     (define-abbrev fortran-mode-abbrev-table  ";e"   "else" nil)
  149.     (define-abbrev fortran-mode-abbrev-table  ";ed"  "enddo" nil)
  150.     (define-abbrev fortran-mode-abbrev-table  ";el"  "elseif" nil)
  151.     (define-abbrev fortran-mode-abbrev-table  ";en"  "endif" nil)
  152.     (define-abbrev fortran-mode-abbrev-table  ";eq"  "equivalence" nil)
  153.     (define-abbrev fortran-mode-abbrev-table  ";ex"  "external" nil)
  154.     (define-abbrev fortran-mode-abbrev-table  ";ey"  "entry" nil)
  155.     (define-abbrev fortran-mode-abbrev-table  ";f"   "format" nil)
  156.     (define-abbrev fortran-mode-abbrev-table  ";fu"  "function" nil)
  157.     (define-abbrev fortran-mode-abbrev-table  ";g"   "goto" nil)
  158.     (define-abbrev fortran-mode-abbrev-table  ";im"  "implicit" nil)
  159.     (define-abbrev fortran-mode-abbrev-table  ";ib"  "implicit byte" nil)
  160.     (define-abbrev fortran-mode-abbrev-table  ";ic"  "implicit complex" nil)
  161.     (define-abbrev fortran-mode-abbrev-table  ";ich" "implicit character" nil)
  162.     (define-abbrev fortran-mode-abbrev-table  ";ii"  "implicit integer" nil)
  163.     (define-abbrev fortran-mode-abbrev-table  ";il"  "implicit logical" nil)
  164.     (define-abbrev fortran-mode-abbrev-table  ";ir"  "implicit real" nil)
  165.     (define-abbrev fortran-mode-abbrev-table  ";inc" "include" nil)
  166.     (define-abbrev fortran-mode-abbrev-table  ";in"  "integer" nil)
  167.     (define-abbrev fortran-mode-abbrev-table  ";intr" "intrinsic" nil)
  168.     (define-abbrev fortran-mode-abbrev-table  ";l"   "logical" nil)
  169.     (define-abbrev fortran-mode-abbrev-table  ";op"  "open" nil)
  170.     (define-abbrev fortran-mode-abbrev-table  ";pa"  "parameter" nil)
  171.     (define-abbrev fortran-mode-abbrev-table  ";pr"  "program" nil)
  172.     (define-abbrev fortran-mode-abbrev-table  ";p"   "print" nil)
  173.     (define-abbrev fortran-mode-abbrev-table  ";re"  "real" nil)
  174.     (define-abbrev fortran-mode-abbrev-table  ";r"   "read" nil)
  175.     (define-abbrev fortran-mode-abbrev-table  ";rt"  "return" nil)
  176.     (define-abbrev fortran-mode-abbrev-table  ";rw"  "rewind" nil)
  177.     (define-abbrev fortran-mode-abbrev-table  ";s"   "stop" nil)
  178.     (define-abbrev fortran-mode-abbrev-table  ";su"  "subroutine" nil)
  179.     (define-abbrev fortran-mode-abbrev-table  ";ty"  "type" nil)
  180.     (define-abbrev fortran-mode-abbrev-table  ";w"   "write" nil)))
  181.  
  182. (defun fortran-mode ()
  183.   "Major mode for editing fortran code.
  184. Tab indents the current fortran line correctly. 
  185. `do' statements must not share a common `continue'.
  186.  
  187. Type `;?' or `;\\[help-command]' to display a list of built-in abbrevs for Fortran keywords.
  188.  
  189. Variables controlling indentation style and extra features:
  190.  
  191.  comment-start
  192.     Normally nil in Fortran mode.  If you want to use comments
  193.     starting with `!', set this to the string \"!\".
  194.  fortran-do-indent
  195.     Extra indentation within do blocks.  (default 3)
  196.  fortran-if-indent
  197.     Extra indentation within if blocks.  (default 3)
  198.  fortran-continuation-indent
  199.     Extra indentation appled to continuation statements.  (default 5)
  200.  fortran-comment-line-column
  201.     Amount of indentation for text within full-line comments. (default 6)
  202.  fortran-comment-indent-style
  203.     nil    means don't change indentation of text in full-line comments,
  204.     fixed  means indent that text at column fortran-comment-line-column
  205.     relative  means indent at fortran-comment-line-column beyond the
  206.            indentation for a line of code.
  207.     Default value is fixed.
  208.  fortran-comment-indent-char
  209.     Character to be inserted instead of space for full-line comment
  210.     indentation.  (default is a space)
  211.  fortran-minimum-statement-indent
  212.     Minimum indentation for fortran statements. (default 6)
  213.  fortran-line-number-indent
  214.     Maximum indentation for line numbers.  A line number will get
  215.     less than this much indentation if necessary to avoid reaching
  216.     column 5.  (default 1)
  217.  fortran-check-all-num-for-matching-do
  218.     Non-nil causes all numbered lines to be treated as possible 'continue'
  219.     statements.  (default nil)
  220.  fortran-continuation-char
  221.     character to be inserted in column 5 of a continuation line.
  222.     (default $)
  223.  fortran-comment-region
  224.     String inserted by \\[fortran-comment-region] at start of each line in 
  225.     region.  (default \"c$$$\")
  226.  fortran-electric-line-number
  227.     Non-nil causes line number digits to be moved to the correct column 
  228.     as typed.  (default t)
  229.  fortran-startup-message
  230.     Set to nil to inhibit message first time fortran-mode is used.
  231.  
  232. Turning on Fortran mode calls the value of the variable fortran-mode-hook 
  233. with no args, if that value is non-nil.
  234. \\{fortran-mode-map}"
  235.   (interactive)
  236.   (kill-all-local-variables)
  237.   (if fortran-startup-message
  238.       (message "Emacs Fortran mode version %s.  Bugs to bug-fortran-mode@erl.mit.edu" fortran-mode-version))
  239.   (setq fortran-startup-message nil)
  240.   (setq local-abbrev-table fortran-mode-abbrev-table)
  241.   (set-syntax-table fortran-mode-syntax-table)
  242.   (make-local-variable 'indent-line-function)
  243.   (setq indent-line-function 'fortran-indent-line)
  244.   (make-local-variable 'comment-indent-hook)
  245.   (setq comment-indent-hook 'fortran-comment-hook)
  246.   (make-local-variable 'comment-line-start-skip)
  247.   (setq comment-line-start-skip "^[Cc*][^ \t\n]*[ \t]*") ;[^ \t\n]* handles comment strings such as c$$$
  248.   (make-local-variable 'comment-line-start)
  249.   (setq comment-line-start "c")
  250.   (make-local-variable 'comment-start-skip)
  251.   (setq comment-start-skip "![ \t]*")
  252.   (make-local-variable 'comment-start)
  253.   (setq comment-start nil)
  254.   (make-local-variable 'require-final-newline)
  255.   (setq require-final-newline t)
  256.   (make-local-variable 'abbrev-all-caps)
  257.   (setq abbrev-all-caps t)
  258.   (make-local-variable 'indent-tabs-mode)
  259.   (setq indent-tabs-mode nil)
  260.   (use-local-map fortran-mode-map)
  261.   (setq mode-name "Fortran")
  262.   (setq major-mode 'fortran-mode)
  263.   (run-hooks 'fortran-mode-hook))
  264.  
  265. (defun fortran-comment-hook ()
  266.   (save-excursion
  267.     (skip-chars-backward " \t")
  268.     (max (+ 1 (current-column))
  269.      comment-column)))
  270.  
  271. (defun fortran-indent-comment ()
  272.   "Align or create comment on current line.
  273. Existing comments of all types are recognized and aligned.
  274. If the line has no comment, a side-by-side comment is inserted and aligned
  275. if the value of  comment-start  is not nil.
  276. Otherwise, a separate-line comment is inserted, on this line
  277. or on a new line inserted before this line if this line is not blank."
  278.   (interactive)
  279.   (beginning-of-line)
  280.   ;; Recognize existing comments of either kind.
  281.   (cond ((looking-at comment-line-start-skip)
  282.      (fortran-indent-line))
  283.     ((re-search-forward comment-start-skip
  284.                 (save-excursion (end-of-line) (point)) t)
  285.      (indent-for-comment))
  286.     ;; No existing comment.
  287.     ;; If side-by-side comments are defined, insert one,
  288.     ;; unless line is now blank.
  289.     ((and comment-start (not (looking-at "^[ \t]*$")))
  290.      (end-of-line)
  291.      (delete-horizontal-space)
  292.      (indent-to (fortran-comment-hook))
  293.      (insert comment-start))
  294.     ;; Else insert separate-line comment, making a new line if nec.
  295.     (t
  296.      (if (looking-at "^[ \t]*$")
  297.          (delete-horizontal-space)
  298.        (beginning-of-line)
  299.        (insert "\n")
  300.        (forward-char -1))
  301.      (insert comment-line-start)
  302.      (insert-char (if (stringp fortran-comment-indent-char)
  303.               (aref fortran-comment-indent-char 0)
  304.               fortran-comment-indent-char)
  305.               (- (calculate-fortran-indent) (current-column))))))
  306.  
  307. (defun fortran-comment-region (beg-region end-region arg)
  308.   "Comments every line in the region.
  309. Puts fortran-comment-region at the beginning of every line in the region. 
  310. BEG-REGION and END-REGION are args which specify the region boundaries. 
  311. With non-nil ARG, uncomments the region."
  312.   (interactive "*r\nP")
  313.   (let ((end-region-mark (make-marker)) (save-point (point-marker)))
  314.     (set-marker end-region-mark end-region)
  315.     (goto-char beg-region)
  316.     (beginning-of-line)
  317.     (if (not arg)            ;comment the region
  318.     (progn (insert fortran-comment-region)
  319.            (while (and  (= (forward-line 1) 0)
  320.                 (< (point) end-region-mark))
  321.          (insert fortran-comment-region)))
  322.       (let ((com (regexp-quote fortran-comment-region))) ;uncomment the region
  323.     (if (looking-at com)
  324.         (delete-region (point) (match-end 0)))
  325.     (while (and  (= (forward-line 1) 0)
  326.              (< (point) end-region-mark))
  327.       (if (looking-at com)
  328.           (delete-region (point) (match-end 0))))))
  329.     (goto-char save-point)
  330.     (set-marker end-region-mark nil)
  331.     (set-marker save-point nil)))
  332.  
  333. (defun fortran-abbrev-start ()
  334.   "Typing \";\\[help-command]\" or \";?\" lists all the fortran abbrevs. 
  335. Any other key combination is executed normally." ;\\[help-command] is just a way to print the value of the variable help-char.
  336.   (interactive)
  337.   (let (c)
  338.     (insert last-command-char)
  339.     (if (or (= (setq c (read-char)) ??)    ;insert char if not equal to `?'
  340.         (= c help-char))
  341.     (fortran-abbrev-help)
  342.       (setq unread-command-char c))))
  343.  
  344. (defun fortran-abbrev-help ()
  345.   "List the currently defined abbrevs in Fortran mode."
  346.   (interactive)
  347.   (message "Listing abbrev table...")
  348.   (require 'abbrevlist)
  349.   (list-one-abbrev-table fortran-mode-abbrev-table "*Help*")
  350.   (message "Listing abbrev table...done"))
  351.  
  352. (defun fortran-column-ruler ()
  353.   "Inserts a column ruler momentarily above current line, till next keystroke.
  354. The ruler is defined by the value of fortran-column-ruler.
  355. The key typed is executed unless it is SPC."
  356.   (interactive)
  357.   (momentary-string-display 
  358.    fortran-column-ruler (save-excursion (beginning-of-line) (point))
  359.    nil "Type SPC or any command to erase ruler."))
  360.  
  361. (defun fortran-window-create ()
  362.   "Makes the window 72 columns wide."
  363.   (interactive)
  364.   (let ((window-min-width 2))
  365.     (split-window-horizontally 73))
  366.   (other-window 1)
  367.   (switch-to-buffer " fortran-window-extra" t)
  368.   (select-window (previous-window)))
  369.  
  370. (defun fortran-split-line ()
  371.   "Break line at point and insert continuation marker and alignment."
  372.   (interactive)
  373.   (delete-horizontal-space)
  374.   (if (save-excursion (beginning-of-line) (looking-at comment-line-start-skip))
  375.       (insert "\n" comment-line-start " ")
  376.     (insert "\n " fortran-continuation-char))
  377.   (fortran-indent-line))
  378.  
  379. (defun delete-horizontal-regexp (chars)
  380.   "Delete all characters in CHARS around point.
  381. CHARS is like the inside of a [...] in a regular expression
  382. except that ] is never special and \ quotes ^, - or \."
  383.   (interactive "*s")
  384.   (skip-chars-backward chars)
  385.   (delete-region (point) (progn (skip-chars-forward chars) (point))))
  386.  
  387. (defun fortran-electric-line-number (arg)
  388.   "Self insert, but if part of a Fortran line number indent it automatically.
  389. Auto-indent does not happen if a numeric arg is used."
  390.   (interactive "P")
  391.   (if (or arg (not fortran-electric-line-number))
  392.       (self-insert-command arg)
  393.     (if (or (save-excursion (re-search-backward "[^ \t0-9]"
  394.                         (save-excursion
  395.                           (beginning-of-line)
  396.                           (point))
  397.                         t)) ;not a line number
  398.         (looking-at "[0-9]"))        ;within a line number
  399.     (insert last-command-char)
  400.       (skip-chars-backward " \t")
  401.       (insert last-command-char)
  402.       (fortran-indent-line))))
  403.  
  404. (defun beginning-of-fortran-subprogram ()
  405.   "Moves point to the beginning of the current fortran subprogram."
  406.   (interactive)
  407.   (let ((case-fold-search t))
  408.     (beginning-of-line -1)
  409.     (re-search-backward "^[ \t0-9]*end\\b[ \t]*[^ \t=(a-z]" nil 'move)
  410.     (if (looking-at "^[ \t0-9]*end\\b[ \t]*[^ \t=(a-z]")
  411.     (forward-line 1))))
  412.  
  413. (defun end-of-fortran-subprogram ()
  414.   "Moves point to the end of the current fortran subprogram."
  415.   (interactive)
  416.   (let ((case-fold-search t))
  417.     (beginning-of-line 2)
  418.     (re-search-forward "^[ \t0-9]*end\\b[ \t]*[^ \t=(a-z]" nil 'move)
  419.     (goto-char (match-beginning 0))
  420.     (forward-line 1)))
  421.  
  422. (defun mark-fortran-subprogram ()
  423.   "Put mark at end of fortran subprogram, point at beginning. 
  424. The marks are pushed."
  425.   (interactive)
  426.   (end-of-fortran-subprogram)
  427.   (push-mark (point))
  428.   (beginning-of-fortran-subprogram))
  429.   
  430. (defun fortran-previous-statement ()
  431.   "Moves point to beginning of the previous fortran statement.
  432. Returns 'first-statement if that statement is the first
  433. non-comment Fortran statement in the file, and nil otherwise."
  434.   (interactive)
  435.   (let (not-first-statement continue-test)
  436.     (beginning-of-line)
  437.     (setq continue-test
  438.       (or (looking-at
  439.             (concat "[ \t]*" (regexp-quote (char-to-string
  440.                          fortran-continuation-char))))
  441.           (looking-at "     [^ 0\n]")))
  442.     (while (and (setq not-first-statement (= (forward-line -1) 0))
  443.         (or (looking-at comment-line-start-skip)
  444.             (looking-at "[ \t]*$")
  445.             (looking-at "     [^ 0\n]")
  446.             (looking-at (concat "[ \t]*"  comment-start-skip)))))
  447.     (cond ((and continue-test
  448.         (not not-first-statement))
  449.        (message "Incomplete continuation statement."))
  450.       (continue-test    
  451.        (fortran-previous-statement))
  452.       ((not not-first-statement)
  453.        'first-statement))))
  454.  
  455. (defun fortran-next-statement ()
  456.   "Moves point to beginning of the next fortran statement.
  457.  Returns 'last-statement if that statement is the last
  458.  non-comment Fortran statement in the file, and nil otherwise."
  459.   (interactive)
  460.   (let (not-last-statement)
  461.     (beginning-of-line)
  462.     (while (and (setq not-last-statement (= (forward-line 1) 0))
  463.          (or (looking-at comment-line-start-skip)
  464.              (looking-at "[ \t]*$")
  465.              (looking-at "     [^ 0\n]")
  466.              (looking-at (concat "[ \t]*"  comment-start-skip)))))
  467.     (if (not not-last-statement)
  468.      'last-statement)))
  469.  
  470. (defun fortran-indent-line ()
  471.   "Indents current fortran line based on its contents and on previous lines."
  472.   (interactive)
  473.   (let ((cfi (calculate-fortran-indent)))
  474.     (save-excursion
  475.       (beginning-of-line)
  476.       (if (or (not (= cfi (fortran-current-line-indentation)))
  477.           (and (re-search-forward "^[ \t]*[0-9]+" (+ (point) 4) t)
  478.            (not (fortran-line-number-indented-correctly-p))))
  479.       (fortran-indent-to-column cfi)
  480.     (beginning-of-line)
  481.     (if (re-search-forward comment-start-skip
  482.                    (save-excursion (end-of-line) (point)) 'move)
  483.         (fortran-indent-comment))))
  484.     ;; Never leave point in left margin.
  485.     (if (< (current-column) cfi)
  486.     (move-to-column cfi))))
  487.  
  488. (defun fortran-indent-subprogram ()
  489.   "Properly indents the Fortran subprogram which contains point."
  490.   (interactive)
  491.   (save-excursion
  492.     (mark-fortran-subprogram)
  493.     (message "Indenting subprogram...")
  494.     (indent-region (point) (mark) nil))
  495.   (message "Indenting subprogram...done."))
  496.  
  497. (defun calculate-fortran-indent ()
  498.   "Calculates the fortran indent column based on previous lines."
  499.   (let (icol first-statement (case-fold-search t))
  500.     (save-excursion
  501.       (setq first-statement (fortran-previous-statement))
  502.       (if first-statement
  503.       (setq icol fortran-minimum-statement-indent)
  504.     (progn
  505.       (if (= (point) (point-min))
  506.           (setq icol fortran-minimum-statement-indent)
  507.         (setq icol (fortran-current-line-indentation)))
  508.       (skip-chars-forward " \t0-9")
  509.       (cond ((looking-at "if[ \t]*(")
  510.          (if (or (looking-at ".*)[ \t]*then\\b[ \t]*[^ \t(=a-z0-9]")
  511.              (let (then-test)    ;multi-line if-then
  512.                (while (and (= (forward-line 1) 0) ;search forward for then
  513.                        (looking-at "     [^ 0]")
  514.                        (not (setq then-test (looking-at ".*then\\b[ \t]*[^ \t(=a-z0-9]")))))
  515.                then-test))
  516.              (setq icol (+ icol fortran-if-indent))))
  517.         ((looking-at "\\(else\\|elseif\\)\\b")
  518.          (setq icol (+ icol fortran-if-indent)))
  519.         ((looking-at "do\\b")
  520.          (setq icol (+ icol fortran-do-indent)))))))
  521.     (save-excursion
  522.       (beginning-of-line)
  523.       (cond ((looking-at "[ \t]*$"))
  524.         ((looking-at comment-line-start-skip)
  525.          (cond ((eq fortran-comment-indent-style 'relative)
  526.             (setq icol (+ icol fortran-comment-line-column)))
  527.            ((eq fortran-comment-indent-style 'fixed)
  528.             (setq icol fortran-comment-line-column))))
  529.         ((or (looking-at (concat "[ \t]*"
  530.                      (regexp-quote (char-to-string fortran-continuation-char))))
  531.          (looking-at "     [^ 0\n]"))
  532.          (setq icol (+ icol fortran-continuation-indent)))
  533.         (first-statement)
  534.         ((and fortran-check-all-num-for-matching-do
  535.           (looking-at "[ \t]*[0-9]+")
  536.           (fortran-check-for-matching-do))
  537.          (setq icol (- icol fortran-do-indent)))
  538.         (t
  539.          (skip-chars-forward " \t0-9")
  540.          (cond ((looking-at "end[ \t]*if\\b")
  541.             (setq icol (- icol fortran-if-indent)))
  542.            ((looking-at "\\(else\\|elseif\\)\\b")
  543.             (setq icol (- icol fortran-if-indent)))
  544.            ((and (looking-at "continue\\b")
  545.              (fortran-check-for-matching-do))
  546.             (setq icol (- icol fortran-do-indent)))
  547.            ((looking-at "end[ \t]*do\\b")
  548.             (setq icol (- icol fortran-do-indent)))
  549.            ((and (looking-at "end\\b[ \t]*[^ \t=(a-z]")
  550.              (not (= icol fortran-minimum-statement-indent)))
  551.              (message "Warning: `end' not in column %d.  Probably an unclosed block." fortran-minimum-statement-indent))))))
  552.     (max fortran-minimum-statement-indent icol)))
  553.  
  554. (defun fortran-current-line-indentation ()
  555.   "Indentation of current line, ignoring Fortran line number or continuation.
  556. This is the column position of the first non-whitespace character
  557. aside from the line number and/or column 5 line-continuation character.
  558. For comment lines, returns indentation of the first
  559. non-indentation text within the comment."
  560.   (save-excursion
  561.     (beginning-of-line)
  562.     (cond ((looking-at comment-line-start-skip)
  563.        (goto-char (match-end 0))
  564.        (skip-chars-forward
  565.          (if (stringp fortran-comment-indent-char)
  566.          fortran-comment-indent-char
  567.              (char-to-string fortran-comment-indent-char))))
  568.       ((looking-at "     [^ 0\n]")
  569.        (goto-char (match-end 0)))
  570.       (t
  571.        ;; Move past line number.
  572.        (move-to-column 5)))
  573.     ;; Move past whitespace.
  574.     (skip-chars-forward " \t")
  575.     (current-column)))
  576.  
  577. (defun fortran-indent-to-column (col)
  578.   "Indents current line with spaces to column COL.
  579. notes: 1) A non-zero/non-blank character in column 5 indicates a continuation
  580.           line, and this continuation character is retained on indentation;
  581.        2) If fortran-continuation-char is the first non-whitespace character,
  582.           this is a continuation line;
  583.        3) A non-continuation line which has a number as the first
  584.           non-whitespace character is a numbered line."
  585.   (save-excursion
  586.     (beginning-of-line)
  587.     (if (looking-at comment-line-start-skip)
  588.     (if fortran-comment-indent-style
  589.         (let ((char (if (stringp fortran-comment-indent-char)
  590.                 (aref fortran-comment-indent-char 0)
  591.                 fortran-comment-indent-char)))
  592.           (goto-char (match-end 0))
  593.           (delete-horizontal-regexp (concat " \t" (char-to-string char)))
  594.           (insert-char char (- col (current-column)))))
  595.       (if (looking-at "     [^ 0\n]")
  596.       (forward-char 6)
  597.     (delete-horizontal-space)
  598.     ;; Put line number in columns 0-4
  599.     ;; or put continuation character in column 5.
  600.     (cond ((eobp))
  601.           ((= (following-char) fortran-continuation-char)
  602.            (indent-to 5)
  603.            (forward-char 1))
  604.           ((looking-at "[0-9]+")
  605.            (let ((extra-space (- 5 (- (match-end 0) (point)))))
  606.          (if (< extra-space 0)
  607.              (message "Warning: line number exceeds 5-digit limit.")
  608.            (indent-to (min fortran-line-number-indent extra-space))))
  609.            (skip-chars-forward "0-9"))))
  610.       ;; Point is now after any continuation character or line number.
  611.       ;; Put body of statement where specified.
  612.       (delete-horizontal-space)
  613.       (indent-to col)
  614.       ;; Indent any comment following code on the same line.
  615.       (if (re-search-forward comment-start-skip
  616.                  (save-excursion (end-of-line) (point)) t)
  617.       (progn (goto-char (match-beginning 0))
  618.          (if (not (= (current-column) (fortran-comment-hook)))
  619.              (progn (delete-horizontal-space)
  620.                 (indent-to (fortran-comment-hook)))))))))
  621.  
  622. (defun fortran-line-number-indented-correctly-p ()
  623.   "Return t if current line's line number is correctly indente.
  624. Do not call if there is no line number."
  625.   (save-excursion
  626.     (beginning-of-line)
  627.     (skip-chars-forward " \t")
  628.     (and (<= (current-column) fortran-line-number-indent)
  629.      (or (= (current-column) fortran-line-number-indent)
  630.          (progn (skip-chars-forward "0-9")
  631.             (= (current-column) 5))))))
  632.  
  633. (defun fortran-check-for-matching-do ()
  634.   "When called from a numbered statement, returns t
  635.  if matching 'do' is found, and nil otherwise."
  636.   (let (charnum
  637.     (case-fold-search t))
  638.     (save-excursion
  639.       (beginning-of-line)
  640.       (if (looking-at "[ \t]*[0-9]+")
  641.       (progn
  642.         (skip-chars-forward " \t")
  643.         (skip-chars-forward "0") ;skip past leading zeros
  644.         (setq charnum (buffer-substring (point)
  645.                         (progn (skip-chars-forward "0-9")
  646.                            (point))))
  647.         (beginning-of-line)
  648.         (and (re-search-backward
  649.           (concat "\\(^[ \t0-9]*end\\b[ \t]*[^ \t=(a-z]\\)\\|\\(^[ \t0-9]*do[ \t]*0*"
  650.               charnum "\\b\\)\\|\\(^[ \t]*0*" charnum "\\b\\)")
  651.           nil t)
  652.          (looking-at (concat "^[ \t0-9]*do[ \t]*0*" charnum))))))))
  653.  
  654.  
  655.