Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 350 lines (312 sloc) 14.583 kB
4418871 New file.
monnier authored
1 ;;; inf-haskell.el --- Interaction with an inferior Haskell process.
2
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
3 ;; Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
4418871 New file.
monnier authored
4
5 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6 ;; Keywords: Haskell
7
8 ;; This file is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option)
11 ;; any later version.
12
13 ;; This file is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING. If not, write to
20 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 ;; Boston, MA 02111-1307, USA.
22
23 ;;; Commentary:
24
25 ;; The code is made of 2 parts: a major mode for the buffer that holds the
225a7c5 (inferior-haskell-load-file): Quote file name.
monnier authored
26 ;; inferior process's session and a minor mode for use in source buffers.
4418871 New file.
monnier authored
27
28 ;;; Code:
29
30 (require 'comint)
31 (require 'shell) ;For directory tracking.
32 (require 'compile)
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
33 (require 'haskell-mode)
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
34 (eval-when-compile (require 'cl))
4418871 New file.
monnier authored
35
36 ;; Here I depart from the inferior-haskell- prefix.
37 ;; Not sure if it's a good idea.
27cc26c (haskell-program-name): Use ghci if hugs is absent.
monnier authored
38 (defcustom haskell-program-name
39 ;; Arbitrarily give preference to hugs over ghci.
40 (or (cond
41 ((not (fboundp 'executable-find)) nil)
42 ((executable-find "hugs") "hugs \"+.\"")
43 ((executable-find "ghci") "ghci"))
44 "hugs \"+.\"")
4418871 New file.
monnier authored
45 "The name of the command to start the inferior Haskell process.
46 The command can include arguments."
8b661ca (haskell-program-name): Fix defcustom delcaration.
monnier authored
47 ;; Custom only supports the :options keyword for a few types, e.g. not
48 ;; for string.
49 ;; :options '("hugs \"+.\"" "ghci")
4418871 New file.
monnier authored
50 :group 'haskell
51 :type '(choice string (repeat string)))
52
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
53 (defconst inferior-haskell-info-xref-re
54 "\t-- Defined at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)$")
55
4418871 New file.
monnier authored
56 (defconst inferior-haskell-error-regexp-alist
57 ;; The format of error messages used by Hugs.
e19bf49 (inferior-haskell-error-regexp-alist): Fix GHCi regexp, support warni…
monnier authored
58 `(("^ERROR \"\\(.+?\\)\"\\(:\\| line \\)\\([0-9]+\\) - " 1 3)
4418871 New file.
monnier authored
59 ;; Format of error messages used by GHCi.
e19bf49 (inferior-haskell-error-regexp-alist): Fix GHCi regexp, support warni…
monnier authored
60 ("^\\(.+?\\):\\([0-9]+\\):\\(\\([0-9]+\\):\\)?\\( \\|\n +\\)\\(Warning\\)?"
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
61 1 2 4 ,@(if (fboundp 'compilation-fake-loc) '((6))))
62 ;; Info xrefs.
63 ,@(if (fboundp 'compilation-fake-loc)
64 `((,inferior-haskell-info-xref-re
65 1 2 3 0))))
4418871 New file.
monnier authored
66 "Regexps for error messages generated by inferior Haskell processes.
67 The format should be the same as for `compilation-error-regexp-alist'.")
68
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
69 (defcustom inferior-haskell-use-cabal t
70 "If non-nil, try and find a Cabal file to get the project root directory."
71 :type 'boolean)
72
4418871 New file.
monnier authored
73 (define-derived-mode inferior-haskell-mode comint-mode "Inf-Haskell"
74 "Major mode for interacting with an inferior Haskell process."
75 (set (make-local-variable 'comint-prompt-regexp)
76 "^\\*?[A-Z][\\._a-zA-Z0-9]*\\( \\*?[A-Z][\\._a-zA-Z0-9]*\\)*> ")
77 (set (make-local-variable 'comint-input-autoexpand) nil)
78
79 ;; Setup directory tracking.
80 (set (make-local-variable 'shell-cd-regexp) ":cd")
bbbe054 (inferior-haskell-mode): Use shell-dirtrack-mode if possible.
monnier authored
81 (condition-case nil
82 (shell-dirtrack-mode 1)
83 (error ;The minor mode function may not exist or not accept an arg.
84 (set (make-local-variable 'shell-dirtrackp) t)
85 (add-hook 'comint-input-filter-functions 'shell-directory-tracker
86 nil 'local)))
4418871 New file.
monnier authored
87
88 ;; Setup `compile' support so you can just use C-x ` and friends.
89 (set (make-local-variable 'compilation-error-regexp-alist)
90 inferior-haskell-error-regexp-alist)
bfd15e1 (inferior-haskell-mode): Hide compilation-mode bindings.
monnier authored
91 (if (and (not (boundp 'minor-mode-overriding-map-alist))
92 (fboundp 'compilation-shell-minor-mode))
93 ;; If we can't remove compilation-minor-mode bindings, at least try to
94 ;; use compilation-shell-minor-mode, so there are fewer
95 ;; annoying bindings.
96 (compilation-shell-minor-mode 1)
97 ;; Else just use compilation-minor-mode but without its bindings because
98 ;; things like mouse-2 are simply too annoying.
99 (compilation-minor-mode 1)
100 (let ((map (make-sparse-keymap)))
101 (dolist (keys '([menu-bar] [follow-link]))
102 ;; Preserve some of the bindings.
103 (define-key map keys (lookup-key compilation-minor-mode-map keys)))
104 (add-to-list 'minor-mode-overriding-map-alist
105 (cons 'compilation-minor-mode map)))))
4418871 New file.
monnier authored
106
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
107 (defun inferior-haskell-string-to-strings (string)
108 "Split the STRING into a list of strings."
109 (let ((i (string-match "[\"]" string)))
110 (if (null i) (split-string string) ; no quoting: easy
111 (append (unless (eq i 0) (split-string (substring string 0 i)))
4418871 New file.
monnier authored
112 (let ((rfs (read-from-string string i)))
113 (cons (car rfs)
114 (inferior-haskell-string-to-strings
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
115 (substring string (cdr rfs)))))))))
4418871 New file.
monnier authored
116
117 (defun inferior-haskell-command (arg)
118 (inferior-haskell-string-to-strings
119 (if (null arg) haskell-program-name
6dd0f3a (inferior-haskell-command): Provide a default.
monnier authored
120 (read-string "Command to run haskell: " haskell-program-name))))
4418871 New file.
monnier authored
121
122 (defvar inferior-haskell-buffer nil
123 "The buffer in which the inferior process is running.")
124
125 (defun inferior-haskell-start-process (command)
126 "Start an inferior haskell process.
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
127 With universal prefix \\[universal-argument], prompts for a COMMAND,
4418871 New file.
monnier authored
128 otherwise uses `haskell-program-name'.
129 It runs the hook `inferior-haskell-hook' after starting the process and
130 setting up the inferior-haskell buffer."
131 (interactive (list (inferior-haskell-command current-prefix-arg)))
132 (setq inferior-haskell-buffer
133 (apply 'make-comint "haskell" (car command) nil (cdr command)))
134 (with-current-buffer inferior-haskell-buffer
135 (inferior-haskell-mode)
136 (run-hooks 'inferior-haskell-hook)))
137
138 (defun inferior-haskell-process (&optional arg)
139 (or (if (buffer-live-p inferior-haskell-buffer)
140 (get-buffer-process inferior-haskell-buffer))
141 (progn
142 (let ((current-prefix-arg arg))
143 (call-interactively 'inferior-haskell-start-process))
144 ;; Try again.
145 (inferior-haskell-process arg))))
146
147 ;;;###autoload
148 (defalias 'run-haskell 'switch-to-haskell)
149 ;;;###autoload
150 (defun switch-to-haskell (&optional arg)
151 "Show the inferior-haskell buffer. Start the process if needed."
152 (interactive "P")
153 (let ((proc (inferior-haskell-process arg)))
154 (pop-to-buffer (process-buffer proc))))
155
ce51306 (with-selected-window): Define while compiling.
monnier authored
156 (eval-when-compile
157 (unless (fboundp 'with-selected-window)
158 (defmacro with-selected-window (win &rest body)
159 `(save-selected-window
160 (select-window ,win)
161 ,@body))))
6dd0f3a (inferior-haskell-command): Provide a default.
monnier authored
162
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
163 (defcustom inferior-haskell-wait-and-jump nil
164 "If non-nil, wait for file loading to terminate and jump to the error."
165 :type 'boolean
166 :group 'haskell)
167
168 (defun inferior-haskell-wait-for-prompt (proc)
169 "Wait until PROC sends us a prompt.
170 The process PROC should be associated to a comint buffer."
171 (with-current-buffer (process-buffer proc)
172 (while (progn
173 (goto-char comint-last-input-end)
174 (and (not (re-search-forward comint-prompt-regexp nil t))
175 (accept-process-output proc))))))
176
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
177 (defvar inferior-haskell-cabal-buffer nil)
178
179 (defun inferior-haskell-cabal-of-buf (buf)
180 (require 'haskell-cabal)
181 (with-current-buffer buf
182 (or inferior-haskell-cabal-buffer
183 (and (not (local-variable-p 'inferior-haskell-cabal-buffer))
184 (set (make-local-variable 'inferior-haskell-cabal-buffer)
185 (haskell-cabal-find-file))))))
186
4418871 New file.
monnier authored
187 ;;;###autoload
188 (defun inferior-haskell-load-file (&optional reload)
189 "Pass the current buffer's file to the inferior haskell process."
190 (interactive)
2eaced4 (inferior-haskell-load-file): Save buffer before using buffer-file-name.
monnier authored
191 ;; Save first, so we're sure that `buffer-file-name' is non-nil afterward.
192 (save-buffer)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
193 (let ((buf (current-buffer))
194 (file buffer-file-name)
4418871 New file.
monnier authored
195 (proc (inferior-haskell-process)))
196 (with-current-buffer (process-buffer proc)
197 (compilation-forget-errors)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
198 (let ((parsing-end (marker-position (process-mark proc)))
199 cabal)
200 ;; Go to the root of the Cabal project, if applicable.
201 (when (and inferior-haskell-use-cabal
202 (setq cabal (inferior-haskell-cabal-of-buf buf)))
203 ;; Not sure if it's useful/needed and if it actually works.
204 (unless (equal default-directory
205 (with-current-buffer cabal default-directory))
206 (setq default-directory
207 (with-current-buffer cabal default-directory))
208 (inferior-haskell-send-command
209 proc (concat ":cd " default-directory)))
210 (setq file (file-relative-name file)))
d8a5cc8 (inferior-haskell-load-file): Fix the compilation-parsing-end fiddlin…
monnier authored
211 (inferior-haskell-send-command
212 proc (if reload ":reload" (concat ":load \"" file "\"")))
213 ;; Move the parsing-end marker after sending the command so
214 ;; that it doesn't point just to the insertion point.
215 ;; Otherwise insertion may move the marker (if done with
216 ;; insert-before-markers) and we'd then miss some errors.
217 (if (boundp 'compilation-parsing-end)
218 (if (markerp compilation-parsing-end)
219 (set-marker compilation-parsing-end parsing-end)
220 (setq compilation-parsing-end parsing-end))))
1cda384 (inferior-haskell-load-file): Simplify and make more
monnier authored
221 (with-selected-window (display-buffer (current-buffer))
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
222 (goto-char (point-max)))
223 (when inferior-haskell-wait-and-jump
224 (inferior-haskell-wait-for-prompt proc)
225 (ignore-errors ;Don't beep if there were no errors.
226 (next-error))))))
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
227
228 (defun inferior-haskell-send-command (proc str)
229 (setq str (concat str "\n"))
230 (with-current-buffer (process-buffer proc)
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
231 (inferior-haskell-wait-for-prompt proc)
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
232 (goto-char (process-mark proc))
233 (insert-before-markers str)
234 (move-marker comint-last-input-end (point))
235 (comint-send-string proc str)))
4418871 New file.
monnier authored
236
237 (defun inferior-haskell-reload-file ()
238 "Tell the inferior haskell process to reread the current buffer's file."
239 (interactive)
240 (inferior-haskell-load-file 'reload))
241
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
242 (defun inferior-haskell-type (expr &optional insert-value)
243 "Query the haskell process for the type of the given expression.
244 If optional argument `insert-value' is non-nil, insert the type above point
245 in the buffer. This can be done interactively with the \\[universal-argument] prefix.
246 The returned info is cached for reuse by `haskell-doc-mode'."
247 (interactive
248 (let ((sym (haskell-ident-at-point)))
249 (list (read-string (if (> (length sym) 0)
250 (format "Show type of (default %s): " sym)
251 "Show type of: ")
252 nil nil sym)
253 current-prefix-arg)))
254 (if (string-match "\\`\\s_+\\'" expr) (setq expr (concat "(" expr ")")))
255 (let* ((proc (inferior-haskell-process))
256 (type
257 (with-current-buffer (process-buffer proc)
258 (let ((parsing-end ; Remember previous spot.
259 (marker-position (process-mark proc))))
260 (inferior-haskell-send-command proc (concat ":type " expr))
261 ;; Find new point.
262 (goto-char (point-max))
263 (inferior-haskell-wait-for-prompt proc)
264 ;; Back up to the previous end-of-line.
265 (end-of-line 0)
266 ;; Extract the type output
267 (buffer-substring-no-properties
268 (save-excursion (goto-char parsing-end)
269 (line-beginning-position 2))
270 (point))))))
271 (if (not (string-match (concat "\\`" (regexp-quote expr) "[ \t]+::[ \t]*")
272 type))
273 (error "No type info: %s" type)
274
275 ;; Cache for reuse by haskell-doc.
276 (when (and (boundp 'haskell-doc-mode) haskell-doc-mode
277 (boundp 'haskell-doc-user-defined-ids)
278 ;; Haskell-doc only works for idents, not arbitrary expr.
279 (string-match "\\`(?\\(\\s_+\\|\\(\\sw\\|\\s'\\)+\\)?[ \t]*::[ \t]*"
280 type))
281 (let ((sym (match-string 1 type)))
282 (setq haskell-doc-user-defined-ids
283 (cons (cons sym (substring type (match-end 0)))
284 (remove-if (lambda (item) (equal (car item) sym))
285 haskell-doc-user-defined-ids)))))
286
287 (if (interactive-p) (message type))
288 (when insert-value
289 (beginning-of-line)
290 (insert type "\n"))
291 type)))
292
293 (defun inferior-haskell-info (sym)
294 "Query the haskell process for the info of the given expression."
295 (interactive
296 (let ((sym (haskell-ident-at-point)))
297 (list (read-string (if (> (length sym) 0)
298 (format "Show info of (default %s): " sym)
299 "Show info of: ")
300 nil nil sym))))
301 (let ((proc (inferior-haskell-process)))
302 (with-current-buffer (process-buffer proc)
303 (let ((parsing-end ; Remember previous spot.
304 (marker-position (process-mark proc))))
305 (inferior-haskell-send-command proc (concat ":info " sym))
306 ;; Find new point.
307 (goto-char (point-max))
308 (inferior-haskell-wait-for-prompt proc)
309 ;; Move to previous end-of-line
310 (end-of-line 0)
311 (let ((result
312 (buffer-substring-no-properties
313 (save-excursion (goto-char parsing-end)
314 (line-beginning-position 2))
315 (point))))
316 ;; Move back to end of process buffer
317 (goto-char (point-max))
318 (if (interactive-p) (message "%s" result))
319 result)))))
320
321 (defun inferior-haskell-find-definition (sym)
322 "Attempt to locate and jump to the definition of the given expression."
323 (interactive
324 (let ((sym (haskell-ident-at-point)))
325 (list (read-string (if (> (length sym) 0)
326 (format "Find definition of (default %s): " sym)
327 "Find definition of: ")
328 nil nil sym))))
329 (let ((info (inferior-haskell-info sym)))
330 (if (not (string-match inferior-haskell-info-xref-re info))
331 (error "No source information available")
332 (let ((file (match-string-no-properties 1 info))
333 (line (string-to-number
334 (match-string-no-properties 2 info)))
335 (col (string-to-number
336 (match-string-no-properties 3 info))))
337 (when file
338 ;; Push current location marker on the ring used by `find-tag'
339 (require 'etags)
340 (ring-insert find-tag-marker-ring (point-marker))
341 (pop-to-buffer (find-file-noselect file))
342 (when line
343 (goto-line line)
344 (when col (move-to-column col))))))))
345
4418871 New file.
monnier authored
346 (provide 'inf-haskell)
bfd15e1 (inferior-haskell-mode): Hide compilation-mode bindings.
monnier authored
347
348 ;; arch-tag: 61804287-63dd-4052-bc0e-90f691b34b40
4418871 New file.
monnier authored
349 ;;; inf-haskell.el ends here
Something went wrong with that request. Please try again.