Racket: capturing and displaying standard error during evaluation
[geiser.git] / elisp / geiser-racket.el
blob6e2de6b7cebc0e3839af15276ee95af2beb1b74a
1 ;; geiser-racket.el -- geiser support for Racket scheme
3 ;; Copyright (C) 2009, 2010, 2011 Jose Antonio Ortega Ruiz
5 ;; This program is free software; you can redistribute it and/or
6 ;; modify it under the terms of the Modified BSD License. You should
7 ;; have received a copy of the license along with this program. If
8 ;; not, see <http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5>.
10 ;; Start date: Sat Apr 25, 2009 21:13
14 (require 'geiser-edit)
15 (require 'geiser-doc)
16 (require 'geiser-eval)
17 (require 'geiser-syntax)
18 (require 'geiser-custom)
19 (require 'geiser-base)
22 ;;; Customization:
24 (defgroup geiser-racket nil
25 "Customization for Geiser's Racket flavour."
26 :group 'geiser)
28 (geiser-custom--defcustom geiser-racket-binary
29 (cond ((eq system-type 'windows-nt) "Racket.exe")
30 (t "racket"))
31 "Name to use to call the racket executable when starting a REPL."
32 :type '(choice string (repeat string))
33 :group 'geiser-racket)
35 (geiser-custom--defcustom geiser-racket-gracket-binary
36 (cond ((eq system-type 'windows-nt) "GRacket-text.exe")
37 (t "gracket-text"))
38 "Name to use to call the gracket executable when starting a REPL.
39 This executable is used by `run-gracket', and, if
40 `geiser-racket-use-gracket-p' is set to t, by `run-racket'."
41 :type '(choice string (repeat string))
42 :group 'geiser-racket)
44 (geiser-custom--defcustom geiser-racket-collects nil
45 "A list of paths to be added to racket's collection directories."
46 :type '(repeat file)
47 :group 'geiser-racket)
49 (geiser-custom--defcustom geiser-racket-init-file "~/.racket-geiser"
50 "Initialization file with user code for the racket REPL."
51 :type 'string
52 :group 'geiser-racket)
54 (geiser-custom--defcustom geiser-racket-use-gracket-p nil
55 "Whether to use the gracket binary to start Racket REPLs."
56 :type 'boolean
57 :group 'geiser-racket)
59 (geiser-custom--defcustom geiser-racket-extra-keywords
60 '("define-syntax-rule" "provide" "require"
61 "unless" "when" "with-handlers")
62 "Extra keywords highlighted in Racket buffers."
63 :type '(repeat string)
64 :group 'geiser-racket)
67 ;;; REPL support:
69 (defsubst geiser-racket--real-binary ()
70 (if geiser-racket-use-gracket-p
71 geiser-racket-gracket-binary
72 geiser-racket-binary))
74 (defun geiser-racket--binary ()
75 (let ((binary (geiser-racket--real-binary)))
76 (if (listp binary) (car binary) binary)))
78 (defun geiser-racket--parameters ()
79 "Return a list with all parameters needed to start racket.
80 This function uses `geiser-racket-init-file' if it exists."
81 (let ((init-file (and (stringp geiser-racket-init-file)
82 (expand-file-name geiser-racket-init-file)))
83 (binary (geiser-racket--real-binary))
84 (rackdir (expand-file-name "racket/" geiser-scheme-dir)))
85 `("-i" "-q"
86 "-S" ,rackdir
87 ,@(apply 'append (mapcar (lambda (p) (list "-S" p))
88 geiser-racket-collects))
89 ,@(and (listp binary) (cdr binary))
90 ,@(and init-file (file-readable-p init-file) (list "-f" init-file))
91 "-f" ,(expand-file-name "geiser/startup.rkt" rackdir))))
93 (defconst geiser-racket--prompt-regexp "\\(mzscheme\\|racket\\)@[^ ]*?> ")
96 ;;; Evaluation support:
98 (defun geiser-racket--language ()
99 (save-excursion
100 (goto-char (point-min))
101 (if (re-search-forward
102 "^\\(?:#lang\\|(module +[^ ]+?\\) +\\([^ ]+?\\|([^)]+)\\) *$" nil t)
103 (car (geiser-syntax--read-from-string (match-string-no-properties 1)))
104 "#f")))
106 (defun geiser-racket--enter-command (module)
107 (when (stringp module)
108 (cond ((zerop (length module)) ",enter #f")
109 ((file-name-absolute-p module) (format ",enter %S" module))
110 (t (format ",enter %s" module)))))
112 (defun geiser-racket--geiser-procedure (proc &rest args)
113 (case proc
114 ((eval compile)
115 (format ",geiser-eval %s %s %s"
116 (or (car args) "#f")
117 (geiser-racket--language)
118 (mapconcat 'identity (cdr args) " ")))
119 ((load-file compile-file)
120 (format ",geiser-eval geiser/main racket (geiser:%s %s)"
121 proc (car args)))
122 ((no-values) ",geiser-no-values")
123 (t (format ",apply geiser:%s (%s)" proc (mapconcat 'identity args " ")))))
125 (defconst geiser-racket--module-re
126 "^(module +\\([^ ]+\\)")
128 (defun geiser-racket--explicit-module ()
129 (save-excursion
130 (goto-char (point-min))
131 (and (re-search-forward geiser-racket--module-re nil t)
132 (ignore-errors
133 (car (geiser-syntax--read-from-string
134 (match-string-no-properties 1)))))))
136 (defsubst geiser-racket--implicit-module ()
137 (save-excursion
138 (goto-char (point-min))
139 (if (re-search-forward "^#lang " nil t)
140 (buffer-file-name)
141 :f)))
143 (defun geiser-racket--get-module (&optional module)
144 (cond ((and (null module) (buffer-file-name)))
145 ;; (geiser-racket--explicit-module)
146 ((null module) (geiser-racket--implicit-module))
147 ((symbolp module) module)
148 ((and (stringp module) (file-name-absolute-p module)) module)
149 ((stringp module) (make-symbol module))
150 (t nil)))
152 (defun geiser-racket--symbol-begin (module)
153 (save-excursion (skip-syntax-backward "^-()>") (point)))
155 (defun geiser-racket--import-command (module)
156 (and (stringp module)
157 (not (zerop (length module)))
158 (format "(require %s)" module)))
160 (defun geiser-racket--exit-command ()
161 (comint-send-eof)
162 (get-buffer-process (current-buffer)))
164 (defconst geiser-racket--binding-forms
165 '("for" "for/list" "for/hash" "for/hasheq" "for/and" "for/or"
166 "for/lists" "for/first" "for/last" "for/fold"
167 "for:" "for/list:" "for/hash:" "for/hasheq:" "for/and:" "for/or:"
168 "for/lists:" "for/first:" "for/last:" "for/fold:"
169 "define-syntax-rule"))
171 (defconst geiser-racket--binding-forms*
172 '("for*" "for*/list" "for*/lists" "for*/hash" "for*/hasheq" "for*/and"
173 "for*/or" "for*/first" "for*/last" "for*/fold"
174 "for*:" "for*/list:" "for*/lists:" "for*/hash:" "for*/hasheq:" "for*/and:"
175 "for*/or:" "for*/first:" "for*/last:" "for*/fold:"))
177 ;;; External help
179 (defsubst geiser-racket--get-help (symbol module)
180 (geiser-eval--send/wait
181 `(:eval (get-help ',symbol '(:module ,module)) geiser/autodoc)))
183 (defun geiser-racket--external-help (id module)
184 (message "Looking up manual for '%s'..." id)
185 (let ((out (geiser-eval--retort-output
186 (geiser-racket--get-help id module))))
187 (when (and out (string-match " but provided by:\n +\\(.+\\)\n" out))
188 (geiser-racket--get-help id (match-string 1 out))))
189 (minibuffer-message "%s done" (current-message))
193 ;;; Error display
195 (defconst geiser-racket--file-rxs
196 '(nil
197 "path:\"?\\([^>\"\n]+\\)\"?>"
198 "module: \"\\([^>\"\n]+\\)\""))
200 (defconst geiser-racket--geiser-file-rx
201 (format "^%s/?racket/geiser" (regexp-quote geiser-scheme-dir)))
203 (defun geiser-racket--purge-trace ()
204 (save-excursion
205 (while (re-search-forward geiser-racket--geiser-file-rx nil t)
206 (kill-whole-line))))
208 (defun geiser-racket--display-error (module key msg)
209 (when key
210 (insert "Error: ")
211 (geiser-doc--insert-button key nil 'racket)
212 (newline 2))
213 (when msg
214 (let ((p (point)))
215 (insert msg)
216 (let ((end (point)))
217 (goto-char p)
218 (when key (geiser-racket--purge-trace))
219 (mapc 'geiser-edit--buttonize-files geiser-racket--file-rxs)
220 (goto-char end)
221 (newline))))
222 (or key (not (zerop (length msg)))))
225 ;;; Trying to ascertain whether a buffer is mzscheme scheme:
227 (defun geiser-racket--guess ()
228 (or (save-excursion
229 (goto-char (point-min))
230 (re-search-forward "#lang " nil t))
231 (geiser-racket--explicit-module)))
234 ;;; Keywords and syntax
235 (defun geiser-racket--keywords ()
236 (cons '("^#lang\\>" . 0)
237 (when geiser-racket-extra-keywords
238 `((,(format "[[(]%s\\>" (regexp-opt geiser-racket-extra-keywords 1))
239 . 1)))))
241 (geiser-syntax--scheme-indent
242 (splicing-let 1)
243 (splicing-letrec 1)
244 (splicing-let-values 1)
245 (splicing-letrec-values 1)
246 (splicing-let-syntax 1)
247 (splicing-letrec-syntax 1)
248 (splicing-let-syntaxes 1)
249 (splicing-letrec-syntaxes 1)
250 (splicing-letrec-syntaxes+values 1)
251 (splicing-local 1))
254 ;;; Remote REPLs
256 (defun connect-to-racket ()
257 "Start a Racket REPL connected to a remote process.
259 The remote process needs to be running a REPL server started
260 using start-geiser, a procedure in the geiser/server module."
261 (interactive)
262 (geiser-connect 'racket))
266 ;;; Implementation definition:
268 (define-geiser-implementation racket
269 (unsupported-procedures '(callers callees generic-methods))
270 (binary geiser-racket--binary)
271 (arglist geiser-racket--parameters)
272 (prompt-regexp geiser-racket--prompt-regexp)
273 (marshall-procedure geiser-racket--geiser-procedure)
274 (find-module geiser-racket--get-module)
275 (enter-command geiser-racket--enter-command)
276 (import-command geiser-racket--import-command)
277 (exit-command geiser-racket--exit-command)
278 (find-symbol-begin geiser-racket--symbol-begin)
279 (display-error geiser-racket--display-error)
280 (external-help geiser-racket--external-help)
281 (check-buffer geiser-racket--guess)
282 (keywords geiser-racket--keywords)
283 (binding-forms geiser-racket--binding-forms)
284 (binding-forms* geiser-racket--binding-forms*))
286 (geiser-impl--add-to-alist 'regexp "\\.ss$" 'racket t)
287 (geiser-impl--add-to-alist 'regexp "\\.rkt$" 'racket t)
289 (defun run-gracket ()
290 "Start the Racket REPL using gracket instead of plain racket."
291 (interactive)
292 (let ((geiser-racket-use-gracket-p t))
293 (run-racket)))
296 (provide 'geiser-racket)