Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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