+;; Haskell major mode
+;; (c) Copyright, Richard McPhee et al.
+;; University of Glasgow, February 1993
+
+
+
+;; if .hs is not recognised then put the extension in auto-mode-list
+
+(if (assoc "\\.hs" auto-mode-alist)
+ nil
+ (nconc auto-mode-alist '(("\\.hs". haskell-mode))))
+
+(if (assoc "\\.hi" auto-mode-alist)
+ nil
+ (nconc auto-mode-alist '(("\\.hi". haskell-mode))))
+
+(if (assoc "\\.gs" auto-mode-alist)
+ nil
+ (nconc auto-mode-alist '(("\\.gs". haskell-mode))))
+
+(defvar haskell-mode-syntax-table nil
+ "Syntax table for haskell-mode buffers.")
+
+(defvar haskell-mode-abbrev-table nil
+ "Abbrev table for haskell-mode buffers.")
+
+(defvar haskell-mode-map (make-sparse-keymap)
+ "Keymap for haskell-mode-buffers.")
+
+
+
+;;; Here are the keymaps used in haskell-mode
+
+(define-key haskell-mode-map "\M-;" 'haskell-insert-comment)
+(define-key haskell-mode-map "\C-c=" 'haskell-insert-concat)
+(define-key haskell-mode-map "\C-c;" 'set-haskell-comment-column)
+(define-key haskell-mode-map "\C-c+" 'set-haskell-concat-column)
+(define-key haskell-mode-map "\C-cn" 'set-haskell-indent-offset)
+(define-key haskell-mode-map "\C-cl" 'set-haskell-list-offset)
+(define-key haskell-mode-map "\C-ci" 'set-haskell-if-offset)
+(define-key haskell-mode-map "\C-ce" 'set-haskell-let-offset)
+(define-key haskell-mode-map "\C-cc" 'set-haskell-case-offset)
+(define-key haskell-mode-map "\C-ct" 'set-haskell-then-offset)
+(define-key haskell-mode-map "\C-co" 'set-haskell-comp-offset)
+(define-key haskell-mode-map "\C-cw" 'set-haskell-where-offset)
+(define-key haskell-mode-map "\C-cg" 'goto-line)
+(define-key haskell-mode-map "\C-j" 'haskell-reindent-then-newline-and-indent)
+(define-key haskell-mode-map "\t" 'haskell-indent-line)
+(define-key haskell-mode-map "}" 'electric-haskell-brace)
+(define-key haskell-mode-map "]" 'electric-haskell-brace)
+(define-key haskell-mode-map ")" 'haskell-insert-round-paren)
+(define-key haskell-mode-map "\C-cr" 'haskell-indent-region)
+(define-key haskell-mode-map "\C-cf" 'haskell-further-indent)
+(define-key haskell-mode-map "\C-cb" 'haskell-lesser-indent)
+(define-key haskell-mode-map "\177" 'backward-delete-char-untabify)
+(define-key haskell-mode-map "\M-\C-\177" 'delete-horizontal-space)
+
+(defun haskell-set-local-vars ()
+ "Set the local variables for haskell-mode."
+ (kill-all-local-variables)
+
+ (setq indent-line-function 'haskell-indent-line)
+
+ (make-local-variable 'haskell-std-list-indent)
+ ;;Non-nil means indent to the offset, 'haskell-list-offset' in a bracket rather than
+ ;; moving to the next word afer a function name
+ (setq haskell-std-list-indent t)
+
+ (make-local-variable 'haskell-nest-ifs)
+ ;;Non-nil means that 'if' statements are nested ie. lined up with `if' not `else'.
+ (setq haskell-nest-ifs nil)
+
+ (make-local-variable 'haskell-align-else-with-then)
+ ;;Non-nil means align an `else' under it's corresponding `then'
+ (setq haskell-align-else-with-then nil)
+
+
+ ;;The local vars for 'where' indentation
+
+ (make-local-variable 'haskell-align-where-with-eq)
+ ;;Non-nil means align a 'where' under it's corresponding equals sign
+ (setq haskell-align-where-with-eq t)
+
+ (make-local-variable 'haskell-align-where-after-eq)
+ ;;Non-nil means align a 'where' after it's corresponding equals sign
+ (setq haskell-align-where-after-eq nil)
+
+ (make-local-variable 'haskell-std-indent-where)
+ ;;put the 'where' the standard offset ie. 'haskell-indent-offset'
+ (setq haskell-std-indent-where nil)
+
+
+ (make-local-variable 'haskell-always-fixup-comment-space)
+ ;;Non-nil means always insert a (single) space after a comment, even
+ ;; if there is more or less than one.
+ (setq haskell-always-fixup-comment-space t)
+
+
+ (make-local-variable 'haskell-indent-offset)
+ ;;Extra indentation for a line continued after a keyword.
+ (setq haskell-indent-offset 4)
+
+ (make-local-variable 'haskell-list-offset)
+ ;;Extra indentation for continuing a list.
+ (setq haskell-list-offset 4)
+
+ (make-local-variable 'haskell-comp-offset)
+ ;;Extra indentation for a list comprehension.
+ (setq haskell-comp-offset 4)
+
+ (make-local-variable 'haskell-case-offset)
+ (setq haskell-case-offset 4)
+
+ (make-local-variable 'haskell-where-offset)
+ (setq haskell-where-offset 4)
+
+ (make-local-variable 'haskell-let-offset)
+ (setq haskell-let-offset 4)
+
+ (make-local-variable 'haskell-then-offset)
+ (setq haskell-then-offset 0)
+
+ (make-local-variable 'haskell-if-offset)
+ (setq haskell-if-offset 4)
+
+ (make-local-variable 'haskell-comment-column)
+ (setq haskell-comment-column 35)
+
+ (make-local-variable 'haskell-concat-column)
+ (setq haskell-concat-column 69)
+
+ (make-local-variable 'haskell-where-threshold)
+ (setq haskell-where-threshold 35)
+
+ (make-local-variable 'line-comment)
+ (setq line-comment "-- ")
+
+ (make-local-variable 'haskell-indent-style)
+ (setq haskell-indent-style "none"))
+
+
+(defun haskell-set-syntax-table ()
+ "Set the syntax table for Haskell-mode."
+ (setq haskell-mode-syntax-table (make-syntax-table))
+ (set-syntax-table haskell-mode-syntax-table)
+ (modify-syntax-entry ?\" "\"")
+ (modify-syntax-entry ?\\ "\\")
+ (modify-syntax-entry ?\' "w")
+ (modify-syntax-entry ?_ "w")
+ (modify-syntax-entry ?# "_")
+ (modify-syntax-entry ?$ "_")
+ (modify-syntax-entry ?% "_")
+ (modify-syntax-entry ?: "_")
+ (modify-syntax-entry ?? "_")
+ (modify-syntax-entry ?@ "_")
+ (modify-syntax-entry ?! "_")
+ (modify-syntax-entry ?^ "_")
+ (modify-syntax-entry ?~ "_")
+ (modify-syntax-entry ?- "_ 12")
+ (modify-syntax-entry ?\n ">")
+ (modify-syntax-entry ?{ "(}")
+ (modify-syntax-entry ?} "){")
+ (set-syntax-table haskell-mode-syntax-table))
+
+
+
+(defun haskell-mode ()
+ "Major mode for editing Haskell code.
+Linefeed reindents current line, takes newline and indents.
+Tab indents current line for Haskell code.
+Functions are seperated by blank lines.
+Delete converts tabs to spaces as it moves back.
+\\{haskell-mode-map}
+Variables controlling indentation style:
+ haskell-indent-offset
+ Standard extra indentation for continuing Haskell
+ code under the scope of an expression. The default is 4.
+
+ haskell-list-offset
+ Extra indentation for indenting in a list. Used if variable
+ haskell-std-list-indent is non-nil. The default is 4.
+
+ haskell-comp-offset
+ Extra indentation for continuing a list comprehension.
+ The default is 4.
+
+ haskell-case-offset
+ Standard extra indentation for continuing Haskell
+ code under the scope of an expression. The default is 4.
+
+ haskell-where-offset
+ Standard extra indentation for continuing Haskell
+ code under the scope of a `where'. The default is 4.
+
+ haskell-let-offset
+ Standard extra indentation for continuing Haskell
+ code under the scope of a `let'. The default is 4.
+
+ haskell-then-offset
+ Standard extra indentation for a `then' beyond
+ its corresponding `if'. The default is 0.
+
+ haskell-if-offset
+ Standard extra indentation for continuing Haskell
+ code under the scope of an `if'. The default is 4.
+
+ haskell-comment-column
+ Column to which line comments `--' will be inserted.
+ The default is 35.
+
+ haskell-concat-column
+ Column to which concatenation operator `++' will be inserted.
+ The default is 69.
+
+ haskell-where-threshold
+ Column beyond which a `where' will be indented to the
+ start of a line (to avoid spilling over lines).
+ The default is 35.
+
+ set-haskell-indent-offset (C-c i)
+ Changes the default value of the local variable,
+ haskell-indent-offset. May be a number from 0-10.
+
+ set-haskell-list-indent (C-c l)
+ Change the value of the local variable,
+ haskell-list-offset. May be a number from 0-100.
+
+ set-haskell-comment-column (C-x ;)
+ Changes the value of the local variable,
+ haskell-comment-column. May be any number from 0-100."
+
+ (interactive)
+ (haskell-set-local-vars)
+ (haskell-set-syntax-table)
+ (use-local-map haskell-mode-map)
+ (setq major-mode 'haskell-mode)
+ (setq mode-name "Haskell")
+ (define-abbrev-table 'haskell-mode-abbrev-table ()))
+
+
+
+
+;;; Returns the indentation column for a comment on this line.
+;;; The point is positioned at the last char of any code on the line.
+
+(defun haskell-comment-indent ()
+ "Returns the indentation for a comment on the given line.
+If the line has code on it or the point is not at the beginning of the line,
+then indent to indent-column.
+Otherwise, don't indent."
+ (cond ((or (haskell-code-on-linep)
+ (not (bolp)))
+ ;;There is code before the haskell-comment-column
+ ;; or not at the beginning of the line
+ ;;Return the largest of
+ ;; the current column +1 and the haskell-comment-column
+ (max (1+ (current-column))
+ haskell-comment-column))
+ (t
+ ;;Otherwise, return 0
+ 0)))
+
+
+
+;;; Returns whether a comment is on the current line
+;;; Search from bol, and beware of "--", {-- etc!
+;;; DOES NOT RECOGNISE {- COMMENTS YET or -- within a string
+
+(defun haskell-comment-on-linep ()
+ "Returns the truth value of whether there is a '--' comment on the current line."
+ (save-excursion
+ (beginning-of-line)
+ (looking-at ".*--")))
+
+
+;;; This doesn't account for comments '{-'. Test explicitly if you use this function!
+
+(defun haskell-code-on-linep ()
+ "Returns a truth value as to whether there is code on the current line."
+ (save-excursion
+ (beginning-of-line)
+ (not
+ ;; Code on line if not looking at a comment directly
+ ;; and the line is not blank
+ (or
+ (looking-at "^[ \t]*--")
+ (looking-at "^[ \t]*$")))))
+
+
+;;; Insert a Haskell "--" comment on the current line.
+;;; Move to the comment position if there's already a comment here.
+;;; Otherwise, the comment is inserted either at the comment column
+;;; or one column after the last non-space character, whichever is further
+;;; to the right.
+;;; This function is executed by M-;
+
+(defun haskell-insert-comment ()
+ "Inserts a '--' comment on the given line."
+ (interactive)
+ (cond ((haskell-comment-on-linep)
+ ;;There is a comment on the line
+ ;;Just reindent existing comment
+ (haskell-reindent-comment))
+ (t
+ (if (haskell-code-on-linep)
+ ;;There is code on the line
+ ;; and guarenteed that a comment
+ ;; does not already exist.
+ ;;Move to the last nonspace char
+ ;; (there may be spaces after the last char)
+ (progn
+ (end-of-line)
+ (skip-chars-backward " \t")))
+ ;;Indent to required level
+ ;; and insert the line comment '--'
+ (indent-to (haskell-comment-indent))
+ (insert line-comment))))
+
+
+;;; Reindents a comment.
+;;; The comment is indented according to the normal rules.
+;;; Skips over ---- and following spaces or tabs
+
+(defun haskell-reindent-comment ()
+ "Indents a comment on a line to keep it at haskell-comment-column,
+if possible.
+It is guaranteed that a comment exists on the current line."
+ (beginning-of-line)
+ ;;Go back to beginning of comment
+ (re-search-forward "--")
+ (forward-char -2)
+ ;;Delete all spaces and reindent to
+ ;; the correct location.
+ (delete-horizontal-space)
+ (indent-to (haskell-comment-indent))
+ ;;Move past the comment and insert
+ ;; only one space between it and the text.
+ ;;Leave point just after comment.
+ (skip-chars-forward "- \t")
+ (if haskell-always-fixup-comment-space
+ (progn
+ (fixup-whitespace)
+ (forward-char 1))))
+
+
+
+;;; Inserts a haskell concatenation operator, `++', at the
+;;; column dictated by haskell-concat-column
+
+(defun haskell-insert-concat()
+ "Inserts a `++' operator on the given line."
+ (interactive)
+ (end-of-line)
+ (skip-chars-backward " \t")
+ ;;Indent to required level
+ ;; and insert the concat operator `++'
+ (indent-to (haskell-concat-indent))
+ (insert "++"))
+
+
+
+;;; Returns the indentation column for a concatenation operator on this line.
+;;; The point is positioned at the last char of any code on the line.
+
+(defun haskell-concat-indent ()
+ "Returns the indentation for a concat operator on the given line."
+ (max (1+ (current-column))
+ haskell-concat-column))
+
+
+
+;;; Returns the indentation of the current line of haskell code.
+;;; A blank line has ZERO indentation
+
+(defun haskell-current-indentation ()
+ "Returns the indentation for the current haskell line. A blank line has
+indentation zero."
+ (save-excursion
+ (beginning-of-line)
+ (if (looking-at "^[ \t]*$")
+ ;;The line is empty
+ ;; so the indentation is zero
+ 0
+ ;;Otherwise find the normal value of indentation
+ (current-indentation))))
+
+
+
+;;; Returns the indentation of the previous line of haskell code.
+;;; A blank line has ZERO indentation
+
+(defun haskell-previous-indentation ()
+ "Returns the previous line's indentation as Haskell indentation."
+ (save-excursion
+ (if (not (bobp))
+ ;;Not at the start of the buffer
+ ;; so get the previous lines indentation
+ (progn
+ (forward-line -1)
+ (haskell-current-indentation))
+ ;;We are at the start of buffer
+ ;;There is no previous line; Indent is zero
+ 0)))
+
+
+
+;;; Move back to the last line which is aligned in the left column.
+;;; Ignores comments and blank lines.
+;;; The point is left at the beginning of the line.
+
+(defun haskell-back-to-zero-indent ()
+ "Moves point to last line which has zero as indentation."
+ ;;Not at the beginning of buffer.
+ ;;Continue to go to the previous line until
+ ;; we find a line whose indentation is non-zero.
+ ;;Blank lines and lines containing only comments
+ ;; are ignored.
+ (beginning-of-line)
+ (while (and
+ (or (not (zerop (haskell-current-indentation)))
+ (looking-at "^[ \t]*\\($\\|--\\)"))
+ (not (bobp)))
+ (haskell-backward-to-noncomment)
+ (beginning-of-line)))
+
+
+
+;;; Find the last symbol, usually an equality.
+
+;;; Note: we check for "=" as a complete WORD (and ignore
+;;; comments) when searching for this. Ie. an `=' may be
+;;; surrounded only by a letter, digit, or whitespace .
+;;; Strings are not considered.
+;;; Don't go beyond the first character in the (possibly narrowed) buffer.
+;;; From the beginning of the line,
+;;; find the comment position (or end-of-line)
+;;; search forward to this position, looking for a "where"
+;;; If one's found, then search forward for "\b=\b"
+;;; If there's no equality sign then
+;;; search forward from the start of the line for an equals
+;;; Otherwise we found it.
+;;; If there's no where then search forward for an equals, as above.
+
+(defun haskell-back-to-symbol (exp)
+ "Goes backward from point until a symbol, EXP, is found.
+The point is left at the first symbol matching the context
+of the haskell code."
+ (let* ((found nil)
+ (symbol (concat "[ \ta-z0-9A-Z]" exp "[ \t\na-z0-9A-Z]"))
+ eol-limit
+ bol-limit
+ (zero-indent (save-excursion
+ (haskell-back-to-zero-indent)
+ (point)))
+ (initial-depth (car (parse-partial-sexp
+ (point)
+ zero-indent))))
+
+ (while (and (not found)
+ (> (point) zero-indent))
+ ;;Not found and point > point min
+ ;;Record the limit of search for the beginning and
+ ;; end of the line.
+ (setq eol-limit (point))
+ (beginning-of-line)
+ (setq bol-limit (point))
+ (goto-char eol-limit)
+ (re-search-backward "\\bwhere\\b" bol-limit 't)
+ ;;Search back from the end of the line
+ ;; to find the most recent 'where'.
+
+ (cond ((and (re-search-backward symbol bol-limit 't)
+ (= initial-depth
+ (car (parse-partial-sexp
+ (point)
+ zero-indent))))
+ ;;Found a symbol sign surrounded by
+ ;; a letter, digit or space only, or at the
+ ;; beginning of the buffer and they are at
+ ;; the same depth level
+ (setq found 't))
+ ((and (re-search-backward symbol bol-limit 't)
+ (zerop
+ (car (parse-partial-sexp
+ (point)
+ zero-indent))))
+ ;; Found a symbol and it is not in any parens
+ (setq found 't))
+ ;;Otherwise, go back a line.
+ (t (haskell-backward-to-noncomment))))
+ (if found
+ (forward-char 1))))
+
+
+;;; Goes back to the last keyword. The point is left at the
+;;; beginning of the keyword.
+;;; The words recognised are:
+;;; `case',`of',`where',`let',`in',`if',`then',`else'
+
+(defun haskell-back-to-keyword ()
+ "Goes backward from point until a keyword is found.
+The point is left after the first keyword."
+ (let* ((found nil)
+ eol-limit
+ bol-limit
+ (zero-indent (save-excursion
+ (haskell-back-to-zero-indent)
+ (point)))
+ (initial-depth (car (parse-partial-sexp
+ (point)
+ zero-indent))))
+
+ (while (and (not found)
+ (>= (point) zero-indent))
+ ;;Not found and point > point min
+ ;;Go back past any comment.
+ ;;Record the limit of search for the beginning and
+ ;; end of the line.
+ (setq eol-limit (point))
+ (beginning-of-line)
+ (setq bol-limit (point))
+ (goto-char eol-limit)
+ (if (and (re-search-backward
+ "\\b\\(case\\|of\\|where\\|let\\|in\\|if\\|then\\|else\\)\\b"
+ bol-limit 't)
+ (= initial-depth
+ (car (parse-partial-sexp
+ (point)
+ zero-indent))))
+ ;;Found a keyword and it is at the same level as the initial position
+ (progn
+ (setq found 't)
+ (forward-word 1))
+ ;;Otherwise, go back a line.
+ (haskell-backward-to-noncomment)))))
+
+
+
+;;; Returns the end of line (point) of the current line, excluding any
+;;; line comments on it.
+
+(defun haskell-eol ()
+ "Returns the end (point) of the current line, excluding any line comments."
+ (save-excursion
+ (end-of-line)
+ (let ((eol-limit (point)))
+ (beginning-of-line)
+ (if (search-forward "--" eol-limit 'move-to-eol)
+ ;;Found a '--'
+ ;;So move to the beginning of the comment
+ ;;If fail then move to end of line
+ (forward-char -2)))
+ (point)))
+
+
+
+;;; Returns whether or not the current line contains an equality outwith a
+;;; comment. The equality may only be surrounded by a letter, digit or
+;;; whitespace.
+
+(defun haskell-looking-at-eqp ()
+ "Returns whether or not the current line contains an equality outwith a
+comment."
+ (save-excursion
+ (beginning-of-line)
+ (re-search-forward "[ \ta-z0-9A-Z]=[ \t\na-z0-9A-Z]" (1+ (haskell-eol)) 't)))
+
+
+;;; This function does not require all keywords, just those which
+;;; may have a bracket before them.
+(defun haskell-looking-at-keywordp ()
+ "Returns whether or not there is a keyword after the point outwith a
+comment."
+ (save-excursion
+ (re-search-forward
+ "\\(\\(=>\\|=\\|++\\|->\\|<-\\|::\\)\\|\\b\\(case\\|of\\|if\\|then\\|else\\|let\\|in\\)\\b\\)"
+ (haskell-eol) 't)))
+
+
+;;; This function returns whether or not there is a keyword contained in
+;;; the region START END. START < END.
+
+(defun haskell-keyword-in-regionp (start end)
+ "Returns whether or not there is a keyword between START and END."
+ (save-excursion
+ (goto-char start)
+ (let ((found nil)
+ (eol-limit (haskell-eol)))
+ (while (and (not found) (< (point) end))
+ (if (> eol-limit end)
+ (setq eol-limit end))
+ (if (re-search-forward
+ "\\b\\(case\\|of\\|if\\|then\\|else\\|let\\|in\\)\\b"
+ eol-limit 'move)
+ (setq found t)
+ ;;Otherwise, have not found a keyword. Now at haskell-eol.
+ (if (< (point) end)
+ ;;We still have an area to search
+ ;; so go forward one line
+ (progn
+ (beginning-of-line)
+ (forward-line 1)
+ (setq eol-limit (haskell-eol))))))
+ ;;found is `t' or point >= end
+ found)))
+
+
+;;; Goes back to the last line which is not entirely commented out.
+;;; The point is left just before the comment.
+
+(defun haskell-backward-to-noncomment ()
+ "Sets the point to the last char on the line of Haskell code before a comment."
+ (let ((comment 't)
+ (limit (point-min)))
+ (while (and comment (> (point) limit))
+ ;; comment is true and point > limit
+ (beginning-of-line)
+ (if (< (forward-line -1) 0)
+ ;;This was the first line in the buffer
+ (setq comment nil)
+ ;;Otherwise, this was not the first line
+ (if (not (looking-at "^[ \t]*\\($\\|--\\)"))
+ ;;There is not a comment at the beginning of the line
+ ;; and the line is not blank
+ (progn
+ ;;The line is either blank or has code on it.
+ (setq comment nil)
+ (goto-char (haskell-eol))))))
+
+ ;;return point
+ (point)))
+
+
+
+;;; Indents a region (by applying "tab" to each line).
+;;; The marker upper-marker is set to the end of the region.
+;;; We indent from the beginning of the region to this marker.
+;;; Implements C-c r.
+
+(defun haskell-indent-region ()
+ "Indents the region between the point and mark."
+ (interactive)
+ (let ((lower-limit (min (point) (mark)))
+ (upper-limit (max (point) (mark))))
+ (indent-region lower-limit upper-limit 'nil)))
+
+
+
+;;; Implements TAB.
+;;; This actually indents a line.
+;;; Eventually it will handle a line split at any point,
+
+(defun haskell-indent-line ()
+ "Indent current line as Haskell code.
+Keeps the point at the same position on the line unless the
+point is less then the current indentation, in which case the
+point is moved to the first char."
+ (interactive)
+ (save-excursion
+ (let ((indent (haskell-calculate-indentation)))
+ (beginning-of-line)
+ (delete-horizontal-space)
+ ;;Kill any spaces that may preceed the code
+ ;; and reindent to the correct level.
+ (indent-to indent)))
+ (if (< (current-column) (current-indentation))
+ ;;The point is in the indentation
+ ;; so move to the first char on the line
+ (move-to-column (current-indentation))))
+
+
+
+;;; This is the haskell version of the Emacs function
+;;; reindent-then-newline-and-indent. It was necessary
+;;; to write this because the Emacs version has the
+;;; terrible property of deleting whitespace BEFORE
+;;; reindenting the original line.
+
+(defun haskell-reindent-then-newline-and-indent ()
+ "Reidents the current line of Haskell code then takes a
+newline and indents this new line."
+ (interactive)
+ (skip-chars-backward " \t")
+ (haskell-indent-line)
+ (newline)
+ (delete-horizontal-space)
+ (haskell-indent-line))
+
+
+
+;;; Returns whether the first word of the last line with zero indentation
+;;; is the same as the first word of the current line.
+;;; This function is based on the (reasonable?) assumption that
+;;; a function definition occurs on the left hand margin.
+;;; This is not quit reasonable since recusive functions are not
+;;; recognised.
+
+(defun haskell-continued-fn-defp ()
+ "Returns whether the first word on the last line with zero indentation
+matches the first word on the current line."
+ (save-excursion
+ (beginning-of-line)
+ (skip-chars-forward " \t")
+ ;;Goto the first non space char
+ (haskell-word-eq (point)
+ (save-excursion
+ (forward-line -1)
+ (haskell-back-to-zero-indent)
+ (point)))))
+
+
+;;; Returns whether two words are the same.
+;;; The beginning of both words are given as their
+;;; respective points in the buffer.
+
+(defun haskell-word-eq (current-pos previous-pos)
+ (let ((OK 't))
+ (goto-char previous-pos)
+ ;;We shall compare the two words starting
+ ;; at previous-pos and current-pos.
+ (while (and OK (looking-at "\\S-"))
+ ;;OK and looking at a word constituent
+ (if (eq (char-after current-pos)
+ (char-after previous-pos))
+ ;;The two chars are the same
+ (progn
+ ;;Increment the two postions
+ ;; and update location of point
+ (setq current-pos (1+ current-pos))
+ (setq previous-pos (1+ previous-pos))
+ (goto-char previous-pos))
+ ;;The two chars are different
+ ;; so set OK to be false
+ (setq OK 'nil)))
+
+ ;;Return the value of OK
+ OK))
+
+
+
+
+;;; This function returns the column of the last unbalanced
+;;; expression.
+;;; It is called when an keyword is found. The point is
+;;; initially placed before the corresponding keyword.
+;;; The function looks at every word to see if it is a
+;;; `let' or `in'. Each word must be outwith a comment.
+
+(defun haskell-last-unbalanced-key-column (open close)
+ "Returns the column of the last unbalanced keyword, open."
+ (save-excursion
+ (let ((original-pos (point))
+ (bol-limit (save-excursion
+ (beginning-of-line)
+ (setq bol-limit (point))))
+ (depth 1))
+ (setq open (concat "\\b" open "\\b"))
+ (setq close (concat "\\b" close "\\b"))
+ (while (and
+ (> depth 0)
+ (> (point) (point-min)))
+ (forward-word -1)
+ (if (< (point) bol-limit)
+ ;;Moved past the beginning of line limit
+ ;; so go back to the previous line past
+ ;; any comments.
+ (progn
+ (goto-char original-pos)
+ (haskell-backward-to-noncomment)
+ (setq original-pos (point))
+ (setq bol-limit (save-excursion
+ (beginning-of-line)
+ (point))))
+ ;;Otherwise, still on the same line
+ (if (looking-at open)
+ ;;This word is an open keyword
+ (setq depth (1- depth))
+ ;;Otherwise,
+ (if (looking-at close)
+ ;;This word is a close keyword
+ (setq depth (1+ depth))))))
+
+ (if (string= open "\\bif\\b")
+ ;;The argument is `if'
+ (if (not (save-excursion (skip-chars-backward " \t") (bolp)))
+ ;;There is something before the `if'
+ (if (and (save-excursion
+ (forward-word -1)
+ (looking-at "\\belse\\b"))
+ (not haskell-nest-ifs))
+ ;;There is an `else' before the 'if'
+ (forward-word -1))))
+
+
+ (current-column))))
+
+
+
+;;; Return the indentation for a line given that we expect a `where'.
+;;; The point lies on the corresponding symbol
+;;; that the `where' scopes over.
+
+(defun haskell-indent-where ()
+ "Return the indentation for a line, given that we expect a `where'
+clause."
+ (let ((symbol (if (looking-at "=")
+ "="
+ "->")))
+
+ (cond ((or haskell-std-indent-where
+ (> (current-column) haskell-where-threshold))
+ ;;Set indentation as the sum of the previous
+ ;; line's layout column and the standard offset
+ ;; (ie. 'haskell-where-offset)
+ (save-excursion
+ (beginning-of-line)
+ (cond ((looking-at (concat "^[ \t]*" symbol))
+ ;;The line starts with the symbol
+ (setq indent (current-indentation)))
+ ((looking-at "^[ \t]*where\\b")
+ ;;The line starts with a 'where'
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (+ (current-column) haskell-where-offset)))
+ (t
+ ;;The line begins on the layout column
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset))))))
+ ((or haskell-align-where-with-eq
+ haskell-align-where-after-eq)
+ (if (looking-at (concat symbol "[ \t]*$"))
+ ;;The symbol is at the end of the line
+ (setq indent (+ (current-indentation)
+ haskell-where-offset))
+ (save-excursion
+ ;;Set the indentation as required
+ (if haskell-align-where-after-eq
+ (skip-chars-forward (concat symbol " \t")))
+ (setq indent (current-column))))))))
+
+
+
+;;; Calculates the indentation for the current line.
+;;; When we come here, we are in a line which we want to indent.
+;;; We should leave the point at the same relative position it
+;;; was in before we called the function, that is, if a line
+;;; is already correctly indented, nothing happens!
+
+;;; The main problems are handling "where" definitions
+;;; and the syntax of expressions when these are continued
+;;; over multiple lines (e.g. tuples, lists, or just plain
+;;; bracketed expressions). Watch out for let ... in, too!
+
+;;; For example, think about the following tricky cases:
+
+;;; f x = x + <NL>
+
+;;; f x = [ x + y, <NL>
+
+;;; f x = [ <NL>
+
+;;; f x = [ -- start of a large list
+;;; -- which I'm commenting in as I go
+;;; <TAB>
+
+(defun haskell-calculate-indentation ()
+ "Returns the indentation level for the current line of haskell code."
+ (save-excursion
+ (let ((indent 0)
+ (eol-position (point)))
+ (beginning-of-line)
+ (cond ((bobp)
+ ;;We are at the beginning of the buffer so do nothing at all
+ (setq indent 0))
+
+ ((looking-at "^[ \t]*--")
+ ;;There is a comment on the line by itself
+ ;;Leave it the way it is
+ (setq indent (current-indentation)))
+
+ ((looking-at "^[ \t]*\\(data\\|type\\|module\\|import\\|instance\\)\\b")
+ ;;There is a 'data', 'type', 'module' or 'import' at start of line
+ (setq indent 0))
+
+ ((haskell-continued-fn-defp)
+ ;;This is clearly same function
+ ;; so set indent to be 0
+ (setq indent 0))
+
+ ((looking-at "^[ \t]*[]}]")
+ ;;There is a "]" or "}" at the start of the line
+ (let ((state (parse-partial-sexp (match-end 0)
+ (save-excursion
+ (haskell-back-to-zero-indent)
+ (point)))))
+ (if (>= (car state) 0)
+ ;;Since the point is just after a parenthesis
+ ;; it has a match if the depth is >= 0
+ (save-excursion
+ (goto-char (nth 2 state))
+ ;;Move to the match.
+ (if (not
+ (save-excursion
+ (skip-chars-backward " \t")
+ (bolp)))
+ ;;There is something before the brace.
+ (progn
+ (let ((initial-pos (point)))
+ (forward-word -1)
+ (if (not (looking-at
+ "\\(let\\|where\\)"))
+ ;;The word is not `where' or `let'
+ ;; so go back.
+ (progn
+ (goto-char initial-pos)
+ (skip-chars-forward " \t"))))))
+ (setq indent (current-column)))
+ (setq indent 0))))
+
+ ((looking-at "^[ \t]*\\(->\\|=>\\)")
+ ;; '->' or '=>' at start of line
+ (save-excursion
+ (haskell-backward-to-noncomment)
+ ;;Go back to previous line
+ (let ((eol-limit (point)))
+ (beginning-of-line)
+ (if (re-search-forward "::" eol-limit 't)
+ ;;There is a '::' on this (previous) line
+ ;; set indent to be at the start of it
+ (setq indent (- (current-column) 2))
+ ;;Otherwise copy this (previous) line's indentation
+ (setq indent (current-indentation))))))
+
+ ((looking-at "^[ \t]*where\\b")
+ ;;There is a 'where' at the start of the line
+ ;;Look for the equality (which will not
+ ;; be on this line).
+ (haskell-backward-to-noncomment)
+ (goto-char (max (save-excursion
+ (haskell-back-to-symbol "=")
+ (point))
+ (save-excursion
+ (haskell-back-to-symbol "->")
+ (point))))
+ (setq indent (haskell-indent-where)))
+
+ ((looking-at "^[ \t]*then\\b")
+ ;;The first thing on the line is a `then'
+ (setq indent (+ (haskell-last-unbalanced-key-column "if" "then")
+ haskell-then-offset)))
+
+ ((looking-at "^[ \t]*else\\b")
+ ;;The first thing on the line is a `else'
+ (if haskell-align-else-with-then
+ (setq indent (haskell-last-unbalanced-key-column "then" "else"))
+ (setq indent (haskell-last-unbalanced-key-column "if" "else"))))
+
+ ((looking-at "^[ \t]*|")
+ ;;There is a `|' at beginning of line
+ (save-excursion
+ (let ((state
+ (parse-partial-sexp (save-excursion
+ (haskell-back-to-zero-indent)
+ (point))
+ (point))))
+ (if (not (or (nth 3 state) (nth 4 state)))
+ ;;Not in a comment or string
+ (if (> (car state) 0)
+ ;;In an unbalanced parenthesis.
+ (progn
+ (goto-char (nth 1 state))
+ ;;Move to the beginning of the unbalanced parentheses
+ (if (and (looking-at "\\[")
+ (search-forward "|" (haskell-eol) 't))
+ ;;It is a list comprehension
+ (setq indent (1- (current-column)))
+ (setq indent (+ (current-column)
+ haskell-comp-offset))))
+ ;;Otherwise, not in an unbalanced parenthesis
+ (setq indent (save-excursion
+ (haskell-back-to-symbol "=")
+ (cond ((not (looking-at "="))
+ ;;Did not find an equals
+ (+ (haskell-previous-indentation)
+ haskell-indent-offset))
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at "^[ \t]*data\\b"))
+ ;;There is a `data' at beginning
+ (setq indent (current-column)))
+ ((save-excursion
+ (beginning-of-line)
+ (search-forward
+ "|" (haskell-eol) 't))
+ ;;There is a `|' on this line
+ ;; so set this to be the indent
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (current-column)))
+ (t
+ ;;Otherwise, set `=' as indent
+ (current-column))))))))))
+
+ ((looking-at "^[ \t]*=")
+ ;;There is an equals at the start of the line
+ ;;Set the indentation to be the previous line's
+ ;; indentation plus the standard offset
+ (setq indent (+ haskell-indent-offset
+ (haskell-previous-indentation))))
+
+ ((looking-at "^[ \t]*in\\b")
+ ;;The line starts with 'in'
+ (beginning-of-line)
+ (setq indent (haskell-last-unbalanced-key-column "let" "in")))
+
+ ((looking-at "^[ \t]*of\\b")
+ ;;The line starts with `of'
+ (beginning-of-line)
+ (setq indent (haskell-last-unbalanced-key-column "case" "of")))
+
+ ((looking-at "^.*::")
+ ;;There is a '::' in the line
+ ;;There are several possibilities for indentation
+ (if (looking-at "[ \t]*::")
+ ;;The '::' is the first thing on the line
+ ;; so set indent to be the previous line's
+ ;; indentation plus the standard offset
+ (setq indent (+ (haskell-previous-indentation)
+ haskell-indent-offset))
+ (save-excursion
+ ;;Otherwise, the '::' is contained in the line somewhere
+ ;; so use contextual indentation
+ (setq indent (haskell-context-indent)))))
+
+ (t
+ ;;Do not recognise the first word on the line.
+ (setq indent (haskell-context-indent))))
+
+ indent))) ;return indent as indentation value
+
+
+
+;;; Returns the indentation for the current line by looking at the
+;;; previous line to give clues to the indentation.
+
+(defun haskell-context-indent ()
+ "Returns the indentation for the current line by looking at
+the previous line to dictate the indentation."
+ (save-excursion
+ (let ((original-position (point))
+ indent)
+ (beginning-of-line)
+ (if (bobp)
+ ;;At the beginning of the buffer
+ (setq indent 0)
+ ;;Otherwise, we are not at the beginning of the buffer
+ (haskell-backward-to-noncomment)
+ (let ((eol-limit (point))
+ ;;Record the (upper) limit for any search on this line
+ bol-limit
+ (paren-indent 'nil))
+ ;;`paren-indent' flags whether we are indenting a list or not
+ (beginning-of-line)
+ (setq bol-limit (point))
+ ;;Record the (lower) limit for any search on this line
+ (goto-char eol-limit) ;goto the end of the line
+ (flag)
+ (if (save-excursion
+ (goto-char eol-limit)
+ (and (re-search-backward
+ "[])][^][()]*" bol-limit 't)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (not (haskell-looking-at-keywordp)))))
+
+ ;;There is a close parenthesis at the end of the line
+ ;; followed by anything except "(", ")", "[", "]"
+ ;; or a keyword
+ (progn
+ ;;Search back for the close parenthesis
+ ;; and move to just after it.
+ (re-search-backward "[])]" bol-limit 't)
+ (forward-char 1)
+ (let ((state
+ (parse-partial-sexp (save-excursion
+ (haskell-back-to-zero-indent)
+ (point))
+ (point))))
+ (if (not (or (nth 3 state) (nth 4 state)))
+ ;;Not in a comment or string
+ (if (>= (car state) 0)
+ ;;The parenthesis has a match
+ (progn
+ (goto-char (nth 2 state))
+ ;;Move to the beginning of the parentheses
+ ;; as this new line will determine
+ ;; further indentation
+ (if (zerop (car state))
+ ;;This paren closes all unbalanced parens
+ ;; so move to
+ ;; the eol of last line with an equality.
+ (progn
+ (setq eol-limit (point))
+ (goto-char
+ (max (save-excursion
+ (haskell-back-to-symbol "=")
+ (point))
+ (save-excursion
+ (haskell-back-to-keyword)
+ (point))))
+ (goto-char eol-limit))
+ ;;esle just go to the end of the line
+ (goto-char (haskell-eol)))
+ (setq paren-indent 't)
+ ;;Set 'paren-indent' to true to indicate we
+ ;; are indenting a list.
+ (setq eol-limit (point))
+ (beginning-of-line)
+ (setq bol-limit (point))
+ ;;Reduce the scope of any later
+ ;; indentation to
+ ;; exclude the balanced parentheses
+ ;; by making this point
+ ;; be the eol-limit.
+ (goto-char eol-limit)))))))
+ (flag)
+ ;;This cond expression is structured, to an
+ ;; extent, such that the keywords with highest
+ ;; indentation precedence come first. Order is important.
+ ;;In each condition, the point of match is noted so
+ ;; that we can see if this point is in a string.
+ (let ((indent-point (point)))
+ (cond ((re-search-backward "\\bof\\b" bol-limit 't)
+ ;; `of' is contained in previous line
+ (setq indent-point (point))
+ (if (looking-at "of[ \t]*$")
+ ;;`of' at end of line
+ (setq indent (+ (haskell-last-unbalanced-key-column
+ "case" "of")
+ haskell-case-offset))
+ ;;Otherwise, `of' is in line
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column))
+ (setq indent (list indent))))
+
+ ((re-search-backward
+ "\\bthen[ \t]*$" bol-limit 't)
+ ;;There is a `then' at the end of the line.
+ (setq indent-point (point))
+ (if haskell-align-else-with-then
+ ;;We want to align the `else' (to follow) with the `then'
+ (setq indent (+ (current-column)
+ haskell-if-offset))
+ (setq indent (+ (haskell-last-unbalanced-key-column
+ "if" "then")
+ haskell-if-offset))))
+ ;; This was here but don't know why (setq indent (list indent))))
+
+ ((save-excursion
+ (and (re-search-backward "\\bif\\b" bol-limit 't)
+ (setq indent-point (point))
+ (not (re-search-forward "\\bthen\\b" eol-limit 't))))
+ ;;There is an `if' on the (previous) line and the line does
+ ;; not have a `then' on it.
+ (setq indent (+ (haskell-last-unbalanced-key-column
+ "if" "then")
+ haskell-then-offset)))
+
+ ((save-excursion
+ (and (re-search-backward "\\bif\\b" bol-limit 't)
+ (setq indent-point (point))
+ (not (re-search-forward "\\belse\\b" eol-limit 't))))
+ ;;There is an `if' on the (previous) line (the line may
+ ;; have a `then' on it) and does not have an else on it.
+ (if (re-search-backward "\\bthen\\b" bol-limit 't)
+ ;;There is a then on the line and it is followed by
+ ;; some code.
+ (progn
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))
+ (if haskell-align-else-with-then
+ ;;We want to align the `else' with the `then'
+ (setq indent (haskell-last-unbalanced-key-column
+ "then" "else"))
+ (setq indent (haskell-last-unbalanced-key-column
+ "if" "else")))))
+
+ ((re-search-backward "\\b\\(let\\|in\\)\\b" bol-limit 't)
+ ;; 'let' or 'in' is contained in the (previous) line
+ (setq indent-point (point))
+ (forward-word 1) ;skip past the word
+ (skip-chars-forward " \t{")
+ (if (looking-at "\\($\\|--\\)")
+ ;;looking-at eol or comment
+ (progn
+ (forward-word -1)
+ (setq indent (+ (current-column)
+ haskell-let-offset)))
+ (setq indent (current-column))))
+
+ ((re-search-backward
+ "\\belse[ \t]*$" bol-limit 't)
+ ;;There is a `else' at end of line
+ (setq indent-point (point))
+ (save-excursion
+ (goto-char eol-limit)
+ (forward-word -1)
+ (setq indent (+ (current-column)
+ haskell-if-offset))))
+
+ ((re-search-backward
+ "\\belse\\b" bol-limit 't)
+ ;;There is a `else' on the line with no if or then
+ (setq indent-point (point))
+ (save-excursion
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column))))
+
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at
+ "^[ \t]*then\\b"))
+ ;;There is a 'then' at beginning of line
+ (setq indent-point (point))
+ (setq indent (current-indentation)))
+
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at "^[ \t]*else[ \t]*if\\b"))
+ (setq indent-point (point))
+ ;;There is an 'else if' at start of (previous) line
+ (save-excursion
+ (beginning-of-line)
+ (if haskell-nest-ifs
+ (save-excursion
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))))
+
+ ((re-search-backward "\\bcase\\b" bol-limit 't)
+ ;;There is a 'case' on the previous line
+ ;; so copy this line's indentation and add on
+ ;; the offset unless there is not an of.
+ (setq indent-point (point))
+ (setq indent (+ (current-column)
+ haskell-case-offset)))
+
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at "^\\(instance\\|class\\)\\b"))
+ ;;This (previous) line has an 'instance' or 'class' at start
+ ;; so just set indentation to be this line indentation
+ ;; plus the standard offset
+ (setq indent-point (point))
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset)))
+
+ ((re-search-backward "where\\b" bol-limit 't)
+ ;;There is a 'where' on the (previous) line
+ (setq indent-point (point))
+ (if (looking-at "where[ \t]*$")
+ ;;There is nothing after the 'where'
+ ;; so set indent to be this column
+ ;; (ie. the column of the 'w')
+ ;; plus the standard offset
+ (if (save-excursion
+ (skip-chars-backward " \t")
+ (bolp))
+ ;;The 'where' is the only thing on the line.
+ (setq indent (+ (current-column)
+ haskell-where-offset))
+ ;;Otherwise, the 'where' is at the end
+ ;; of the line and there is code before it.
+ ;;Look before the 'where' for the symbol
+ ;; it scopes over.
+ (forward-word -1)
+ (goto-char (max (save-excursion
+ (haskell-back-to-symbol "=")
+ (point))
+ (save-excursion
+ (haskell-back-to-symbol "->")
+ (point))))
+ (setq indent (haskell-indent-where)))
+
+ ;;Otherwise, go past the 'where'
+ ;; and goto the last non space character.
+ ;;Set this column to be the indentation.
+ (forward-word 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column))))
+
+ ((re-search-backward
+ "[ \ta-z0-9A-Z]=[ \t]*$" bol-limit 't)
+ ;;There is an equals is at the end of line
+ ;; so make the indentation be this line's indentation
+ ;; plus the standard offset
+ (setq indent-point (point))
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset)))
+
+ ((re-search-backward
+ "[ \ta-z0-9A-Z]\\+\\+[ \t]*$" bol-limit 't)
+ ;;There is a concat operator at the end of line
+ ;; so make the indentation be this line's indentation
+ (setq indent-point (point))
+ (setq indent (current-indentation)))
+
+ ((save-excursion
+ (beginning-of-line)
+ (looking-at
+ "^[ \t]*=[ \ta-z0-9A-Z]"))
+ ;;There is an equals is at the beginning of line
+ ;; so make the indentation be the previous line's
+ ;; indentation unless the previous line's
+ ;; indentation is zero.
+ (setq indent-point (point))
+ (save-excursion
+ (haskell-backward-to-noncomment)
+ (if (zerop (current-indentation))
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset))
+ (setq indent (haskell-current-indentation)))))
+
+ ((re-search-backward "|" bol-limit 't)
+ ;;There is an `|' on this line.
+ (setq indent-point (point))
+ (if (save-excursion
+ (goto-char original-position)
+ (looking-at "^[ \t]*\\($\\|--\\||\\)"))
+ ;;The original line is empty or has a `|' at the
+ ;; start. So set indent to be first `|' on this line
+ (save-excursion
+ (goto-char bol-limit)
+ (re-search-forward "|" eol-limit 't)
+ (setq indent (1- (current-column))))
+ ;;Otherwise set indent to be this (previous) line's
+ (setq indent 0)))
+
+ ((re-search-backward "->" bol-limit 't)
+ ;;There is a `->' in the line.
+ ;;This may be from a `case' or a
+ ;; type declaration.
+ (setq indent-point (point))
+ (save-excursion
+ (if (re-search-backward "::" bol-limit 't)
+ ;;There is a '::' on this line
+ (if (looking-at ".*->[ \t]*$")
+ ;;The '->' is at the end of line.
+ ;;Move past the '::' and any spaces
+ ;; and set indent to be this column.
+ (progn
+ (skip-chars-forward ": \t")
+ (setq indent (current-column)))
+ ;;Otherwise, the '->' is not at end of line
+ ;; so copy the indentation
+ (setq indent (haskell-context-indent)))
+
+ ;;Otherwise, there is not a
+ ;; `::' on this line so copy this
+ ;; (previous) indentation.
+ (setq indent (haskell-context-indent)))))
+
+ ((re-search-backward "::" bol-limit 't)
+ ;;There is an '::' on this line.
+ ;;We know that the line does not end with '->'.
+ (setq indent-point (point))
+ (if (looking-at "::[ \t]*$")
+ ;;The '::' is at the end of the line
+ ;; so set indent to be this line's
+ ;; indentation plus the offset.
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset))
+ ;;Otherwise the `::' is in the line
+ (setq indent (current-indentation))))
+
+ ((re-search-backward
+ "\\b\\(import\\|class\\)\\b"
+ bol-limit 't)
+ ;;There is an `import' or `class' on the line.
+ ;;Copy this indentation.
+ (setq indent-point (point))
+ (setq indent (current-indentation)))
+
+ ((or
+ (haskell-looking-at-eqp)
+ (save-excursion
+ (beginning-of-line)
+ (looking-at "^[ \t]*$")))
+ ;;There is an '=' on the line
+ ;; or it is blank
+ (setq indent-point (point))
+ (cond ((save-excursion
+ (beginning-of-line)
+ (looking-at "^[ \t]*data\\b"))
+ ;;`data' at start of line
+ ;; so expect a `|'
+ (haskell-back-to-symbol "=")
+ (setq indent (current-column)))
+ ((zerop (current-indentation))
+ ;;If the indentation is zero, we expect a `where'
+ (goto-char eol-limit)
+ (haskell-back-to-symbol "=")
+ (setq indent (haskell-indent-where)))
+ ((looking-at "^[ \t]*=[ \t\na-z0-9A-Z]")
+ ;;The equality is the first thing on the line
+ ;; so copy the last lines indentation
+ (save-excursion
+ (haskell-backward-to-noncomment)
+ (setq indent (current-indentation))))
+ (t
+ ;;Otherwise, copy the indentation
+ (setq indent (current-indentation)))))
+
+ ((save-excursion
+ (beginning-of-line)
+ (and (zerop (current-indentation))
+ (not (looking-at "^[ \t]*$"))))
+ ;;The line is not blank and its indentation is zero
+ ;;It is a function definition. We know that
+ ;; there is not an equals on the line
+ (goto-char eol-limit)
+ ;;We expect a keyword
+ ;; so set indent to be this line's indentation
+ ;; plus the offset
+ (setq indent-point (point))
+ (setq indent (+ (current-indentation)
+ haskell-indent-offset)))
+
+ ((bobp)
+ ;;At the beginning of buffer
+ (setq indent 0))
+
+ (paren-indent
+ ;;We are indenting a list and none
+ ;; of the above indentations are applicable
+ ;; so copy the indentation of this line
+ (setq indent (current-indentation)))
+
+ (t
+ (save-excursion
+ (setq indent (haskell-context-indent)))))
+
+ (if (nth 3 (parse-partial-sexp
+ (save-excursion
+ (goto-char indent-point)
+ (haskell-back-to-zero-indent)
+ (point))
+ (save-excursion
+ (goto-char indent-point))))
+ ;;The point we determined indentation at is in a
+ ;; string so go to this point and go back one line to
+ ;; find indentation.
+ (setq indent (haskell-context-indent))))
+
+
+ ;;HOWEVER, we may have to override any indentation if we are in
+ ;; an unbalanced parenthesis (on the original line).
+ (flag)
+ (save-excursion
+ (goto-char original-position)
+ (let* ((eq-point (save-excursion
+ (haskell-back-to-symbol "=")
+ (point)))
+ (state (parse-partial-sexp
+ eq-point
+ (point))))
+ (if (> (car state) 0)
+ ;;There is an unbalanced parenthesis between
+ ;; the function and here.
+ (if (not (or (nth 3 state) (nth 4 state)))
+ ;;We are not in a string or comment
+ ;; so goto the parenthesis
+ (progn
+ (goto-char (nth 1 state))
+ (if (not (haskell-keyword-in-regionp
+ (point)
+ original-position))
+ ;;There is not a keyword after the open
+ ;; bracket so we override the indentation
+ (progn
+ (if (not (looking-at "{"))
+ ;;The parenthesis is not a `{'
+ (if (or (looking-at "\\[")
+ (save-excursion
+ (goto-char (haskell-eol))
+ (skip-chars-backward " \t")
+ (and
+ (char-equal (preceding-char) ?,)
+ (= (car state)
+ (car (parse-partial-sexp
+ eq-point
+ (point)))))))
+ ;;The paren is a square one
+ ;; or it is a tuple.
+ ;;Don't ignore what is after it.
+ (setq indent (haskell-list-align (haskell-eol)))
+ ;;Otherwise, ignore what comes after it.
+ (setq indent (haskell-list-align (point))))))))))))
+ ))
+
+ indent)))
+
+
+;;; Inserts the close parenthesis and reindents the line.
+;;; We want to reindent the line if the parenthesis is
+;;; the first character on the line. The parenthesis
+;;; recognised by this function are `]', `}'.
+
+(defun electric-haskell-brace ()
+ "Inserts the character `]' or `}' and reindents the current line."
+ "Insert character and correct line's indentation."
+ (interactive)
+ (if (save-excursion
+ (skip-chars-backward " \t")
+ (bolp))
+ ;;The parenthesis is at the beginning of the line.
+ (progn
+ (insert last-command-char)
+ (haskell-indent-line))
+ ;;Otherwise it is not at the beginning of line.
+ (insert last-command-char))
+ ;; Match its beginning.
+ (haskell-blink-open))
+
+
+
+
+;;; This function returns the indentation for the next line given
+;;; that it is contained in a bracket or we are extending a functions
+;;; parameters over a line. For the case of being in an unbalanced
+;;; parenthesis list, the point lies on the unbalanced parenthesis.
+;;; The parameter eol-limit is used to delimit the end of the line.
+
+(defun haskell-list-align (eol-limit)
+ "Returns the indentation for the next line given that
+the point lies on an unbalanced open parenthesis."
+ (save-excursion
+ (let ((indent (1+ (current-column))))
+ ;;Set indent to be the next char (at least).
+
+ (cond ((not
+ (looking-at ".[ \t]*\\($\\|--\\)"))
+ ;;There is something after the parenthesis
+ ;;ie. the line is not empty and ignore comments
+ (cond ((save-excursion
+ (goto-char eol-limit)
+ (skip-chars-backward " \t")
+ (and (char-equal (preceding-char) ?,)
+ (save-excursion
+ (beginning-of-line)
+ (not (search-forward "|" eol-limit 't)))))
+ ;;This is a normal list since a `,' at end
+ ;; and there is no a `|' on the line.
+ (forward-char 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))
+
+ ((looking-at "\\[")
+ ;;It is a list comp we are looking at
+ ;;Goto the bar.
+ (forward-char 1)
+ (search-forward "|" eol-limit 't)
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))
+
+ ((looking-at ".[ \t]*(")
+ ;;We are looking at an open parenthesis
+ ;; after this character.
+ ;;It must be balanced so
+ ;; move to the start of this paren
+ ;; and set indent to be here
+ (forward-char 1)
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))
+
+ (t
+ (forward-word 1)
+ ;;We are not looking at another open
+ ;; parenthesis, so move forward past the
+ ;; (assumed) function name.
+ (if (or
+ haskell-std-list-indent
+ (looking-at"[ \t]*\\($\\|--\\)"))
+ ;;There is nothing after the name
+ ;; or haskell-std-list-offset is set
+ ;; so set indent to be its original
+ ;; value plus the offset minus 1
+ ;; since we added one on earlier.
+ (setq indent
+ (+ indent
+ (1- haskell-list-offset)))
+
+ ;;Otherwise there is something after the
+ ;; name, so skip to the first non space
+ ;; character.
+ (skip-chars-forward " \t")
+ (setq indent (current-column)))))))
+
+
+ indent)))
+
+
+
+(defun haskell-insert-round-paren ()
+ "Inserts a `(' and blinks to its matching parenthesis."
+ (interactive)
+ (insert last-command-char)
+ (haskell-blink-open))
+
+
+
+;;; This function is called when a close parenthesis
+;;; `)', `]', or `}' is typed.
+;;; Blinks the cursor on the corresponding open parnethesis.
+;;; The point lies just after the close parenthesis.
+
+(defun haskell-blink-open ()
+ "Blinks the cursor to the matching open parenthesis.
+The point lies just after a parenthesis."
+ (let ((state (parse-partial-sexp (point)
+ (save-excursion
+ (haskell-back-to-zero-indent)
+ (point)))))
+ (if (and
+ (>= (car state) 0)
+ (not (or (nth 3 state) (nth 4 state))))
+ ;;The parenthesis just inserted has a match
+ ;; and is not in a string or a comment
+ ;; so blink on its match
+ (save-excursion
+ (goto-char (nth 2 state))
+ (sit-for 1)))))
+
+
+
+;;; This function indents the line expecting the line to be a
+;;; continued function application.
+
+;;; foo a = bar a
+;;; b {haskell-further-indent applied to this line
+;;; indents the line as shown}
+
+;;; The line would look like this if only tab had been applied:
+;;; foo a = bar a
+;;; b
+
+(defun haskell-further-indent ()
+ "Indents the line more than the ordinary indentation in order to
+extend function arguments over multiple lines."
+ (interactive)
+ (let (indent
+ (new-point (max (save-excursion
+ (haskell-back-to-symbol "=")
+ (point))
+ (save-excursion
+ (haskell-back-to-keyword)
+ (point)))))
+ (save-excursion
+ ;;This may be a continuation of a function
+ ;; application so go back to the last '='
+ ;; and set indent as designated by the style chosen
+ (goto-char new-point)
+ (skip-chars-forward "= \t")
+ (setq indent (haskell-list-align (haskell-eol))))
+ ;;The argument to haskell-list-align is not important here.
+ (save-excursion
+ (beginning-of-line)
+ (delete-horizontal-space)
+ (indent-to indent))
+ (if (< (current-column) indent)
+ (move-to-column indent))))
+
+
+;;; This function indents the current line to the first previous
+;;; indentation value which is less than the current indentation.
+
+(defun haskell-lesser-indent ()
+ "Indents the current line to the first previous indentation
+value which is less than the current indentation."
+ (interactive)
+ (let ((original-indent
+ (current-indentation))
+ (indent (haskell-context-indent))
+ (done nil))
+ (save-excursion
+ (while (not done)
+ (while (and (not (bobp))
+ (not (zerop (current-indentation)))
+ (>= indent original-indent))
+ (haskell-backward-to-noncomment)
+ (setq indent (current-indentation)))
+ ;;bobp or indent < original-indent
+ (if (>= indent original-indent)
+ ;;indent is still greater than or equal to original indent
+ (progn
+ (setq indent 0)
+ (setq done t))
+ ;;Otherwise, indent is less than orignal indent.
+ (forward-line 1)
+ (setq indent (haskell-context-indent))
+ (if (< indent original-indent)
+ ;;The new indent is an improvement
+ (setq done t)
+ ;;Otherwise, indent is still >= original
+ ;; so go back to the line and keep typing.
+ (forward-line -1)))))
+ (save-excursion
+ (beginning-of-line)
+ (delete-horizontal-space)
+ (indent-to indent))
+ (if (< (current-column) indent)
+ (move-to-column indent))))
+
+
+
+;;; Here are the functions which change the local variables
+;;; to facilitate tailorability.
+
+(defun default-mode ()
+ "Calls the function haskell-mode."
+ (interactive)
+ (haskell-mode)
+ (message haskell-indent-style))
+
+(defun wadler-mode ()
+ "Sets defaults according to Dr. Philip L. Wadler's preferences.
+ - Aligns `where' clauses with the corresponding equality.
+ - Aligns `else' keyword with the corresponding `then'
+ - haskell-list-offset 2
+ - haskell-indent-offset 8
+ - haskell-if-indent 2
+ - haskell-comment-column 0
+ - haskell-case-offset 2
+ - haskell-let-offset 5."
+ ;;Preferences:
+ ;;'haskell-align-where-with-eq non-nil
+ ;;'haskell-list-offset 2
+ (interactive)
+ (haskell-mode)
+ (or haskell-align-where-with-eq
+ (progn
+ (setq haskell-align-where-with-eq t)
+ (setq haskell-std-indent-where nil)))
+ (setq haskell-align-else-with-then t)
+ (setq haskell-list-offset 2)
+ (setq haskell-indent-offset 8)
+ (setq haskell-if-offset 2)
+ (setq haskell-case-offset 2)
+ (setq haskell-let-offset 5)
+ (setq haskell-comment-column 0)
+ (setq haskell-indent-style "Wadler")
+ (message haskell-indent-style))
+
+
+(defun report-mode ()
+ "Sets defaults according to the style of the Haskell Report.
+ - Aligns `where' clauses after the corresponding equality.
+ - Aligns `else' with `then'.
+ - haskell-then-offset = 3
+ - haskell-where-offset = 0.
+ - haskell-case-offset = 5."
+ ;;Preferences:
+ ;; haskell-align-where-after-eq non-nil
+ ;; haskell-then-offset 3
+ ;; haskell-where-offset 0
+ ;; haskell-case-offset 5
+ (interactive)
+ (haskell-mode)
+ (haskell-align-where-after-eq)
+ (or haskell-align-else-with-then
+ (haskell-align-else-with-then))
+ (setq haskell-then-offset 3)
+ (setq haskell-where-offset 0)
+ (setq haskell-case-offset 5)
+ (setq haskell-indent-style "Report")
+ (message haskell-indent-style))
+
+
+(defun haskell-align-where-with-eq ()
+ "Sets indentation so that a 'where' clause lines up underneath
+its corresponding equals sign."
+ (interactive)
+ (or haskell-align-where-with-eq
+ (progn
+ (setq haskell-align-where-after-eq nil)
+ (setq haskell-std-indent-where nil)
+ (setq haskell-align-where-with-eq t)
+ haskell-align-where-with-eq)))
+
+
+
+(defun haskell-align-where-after-eq ()
+ "Sets indentation so that a 'where' clause lines up underneath
+the first nonspace character after its corresponding equals sign."
+ (interactive)
+ (or haskell-align-where-after-eq
+ (progn
+ (setq haskell-align-where-with-eq nil)
+ (setq haskell-std-indent-where nil)
+ (setq haskell-align-where-after-eq t)
+ haskell-align-where-after-eq)))
+
+
+(defun haskell-std-indent-where ()
+ "Sets indentation so that a `where' clause lines up underneath
+its corresponding equals sign."
+ (interactive)
+ (or haskell-std-indent-where
+ (progn
+ (setq haskell-align-where-after-eq nil)
+ (setq haskell-align-where-with-eq nil)
+ (setq haskell-std-indent-where t)
+ haskell-std-indent-where)))
+
+
+(defun haskell-align-else-with-then ()
+ "Sets indentation so that an `else' lines up underneath
+it's corresponding `then'."
+ (interactive)
+ (setq haskell-align-else-with-then
+ (not haskell-align-else-with-then))
+ (setq haskell-nest-ifs nil))
+
+(defun haskell-nest-ifs ()
+ "Sets indentation so that an `if' is lined up
+under an `if' in an `else ."
+ (interactive)
+ (setq haskell-nest-ifs
+ (not haskell-nest-ifs))
+ (setq haskell-align-else-with-then nil))
+
+
+(defun haskell-always-fixup-comment-space ()
+ "Non-nil means always position one space after a line comment `--',
+when reindenting or inserting a comment,
+whether or not one space exists."
+ (setq haskell-always-fixup-comment-space
+ (not haskell-always-fixup-comment-space))
+ haskell-always-fixup-comment-space)
+
+(defun haskell-indent-style ()
+ "Echos the chosen indentation style in the mini-buffer."
+ (interactive)
+ (message haskell-indent-style))
+
+(defun set-haskell-let-offset (offset)
+ "Changes the value of haskell-let-offset, the variable which
+determines extra indentation after a `let' and `in'."
+ (interactive "nSet haskell-let-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-let-offset offset)))
+
+(defun set-haskell-if-offset (offset)
+ "Changes the value of haskell-let-offset, the variable which
+determines extra indentation after an `if', `then' and `else'."
+ (interactive "nSet haskell-if-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-if-offset offset)))
+
+(defun set-haskell-case-offset (offset)
+ "Changes the value of haskell-case-offset, the variable which
+determines extra indentation after a `case' and `of'."
+ (interactive "nSet haskell-case-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-case-offset offset)))
+
+
+(defun set-haskell-where-offset (offset)
+ "Changes the value of haskell-where-offset, the variable which
+determines extra indentation after a line of haskell code."
+ (interactive "nSet haskell-where-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-where-offset offset)))
+
+
+(defun set-haskell-indent-offset (offset)
+ "Changes the value of haskell-indent-offset, the variable which
+determines extra indentation after a line of haskell code."
+ (interactive "nSet haskell-indent-offset to: ")
+ (if (and (>= offset 1) (<= offset 10))
+ (setq haskell-indent-offset offset)))
+
+
+(defun set-haskell-list-offset (offset)
+ "Changes the value of haskell-list-offset, the variable which
+determines extra indentation after a line of haskell code for a list."
+ (interactive "nSet haskell-list-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-list-offset offset)))
+
+
+(defun set-haskell-comp-offset (offset)
+ "Changes the value of haskell-comp-offset, the variable which
+determines extra indentation after a list comprehension."
+ (interactive "nSet haskell-comp-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-comp-offset offset)))
+
+
+(defun set-haskell-then-offset (offset)
+ "Changes the value of haskell-then-offset, the variable which
+determines extra indentation for a `then' keyword after an `if'."
+ (interactive "nSet haskell-then-offset to: ")
+ (if (and (>= offset 0) (<= offset 10))
+ (setq haskell-then-offset offset)))
+
+
+(defun set-haskell-comment-column (column)
+ "Changes the value of haskell-comment-column, the variable which
+determines where to postition a line comment `--'."
+ (interactive "nSet haskell-comment-column to: ")
+ (if (and (>= column 0) (<= column 100))
+ (setq haskell-comment-column column)))
+
+(defun set-haskell-concat-column (column)
+ "Changes the value of haskell-concat-column, the variable which
+determines where to postition a concatenation operator `++'."
+ (interactive "nSet haskell-concat-column to: ")
+ (if (and (>= column 0) (<= column 100))
+ (setq haskell-concat-column column)))
+
+(defun set-haskell-where-threshold (column)
+ "Changes the value of haskell-where-threshold, the variable which
+determines when to override positioning a `where' under or after
+its corresponding equality."
+ (interactive "nSet haskell-where-threshold to: ")
+ (if (and (>= column 0) (<= column 100))
+ (setq haskell-where-threshold column)))
+
+(defun flag ())
\ No newline at end of file