[project @ 1997-03-14 05:31:07 by sof]
[ghc-hetmet.git] / ghc / CONTRIB / haskell-modes / chalmers / original / 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
5 ;; This file is not officially part of GNU Emacs.
6
7 ;; GNU Emacs is distributed in the hope that it will be useful,
8 ;; but WITHOUT ANY WARRANTY.  No author or distributor
9 ;; accepts responsibility to anyone for the consequences of using it
10 ;; or for whether it serves any particular purpose or works at all,
11 ;; unless he says so in writing.  Refer to the GNU Emacs General Public
12 ;; License for full details.
13
14 ;; Everyone is granted permission to copy, modify and redistribute
15 ;; GNU Emacs, but only under the conditions described in the
16 ;; GNU Emacs General Public License.   A copy of this license is
17 ;; supposed to have been given to you along with GNU Emacs so you
18 ;; can know your rights and responsibilities.  It should be in a
19 ;; file named COPYING.  Among other things, the copyright notice
20 ;; and this notice must be preserved on all copies.
21
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;;
24 ;; Haskell Mode. A major mode for editing and running Haskell. (Version 0.0)
25 ;; =================================================================
26 ;;
27 ;; This is a mode for editing and running Haskell.
28 ;; It is very much based on the sml mode for GNU Emacs. It
29 ;; features:
30 ;;
31 ;;      - Inferior shell running Haskell. No need to leave emacs, just
32 ;;        keep right on editing while Haskell runs in another window.
33 ;;
34 ;;      - Automatic "load file" in inferior shell. Send regions of code
35 ;;        to the Haskell program.
36 ;;
37 ;;
38 ;; 1. HOW TO USE THE Haskell-MODE
39 ;; ==========================
40 ;;
41 ;; Here is a short introduction to the mode.
42 ;;
43 ;; 1.1 GETTING STARTED
44 ;; -------------------
45 ;;
46 ;; If you are an experienced user of Emacs, just skip this section.
47 ;;
48 ;; To use the haskell-mode, insert this in your "~/.emacs" file (Or ask your
49 ;; emacs-administrator to help you.):
50 ;;
51 ;;    (setq auto-mode-alist (cons '("\\.hs$" . haskell-mode) (cons '("\\.lhs$" . haskell-mode)
52 ;;                           auto-mode-alist)))
53 ;;    (autoload 'haskell-mode "haskell-mode" "Major mode for editing Haskell." t)
54 ;;
55 ;; Now every time a file with the extension `.hs' or `.lhs' is found, it is
56 ;; automatically started up in haskell-mode.
57 ;;
58 ;; You will also have to specify the path to this file, so you will have
59 ;; to add this as well:
60 ;;
61 ;;    (setq load-path (cons "/usr/me/emacs" load-path))
62 ;;
63 ;; where "/usr/me/emacs" is the directory where this file is.
64 ;;
65 ;; You may also want to compile the this file (M-x byte-compile-file)
66 ;; for speed.
67 ;;
68 ;; You are now ready to start using haskell-mode. If you have tried other
69 ;; language modes (like lisp-mode or C-mode), you should have no
70 ;; problems. There are only a few extra functions in this mode.
71 ;;
72 ;; 1.2. EDITING COMMANDS.
73 ;; ----------------------
74 ;;
75 ;; The following editing and inferior-shell commands can ONLY be issued
76 ;; from within a buffer in haskell-mode.
77 ;;
78 ;; LFD (reindent-then-newline-and-indent).  
79 ;;     This is probably the function you will be using the most (press
80 ;;     CTRL while you press Return, press C-j or press Newline). It
81 ;;     will reindent the line, then make a new line and perform a new
82 ;;     indentation.
83 ;;
84 ;; M-; (indent-for-comment).
85 ;;     Like in other language modes, this command will give you a comment
86 ;;     at the of the current line. The column where the comment starts is
87 ;;     determined by the variable comment-column (default: 40).
88 ;;    
89 ;; C-c C-v (haskell-mode-version). 
90 ;;     Get the version of the haskell-mode.
91 ;;
92 ;;
93 ;; 1.3. COMMANDS RELATED TO THE INFERIOR SHELL
94 ;; -------------------------------------------
95 ;;
96 ;; C-c C-s (haskell-pop-to-shell).
97 ;;     This command starts up an inferior shell running haskell. If the shell
98 ;;     is running, it will just pop up the shell window.
99 ;;
100 ;; C-c C-u (haskell-save-buffer-use-file).
101 ;;     This command will save the current buffer and send a "load file",
102 ;;     where file is the file visited by the current buffer, to the
103 ;;     inferior shell running haskell.
104 ;;
105 ;; C-c C-f (haskell-run-on-file).
106 ;;     Will send a "load file" to the inferior shell running haskell,
107 ;;     prompting you for the file name.
108 ;;    
109 ;; C-c C-r (haskell-send-region). 
110 ;;     Will send region, from point to mark, to the inferior shell
111 ;;     running haskell.
112 ;;
113 ;; C-c C-b (haskell-send-buffer). 
114 ;;     Will send whole buffer to inferior shell running haskell.
115 ;;
116 ;; 2. INDENTATION
117 ;; ================
118 ;; Not yet.
119 ;;
120 ;; 3. INFERIOR SHELL.
121 ;; ==================
122 ;;
123 ;; The mode for Standard ML also contains a mode for an inferior shell
124 ;; running haskell. The mode is the same as the shell-mode, with just one
125 ;; extra command.
126 ;;
127 ;; 3.1. INFERIOR SHELL COMMANDS
128 ;; ----------------------------
129 ;;
130 ;; C-c C-f (haskell-run-on-file).  Send a `load file' to the process running
131 ;; haskell.
132 ;;
133 ;; 3.2. CONSTANTS CONTROLLING THE INFERIOR SHELL MODE
134 ;; --------------------------------------------------
135 ;;
136 ;; Because haskell is called differently on various machines, and the
137 ;; haskell-systems have their own command for reading in a file, a set of
138 ;; constants controls the behavior of the inferior shell running haskell (to
139 ;; change these constants: See CUSTOMIZING YOUR Haskell-MODE below).
140 ;;
141 ;; haskell-prog-name (default "hbi").
142 ;;     This constant is a string, containing the command to invoke
143 ;;     Standard ML on your system. 
144 ;;
145 ;; haskell-use-right-delim (default "\"")
146 ;; haskell-use-left-delim  (default "\"")
147 ;;     The left and right delimiter used by your version of haskell, for
148 ;;     `use file-name'.
149 ;;
150 ;; haskell-process-name (default "Haskell"). 
151 ;;     The name of the process running haskell. (This will be the name
152 ;;     appearing on the mode line of the buffer)
153 ;;
154 ;; NOTE: The haskell-mode functions: haskell-send-buffer, haskell-send-function and
155 ;; haskell-send-region, creates temporary files (I could not figure out how
156 ;; to send large amounts of data to a process). These files will be
157 ;; removed when you leave emacs.
158 ;;
159 ;;
160 ;; 4. CUSTOMIZING YOUR Haskell-MODE
161 ;; ============================
162 ;;
163 ;; If you have to change some of the constants, you will have to add a
164 ;; `hook' to the haskell-mode. Insert this in your "~/.emacs" file.
165 ;;
166 ;;    (setq haskell-mode-hook 'my-haskell-constants)
167 ;;
168 ;; Your function "my-haskell-constants" will then be executed every time
169 ;; "haskell-mode" is invoked.  Now you only have to write the emacs-lisp
170 ;; function "my-haskell-constants", and put it in your "~/.emacs" file.
171 ;;
172 ;; Say you are running a version of haskell that uses the syntax `load
173 ;; ["file"]', is invoked by the command "OurHaskell" and you don't want the
174 ;; indentation algorithm to indent according to open parenthesis, your
175 ;; function should look like this:
176 ;;
177 ;;    (defun my-haskell-constants ()
178 ;;       (setq haskell-prog-name "OurHaskell")
179 ;;       (setq haskell-use-left-delim "[\"")
180 ;;       (setq haskell-use-right-delim "\"]")
181 ;;       (setq haskell-paren-lookback nil))
182 ;;
183 ;; The haskell-shell also runs a `hook' (haskell-shell-hook) when it is invoked.
184 ;;
185 ;;
186 ;;
187 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
188 ;;
189 ;;
190 ;; ORIGINAL AUTHOR
191 ;;         Lars Bo Nielsen
192 ;;         Aalborg University
193 ;;         Computer Science Dept.
194 ;;         9000 Aalborg
195 ;;         Denmark
196 ;;
197 ;;         lbn@iesd.dk
198 ;;         or: ...!mcvax!diku!iesd!lbn
199 ;;         or: mcvax!diku!iesd!lbn@uunet.uu.net
200 ;;
201 ;; MODIFIED FOR Haskell BY
202 ;;         Lennart Augustsson
203 ;;
204 ;;
205 ;; Please let me know if you come up with any ideas, bugs, or fixes.
206 ;;
207 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
208
209 (defconst haskell-mode-version-string
210   "HASKELL-MODE, Version 0.1")
211
212 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
213 ;;;
214 ;;; CONSTANTS CONTROLLING THE MODE.
215 ;;;
216 ;;; These are the constants you might want to change
217 ;;; 
218
219 ;; The command used to start up the haskell-program.
220 (defconst haskell-prog-name "hbi" "*Name of program to run as haskell.")
221
222 ;; The left delimmitter for `load file'
223 (defconst haskell-use-left-delim "\""
224   "*The left delimiter for the filename when using \"load\".")
225
226 ;; The right delimmitter for `load file'
227 (defconst haskell-use-right-delim "\""
228   "*The right delimiter for the filename when using \"load\".")
229
230 ;; A regular expression matching the prompt pattern in the inferior
231 ;; shell
232 (defconst haskell-shell-prompt-pattern "^> *"
233   "*The prompt pattern for the inferion shell running haskell.")
234
235 ;; The template used for temporary files, created when a region is
236 ;; send to the inferior process running haskell.
237 (defconst haskell-tmp-template "/tmp/haskell.tmp."
238   "*Template for the temporary file, created by haskell-simulate-send-region.")
239
240 ;; The name of the process running haskell (This will also be the name of
241 ;; the buffer).
242 (defconst haskell-process-name "Haskell" "*The name of the Haskell-process")
243
244 ;;;
245 ;;; END OF CONSTANTS CONTROLLING THE MODE.
246 ;;;
247 ;;; If you change anything below, you are on your own.
248 ;;; 
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250
251
252 (defvar haskell-mode-syntax-table nil "The syntax table used in haskell-mode.")
253
254 (defvar haskell-mode-map nil "The mode map used in haskell-mode.")
255
256 (defun haskell-mode ()
257   "Major mode for editing Haskell code.
258 Tab indents for Haskell code.
259 Comments are delimited with --
260 Paragraphs are separated by blank lines only.
261 Delete converts tabs to spaces as it moves back.
262
263 Key bindings:
264 =============
265
266 \\[haskell-pop-to-shell]\t  Pop to the haskell window.
267 \\[haskell-save-buffer-use-file]\t  Save the buffer, and send a \"load file\".
268 \\[haskell-send-region]\t  Send region (point and mark) to haskell.
269 \\[haskell-run-on-file]\t  Send a \"load file\" to haskell.
270 \\[haskell-send-buffer]\t  Send whole buffer to haskell.
271 \\[haskell-mode-version]\t  Get the version of haskell-mode.
272 \\[haskell-evaluate-expression]\t  Prompt for an expression and evalute it.
273
274
275 Mode map
276 ========
277 \\{haskell-mode-map}
278 Runs haskell-mode-hook if non nil."
279   (interactive)
280   (kill-all-local-variables)
281   (if haskell-mode-map
282       ()
283     (setq haskell-mode-map (make-sparse-keymap))
284     (define-key haskell-mode-map "\C-c\C-v" 'haskell-mode-version)
285     (define-key haskell-mode-map "\C-c\C-u" 'haskell-save-buffer-use-file)
286     (define-key haskell-mode-map "\C-c\C-s" 'haskell-pop-to-shell)
287     (define-key haskell-mode-map "\C-c\C-r" 'haskell-send-region)
288     (define-key haskell-mode-map "\C-c\C-m" 'haskell-region)
289     (define-key haskell-mode-map "\C-c\C-f" 'haskell-run-on-file)
290     (define-key haskell-mode-map "\C-c\C-b" 'haskell-send-buffer)
291     (define-key haskell-mode-map "\C-ce"    'haskell-evaluate-expression)
292     (define-key haskell-mode-map "\C-j" 'reindent-then-newline-and-indent)
293     (define-key haskell-mode-map "\177" 'backward-delete-char-untabify))
294   (use-local-map haskell-mode-map)
295   (setq major-mode 'haskell-mode)
296   (setq mode-name "Haskell")
297   (define-abbrev-table 'haskell-mode-abbrev-table ())
298   (setq local-abbrev-table haskell-mode-abbrev-table)
299   (if haskell-mode-syntax-table
300       ()
301     (setq haskell-mode-syntax-table (make-syntax-table))
302     (modify-syntax-entry ?\( "()1" haskell-mode-syntax-table)
303     (modify-syntax-entry ?\) ")(4" haskell-mode-syntax-table)
304     (modify-syntax-entry ?\\ "." haskell-mode-syntax-table)
305     (modify-syntax-entry ?* ". 23" haskell-mode-syntax-table)
306         ;; Special characters in haskell-mode to be treated as normal
307     ;; characters:
308     (modify-syntax-entry ?_ "w" haskell-mode-syntax-table)
309     (modify-syntax-entry ?\' "w" haskell-mode-syntax-table)
310     )
311   (set-syntax-table haskell-mode-syntax-table)
312   (make-local-variable 'require-final-newline) ; Always put a new-line
313   (setq require-final-newline t)        ; in the end of file
314   (make-local-variable 'indent-line-function)
315   (setq indent-line-function 'haskell-indent-line)
316   (make-local-variable 'comment-start)
317   (setq comment-start "-- ")
318   (make-local-variable 'comment-end)
319   (setq comment-end "")
320   (make-local-variable 'comment-column)
321   (setq comment-column 39)              ; Start of comment in this column
322   (make-local-variable 'comment-start-skip)
323   (setq comment-start-skip "(\\*+[ \t]?") ; This matches a start of comment
324   (make-local-variable 'comment-indent-hook)
325   (setq comment-indent-hook 'haskell-comment-indent)
326   ;;
327   ;; Adding these will fool the matching of parens. I really don't
328   ;; know why. It would be nice to have comments treated as
329   ;; white-space
330   ;; 
331   ;; (make-local-variable 'parse-sexp-ignore-comments)
332   ;; (setq parse-sexp-ignore-comments t)
333   ;; 
334   (run-hooks 'haskell-mode-hook))               ; Run the hook
335
336 (defun haskell-mode-version ()
337   (interactive)
338   (message haskell-mode-version-string))
339
340
341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
342 ;;;
343 ;;; INDENTATION
344 ;;;
345 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
346 (defun haskell-indent-line ()
347   "Indent current line of Haskell code."
348   (interactive)
349   (let ((indent (haskell-calculate-indentation)))
350     (if (/= (current-indentation) indent)
351         (let ((beg (progn (beginning-of-line) (point))))
352           (skip-chars-forward "\t ")
353           (delete-region beg (point))
354           (indent-to indent))
355       ;; If point is before indentation, move point to indentation
356       (if (< (current-column) (current-indentation))
357           (skip-chars-forward "\t ")))))
358
359 (defun haskell-calculate-indentation ()
360   (save-excursion
361     (previous-line 1)
362     (beginning-of-line)                 ; Go to first non whitespace
363     (skip-chars-forward "\t ")          ; on the line.
364     (current-column)))
365
366 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
367 ;;;
368 ;;; INFERIOR SHELL
369 ;;;
370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
371
372 (defvar haskell-shell-map nil "The mode map for haskell-shell.")
373
374 (defun haskell-shell ()
375   "Inferior shell invoking Haskell.
376 It is not possible to have more than one shell running Haskell.
377 Like the shell mode with the additional command:
378
379 \\[haskell-run-on-file]\t Runs haskell on the file.
380 \\{haskell-shell-map}
381 Variables controlling the mode:
382
383 haskell-prog-name (default \"hbi\")
384     The string used to invoke the haskell program.
385
386 haskell-use-right-delim (default \"\\\"\")
387 haskell-use-left-delim  (default \"\\\"\")
388     The left and right delimiter used by your version of haskell, for
389     \"load file-name\".
390
391 haskell-process-name (default \"Haskell\")
392     The name of the process running haskell.
393
394 haskell-shell-prompt-pattern (default \"^> *\")
395     The prompt pattern.
396
397 Runs haskell-shell-hook if not nil."
398   (interactive)
399   (if (not (process-status haskell-process-name))
400       (save-excursion                   ; Process is not running
401         (message "Starting Haskell...") ; start up a new process
402         (require 'shell)
403         (set-buffer (make-shell haskell-process-name haskell-prog-name))
404         (erase-buffer)                  ; Erase the buffer if a previous
405         (if haskell-shell-map           ; process died in there
406             ()
407           (setq haskell-shell-map (copy-sequence shell-mode-map))
408           (define-key haskell-shell-map "\C-c\C-f" 'haskell-run-on-file))
409         (use-local-map haskell-shell-map)
410         (make-local-variable 'shell-prompt-pattern)
411         (setq shell-prompt-pattern haskell-shell-prompt-pattern)
412         (setq major-mode 'haskell-shell)
413         (setq mode-name "Haskell Shell")
414         (setq mode-line-format 
415               "-----Emacs: %17b   %M   %[(%m: %s)%]----%3p--%-")
416         (set-process-filter (get-process haskell-process-name) 'haskell-process-filter)
417         (message "Starting Haskell...done.")
418         (run-hooks 'haskell-shell-hook))))
419
420 (defun haskell-process-filter (proc str)
421   (let ((cur (current-buffer))
422         (pop-up-windows t))
423     (pop-to-buffer (concat "*" haskell-process-name "*"))
424     (goto-char (point-max))
425     (if (string= str "\b\b\b  \b\b\b")
426         (backward-delete-char 4)
427       (insert str))
428     (set-marker (process-mark proc) (point-max))
429     (pop-to-buffer cur)))
430
431 (defun haskell-pop-to-shell ()
432   (interactive)
433   (haskell-shell)
434   (pop-to-buffer (concat "*" haskell-process-name "*")))
435
436 (defun haskell-run-on-file (fil)
437   (interactive "FRun Haskell on : ")
438   (haskell-shell)
439   (save-some-buffers)
440   (send-string haskell-process-name
441                (concat "load " haskell-use-left-delim (expand-file-name fil)
442                        haskell-use-right-delim ";\n")))
443
444 (defun haskell-save-buffer-use-file ()
445   "Save the buffer, and send a `use file' to the inferior shell
446 running Haskell."
447   (interactive)
448   (let (file)
449     (if (setq file (buffer-file-name))  ; Is the buffer associated
450         (progn                          ; with file ?
451           (save-buffer)
452           (haskell-shell)
453           (send-string haskell-process-name
454                        (concat "load " haskell-use-left-delim
455                                (expand-file-name file)
456                                haskell-use-right-delim ";\n")))
457       (error "Buffer not associated with file."))))
458
459 (defvar haskell-tmp-files-list nil
460   "List of all temporary files created by haskell-simulate-send-region.
461 Each element in the list is a list with the format:
462
463       (\"tmp-filename\"  buffer  start-line)")
464
465 (defvar haskell-simulate-send-region-called-p nil
466   "Has haskell-simulate-send-region been called previously.")
467
468 (defun haskell-make-temp-name (pre)
469   (concat (make-temp-name pre) ".m"))
470
471 (defun haskell-simulate-send-region (point1 point2)
472   "Simulate send region. As send-region only can handle what ever the
473 system sets as the default, we have to make a temporary file.
474 Updates the list of temporary files (haskell-tmp-files-list)."
475   (let ((file (expand-file-name (haskell-make-temp-name haskell-tmp-template))))
476     ;; Remove temporary files when we leave emacs
477     (if (not haskell-simulate-send-region-called-p)
478         (progn
479           (setq haskell-old-kill-emacs-hook kill-emacs-hook)
480           (setq kill-emacs-hook 'haskell-remove-tmp-files)
481           (setq haskell-simulate-send-region-called-p t)))
482     (save-excursion
483       (goto-char point1)
484       (setq haskell-tmp-files-list
485             (cons (list file
486                         (current-buffer)
487                         (save-excursion ; Calculate line no.
488                           (beginning-of-line)
489                           (1+ (count-lines 1 (point)))))
490                   haskell-tmp-files-list)))
491     (write-region point1 point2 file nil 'dummy)
492     (haskell-shell)
493     (message "Using temporary file: %s" file)
494     (send-string
495      haskell-process-name
496      ;; string to send: load file;
497      (concat "load " haskell-use-left-delim file haskell-use-right-delim ";\n"))))
498
499 (defvar haskell-old-kill-emacs-hook nil
500   "Old value of kill-emacs-hook")
501
502 (defun haskell-remove-tmp-files ()
503   "Remove the temporary files, created by haskell-simulate-send-region, if
504 they still exist. Only files recorded in haskell-tmp-files-list are removed."
505   (message "Removing temporary files created by haskell-mode...")
506   (while haskell-tmp-files-list
507     (condition-case ()
508         (delete-file (car (car haskell-tmp-files-list)))
509       (error ()))
510     (setq haskell-tmp-files-list (cdr haskell-tmp-files-list)))
511   (message "Removing temporary files created by haskell-mode...done.")
512   (run-hooks 'haskell-old-kill-emacs-hook))
513
514 (defun haskell-send-region ()
515   "Send region."
516   (interactive)
517   (let (start end)
518     (save-excursion
519       (setq end (point))
520       (exchange-point-and-mark)
521       (setq start (point)))
522     (haskell-simulate-send-region start end)))
523
524 (defun haskell-send-buffer ()
525   "Send the buffer."
526   (interactive)
527   (haskell-simulate-send-region (point-min) (point-max)))
528
529 (defun haskell-evaluate-expression (h-expr)
530   "Prompt for and evaluate an expression"
531   (interactive "sExpression: ")
532   (let ((str (concat h-expr ";\n"))
533         (buf (current-buffer)))
534     (haskell-pop-to-shell)
535     (insert str)
536     (send-string haskell-process-name str)
537     (pop-to-buffer buf)))
538
539 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
540 ;;;
541 ;;; END OF Haskell-MODE
542 ;;;
543 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;