You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
9.6 KiB
270 lines
9.6 KiB
;;; haskell-simple-indent.el --- Simple indentation module for Haskell Mode -*- lexical-binding: t -*- |
|
|
|
;; Copyright (C) 1998 Heribert Schuetz, Graeme E Moss |
|
|
|
;; Author: Heribert Schuetz <Heribert.Schuetz@informatik.uni-muenchen.de> |
|
;; Graeme E Moss <gem@cs.york.ac.uk> |
|
;; Keywords: indentation files Haskell |
|
|
|
;; This file is not part of GNU Emacs. |
|
|
|
;; This file is free software; you can redistribute it and/or modify |
|
;; it under the terms of the GNU General Public License as published by |
|
;; the Free Software Foundation; either version 3, or (at your option) |
|
;; any later version. |
|
|
|
;; This file is distributed in the hope that it will be useful, |
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
;; GNU General Public License for more details. |
|
|
|
;; You should have received a copy of the GNU General Public License |
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
;;; Commentary: |
|
|
|
;; Purpose: |
|
;; |
|
;; To support simple indentation of Haskell scripts. |
|
;; |
|
;; |
|
;; Installation: |
|
;; |
|
;; To bind TAB to the indentation command for all Haskell buffers, add |
|
;; this to .emacs: |
|
;; |
|
;; (add-hook 'haskell-mode-hook 'turn-on-haskell-simple-indent) |
|
;; |
|
;; Otherwise, call `turn-on-haskell-simple-indent'. |
|
;; |
|
;; |
|
;; Customisation: |
|
;; |
|
;; None supported. |
|
;; |
|
;; |
|
;; History: |
|
;; |
|
;; If you have any problems or suggestions, after consulting the list |
|
;; below, email gem@cs.york.ac.uk quoting the version of you are |
|
;; using, the version of Emacs you are using, and a small example of |
|
;; the problem or suggestion. |
|
;; |
|
;; Version 1.0: |
|
;; Brought over from Haskell mode v1.1. |
|
;; |
|
;; Present Limitations/Future Work (contributions are most welcome!): |
|
;; |
|
;; (None so far.) |
|
|
|
;;; Code: |
|
|
|
;; All functions/variables start with |
|
;; `(turn-(on/off)-)haskell-simple-indent'. |
|
|
|
(require 'haskell-mode) |
|
|
|
;;;###autoload |
|
(defgroup haskell-simple-indent nil |
|
"Simple Haskell indentation." |
|
:link '(custom-manual "(haskell-mode)Indentation") |
|
:group 'haskell |
|
:prefix "haskell-simple-indent-") |
|
|
|
;; Version. |
|
(defconst haskell-simple-indent-version "1.2" |
|
"`haskell-simple-indent' version number.") |
|
(defun haskell-simple-indent-version () |
|
"Echo the current version of `haskell-simple-indent' in the minibuffer." |
|
(interactive) |
|
(message "Using haskell-simple-indent version %s" |
|
haskell-simple-indent-version)) |
|
|
|
;; Partly stolen from `indent-relative' in indent.el: |
|
(defun haskell-simple-indent () |
|
"Space out to under next visible indent point. |
|
|
|
Indent points are positions of non-whitespace following |
|
whitespace in lines preceeding point. Example: |
|
|
|
func arg cx = when (isTrue) $ do |
|
print 42 |
|
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ |
|
|
|
A position is visible if it is to the left of the first |
|
non-whitespace (indentation) of every nonblank line between the |
|
position and the current line. If there is no visible indent |
|
point beyond the current column, position given by |
|
`indent-next-tab-stop' is used instead." |
|
(interactive) |
|
(let* ((start-column (or (save-excursion |
|
(back-to-indentation) |
|
(if (not (eolp)) |
|
(current-column))) |
|
(current-column))) |
|
(invisible-from nil) ; `nil' means infinity here |
|
(found) |
|
(indent)) |
|
(save-excursion |
|
;; Loop stops if there no more lines above this one or when has |
|
;; found a line starting at first column. |
|
(while (and (not found) |
|
(or (not invisible-from) |
|
(not (zerop invisible-from))) |
|
(zerop (forward-line -1))) |
|
;; Ignore empty lines. |
|
(if (not (looking-at "[ \t]*\n")) |
|
(let ((this-indentation (current-indentation))) |
|
;; Is this line so indented that it cannot have |
|
;; influence on indentation points? |
|
(if (or (not invisible-from) |
|
(< this-indentation invisible-from)) |
|
(if (> this-indentation start-column) |
|
(setq invisible-from this-indentation) |
|
(let ((end (line-end-position))) |
|
(move-to-column start-column) |
|
;; Is start-column inside a tab on this line? |
|
(if (> (current-column) start-column) |
|
(backward-char 1)) |
|
;; Skip to the end of non-whitespace. |
|
(skip-chars-forward "^ \t" end) |
|
;; Skip over whitespace. |
|
(skip-chars-forward " \t" end) |
|
;; Indentation point found if not at the end of |
|
;; line and if not covered by any line below |
|
;; this one. In that case use invisible-from. |
|
(setq indent (if (or (= (point) end) |
|
(and invisible-from |
|
(> (current-column) invisible-from))) |
|
invisible-from |
|
(current-column))) |
|
;; Signal that solution is found. |
|
(setq found t)))))))) |
|
|
|
|
|
(let ((opoint (point-marker))) |
|
;; Indent to the calculated indent or last know invisible-from |
|
;; or use tab-to-tab-stop. Try hard to keep cursor in the same |
|
;; place or move it to the indentation if it was before it. And |
|
;; keep content of the line intact. |
|
(setq indent (or indent |
|
invisible-from |
|
(if (fboundp 'indent-next-tab-stop) |
|
(indent-next-tab-stop start-column)) |
|
(let ((tabs tab-stop-list)) |
|
(while (and tabs (>= start-column (car tabs))) |
|
(setq tabs (cdr tabs))) |
|
(if tabs (car tabs))) |
|
(* (/ (+ start-column tab-width) tab-width) tab-width))) |
|
(indent-line-to indent) |
|
(if (> opoint (point)) |
|
(goto-char opoint)) |
|
(set-marker opoint nil)))) |
|
|
|
(defun haskell-simple-indent-backtab () |
|
"Indent backwards. Dual to `haskell-simple-indent'." |
|
(interactive) |
|
(let ((saved-column (or (save-excursion |
|
(back-to-indentation) |
|
(if (not (eolp)) |
|
(current-column))) |
|
(current-column))) |
|
(i 0) |
|
(x 0)) |
|
|
|
(save-excursion |
|
(back-to-indentation) |
|
(delete-region (line-beginning-position) (point))) |
|
(while (< (or (save-excursion |
|
(back-to-indentation) |
|
(if (not (eolp)) |
|
(current-column))) |
|
(current-column)) saved-column) |
|
(haskell-simple-indent) |
|
(setq i (+ i 1))) |
|
|
|
(save-excursion |
|
(back-to-indentation) |
|
(delete-region (line-beginning-position) (point))) |
|
(while (< x (- i 1)) |
|
(haskell-simple-indent) |
|
(setq x (+ x 1))))) |
|
|
|
(defun haskell-simple-indent-newline-same-col () |
|
"Make a newline and go to the same column as the current line." |
|
(interactive) |
|
(let ((start-end |
|
(save-excursion |
|
(let* ((start (line-beginning-position)) |
|
(end (progn (goto-char start) |
|
(search-forward-regexp |
|
"[^ ]" (line-end-position) t 1)))) |
|
(when end (cons start (1- end))))))) |
|
(if start-end |
|
(progn (newline) |
|
(insert (buffer-substring-no-properties |
|
(car start-end) (cdr start-end)))) |
|
(newline)))) |
|
|
|
(defun haskell-simple-indent-newline-indent () |
|
"Make a newline on the current column and indent on step." |
|
(interactive) |
|
(haskell-simple-indent-newline-same-col) |
|
(insert (make-string haskell-indent-spaces ? ))) |
|
|
|
(defun haskell-simple-indent-comment-indent-function () |
|
"Haskell version of `comment-indent-function'." |
|
;; This is required when filladapt is turned off. Without it, when |
|
;; filladapt is not used, comments which start in column zero |
|
;; cascade one character to the right |
|
(save-excursion |
|
(beginning-of-line) |
|
(let ((eol (line-end-position))) |
|
(and comment-start-skip |
|
(re-search-forward comment-start-skip eol t) |
|
(setq eol (match-beginning 0))) |
|
(goto-char eol) |
|
(skip-chars-backward " \t") |
|
(max comment-column (+ (current-column) (if (bolp) 0 1)))))) |
|
|
|
;;;###autoload |
|
(define-minor-mode haskell-simple-indent-mode |
|
"Simple Haskell indentation mode that uses simple heuristic. |
|
In this minor mode, `indent-for-tab-command' (bound to <tab> by |
|
default) will move the cursor to the next indent point in the |
|
previous nonblank line, whereas `haskell-simple-indent-backtab' |
|
\ (bound to <backtab> by default) will move the cursor the |
|
previous indent point. An indent point is a non-whitespace |
|
character following whitespace. |
|
|
|
Runs `haskell-simple-indent-hook' on activation." |
|
:lighter " Ind" |
|
:group 'haskell-simple-indent |
|
:keymap '(([backtab] . haskell-simple-indent-backtab)) |
|
(kill-local-variable 'comment-indent-function) |
|
(kill-local-variable 'indent-line-function) |
|
(when haskell-simple-indent-mode |
|
(set (make-local-variable 'comment-indent-function) #'haskell-simple-indent-comment-indent-function) |
|
(set (make-local-variable 'indent-line-function) 'haskell-simple-indent) |
|
(run-hooks 'haskell-simple-indent-hook))) |
|
|
|
;; The main functions. |
|
;;;###autoload |
|
(defun turn-on-haskell-simple-indent () |
|
"Turn on function `haskell-simple-indent-mode'." |
|
(interactive) |
|
(haskell-simple-indent-mode)) |
|
(make-obsolete 'turn-on-haskell-simple-indent |
|
'haskell-simple-indent-mode |
|
"2015-07-23") |
|
|
|
(defun turn-off-haskell-simple-indent () |
|
"Turn off function `haskell-simple-indent-mode'." |
|
(interactive) |
|
(haskell-simple-indent-mode 0)) |
|
|
|
;; Provide ourselves: |
|
|
|
(provide 'haskell-simple-indent) |
|
|
|
;;; haskell-simple-indent.el ends here
|
|
|