Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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 warnings...
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 warnings...
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. Call...
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. Call...
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. Call...
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 fiddling s...
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.