[project @ 1997-03-14 05:31:07 by sof]
[ghc-hetmet.git] / ghc / CONTRIB / haskell-modes / glasgow / original / haskell-mode.el
1 ;; Haskell major mode
2 ;; (c) Copyright, Richard McPhee et al. 
3 ;; University of Glasgow, February 1993
4
5
6
7 ;; if .hs is not recognised then put the extension in auto-mode-list
8
9 (if (assoc "\\.hs" auto-mode-alist)
10     nil
11   (nconc auto-mode-alist '(("\\.hs". haskell-mode))))
12
13 (if (assoc "\\.hi" auto-mode-alist)
14     nil
15   (nconc auto-mode-alist '(("\\.hi". haskell-mode))))
16
17 (if (assoc "\\.gs" auto-mode-alist)
18     nil
19   (nconc auto-mode-alist '(("\\.gs". haskell-mode))))
20
21 (defvar haskell-mode-syntax-table nil
22   "Syntax table for haskell-mode buffers.")
23
24 (defvar haskell-mode-abbrev-table nil
25   "Abbrev table for haskell-mode buffers.")
26
27 (defvar haskell-mode-map (make-sparse-keymap)
28   "Keymap for haskell-mode-buffers.")
29
30
31
32 ;;; Here are the keymaps used in haskell-mode
33
34 (define-key haskell-mode-map "\M-;"  'haskell-insert-comment)
35 (define-key haskell-mode-map "\C-c=" 'haskell-insert-concat)
36 (define-key haskell-mode-map "\C-c;" 'set-haskell-comment-column)
37 (define-key haskell-mode-map "\C-c+" 'set-haskell-concat-column)
38 (define-key haskell-mode-map "\C-cn" 'set-haskell-indent-offset)
39 (define-key haskell-mode-map "\C-cl" 'set-haskell-list-offset)
40 (define-key haskell-mode-map "\C-ci" 'set-haskell-if-offset)
41 (define-key haskell-mode-map "\C-ce" 'set-haskell-let-offset)
42 (define-key haskell-mode-map "\C-cc" 'set-haskell-case-offset)
43 (define-key haskell-mode-map "\C-ct" 'set-haskell-then-offset)
44 (define-key haskell-mode-map "\C-co" 'set-haskell-comp-offset)
45 (define-key haskell-mode-map "\C-cw" 'set-haskell-where-offset)
46 (define-key haskell-mode-map "\C-cg" 'goto-line)
47 (define-key haskell-mode-map "\C-j"  'haskell-reindent-then-newline-and-indent)
48 (define-key haskell-mode-map "\t"    'haskell-indent-line)
49 (define-key haskell-mode-map "}"     'electric-haskell-brace)
50 (define-key haskell-mode-map "]"     'electric-haskell-brace)
51 (define-key haskell-mode-map ")"     'haskell-insert-round-paren)
52 (define-key haskell-mode-map "\C-cr" 'haskell-indent-region)
53 (define-key haskell-mode-map "\C-cf" 'haskell-further-indent)
54 (define-key haskell-mode-map "\C-cb" 'haskell-lesser-indent)
55 (define-key haskell-mode-map "\177"  'backward-delete-char-untabify)
56 (define-key haskell-mode-map "\M-\C-\177" 'delete-horizontal-space)
57                                         
58 (defun haskell-set-local-vars ()
59   "Set the local variables for haskell-mode."
60   (kill-all-local-variables)
61
62   (setq indent-line-function 'haskell-indent-line)
63
64   (make-local-variable 'haskell-std-list-indent)
65   ;;Non-nil means indent to the offset, 'haskell-list-offset' in a bracket rather than
66   ;; moving to the next word afer a function name
67   (setq haskell-std-list-indent t)
68
69   (make-local-variable 'haskell-nest-ifs)
70   ;;Non-nil means that 'if' statements are nested ie. lined up with `if' not `else'.
71   (setq haskell-nest-ifs nil)
72
73   (make-local-variable 'haskell-align-else-with-then)
74   ;;Non-nil means align an `else' under it's corresponding `then'
75   (setq haskell-align-else-with-then nil)
76
77
78   ;;The local vars for 'where' indentation
79
80   (make-local-variable 'haskell-align-where-with-eq)
81   ;;Non-nil means align a 'where' under it's corresponding equals sign
82   (setq haskell-align-where-with-eq t)
83
84   (make-local-variable 'haskell-align-where-after-eq)
85   ;;Non-nil means align a 'where' after it's corresponding equals sign
86   (setq haskell-align-where-after-eq nil)
87
88   (make-local-variable 'haskell-std-indent-where)
89   ;;put the 'where' the standard offset ie. 'haskell-indent-offset'
90   (setq haskell-std-indent-where nil)   
91
92
93   (make-local-variable 'haskell-always-fixup-comment-space)
94   ;;Non-nil means always insert a (single) space after a comment, even
95   ;; if there is more or less than one.
96   (setq haskell-always-fixup-comment-space t)
97
98   
99   (make-local-variable 'haskell-indent-offset)
100   ;;Extra indentation for a line continued after a keyword.
101   (setq haskell-indent-offset 4)
102
103   (make-local-variable 'haskell-list-offset)
104   ;;Extra indentation for continuing a list.
105   (setq haskell-list-offset 4)
106   
107   (make-local-variable 'haskell-comp-offset)
108   ;;Extra indentation for a list comprehension.
109   (setq haskell-comp-offset 4)
110   
111   (make-local-variable 'haskell-case-offset)
112   (setq haskell-case-offset 4)
113
114   (make-local-variable 'haskell-where-offset)
115   (setq haskell-where-offset 4)
116
117   (make-local-variable 'haskell-let-offset)
118   (setq haskell-let-offset 4)
119
120   (make-local-variable 'haskell-then-offset)
121   (setq haskell-then-offset 0)
122
123   (make-local-variable 'haskell-if-offset)
124   (setq haskell-if-offset 4)
125
126   (make-local-variable 'haskell-comment-column)
127   (setq haskell-comment-column 35)
128   
129   (make-local-variable 'haskell-concat-column)
130   (setq haskell-concat-column 69)
131   
132   (make-local-variable 'haskell-where-threshold)
133   (setq haskell-where-threshold 35)
134   
135   (make-local-variable 'line-comment)
136   (setq line-comment "-- ")
137
138   (make-local-variable 'haskell-indent-style)
139   (setq haskell-indent-style "none"))
140
141
142 (defun haskell-set-syntax-table ()
143   "Set the syntax table for Haskell-mode."
144   (setq haskell-mode-syntax-table (make-syntax-table))
145   (set-syntax-table haskell-mode-syntax-table)
146   (modify-syntax-entry ?\" "\"")
147   (modify-syntax-entry ?\\ "\\")
148   (modify-syntax-entry ?\' "w")
149   (modify-syntax-entry ?_  "w")
150   (modify-syntax-entry ?#  "_")
151   (modify-syntax-entry ?$  "_")
152   (modify-syntax-entry ?%  "_")
153   (modify-syntax-entry ?:  "_")
154   (modify-syntax-entry ??  "_")
155   (modify-syntax-entry ?@  "_")
156   (modify-syntax-entry ?!  "_")
157   (modify-syntax-entry ?^  "_")
158   (modify-syntax-entry ?~  "_")
159   (modify-syntax-entry ?-  "_ 12")
160   (modify-syntax-entry ?\n ">")
161   (modify-syntax-entry ?{  "(}")
162   (modify-syntax-entry ?}  "){")
163   (set-syntax-table haskell-mode-syntax-table))
164
165
166
167 (defun haskell-mode ()
168   "Major mode for editing Haskell code.
169 Linefeed reindents current line, takes newline and indents.
170 Tab indents current line for Haskell code.
171 Functions are seperated by blank lines.
172 Delete converts tabs to spaces as it moves back.
173 \\{haskell-mode-map}
174 Variables controlling indentation style:
175  haskell-indent-offset
176     Standard extra indentation for continuing Haskell
177     code under the scope of an expression.  The default is 4.
178
179  haskell-list-offset
180     Extra indentation for indenting in a list.  Used if variable
181     haskell-std-list-indent is non-nil.  The default is 4.
182
183  haskell-comp-offset
184     Extra indentation for continuing a list comprehension.  
185     The default is 4.
186
187  haskell-case-offset
188     Standard extra indentation for continuing Haskell
189     code under the scope of an expression.  The default is 4.
190
191  haskell-where-offset
192     Standard extra indentation for continuing Haskell
193     code under the scope of a `where'.  The default is 4.
194
195  haskell-let-offset
196     Standard extra indentation for continuing Haskell
197     code under the scope of a `let'.  The default is 4.
198
199  haskell-then-offset
200     Standard extra indentation for a `then' beyond
201     its corresponding `if'.  The default is 0.
202
203  haskell-if-offset
204     Standard extra indentation for continuing Haskell
205     code under the scope of an `if'.  The default is 4.
206
207  haskell-comment-column
208     Column to which line comments `--' will be inserted.
209     The default is 35.
210
211  haskell-concat-column
212     Column to which concatenation operator `++' will be inserted.
213     The default is 69.
214
215  haskell-where-threshold
216     Column beyond which a `where' will be indented to the
217     start of a line (to avoid spilling over lines).
218     The default is 35.
219
220  set-haskell-indent-offset (C-c i)
221     Changes the default value of the local variable,
222     haskell-indent-offset.  May be a number from 0-10.
223
224  set-haskell-list-indent (C-c l)
225     Change the value of the local variable, 
226     haskell-list-offset.  May be a number from 0-100.
227
228  set-haskell-comment-column (C-x ;)
229     Changes the value of the local variable,
230     haskell-comment-column.  May be any number from 0-100."
231
232   (interactive)
233   (haskell-set-local-vars)
234   (haskell-set-syntax-table)
235   (use-local-map haskell-mode-map)
236   (setq major-mode 'haskell-mode)
237   (setq mode-name "Haskell") 
238   (define-abbrev-table 'haskell-mode-abbrev-table ()))
239
240
241
242
243 ;;; Returns the indentation column for a comment on this line.
244 ;;; The point is positioned at the last char of any code on the line.
245
246 (defun haskell-comment-indent ()
247   "Returns the indentation for a comment on the given line.
248 If the line has code on it or the point is not at the beginning of the line,
249 then indent to indent-column.
250 Otherwise, don't indent."
251   (cond ((or (haskell-code-on-linep)    
252              (not (bolp)))               
253          ;;There is code before the haskell-comment-column
254          ;; or not at the beginning of the line
255          ;;Return the largest of
256          ;; the current column +1 and the haskell-comment-column
257          (max (1+ (current-column))     
258               haskell-comment-column))          
259         (t
260          ;;Otherwise, return 0
261          0)))
262
263
264
265 ;;; Returns whether a comment is on the current line
266 ;;; Search from bol, and beware of "--", {-- etc!
267 ;;; DOES NOT RECOGNISE {- COMMENTS YET or -- within a string
268
269 (defun haskell-comment-on-linep ()
270   "Returns the truth value of whether there is a '--' comment on the current line."
271   (save-excursion
272     (beginning-of-line)         
273     (looking-at ".*--")))
274
275
276 ;;; This doesn't account for comments '{-'.  Test explicitly if you use this function!
277
278 (defun haskell-code-on-linep ()
279   "Returns a truth value as to whether there is code on the current line."
280   (save-excursion
281     (beginning-of-line)
282     (not
283      ;; Code on line if not looking at a comment directly
284      ;; and the line is not blank
285      (or
286           (looking-at "^[ \t]*--")      
287           (looking-at "^[ \t]*$")))))   
288
289
290 ;;; Insert a Haskell "--" comment on the current line.
291 ;;; Move to the comment position if there's already a comment here.
292 ;;; Otherwise, the comment is inserted either at the comment column
293 ;;; or one column after the last non-space character, whichever is further
294 ;;; to the right.
295 ;;; This function is executed by M-;
296
297 (defun haskell-insert-comment ()
298   "Inserts a '--' comment on the given line."
299   (interactive)
300   (cond ((haskell-comment-on-linep)
301          ;;There is a comment on the line
302          ;;Just reindent existing comment
303          (haskell-reindent-comment))    
304         (t
305          (if (haskell-code-on-linep)
306              ;;There is code on the line
307              ;; and guarenteed that a comment
308              ;; does not already exist.
309              ;;Move to the last nonspace char
310              ;; (there may be spaces after the last char)
311              (progn
312                (end-of-line)                    
313                (skip-chars-backward " \t")))
314          ;;Indent to required level
315          ;; and insert the line comment '--'
316          (indent-to (haskell-comment-indent)) 
317          (insert line-comment))))               
318
319
320 ;;; Reindents a comment.
321 ;;; The comment is indented according to the normal rules.
322 ;;; Skips over ---- and following spaces or tabs
323
324 (defun haskell-reindent-comment ()
325   "Indents a comment on a line to keep it at haskell-comment-column,
326 if possible.
327 It is guaranteed that a comment exists on the current line."
328     (beginning-of-line)
329     ;;Go back to beginning of comment 
330     (re-search-forward "--")            
331     (forward-char -2)
332     ;;Delete all spaces and reindent to
333     ;; the correct location.
334     (delete-horizontal-space)           
335     (indent-to (haskell-comment-indent)) 
336     ;;Move past the comment and insert
337     ;; only one space between it and the text.
338     ;;Leave point just after comment.
339     (skip-chars-forward "- \t")
340     (if haskell-always-fixup-comment-space
341         (progn
342         (fixup-whitespace)                      
343         (forward-char 1))))
344
345
346
347 ;;; Inserts a haskell concatenation operator, `++', at the
348 ;;; column dictated by haskell-concat-column
349
350 (defun haskell-insert-concat()
351   "Inserts a `++' operator on the given line."
352   (interactive)
353   (end-of-line)                 
354   (skip-chars-backward " \t")
355   ;;Indent to required level
356   ;; and insert the concat operator `++'
357   (indent-to (haskell-concat-indent)) 
358   (insert "++"))
359
360
361
362 ;;; Returns the indentation column for a concatenation operator on this line.
363 ;;; The point is positioned at the last char of any code on the line.
364
365 (defun haskell-concat-indent ()
366   "Returns the indentation for a concat operator on the given line."
367   (max (1+ (current-column))    
368        haskell-concat-column))  
369
370
371
372 ;;; Returns the indentation of the current line of haskell code.
373 ;;; A blank line has ZERO indentation
374
375 (defun haskell-current-indentation ()
376   "Returns the indentation for the current haskell line. A blank line has 
377 indentation zero."
378   (save-excursion
379     (beginning-of-line)
380     (if (looking-at "^[ \t]*$")
381         ;;The line is empty
382         ;; so the indentation is zero
383         0
384       ;;Otherwise find the normal value of indentation
385       (current-indentation))))          
386
387
388
389 ;;; Returns the indentation of the previous line of haskell code.
390 ;;; A blank line has ZERO indentation
391
392 (defun haskell-previous-indentation ()
393   "Returns the previous line's indentation as Haskell indentation."
394   (save-excursion
395     (if (not (bobp))
396         ;;Not at the start of the buffer
397         ;; so get the previous lines indentation
398         (progn
399           (forward-line -1)
400           (haskell-current-indentation))
401       ;;We are at the start of buffer
402       ;;There is no previous line; Indent is zero
403       0)))                              
404
405
406
407 ;;; Move back to the last line which is aligned in the left column.
408 ;;; Ignores comments and blank lines.
409 ;;; The point is left at the beginning of the line.
410
411 (defun haskell-back-to-zero-indent ()
412   "Moves point to last line which has zero as indentation."
413   ;;Not at the beginning of buffer.
414   ;;Continue to go to the previous line until
415   ;; we find a line whose indentation is non-zero.
416   ;;Blank lines and lines containing only comments
417   ;; are ignored.
418   (beginning-of-line)
419   (while (and
420           (or (not (zerop (haskell-current-indentation)))       
421               (looking-at "^[ \t]*\\($\\|--\\)"))
422             (not (bobp)))
423     (haskell-backward-to-noncomment)
424     (beginning-of-line)))
425
426
427
428 ;;; Find the last symbol, usually an equality.
429
430 ;;; Note: we check for "=" as a complete WORD (and ignore
431 ;;; comments) when searching for this.  Ie. an `=' may be
432 ;;; surrounded only by a letter, digit, or whitespace .
433 ;;; Strings are not considered.
434 ;;; Don't go beyond the first character in the (possibly narrowed) buffer.
435 ;;;   From the beginning of the line,
436 ;;;     find the comment position (or end-of-line)
437 ;;;     search forward to this position, looking for a "where"
438 ;;;     If one's found, then search forward for "\b=\b"
439 ;;;        If there's no equality sign then
440 ;;;             search forward from the start of the line for an equals
441 ;;;        Otherwise we found it.
442 ;;;    If there's no where then search forward for an equals, as above.
443                                  
444 (defun haskell-back-to-symbol (exp)
445   "Goes backward from point until a symbol, EXP, is found.
446 The point is left at the first symbol matching the context 
447 of the haskell code."
448   (let* ((found nil)
449          (symbol (concat "[ \ta-z0-9A-Z]" exp "[ \t\na-z0-9A-Z]"))
450          eol-limit
451          bol-limit
452          (zero-indent (save-excursion
453                         (haskell-back-to-zero-indent)
454                         (point)))
455          (initial-depth (car (parse-partial-sexp
456                               (point)
457                               zero-indent))))
458         
459     (while (and (not found)
460                 (> (point) zero-indent))
461       ;;Not found and point > point min
462       ;;Record the limit of search for the beginning and
463       ;; end of the line.
464       (setq eol-limit (point))  
465       (beginning-of-line)
466       (setq bol-limit (point))  
467       (goto-char eol-limit)             
468       (re-search-backward "\\bwhere\\b" bol-limit 't)
469       ;;Search back from the end of the line
470       ;; to find the most recent 'where'.
471
472       (cond ((and (re-search-backward symbol bol-limit 't)
473                   (= initial-depth
474                      (car (parse-partial-sexp
475                            (point)
476                            zero-indent))))
477              ;;Found a symbol sign surrounded by
478              ;; a letter, digit or space only, or at the
479              ;; beginning of the buffer and they are at
480              ;; the same depth level
481              (setq found 't))
482             ((and (re-search-backward symbol bol-limit 't)
483                   (zerop
484                    (car (parse-partial-sexp
485                          (point)
486                          zero-indent))))
487              ;; Found a symbol and it is not in any parens
488              (setq found 't))
489             ;;Otherwise, go back a line.
490             (t (haskell-backward-to-noncomment))))
491     (if found
492         (forward-char 1))))
493
494
495 ;;; Goes back to the last keyword.  The point is left at the
496 ;;; beginning of the keyword.
497 ;;; The words recognised are:
498 ;;;   `case',`of',`where',`let',`in',`if',`then',`else'
499
500 (defun haskell-back-to-keyword ()
501   "Goes backward from point until a keyword is found.
502 The point is left after the first keyword."
503   (let* ((found nil)
504          eol-limit
505          bol-limit
506          (zero-indent (save-excursion
507                         (haskell-back-to-zero-indent)
508                         (point)))
509          (initial-depth (car (parse-partial-sexp
510                               (point)
511                               zero-indent))))
512
513     (while (and (not found)
514                 (>= (point) zero-indent))
515       ;;Not found and point > point min
516       ;;Go back past any comment.
517       ;;Record the limit of search for the beginning and
518       ;; end of the line.
519       (setq eol-limit (point))  
520       (beginning-of-line)
521       (setq bol-limit (point))  
522       (goto-char eol-limit)             
523       (if (and (re-search-backward
524                 "\\b\\(case\\|of\\|where\\|let\\|in\\|if\\|then\\|else\\)\\b"
525                 bol-limit 't)
526                (= initial-depth
527                   (car (parse-partial-sexp
528                         (point)
529                         zero-indent))))
530           ;;Found a keyword and it is at the same level as the initial position
531           (progn
532             (setq found 't)
533             (forward-word 1))
534         ;;Otherwise, go back a line.
535         (haskell-backward-to-noncomment)))))
536
537
538
539 ;;; Returns the end of line (point) of the current line, excluding any
540 ;;; line comments on it.
541
542 (defun haskell-eol ()
543   "Returns the end (point) of the current line, excluding any line comments."
544   (save-excursion
545     (end-of-line)
546     (let ((eol-limit (point)))  
547       (beginning-of-line)
548       (if (search-forward "--" eol-limit 'move-to-eol)
549           ;;Found a '--' 
550           ;;So move to the beginning of the comment
551           ;;If fail then move to end of line
552           (forward-char -2)))
553     (point)))
554
555
556
557 ;;; Returns whether or not the current line contains an equality outwith a
558 ;;; comment.  The equality may only be surrounded by a letter, digit or
559 ;;; whitespace. 
560
561 (defun haskell-looking-at-eqp ()
562   "Returns whether or not the current line contains an equality outwith a
563 comment."
564   (save-excursion
565     (beginning-of-line)
566     (re-search-forward "[ \ta-z0-9A-Z]=[ \t\na-z0-9A-Z]" (1+ (haskell-eol)) 't)))
567           
568           
569 ;;; This function does not require all keywords, just those which
570 ;;; may have a bracket before them.
571 (defun haskell-looking-at-keywordp ()
572   "Returns whether or not there is a keyword after the point outwith a
573 comment."
574   (save-excursion
575     (re-search-forward 
576      "\\(\\(=>\\|=\\|++\\|->\\|<-\\|::\\)\\|\\b\\(case\\|of\\|if\\|then\\|else\\|let\\|in\\)\\b\\)"
577      (haskell-eol) 't)))
578           
579           
580 ;;; This function returns whether or not there is a keyword contained in
581 ;;; the region START END.  START < END.
582
583 (defun haskell-keyword-in-regionp (start end)
584   "Returns whether or not there is a keyword between START and END."
585   (save-excursion
586     (goto-char start)
587     (let ((found nil)
588           (eol-limit (haskell-eol)))
589       (while (and (not found) (< (point) end))
590         (if (> eol-limit end)
591             (setq eol-limit end))
592         (if (re-search-forward
593              "\\b\\(case\\|of\\|if\\|then\\|else\\|let\\|in\\)\\b"
594              eol-limit 'move)
595             (setq found t)
596           ;;Otherwise, have not found a keyword.  Now at haskell-eol.
597           (if (< (point) end)
598               ;;We still have an area to search
599               ;; so go forward one line
600               (progn
601                 (beginning-of-line)
602                 (forward-line 1)
603                 (setq eol-limit (haskell-eol))))))
604       ;;found is `t' or point >= end
605       found)))
606          
607
608 ;;;  Goes back to the last line which is not entirely commented out.
609 ;;;  The point is left just before the comment.  
610           
611 (defun haskell-backward-to-noncomment ()
612   "Sets the point to the last char on the line of Haskell code before a comment."
613   (let ((comment 't)
614         (limit (point-min)))
615       (while (and comment (> (point) limit))
616         ;; comment is true and point > limit
617         (beginning-of-line)
618         (if (< (forward-line -1) 0)
619             ;;This was the first line in the buffer
620             (setq comment nil)
621           ;;Otherwise, this was not the first line
622           (if (not (looking-at "^[ \t]*\\($\\|--\\)"))
623               ;;There is not a comment at the beginning of the line
624               ;; and the line is not blank
625               (progn
626                 ;;The line is either blank or has code on it.
627                 (setq comment nil)
628                 (goto-char (haskell-eol))))))
629
630       ;;return point
631       (point)))
632
633
634
635 ;;; Indents a region (by applying "tab" to each line).
636 ;;; The marker upper-marker is set to the end of the region.
637 ;;; We indent from the beginning of the region to this marker.
638 ;;; Implements C-c r.
639                       
640 (defun haskell-indent-region ()
641   "Indents the region between the point and mark."
642   (interactive)
643   (let ((lower-limit (min (point) (mark)))
644         (upper-limit (max (point) (mark))))
645     (indent-region lower-limit upper-limit 'nil)))
646       
647
648
649 ;;; Implements TAB.
650 ;;; This actually indents a line.
651 ;;; Eventually it will handle a line split at any point,
652
653 (defun haskell-indent-line ()
654   "Indent current line as Haskell code.
655 Keeps the point at the same position on the line unless the 
656 point is less then the current indentation, in which case the 
657 point is moved to the first char."
658   (interactive)
659   (save-excursion
660     (let ((indent (haskell-calculate-indentation)))
661       (beginning-of-line)
662       (delete-horizontal-space)
663       ;;Kill any spaces that may preceed the code
664       ;; and reindent to the correct level.
665       (indent-to indent)))
666   (if (< (current-column) (current-indentation))
667       ;;The point is in the indentation
668       ;; so move to the first char on the line
669       (move-to-column (current-indentation))))
670
671
672
673 ;;; This is the haskell version of the Emacs function
674 ;;; reindent-then-newline-and-indent.  It was necessary
675 ;;; to write this because the Emacs version has the 
676 ;;; terrible property of deleting whitespace BEFORE 
677 ;;; reindenting the original line.
678
679 (defun haskell-reindent-then-newline-and-indent ()
680   "Reidents the current line of Haskell code then takes a
681 newline and indents this new line."
682   (interactive)
683   (skip-chars-backward " \t")
684   (haskell-indent-line)
685   (newline)
686   (delete-horizontal-space)
687   (haskell-indent-line))
688
689
690
691 ;;; Returns whether the first word of the last line with zero indentation 
692 ;;; is the same as the first word of the current line.
693 ;;; This function is based on the (reasonable?) assumption that 
694 ;;; a function definition occurs on the left hand margin.
695 ;;; This is not quit reasonable since recusive functions are not
696 ;;; recognised.
697
698 (defun haskell-continued-fn-defp ()
699   "Returns whether the first word on the last line with zero indentation
700 matches the first word on the current line."
701   (save-excursion
702     (beginning-of-line)
703     (skip-chars-forward " \t")
704     ;;Goto the first non space char 
705     (haskell-word-eq (point)
706                      (save-excursion
707                        (forward-line -1)
708                        (haskell-back-to-zero-indent)
709                        (point)))))
710
711
712 ;;; Returns whether two words are the same.
713 ;;; The beginning of both words are given as their
714 ;;; respective points in the buffer.  
715
716 (defun haskell-word-eq (current-pos previous-pos)
717   (let ((OK 't))
718     (goto-char previous-pos)
719     ;;We shall compare the two words starting
720     ;; at previous-pos and current-pos.
721       (while (and OK (looking-at "\\S-"))
722         ;;OK and looking at a word constituent
723         (if (eq (char-after current-pos) 
724                 (char-after previous-pos))
725             ;;The two chars are the same
726             (progn
727               ;;Increment the two postions
728               ;; and update location of point
729               (setq current-pos (1+ current-pos))
730               (setq previous-pos (1+ previous-pos))
731               (goto-char previous-pos))
732           ;;The two chars are different
733           ;; so set OK to be false
734           (setq OK 'nil)))
735
736       ;;Return the value of OK
737       OK))                              
738       
739
740
741
742 ;;; This function returns the column of the last unbalanced
743 ;;; expression.
744 ;;; It is called when an keyword is found.  The point is 
745 ;;; initially placed before the corresponding keyword.
746 ;;; The function looks at every word to see if it is a
747 ;;; `let' or `in'.  Each word must be outwith a comment.
748
749 (defun haskell-last-unbalanced-key-column (open close)
750   "Returns the column of the last unbalanced keyword, open."
751   (save-excursion
752     (let ((original-pos (point))
753           (bol-limit (save-excursion
754                        (beginning-of-line)
755                        (setq bol-limit (point))))
756           (depth  1))
757       (setq open (concat "\\b" open "\\b"))
758       (setq close (concat "\\b" close "\\b"))
759       (while (and
760               (> depth 0)
761               (> (point) (point-min)))
762         (forward-word -1)
763         (if (< (point) bol-limit)
764             ;;Moved past the beginning of line limit
765             ;; so go back to the previous line past
766             ;; any comments.
767             (progn
768               (goto-char original-pos)
769               (haskell-backward-to-noncomment)
770               (setq original-pos (point))
771               (setq bol-limit (save-excursion
772                                 (beginning-of-line)
773                                 (point))))
774           ;;Otherwise, still on the same line
775           (if (looking-at open)
776               ;;This word is an open keyword
777               (setq depth (1- depth))
778             ;;Otherwise,
779             (if (looking-at close)
780                 ;;This word is a close keyword
781                 (setq depth (1+ depth))))))
782
783       (if (string= open "\\bif\\b")
784           ;;The argument is `if'
785           (if (not (save-excursion (skip-chars-backward " \t") (bolp)))
786               ;;There is something before the `if'
787               (if (and (save-excursion
788                          (forward-word -1)
789                          (looking-at "\\belse\\b"))
790                        (not haskell-nest-ifs))
791                   ;;There is an `else' before the 'if'
792                   (forward-word -1))))
793       
794       
795       (current-column))))
796         
797           
798
799 ;;; Return the indentation for a line given that we expect a `where'.
800 ;;; The point lies on the corresponding symbol 
801 ;;; that the `where' scopes over.
802
803 (defun haskell-indent-where ()
804   "Return the indentation for a line, given that we expect a `where'
805 clause."
806   (let ((symbol (if (looking-at "=")
807                     "="
808                   "->")))
809     
810     (cond ((or haskell-std-indent-where
811                (> (current-column) haskell-where-threshold))
812            ;;Set indentation as the sum of the previous
813            ;; line's layout column and the standard offset
814            ;; (ie. 'haskell-where-offset)
815            (save-excursion
816              (beginning-of-line)
817              (cond ((looking-at (concat "^[ \t]*" symbol))
818                     ;;The line starts with the symbol
819                     (setq indent (current-indentation)))
820                    ((looking-at "^[ \t]*where\\b")
821                     ;;The line starts with a 'where'
822                     (forward-word 1)
823                     (skip-chars-forward " \t")
824                     (setq indent (+ (current-column) haskell-where-offset)))
825                    (t
826                     ;;The line begins on the layout column
827                     (setq indent (+ (current-indentation) 
828                                     haskell-indent-offset))))))
829           ((or haskell-align-where-with-eq
830                haskell-align-where-after-eq)
831            (if (looking-at (concat symbol "[ \t]*$"))
832                ;;The symbol is at the end of the line
833                (setq indent (+ (current-indentation)
834                                haskell-where-offset))
835              (save-excursion
836                ;;Set the indentation as required
837                (if haskell-align-where-after-eq
838                    (skip-chars-forward (concat symbol " \t")))
839                (setq indent (current-column))))))))
840
841
842
843 ;;; Calculates the indentation for the current line.
844 ;;; When we come here, we are in a line which we want to indent.
845 ;;; We should leave the point at the same relative position it
846 ;;; was in before we called the function, that is, if a line
847 ;;; is already correctly indented, nothing happens!
848
849 ;;; The main problems are handling "where" definitions
850 ;;; and the syntax of expressions when these are continued
851 ;;; over multiple lines (e.g. tuples, lists, or just plain
852 ;;; bracketed expressions).  Watch out for let ... in, too!
853
854 ;;; For example, think about the following tricky cases:
855
856 ;;;    f x = x + <NL>
857       
858 ;;;    f x = [  x + y, <NL>
859
860 ;;;    f x = [  <NL>
861
862 ;;;    f x = [  -- start of a large list
863 ;;;             -- which I'm commenting in as I go
864 ;;;  <TAB>
865
866 (defun haskell-calculate-indentation ()
867   "Returns the indentation level for the current line of haskell code."
868   (save-excursion
869     (let ((indent 0)
870           (eol-position (point)))
871       (beginning-of-line)
872       (cond ((bobp)
873              ;;We are at the beginning of the buffer so do nothing at all
874              (setq indent 0))   
875
876             ((looking-at "^[ \t]*--")
877              ;;There is a comment on the line by itself
878              ;;Leave it the way it is
879              (setq indent (current-indentation)))
880
881             ((looking-at "^[ \t]*\\(data\\|type\\|module\\|import\\|instance\\)\\b")
882              ;;There is a 'data', 'type', 'module' or 'import' at start of line
883              (setq indent 0))
884
885             ((haskell-continued-fn-defp)
886              ;;This is clearly same function
887              ;; so set indent to be 0
888              (setq indent 0))
889                   
890             ((looking-at "^[ \t]*[]}]")
891              ;;There is a "]" or "}" at the start of the line
892              (let ((state (parse-partial-sexp (match-end 0)
893                                               (save-excursion
894                                                 (haskell-back-to-zero-indent)
895                                                 (point)))))
896                (if (>= (car state) 0)
897                    ;;Since the point is just after a parenthesis
898                    ;; it has a match if the depth is >= 0
899                    (save-excursion
900                      (goto-char (nth 2 state))
901                      ;;Move to the match.
902                      (if (not
903                           (save-excursion
904                             (skip-chars-backward " \t")
905                             (bolp)))
906                          ;;There is something before the brace.
907                          (progn
908                            (let ((initial-pos (point)))
909                              (forward-word -1)
910                              (if (not (looking-at
911                                        "\\(let\\|where\\)"))
912                                  ;;The word is not `where' or `let'
913                                  ;; so go back.
914                                  (progn
915                                    (goto-char initial-pos)
916                                    (skip-chars-forward " \t"))))))
917                      (setq indent (current-column)))
918                  (setq indent 0))))
919
920             ((looking-at "^[ \t]*\\(->\\|=>\\)")
921              ;; '->' or '=>' at start of line
922              (save-excursion
923                (haskell-backward-to-noncomment)
924                ;;Go back to previous line
925                (let ((eol-limit (point)))
926                  (beginning-of-line)
927                  (if (re-search-forward "::" eol-limit 't)
928                      ;;There is a '::' on this (previous) line
929                      ;; set indent to be at the start of it
930                      (setq indent (- (current-column) 2))
931                    ;;Otherwise copy this (previous) line's indentation
932                    (setq indent (current-indentation)))))) 
933                              
934             ((looking-at "^[ \t]*where\\b")
935              ;;There is a 'where' at the start of the line
936              ;;Look for the equality (which will not
937              ;; be on this line).
938              (haskell-backward-to-noncomment)
939              (goto-char (max (save-excursion
940                                (haskell-back-to-symbol "=")
941                                (point))
942                               (save-excursion
943                                (haskell-back-to-symbol "->")
944                                (point))))
945              (setq indent (haskell-indent-where)))
946
947             ((looking-at "^[ \t]*then\\b")
948              ;;The first thing on the line is a `then'
949              (setq indent (+ (haskell-last-unbalanced-key-column "if" "then")
950                              haskell-then-offset)))
951
952             ((looking-at "^[ \t]*else\\b")
953              ;;The first thing on the line is a `else'
954              (if haskell-align-else-with-then
955                  (setq indent (haskell-last-unbalanced-key-column "then" "else"))
956                (setq indent (haskell-last-unbalanced-key-column "if" "else"))))
957                              
958             ((looking-at "^[ \t]*|")
959              ;;There is a `|' at beginning of line
960              (save-excursion
961                (let ((state
962                      (parse-partial-sexp (save-excursion
963                                            (haskell-back-to-zero-indent)
964                                            (point))
965                                          (point))))
966                  (if (not (or (nth 3 state) (nth 4 state)))
967                      ;;Not in a comment or string
968                      (if (> (car state) 0)
969                          ;;In an unbalanced parenthesis.
970                          (progn
971                            (goto-char (nth 1 state))
972                            ;;Move to the beginning of the unbalanced parentheses
973                            (if (and (looking-at "\\[")
974                                     (search-forward "|" (haskell-eol) 't))
975                                ;;It is a list comprehension
976                                (setq indent (1- (current-column)))
977                              (setq indent (+ (current-column)
978                                              haskell-comp-offset))))
979                        ;;Otherwise, not in an unbalanced parenthesis
980                        (setq indent (save-excursion
981                                       (haskell-back-to-symbol "=")
982                                       (cond ((not (looking-at "="))
983                                              ;;Did not find an equals
984                                              (+ (haskell-previous-indentation)
985                                                 haskell-indent-offset))
986                                             ((save-excursion
987                                                (beginning-of-line)
988                                                (looking-at "^[ \t]*data\\b"))
989                                              ;;There is a `data' at beginning
990                                              (setq indent (current-column)))
991                                             ((save-excursion
992                                                (beginning-of-line)
993                                                (search-forward
994                                                 "|" (haskell-eol) 't))
995                                              ;;There is a `|' on this line
996                                              ;; so set this to be the indent
997                                              (save-excursion
998                                                (goto-char (match-beginning 0))
999                                                (current-column)))
1000                                             (t
1001                                              ;;Otherwise, set `=' as indent
1002                                              (current-column))))))))))
1003                    
1004             ((looking-at "^[ \t]*=")
1005              ;;There is an equals at the start of the line
1006              ;;Set the indentation to be the previous line's 
1007              ;; indentation plus the standard offset
1008              (setq indent (+ haskell-indent-offset
1009                              (haskell-previous-indentation))))
1010
1011             ((looking-at "^[ \t]*in\\b")
1012              ;;The line starts with 'in'
1013              (beginning-of-line)
1014              (setq indent (haskell-last-unbalanced-key-column "let" "in")))
1015
1016             ((looking-at "^[ \t]*of\\b")
1017              ;;The line starts with `of'
1018              (beginning-of-line)
1019              (setq indent (haskell-last-unbalanced-key-column "case" "of")))
1020
1021             ((looking-at "^.*::")
1022              ;;There is a '::' in the line
1023              ;;There are several possibilities for indentation
1024              (if (looking-at "[ \t]*::")
1025                  ;;The '::' is the first thing on the line
1026                  ;; so set indent to be the previous line's
1027                  ;; indentation plus the standard offset
1028                  (setq indent (+ (haskell-previous-indentation)
1029                                  haskell-indent-offset))
1030                (save-excursion
1031                  ;;Otherwise, the '::' is contained in the line somewhere
1032                  ;; so use contextual indentation
1033                  (setq indent (haskell-context-indent)))))
1034            
1035             (t
1036              ;;Do not recognise the first word on the line.
1037              (setq indent (haskell-context-indent))))
1038       
1039       indent)))                         ;return indent as indentation value
1040
1041
1042
1043 ;;; Returns the indentation for the current line by looking at the 
1044 ;;; previous line to give clues to the indentation.
1045
1046 (defun haskell-context-indent ()
1047   "Returns the indentation for the current line by looking at 
1048 the previous line to dictate the indentation."
1049   (save-excursion
1050     (let ((original-position (point))
1051           indent)
1052       (beginning-of-line)
1053       (if (bobp)
1054           ;;At the beginning of the buffer
1055           (setq indent 0)
1056         ;;Otherwise, we are not at the beginning of the buffer
1057         (haskell-backward-to-noncomment)
1058         (let ((eol-limit (point))
1059               ;;Record the (upper) limit for any search on this line
1060               bol-limit
1061               (paren-indent 'nil))
1062           ;;`paren-indent' flags whether we are indenting a list or not
1063           (beginning-of-line)
1064           (setq bol-limit (point))
1065           ;;Record the (lower) limit for any search on this line
1066           (goto-char eol-limit) ;goto the end of the line
1067           (flag)
1068           (if (save-excursion
1069                 (goto-char eol-limit)
1070                 (and (re-search-backward
1071                       "[])][^][()]*" bol-limit 't)
1072                      (save-excursion
1073                        (goto-char (match-beginning 0))
1074                        (not (haskell-looking-at-keywordp)))))
1075                         
1076               ;;There is a close parenthesis at the end of the line
1077               ;; followed by anything except "(", ")", "[", "]"
1078               ;; or a keyword
1079               (progn
1080                 ;;Search back for the close parenthesis
1081                 ;; and move to just after it.
1082                 (re-search-backward "[])]" bol-limit 't)
1083                 (forward-char 1) 
1084                 (let ((state
1085                        (parse-partial-sexp (save-excursion
1086                                              (haskell-back-to-zero-indent)
1087                                              (point))
1088                                            (point))))
1089                   (if (not (or (nth 3 state) (nth 4 state)))
1090                       ;;Not in a comment or string
1091                       (if (>= (car state) 0)
1092                           ;;The parenthesis has a match
1093                           (progn
1094                             (goto-char (nth 2 state))
1095                             ;;Move to the beginning of the parentheses
1096                             ;; as this new line will determine
1097                             ;; further indentation
1098                             (if (zerop (car state))
1099                                 ;;This paren closes all unbalanced parens
1100                                 ;; so move to
1101                                 ;; the eol of last line with an equality.
1102                                 (progn
1103                                   (setq eol-limit (point))
1104                                   (goto-char
1105                                    (max (save-excursion
1106                                           (haskell-back-to-symbol "=")
1107                                           (point))
1108                                         (save-excursion
1109                                           (haskell-back-to-keyword)
1110                                           (point))))
1111                                   (goto-char eol-limit))
1112                               ;;esle just go to the end of the line
1113                               (goto-char (haskell-eol)))
1114                             (setq paren-indent 't)
1115                             ;;Set 'paren-indent' to true to indicate we
1116                             ;; are indenting a list.
1117                             (setq eol-limit (point))
1118                             (beginning-of-line) 
1119                             (setq bol-limit (point))
1120                             ;;Reduce the scope of any later
1121                             ;; indentation to
1122                             ;; exclude the balanced parentheses
1123                             ;; by making this point
1124                             ;; be the eol-limit.  
1125                             (goto-char eol-limit)))))))
1126           (flag)
1127           ;;This cond expression is structured, to an 
1128           ;; extent, such that the keywords with highest
1129           ;; indentation precedence come first.  Order is important.
1130           ;;In each condition, the point of match is noted so
1131           ;; that we can see if this point is in a string.
1132           (let ((indent-point (point)))
1133             (cond ((re-search-backward "\\bof\\b" bol-limit 't)
1134                    ;; `of' is contained in previous line
1135                    (setq indent-point (point))
1136                    (if (looking-at "of[ \t]*$")
1137                      ;;`of' at end of line
1138                        (setq indent (+ (haskell-last-unbalanced-key-column
1139                                         "case" "of")
1140                                        haskell-case-offset))
1141                      ;;Otherwise, `of' is in line
1142                      (forward-word 1)
1143                      (skip-chars-forward " \t")
1144                      (setq indent (current-column))
1145                      (setq indent (list indent))))
1146                   
1147                   ((re-search-backward
1148                     "\\bthen[ \t]*$" bol-limit 't)
1149                    ;;There is a `then' at the end of the line.
1150                    (setq indent-point (point))
1151                    (if haskell-align-else-with-then
1152                        ;;We want to align the `else' (to follow) with the `then'
1153                        (setq indent (+ (current-column)
1154                                        haskell-if-offset))
1155                      (setq indent (+ (haskell-last-unbalanced-key-column 
1156                                       "if" "then")      
1157                                      haskell-if-offset))))
1158                   ;; This was here but don't know why (setq indent (list indent))))
1159                   
1160                   ((save-excursion
1161                      (and (re-search-backward "\\bif\\b" bol-limit 't)
1162                           (setq indent-point (point))
1163                           (not (re-search-forward "\\bthen\\b" eol-limit 't))))
1164                    ;;There is an `if' on the (previous) line and the line does
1165                    ;; not have a `then' on it.
1166                    (setq indent (+ (haskell-last-unbalanced-key-column 
1167                                     "if" "then")
1168                                    haskell-then-offset)))
1169                   
1170                   ((save-excursion
1171                      (and (re-search-backward "\\bif\\b" bol-limit 't)
1172                           (setq indent-point (point))
1173                           (not (re-search-forward "\\belse\\b" eol-limit 't))))
1174                    ;;There is an `if' on the (previous) line (the line may
1175                    ;; have a `then' on it) and does not have an else on it.
1176                    (if (re-search-backward "\\bthen\\b" bol-limit 't)
1177                        ;;There is a then on the line and it is followed by
1178                        ;; some code.
1179                        (progn
1180                          (forward-word 1)
1181                          (skip-chars-forward " \t")
1182                          (setq indent (current-column)))
1183                      (if haskell-align-else-with-then
1184                          ;;We want to align the `else' with the `then'
1185                          (setq indent (haskell-last-unbalanced-key-column 
1186                                        "then" "else"))  
1187                        (setq indent (haskell-last-unbalanced-key-column 
1188                                      "if" "else")))))
1189                   
1190                   ((re-search-backward "\\b\\(let\\|in\\)\\b" bol-limit 't)
1191                    ;; 'let' or 'in' is contained in the (previous) line
1192                    (setq indent-point (point))
1193                    (forward-word 1) ;skip past the word
1194                    (skip-chars-forward " \t{")
1195                    (if (looking-at "\\($\\|--\\)")
1196                        ;;looking-at eol or comment
1197                        (progn
1198                          (forward-word -1)
1199                          (setq indent (+ (current-column)
1200                                          haskell-let-offset)))
1201                      (setq indent (current-column))))
1202                   
1203                   ((re-search-backward
1204                     "\\belse[ \t]*$" bol-limit 't)
1205                    ;;There is a `else' at end of line
1206                    (setq indent-point (point))
1207                    (save-excursion
1208                      (goto-char eol-limit)
1209                      (forward-word -1)
1210                      (setq indent (+ (current-column)
1211                                      haskell-if-offset))))
1212                   
1213                   ((re-search-backward
1214                     "\\belse\\b" bol-limit 't)
1215                    ;;There is a `else' on the line with no if or then
1216                    (setq indent-point (point))
1217                    (save-excursion
1218                      (forward-word 1)
1219                      (skip-chars-forward " \t")
1220                      (setq indent (current-column))))
1221                   
1222                   ((save-excursion
1223                      (beginning-of-line)
1224                      (looking-at 
1225                       "^[ \t]*then\\b"))
1226                    ;;There is a 'then' at beginning of line
1227                    (setq indent-point (point))
1228                    (setq indent (current-indentation)))
1229                   
1230                   ((save-excursion
1231                      (beginning-of-line)
1232                      (looking-at "^[ \t]*else[ \t]*if\\b"))
1233                    (setq indent-point (point))
1234                    ;;There is an 'else if' at start of (previous) line
1235                    (save-excursion
1236                      (beginning-of-line)
1237                      (if haskell-nest-ifs
1238                          (save-excursion
1239                            (forward-word 1)
1240                            (skip-chars-forward " \t")
1241                            (setq indent (current-column)))
1242                        (skip-chars-forward " \t")
1243                        (setq indent (current-column)))))
1244                   
1245                   ((re-search-backward "\\bcase\\b" bol-limit 't)
1246                    ;;There is a 'case' on the previous line
1247                    ;; so copy this line's indentation and add on
1248                    ;; the offset unless there is not an of.
1249                    (setq indent-point (point))
1250                    (setq indent (+ (current-column) 
1251                                    haskell-case-offset)))
1252                   
1253                   ((save-excursion
1254                      (beginning-of-line)
1255                      (looking-at "^\\(instance\\|class\\)\\b"))
1256                    ;;This (previous) line has an 'instance' or 'class' at start
1257                    ;; so just set indentation to be this line indentation
1258                    ;; plus the standard offset
1259                    (setq indent-point (point))
1260                    (setq indent (+ (current-indentation)
1261                                    haskell-indent-offset)))
1262                   
1263                   ((re-search-backward "where\\b" bol-limit 't)
1264                    ;;There is a 'where' on the (previous) line
1265                    (setq indent-point (point))
1266                    (if (looking-at "where[ \t]*$")
1267                        ;;There is nothing after the 'where'
1268                        ;; so set indent to be this column
1269                        ;; (ie. the column of the 'w')
1270                        ;; plus the standard offset
1271                        (if (save-excursion
1272                              (skip-chars-backward " \t")
1273                              (bolp))
1274                            ;;The 'where' is the only thing on the line.
1275                            (setq indent (+ (current-column) 
1276                                            haskell-where-offset))
1277                          ;;Otherwise, the 'where' is at the end
1278                          ;; of the line and there is code before it.
1279                          ;;Look before the 'where' for the symbol
1280                          ;; it scopes over.
1281                          (forward-word -1)
1282                          (goto-char (max (save-excursion
1283                                            (haskell-back-to-symbol "=")
1284                                            (point))
1285                                          (save-excursion
1286                                            (haskell-back-to-symbol "->")
1287                                            (point))))
1288                          (setq indent (haskell-indent-where)))
1289                      
1290                      ;;Otherwise, go past the 'where'
1291                      ;; and goto the last non space character.
1292                      ;;Set this column to be the indentation.
1293                      (forward-word 1) 
1294                      (skip-chars-forward " \t") 
1295                      (setq indent (current-column))))   
1296                   
1297                   ((re-search-backward
1298                     "[ \ta-z0-9A-Z]=[ \t]*$" bol-limit 't)
1299                    ;;There is an equals is at the end of line
1300                    ;; so make the indentation be this line's indentation
1301                    ;; plus the standard offset
1302                    (setq indent-point (point))
1303                    (setq indent (+ (current-indentation)
1304                                    haskell-indent-offset))) 
1305                   
1306                   ((re-search-backward
1307                     "[ \ta-z0-9A-Z]\\+\\+[ \t]*$" bol-limit 't)
1308                    ;;There is a concat operator at the end of line
1309                    ;; so make the indentation be this line's indentation
1310                    (setq indent-point (point))
1311                    (setq indent (current-indentation)))
1312                   
1313                   ((save-excursion
1314                      (beginning-of-line)
1315                      (looking-at
1316                       "^[ \t]*=[ \ta-z0-9A-Z]"))
1317                    ;;There is an equals is at the beginning of line
1318                    ;; so make the indentation be the previous line's
1319                    ;; indentation unless the previous line's
1320                    ;; indentation is zero.
1321                    (setq indent-point (point))
1322                    (save-excursion
1323                      (haskell-backward-to-noncomment)
1324                      (if (zerop (current-indentation))
1325                          (setq indent (+ (current-indentation)
1326                                          haskell-indent-offset))
1327                        (setq indent (haskell-current-indentation)))))
1328                   
1329                   ((re-search-backward "|" bol-limit 't)
1330                    ;;There is  an `|' on this line.
1331                    (setq indent-point (point))
1332                    (if (save-excursion
1333                          (goto-char original-position)
1334                          (looking-at "^[ \t]*\\($\\|--\\||\\)"))
1335                        ;;The original line is empty or has a `|' at the 
1336                        ;; start.  So set indent to be first `|' on this line
1337                        (save-excursion
1338                          (goto-char bol-limit)
1339                          (re-search-forward "|" eol-limit 't)
1340                          (setq indent (1- (current-column))))
1341                      ;;Otherwise set indent to be this (previous) line's
1342                      (setq indent 0)))
1343                   
1344                   ((re-search-backward "->" bol-limit 't)
1345                    ;;There is a `->' in the line.
1346                    ;;This may be from a `case' or a
1347                    ;; type declaration.
1348                    (setq indent-point (point))
1349                    (save-excursion
1350                      (if (re-search-backward "::" bol-limit 't)
1351                          ;;There is a '::' on this line
1352                          (if (looking-at ".*->[ \t]*$")
1353                              ;;The '->' is at the end of line.
1354                              ;;Move past the '::' and any spaces
1355                              ;; and set indent to be this column.
1356                              (progn
1357                                (skip-chars-forward ": \t") 
1358                                (setq indent (current-column)))
1359                            ;;Otherwise, the '->' is not at end of line
1360                            ;; so copy the indentation
1361                            (setq indent (haskell-context-indent)))
1362                        
1363                        ;;Otherwise, there is not a
1364                        ;; `::' on this line so copy this
1365                        ;; (previous) indentation.
1366                        (setq indent (haskell-context-indent)))))
1367                   
1368                   ((re-search-backward "::" bol-limit 't)
1369                    ;;There is  an '::' on this line.
1370                    ;;We know that the line does not end with '->'.
1371                    (setq indent-point (point))
1372                    (if (looking-at "::[ \t]*$")
1373                        ;;The '::' is at the end of the line
1374                        ;; so set indent to be this line's
1375                        ;; indentation plus the offset.
1376                        (setq indent (+ (current-indentation) 
1377                                        haskell-indent-offset))
1378                      ;;Otherwise the `::' is in the line
1379                      (setq indent (current-indentation))))
1380                   
1381                   ((re-search-backward
1382                     "\\b\\(import\\|class\\)\\b"
1383                     bol-limit 't)
1384                    ;;There is an `import' or `class' on the line.
1385                    ;;Copy this indentation.
1386                    (setq indent-point (point))
1387                    (setq indent (current-indentation)))
1388                   
1389                   ((or
1390                     (haskell-looking-at-eqp)
1391                     (save-excursion
1392                       (beginning-of-line)
1393                       (looking-at "^[ \t]*$")))
1394                    ;;There is an '=' on the line
1395                    ;; or it is blank
1396                    (setq indent-point (point))
1397                    (cond ((save-excursion
1398                             (beginning-of-line)
1399                             (looking-at "^[ \t]*data\\b"))
1400                           ;;`data' at start of line
1401                           ;; so expect a `|'
1402                           (haskell-back-to-symbol "=")
1403                           (setq indent (current-column)))
1404                          ((zerop (current-indentation))
1405                           ;;If the indentation is zero, we expect a `where'
1406                           (goto-char eol-limit)
1407                           (haskell-back-to-symbol "=")
1408                           (setq indent (haskell-indent-where)))
1409                          ((looking-at "^[ \t]*=[ \t\na-z0-9A-Z]")
1410                           ;;The equality is the first thing on the line
1411                           ;; so copy the last lines indentation
1412                           (save-excursion
1413                             (haskell-backward-to-noncomment)
1414                             (setq indent (current-indentation))))
1415                          (t
1416                           ;;Otherwise, copy the indentation
1417                           (setq indent (current-indentation)))))
1418                   
1419                   ((save-excursion
1420                      (beginning-of-line)
1421                      (and (zerop (current-indentation))
1422                           (not (looking-at "^[ \t]*$"))))
1423                    ;;The line is not blank and its indentation is zero
1424                    ;;It is a function definition.  We know that 
1425                    ;; there is not an equals on the line
1426                    (goto-char eol-limit)
1427                    ;;We expect a keyword
1428                    ;; so set indent to be this line's indentation
1429                    ;; plus the offset
1430                    (setq indent-point (point))
1431                    (setq indent (+ (current-indentation)
1432                                    haskell-indent-offset)))
1433                   
1434                   ((bobp)
1435                    ;;At the beginning of buffer
1436                    (setq indent 0))
1437                   
1438                   (paren-indent
1439                    ;;We are indenting a list and none
1440                    ;; of the above indentations are applicable
1441                    ;; so copy the indentation of this line
1442                    (setq indent (current-indentation)))
1443                   
1444                   (t
1445                    (save-excursion
1446                      (setq indent (haskell-context-indent)))))
1447
1448             (if (nth 3 (parse-partial-sexp
1449                         (save-excursion
1450                           (goto-char indent-point)
1451                           (haskell-back-to-zero-indent)
1452                           (point))
1453                         (save-excursion
1454                           (goto-char indent-point))))
1455                 ;;The point we determined indentation at is in a
1456                 ;; string so go to this point and go back one line to
1457                 ;; find indentation.
1458                 (setq indent (haskell-context-indent))))
1459           
1460           
1461           ;;HOWEVER, we may have to override any indentation if we are in
1462           ;; an unbalanced parenthesis (on the original line).
1463           (flag)
1464           (save-excursion
1465             (goto-char original-position)
1466             (let* ((eq-point (save-excursion
1467                                (haskell-back-to-symbol "=")
1468                                (point)))
1469                    (state (parse-partial-sexp
1470                            eq-point
1471                            (point))))
1472               (if (> (car state) 0)
1473                   ;;There is an unbalanced parenthesis between
1474                   ;; the function and here.
1475                   (if (not (or (nth 3 state) (nth 4 state)))
1476                       ;;We are not in a string or comment
1477                       ;; so goto the parenthesis
1478                       (progn
1479                         (goto-char (nth 1 state))
1480                         (if (not (haskell-keyword-in-regionp
1481                                   (point)
1482                                   original-position))
1483                             ;;There is not a keyword after the open
1484                             ;; bracket so we override the indentation
1485                             (progn
1486                               (if (not (looking-at "{"))
1487                                   ;;The parenthesis is not a `{'
1488                                   (if (or (looking-at "\\[")
1489                                           (save-excursion
1490                                             (goto-char (haskell-eol))
1491                                             (skip-chars-backward " \t")
1492                                             (and
1493                                              (char-equal (preceding-char) ?,)
1494                                              (= (car state)
1495                                                 (car (parse-partial-sexp
1496                                                       eq-point
1497                                                       (point)))))))
1498                                       ;;The paren is a square one
1499                                       ;; or it is a tuple.
1500                                       ;;Don't ignore what is after it.
1501                                       (setq indent (haskell-list-align (haskell-eol)))
1502                                     ;;Otherwise, ignore what comes after it.
1503                                     (setq indent (haskell-list-align (point))))))))))))
1504           ))
1505       
1506       indent)))
1507   
1508
1509 ;;; Inserts the close parenthesis and reindents the line.
1510 ;;; We want to reindent the line if the parenthesis is 
1511 ;;; the first character on the line.  The parenthesis
1512 ;;; recognised by this function are `]', `}'.
1513
1514 (defun electric-haskell-brace ()
1515   "Inserts the character `]' or `}' and reindents the current line."
1516   "Insert character and correct line's indentation."
1517   (interactive)
1518   (if (save-excursion
1519         (skip-chars-backward " \t")
1520         (bolp))
1521       ;;The parenthesis is at the beginning of the line.
1522       (progn
1523         (insert last-command-char)
1524         (haskell-indent-line))
1525     ;;Otherwise it is not at the beginning of line.
1526     (insert last-command-char))
1527   ;; Match its beginning.
1528   (haskell-blink-open))
1529
1530
1531
1532
1533 ;;; This function returns the indentation for the next line given
1534 ;;; that it is contained in a bracket or we are extending a functions
1535 ;;; parameters over a line.  For the case of being in an unbalanced
1536 ;;; parenthesis list, the point lies on the unbalanced parenthesis.
1537 ;;; The parameter eol-limit is used to delimit the end of the line.
1538
1539 (defun haskell-list-align (eol-limit)
1540   "Returns the indentation for the next line given that
1541 the point lies on an unbalanced open parenthesis."
1542   (save-excursion
1543     (let ((indent (1+ (current-column))))
1544       ;;Set indent to be the next char (at least).
1545
1546       (cond ((not 
1547               (looking-at ".[ \t]*\\($\\|--\\)"))
1548              ;;There is something after the parenthesis
1549              ;;ie. the line is not empty and ignore comments
1550              (cond ((save-excursion
1551                       (goto-char eol-limit)
1552                       (skip-chars-backward " \t")
1553                       (and (char-equal (preceding-char) ?,)
1554                            (save-excursion
1555                              (beginning-of-line)
1556                              (not (search-forward "|" eol-limit 't)))))
1557                     ;;This is a normal list since a `,' at end
1558                     ;; and there is no a `|' on the line.
1559                     (forward-char 1)
1560                     (skip-chars-forward " \t")
1561                     (setq indent (current-column)))
1562
1563                    ((looking-at "\\[")
1564                     ;;It is a list comp we are looking at
1565                     ;;Goto the bar.
1566                     (forward-char 1)    
1567                     (search-forward "|" eol-limit 't)
1568                     (skip-chars-forward " \t")
1569                     (setq indent (current-column)))
1570           
1571                    ((looking-at ".[ \t]*(")
1572                     ;;We are looking at an open parenthesis
1573                     ;; after this character.
1574                     ;;It must be balanced so 
1575                     ;; move to the start of this paren
1576                     ;; and set indent to be here
1577                     (forward-char 1) 
1578                     (skip-chars-forward " \t")
1579                     (setq indent (current-column)))
1580                    
1581                    (t
1582                     (forward-word 1)
1583                     ;;We are not looking at another open
1584                     ;; parenthesis, so move forward past the
1585                     ;; (assumed) function name.
1586                     (if (or
1587                          haskell-std-list-indent
1588                          (looking-at"[ \t]*\\($\\|--\\)"))
1589                         ;;There is nothing after the name
1590                         ;; or haskell-std-list-offset is set
1591                         ;; so set indent to be its original
1592                         ;; value plus the offset minus 1
1593                         ;; since we added one on earlier.
1594                         (setq indent
1595                               (+ indent
1596                                  (1- haskell-list-offset)))
1597                       
1598                       ;;Otherwise there is something after the
1599                       ;; name, so skip to the first non space
1600                       ;; character.
1601                       (skip-chars-forward " \t")
1602                       (setq indent (current-column)))))))
1603
1604
1605       indent)))
1606
1607
1608
1609 (defun haskell-insert-round-paren ()
1610   "Inserts a `(' and blinks to its matching parenthesis."
1611   (interactive)
1612   (insert last-command-char)
1613   (haskell-blink-open))
1614
1615
1616
1617 ;;; This function is called when a close parenthesis 
1618 ;;; `)', `]', or `}' is typed.
1619 ;;; Blinks the cursor on the corresponding open parnethesis.
1620 ;;; The point lies just after the close parenthesis.
1621
1622 (defun haskell-blink-open ()
1623   "Blinks the cursor to the matching open parenthesis.
1624 The point lies just after a parenthesis."
1625   (let ((state (parse-partial-sexp (point)
1626                                    (save-excursion
1627                                      (haskell-back-to-zero-indent)
1628                                      (point)))))
1629     (if (and
1630          (>= (car state) 0)
1631          (not (or (nth 3 state) (nth 4 state))))
1632         ;;The parenthesis just inserted has a match
1633         ;; and is not in a string or a comment
1634         ;; so blink on its match
1635         (save-excursion
1636           (goto-char (nth 2 state))
1637           (sit-for 1)))))
1638
1639
1640
1641 ;;; This function indents the line expecting the line to be a 
1642 ;;; continued function application.
1643
1644 ;;;   foo a = bar a
1645 ;;;               b     {haskell-further-indent applied to this line
1646 ;;;                      indents the line as shown}
1647
1648 ;;; The line would look like this if only tab had been applied:
1649 ;;;   foo a = bar a
1650 ;;;         b
1651
1652 (defun haskell-further-indent ()
1653   "Indents the line more than the ordinary indentation in order to 
1654 extend function arguments over multiple lines."
1655   (interactive)
1656   (let (indent
1657         (new-point (max (save-excursion
1658                           (haskell-back-to-symbol "=")
1659                           (point))
1660                         (save-excursion
1661                           (haskell-back-to-keyword)
1662                           (point)))))
1663     (save-excursion
1664       ;;This may be a continuation of a function
1665       ;; application so go back to the last '='
1666       ;; and set indent as designated by the style chosen
1667       (goto-char new-point)
1668       (skip-chars-forward "= \t")
1669       (setq indent (haskell-list-align (haskell-eol))))
1670     ;;The argument to haskell-list-align is not important here.
1671     (save-excursion
1672       (beginning-of-line)
1673       (delete-horizontal-space)
1674       (indent-to indent))
1675     (if (< (current-column) indent)
1676         (move-to-column indent))))
1677
1678
1679 ;;; This function indents the current line to the first previous
1680 ;;; indentation value which is less than the current indentation.
1681
1682 (defun haskell-lesser-indent ()
1683   "Indents the current line to the first previous indentation
1684 value which is less than the current indentation."
1685   (interactive)
1686   (let ((original-indent
1687          (current-indentation))
1688         (indent (haskell-context-indent))
1689         (done nil))
1690     (save-excursion
1691       (while (not done)
1692         (while (and (not (bobp))
1693                     (not (zerop (current-indentation)))
1694                     (>= indent original-indent))
1695           (haskell-backward-to-noncomment)
1696           (setq indent (current-indentation)))
1697         ;;bobp or indent < original-indent
1698         (if (>=  indent original-indent)
1699             ;;indent is still greater than or equal to original indent
1700             (progn 
1701               (setq indent 0)
1702               (setq done t))
1703           ;;Otherwise, indent is less than orignal indent.
1704           (forward-line 1)
1705           (setq indent (haskell-context-indent))
1706           (if (< indent original-indent)
1707               ;;The new indent is an improvement
1708               (setq done t)
1709             ;;Otherwise, indent is still >= original
1710             ;; so go back to the line and keep typing.
1711             (forward-line -1)))))
1712     (save-excursion
1713       (beginning-of-line)
1714       (delete-horizontal-space)
1715       (indent-to indent))
1716     (if (< (current-column) indent)
1717         (move-to-column indent))))
1718
1719       
1720
1721 ;;; Here are the functions which change the local variables
1722 ;;; to facilitate tailorability.
1723
1724 (defun default-mode ()
1725   "Calls the function haskell-mode."
1726   (interactive)
1727   (haskell-mode)
1728   (message haskell-indent-style))
1729
1730 (defun wadler-mode ()
1731   "Sets defaults according to Dr. Philip L. Wadler's preferences.
1732    - Aligns `where' clauses with the corresponding equality.
1733    - Aligns `else' keyword with the corresponding `then'
1734    - haskell-list-offset 2
1735    - haskell-indent-offset 8
1736    - haskell-if-indent   2
1737    - haskell-comment-column 0
1738    - haskell-case-offset 2
1739    - haskell-let-offset  5."
1740   ;;Preferences:
1741   ;;'haskell-align-where-with-eq  non-nil
1742   ;;'haskell-list-offset 2
1743   (interactive)
1744   (haskell-mode)
1745   (or haskell-align-where-with-eq
1746       (progn
1747         (setq haskell-align-where-with-eq t)
1748         (setq haskell-std-indent-where nil)))
1749   (setq haskell-align-else-with-then t)
1750   (setq haskell-list-offset 2)
1751   (setq haskell-indent-offset 8)
1752   (setq haskell-if-offset 2)
1753   (setq haskell-case-offset 2)
1754   (setq haskell-let-offset 5)
1755   (setq haskell-comment-column 0)
1756   (setq haskell-indent-style "Wadler")
1757   (message haskell-indent-style))
1758               
1759
1760 (defun report-mode ()
1761   "Sets defaults according to the style of the Haskell Report.
1762    - Aligns `where' clauses after the corresponding equality.
1763    - Aligns `else' with `then'.
1764    - haskell-then-offset   = 3
1765    - haskell-where-offset  = 0.
1766    - haskell-case-offset   = 5."
1767   ;;Preferences:
1768   ;; haskell-align-where-after-eq  non-nil
1769   ;; haskell-then-offset  3
1770   ;; haskell-where-offset 0
1771   ;; haskell-case-offset  5
1772   (interactive)
1773   (haskell-mode)
1774   (haskell-align-where-after-eq)
1775   (or haskell-align-else-with-then
1776       (haskell-align-else-with-then))
1777   (setq haskell-then-offset 3)
1778   (setq haskell-where-offset 0)
1779   (setq haskell-case-offset 5)
1780   (setq haskell-indent-style "Report")
1781   (message haskell-indent-style))
1782               
1783
1784 (defun haskell-align-where-with-eq ()
1785   "Sets indentation so that a 'where' clause lines up underneath
1786 its corresponding equals sign."
1787   (interactive)
1788   (or haskell-align-where-with-eq
1789       (progn
1790         (setq haskell-align-where-after-eq nil)
1791         (setq haskell-std-indent-where nil)
1792         (setq haskell-align-where-with-eq t)
1793         haskell-align-where-with-eq)))
1794
1795
1796
1797 (defun haskell-align-where-after-eq ()
1798   "Sets indentation so that a 'where' clause lines up underneath
1799 the first nonspace character after its corresponding equals sign."
1800   (interactive)
1801   (or haskell-align-where-after-eq
1802       (progn
1803         (setq haskell-align-where-with-eq nil)
1804         (setq haskell-std-indent-where nil)
1805         (setq haskell-align-where-after-eq t)
1806         haskell-align-where-after-eq)))
1807
1808
1809 (defun haskell-std-indent-where ()
1810   "Sets indentation so that a `where' clause lines up underneath
1811 its corresponding equals sign."
1812   (interactive)
1813   (or haskell-std-indent-where
1814       (progn
1815         (setq haskell-align-where-after-eq nil)
1816         (setq haskell-align-where-with-eq nil)
1817         (setq haskell-std-indent-where t)
1818         haskell-std-indent-where)))
1819
1820
1821 (defun haskell-align-else-with-then ()
1822   "Sets indentation so that an `else' lines up underneath
1823 it's corresponding `then'."
1824   (interactive)
1825   (setq haskell-align-else-with-then
1826         (not haskell-align-else-with-then))
1827   (setq haskell-nest-ifs nil))
1828
1829 (defun haskell-nest-ifs ()
1830   "Sets indentation so that an `if' is lined up
1831 under an `if' in an `else ."
1832   (interactive)
1833   (setq haskell-nest-ifs
1834         (not haskell-nest-ifs))
1835   (setq haskell-align-else-with-then nil))
1836
1837
1838 (defun haskell-always-fixup-comment-space ()
1839   "Non-nil means always position one space after a line comment `--',
1840 when reindenting or inserting a comment,
1841 whether or not one space exists."
1842   (setq haskell-always-fixup-comment-space
1843         (not haskell-always-fixup-comment-space))
1844   haskell-always-fixup-comment-space)
1845
1846 (defun haskell-indent-style ()
1847   "Echos the chosen indentation style in the mini-buffer."
1848   (interactive)
1849   (message haskell-indent-style))
1850
1851 (defun set-haskell-let-offset (offset)
1852   "Changes the value of haskell-let-offset, the variable which
1853 determines extra indentation after a `let' and  `in'."
1854   (interactive "nSet haskell-let-offset to: ")
1855   (if (and (>= offset 0) (<= offset 10))
1856       (setq haskell-let-offset offset)))
1857
1858 (defun set-haskell-if-offset (offset)
1859   "Changes the value of haskell-let-offset, the variable which
1860 determines extra indentation after an `if', `then' and `else'."
1861   (interactive "nSet haskell-if-offset to: ")
1862   (if (and (>= offset 0) (<= offset 10))
1863       (setq haskell-if-offset offset)))
1864
1865 (defun set-haskell-case-offset (offset)
1866   "Changes the value of haskell-case-offset, the variable which
1867 determines extra indentation after a `case' and `of'."
1868   (interactive "nSet haskell-case-offset to: ")
1869   (if (and (>= offset 0) (<= offset 10))
1870       (setq haskell-case-offset offset)))
1871
1872
1873 (defun set-haskell-where-offset (offset)
1874   "Changes the value of haskell-where-offset, the variable which
1875 determines extra indentation after a line of haskell code."
1876   (interactive "nSet haskell-where-offset to: ")
1877   (if (and (>= offset 0) (<= offset 10))
1878       (setq haskell-where-offset offset)))
1879
1880
1881 (defun set-haskell-indent-offset (offset)
1882   "Changes the value of haskell-indent-offset, the variable which
1883 determines extra indentation after a line of haskell code."
1884   (interactive "nSet haskell-indent-offset to: ")
1885   (if (and (>= offset 1) (<= offset 10))
1886       (setq haskell-indent-offset offset)))
1887
1888
1889 (defun set-haskell-list-offset (offset)
1890   "Changes the value of haskell-list-offset, the variable which
1891 determines extra indentation after a line of haskell code for a list."
1892   (interactive "nSet haskell-list-offset to: ")
1893   (if (and (>= offset 0) (<= offset 10))
1894       (setq haskell-list-offset offset)))
1895
1896
1897 (defun set-haskell-comp-offset (offset)
1898   "Changes the value of haskell-comp-offset, the variable which
1899 determines extra indentation after a list comprehension."
1900   (interactive "nSet haskell-comp-offset to: ")
1901   (if (and (>= offset 0) (<= offset 10))
1902       (setq haskell-comp-offset offset)))
1903
1904
1905 (defun set-haskell-then-offset (offset)
1906   "Changes the value of haskell-then-offset, the variable which
1907 determines extra indentation for a `then' keyword after an `if'."
1908   (interactive "nSet haskell-then-offset to: ")
1909   (if (and (>= offset 0) (<= offset 10))
1910       (setq haskell-then-offset offset)))
1911
1912
1913 (defun set-haskell-comment-column (column)
1914   "Changes the value of haskell-comment-column, the variable which
1915 determines where to postition a line comment `--'."
1916   (interactive "nSet haskell-comment-column to: ")
1917   (if (and (>= column 0) (<= column 100))
1918       (setq haskell-comment-column column)))
1919          
1920 (defun set-haskell-concat-column (column)
1921   "Changes the value of haskell-concat-column, the variable which
1922 determines where to postition a concatenation operator `++'."
1923   (interactive "nSet haskell-concat-column to: ")
1924   (if (and (>= column 0) (<= column 100))
1925       (setq haskell-concat-column column)))
1926          
1927 (defun set-haskell-where-threshold (column)
1928   "Changes the value of haskell-where-threshold, the variable which
1929 determines when to override positioning a `where' under or after
1930 its corresponding equality."
1931   (interactive "nSet haskell-where-threshold to: ")
1932   (if (and (>= column 0) (<= column 100))
1933       (setq haskell-where-threshold column)))
1934          
1935 (defun flag ())