diff --git a/haskell-indentation.el b/haskell-indentation.el index 504cb7268..f053a7978 100644 --- a/haskell-indentation.el +++ b/haskell-indentation.el @@ -1,4 +1,4 @@ -;;; haskell-indentation.el -- indentation module for Haskell Mode +;;; haskell-indentation.el --- indentation module for Haskell Mode ;; Copyright (C) 2013 Kristof Bastiaensen, Gergely Risko @@ -6,6 +6,7 @@ ;; Author: Gergely Risko ;; Keywords: indentation haskell ;; URL: https://github.com/haskell/haskell-mode + ;; This file is not part of GNU Emacs. ;; This file is free software; you can redistribute it and/or modify @@ -25,23 +26,24 @@ ;; Installation: ;; -;; To turn indentation on for all Haskell buffers under Haskell mode -;; add this to .emacs: +;; To turn indentation on for all Haskell buffers under Haskell mode add +;; this to your configuration file: ;; -;; (add-hook haskell-mode-hook 'haskell-indentation-mode) +;; (add-hook haskell-mode-hook 'haskell-indentation-mode) ;; ;; Otherwise, call `haskell-indentation-mode'. ;;; Code: +;; TODO eliminate magic number 2 where possible, use a variable + +;; TODO `haskell-indentation-find-indentation' — fix it, get rid of "safe" +;; version + (require 'hl-line) (require 'syntax) (require 'cl-lib) -(defvar haskell-indentation-dyn-first-position) -(defvar haskell-indentation-dyn-last-direction) -(defvar haskell-indentation-dyn-last-indentations) - ;;;###autoload (defgroup haskell-indentation nil "Haskell indentation." @@ -76,48 +78,53 @@ modes (e.g. fill-column-indicator)." ;;;###autoload (defface haskell-indentation-show-hl-line-face '((t :underline t :inherit hl-line)) - "Face used for indentations overlay after EOL if hl-line mode is enabled." + "Face used for indentations overlay after EOL if `hl-line-mode' +is enabled." :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-indent-leftmost t - "Indent to the left margin after certain keywords (for example after let .. in, case .. of). If set to t it will only indent to the left. If nil only relative to the containing expression. If set to the keyword 'both then both positions are allowed." + "Indent to the left margin after certain keywords. +For example after \"let .. in\", \"case .. of\"). If set to t it +will only indent to the left. If nil only relative to the +containing expression. If set to the symbol 'both then both +positions are allowed." :type 'symbol :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-layout-offset 2 - "Extra indentation to add before expressions in a haskell layout list." + "Extra indentation to add before expressions in a Haskell layout list." :type 'integer :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-starter-offset 2 - "Extra indentation after an opening keyword (e.g. let)." + "Extra indentation after an opening keyword (e.g. \"let\")." :type 'integer :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-left-offset 2 - "Extra indentation after an indentation to the left (e.g. after do)." + "Extra indentation after an indentation to the left (e.g. after \"do\")." :type 'integer :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-ifte-offset 2 - "Extra indentation after the keywords `if' `then' or `else'." + "Extra indentation after the keywords \"if\", \"then\", or \"else\"." :type 'integer :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-where-pre-offset 2 - "Extra indentation before the keyword `where'." + "Extra indentation before the keyword \"where\"." :type 'integer :group 'haskell-indentation) ;;;###autoload (defcustom haskell-indentation-where-post-offset 2 - "Extra indentation after the keyword `where'." + "Extra indentation after the keyword \"where\"." :type 'integer :group 'haskell-indentation) @@ -132,7 +139,7 @@ modes (e.g. fill-column-indicator)." "Haskell indentation mode that deals with the layout rule. It rebinds RET, DEL and BACKSPACE, so that indentations can be set and deleted as if they were real tabs. It supports -autofill-mode. +`auto-fill-mode'. It is possible to render indent stops for current line as overlays. Please read documentation for option @@ -140,40 +147,48 @@ overlays. Please read documentation for option `haskell-indentation-show-indentations-after-eol'. These options were disabled by default because in most cases occurs overlay clashing with other modes." - :lighter " Ind" :keymap haskell-indentation-mode-map (kill-local-variable 'indent-line-function) (kill-local-variable 'indent-region-function) (kill-local-variable 'normal-auto-fill-function) (when haskell-indentation-mode - (setq max-lisp-eval-depth (max max-lisp-eval-depth 600)) ;; set a higher limit for recursion - (set (make-local-variable 'indent-line-function) 'haskell-indentation-indent-line) - (set (make-local-variable 'indent-region-function) 'haskell-indentation-indent-region) - (set (make-local-variable 'normal-auto-fill-function) 'haskell-indentation-auto-fill-function) - (when haskell-indentation-show-indentations (haskell-indentation-enable-show-indentations)))) + (set (make-local-variable 'indent-line-function) + 'haskell-indentation-indent-line) + (set (make-local-variable 'indent-region-function) + 'haskell-indentation-indent-region) + (set (make-local-variable 'normal-auto-fill-function) + 'haskell-indentation-auto-fill-function) + (when haskell-indentation-show-indentations + (haskell-indentation-enable-show-indentations)))) ;;;###autoload (defun turn-on-haskell-indentation () "Turn on the haskell-indentation minor mode." (interactive) (haskell-indentation-mode t)) + (make-obsolete 'turn-on-haskell-indentation 'haskell-indentation-mode "2015-05-25") (defun haskell-indentation-parse-error (&rest args) + "Create error message from ARGS, log it and throw." (let ((msg (apply 'format args))) (message "%s" msg) (throw 'parse-error msg))) -(defvar haskell-literate) -(defun haskell-indentation-birdp () - "Return t if this is a literate haskell buffer in bird style, nil otherwise." +(defvar haskell-literate) ; defined in haskell-mode.el + +(defun haskell-indentation-bird-p () + "Return t if this is a literate Haskell buffer in bird style, +NIL otherwise." (eq haskell-literate 'bird)) -;;---------------------------------------- UI starts here +;;---------------------------------------------------------------------------- +;; UI starts here (defun haskell-indentation-auto-fill-function () + "" ; FIXME (when (> (current-column) fill-column) (while (> (current-column) fill-column) (skip-syntax-backward "-") @@ -181,12 +196,13 @@ clashing with other modes." (let ((indent (car (last (haskell-indentation-find-indentations-safe))))) (delete-horizontal-space) (newline) - (when (haskell-indentation-birdp) (insert ">")) + (when (haskell-indentation-bird-p) + (insert ">")) (indent-to indent) (end-of-line)))) (defun haskell-indentation-reindent-to (col &optional move) - "Reindent current line to COL, also move the point there if MOVE" + "Reindent current line to COL, move the point there if MOVE is non-NIL." (let* ((cc (current-column)) (ci (haskell-indentation-current-indentation))) (save-excursion @@ -200,8 +216,8 @@ clashing with other modes." (defun haskell-indentation-indent-rigidly (start end arg) "Indent all lines starting in the region sideways by ARG columns. Called from a program, takes three arguments, START, END and ARG. -You can remove all indentation from a region by giving a large negative ARG. -Handles bird style literate haskell too." +You can remove all indentation from a region by giving a large +negative ARG. Handles bird style literate Haskell too." (interactive "r\np") (save-excursion (goto-char end) @@ -220,60 +236,67 @@ Handles bird style literate haskell too." (move-marker end-marker nil)))) (defun haskell-indentation-current-indentation () - "Column position of first non whitespace character in current line" + "Column position of first non-whitespace character in current line." (save-excursion (beginning-of-line) - (when (haskell-indentation-birdp) (forward-char)) + (when (haskell-indentation-bird-p) + (forward-char)) (skip-syntax-forward "-") (current-column))) -(defun haskell-indentation-bird-outside-codep () - "True iff we are in bird literate mode, but outside of code" - (and (haskell-indentation-birdp) +(defun haskell-indentation-bird-outside-code-p () + "Non-NIL if we are in bird literate mode, but outside of code." + (and (haskell-indentation-bird-p) (or (< (current-column) 2) (save-excursion (beginning-of-line) (not (eq (char-after) ?>)))))) -(defun haskell-indentation-delete-horizontal-space-and-newline () - (delete-horizontal-space) - (newline)) - (defun haskell-indentation-newline-and-indent () - "Ran on C-j or RET" + "Insert newline and indent." (interactive) - ;; On RET (or C-j), we: - ;; - just jump to the next line if literate haskell, but outside code - (if (haskell-indentation-bird-outside-codep) - (haskell-indentation-delete-horizontal-space-and-newline) - ;; - just jump to the next line if parse-error + (delete-horizontal-space) + (newline) + (unless (haskell-indentation-bird-outside-code-p) (catch 'parse-error - (haskell-indentation-delete-horizontal-space-and-newline) - (let* ((cc (current-column)) - (ci (haskell-indentation-current-indentation)) - (indentations (haskell-indentation-find-indentations-safe))) - (when (haskell-indentation-birdp) (insert "> ")) - (haskell-indentation-reindent-to - (haskell-indentation-next-indentation (- ci 1) indentations 'nofail) - 'move))))) + (let* ((cc (current-column)) + (ci (haskell-indentation-current-indentation)) + (indentations (haskell-indentation-find-indentations-safe))) + (when (haskell-indentation-bird-p) + (insert "> ")) + (haskell-indentation-reindent-to + (haskell-indentation-next-indentation (- ci 1) indentations 'nofail) + 'move))))) (defun haskell-indentation-next-indentation (col indentations &optional nofail) "Find the leftmost indentation which is greater than COL. -Or returns the last indentation if there are no bigger ones and -NOFAIL is non-nil." - (when (null indentations) (error "haskell-indentation-next-indentation called with empty list")) - (or (cl-find-if #'(lambda (i) (> i col)) indentations) - (when nofail (car (last indentations))))) +Indentations are taken from INDENTATIONS, which should be a +list. Return the last indentation if there are no bigger ones and +NOFAIL is non-NIL." + (when (null indentations) + (error "haskell-indentation-next-indentation called with empty list")) + (or (cl-find-if (lambda (i) (> i col)) indentations) + (when nofail + (car (last indentations))))) (defun haskell-indentation-previous-indentation (col indentations &optional nofail) "Find the rightmost indentation which is less than COL." - (when (null indentations) (error "haskell-indentation-previous-indentation called with empty list")) + (when (null indentations) + (error "haskell-indentation-previous-indentation called with empty list")) (let ((rev (reverse indentations))) - (or (cl-find-if #'(lambda (i) (< i col)) rev) - (when nofail (car rev))))) + (or (cl-find-if (lambda (i) (< i col)) rev) + (when nofail + (car rev))))) + +(defvar haskell-indentation-dyn-first-position nil + "") ; FIXME +(defvar haskell-indentation-dyn-last-direction nil + "") ; FIXME +(defvar haskell-indentation-dyn-last-indentations nil + "") ; FIXME (defun haskell-indentation-indent-line () - "Auto indentation on TAB. + "Indent current line, cycle though indentation positions. Do nothing inside multiline comments and multiline strings. Start enumerating the indentation points to the right. The user can continue by repeatedly pressing TAB. When there is no more @@ -287,8 +310,8 @@ indentation points to the right, we switch going to the left." (beginning-of-line) (nth 8 (syntax-ppss))) ;; parse error is intentionally not catched here, it may come from - ;; haskell-indentation-find-indentations-safe, but escapes the scope and aborts the - ;; opertaion before any moving happens + ;; `haskell-indentation-find-indentations-safe', but escapes the scope + ;; and aborts the opertaion before any moving happens (let* ((cc (current-column)) (ci (haskell-indentation-current-indentation)) (inds (save-excursion @@ -296,23 +319,32 @@ indentation points to the right, we switch going to the left." (haskell-indentation-find-indentations-safe))) (valid (memq ci inds)) (cursor-in-whitespace (< cc ci))) - ;; can't happen right now, because of -safe, but we may want to have this in the future + ;; can't happen right now, because of -safe, but we may want to have + ;; this in the future ;; (when (null inds) ;; (error "returned indentations empty, but no parse error")) (if (and valid cursor-in-whitespace) (move-to-column ci) - (haskell-indentation-reindent-to (haskell-indentation-next-indentation ci inds 'nofail) cursor-in-whitespace)) - (setq haskell-indentation-dyn-last-direction 'right) - (setq haskell-indentation-dyn-first-position (haskell-indentation-current-indentation)) - (setq haskell-indentation-dyn-last-indentations inds))))) + (haskell-indentation-reindent-to + (haskell-indentation-next-indentation ci inds 'nofail) + cursor-in-whitespace)) + (setq haskell-indentation-dyn-last-direction 'right + haskell-indentation-dyn-first-position + (haskell-indentation-current-indentation) + haskell-indentation-dyn-last-indentations inds))))) (defun haskell-indentation-indent-line-repeat () - "Ran if the user repeatedly presses the TAB key" + "Cycle though indentation positions." (cond - ((and (memq last-command '(indent-for-tab-command haskell-indentation-indent-backwards)) + ((and (memq last-command + '(indent-for-tab-command + haskell-indentation-indent-backwards)) (eq haskell-indentation-dyn-last-direction 'region)) (let ((mark-even-if-inactive t)) - (haskell-indentation-indent-rigidly (region-beginning) (region-end) 1)) + (haskell-indentation-indent-rigidly + (region-beginning) + (region-end) + 1)) t) ((and (eq last-command 'indent-for-tab-command) (memq haskell-indentation-dyn-last-direction '(left right)) @@ -320,31 +352,39 @@ indentation points to the right, we switch going to the left." (let* ((cc (current-column)) (ci (haskell-indentation-current-indentation))) (if (eq haskell-indentation-dyn-last-direction 'left) - (haskell-indentation-reindent-to (haskell-indentation-previous-indentation ci haskell-indentation-dyn-last-indentations 'nofail)) + (haskell-indentation-reindent-to + (haskell-indentation-previous-indentation + ci haskell-indentation-dyn-last-indentations 'nofail)) ;; right - (if (haskell-indentation-next-indentation ci haskell-indentation-dyn-last-indentations) - (haskell-indentation-reindent-to (haskell-indentation-next-indentation ci haskell-indentation-dyn-last-indentations 'nofail)) + (if (haskell-indentation-next-indentation + ci haskell-indentation-dyn-last-indentations) + (haskell-indentation-reindent-to + (haskell-indentation-next-indentation + ci haskell-indentation-dyn-last-indentations 'nofail)) ;; but failed, switch to left (setq haskell-indentation-dyn-last-direction 'left) ;; and skip to the point where the user started pressing TABs. ;; except if there are <= 2 indentation points, because this ;; behavior is very confusing in that case (when (< 2 (length haskell-indentation-dyn-last-indentations)) - (haskell-indentation-reindent-to haskell-indentation-dyn-first-position)) - (haskell-indentation-indent-line-repeat)))) - t) + (haskell-indentation-reindent-to + haskell-indentation-dyn-first-position)) + (haskell-indentation-indent-line-repeat))) + t)) (t nil))) (defun haskell-indentation-indent-region (start end) + "Indent region from START to END." (setq haskell-indentation-dyn-last-direction 'region) (haskell-indentation-indent-rigidly start end 1) (message "Press TAB or S-TAB again to indent the region more")) (defun haskell-indentation-indent-backwards () - "Indent the current line to the previous indentation point" + "Indent the current line to the previous indentation point." (interactive) (cond - ((and (memq last-command '(indent-for-tab-command haskell-indentation-indent-backwards)) + ((and (memq last-command + '(indent-for-tab-command haskell-indentation-indent-backwards)) (eq haskell-indentation-dyn-last-direction 'region)) (let ((mark-even-if-inactive t)) (haskell-indentation-indent-rigidly (region-beginning) (region-end) -1))) @@ -363,15 +403,21 @@ indentation points to the right, we switch going to the left." (pi (haskell-indentation-previous-indentation ci inds))) (if (null pi) ;; if there are no more indentations to the left, just go to column 0 - (haskell-indentation-reindent-to (car (haskell-indentation-first-indentation)) cursor-in-whitespace) + (haskell-indentation-reindent-to + (car (haskell-indentation-first-indentation)) cursor-in-whitespace) (haskell-indentation-reindent-to pi cursor-in-whitespace)))))) -;;---------------------------------------- haskell-indentation show indentations UI starts here +;;---------------------------------------------------------------------------- +;; haskell-indentation show indentations UI starts here + (defvar haskell-indentation-dyn-show-indentations nil "Whether showing of indentation points is enabled in this buffer.") + (make-variable-buffer-local 'haskell-indentation-dyn-show-indentations) + (defvar haskell-indentation-dyn-overlays nil "Overlays used by haskell-indentation-enable-show-indentations.") + (make-variable-buffer-local 'haskell-indentation-dyn-overlays) (defun haskell-indentation-init-overlays (n) @@ -392,20 +438,24 @@ indentation points to the right, we switch going to the left." Holds time object value as received from `run-at-time'. Used to debounce `haskell-indentation-delay-show-overlays'.") + (make-local-variable 'haskell-indentation-pending-delay-show-overlays) (defun haskell-indentation-delay-show-overlays () - "Show overlays after a little while so that it does not get in -the way of normal cursor movement. + "Show overlays after a little while. + +We use delay here so that it does not get in the way of normal +cursor movement. If there is a running show overlays timer cancel it first." (when haskell-indentation-pending-delay-show-overlays (cancel-timer haskell-indentation-pending-delay-show-overlays)) (setq haskell-indentation-pending-delay-show-overlays - (run-at-time "0.1 sec" nil - (lambda () - (setq haskell-indentation-pending-delay-show-overlays nil) - (haskell-indentation-show-overlays))))) + (run-at-time + "0.1 sec" nil + (lambda () + (setq haskell-indentation-pending-delay-show-overlays nil) + (haskell-indentation-show-overlays))))) (defun haskell-indentation-show-overlays () "Put an underscore overlay at all the indentations points in @@ -420,7 +470,10 @@ the current buffer." (current-column))) (ci (haskell-indentation-current-indentation)) (allinds (save-excursion - (move-to-column ci); XXX: remove when haskell-indentation-find-indentations is fixed + ;; XXX: remove when + ;; haskell-indentation-find-indentations is + ;; fixed + (move-to-column ci) ;; don't freak out on parse-error (haskell-indentation-find-indentations-safe))) ;; indentations that are easy to show @@ -428,11 +481,15 @@ the current buffer." ;; tricky indentations, that are after the current EOL (overinds (cl-member-if (lambda (i) (>= i columns)) allinds)) ;; +1: leave space for an extra overlay to show overinds - (overlays (haskell-indentation-init-overlays (+ 1 (length inds))))) + (overlays (haskell-indentation-init-overlays + (+ 1 (length inds))))) (while inds (move-to-column (car inds)) - (overlay-put (car overlays) 'face 'haskell-indentation-show-normal-face) - (overlay-put (car overlays) 'after-string nil) + (overlay-put (car overlays) + 'face + 'haskell-indentation-show-normal-face) + (overlay-put (car overlays) + 'after-string nil) (move-overlay (car overlays) (point) (+ 1 (point))) (setq inds (cdr inds)) (setq overlays (cdr overlays))) @@ -441,9 +498,10 @@ the current buffer." (let ((o (car overlays)) (s (make-string (+ 1 (- (car (last overinds)) columns)) ? ))) ;; needed for the cursor to be in the good position, see: - ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2013-03/msg00079.html + ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2013-03/msg00079.html (put-text-property 0 1 'cursor t s) - ;; color the whole line ending overlay with hl-line face if needed + ;; color the whole line ending overlay with `hl-line-mode' + ;; face if needed (when (or hl-line-mode global-hl-line-mode) (put-text-property 0 (length s) 'face 'hl-line s)) ;; put in the underlines at the correct positions @@ -477,42 +535,46 @@ the current buffer." (remove-hook 'change-major-mode-hook #'haskell-indentation-unshow-overlays t) (remove-hook 'pre-command-hook #'haskell-indentation-unshow-overlays t)) -;;---------------------------------------- parser starts here - -;; The parser is implemented als a recursive descent parser. Each -;; parser advances the point to after the expression it parses, and -;; sets the dynamic scoped variables containing the information about -;; the indentations. The dynamic scoping allows transparent -;; backtracking to previous states of these variables. A new state -;; can be set using LET. When the scope of this function ends, -;; the variable is automatically reverted to it's old value. - -;; This is basicly a performance hack. It would have been possible -;; to thread this state using a association-list through the parsers, but it -;; would be probably more complicated and slower due to the lack -;; of real closures in ELISP. +;;---------------------------------------------------------------------------- +;; Parser Starts Here + +;; The parser is implemented as a recursive descent parser. Each parser +;; advances the point to after the expression it parses, and sets the +;; dynamic scoped variables containing the information about the +;; indentations. The dynamic scoping allows transparent backtracking to +;; previous states of these variables. A new state can be set using `let'. +;; When the scope of this function ends, the variable is automatically +;; reverted to its old value. + +;; This is basicly a performance hack. It would have been possible to +;; thread this state using a association-list through the parsers, but it +;; would be probably more complicated and slower due to the lack of real +;; closures in Emacs Lisp. ;; ;; When finished parsing, the tokenizer returns 'end-token, and -;; following-token is set to the token after point. The parser adds -;; its indentations to possible-indentations and returns to it's -;; parent, or exits non-locally by throwing parse-end, so that the -;; parent will not add new indentations to it. - -;; the parse 'state': -(defvar following-token) ;; the next token after parsing finished -(defvar current-token) ;;; the token at the current parser point or a pseudo-token (see haskell-indentation-read-next-token) -(defvar left-indent) ;; most left possible indentation -(defvar starter-indent) ;; column at a keyword -(defvar current-indent) ;; the most right indentation -(defvar layout-indent) ;; the column of the layout list -(defvar parse-line-number) ;; the number of lines parsed -(defvar possible-indentations) ;; the return value of the indentations -(defvar indentation-point) ;; where to stop parsing -(defvar implicit-layout-active) ;; is 'off-side' rule active? +;; following-token is set to the token after point. The parser adds its +;; indentations to possible-indentations and returns to it's parent, or +;; exits non-locally by throwing parse-end, so that the parent will not add +;; new indentations to it. + +;; the parse state: +(defvar following-token) ;; the next token after parsing finished +;; the token at the current parser point or a pseudo-token (see +;; `haskell-indentation-read-next-token') +(defvar current-token) +(defvar left-indent) ;; most left possible indentation +(defvar starter-indent) ;; column at a keyword +(defvar current-indent) ;; the most right indentation +(defvar layout-indent) ;; the column of the layout list +(defvar parse-line-number) ;; the number of lines parsed +(defvar possible-indentations) ;; the return value of the indentations +(defvar indentation-point) ;; where to stop parsing +(defvar implicit-layout-active) ;; is "off-side" rule active? (defun haskell-indentation-goto-least-indentation () + "" ; FIXME (beginning-of-line) - (if (haskell-indentation-birdp) + (if (haskell-indentation-bird-p) (catch 'return (while t (when (not (eq (char-after) ?>)) @@ -546,6 +608,7 @@ the current buffer." (forward-comment (buffer-size))))) (defun haskell-indentation-parse-to-indentations () + "" ; FIXME (save-excursion (skip-syntax-forward "-") (let ((indentation-point (point)) @@ -565,13 +628,16 @@ the current buffer." (catch 'parse-end (haskell-indentation-toplevel) (unless (eq current-token 'end-tokens) - (haskell-indentation-parse-error "Illegal token: %s" current-token))) + (haskell-indentation-parse-error + "Illegal token: %s" current-token))) possible-indentations)))) (defun haskell-indentation-first-indentation () - (if (haskell-indentation-birdp) '(2) '(0))) + "Return column of first indentation." + (list (if (haskell-indentation-bird-p) 2 0))) (defun haskell-indentation-find-indentations () + "Return list of indentation positions corresponding to actual cursor position." (let ((ppss (syntax-ppss))) (cond ((nth 3 ppss) @@ -603,88 +669,129 @@ the current buffer." ("⤛" . "-<<") ;; #x291B LEFTWARDS DOUBLE ARROW-TAIL ("⤜" . ">>-") ;; #x291C RIGHTWARDS DOUBLE ARROW-TAIL ("★" . "*")) ;; #x2605 BLACK STAR - "Translation dictionary from UnicodeSyntax tokens to their ASCII representation.") + "Translation from UnicodeSyntax tokens to their ASCII representation.") -;; toplevel keywords (defconst haskell-indentation-toplevel-list - '(("module" . haskell-indentation-module) - ("data" . (lambda () (haskell-indentation-statement-right #'haskell-indentation-data))) - ("type" . (lambda () (haskell-indentation-statement-right #'haskell-indentation-data))) - ("newtype" . (lambda () (haskell-indentation-statement-right #'haskell-indentation-data))) - ("class" . haskell-indentation-class-declaration) - ("instance" . haskell-indentation-class-declaration ))) - -;; tokens in type declarations + `(("module" . haskell-indentation-module) + ("data" . + ,(apply-partially 'haskell-indentation-statement-right + 'haskell-indentation-data)) + ("type" . + ,(apply-partially 'haskell-indentation-statement-right + 'haskell-indentation-data)) + ("newtype" . + ,(apply-partially 'haskell-indentation-statement-right + 'haskell-indentation-data)) + ("class" . haskell-indentation-class-declaration) + ("instance" . haskell-indentation-class-declaration)) + "Alist of toplevel keywords with associated parsers.") + (defconst haskell-indentation-type-list - '(("::" . (lambda () (haskell-indentation-with-starter - (lambda () (haskell-indentation-separated #'haskell-indentation-type "->"))))) - ("(" . (lambda () (haskell-indentation-list #'haskell-indentation-type ")" ","))) - ("[" . (lambda () (haskell-indentation-list #'haskell-indentation-type "]" ","))) - ("{" . (lambda () (haskell-indentation-list #'haskell-indentation-type "}" ","))))) + `(("::" . + ,(apply-partially 'haskell-indentation-with-starter + (apply-partially 'haskell-indentation-separated + 'haskell-indentation-type "->"))) + ("(" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-type ")" ",")) + ("[" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-type "]" ",")) + ("{" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-type "}" ","))) + "Alist of tokens in type declarations with associated parsers.") -;; keywords in expressions (defconst haskell-indentation-expression-list - '(("data" . haskell-indentation-data) - ("type" . haskell-indentation-data) + `(("data" . haskell-indentation-data) + ("type" . haskell-indentation-data) ("newtype" . haskell-indentation-data) - ("if" . haskell-indentation-if) - ("let" . (lambda () (haskell-indentation-phrase - '(haskell-indentation-declaration-layout - "in" haskell-indentation-expression)))) - ("do" . (lambda () (haskell-indentation-with-starter - #'haskell-indentation-expression-layout))) - ("mdo" . (lambda () (haskell-indentation-with-starter - #'haskell-indentation-expression-layout))) - ("rec" . (lambda () (haskell-indentation-with-starter - #'haskell-indentation-expression-layout))) - ("case" . (lambda () (haskell-indentation-phrase - '(haskell-indentation-expression - "of" haskell-indentation-case-layout)))) - ("\\" . (lambda () (haskell-indentation-with-starter - #'haskell-indentation-lambda-maybe-lambdacase))) - ("proc" . (lambda () (haskell-indentation-phrase - '(haskell-indentation-expression - "->" haskell-indentation-expression)))) - ("where" . (lambda () (haskell-indentation-with-starter - #'haskell-indentation-declaration-layout nil t))) - ("::" . (lambda () (haskell-indentation-with-starter - (lambda () (haskell-indentation-separated #'haskell-indentation-type "->"))))) - ("=" . (lambda () (haskell-indentation-statement-right #'haskell-indentation-expression))) - ("<-" . (lambda () (haskell-indentation-statement-right #'haskell-indentation-expression))) - ("(" . (lambda () (haskell-indentation-list #'haskell-indentation-expression ")" '(list "," "->")))) - ("[" . (lambda () (haskell-indentation-list #'haskell-indentation-expression "]" "," "|"))) - ("{" . (lambda () (haskell-indentation-list #'haskell-indentation-expression "}" ","))))) - -;; a layout list with expressions, such as after do + ("if" . haskell-indentation-if) + ("let" . + ,(apply-partially 'haskell-indentation-phrase + '(haskell-indentation-declaration-layout + "in" haskell-indentation-expression))) + ("do" . + ,(apply-partially 'haskell-indentation-with-starter + 'haskell-indentation-expression-layout)) + ("mdo" . + ,(apply-partially 'haskell-indentation-with-starter + 'haskell-indentation-expression-layout)) + ("rec" . + ,(apply-partially 'haskell-indentation-with-starter + 'haskell-indentation-expression-layout)) + ("case" . + ,(apply-partially 'haskell-indentation-phrase + '(haskell-indentation-expression + "of" haskell-indentation-case-layout))) + ("\\" . + ,(apply-partially 'haskell-indentation-with-starter + 'haskell-indentation-lambda-maybe-lambdacase)) + ("proc" . + ,(apply-partially 'haskell-indentation-phrase + '(haskell-indentation-expression + "->" haskell-indentation-expression))) + ("where" . + ,(apply-partially 'haskell-indentation-with-starter + 'haskell-indentation-declaration-layout nil t)) + ("::" . + ,(apply-partially 'haskell-indentation-with-starter + (apply-partially #'haskell-indentation-separated + #'haskell-indentation-type "->"))) + ("=" . + ,(apply-partially 'haskell-indentation-statement-right + 'haskell-indentation-expression)) + ("<-" . + ,(apply-partially 'haskell-indentation-statement-right + 'haskell-indentation-expression)) + ("(" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-expression + ")" + '(list "," "->"))) + ("[" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-expression "]" "," "|")) + ("{" . + ,(apply-partially 'haskell-indentation-list + 'haskell-indentation-expression "}" ","))) + "Alist of keywords in expressions with associated parsers.") + (defun haskell-indentation-expression-layout () + "Parse layout list with expressions, such as after \"do\"." (haskell-indentation-layout #'haskell-indentation-expression)) -;; a layout list with declarations, such as after where (defun haskell-indentation-declaration-layout () + "Parse layout list with declarations, such as after \"where\"." (haskell-indentation-layout #'haskell-indentation-declaration)) -;; a layout list with case expressions (defun haskell-indentation-case-layout () + "Parse layout list with case expressions." (haskell-indentation-layout #'haskell-indentation-case)) -;; After a lambda (backslash) there are two possible cases: -;; - the new lambdacase expression, that can be recognized by the -;; next token being "case", -;; - or simply an anonymous function definition in the form of -;; "expression -> expression". (defun haskell-indentation-lambda-maybe-lambdacase () + "Parse lambda or lambda-case expression. +After a lambda (backslash) there are two possible cases: + +- the new lambdacase expression, that can be recognized by the + next token being \"case\"; + +- or simply an anonymous function definition in the form of + \"expression -> expression\"." (if (string= current-token "case") (haskell-indentation-with-starter #'haskell-indentation-case-layout) (haskell-indentation-phrase-rest '(haskell-indentation-expression "->" haskell-indentation-expression)))) -;; a functional dependency (defun haskell-indentation-fundep () + "Parse functional dependency." (haskell-indentation-with-starter - (lambda () (haskell-indentation-separated #'haskell-indentation-fundep1 ",")))) + (apply-partially #'haskell-indentation-separated + #'haskell-indentation-fundep1 ","))) (defun haskell-indentation-fundep1 () + "Parse an item in functional dependency declaration." (let ((current-indent (current-column))) (while (member current-token '(value "->")) (haskell-indentation-read-next-token)) @@ -692,8 +799,8 @@ the current buffer." (member following-token '(value "->"))) (haskell-indentation-add-indentation current-indent)))) -;; the toplevel parser (defun haskell-indentation-toplevel () + "Parse toplevel statements." (haskell-indentation-layout (lambda () (let ((parser (assoc current-token haskell-indentation-toplevel-list))) @@ -701,8 +808,8 @@ the current buffer." (funcall (cdr parser)) (haskell-indentation-declaration)))))) -;; a type declaration (defun haskell-indentation-type () + "Parse type declaration." (let ((current-indent (current-column))) (catch 'return (while t @@ -721,8 +828,8 @@ the current buffer." (throw 'return nil) (funcall (cdr parser)))))))))) -;; a data or type declaration (defun haskell-indentation-data () + "Parse data or type declaration." (haskell-indentation-with-starter (lambda () (when (string= current-token "instance") @@ -730,13 +837,14 @@ the current buffer." (haskell-indentation-type) (cond ((string= current-token "=") (haskell-indentation-with-starter - (lambda () (haskell-indentation-separated #'haskell-indentation-type "|" "deriving")))) - ((string= current-token "where") + (apply-partially #'haskell-indentation-separated + #'haskell-indentation-type "|" "deriving"))) + ((string= current-token "where") (haskell-indentation-with-starter #'haskell-indentation-expression-layout nil)))))) -;; a class declaration (defun haskell-indentation-class-declaration () + "Parse class declaration." (haskell-indentation-with-starter (lambda () (haskell-indentation-type) @@ -746,8 +854,8 @@ the current buffer." (haskell-indentation-with-starter #'haskell-indentation-declaration-layout nil))))) -;; a module declaration (defun haskell-indentation-module () + "Parse module declaration." (haskell-indentation-with-starter (lambda () (let ((current-indent (current-column))) @@ -766,8 +874,8 @@ the current buffer." (throw 'parse-end nil)) (haskell-indentation-layout #'haskell-indentation-toplevel)))))) -;; an export list (defun haskell-indentation-module-export () + "Parse export list." (cond ((string= current-token "module") (let ((current-indent (current-column))) (haskell-indentation-read-next-token) @@ -777,47 +885,57 @@ the current buffer." (haskell-indentation-read-next-token))))) (t (haskell-indentation-type)))) -;; an list, pair or other expression containing multiple -;; items parsed by parser, separated by sep or stmt-sep, and ending in -;; end. (defun haskell-indentation-list (parser end sep &optional stmt-sep) - (let ((implicit-layout-active nil)) + "Parse a list, pair or other expression containing multiple +items parsed by PARSER, separated by SEP or STMT-SEP, and ending +with END." + (let (implicit-layout-active) (haskell-indentation-with-starter - `(lambda () (haskell-indentation-separated #',parser - ,sep - ,stmt-sep)) + (apply-partially #'haskell-indentation-separated + parser sep stmt-sep) end))) -;; An expression starting with a keyword or paren. Skip the keyword -;; or paren. (defun haskell-indentation-with-starter (parser &optional end where-expr?) + "Parse an expression starting with a keyword or parenthesis. +Skip the keyword or parenthesis." ; FIXME: better description needed (let ((starter-column (current-column)) (current-indent current-indent) - (left-indent (if (= (current-column) (haskell-indentation-current-indentation)) - (current-column) left-indent))) + (left-indent + (if (= (current-column) (haskell-indentation-current-indentation)) + (current-column) + left-indent))) (haskell-indentation-read-next-token) (when (eq current-token 'end-tokens) (cond ((equal following-token end) - (haskell-indentation-add-indentation starter-column)) ; indent before keyword or paren - (where-expr? - (haskell-indentation-add-where-post-indent left-indent)) ;; left indent + where post indent - (t - (haskell-indentation-add-left-indent))) + ;; indent before keyword or parenthesis + (haskell-indentation-add-indentation starter-column)) + (where-expr? + ;; left indent + where post indent + (haskell-indentation-add-where-post-indent left-indent)) + (t + (haskell-indentation-add-left-indent))) (throw 'parse-end nil)) (let* ((current-indent (current-column)) (starter-indent (min starter-column current-indent)) - (left-indent (if end (+ current-indent haskell-indentation-starter-offset) - left-indent))) + (left-indent + (if end + (+ current-indent haskell-indentation-starter-offset) + left-indent))) (funcall parser) (cond ((eq current-token 'end-tokens) (when (equal following-token end) - (haskell-indentation-add-indentation starter-indent)) ; indent before keyword or paren - (when end (throw 'parse-end nil))) ;; add no more indentations if we expect a closing keyword + ;; indent before keyword or parenthesis + (haskell-indentation-add-indentation starter-indent)) + ;; add no more indentations if we expect a closing keyword + (when end + (throw 'parse-end nil))) ((equal current-token end) - (haskell-indentation-read-next-token)) ;; continue - (end (haskell-indentation-parse-error "Illegal token: %s" current-token)))))) + (haskell-indentation-read-next-token)) ; continue + (end (haskell-indentation-parse-error + "Illegal token: %s" current-token)))))) (defun haskell-indentation-case-alternative () + "" ; FIXME (setq left-indent (current-column)) (haskell-indentation-separated #'haskell-indentation-expression "," nil) (cond ((eq current-token 'end-tokens) @@ -828,23 +946,24 @@ the current buffer." )) (defun haskell-indentation-case () + "" ; FIXME (haskell-indentation-expression) (cond ((eq current-token 'end-tokens) (haskell-indentation-add-indentation current-indent)) ((string= current-token "|") - (haskell-indentation-with-starter - (lambda () - (haskell-indentation-separated #'haskell-indentation-case-alternative "|" nil)) - nil)) + (haskell-indentation-with-starter + (apply-partially #'haskell-indentation-separated + #'haskell-indentation-case-alternative "|" nil) + nil)) ((string= current-token "->") (haskell-indentation-statement-right #'haskell-indentation-expression)) ;; otherwise fallthrough )) -;; the right side of a statement. Sets current-indent -;; to the current column and cals the given parser. -;; if parsing ends here, set indentation to left-indent. (defun haskell-indentation-statement-right (parser) + "Process right side of a statement. +Set `current-indent' to the current column and calls the given +parser. If parsing ends here, set indentation to left-indent." (haskell-indentation-read-next-token) (when (eq current-token 'end-tokens) (haskell-indentation-add-left-indent) @@ -853,77 +972,82 @@ the current buffer." (let ((current-indent (current-column))) (funcall parser))) - (defun haskell-indentation-guard () + "Parse \"guard\" statement." (setq left-indent (current-column)) (haskell-indentation-separated #'haskell-indentation-expression "," nil)) -;; function or type declaration (defun haskell-indentation-declaration () + "Parse function or type declaration." (haskell-indentation-separated #'haskell-indentation-expression "," nil) (cond ((string= current-token "|") (haskell-indentation-with-starter - (lambda () (haskell-indentation-separated - #'haskell-indentation-guard "|" nil)) + (apply-partially #'haskell-indentation-separated + #'haskell-indentation-guard "|" nil) nil)) ((eq current-token 'end-tokens) (when (member following-token '("|" "=" "::" ",")) (haskell-indentation-add-indentation current-indent) (throw 'parse-end nil))))) -;; enter a layout list, where each layout item is parsed by parser. (defun haskell-indentation-layout (parser) + "Parse layout list, where each layout item is parsed by parser." (if (string= current-token "{") - (haskell-indentation-list parser "}" ";") ;; explicit layout + (haskell-indentation-list parser "}" ";") ; explicit layout (haskell-indentation-implicit-layout-list parser))) -(defun haskell-indentation-expression-token (token) - (member token '("if" "let" "do" "case" "\\" "(" "{" "[" "::" - value operator no-following-token))) +(defun haskell-indentation-expression-token-p (token) + "Return non-NIL value if TOKEN is an expression token." + (member token + '("if" "let" "do" "case" "\\" "(" "{" "[" "::" + value operator no-following-token))) -;; parse an expression until an unknown token is encountered. (defun haskell-indentation-expression () + "Parse an expression until an unknown token is encountered." (let ((current-indent (current-column))) (catch 'return (while t (cond ((memq current-token '(value operator)) (haskell-indentation-read-next-token)) - ((eq current-token 'end-tokens) (cond ((string= following-token "where") (haskell-indentation-add-where-pre-indent)) ; before a where - ((haskell-indentation-expression-token following-token) + ((haskell-indentation-expression-token-p following-token) (haskell-indentation-add-indentation - current-indent))) ;; a normal expression + current-indent))) ; a normal expression (throw 'return nil)) - - (t (let ((parser (assoc current-token haskell-indentation-expression-list))) + (t (let ((parser (assoc current-token + haskell-indentation-expression-list))) (when (null parser) (throw 'return nil)) ; not expression token, so exit - (funcall (cdr parser)) ; run parser + (funcall (cdr parser)) ; run parser (when (and (eq current-token 'end-tokens) (string= (car parser) "let") (= haskell-indentation-layout-offset current-indent) - (haskell-indentation-expression-token following-token)) + (haskell-indentation-expression-token-p following-token)) ;; inside a layout, after a let construct - ;; for example: do let a = 20 + ;; for example: "do let a = 20" (haskell-indentation-add-layout-indent) (throw 'parse-end nil)) - - ;; after an 'open' expression such as 'if', exit + ;; after an 'open' expression such as 'if', exit (unless (member (car parser) '("(" "[" "{" "do" "case")) (throw 'return nil))))))))) (defun haskell-indentation-test-indentations () + "Insert markers on a fresh line indicating indentation positions. +Use for testing." (interactive) - (let ((indentations (save-excursion (haskell-indentation-find-indentations-safe))) + (let ((indentations + (save-excursion + (haskell-indentation-find-indentations-safe))) (str "") (pos 0)) (while indentations (when (>= (car indentations) pos) - (setq str (concat str (make-string (- (car indentations) pos) ?\ ) + (setq str (concat str + (make-string (- (car indentations) pos) ?\ ) "|")) (setq pos (+ 1 (car indentations)))) (setq indentations (cdr indentations))) @@ -931,14 +1055,14 @@ the current buffer." (newline) (insert str))) - -;; evaluate parser separated by separator and stmt-separator. -;; if stmt-separator is not nil, it will be used to set a -;; new starter-indent. -;; for example -;; [ i | i <- [1..10] -;; , (defun haskell-indentation-separated (parser separator &optional stmt-separator) + "Evaluate PARSER separated by SEPARATOR and STMT-SEPARATOR. +If STMT-SEPARATOR is not NIL, it will be used to set a new starter-indent. + +For example: + +[ i | i <- [1..10] + ," (catch 'return (unless (listp separator) (setq separator (list separator))) @@ -956,25 +1080,23 @@ the current buffer." ((eq current-token 'end-tokens) (cond ((or (member following-token separator) (member following-token stmt-separator)) - ;; set an indentation before a separator, - ;; for example: - ;; [ 1 or [ 1 | a - ;; , 2 , 20 + ;; Set an indentation before a separator, for example: + ;; [ 1 or [ 1 | a + ;; , 2 , 20 (haskell-indentation-add-indentation starter-indent) (throw 'parse-end nil))) (throw 'return nil)) - (t (throw 'return nil)))))) -;; At a separator. -;; If at a new line, set starter-indent at the separator -;; and current-indent after the separator -;; For example: -;; l = [ 1 -;; , 2 -;; , -- start now here - (defun haskell-indentation-at-separator () + "At a separator. + +If at a new line, set starter-indent at the separator +and current-indent after the separator, for example: + +l = [ 1 + , 2 + , -- start now here." (let ((separator-column (and (= (current-column) (haskell-indentation-current-indentation)) (current-column)))) @@ -982,13 +1104,14 @@ the current buffer." (cond ((eq current-token 'end-tokens) (haskell-indentation-add-indentation current-indent) (throw 'return nil)) - (separator-column ;; on the beginning of the line + (separator-column ; on the beginning of the line (setq current-indent (current-column)) (setq starter-indent separator-column))))) -;; An implicit layout list. This sets the layout-indent -;; variable to the column where the layout starts. (defun haskell-indentation-implicit-layout-list (parser) + "An implicit layout list, elements are parsed with PARSER. +This sets the `layout-indent' variable to the column where the +layout starts." (let* ((layout-indent (current-column)) (current-indent (current-column)) (left-indent (current-column)) @@ -1000,37 +1123,41 @@ the current buffer." (cond ((member current-token '(layout-item ";")) (haskell-indentation-read-next-token)) ((eq current-token 'end-tokens) - (when (or (haskell-indentation-expression-token following-token) + (when (or (haskell-indentation-expression-token-p following-token) (string= following-token ";")) (haskell-indentation-add-layout-indent)) (throw 'return nil)) (t (throw 'return nil)))))) - ;; put haskell-indentation-read-next-token outside the current-indent definition - ;; so it will not return 'layout-end again + ;; put `haskell-indentation-read-next-token' outside the current-indent + ;; definition so it will not return 'layout-end again (when (eq current-token 'layout-end) - (haskell-indentation-read-next-token))) ;; leave layout at 'layout-end or illegal token + ;; leave layout at 'layout-end or illegal token + (haskell-indentation-read-next-token))) (defun haskell-indentation-if () + "" ; FIXME (haskell-indentation-with-starter (lambda () (if (string= current-token "|") - (haskell-indentation-with-starter - (lambda () - (haskell-indentation-separated - #'haskell-indentation-case-alternative "|" nil)) - nil) + (haskell-indentation-with-starter + (lambda () + (haskell-indentation-separated + #'haskell-indentation-case-alternative "|" nil)) + nil) (haskell-indentation-phrase-rest - '(haskell-indentation-expression - "then" haskell-indentation-expression - "else" haskell-indentation-expression)))) + '(haskell-indentation-expression + "then" haskell-indentation-expression + "else" haskell-indentation-expression)))) nil)) (defun haskell-indentation-phrase (phrase) + "" ; FIXME (haskell-indentation-with-starter - `(lambda () (haskell-indentation-phrase-rest ',phrase)) + (apply-partially #'haskell-indentation-phrase-rest phrase) nil)) (defun haskell-indentation-phrase-rest (phrase) + "" ; FIXME (let ((starter-line parse-line-number)) (let ((current-indent (current-column))) (funcall (car phrase))) @@ -1045,59 +1172,59 @@ the current buffer." (haskell-indentation-add-layout-indent) (throw 'parse-end nil))) (t (throw 'parse-end nil)))) - ((null (cdr phrase))) - ((equal (cadr phrase) current-token) - (let* ((on-new-line (= (current-column) (haskell-indentation-current-indentation))) + (let* ((on-new-line (= (current-column) + (haskell-indentation-current-indentation))) (lines-between (- parse-line-number starter-line)) (left-indent (if (<= lines-between 0) left-indent starter-indent))) (haskell-indentation-read-next-token) - (when (eq current-token 'end-tokens) + (when (eq current-token 'end-tokens) (cond ((member (cadr phrase) '("then" "else")) - (haskell-indentation-add-indentation - (+ starter-indent haskell-indentation-ifte-offset))) - - ((member (cadr phrase) '("in" "->")) - ;; expression ending in another expression - (when (or (not haskell-indentation-indent-leftmost) - (eq haskell-indentation-indent-leftmost 'both)) - (haskell-indentation-add-indentation - (+ starter-indent haskell-indentation-starter-offset))) - (when haskell-indentation-indent-leftmost - (haskell-indentation-add-indentation - (if on-new-line - (+ left-indent haskell-indentation-starter-offset) - left-indent)))) - - (t - (when (or (not haskell-indentation-indent-leftmost) - (eq haskell-indentation-indent-leftmost 'both)) - (haskell-indentation-add-indentation - (+ starter-indent haskell-indentation-starter-offset))) - (when haskell-indentation-indent-leftmost - (haskell-indentation-add-indentation - (if on-new-line - (+ left-indent haskell-indentation-starter-offset) - left-indent))))) + (haskell-indentation-add-indentation + (+ starter-indent haskell-indentation-ifte-offset))) + + ((member (cadr phrase) '("in" "->")) + ;; expression ending in another expression + (when (or (not haskell-indentation-indent-leftmost) + (eq haskell-indentation-indent-leftmost 'both)) + (haskell-indentation-add-indentation + (+ starter-indent haskell-indentation-starter-offset))) + (when haskell-indentation-indent-leftmost + (haskell-indentation-add-indentation + (if on-new-line + (+ left-indent haskell-indentation-starter-offset) + left-indent)))) + (t + (when (or (not haskell-indentation-indent-leftmost) + (eq haskell-indentation-indent-leftmost 'both)) + (haskell-indentation-add-indentation + (+ starter-indent haskell-indentation-starter-offset))) + (when haskell-indentation-indent-leftmost + (haskell-indentation-add-indentation + (if on-new-line + (+ left-indent haskell-indentation-starter-offset) + left-indent))))) (throw 'parse-end nil)) (haskell-indentation-phrase-rest (cddr phrase)))) - - ((string= (cadr phrase) "in")) ;; fallthrough + ((string= (cadr phrase) "in")) ; fallthrough (t (haskell-indentation-parse-error "Expecting %s" (cadr phrase)))))) (defun haskell-indentation-add-indentation (indent) + "" ; FIXME (haskell-indentation-push-indentation (if (<= indent layout-indent) (+ layout-indent haskell-indentation-layout-offset) indent))) (defun haskell-indentation-add-layout-indent () + "" ; FIXME (haskell-indentation-push-indentation layout-indent)) (defun haskell-indentation-add-where-pre-indent () + "" ; FIXME (haskell-indentation-push-indentation (+ layout-indent haskell-indentation-where-pre-offset)) (if (= layout-indent haskell-indentation-layout-offset) @@ -1105,20 +1232,24 @@ the current buffer." haskell-indentation-where-pre-offset))) (defun haskell-indentation-add-where-post-indent (indent) + "" ; FIXME (haskell-indentation-push-indentation (+ indent haskell-indentation-where-post-offset))) (defun haskell-indentation-add-left-indent () + "" ; FIXME (haskell-indentation-add-indentation (+ left-indent haskell-indentation-left-offset))) (defun haskell-indentation-push-indentation (indent) + "" ; FIXME (when (or (null possible-indentations) (< indent (car possible-indentations))) (setq possible-indentations (cons indent possible-indentations)))) (defun haskell-indentation-token-test () + "" ; FIXME (let ((current-token nil) (following-token nil) (layout-indent 0) @@ -1126,24 +1257,27 @@ the current buffer." (indentation-point (mark))) (haskell-indentation-read-next-token))) -;; Go to the next token and set current-token to the next token. -;; The following symbols are used as pseudo tokens: -;; -;; 'layout-item: A new item in a layout list. The next token -;; will be the first token from the item. -;; 'layout-end: the end of a layout list. Next token will be -;; the first token after the layout list. -;; 'end-tokens: back at point where we started, following-token -;; will be set to the next token. -;; -;; Pseudo tokens are used only when implicit-layout-active is t. That -;; is the case only after keywords 'do', 'where', 'let' and 'of'. -;; -;; if we are at a new line, parse-line is increased, and -;; current-indent and left-indent are set to the indentation -;; of the line. - (defun haskell-indentation-read-next-token () + "Go to the next token and set current-token to the next token. + +The following symbols are used as pseudo tokens: + +'layout-item: A new item in a layout list. The next token + will be the first token from the item. + +'layout-end: the end of a layout list. Next token will be + the first token after the layout list. + +'end-tokens: back at point where we started, following-token + will be set to the next token. + +Pseudo tokens are used only when implicit-layout-active is +t. That is the case only after keywords \"do\", \"where\", +\"let\" and \"of\". + +If we are at a new line, parse-line is increased, and +current-indent and left-indent are set to the indentation of the +line." (cond ((and implicit-layout-active (eq current-token 'end-tokens)) 'end-tokens) @@ -1201,12 +1335,13 @@ the current buffer." (defun haskell-indentation-skip-token () "Skip to the next token." (let ((case-fold-search nil)) - (if (or (looking-at "'\\([^\\']\\|\\\\.\\)*'") (looking-at "\"\\([^\\\"]\\|\\\\.\\)*\"") - (looking-at ; Hierarchical names always start with uppercase + ;; Hierarchical names always start with uppercase + (looking-at "[[:upper:]]\\(\\s_\\|\\sw\\|'\\)*\\(\\.\\(\\s_\\|\\sw\\|'\\)+\\)*") - (looking-at "\\(\\s_\\|\\sw\\)\\(\\s_\\|\\sw\\|'\\)*") ; Only unqualified vars can start with lowercase + ;; Only unqualified vars can start with lowercase. + (looking-at "\\(\\s_\\|\\sw\\)\\(\\s_\\|\\sw\\|'\\)*") (looking-at "[0-9][0-9oOxXeE+-]*") (looking-at "[-:!#$%&*+./<=>?@\\\\^|~]+") (looking-at "[](){}[,;]") @@ -1215,7 +1350,7 @@ the current buffer." ;; otherwise skip until space found (skip-syntax-forward "^-")) (forward-comment (buffer-size)) - (while (and (haskell-indentation-birdp) + (while (and (haskell-indentation-bird-p) (bolp) (eq (char-after) ?>)) (forward-char) diff --git a/haskell-mode.el b/haskell-mode.el index d10adf59e..5bcffbbca 100644 --- a/haskell-mode.el +++ b/haskell-mode.el @@ -172,7 +172,7 @@ With prefix argument HERE, insert it at point." ;; Are we looking at a literate script? (defvar haskell-literate nil - "*If not nil, the current buffer contains a literate Haskell script. + "If not nil, the current buffer contains a literate Haskell script. Possible values are: `bird' and `tex', for Bird-style and LaTeX-style literate scripts respectively. Set by `haskell-mode' and `literate-haskell-mode'. For an ambiguous literate buffer -- i.e. does