Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 719 lines (646 sloc) 31.379 kb
4418871 New file.
monnier authored
1 ;;; inf-haskell.el --- Interaction with an inferior Haskell process.
2
d549980 (inferior-haskell-type, inferior-haskell-info):
monnier authored
3 ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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
30d658f (displayed-month): Remove declaration since it's not used here.
monnier authored
10 ;; the Free Software Foundation; either version 3, or (at your option)
4418871 New file.
monnier authored
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
3e6c2e0 *** empty log message ***
monnier authored
28 ;; Todo:
29
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
30 ;; - Check out Shim for ideas.
9ed46b1 (inferior-haskell-load-file): Re-add the `reload' arg.
monnier authored
31 ;; - i-h-load-buffer and i-h-send-region.
3e6c2e0 *** empty log message ***
monnier authored
32
4418871 New file.
monnier authored
33 ;;; Code:
34
35 (require 'comint)
36 (require 'shell) ;For directory tracking.
37 (require 'compile)
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
38 (require 'haskell-mode)
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
39 (eval-when-compile (require 'cl))
4418871 New file.
monnier authored
40
4456be9 (subst-char-in-string, make-temp-file): Add fallback definitions for …
monnier authored
41 ;; XEmacs compatibility.
42
43 (unless (fboundp 'subst-char-in-string)
44 (defun subst-char-in-string (fromchar tochar string &optional inplace)
45 ;; This is Haskell-mode, we don't want no stinkin' `aset'.
46 (apply 'string (mapcar (lambda (c) (if (eq c fromchar) tochar c)) string))))
47
48 (unless (fboundp 'make-temp-file)
49 (defun make-temp-file (prefix &optional dir-flag)
50 (catch 'done
51 (while t
52 (let ((f (make-temp-name (expand-file-name prefix (temp-directory)))))
53 (condition-case ()
54 (progn
55 (if dir-flag (make-directory f)
56 (write-region "" nil f nil 'silent nil))
57 (throw 'done f))
58 (file-already-exists t)))))))
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
59
94ad4fc (inferior-haskell-cabal-of-buf): Don't return
monnier authored
60 (unless (fboundp 'replace-regexp-in-string)
61 (defun replace-regexp-in-string (regexp rep string)
62 (replace-in-string string regexp rep)))
4456be9 (subst-char-in-string, make-temp-file): Add fallback definitions for …
monnier authored
63
4418871 New file.
monnier authored
64 ;; Here I depart from the inferior-haskell- prefix.
65 ;; Not sure if it's a good idea.
27cc26c (haskell-program-name): Use ghci if hugs is absent.
monnier authored
66 (defcustom haskell-program-name
67 ;; Arbitrarily give preference to hugs over ghci.
68 (or (cond
69 ((not (fboundp 'executable-find)) nil)
70 ((executable-find "hugs") "hugs \"+.\"")
71 ((executable-find "ghci") "ghci"))
72 "hugs \"+.\"")
4418871 New file.
monnier authored
73 "The name of the command to start the inferior Haskell process.
74 The command can include arguments."
8b661ca (haskell-program-name): Fix defcustom delcaration.
monnier authored
75 ;; Custom only supports the :options keyword for a few types, e.g. not
76 ;; for string.
77 ;; :options '("hugs \"+.\"" "ghci")
4418871 New file.
monnier authored
78 :group 'haskell
79 :type '(choice string (repeat string)))
80
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
81 (defconst inferior-haskell-info-xref-re
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
82 "\t-- Defined at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?$")
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
83
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
84 (defconst inferior-haskell-module-re
85 "\t-- Defined in \\(.+\\)$"
86 "Regular expression for matching module names in :info.")
87
4418871 New file.
monnier authored
88 (defconst inferior-haskell-error-regexp-alist
89 ;; The format of error messages used by Hugs.
e19bf49 (inferior-haskell-error-regexp-alist): Fix GHCi regexp, support warni…
monnier authored
90 `(("^ERROR \"\\(.+?\\)\"\\(:\\| line \\)\\([0-9]+\\) - " 1 3)
4418871 New file.
monnier authored
91 ;; Format of error messages used by GHCi.
a9fe15b (inferior-haskell-error-regexp-alist): Be more careful
monnier authored
92 ("^\\(.+?\\):\\([0-9]+\\):\\(\\([0-9]+\\):\\)?\\( \\|\n *\\)\\(Warning\\)?"
93 1 2 4 ,@(if (fboundp 'compilation-fake-loc)
94 '((6) nil (5 '(face nil font-lock-multiline t)))))
5a72afa (inferior-haskell-error-regexp-alist): Add entries for GHCI's excepti…
monnier authored
95 ;; Runtime exceptions, from ghci.
96 ("^\\*\\*\\* Exception: \\(.+?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\)): .*"
97 1 ,@(if (fboundp 'compilation-fake-loc) '((2 . 4) (3 . 5)) '(2 3)))
a9fe15b (inferior-haskell-error-regexp-alist): Be more careful
monnier authored
98 ;; GHCi uses two different forms for line/col ranges, depending on
ff46f40 (inferior-haskell-error-regexp-alist):
monnier authored
99 ;; whether it's all on the same line or not :-( In Emacs-23, I could use
100 ;; explicitly numbered subgroups to merge the two patterns.
5a72afa (inferior-haskell-error-regexp-alist): Add entries for GHCI's excepti…
monnier authored
101 ("^\\*\\*\\* Exception: \\(.+?\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\): .*"
102 1 2 ,(if (fboundp 'compilation-fake-loc) '(3 . 4) 3))
a9fe15b (inferior-haskell-error-regexp-alist): Be more careful
monnier authored
103 ;; Info messages. Not errors per se.
ff46f40 (inferior-haskell-error-regexp-alist):
monnier authored
104 ,@(when (fboundp 'compilation-fake-loc)
105 `(;; Other GHCi patterns used in type errors.
106 ("^[ \t]+at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)$"
107 1 2 (3 . 4) 0)
108 ;; Foo.hs:318:80:
109 ;; Ambiguous occurrence `Bar'
110 ;; It could refer to either `Bar', defined at Zork.hs:311:5
111 ;; or `Bar', imported from Bars at Frob.hs:32:0-16
112 ;; (defined at Location.hs:97:5)
113 ("[ (]defined at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\))?$" 1 2 3 0)
114 ("imported from .* at \\(.+\\):\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)$"
115 1 2 (3 . 4) 0)
116 ;; Info xrefs.
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
117 (,inferior-haskell-info-xref-re 1 2 (3 . 4) 0))))
4418871 New file.
monnier authored
118 "Regexps for error messages generated by inferior Haskell processes.
119 The format should be the same as for `compilation-error-regexp-alist'.")
120
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
121 (defcustom inferior-haskell-find-project-root t
122 "If non-nil, try and find the project root directory of this file.
123 This will either look for a Cabal file or a \"module\" statement in the file."
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
124 :type 'boolean)
125
4418871 New file.
monnier authored
126 (define-derived-mode inferior-haskell-mode comint-mode "Inf-Haskell"
127 "Major mode for interacting with an inferior Haskell process."
128 (set (make-local-variable 'comint-prompt-regexp)
129 "^\\*?[A-Z][\\._a-zA-Z0-9]*\\( \\*?[A-Z][\\._a-zA-Z0-9]*\\)*> ")
130 (set (make-local-variable 'comint-input-autoexpand) nil)
ce971bb (inferior-haskell-spot-prompt): New function.
monnier authored
131 (add-hook 'comint-output-filter-functions 'inferior-haskell-spot-prompt nil t)
4418871 New file.
monnier authored
132
133 ;; Setup directory tracking.
134 (set (make-local-variable 'shell-cd-regexp) ":cd")
bbbe054 (inferior-haskell-mode): Use shell-dirtrack-mode if possible.
monnier authored
135 (condition-case nil
136 (shell-dirtrack-mode 1)
137 (error ;The minor mode function may not exist or not accept an arg.
138 (set (make-local-variable 'shell-dirtrackp) t)
139 (add-hook 'comint-input-filter-functions 'shell-directory-tracker
140 nil 'local)))
4418871 New file.
monnier authored
141
142 ;; Setup `compile' support so you can just use C-x ` and friends.
143 (set (make-local-variable 'compilation-error-regexp-alist)
144 inferior-haskell-error-regexp-alist)
5a72afa (inferior-haskell-error-regexp-alist): Add entries for GHCI's excepti…
monnier authored
145 (set (make-local-variable 'compilation-first-column) 0) ;GHCI counts from 0.
bfd15e1 (inferior-haskell-mode): Hide compilation-mode bindings.
monnier authored
146 (if (and (not (boundp 'minor-mode-overriding-map-alist))
147 (fboundp 'compilation-shell-minor-mode))
148 ;; If we can't remove compilation-minor-mode bindings, at least try to
149 ;; use compilation-shell-minor-mode, so there are fewer
150 ;; annoying bindings.
151 (compilation-shell-minor-mode 1)
152 ;; Else just use compilation-minor-mode but without its bindings because
153 ;; things like mouse-2 are simply too annoying.
154 (compilation-minor-mode 1)
155 (let ((map (make-sparse-keymap)))
156 (dolist (keys '([menu-bar] [follow-link]))
157 ;; Preserve some of the bindings.
158 (define-key map keys (lookup-key compilation-minor-mode-map keys)))
159 (add-to-list 'minor-mode-overriding-map-alist
160 (cons 'compilation-minor-mode map)))))
4418871 New file.
monnier authored
161
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
162 (defun inferior-haskell-string-to-strings (string)
163 "Split the STRING into a list of strings."
164 (let ((i (string-match "[\"]" string)))
165 (if (null i) (split-string string) ; no quoting: easy
166 (append (unless (eq i 0) (split-string (substring string 0 i)))
4418871 New file.
monnier authored
167 (let ((rfs (read-from-string string i)))
168 (cons (car rfs)
169 (inferior-haskell-string-to-strings
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
170 (substring string (cdr rfs)))))))))
4418871 New file.
monnier authored
171
172 (defun inferior-haskell-command (arg)
173 (inferior-haskell-string-to-strings
174 (if (null arg) haskell-program-name
6dd0f3a (inferior-haskell-command): Provide a default.
monnier authored
175 (read-string "Command to run haskell: " haskell-program-name))))
4418871 New file.
monnier authored
176
177 (defvar inferior-haskell-buffer nil
178 "The buffer in which the inferior process is running.")
179
180 (defun inferior-haskell-start-process (command)
181 "Start an inferior haskell process.
6784a8b (inferior-haskell-string-to-strings): Remove `separator' argument. C…
monnier authored
182 With universal prefix \\[universal-argument], prompts for a COMMAND,
4418871 New file.
monnier authored
183 otherwise uses `haskell-program-name'.
184 It runs the hook `inferior-haskell-hook' after starting the process and
185 setting up the inferior-haskell buffer."
186 (interactive (list (inferior-haskell-command current-prefix-arg)))
187 (setq inferior-haskell-buffer
188 (apply 'make-comint "haskell" (car command) nil (cdr command)))
189 (with-current-buffer inferior-haskell-buffer
190 (inferior-haskell-mode)
191 (run-hooks 'inferior-haskell-hook)))
192
193 (defun inferior-haskell-process (&optional arg)
194 (or (if (buffer-live-p inferior-haskell-buffer)
195 (get-buffer-process inferior-haskell-buffer))
196 (progn
197 (let ((current-prefix-arg arg))
198 (call-interactively 'inferior-haskell-start-process))
199 ;; Try again.
200 (inferior-haskell-process arg))))
201
202 ;;;###autoload
203 (defalias 'run-haskell 'switch-to-haskell)
204 ;;;###autoload
205 (defun switch-to-haskell (&optional arg)
206 "Show the inferior-haskell buffer. Start the process if needed."
207 (interactive "P")
208 (let ((proc (inferior-haskell-process arg)))
209 (pop-to-buffer (process-buffer proc))))
210
ce51306 (with-selected-window): Define while compiling.
monnier authored
211 (eval-when-compile
212 (unless (fboundp 'with-selected-window)
213 (defmacro with-selected-window (win &rest body)
214 `(save-selected-window
215 (select-window ,win)
216 ,@body))))
6dd0f3a (inferior-haskell-command): Provide a default.
monnier authored
217
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
218 (defcustom inferior-haskell-wait-and-jump nil
219 "If non-nil, wait for file loading to terminate and jump to the error."
220 :type 'boolean
221 :group 'haskell)
222
ce971bb (inferior-haskell-spot-prompt): New function.
monnier authored
223 (defvar inferior-haskell-seen-prompt nil)
224 (make-variable-buffer-local 'inferior-haskell-seen-prompt)
225
226 (defun inferior-haskell-spot-prompt (string)
227 (let ((proc (get-buffer-process (current-buffer))))
228 (when proc
229 (save-excursion
230 (goto-char (process-mark proc))
231 (if (re-search-backward comint-prompt-regexp
232 (line-beginning-position) t)
233 (setq inferior-haskell-seen-prompt t))))))
234
c6cbffc (inferior-haskell-wait-for-prompt): Add timeout arg.
monnier authored
235 (defun inferior-haskell-wait-for-prompt (proc &optional timeout)
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
236 "Wait until PROC sends us a prompt.
237 The process PROC should be associated to a comint buffer."
238 (with-current-buffer (process-buffer proc)
ce971bb (inferior-haskell-spot-prompt): New function.
monnier authored
239 (while (progn
240 (goto-char comint-last-input-end)
241 (not (or inferior-haskell-seen-prompt
242 (setq inferior-haskell-seen-prompt
243 (re-search-forward comint-prompt-regexp nil t))
244 (not (accept-process-output proc timeout))))))
245 (unless inferior-haskell-seen-prompt
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
246 (error "Can't find the prompt"))))
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
247
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
248 (defvar inferior-haskell-cabal-buffer nil)
249
250 (defun inferior-haskell-cabal-of-buf (buf)
251 (require 'haskell-cabal)
252 (with-current-buffer buf
94ad4fc (inferior-haskell-cabal-of-buf): Don't return
monnier authored
253 (or (and (buffer-live-p inferior-haskell-cabal-buffer)
254 inferior-haskell-cabal-buffer)
439a89e (inferior-haskell-cabal-of-buf)
monnier authored
255 (and (not (local-variable-p 'inferior-haskell-cabal-buffer
256 ;; XEmacs needs this argument.
837c948 (inferior-haskell-cabal-of-buf): Fix typo.
monnier authored
257 (current-buffer)))
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
258 (set (make-local-variable 'inferior-haskell-cabal-buffer)
259 (haskell-cabal-find-file))))))
260
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
261 (defun inferior-haskell-find-project-root (buf)
262 (with-current-buffer buf
263 (let ((cabal (inferior-haskell-cabal-of-buf buf)))
264 (or (when cabal
265 (with-current-buffer cabal
f11a557 (inferior-haskell-find-project-root): Use it.
monnier authored
266 (let ((hsd (haskell-cabal-get-setting "hs-source-dirs")))
267 (if (null hsd)
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
268 ;; If there's a Cabal file with no Hs-Source-Dirs, then
269 ;; just use the Cabal file's directory.
270 default-directory
271 ;; If there is an HSD, then check that it's an existing
272 ;; dir (otherwise, it may be a list of dirs and we don't
273 ;; know what to do with those). If it doesn't exist, then
274 ;; give up.
3afce2f (inferior-haskell-find-project-root): Minor simplification.
monnier authored
275 (if (file-directory-p hsd) (expand-file-name hsd))))))
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
276 ;; If there's no Cabal file or it's not helpful, try to look for
277 ;; a "module" statement and count the number of "." in the
278 ;; module name.
279 (save-excursion
280 (goto-char (point-min))
281 (let ((case-fold-search nil))
282 (when (re-search-forward
283 "^module[ \t]+\\([^- \t\n]+\\.[^- \t\n]+\\)[ \t]+where\\>" nil t)
284 (let* ((dir default-directory)
285 (module (match-string 1))
286 (pos 0))
287 (while (string-match "\\." module pos)
288 (setq pos (match-end 0))
289 (setq dir (expand-file-name ".." dir)))
290 ;; Let's check that the module name matches the file name,
291 ;; otherwise the project root is probably not what we think.
292 (if (eq t (compare-strings
293 (file-name-sans-extension buffer-file-name)
294 nil nil
295 (expand-file-name
296 (replace-regexp-in-string "\\." "/" module)
297 dir)
298 nil nil t))
299 dir
300 ;; If they're not equal, it means the local directory
301 ;; hierarchy doesn't match the module name. This seems
302 ;; odd, so let's warn the user about it. May help us
303 ;; debug this code as well.
304 (message "Ignoring inconsistent `module' info: %s in %s"
305 module buffer-file-name)
306 nil)))))))))
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
307
308
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
309
4418871 New file.
monnier authored
310 ;;;###autoload
9ed46b1 (inferior-haskell-load-file): Re-add the `reload' arg.
monnier authored
311 (defun inferior-haskell-load-file (&optional reload)
0294d90 (inferior-haskell-load-file): Do reload if prefix arg.
monnier authored
312 "Pass the current buffer's file to the inferior haskell process.
313 If prefix arg \\[universal-argument] is given, just reload the previous file."
8af5157 (inferior-haskell-load-file): Typo.
monnier authored
314 (interactive "P")
2eaced4 (inferior-haskell-load-file): Save buffer before using buffer-file-name.
monnier authored
315 ;; Save first, so we're sure that `buffer-file-name' is non-nil afterward.
316 (save-buffer)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
317 (let ((buf (current-buffer))
318 (file buffer-file-name)
4418871 New file.
monnier authored
319 (proc (inferior-haskell-process)))
320 (with-current-buffer (process-buffer proc)
321 (compilation-forget-errors)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
322 (let ((parsing-end (marker-position (process-mark proc)))
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
323 root)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
324 ;; Go to the root of the Cabal project, if applicable.
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
325 (when (and inferior-haskell-find-project-root
326 (setq root (inferior-haskell-find-project-root buf)))
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
327 ;; Not sure if it's useful/needed and if it actually works.
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
328 (unless (equal default-directory root)
329 (setq default-directory root)
c7c6278 (inferior-haskell-use-cabal): New custom var.
monnier authored
330 (inferior-haskell-send-command
331 proc (concat ":cd " default-directory)))
332 (setq file (file-relative-name file)))
9ed46b1 (inferior-haskell-load-file): Re-add the `reload' arg.
monnier authored
333 (inferior-haskell-send-command
bd32a3c (inferior-haskell-load-file): Escape backslashes (and quotes) in file…
monnier authored
334 proc (if reload ":reload"
335 (concat ":load \""
336 ;; Espace the backslashes that may occur in file names.
337 (replace-regexp-in-string "[\\\"]" "\\\\\&" file)
338 "\"")))
30d658f (displayed-month): Remove declaration since it's not used here.
monnier authored
339 ;; Move the parsing-end marker *after* sending the command so
d8a5cc8 (inferior-haskell-load-file): Fix the compilation-parsing-end fiddlin…
monnier authored
340 ;; that it doesn't point just to the insertion point.
341 ;; Otherwise insertion may move the marker (if done with
342 ;; insert-before-markers) and we'd then miss some errors.
343 (if (boundp 'compilation-parsing-end)
344 (if (markerp compilation-parsing-end)
345 (set-marker compilation-parsing-end parsing-end)
346 (setq compilation-parsing-end parsing-end))))
1cda384 (inferior-haskell-load-file): Simplify and make more
monnier authored
347 (with-selected-window (display-buffer (current-buffer))
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
348 (goto-char (point-max)))
30d658f (displayed-month): Remove declaration since it's not used here.
monnier authored
349 ;; Use compilation-auto-jump-to-first-error if available.
350 ;; (if (and (boundp 'compilation-auto-jump-to-first-error)
351 ;; compilation-auto-jump-to-first-error
352 ;; (boundp 'compilation-auto-jump-to-next))
353 ;; (setq compilation-auto-jump-to-next t)
354 (when inferior-haskell-wait-and-jump
355 (inferior-haskell-wait-for-prompt proc)
356 (ignore-errors ;Don't beep if there were no errors.
357 (next-error)))))) ;; )
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
358
3afdf6b (inferior-haskell-run-command): New var.
monnier authored
359 (defvar inferior-haskell-run-command ":main")
360
361 (defun inferior-haskell-load-and-run (command)
9ed46b1 (inferior-haskell-load-file): Re-add the `reload' arg.
monnier authored
362 "Pass the current buffer's file to haskell and then run a COMMAND."
3afdf6b (inferior-haskell-run-command): New var.
monnier authored
363 (interactive
364 (list
365 (if (and inferior-haskell-run-command (not current-prefix-arg))
366 inferior-haskell-run-command
367 (read-string "Command to run: " nil nil inferior-haskell-run-command))))
368 (setq inferior-haskell-run-command command)
40fa207 * inf-haskell.el (inferior-haskell-load-and-run): Don't run if there
monnier authored
369 (let* ((inferior-haskell-errors nil)
370 (neh (lambda () (setq inferior-haskell-errors t))))
371 (unwind-protect
372 (let ((inferior-haskell-wait-and-jump t))
373 (add-hook 'next-error-hook neh)
374 (inferior-haskell-load-file))
375 (remove-hook 'next-error-hook neh))
376 (unless inferior-haskell-errors
377 (inferior-haskell-send-command (inferior-haskell-process) command)
378 (switch-to-haskell))))
3afdf6b (inferior-haskell-run-command): New var.
monnier authored
379
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
380 (defun inferior-haskell-send-command (proc str)
381 (setq str (concat str "\n"))
382 (with-current-buffer (process-buffer proc)
7410480 (inferior-haskell-wait-for-prompt): New fun, extracted
monnier authored
383 (inferior-haskell-wait-for-prompt proc)
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
384 (goto-char (process-mark proc))
385 (insert-before-markers str)
386 (move-marker comint-last-input-end (point))
ce971bb (inferior-haskell-spot-prompt): New function.
monnier authored
387 (setq inferior-haskell-seen-prompt nil)
8512ba1 (inferior-haskell-mode): Typo.
monnier authored
388 (comint-send-string proc str)))
4418871 New file.
monnier authored
389
9ed46b1 (inferior-haskell-load-file): Re-add the `reload' arg.
monnier authored
390 (defun inferior-haskell-reload-file ()
391 "Tell the inferior haskell process to reread the current buffer's file."
392 (interactive)
393 (inferior-haskell-load-file 'reload))
394
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
395 ;;;###autoload
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
396 (defun inferior-haskell-type (expr &optional insert-value)
397 "Query the haskell process for the type of the given expression.
398 If optional argument `insert-value' is non-nil, insert the type above point
399 in the buffer. This can be done interactively with the \\[universal-argument] prefix.
400 The returned info is cached for reuse by `haskell-doc-mode'."
401 (interactive
402 (let ((sym (haskell-ident-at-point)))
403 (list (read-string (if (> (length sym) 0)
404 (format "Show type of (default %s): " sym)
405 "Show type of: ")
406 nil nil sym)
407 current-prefix-arg)))
408 (if (string-match "\\`\\s_+\\'" expr) (setq expr (concat "(" expr ")")))
409 (let* ((proc (inferior-haskell-process))
410 (type
411 (with-current-buffer (process-buffer proc)
412 (let ((parsing-end ; Remember previous spot.
413 (marker-position (process-mark proc))))
414 (inferior-haskell-send-command proc (concat ":type " expr))
415 ;; Find new point.
416 (inferior-haskell-wait-for-prompt proc)
d549980 (inferior-haskell-type, inferior-haskell-info):
monnier authored
417 (goto-char (point-max))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
418 ;; Back up to the previous end-of-line.
419 (end-of-line 0)
420 ;; Extract the type output
421 (buffer-substring-no-properties
422 (save-excursion (goto-char parsing-end)
423 (line-beginning-position 2))
424 (point))))))
f846b94 @Baughn Patch courtesy of Alex Ott
Baughn authored
425 (if (not (string-match (concat "^\\(" (regexp-quote expr) "[ \t\n]+::[ \t\n]*\\(.\\|\n\\)*\\)")
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
426 type))
427 (error "No type info: %s" type)
f846b94 @Baughn Patch courtesy of Alex Ott
Baughn authored
428 (progn
429 (setf type (match-string 1 type))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
430 ;; Cache for reuse by haskell-doc.
431 (when (and (boundp 'haskell-doc-mode) haskell-doc-mode
432 (boundp 'haskell-doc-user-defined-ids)
433 ;; Haskell-doc only works for idents, not arbitrary expr.
434 (string-match "\\`(?\\(\\s_+\\|\\(\\sw\\|\\s'\\)+\\)?[ \t]*::[ \t]*"
435 type))
436 (let ((sym (match-string 1 type)))
437 (setq haskell-doc-user-defined-ids
438 (cons (cons sym (substring type (match-end 0)))
ce971bb (inferior-haskell-spot-prompt): New function.
monnier authored
439 (delq (assoc sym haskell-doc-user-defined-ids)
440 haskell-doc-user-defined-ids)))))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
441
011c502 (inferior-haskell-type): Fix call to message.
monnier authored
442 (if (interactive-p) (message "%s" type))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
443 (when insert-value
444 (beginning-of-line)
445 (insert type "\n"))
f846b94 @Baughn Patch courtesy of Alex Ott
Baughn authored
446 type))))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
447
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
448 ;;;###autoload
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
449 (defun inferior-haskell-info (sym)
450 "Query the haskell process for the info of the given expression."
451 (interactive
452 (let ((sym (haskell-ident-at-point)))
453 (list (read-string (if (> (length sym) 0)
454 (format "Show info of (default %s): " sym)
455 "Show info of: ")
456 nil nil sym))))
457 (let ((proc (inferior-haskell-process)))
458 (with-current-buffer (process-buffer proc)
459 (let ((parsing-end ; Remember previous spot.
460 (marker-position (process-mark proc))))
461 (inferior-haskell-send-command proc (concat ":info " sym))
462 ;; Find new point.
463 (inferior-haskell-wait-for-prompt proc)
d549980 (inferior-haskell-type, inferior-haskell-info):
monnier authored
464 (goto-char (point-max))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
465 ;; Move to previous end-of-line
466 (end-of-line 0)
467 (let ((result
468 (buffer-substring-no-properties
469 (save-excursion (goto-char parsing-end)
470 (line-beginning-position 2))
471 (point))))
472 ;; Move back to end of process buffer
473 (goto-char (point-max))
474 (if (interactive-p) (message "%s" result))
475 result)))))
476
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
477 ;;;###autoload
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
478 (defun inferior-haskell-find-definition (sym)
479 "Attempt to locate and jump to the definition of the given expression."
480 (interactive
481 (let ((sym (haskell-ident-at-point)))
482 (list (read-string (if (> (length sym) 0)
483 (format "Find definition of (default %s): " sym)
484 "Find definition of: ")
485 nil nil sym))))
486 (let ((info (inferior-haskell-info sym)))
487 (if (not (string-match inferior-haskell-info-xref-re info))
488 (error "No source information available")
489 (let ((file (match-string-no-properties 1 info))
490 (line (string-to-number
491 (match-string-no-properties 2 info)))
492 (col (string-to-number
493 (match-string-no-properties 3 info))))
494 (when file
c6cbffc (inferior-haskell-wait-for-prompt): Add timeout arg.
monnier authored
495 (with-current-buffer (process-buffer (inferior-haskell-process))
496 ;; The file name is relative to the process's cwd.
497 (setq file (expand-file-name file)))
36dd8bd (inferior-haskell-info-xref-re): New cst.
monnier authored
498 ;; Push current location marker on the ring used by `find-tag'
499 (require 'etags)
500 (ring-insert find-tag-marker-ring (point-marker))
501 (pop-to-buffer (find-file-noselect file))
502 (when line
503 (goto-line line)
504 (when col (move-to-column col))))))))
505
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
506 ;;; Functions to find the documentation of a given function.
507 ;;
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
508 ;; TODO for this section:
509 ;;
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
510 ;; * Support fetching of local Haddock docs pulled directly from source files.
511 ;; * Display docs locally? w3m?
512
513 (defcustom inferior-haskell-use-web-docs
514 'fallback
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
515 "Whether to use the online documentation. Possible values:
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
516 `never', meaning always use local documentation, unless the local
517 file doesn't exist, when do nothing, `fallback', which means only
518 use the online documentation when the local file doesn't exist,
519 or `always', meaning always use the online documentation,
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
520 regardless of existance of local files. Default is `fallback'."
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
521 :group 'haskell
522 :type '(choice (const :tag "Never" never)
523 (const :tag "As fallback" fallback)
524 (const :tag "Always" always)))
525
526 (defcustom inferior-haskell-web-docs-base
527 "http://haskell.org/ghc/docs/latest/html/libraries/"
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
528 "The base URL of the online libraries documentation.
529 This will only be used if the value of `inferior-haskell-use-web-docs'
530 is `always' or `fallback'."
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
531 :group 'haskell
532 :type 'string)
533
534 (defcustom haskell-package-manager-name "ghc-pkg"
535 "Name of the program to consult regarding package details."
536 :group 'haskell
537 :type 'string)
538
539 (defcustom haskell-package-conf-file
744f5ce (haskell-package-conf-file): Don't use `ignore-errors'
monnier authored
540 (condition-case nil
541 (with-temp-buffer
542 (call-process "ghc" nil t nil "--print-libdir")
543 (expand-file-name "package.conf"
544 (buffer-substring (point-min) (1- (point-max)))))
545 ;; Don't use `ignore-errors' because this form is not byte-compiled :-(
546 (error nil))
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
547 "Where the package configuration file for the package manager resides.
548 By default this is set to `ghc --print-libdir`/package.conf."
549 :group 'haskell
550 :type 'string)
551
552 (defun inferior-haskell-get-module (sym)
553 "Fetch the module in which SYM is defined."
554 (let ((info (inferior-haskell-info sym)))
555 (unless (string-match inferior-haskell-module-re info)
947a32d (inferior-haskell-info-xref-re): Allow a column-range.
monnier authored
556 (error
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
557 "No documentation information available. Did you forget to C-c C-l?"))
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
558 (match-string-no-properties 1 info)))
559
560 (defun inferior-haskell-query-ghc-pkg (&rest args)
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
561 "Send ARGS to `haskell-package-manager-name'.
562 Insert the output into the current buffer."
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
563 (apply 'call-process haskell-package-manager-name nil t nil args))
564
565 (defun inferior-haskell-get-package-list ()
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
566 "Get the list of packages from `haskell-package-manager-name'."
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
567 (with-temp-buffer
568 (inferior-haskell-query-ghc-pkg "--simple-output" "list")
569 (split-string (buffer-substring (point-min) (point-max)))))
570
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
571 (defun inferior-haskell-compute-module-alist ()
572 "Compute a list mapping modules to package names and haddock URLs using ghc-pkg."
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
573 (message "Generating module alist...")
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
574 (let ((module-alist ()))
575 (with-temp-buffer
576 (dolist (package (inferior-haskell-get-package-list))
577 (erase-buffer)
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
578 (inferior-haskell-query-ghc-pkg "describe" package)
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
579
580 (let ((package-w/o-version
581 (replace-regexp-in-string "[-.0-9]*\\'" "" package))
582 ;; Find the Haddock documentation URL for this package
583 (haddock
584 (progn
585 (goto-char (point-min))
bf80267 (inferior-haskell-compute-module-alist): Fix regexps.
monnier authored
586 (when (re-search-forward "haddock-html:[ \t]+\\(.*[^ \t\n]\\)"
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
587 nil t)
588 (match-string 1)))))
589
590 ;; Fetch the list of exposed modules for this package
591 (goto-char (point-min))
bf80267 (inferior-haskell-compute-module-alist): Fix regexps.
monnier authored
592 (when (re-search-forward "^exposed-modules:\\(.*\\(\n[ \t].*\\)*\\)"
593 nil t)
594 (dolist (module (split-string (match-string 1)))
595 (push (list module package-w/o-version haddock)
596 module-alist)))))
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
597
598 (message "Generating module alist... done")
599 module-alist)))
600
601
602 (defcustom inferior-haskell-module-alist-file
603 ;; (expand-file-name "~/.inf-haskell-module-alist")
bf80267 (inferior-haskell-compute-module-alist): Fix regexps.
monnier authored
604 (expand-file-name (concat "inf-haskell-module-alist-"
605 (number-to-string (user-uid)))
439a89e (inferior-haskell-cabal-of-buf)
monnier authored
606 (if (fboundp 'temp-directory)
607 (temp-directory)
608 temporary-file-directory))
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
609 "Where to save the module -> package lookup table.
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
610 Set this to nil to never cache to a file."
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
611 :group 'haskell
612 :type '(choice (const :tag "Don't cache to file" nil) string))
613
614 (defvar inferior-haskell-module-alist nil
615 "Association list of modules to their packages.
616 Each element is of the form (MODULE PACKAGE HADDOCK), where
617 MODULE is the name of a module,
618 PACKAGE is the package it belongs to, and
619 HADDOCK is the path to that package's Haddock documentation.
620
621 This is calculated on-demand using `inferior-haskell-compute-module-alist'.
622 It's also cached in the file `inferior-haskell-module-alist-file',
623 so that it can be obtained more quickly next time.")
624
625 (defun inferior-haskell-module-alist ()
626 "Get the module alist from cache or ghc-pkg's info."
627 (or
628 ;; If we already have computed the alist, use it...
629 inferior-haskell-module-alist
630 (setq inferior-haskell-module-alist
631 (or
632 ;; ...otherwise try to read it from the cache file...
633 (and
634 inferior-haskell-module-alist-file
635 (file-readable-p inferior-haskell-module-alist-file)
636 (file-newer-than-file-p inferior-haskell-module-alist-file
637 haskell-package-conf-file)
638 (with-temp-buffer
639 (insert-file-contents inferior-haskell-module-alist-file)
640 (goto-char (point-min))
641 (prog1 (read (current-buffer))
642 (message "Read module alist from file cache."))))
643
644 ;; ...or generate it again and save it in a file for later.
645 (let ((alist (inferior-haskell-compute-module-alist)))
646 (when inferior-haskell-module-alist-file
6e300b7 (inferior-haskell-find-project-root): New var, to
monnier authored
647 (with-temp-buffer
648 (print alist (current-buffer))
649 ;; Do the write to a temp file first, then rename it.
650 ;; This makes it more atomic, and suffers from fewer security
651 ;; holes related to race conditions if the file is in /tmp.
652 (let ((tmp (make-temp-file inferior-haskell-module-alist-file)))
653 (write-region (point-min) (point-max) tmp)
654 (rename-file tmp inferior-haskell-module-alist-file
655 'ok-if-already-exists))))
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
656 alist)))))
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
657
3afdf6b (inferior-haskell-run-command): New var.
monnier authored
658 (defvar inferior-haskell-ghc-internal-ident-alist
659 ;; FIXME: Fill this table, ideally semi-automatically.
660 '(("GHC.Base.return" . "Control.Monad.return")
661 ("GHC.List" . "Data.List")))
662
663 (defun inferior-haskell-map-internal-ghc-ident (ident)
664 "Try to translate some internal GHC identifier to its alter ego in haskell docs."
665 (let ((head ident)
666 (tail "")
667 remapped)
668 (while (and (not
669 (setq remapped
670 (cdr (assoc head
671 inferior-haskell-ghc-internal-ident-alist))))
672 (string-match "\\.[^.]+\\'" head))
673 (setq tail (concat (match-string 0 head) tail))
674 (setq head (substring head 0 (match-beginning 0))))
675 (concat (or remapped head) tail)))
676
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
677 ;;;###autoload
678 (defun inferior-haskell-find-haddock (sym)
679 "Find and open the Haddock documentation of SYM.
680 Make sure to load the file into GHCi or Hugs first by using C-c C-l.
681 Only works for functions in a package installed with ghc-pkg, or
682 whatever the value of `haskell-package-manager-name' is.
683
684 This function needs to find which package a given module belongs
685 to. In order to do this, it computes a module-to-package lookup
686 alist, which is expensive to compute (it takes upwards of five
687 seconds with more than about thirty installed packages). As a
688 result, we cache it across sessions using the cache file
689 referenced by `inferior-haskell-module-alist-file'. We test to
690 see if this is newer than `haskell-package-conf-file' every time
691 we load it."
692 (interactive
693 (let ((sym (haskell-ident-at-point)))
694 (list (read-string (if (> (length sym) 0)
695 (format "Find documentation of (default %s): " sym)
696 "Find documentation of: ")
697 nil nil sym))))
3afdf6b (inferior-haskell-run-command): New var.
monnier authored
698 (setq sym (inferior-haskell-map-internal-ghc-ident sym))
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
699 (let* (;; Find the module and look it up in the alist
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
700 (module (inferior-haskell-get-module sym))
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
701 (alist-record (assoc module (inferior-haskell-module-alist)))
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
702 (package (nth 1 alist-record))
703 (file-name (concat (subst-char-in-string ?. ?- module) ".html"))
704 (local-path (concat (nth 2 alist-record) "/" file-name))
705 (url (if (or (eq inferior-haskell-use-web-docs 'always)
706 (and (not (file-exists-p local-path))
707 (eq inferior-haskell-use-web-docs 'fallback)))
f2fd5e5 (inferior-haskell-find-haddock): Jump to the symbol anchor within Had…
monnier authored
708 (concat inferior-haskell-web-docs-base package "/" file-name
709 ;; Jump to the symbol anchor within Haddock.
710 "#v:" sym)
b74e553 (inferior-haskell-module-alist-file): Use a file in /tmp rather than ~/.
monnier authored
711 (and (file-exists-p local-path)
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
712 (concat "file://" local-path)))))
2b067d9 @loveshack Comment/doc/message fixes.
loveshack authored
713 (if url (browse-url url) (error "Local file doesn't exist"))))
c5f1425 (inferior-haskell-module-alist-file)
monnier authored
714
4418871 New file.
monnier authored
715 (provide 'inf-haskell)
bfd15e1 (inferior-haskell-mode): Hide compilation-mode bindings.
monnier authored
716
717 ;; arch-tag: 61804287-63dd-4052-bc0e-90f691b34b40
4418871 New file.
monnier authored
718 ;;; inf-haskell.el ends here
Something went wrong with that request. Please try again.