[project @ 1997-03-14 05:31:07 by sof]
[ghc-hetmet.git] / ghc / CONTRIB / haskell-modes / chalmers / sof / haskell-mode.el
1 ;; haskell-mode.el. Major mode for editing Haskell.
2 ;; Copyright (C) 1989, Free Software Foundation, Inc., Lars Bo Nielsen
3 ;; and Lennart Augustsson
4 ;; modified by Peter Thiemann, March 1994
5
6 ;; This file is not officially part of GNU Emacs.
7
8 ;; GNU Emacs is distributed in the hope that it will be useful,
9 ;; but WITHOUT ANY WARRANTY.  No author or distributor
10 ;; accepts responsibility to anyone for the consequences of using it
11 ;; or for whether it serves any particular purpose or works at all,
12 ;; unless he says so in writing.  Refer to the GNU Emacs General Public
13 ;; License for full details.
14
15 ;; Everyone is granted permission to copy, modify and redistribute
16 ;; GNU Emacs, but only under the conditions described in the
17 ;; GNU Emacs General Public License.   A copy of this license is
18 ;; supposed to have been given to you along with GNU Emacs so you
19 ;; can know your rights and responsibilities.  It should be in a
20 ;; file named COPYING.  Among other things, the copyright notice
21 ;; and this notice must be preserved on all copies.
22
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;;
25 ;; Haskell Mode. A major mode for editing and running Haskell. (Version 0.0)
26 ;; =================================================================
27 ;;
28 ;; This is a mode for editing and running Haskell.
29 ;; It is very much based on the sml mode for GNU Emacs. It
30 ;; features:
31 ;;
32 ;;      - Inferior shell running Haskell. No need to leave emacs, just
33 ;;        keep right on editing while Haskell runs in another window.
34 ;;
35 ;;      - Automatic "load file" in inferior shell. Send regions of code
36 ;;        to the Haskell program.
37 ;;
38 ;;
39 ;; 1. HOW TO USE THE Haskell-MODE
40 ;; ==========================
41 ;;
42 ;; Here is a short introduction to the mode.
43 ;;
44 ;; 1.1 GETTING STARTED
45 ;; -------------------
46 ;;
47 ;; If you are an experienced user of Emacs, just skip this section.
48 ;;
49 ;; To use the haskell-mode, insert this in your "~/.emacs" file (Or ask your
50 ;; emacs-administrator to help you.):
51 ;;
52 ;;    (setq auto-mode-alist (cons '("\\.hs$" . haskell-mode) (cons '("\\.lhs$" . haskell-mode)
53 ;;                           auto-mode-alist)))
54 ;;    (autoload 'haskell-mode "haskell-mode" "Major mode for editing Haskell." t)
55 ;;
56 ;; Now every time a file with the extension `.hs' or `.lhs' is found, it is
57 ;; automatically started up in haskell-mode.
58 ;;
59 ;; You will also have to specify the path to this file, so you will have
60 ;; to add this as well:
61 ;;
62 ;;    (setq load-path (cons "/usr/me/emacs" load-path))
63 ;;
64 ;; where "/usr/me/emacs" is the directory where this file is.
65 ;;
66 ;; You may also want to compile the this file (M-x byte-compile-file)
67 ;; for speed.
68 ;;
69 ;; You are now ready to start using haskell-mode. If you have tried other
70 ;; language modes (like lisp-mode or C-mode), you should have no
71 ;; problems. There are only a few extra functions in this mode.
72 ;;
73 ;; 1.2. EDITING COMMANDS.
74 ;; ----------------------
75 ;;
76 ;; The following editing and inferior-shell commands can ONLY be issued
77 ;; from within a buffer in haskell-mode.
78 ;;
79 ;; LFD (haskell-newline-and-indent).  
80 ;;     This is probably the function you will be using the most (press
81 ;;     CTRL while you press Return, press C-j or press Newline). It
82 ;;     makes a new line and performs indentation based on the last 
83 ;;     preceding non-comment line.
84 ;;
85 ;; M-; (indent-for-comment).
86 ;;     Like in other language modes, this command will give you a comment
87 ;;     at the of the current line. The column where the comment starts is
88 ;;     determined by the variable comment-column (default: 40).
89 ;;    
90 ;; C-c C-v (haskell-mode-version). 
91 ;;     Get the version of the haskell-mode.
92 ;;
93 ;;
94 ;; 1.3. COMMANDS RELATED TO THE INFERIOR SHELL
95 ;; -------------------------------------------
96 ;;
97 ;; C-c C-s (haskell-pop-to-shell).
98 ;;     This command starts up an inferior shell running haskell. If the shell
99 ;;     is running, it will just pop up the shell window.
100 ;;
101 ;; C-c C-u (haskell-save-buffer-use-file).
102 ;;     This command will save the current buffer and send a "load file",
103 ;;     where file is the file visited by the current buffer, to the
104 ;;     inferior shell running haskell.
105 ;;
106 ;; C-c C-f (haskell-run-on-file).
107 ;;     Will send a "load file" to the inferior shell running haskell,
108 ;;     prompting you for the file name.
109 ;;    
110 ;; C-c C-r (haskell-send-region). 
111 ;;     Will send region, from point to mark, to the inferior shell
112 ;;     running haskell.
113 ;;
114 ;; C-c C-b (haskell-send-buffer). 
115 ;;     Will send whole buffer to inferior shell running haskell.
116 ;;
117 ;; 2. INDENTATION
118 ;; ================
119 ;; 
120 ;; The first indentation command (using C-j or TAB) on a given line
121 ;; indents like the last preceding non-comment line. The next TAB
122 ;; indents to the indentation of the innermost enclosing scope. Further
123 ;; TABs get you to further enclosing scopes. After indentation has
124 ;; reached the first column, the process restarts using the indentation
125 ;; of the preceding non-comment line, again.
126 ;;
127 ;; 3. INFERIOR SHELL.
128 ;; ==================
129 ;;
130 ;; The mode for Standard ML also contains a mode for an inferior shell
131 ;; running haskell. The mode is the same as the shell-mode, with just one
132 ;; extra command.
133 ;;
134 ;; 3.1. INFERIOR SHELL COMMANDS
135 ;; ----------------------------
136 ;;
137 ;; C-c C-f (haskell-run-on-file).  Send a `load file' to the process running
138 ;; haskell.
139 ;;
140 ;; 3.2. CONSTANTS CONTROLLING THE INFERIOR SHELL MODE
141 ;; --------------------------------------------------
142 ;;
143 ;; Because haskell is called differently on various machines, and the
144 ;; haskell-systems have their own command for reading in a file, a set of
145 ;; constants controls the behavior of the inferior shell running haskell (to
146 ;; change these constants: See CUSTOMIZING YOUR Haskell-MODE below).
147 ;;
148 ;; haskell-prog-name (default "hbi").
149 ;;     This constant is a string, containing the command to invoke
150 ;;     Standard ML on your system. 
151 ;;
152 ;; haskell-use-right-delim (default "\"")
153 ;; haskell-use-left-delim  (default "\"")
154 ;;     The left and right delimiter used by your version of haskell, for
155 ;;     `use file-name'.
156 ;;
157 ;; haskell-process-name (default "Haskell"). 
158 ;;     The name of the process running haskell. (This will be the name
159 ;;     appearing on the mode line of the buffer)
160 ;;
161 ;; NOTE: The haskell-mode functions: haskell-send-buffer, haskell-send-function and
162 ;; haskell-send-region, creates temporary files (I could not figure out how
163 ;; to send large amounts of data to a process). These files will be
164 ;; removed when you leave emacs.
165 ;;
166 ;; 4. FONTIFICATION
167 ;;
168 ;; There is support for Jamie Zawinski's font-lock-mode through the
169 ;; variable "haskell-font-lock-keywords".
170 ;;
171 ;; 5. CUSTOMIZING YOUR Haskell-MODE
172 ;; ============================
173 ;;
174 ;; If you have to change some of the constants, you will have to add a
175 ;; `hook' to the haskell-mode. Insert this in your "~/.emacs" file.
176 ;;
177 ;;    (setq haskell-mode-hook 'my-haskell-constants)
178 ;;
179 ;; Your function "my-haskell-constants" will then be executed every time
180 ;; "haskell-mode" is invoked.  Now you only have to write the emacs-lisp
181 ;; function "my-haskell-constants", and put it in your "~/.emacs" file.
182 ;;
183 ;; Say you are running a version of haskell that uses the syntax `load
184 ;; ["file"]', is invoked by the command "OurHaskell" and you don't want the
185 ;; indentation algorithm to indent according to open parenthesis, your
186 ;; function should look like this:
187 ;;
188 ;;    (defun my-haskell-constants ()
189 ;;       (setq haskell-prog-name "OurHaskell")
190 ;;       (setq haskell-use-left-delim "[\"")
191 ;;       (setq haskell-use-right-delim "\"]")
192 ;;       (setq haskell-paren-lookback nil))
193 ;;
194 ;; The haskell-shell also runs a `hook' (haskell-shell-hook) when it is invoked.
195 ;;
196 ;;
197 ;;
198 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
199 ;;
200 ;;
201 ;; ORIGINAL AUTHOR
202 ;;         Lars Bo Nielsen
203 ;;         Aalborg University
204 ;;         Computer Science Dept.
205 ;;         9000 Aalborg
206 ;;         Denmark
207 ;;
208 ;;         lbn@iesd.dk
209 ;;         or: ...!mcvax!diku!iesd!lbn
210 ;;         or: mcvax!diku!iesd!lbn@uunet.uu.net
211 ;;
212 ;; MODIFIED FOR Haskell BY
213 ;;         Lennart Augustsson
214 ;;         indentation stuff by Peter Thiemann
215 ;;
216 ;;
217 ;; Please let me know if you come up with any ideas, bugs, or fixes.
218 ;;
219 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
220
221 (defconst haskell-mode-version-string
222   "HASKELL-MODE, Version 0.2, PJT indentation")
223
224 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
225 ;;;
226 ;;; CONSTANTS CONTROLLING THE MODE.
227 ;;;
228 ;;; These are the constants you might want to change
229 ;;; 
230
231 ;; The command used to start up the haskell-program.
232 (defconst haskell-prog-name "hbi" "*Name of program to run as haskell.")
233
234 ;; The left delimmitter for `load file'
235 (defconst haskell-use-left-delim "\""
236   "*The left delimiter for the filename when using \"load\".")
237
238 ;; The right delimmitter for `load file'
239 (defconst haskell-use-right-delim "\""
240   "*The right delimiter for the filename when using \"load\".")
241
242 ;; A regular expression matching the prompt pattern in the inferior
243 ;; shell
244 (defconst haskell-shell-prompt-pattern "^> *"
245   "*The prompt pattern for the inferion shell running haskell.")
246
247 ;; The template used for temporary files, created when a region is
248 ;; send to the inferior process running haskell.
249 (defconst haskell-tmp-template "/tmp/haskell.tmp."
250   "*Template for the temporary file, created by haskell-simulate-send-region.")
251
252 ;; The name of the process running haskell (This will also be the name of
253 ;; the buffer).
254 (defconst haskell-process-name "Haskell" "*The name of the Haskell-process")
255
256 ;;;
257 ;;; END OF CONSTANTS CONTROLLING THE MODE.
258 ;;;
259 ;;; If you change anything below, you are on your own.
260 ;;; 
261 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
262
263
264 (defvar haskell-mode-syntax-table nil "The syntax table used in haskell-mode.")
265
266 (defvar haskell-mode-map nil "The mode map used in haskell-mode.")
267
268 (defvar haskell-mode-abbrev-table nil "The abbrev-table used in haskell-mode.")
269
270 (defvar haskell-old-kill-emacs-hook nil "Old value of kill-emacs-hook")
271
272 (defun haskell-mode ()
273   "Major mode for editing Haskell code.
274 Tab indents for Haskell code.
275 Comments are delimited with --
276 Paragraphs are separated by blank lines only.
277 Delete converts tabs to spaces as it moves back.
278
279 Key bindings:
280 =============
281
282 \\[haskell-pop-to-shell]\t  Pop to the haskell window.
283 \\[haskell-save-buffer-use-file]\t  Save the buffer, and send a \"load file\".
284 \\[haskell-send-region]\t  Send region (point and mark) to haskell.
285 \\[haskell-run-on-file]\t  Send a \"load file\" to haskell.
286 \\[haskell-send-buffer]\t  Send whole buffer to haskell.
287 \\[haskell-mode-version]\t  Get the version of haskell-mode.
288 \\[haskell-evaluate-expression]\t  Prompt for an expression and evalute it.
289
290
291 Mode map
292 ========
293 \\{haskell-mode-map}
294 Runs haskell-mode-hook if non nil."
295   (interactive)
296   (kill-all-local-variables)
297   (if haskell-mode-map
298       ()
299     (setq haskell-mode-map (make-sparse-keymap))
300     (define-key haskell-mode-map "\C-c\C-v" 'haskell-mode-version)
301     (define-key haskell-mode-map "\C-c\C-u" 'haskell-save-buffer-use-file)
302     (define-key haskell-mode-map "\C-c\C-s" 'haskell-pop-to-shell)
303     (define-key haskell-mode-map "\C-c\C-r" 'haskell-send-region)
304     (define-key haskell-mode-map "\C-c\C-m" 'haskell-region)
305     (define-key haskell-mode-map "\C-c\C-f" 'haskell-run-on-file)
306     (define-key haskell-mode-map "\C-c\C-b" 'haskell-send-buffer)
307     (define-key haskell-mode-map "\C-c\C-l" 'comment-line)
308     (define-key haskell-mode-map "\C-ce"    'haskell-evaluate-expression)
309 ;    (define-key haskell-mode-map "\C-j"     'haskell-newline-and-indent)
310     (define-key haskell-mode-map [S-tab]    'tab-to-tab-stop)
311     (define-key haskell-mode-map "\177"     'backward-delete-char-untabify))
312   (use-local-map haskell-mode-map)
313   (setq major-mode 'haskell-mode)
314   (setq mode-name "Haskell")
315   (define-abbrev-table 'haskell-mode-abbrev-table ())
316   (setq local-abbrev-table haskell-mode-abbrev-table)
317   (if haskell-mode-syntax-table
318       ()
319     (setq haskell-mode-syntax-table (make-syntax-table))
320     (modify-syntax-entry ?{  "(}1"    haskell-mode-syntax-table)
321     (modify-syntax-entry ?}  "){4"    haskell-mode-syntax-table)
322 ; partain: out
323 ;    (modify-syntax-entry ?-  "_ 2356" haskell-mode-syntax-table)
324 ;    (modify-syntax-entry ?\f "> b"    haskell-mode-syntax-table)
325 ;    (modify-syntax-entry ?\n "> b"    haskell-mode-syntax-table)
326 ; partain: end out
327 ; partain: in
328     (modify-syntax-entry ?-  "_ 23" haskell-mode-syntax-table)
329 ;    (modify-syntax-entry ?\f "> b"    haskell-mode-syntax-table)
330 ;    (modify-syntax-entry ?\n "> b"    haskell-mode-syntax-table)
331 ; partain: end in
332     (modify-syntax-entry ?\\ "\\"     haskell-mode-syntax-table)
333     (modify-syntax-entry ?*  "_"      haskell-mode-syntax-table)
334     (modify-syntax-entry ?_  "_"      haskell-mode-syntax-table)
335     (modify-syntax-entry ?'  "_"      haskell-mode-syntax-table)
336     (modify-syntax-entry ?:  "_"      haskell-mode-syntax-table)
337     (modify-syntax-entry ?|  "."      haskell-mode-syntax-table)
338     )
339   (set-syntax-table haskell-mode-syntax-table)
340   (make-local-variable 'require-final-newline) ; Always put a new-line
341   (setq require-final-newline t)        ; in the end of file
342 ;  (make-local-variable 'change-major-mode-hook)
343 ;  (setq change-major-mode-hook nil)
344 ;  (make-local-variable 'indent-line-function)
345 ;  (setq indent-line-function 'haskell-indent-line)
346   (make-local-variable 'comment-start)
347   (setq comment-start "-- ")
348 ;  (setq comment-start "{- ")
349   (make-local-variable 'comment-end)
350   (setq comment-end "")
351 ;  (setq comment-end " -}")
352   (make-local-variable 'comment-column)
353   (setq comment-column 60)              ; Start of comment in this column
354   (make-local-variable 'comment-start-skip)
355   (setq comment-start-skip "{-+ *\\|--+ *") ; This matches a start of comment
356   (make-local-variable 'comment-multi-line)
357   (setq comment-multi-line nil)
358 ;  (make-local-variable 'comment-indent-function)
359 ;  (setq comment-indent-function 'haskell-comment-indent)
360   ;;
361   ;; Adding these will fool the matching of parens. I really don't
362   ;; know why. It would be nice to have comments treated as
363   ;; white-space
364   ;; 
365   ;; (make-local-variable 'parse-sexp-ignore-comments)
366   ;; (setq parse-sexp-ignore-comments t)
367   ;; 
368   (run-hooks 'haskell-mode-hook))               ; Run the hook
369
370 (defun haskell-mode-version ()
371   (interactive)
372   (message haskell-mode-version-string))
373
374
375 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
376 ;;;
377 ;;; INDENTATION
378 ;;;
379 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
380
381 ;;; some variables for later use
382
383 (defvar haskell-open-comment "{-")
384 (defvar haskell-close-comment "-}")
385 (defvar haskell-indentation-counter 0
386   "count repeated invocations of indent-for-tab-command")
387 (defvar haskell-literate-flag nil
388   "used to guide literate/illiterate behavior, set automagically")
389
390 (defun haskell-newline-and-indent ()
391   (interactive)
392   (setq haskell-literate-flag
393         (save-excursion
394           (beginning-of-line)
395           (= (following-char) ?>)))
396   (newline)
397   (if haskell-literate-flag (insert ">"))
398   (haskell-indent-line))
399
400 (defun haskell-indent-line ()
401   "Indent current line of ordinary or literate Haskell code."
402   (interactive)
403   (let ((indent (haskell-calculate-indentation-pjt-2)))
404     (if (/= (current-indentation) indent)
405         (let ((beg (progn
406                      (beginning-of-line)
407                      (if (= (following-char) ?>) (forward-char 1)) ;LITERATE
408                      (point))))
409           (skip-chars-forward "\t ")
410           (delete-region beg (point))
411           (indent-to indent))
412       ;; If point is before indentation, move point to indentation
413       (if (< (current-column) (current-indentation))
414           (skip-chars-forward "\t ")))))
415
416 (defun haskell-calculate-indentation ()
417   (save-excursion
418     (let ((col (current-column)))
419       (while (and (not (bobp))                  ;skip over empty and comment-only lines
420                   (= col (current-column)))
421         (previous-line 1)
422         (beginning-of-line)                     ; Go to first non whitespace
423         (if (= (following-char) ?>)             ;LITERATE
424             (forward-char 1)
425           (if haskell-literate-flag             ;ignore illiterate lines
426               (end-of-line)))
427         (skip-chars-forward "\t ")              ; on the line.
428         (setq col (current-column))
429         (search-forward-regexp (concat haskell-open-comment "\\|--\\|\n") nil 0)
430         (goto-char (match-beginning 0)))
431       (search-backward-regexp "\\b\\(where\\|let\\|of\\|in\\)\\b\\|\n" nil 0)
432       (if (looking-at "\n")
433           ()
434         (setq col (current-column))
435         (forward-word 1)
436         (skip-chars-forward "\t ")
437         (if (looking-at "\\w")
438             (setq col (current-column))
439           (setq col (+ 2 col))))
440       col)))
441
442 (defun haskell-calculate-indentation-pjt-2 ()
443   "Calculate indentation for Haskell program code, versatile version"
444   (save-excursion
445     (if (eq last-command 'haskell-indentation)
446         (setq haskell-indentation-counter (1+ haskell-indentation-counter))
447       (setq haskell-indentation-counter -1))
448     (setq this-command 'haskell-indentation)
449     (let* ((simple-indent (haskell-calculate-indentation))
450            (count haskell-indentation-counter)
451            (min-indent simple-indent)   ; minimum indentation found in a non-comment line
452            (last-indent simple-indent)  ; indentation of the following non-comment line
453            (return-indent nil)          ; computed indentation
454            (comment-depth 0))
455       (previous-line 1)
456       (if (< haskell-indentation-counter 0) ; 1st tab gives simple indentation
457           (setq return-indent simple-indent))
458       (while (not return-indent)
459         (if (search-backward-regexp "\\b\\(where\\|let\\|of\\)\\b\\|\n\\|{-\\|-}" nil t 1)
460             (cond
461              ((looking-at haskell-open-comment)
462               (setq comment-depth (1- comment-depth)))
463              ((looking-at haskell-close-comment)
464               (setq comment-depth (1+ comment-depth)))
465              ((= 0 comment-depth)
466               (cond
467                ((looking-at "\n")
468                 (save-excursion
469                   (forward-char 1)
470                   (if (= (following-char) ?>)
471                       (forward-char 1)
472                     (if haskell-literate-flag
473                         (end-of-line))) ;LITERATE: ignore lines w/o >
474                   (skip-chars-forward "\t ")
475                   (if (looking-at (concat haskell-open-comment "\\|--\\|\n"))
476                       ()
477                     (setq last-indent (current-column))
478                     (if (< last-indent min-indent)
479                         (setq min-indent last-indent)))))
480                (t                       ; looking at a keyword
481                 (save-excursion
482                   (forward-word 1)
483                   (skip-chars-forward " \t")
484                   (if (and haskell-literate-flag ;LITERATE: ignore lines w/o >
485                            (save-excursion
486                              (beginning-of-line)
487                              (/= (following-char) ?>)))
488                       (end-of-line))
489                   (if (looking-at (concat haskell-open-comment "\\|--\\|\n"))
490                       ()
491                     (setq last-indent (current-column)))
492                   (if (<= last-indent min-indent)
493                       (if (> count 0)
494                           (setq count (1- count))
495                         (setq return-indent last-indent)))
496                   (if (< last-indent min-indent)
497                       (setq min-indent last-indent)))))))
498           (setq return-indent simple-indent)
499           (setq haskell-indentation-counter -1)))
500       return-indent)))
501
502 (defun haskell-skip-nested-comment ()
503   ;; point looks at opening {-, move over closing -}
504   ;; todo: specify what happens on failure, bounds check ...
505   (forward-char 2)
506   (let ((comment-depth 1))
507     (while (> comment-depth 0)
508       (search-forward-regexp "{-\\|-}")
509       (goto-char (match-beginning 0))
510       (setq comment-depth
511             (if (= (following-char) 123) ; code for opening brace
512                 (1+ comment-depth)
513               (1- comment-depth)))
514       (goto-char (match-end 0)))))
515
516
517 ;;;seemingly obsolete functions
518 (defun haskell-inside-of-inline-comment ()
519   (let ((bolp (save-excursion
520                (beginning-of-line)
521                (point))))
522     (search-backward comment-start bolp t 1)))
523
524 (defun haskell-inside-of-nested-comment ()
525   (save-excursion
526     (let ((count 0))
527       (while
528           (search-backward-regexp "\\({-\\|-}\\)" 0 t 1)
529         (if (haskell-inside-of-inline-comment)
530             ()
531           (if (looking-at haskell-open-comment)
532               (setq count (1+ count))
533             (setq count (1- count)))))
534       (> count 0))))
535
536 (defun haskell-inside-of-comment ()
537   (or (haskell-inside-of-inline-comment)
538       (haskell-inside-of-nested-comment)))
539
540 ;;;stolen from sml-mode.el
541 (defun haskell-comment-indent ()
542   "Compute indentation for Haskell comments"
543   (if (looking-at "^--")
544       0
545     (save-excursion
546       (skip-chars-backward " \t")
547       (max (1+ (current-column))
548            comment-column))))
549
550 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
551 ;;;
552 ;;; INFERIOR SHELL
553 ;;;
554 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
555
556 (defvar haskell-shell-map nil "The mode map for haskell-shell.")
557
558 (defun haskell-shell ()
559   "Inferior shell invoking Haskell.
560 It is not possible to have more than one shell running Haskell.
561 Like the shell mode with the additional command:
562
563 \\[haskell-run-on-file]\t Runs haskell on the file.
564 \\{haskell-shell-map}
565 Variables controlling the mode:
566
567 haskell-prog-name (default \"hbi\")
568     The string used to invoke the haskell program.
569
570 haskell-use-right-delim (default \"\\\"\")
571 haskell-use-left-delim  (default \"\\\"\")
572     The left and right delimiter used by your version of haskell, for
573     \"load file-name\".
574
575 haskell-process-name (default \"Haskell\")
576     The name of the process running haskell.
577
578 haskell-shell-prompt-pattern (default \"^> *\")
579     The prompt pattern.
580
581 Runs haskell-shell-hook if not nil."
582   (interactive)
583   (if (not (process-status haskell-process-name))
584       (save-excursion                   ; Process is not running
585         (message "Starting Haskell...") ; start up a new process
586         (require 'shell)
587         (set-buffer (make-comint haskell-process-name haskell-prog-name))
588         (erase-buffer)                  ; Erase the buffer if a previous
589         (if haskell-shell-map           ; process died in there
590             ()
591           (setq haskell-shell-map (copy-keymap shell-mode-map))
592           (define-key haskell-shell-map "\C-c\C-f" 'haskell-run-on-file))
593         (use-local-map haskell-shell-map)
594         (make-local-variable 'shell-prompt-pattern)
595         (setq shell-prompt-pattern haskell-shell-prompt-pattern)
596         (setq major-mode 'haskell-shell)
597         (setq mode-name "Haskell Shell")
598         (setq mode-line-format 
599               "-----Emacs: %17b   %M   %[(%m: %s)%]----%3p--%-")
600         (set-process-filter (get-process haskell-process-name) 'haskell-process-filter)
601         (message "Starting Haskell...done.")
602         (run-hooks 'haskell-shell-hook))))
603
604 (defun haskell-process-filter (proc str)
605   (let ((cur (current-buffer))
606         (pop-up-windows t))
607     (pop-to-buffer (concat "*" haskell-process-name "*"))
608     (goto-char (point-max))
609     (if (string= str "\b\b\b  \b\b\b")
610         (backward-delete-char 4)
611       (insert str))
612     (set-marker (process-mark proc) (point-max))
613     (pop-to-buffer cur)))
614
615 (defun haskell-pop-to-shell ()
616   (interactive)
617   (haskell-shell)
618   (pop-to-buffer (concat "*" haskell-process-name "*")))
619
620 (defun haskell-run-on-file (fil)
621   (interactive "FRun Haskell on : ")
622   (haskell-shell)
623   (save-some-buffers)
624   (process-send-string haskell-process-name
625                (concat "load " haskell-use-left-delim (expand-file-name fil)
626                        haskell-use-right-delim ";\n")))
627
628 (defun haskell-save-buffer-use-file ()
629   "Save the buffer, and send a `use file' to the inferior shell
630 running Haskell."
631   (interactive)
632   (let (file)
633     (if (setq file (buffer-file-name))  ; Is the buffer associated
634         (progn                          ; with file ?
635           (save-buffer)
636           (haskell-shell)
637           (process-send-string haskell-process-name
638                        (concat "load " haskell-use-left-delim
639                                (expand-file-name file)
640                                haskell-use-right-delim ";\n")))
641       (error "Buffer not associated with file."))))
642
643 (defvar haskell-tmp-files-list nil
644   "List of all temporary files created by haskell-simulate-send-region.
645 Each element in the list is a list with the format:
646
647       (\"tmp-filename\"  buffer  start-line)")
648
649 (defvar haskell-simulate-send-region-called-p nil
650   "Has haskell-simulate-send-region been called previously.")
651
652 (defun haskell-make-temp-name (pre)
653   (concat (make-temp-name pre) ".m"))
654
655 (defun haskell-simulate-send-region (point1 point2)
656   "Simulate send region. As send-region only can handle what ever the
657 system sets as the default, we have to make a temporary file.
658 Updates the list of temporary files (haskell-tmp-files-list)."
659   (let ((file (expand-file-name (haskell-make-temp-name haskell-tmp-template))))
660     ;; Remove temporary files when we leave emacs
661     (if (not haskell-simulate-send-region-called-p)
662         (progn
663           (setq haskell-old-kill-emacs-hook kill-emacs-hook)
664           (setq kill-emacs-hook 'haskell-remove-tmp-files)
665           (setq haskell-simulate-send-region-called-p t)))
666     (save-excursion
667       (goto-char point1)
668       (setq haskell-tmp-files-list
669             (cons (list file
670                         (current-buffer)
671                         (save-excursion ; Calculate line no.
672                           (beginning-of-line)
673                           (1+ (count-lines 1 (point)))))
674                   haskell-tmp-files-list)))
675     (write-region point1 point2 file nil 'dummy)
676     (haskell-shell)
677     (message "Using temporary file: %s" file)
678     (process-send-string
679      haskell-process-name
680      ;; string to send: load file;
681      (concat "load " haskell-use-left-delim file haskell-use-right-delim ";\n"))))
682
683 (defun haskell-remove-tmp-files ()
684   "Remove the temporary files, created by haskell-simulate-send-region, if
685 they still exist. Only files recorded in haskell-tmp-files-list are removed."
686   (message "Removing temporary files created by haskell-mode...")
687   (while haskell-tmp-files-list
688     (condition-case ()
689         (delete-file (car (car haskell-tmp-files-list)))
690       (error ()))
691     (setq haskell-tmp-files-list (cdr haskell-tmp-files-list)))
692   (message "Removing temporary files created by haskell-mode...done.")
693   (run-hooks 'haskell-old-kill-emacs-hook))
694
695 (defun haskell-send-region ()
696   "Send region."
697   (interactive)
698   (let (start end)
699     (save-excursion
700       (setq end (point))
701       (exchange-point-and-mark)
702       (setq start (point)))
703     (haskell-simulate-send-region start end)))
704
705 (defun haskell-send-buffer ()
706   "Send the buffer."
707   (interactive)
708   (haskell-simulate-send-region (point-min) (point-max)))
709
710 (defun haskell-evaluate-expression (h-expr)
711   "Prompt for and evaluate an expression"
712   (interactive "sExpression: ")
713   (let ((str (concat h-expr ";\n"))
714         (buf (current-buffer)))
715     (haskell-pop-to-shell)
716     (insert str)
717     (process-send-string haskell-process-name str)
718     (pop-to-buffer buf)))
719
720
721 ;;
722 ;; font-lock-mode patterns, based on specs. in an earlier version
723 ;; of haskell-mode.el
724 ;; (these patterns have only been tested with 19.30)
725
726 (defconst haskell-font-lock-keywords nil
727  "Conservative highlighting of a Haskell buffer
728 (using font-lock.)")
729
730 (let ((haskell-id "[a-z_][a-zA-Z0-9_'#]+")
731       (haskell-reserved-ids
732            (concat "\\b\\(" 
733                    (mapconcat 
734                        'identity
735                        '("case"    "class"     "data"
736                          "default" "deriving"  "else"
737                          "hiding"  "if" "import"   "in"
738                          "instance" "interface" "let"
739                          "module" "of"   "renaming"
740                          "then"  "to" "type" "where" "infix[rl]?")
741                         "\\|")
742                    "\\)[ \t\n:,]"))
743        (haskell-basic-types 
744            (concat "\\b\\("
745                    (mapconcat 'identity
746                               '("Bool" "()" "String" "Char" "Int"
747                                 "Integer" "Float" "Double" "Ratio"
748                                 "Assoc" "Rational" "Array")
749                               "\\|")
750                    "\\)\\b"))
751        (haskell-prelude-classes
752            (concat "\\b\\("
753                    (mapconcat 'identity
754                               '("Eq" "Ord" "Text" "Num" "Real" "Fractional" 
755                                  "Integral"   "RealFrac" "Floating" "RealFloat"
756                                  "Complex" "Ix" "Enum"
757                                  ;; ghc-isms
758                                  "_CCallable" "_CReturnable")
759                               "\\|")
760                    "\\)\\b"))
761        (haskell-reserved-ops 
762            (mapconcat 'identity
763                       '("\\.\\."  "::"
764                         "=>" "/=" "@"
765                         "<-" "->")
766                       "\\|"))
767        (glasgow-haskell-ops
768            (concat "\\b\\(" 
769                    (mapconcat 
770                       'identity
771                       '(">>"    ">>="  "thenPrimIO"
772                         "seqPrimIO" "returnPrimIO" 
773                         "return" "_ccall_" "_casm_"
774                         "thenST" "seqST" "returnST"
775                         "thenStrictlyST" "seqStrictlyST" "returnStrictlyST"
776                         "unsafeInterleavePrimIO" "unsafePerformIO")
777                       "\\|")
778                    "\\)\\b"))
779        (glasgow-haskell-types
780            (concat "\\b\\(" 
781                    (mapconcat 
782                       'identity
783                       '("IO"    "PrimIO"  "_?ST"
784                         "_Word" "_Addr"   "_?MVar"
785                         "_?IVar" "_RealWorld"
786                         "_?MutableByteArray"
787                         "_?ByteArray")
788                       "\\|")
789                    "\\)\\b")))
790       (setq haskell-font-lock-keywords
791        (list
792          '("--.*$" . font-lock-comment-face)
793          (list "[ \t\n]*\\([A-Za-z[(_][]A-Za-z0-9_$', ~@|:[)(#]*[ \t\n]*\\)=" 1 font-lock-function-name-face)
794          (list (concat "^>?[ \t\n]*\\(" haskell-id "\\)[ \t]*::") 1 'font-lock-function-name-face)
795          (list haskell-reserved-ids    0 'font-lock-function-name-face)
796          (list glasgow-haskell-ops     0 'font-lock-function-name-face)
797          (list glasgow-haskell-types   0 'font-lock-type-face)
798          (list haskell-basic-types     0 'font-lock-type-face)
799          (list haskell-prelude-classes 0 'font-lock-type-face)
800          (list "^[ \t\n]*\\([A-Za-z[(_][]A-Za-z0-9_$', @:[)(#]*[ \t\n]*\\)->" 1 font-lock-variable-name-face)
801         )))
802
803 ;;
804 ;; To enable font-lock-mode for Haskell buffers, add something
805 ;; like this to your ~/.emacs
806
807 ;(cond (window-system
808 ;  (require 'font-lock)
809 ;  (add-hook 'haskell-mode-hook
810 ;    '(lambda () (make-local-variable 'font-lock-defaults)
811 ;               (make-local-variable 'font-lock-mode-hook) ; don't affect other buffers      
812 ;               (setq font-lock-mode-hook nil)
813 ;               (add-hook 'font-lock-mode-hook 
814 ;                    '(lambda ()
815 ;                         (setq font-lock-keywords haskell-font-lock-keywords)))
816 ;               (font-lock-mode 1))))
817
818
819
820 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
821 ;;;;
822 ;;;; END OF Haskell-MODE
823 ;;;;
824 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
825 (provide 'haskell-mode)