You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1174 lines
46 KiB
1174 lines
46 KiB
;;; haskell-indentation.el --- indentation module for Haskell Mode -*- lexical-binding: t -*- |
|
|
|
;; Copyright (C) 2013 Kristof Bastiaensen, Gergely Risko |
|
|
|
;; Author: Kristof Bastiaensen <kristof.bastiaensen@vleeuwen.org> |
|
;; Author: Gergely Risko <errge@nilcons.com> |
|
;; 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 |
|
;; it under the terms of the GNU General Public License as published by |
|
;; the Free Software Foundation; either version 3, or (at your option) |
|
;; any later version. |
|
|
|
;; This file is distributed in the hope that it will be useful, |
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
;; GNU General Public License for more details. |
|
|
|
;; You should have received a copy of the GNU General Public License |
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
;;; Commentary: |
|
|
|
;; Installation: |
|
;; |
|
;; 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) |
|
;; |
|
;; 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 'cl-lib) |
|
|
|
;;;###autoload |
|
(defgroup haskell-indentation nil |
|
"Haskell indentation." |
|
:link '(custom-manual "(haskell-mode)Indentation") |
|
:group 'haskell |
|
:prefix "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 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." |
|
:type 'integer |
|
:group 'haskell-indentation) |
|
|
|
;;;###autoload |
|
(defcustom haskell-indentation-starter-offset 2 |
|
"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\")." |
|
:type 'integer |
|
:group 'haskell-indentation) |
|
|
|
;;;###autoload |
|
(defcustom haskell-indentation-ifte-offset 2 |
|
"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\"." |
|
:type 'integer |
|
:group 'haskell-indentation) |
|
|
|
;;;###autoload |
|
(defcustom haskell-indentation-where-post-offset 2 |
|
"Extra indentation after the keyword \"where\"." |
|
:type 'integer |
|
:group 'haskell-indentation) |
|
|
|
(defconst haskell-indentation-mode-map |
|
(let ((keymap (make-sparse-keymap))) |
|
(define-key keymap (kbd "RET") 'haskell-indentation-newline-and-indent) |
|
(define-key keymap (kbd "<backtab>") 'haskell-indentation-indent-backwards) |
|
keymap)) |
|
|
|
;;;###autoload |
|
(define-minor-mode haskell-indentation-mode |
|
"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." |
|
:keymap haskell-indentation-mode-map |
|
(kill-local-variable 'indent-line-function) |
|
(kill-local-variable 'indent-region-function) |
|
|
|
(when haskell-indentation-mode |
|
(set (make-local-variable 'indent-line-function) |
|
'haskell-indentation-indent-line) |
|
(set (make-local-variable 'indent-region-function) |
|
'haskell-indentation-indent-region))) |
|
|
|
;;;###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) ; 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 |
|
|
|
(defun haskell-indentation-reindent-to (col &optional move) |
|
"Reindent current line to COL, move the point there if MOVE is non-NIL." |
|
(let* ((ci (haskell-indentation-current-indentation))) |
|
(save-excursion |
|
(move-to-column ci) |
|
(if (<= ci col) |
|
(insert-before-markers (make-string (- col ci) ? )) |
|
(delete-char (- col ci)))) |
|
(when move |
|
(move-to-column col)))) |
|
|
|
(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." |
|
(interactive "r\np") |
|
(save-excursion |
|
(goto-char end) |
|
(let ((end-marker (point-marker))) |
|
(goto-char start) |
|
(or (bolp) (forward-line 0)) |
|
(while (< (point) end-marker) |
|
(let ((ci (haskell-indentation-current-indentation))) |
|
(when (and t |
|
(eq (char-after) ?>)) |
|
(forward-char 1)) |
|
(skip-syntax-forward "-") |
|
(unless (eolp) |
|
(haskell-indentation-reindent-to (max 0 (+ ci arg)))) |
|
(forward-line 1))) |
|
(move-marker end-marker nil)))) |
|
|
|
(defun haskell-indentation-current-indentation () |
|
"Column position of first non-whitespace character in current line." |
|
(save-excursion |
|
(beginning-of-line) |
|
(when (haskell-indentation-bird-p) |
|
(forward-char)) |
|
(skip-syntax-forward "-") |
|
(current-column))) |
|
|
|
(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-newline-and-indent () |
|
"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-code-p) |
|
(progn |
|
(delete-horizontal-space) |
|
(newline)) |
|
(catch 'parse-error |
|
;; - save the current column |
|
(let* ((ci (haskell-indentation-current-indentation)) |
|
(indentations (haskell-indentation-find-indentations-safe))) |
|
;; - jump to the next line and reindent to at the least same level |
|
(delete-horizontal-space) |
|
(newline) |
|
(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. |
|
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")) |
|
(let ((rev (reverse indentations))) |
|
(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 () |
|
"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 |
|
indentation points to the right, we switch going to the left." |
|
(interactive) |
|
;; try to repeat |
|
(when (not (haskell-indentation-indent-line-repeat)) |
|
(setq haskell-indentation-dyn-last-direction nil) |
|
;; do nothing if we're inside a string or comment |
|
(unless (save-excursion |
|
(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 |
|
(let* ((cc (current-column)) |
|
(ci (haskell-indentation-current-indentation)) |
|
(inds (save-excursion |
|
(move-to-column ci) |
|
(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 |
|
;; (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 |
|
haskell-indentation-dyn-first-position |
|
(haskell-indentation-current-indentation) |
|
haskell-indentation-dyn-last-indentations inds))))) |
|
|
|
(defun haskell-indentation-indent-line-repeat () |
|
"Cycle though indentation positions." |
|
(cond |
|
((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)) |
|
t) |
|
((and (eq last-command 'indent-for-tab-command) |
|
(memq haskell-indentation-dyn-last-direction '(left right)) |
|
haskell-indentation-dyn-last-indentations) |
|
(let ((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)) |
|
;; 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)) |
|
;; 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)) |
|
(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." |
|
(interactive) |
|
(cond |
|
((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))) |
|
((use-region-p) |
|
(setq haskell-indentation-dyn-last-direction 'region) |
|
(haskell-indentation-indent-rigidly (region-beginning) (region-end) -1) |
|
(message "Press TAB or S-TAB again to indent the region more")) |
|
(t |
|
(setq haskell-indentation-dyn-last-direction nil) |
|
(let* ((cc (current-column)) |
|
(ci (haskell-indentation-current-indentation)) |
|
(inds (save-excursion |
|
(move-to-column ci) |
|
(haskell-indentation-find-indentations-safe))) |
|
(cursor-in-whitespace (< cc ci)) |
|
(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 pi cursor-in-whitespace)))))) |
|
|
|
|
|
;;---------------------------------------------------------------------------- |
|
;; 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 |
|
;; 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-bird-p) |
|
(catch 'return |
|
(while t |
|
(when (not (eq (char-after) ?>)) |
|
(forward-line) |
|
(forward-char 2) |
|
(throw 'return nil)) |
|
(let ((ps (nth 8 (syntax-ppss)))) |
|
(when ps ;; inside comment or string |
|
(goto-char ps) |
|
(beginning-of-line))) |
|
(when (and (>= 2 (haskell-indentation-current-indentation)) |
|
(not (looking-at ">\\s-*$"))) |
|
(forward-char 2) |
|
(throw 'return nil)) |
|
(when (bobp) |
|
(forward-char 2) |
|
(throw 'return nil)) |
|
(forward-line -1))) |
|
;; not bird style |
|
(catch 'return |
|
(while (not (bobp)) |
|
(forward-comment (- (buffer-size))) |
|
(beginning-of-line) |
|
(let* ((ps (syntax-ppss)) |
|
(start-of-comment-or-string (nth 8 ps)) |
|
(start-of-list-expression (nth 1 ps))) |
|
(cond |
|
(start-of-comment-or-string |
|
;; inside comment or string |
|
(goto-char start-of-comment-or-string)) |
|
(start-of-list-expression |
|
;; inside a parenthesized expression |
|
(goto-char start-of-list-expression)) |
|
((= 0 (haskell-indentation-current-indentation)) |
|
(throw 'return nil)))))) |
|
(beginning-of-line) |
|
(when (bobp) |
|
(forward-comment (buffer-size))))) |
|
|
|
(defun haskell-indentation-parse-to-indentations () |
|
"" ; FIXME |
|
(save-excursion |
|
(skip-syntax-forward "-") |
|
(let ((indentation-point (point)) |
|
(layout-indent 0) |
|
(parse-line-number 0) |
|
(current-indent haskell-indentation-layout-offset) |
|
(starter-indent haskell-indentation-layout-offset) |
|
(left-indent haskell-indentation-layout-offset) |
|
(case-fold-search nil) |
|
current-token |
|
following-token |
|
possible-indentations |
|
implicit-layout-active) |
|
(haskell-indentation-goto-least-indentation) |
|
(if (<= indentation-point (point)) |
|
(haskell-indentation-first-indentation) |
|
(setq current-token (haskell-indentation-peek-token)) |
|
(catch 'parse-end |
|
(haskell-indentation-toplevel) |
|
(unless (eq current-token 'end-tokens) |
|
(haskell-indentation-parse-error |
|
"Illegal token: %s" current-token))) |
|
possible-indentations)))) |
|
|
|
(defun haskell-indentation-first-indentation () |
|
"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) |
|
(haskell-indentation-first-indentation)) |
|
((nth 4 ppss) |
|
(if (save-excursion |
|
(and (skip-syntax-forward "-") |
|
(eolp) |
|
(not (> (forward-line 1) 0)) |
|
(not (nth 4 (syntax-ppss))))) |
|
(haskell-indentation-parse-to-indentations) |
|
(haskell-indentation-first-indentation))) |
|
(t |
|
(haskell-indentation-parse-to-indentations))))) |
|
|
|
;; XXX: this is a hack, the parser shouldn't return nil without parse-error |
|
(defun haskell-indentation-find-indentations-safe () |
|
(or (haskell-indentation-find-indentations) |
|
(haskell-indentation-first-indentation))) |
|
|
|
(defconst haskell-indentation-unicode-tokens |
|
'(("→" . "->") ;; #x2192 RIGHTWARDS ARROW |
|
("∷" . "::") ;; #x2237 PROPORTION |
|
("←" . "<-") ;; #x2190 LEFTWARDS ARROW |
|
("⇒" . "=>") ;; #x21D2 RIGHTWARDS DOUBLE ARROW |
|
("∀" . "forall") ;; #x2200 FOR ALL |
|
("⤙" . "-<") ;; #x2919 LEFTWARDS ARROW-TAIL |
|
("⤚" . ">-") ;; #x291A RIGHTWARDS ARROW-TAIL |
|
("⤛" . "-<<") ;; #x291B LEFTWARDS DOUBLE ARROW-TAIL |
|
("⤜" . ">>-") ;; #x291C RIGHTWARDS DOUBLE ARROW-TAIL |
|
("★" . "*")) ;; #x2605 BLACK STAR |
|
"Translation from UnicodeSyntax tokens to their ASCII representation.") |
|
|
|
(defconst haskell-indentation-toplevel-list |
|
`(("module" . haskell-indentation-module) |
|
("data" . haskell-indentation-data) |
|
("type" . haskell-indentation-data) |
|
("newtype" . 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 |
|
`(("::" . |
|
,(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.") |
|
|
|
(defconst haskell-indentation-expression-list |
|
`(("data" . haskell-indentation-data) |
|
("type" . haskell-indentation-data) |
|
("newtype" . haskell-indentation-data) |
|
("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)) |
|
("::" . haskell-indentation-scoped-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)) |
|
|
|
(defun haskell-indentation-declaration-layout () |
|
"Parse layout list with declarations, such as after \"where\"." |
|
(haskell-indentation-layout #'haskell-indentation-declaration)) |
|
|
|
(defun haskell-indentation-case-layout () |
|
"Parse layout list with case expressions." |
|
(haskell-indentation-layout #'haskell-indentation-case)) |
|
|
|
(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)))) |
|
|
|
(defun haskell-indentation-fundep () |
|
"Parse functional dependency." |
|
(haskell-indentation-with-starter |
|
(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)) |
|
(when (and (eq current-token 'end-tokens) |
|
(member following-token '(value "->"))) |
|
(haskell-indentation-add-indentation current-indent)))) |
|
|
|
(defun haskell-indentation-toplevel () |
|
"Parse toplevel statements." |
|
(haskell-indentation-layout |
|
(lambda () |
|
(let ((parser (assoc current-token haskell-indentation-toplevel-list))) |
|
(if parser |
|
(funcall (cdr parser)) |
|
(haskell-indentation-declaration)))))) |
|
|
|
(defun haskell-indentation-type () |
|
"Parse type declaration." |
|
(let ((current-indent (current-column))) |
|
(catch 'return |
|
(while t |
|
(cond |
|
((member current-token '(value operator "->")) |
|
(haskell-indentation-read-next-token)) |
|
|
|
((eq current-token 'end-tokens) |
|
(when (member following-token |
|
'(value operator no-following-token |
|
"->" "(" "[" "{" "::")) |
|
(haskell-indentation-add-indentation current-indent)) |
|
(throw 'return nil)) |
|
(t (let ((parser (assoc current-token haskell-indentation-type-list))) |
|
(if (not parser) |
|
(throw 'return nil) |
|
(funcall (cdr parser)))))))))) |
|
|
|
(defun haskell-indentation-scoped-type () |
|
"Parse scoped type declaration. |
|
|
|
For example |
|
let x :: Int = 12 |
|
do x :: Int <- return 12" |
|
(haskell-indentation-with-starter |
|
(apply-partially #'haskell-indentation-separated #'haskell-indentation-type "->")) |
|
(when (member current-token '("<-" "=")) |
|
(haskell-indentation-statement-right #'haskell-indentation-expression))) |
|
|
|
(defun haskell-indentation-data () |
|
"Parse data or type declaration." |
|
(haskell-indentation-with-starter |
|
(lambda () |
|
(when (string= current-token "instance") |
|
(haskell-indentation-read-next-token)) |
|
(haskell-indentation-type) |
|
(cond ((string= current-token "=") |
|
(haskell-indentation-with-starter |
|
(apply-partially #'haskell-indentation-separated |
|
#'haskell-indentation-type "|" "deriving"))) |
|
((string= current-token "where") |
|
(haskell-indentation-with-starter |
|
#'haskell-indentation-expression-layout nil)))))) |
|
|
|
(defun haskell-indentation-class-declaration () |
|
"Parse class declaration." |
|
(haskell-indentation-with-starter |
|
(lambda () |
|
(haskell-indentation-type) |
|
(when (string= current-token "|") |
|
(haskell-indentation-fundep)) |
|
(when (string= current-token "where") |
|
(haskell-indentation-with-starter |
|
#'haskell-indentation-declaration-layout nil))))) |
|
|
|
(defun haskell-indentation-module () |
|
"Parse module declaration." |
|
(haskell-indentation-with-starter |
|
(lambda () |
|
(let ((current-indent (current-column))) |
|
(haskell-indentation-read-next-token) |
|
(when (string= current-token "(") |
|
(haskell-indentation-list |
|
#'haskell-indentation-module-export |
|
")" ",")) |
|
(when (eq current-token 'end-tokens) |
|
(haskell-indentation-add-indentation current-indent) |
|
(throw 'parse-end nil)) |
|
(when (string= current-token "where") |
|
(haskell-indentation-read-next-token) |
|
(when (eq current-token 'end-tokens) |
|
(haskell-indentation-add-layout-indent) |
|
(throw 'parse-end nil)) |
|
(haskell-indentation-layout #'haskell-indentation-toplevel)))))) |
|
|
|
(defun haskell-indentation-module-export () |
|
"Parse export list." |
|
(cond ((string= current-token "module") |
|
(let ((current-indent (current-column))) |
|
(haskell-indentation-read-next-token) |
|
(cond ((eq current-token 'end-tokens) |
|
(haskell-indentation-add-indentation current-indent)) |
|
((eq current-token 'value) |
|
(haskell-indentation-read-next-token))))) |
|
(t (haskell-indentation-type)))) |
|
|
|
(defun haskell-indentation-list (parser end sep &optional stmt-sep) |
|
"Parse a list, pair or other expression containing multiple |
|
items parsed by PARSER, separated by SEP or STMT-SEP, and ending |
|
with END." |
|
;; note that we use macro expansion here to preserver Emacs 23 |
|
;; compatibility and its lack of lexical binding |
|
(haskell-indentation-with-starter |
|
`(lambda () |
|
(let ((implicit-layout-active nil)) |
|
(haskell-indentation-separated |
|
#',parser ,sep ,stmt-sep))) |
|
end)) |
|
|
|
(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))) |
|
(haskell-indentation-read-next-token) |
|
(when (eq current-token 'end-tokens) |
|
(cond ((equal following-token end) |
|
;; 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))) |
|
(funcall parser) |
|
(cond ((eq current-token 'end-tokens) |
|
(when (equal following-token end) |
|
;; 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)))))) |
|
|
|
(defun haskell-indentation-case-alternative () |
|
"" ; FIXME |
|
(setq left-indent (current-column)) |
|
(haskell-indentation-separated #'haskell-indentation-expression "," nil) |
|
(cond ((eq current-token 'end-tokens) |
|
(haskell-indentation-add-indentation current-indent)) |
|
((string= current-token "->") |
|
(haskell-indentation-statement-right #'haskell-indentation-expression)) |
|
;; otherwise fallthrough |
|
)) |
|
|
|
(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 |
|
(apply-partially #'haskell-indentation-separated |
|
#'haskell-indentation-case-alternative "|" nil) |
|
nil)) |
|
((string= current-token "->") |
|
(haskell-indentation-statement-right #'haskell-indentation-expression)) |
|
;; otherwise fallthrough |
|
)) |
|
|
|
(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) |
|
(haskell-indentation-add-indentation current-indent) |
|
(throw 'parse-end nil)) |
|
(let ((current-indent (current-column))) |
|
(funcall parser) |
|
(when (equal current-token "where") |
|
(haskell-indentation-with-starter |
|
#'haskell-indentation-expression-layout nil)))) |
|
|
|
(defun haskell-indentation-guard () |
|
"Parse \"guard\" statement." |
|
(setq left-indent (current-column)) |
|
(haskell-indentation-separated |
|
#'haskell-indentation-expression "," nil)) |
|
|
|
(defun haskell-indentation-declaration () |
|
"Parse function or type declaration." |
|
(haskell-indentation-separated #'haskell-indentation-expression "," nil) |
|
(cond ((string= current-token "|") |
|
(haskell-indentation-with-starter |
|
(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))))) |
|
|
|
(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-implicit-layout-list parser))) |
|
|
|
(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))) |
|
|
|
(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-p following-token) |
|
(haskell-indentation-add-indentation |
|
current-indent))) ; a normal expression |
|
(throw 'return nil)) |
|
(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 |
|
(when (and (eq current-token 'end-tokens) |
|
(string= (car parser) "let") |
|
(= haskell-indentation-layout-offset current-indent) |
|
(haskell-indentation-expression-token-p following-token)) |
|
;; inside a layout, after a let construct |
|
;; for example: "do let a = 20" |
|
(haskell-indentation-add-layout-indent) |
|
(throw 'parse-end nil)) |
|
;; after an 'open' expression such as 'if', exit |
|
(unless (member (car parser) '("(" "[" "{" "case")) |
|
(throw 'return nil))))))))) |
|
|
|
(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))) |
|
(unless (listp stmt-separator) |
|
(setq stmt-separator (list stmt-separator))) |
|
(while t |
|
(funcall parser) |
|
(cond ((member current-token separator) |
|
(haskell-indentation-at-separator)) |
|
|
|
((member current-token stmt-separator) |
|
(setq starter-indent (current-column)) |
|
(haskell-indentation-at-separator)) |
|
|
|
((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 |
|
(haskell-indentation-add-indentation starter-indent) |
|
(throw 'parse-end nil))) |
|
(throw 'return nil)) |
|
(t (throw 'return nil)))))) |
|
|
|
(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)))) |
|
(haskell-indentation-read-next-token) |
|
(cond ((eq current-token 'end-tokens) |
|
(haskell-indentation-add-indentation current-indent) |
|
(throw 'return nil)) |
|
(separator-column ; on the beginning of the line |
|
(setq current-indent (current-column)) |
|
(setq starter-indent separator-column))))) |
|
|
|
(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)) |
|
(implicit-layout-active t)) |
|
(catch 'return |
|
(while t |
|
(let ((left-indent left-indent)) |
|
(funcall parser)) |
|
(cond ((member current-token '(layout-item ";")) |
|
(haskell-indentation-read-next-token)) |
|
((eq current-token 'end-tokens) |
|
(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 |
|
(when (eq current-token 'layout-end) |
|
(let ((implicit-layout-active t)) |
|
;; 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-phrase-rest |
|
'(haskell-indentation-expression |
|
"then" haskell-indentation-expression |
|
"else" haskell-indentation-expression)))) |
|
nil)) |
|
|
|
(defun haskell-indentation-phrase (phrase) |
|
"" ; FIXME |
|
(haskell-indentation-with-starter |
|
(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))) |
|
(cond |
|
((eq current-token 'end-tokens) |
|
(cond ((null (cdr phrase))) ;; fallthrough |
|
((equal following-token (cadr phrase)) |
|
(haskell-indentation-add-indentation starter-indent) |
|
(throw 'parse-end nil)) |
|
((string= (cadr phrase) "in") |
|
(when (= left-indent layout-indent) |
|
(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))) |
|
(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) |
|
(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))))) |
|
(throw 'parse-end nil)) |
|
(haskell-indentation-phrase-rest (cddr phrase)))) |
|
((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) |
|
(haskell-indentation-push-indentation |
|
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-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) |
|
((and implicit-layout-active |
|
(eq current-token 'layout-end)) |
|
(cond ((> layout-indent (current-column)) |
|
'layout-end) |
|
((= layout-indent (current-column)) |
|
(setq current-token 'layout-item)) |
|
((< layout-indent (current-column)) |
|
(setq current-token (haskell-indentation-peek-token))))) |
|
((and implicit-layout-active |
|
(eq current-token 'layout-item)) |
|
(setq current-token (haskell-indentation-peek-token))) |
|
((and implicit-layout-active |
|
(> layout-indent (current-column))) |
|
(setq current-token 'layout-end)) |
|
(t |
|
(haskell-indentation-skip-token) |
|
(if (>= (point) indentation-point) |
|
(progn |
|
(setq following-token |
|
(if (= (point) indentation-point) |
|
(haskell-indentation-peek-token) |
|
'no-following-token)) |
|
(setq current-token 'end-tokens)) |
|
(when (= (current-column) (haskell-indentation-current-indentation)) |
|
;; on a new line |
|
(setq current-indent (current-column)) |
|
(setq left-indent (current-column)) |
|
(setq parse-line-number (+ parse-line-number 1))) |
|
(cond ((and implicit-layout-active |
|
(> layout-indent (current-column))) |
|
(setq current-token 'layout-end)) |
|
((and implicit-layout-active |
|
(= layout-indent (current-column))) |
|
(setq current-token 'layout-item)) |
|
(t (setq current-token (haskell-indentation-peek-token)))))))) |
|
|
|
(defun haskell-indentation-peek-token () |
|
"Return token starting at point." |
|
(cond ((looking-at "\\(if\\|then\\|else\\|let\\|in\\|mdo\\|rec\\|do\\|proc\\|case\\|of\\|where\\|module\\|deriving\\|data\\|type\\|newtype\\|class\\|instance\\)\\([^[:alnum:]'_]\\|$\\)") |
|
(match-string-no-properties 1)) |
|
((looking-at "[][(){}[,;]") |
|
(match-string-no-properties 0)) |
|
((looking-at "\\(\\\\\\|->\\|<-\\|::\\|=\\||\\)\\([^-:!#$%&*+./<=>?@\\\\^|~]\\|$\\)") |
|
(match-string-no-properties 1)) |
|
((looking-at "\\(→\\|←\\|∷\\)\\([^-:!#$%&*+./<=>?@\\\\^|~]\\|$\\)") |
|
(let ((tok (match-string-no-properties 1))) |
|
(or (cdr (assoc tok haskell-indentation-unicode-tokens)) tok))) |
|
((looking-at"[-:!#$%&*+./<=>?@\\\\^|~`]" ) |
|
'operator) |
|
(t 'value))) |
|
|
|
(defun haskell-indentation-skip-token () |
|
"Skip to the next token." |
|
(let ((case-fold-search nil)) |
|
(if (or (looking-at "'\\([^\\']\\|\\\\.\\)'") |
|
(looking-at "'\\\\\\([^\\']\\|\\\\.\\)*'") |
|
(looking-at "\"\\([^\\\"]\\|\\\\.\\)*\"") |
|
;; QuasiQuotes, with help of propertize buffer and string delimeters |
|
(looking-at "\\s\"\\S\"*\\s\"") |
|
;; Hierarchical names always start with uppercase |
|
(looking-at |
|
"[[:upper:]]\\(\\s_\\|\\sw\\|'\\)*\\(\\.\\(\\s_\\|\\sw\\|'\\)+\\)*") |
|
;; Only unqualified vars can start with lowercase. |
|
(looking-at "\\(\\s_\\|\\sw\\)\\(\\s_\\|\\sw\\|'\\)*") |
|
(looking-at "[0-9][0-9oOxXeE+-]*") |
|
(looking-at "[-:!#$%&*+./<=>?@\\\\^|~]+") |
|
(looking-at "[](){}[,;]") |
|
(looking-at "`[[:alnum:]']*`")) |
|
(goto-char (match-end 0)) |
|
;; otherwise skip until space found |
|
(skip-syntax-forward "^-")) |
|
(forward-comment (buffer-size)) |
|
(while (and (haskell-indentation-bird-p) |
|
(bolp) |
|
(eq (char-after) ?>)) |
|
(forward-char) |
|
(forward-comment (buffer-size))))) |
|
|
|
(provide 'haskell-indentation) |
|
|
|
;; Local Variables: |
|
;; tab-width: 8 |
|
;; End: |
|
|
|
;;; haskell-indentation.el ends here
|
|
|