Fortran is a major language for scientific computation but a minor for other.
(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))
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\\|!--\\|!>\\)")
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))
(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))
(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!")))
(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))))
(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))))))
(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)))
(defun fortran-insert-percent ()
(interactive)
(unless (looking-back " ")
(insert " "))
(insert "% "))
(with-eval-after-load 'f90
(key-chord-define f90-mode-map ".." #'fortran-insert-percent))