X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2FCONTRIB%2Fhaskell-modes%2Fglasgow%2Foriginal%2Fhaskell-mode.el;fp=ghc%2FCONTRIB%2Fhaskell-modes%2Fglasgow%2Foriginal%2Fhaskell-mode.el;h=b9a490ffd14af118ede1126d187151ca6b92b39d;hb=10521d8418fd3a1cf32882718b5bd28992db36fd;hp=0000000000000000000000000000000000000000;hpb=7fa716e248a1f11fa686965f57aebbb83b74fa7b;p=ghc-hetmet.git diff --git a/ghc/CONTRIB/haskell-modes/glasgow/original/haskell-mode.el b/ghc/CONTRIB/haskell-modes/glasgow/original/haskell-mode.el new file mode 100644 index 0000000..b9a490f --- /dev/null +++ b/ghc/CONTRIB/haskell-modes/glasgow/original/haskell-mode.el @@ -0,0 +1,1935 @@ +;; 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 + + +;;; f x = [ x + y, + +;;; f x = [ + +;;; f x = [ -- start of a large list +;;; -- which I'm commenting in as I go +;;; + +(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