Skip to content

Latest commit

 

History

History
348 lines (310 loc) · 10.1 KB

starter-kit-fortran.org

File metadata and controls

348 lines (310 loc) · 10.1 KB

Starter Kit Fortran

Starter Kit Fortran

Fortran is a major language for scientific computation but a minor for other.

Fortran configuration

(defun my-f90-config ()
  (modify-syntax-entry ?_ "w")
  (f90-backslash-not-special)
  (setq-local er/try-expand-list
              (append er/try-expand-list
                      '(f90-mark-block)))
  (evil-define-key 'normal f90-mode-map
    "J" 'my-f90-join-lines)
  (evil-define-key 'normal f90-mode-map
    "%" 'f-evil-jump-item))

(eval-after-load "f90"
  `(progn
     (require 'fortran)
     (require 'expand-region)
     (add-hook 'f90-mode-hook 'my-f90-config)))

(defun my-fortran-config ()
  (modify-syntax-entry ?_ "w")
  (evil-define-key 'normal fortran-mode-map
    "J" 'my-fortran-join-lines)
  (evil-define-key 'normal fortran-mode-map
    "%" 'f-evil-jump-item))

(with-eval-after-load 'fortran
  (require 'f90)
  (add-hook 'fortran-mode-hook 'my-fortran-config))

(defadvice f90-beginning-of-subprogram (before push-mark-before-fbos activate)
  (push-mark (point) t))
(defadvice f90-end-of-subprogram (before push-mark-before-feos activate)
  (push-mark (point) t))

Fortran indent

Default indentation for Fortran is not that friendly.

(setq fortran-do-indent 1)
(setq fortran-if-indent 1)
(setq fortran-structure-indent 1)
(setq fortran-continuation-indent 1)
(setq fortran-comment-indent-style 'relative)
(setq f90-do-indent 2)
(setq f90-if-indent 2)
(setq f90-structure-indent 2)
(setq f90-continuation-indent 2)
(setq f90-type-indent 2)

Don’t indent for some special statements.

(setq f90-directive-comment-re "\\(!hpf\\$\\|!\\$omp\\|!--\\|!>\\)")

Declaration align

Align variable declaration. Key binding is C-c C-i.

(defun my-f90-align-declare ()
  "Align declaration of Fortran variables. The declaration before align
should have \"::\" and \"!\". Declaration like these are valid:

integer :: i ! an integer
type(SomeType) :: atype ! object of SomeType"
  (interactive)
  (let ((begin (region-beginning))
        (end (region-end)))
    (indent-region begin end)
    (align-regexp begin end "\\(\\s-*\\)::" 1 1 nil)
    (align-regexp begin end "::\\(\\s-*\\)" 1 1 nil)
    (align-regexp begin end "\\(\\s-*\\)!" 1 1 nil)))

(defun f90-align-comment-or-regexp (arg)
  (interactive "P")
  (if arg
      (call-interactively 'align-regexp)
    (align-regexp (region-beginning)
                  (region-end)
                  "[\]a-zA-Z0-9_)\\.\"]\\(\\s-*\\)!"
                  1 1 nil)))

(defun f90-align-comment-buffer ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let (b0 b1)
      (while (re-search-forward
              "[ \t]*\\(type.*::\\|[^!]*\\(subroutine\\|function\\)\\)"
              nil t)
        (setq b0 (line-beginning-position))
        (f90-end-of-block)
        (setq b1 (line-end-position))
        (align-regexp b0 b1 "[\]a-zA-Z0-9_)\\.\"]\\(\\s-*\\)!" 1 1 nil)))))

(with-eval-after-load 'f90
  (define-key f90-mode-map (kbd "M-\\") 'f90-align-comment-or-regexp))

Fortran join lines

(defun my-f90-join-lines ()
  (interactive)
  (if (region-active-p)
      (let* ((beg (region-beginning))
             (end (region-end))
             (nlines (1- (count-lines beg end)))
             arg)
        (when (= beg (point))
          (setq arg t))
        (dotimes (var nlines)
          (f90-join-lines arg)))
    (f90-join-lines t)))

(defun my-fortran-join-lines ()
  (interactive)
  (fortran-join-line -1))

Down case Fortran buffer

(defun fortran-downcase-buffer ()
  "Down case all words in current buffer in `fortran-mode'. Let words in
comment and string untouched."
  (interactive)
  (if (eq major-mode 'fortran-mode)
      (save-excursion
        (goto-char (point-min))
        (while (not (eobp))
          (forward-word)
          (let ((sp (syntax-ppss)))
            (unless (or
                     (nth 3 sp)
                     (nth 4 sp))
              (downcase-word -1)))))
    (error "Current buffer is not fortran-mode!")))

F90 expand region

(defun f90-mark-block ()
  (interactive)
  (let (reach-beg reach-end)
    (setq reach-beg (f90-reach-beg-of-block))
    (if (region-active-p)
        (progn
          (exchange-point-and-mark)
          (setq reach-end (f90-reach-end-of-block))
          (exchange-point-and-mark))
      (setq reach-end (f90-reach-end-of-block)))
    (unless (region-active-p)
      (set-mark (point)))
    (cond ((and reach-beg reach-end)
           (exchange-point-and-mark)
           (f90-end-of-block)
           (exchange-point-and-mark)
           (f90-beginning-of-block))
          ((and reach-beg (not reach-end))
           (exchange-point-and-mark)
           (f90-end-or-next-block)
           (exchange-point-and-mark))
          ((and (not reach-beg) reach-end)
           (f90-beg-or-prev-block))
          (t
           (f90-beg-or-prev-block)
           (exchange-point-and-mark)
           (f90-end-or-next-block)
           (exchange-point-and-mark)))))

(defun f90-reach-beg-of-block ()
  (interactive)
  (and
   (looking-at
    (format "\\([a-zA-Z0-9_]+[ \t]*:[ \t]*\\)?%s" f90-blocks-re))
   (looking-back "^[ \t]*")))

(defun f90-reach-end-of-block ()
  (interactive)
  (and
   (looking-at "[ \t]*$")
   (looking-back
    (format "end[ \t]*%s\\([ \t]+[a-zA-Z0-9_]+\\)?" f90-blocks-re))))

(defun f90-beg-or-prev-block ()
  (interactive)
  (cond ((f90-reach-beg-of-block)
         (f90-beginning-of-block))
        ((save-excursion
           (beginning-of-line)
           (looking-at
            (format "[ \t]*\\([a-zA-Z0-9_]+[ \t]*:[ \t]*\\)?%s" f90-blocks-re)))
         (back-to-indentation))
        (t
         (f90-beginning-of-block))))

(defun f90-end-or-next-block ()
  (interactive)
  (cond ((f90-reach-end-of-block)
         (f90-end-of-block))
        ((save-excursion
           (beginning-of-line)
           (looking-at (format "[ \t]*end[ \t]*%s" f90-blocks-re)))
         (end-of-line))
        (t
         (f90-end-of-block))))

Fortran jump item

(defun f-beginning-of-subprogram ()
  (if (eq major-mode 'f90-mode)
      (progn
        (f90-beginning-of-subprogram)
        (back-to-indentation))
    (fortran-beginning-of-subprogram)
    (back-to-indentation)))

(defun f-end-of-subprogram ()
  (if (eq major-mode 'f90-mode)
      (f90-end-of-subprogram)
    (fortran-end-of-subprogram)
    (end-of-line 0)))

(defun f-line-match-block (line)
  (and
   (string-match (concat "\\`[ \t]*\\([a-zA-Z_]+:[ \t]*\\)?" f90-blocks-re) line)
   (not (string-match "type[ \t]*(.+)" line))))

(defun f-evil-jump-item (arg)
  (interactive "P")
  (push-mark (point) t)
  (let ((current-line
         (buffer-substring-no-properties
          (line-beginning-position)
          (line-end-position)))
        (current-char
         (buffer-substring-no-properties (point) (1+ (point))))
        (parens-depth
         (nth 0 (parse-partial-sexp (line-beginning-position) (point)))))
    (cond
     ;; jump to arg percent of current buffer
     (arg
      (evil-jump-item arg))
     ;; inside parens
     ((or
       (> parens-depth 0)
       (string-match "[\[({]" current-char))
      (evil-jump-item))
     ;; end of sub-program
     ((string-match
       "\\`[ \t]*\\(end *\\(function\\|subroutine\\|module\\|program\\)\\|end[ \t]*\\'\\)"
       current-line)
      (f-beginning-of-subprogram))
     ;; beginning of sub-program
     ((string-match
       "\\`[ \t]*\\(\\([a-zA-Z_]+[ \t]+\\)?\\(function\\|subroutine\\)\\|\\(module\\|program\\)\\)"
       current-line)
      (f-end-of-subprogram))
     ;; end of block
     ((string-match f90-end-block-re current-line)
      (f90-beginning-of-block))
     ;; one line if statement
     ((and
       (string-match "\\`[ \t]*if" current-line)
       (not (string-match "\\_<then\\_>" current-line)))
      (evil-jump-item))
     ;; beginning or else like block
     ((or
       (f-line-match-block current-line)
       (string-match (concat "\\`[ \t]*" f90-else-like-re) current-line))
      (catch 'goto-else-or-end
        (while t
          (forward-line 1)
          (let ((current-line
                 (buffer-substring-no-properties
                  (line-beginning-position)
                  (line-end-position))))
            (cond
             ;; beginning of inside block
             ((f-line-match-block current-line)
              (if (and
                   (string-match "\\`[ \t]*if" current-line)
                   (not (string-match "\\_<then\\_>" current-line)))
                  (forward-line 1)
                (f90-end-of-block)))
             ;; else like statement
             ((string-match
               (concat "\\`[ \t]*" f90-else-like-re)
               current-line)
              (back-to-indentation)
              (throw 'goto-else-or-end t))
             ;; end of block
             ((string-match f90-end-block-re current-line)
              (end-of-line)
              (throw 'goto-else-or-end t)))))))
     ;; comment or blank line
     ((string-match "\\`\\(c\\|[ \t]*[!\n]\\)" current-line)
      (f90-beginning-of-block)
      (back-to-indentation))
     ;; otherwise run `evil-jump-item' or go to beginning of block
     (t
      (unless (ignore-errors (evil-jump-item))
        (f90-beginning-of-block))))))

Hack bounds-of-thing-at-point for Fortran

(defadvice bounds-of-thing-at-point (around bnds-of-f90-subprogram activate)
  (setq ad-return-value
        (if (equal thing 'f90-subprogram)
            (save-excursion
              (cons
               (progn
                 (f90-beginning-of-subprogram)
                 (point))
               (progn
                 (f90-end-of-subprogram)
                 (point))))
          ad-do-it)))

Fortran insert percent token

(defun fortran-insert-percent ()
  (interactive)
  (unless (looking-back " ")
    (insert " "))
  (insert "% "))

(with-eval-after-load 'f90
  (key-chord-define f90-mode-map ".." #'fortran-insert-percent))