Skip to content

HTTPS clone URL

Subversion checkout URL

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