Let's make it *any* submodule
[geiser.git] / elisp / geiser-racket.el
blob435fb39af6acacb0ec4d5f8ae45505a5f9c48e59
1 ;; geiser-racket.el -- geiser support for Racket scheme
3 ;; Copyright (C) 2009, 2010, 2011, 2012, 2013 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-image)
18 (require 'geiser-syntax)
19 (require 'geiser-custom)
20 (require 'geiser-base)
21 (require 'geiser)
23 (eval-when-compile (require 'cl))
26 ;;; Customization:
28 (defgroup geiser-racket nil
29 "Customization for Geiser's Racket flavour."
30 :group 'geiser)
32 (geiser-custom--defcustom geiser-racket-binary
33 (cond ((eq system-type 'windows-nt) "Racket.exe")
34 (t "racket"))
35 "Name to use to call the racket executable when starting a REPL."
36 :type '(choice string (repeat string))
37 :group 'geiser-racket)
39 (geiser-custom--defcustom geiser-racket-gracket-binary
40 (cond ((eq system-type 'windows-nt) "GRacket-text.exe")
41 (t "gracket-text"))
42 "Name to use to call the gracket executable when starting a REPL.
43 This executable is used by `run-gracket', and, if
44 `geiser-racket-use-gracket-p' is set to t, by `run-racket'."
45 :type '(choice string (repeat string))
46 :group 'geiser-racket)
48 (geiser-custom--defcustom geiser-racket-collects nil
49 "A list of paths to be added to racket's collection directories."
50 :type '(repeat file)
51 :group 'geiser-racket)
53 (geiser-custom--defcustom geiser-racket-init-file "~/.racket-geiser"
54 "Initialization file with user code for the racket REPL."
55 :type 'string
56 :group 'geiser-racket)
58 (geiser-custom--defcustom geiser-racket-use-gracket-p nil
59 "Whether to use the gracket binary to start Racket REPLs."
60 :type 'boolean
61 :group 'geiser-racket)
63 (geiser-custom--defcustom geiser-racket-extra-keywords
64 '("define-syntax-rule" "provide" "require"
65 "unless" "when" "with-handlers")
66 "Extra keywords highlighted in Racket buffers."
67 :type '(repeat string)
68 :group 'geiser-racket)
70 (geiser-custom--defcustom geiser-racket-case-sensitive-p t
71 "Non-nil means keyword highlighting is case-sensitive."
72 :type 'boolean
73 :group 'geiser-racket)
76 ;;; REPL support:
78 (defsubst geiser-racket--real-binary ()
79 (if geiser-racket-use-gracket-p
80 geiser-racket-gracket-binary
81 geiser-racket-binary))
83 (defun geiser-racket--binary ()
84 (let ((binary (geiser-racket--real-binary)))
85 (if (listp binary) (car binary) binary)))
87 (defun geiser-racket--parameters ()
88 "Return a list with all parameters needed to start racket.
89 This function uses `geiser-racket-init-file' if it exists."
90 (let ((init-file (and (stringp geiser-racket-init-file)
91 (expand-file-name geiser-racket-init-file)))
92 (binary (geiser-racket--real-binary))
93 (rackdir (expand-file-name "racket/" geiser-scheme-dir)))
94 `("-i" "-q" "-S" ,rackdir
95 ,@(apply 'append (mapcar (lambda (p) (list "-S" p))
96 geiser-racket-collects))
97 ,@(and (listp binary) (cdr binary))
98 ,@(and init-file (file-readable-p init-file) (list "-f" init-file))
99 "-f" ,(expand-file-name "geiser/startup.rkt" rackdir))))
101 (defconst geiser-racket--prompt-regexp "\\(mzscheme\\|racket\\)@[^ ]*> ")
104 ;;; Remote REPLs
106 (defun connect-to-racket ()
107 "Start a Racket REPL connected to a remote process.
109 The remote process needs to be running a REPL server started
110 using start-geiser, a procedure in the geiser/server module."
111 (interactive)
112 (geiser-connect 'racket))
116 ;;; Evaluation support:
118 (defconst geiser-racket--module-re
119 "^(module[+*]? +\\([^ ]+\\)\\W+\\([^ ]+\\)?")
121 (defun geiser-racket--explicit-module ()
122 (save-excursion
123 (ignore-errors
124 (while (not (zerop (geiser-syntax--nesting-level)))
125 (backward-up-list)))
126 (and (looking-at geiser-racket--module-re)
127 (let ((mod (match-string-no-properties 1))
128 (lang (match-string-no-properties 2)))
129 (cons (geiser-syntax--form-from-string mod)
130 (geiser-syntax--form-from-string lang))))))
132 (defun geiser-racket--language ()
133 (or (cdr (geiser-racket--explicit-module))
134 (save-excursion
135 (goto-char (point-min))
136 (if (re-search-forward "^#lang +\\([^ ]+\\)" nil t)
137 (geiser-syntax--form-from-string (match-string-no-properties 1))))
138 "#f"))
140 (defun geiser-racket--implicit-module ()
141 (save-excursion
142 (goto-char (point-min))
143 (when (re-search-forward "^#lang " nil t)
144 (buffer-file-name))))
146 (defun geiser-racket--find-module ()
147 (let ((bf (geiser-racket--implicit-module))
148 (sub (car (geiser-racket--explicit-module))))
149 (cond ((and (not bf) (not sub)) nil)
150 ((and (not bf) sub) sub)
151 (sub `(submod (file ,bf) ,sub))
152 (t bf))))
154 (defun geiser-racket--enter-command (module)
155 (when (or (stringp module) (listp module))
156 (cond ((zerop (length module)) ",enter #f")
157 ((or (listp module)
158 (file-name-absolute-p module)) (format ",enter %S" module))
159 (t (format ",enter %s" module)))))
161 (defun geiser-racket--geiser-procedure (proc &rest args)
162 (case proc
163 ((eval compile)
164 (format ",geiser-eval %s %s %s"
165 (or (car args) "#f")
166 (geiser-racket--language)
167 (mapconcat 'identity (cdr args) " ")))
168 ((load-file compile-file)
169 (format ",geiser-load %S" (geiser-racket--find-module)))
170 ((no-values) ",geiser-no-values")
171 (t (format ",apply geiser:%s (%s)" proc (mapconcat 'identity args " ")))))
173 (defun geiser-racket--get-module (&optional module)
174 (cond ((null module) (or (geiser-racket--find-module) :f))
175 ((symbolp module) module)
176 ((and (stringp module) (file-name-absolute-p module)) module)
177 ((stringp module) (make-symbol module))
178 (t nil)))
180 (defun geiser-racket--symbol-begin (module)
181 (save-excursion (skip-syntax-backward "^-()>") (point)))
183 (defun geiser-racket--import-command (module)
184 (and (stringp module)
185 (not (zerop (length module)))
186 (format "(require %s)" module)))
188 (defun geiser-racket--exit-command ()
189 (comint-send-eof)
190 (get-buffer-process (current-buffer)))
192 (defconst geiser-racket--binding-forms
193 '("for" "for/list" "for/hash" "for/hasheq" "for/and" "for/or"
194 "for/lists" "for/first" "for/last" "for/fold"
195 "for:" "for/list:" "for/hash:" "for/hasheq:" "for/and:" "for/or:"
196 "for/lists:" "for/first:" "for/last:" "for/fold:"
197 "define-syntax-rule"))
199 (defconst geiser-racket--binding-forms*
200 '("for*" "for*/list" "for*/lists" "for*/hash" "for*/hasheq" "for*/and"
201 "for*/or" "for*/first" "for*/last" "for*/fold"
202 "for*:" "for*/list:" "for*/lists:" "for*/hash:" "for*/hasheq:" "for*/and:"
203 "for*/or:" "for*/first:" "for*/last:" "for*/fold:"))
205 ;;; External help
207 (defsubst geiser-racket--get-help (symbol module)
208 (geiser-eval--send/wait `(:scm ,(format ",help %s %s" symbol module))))
210 (defun geiser-racket--external-help (id module)
211 (message "Looking up manual for '%s'..." id)
212 (let* ((ret (geiser-racket--get-help id (format "%S" module)))
213 (out (geiser-eval--retort-output ret))
214 (ret (if (and out (string-match " but provided by:\n +\\(.+\\)\n" out))
215 (geiser-racket--get-help id (match-string 1 out))
216 ret)))
217 (unless (string-match "^Sending to web browser.+"
218 (geiser-eval--retort-output ret))
219 (minibuffer-message "%s not found" (current-message)))
223 ;;; Error display
225 (defconst geiser-racket--file-rxs
226 '(nil
227 "path:\"?\\([^>\"\n]+\\)\"?>"
228 "module: \"\\([^>\"\n]+\\)\""))
230 (defconst geiser-racket--geiser-file-rx
231 (format "^ *%s/?racket/geiser" (regexp-quote geiser-scheme-dir)))
233 (defun geiser-racket--purge-trace ()
234 (save-excursion
235 (while (re-search-forward geiser-racket--geiser-file-rx nil t)
236 (kill-whole-line))))
238 (defun geiser-racket--display-error (module key msg)
239 (when key
240 (insert "Error: ")
241 (geiser-doc--insert-button key nil 'racket)
242 (newline 2))
243 (when msg
244 (let ((p (point)))
245 (insert msg)
246 (let ((end (point)))
247 (goto-char p)
248 (when key (geiser-racket--purge-trace))
249 (mapc 'geiser-edit--buttonize-files geiser-racket--file-rxs)
250 (goto-char end)
251 (newline))))
252 (or key (not (zerop (length msg)))))
255 ;;; Trying to ascertain whether a buffer is mzscheme scheme:
257 (defun geiser-racket--guess ()
258 (or (save-excursion
259 (goto-char (point-min))
260 (re-search-forward "#lang " nil t))
261 (geiser-racket--explicit-module)))
264 ;;; Keywords and syntax
266 (setq geiser-racket-font-lock-forms
267 '(("^#lang\\>" . 0)
268 ("\\[\\(else\\)\\>" . 1)
269 ("(\\(define/match\\)\\W+[[(]?\\(\\w+\\)+\\b"
270 (1 font-lock-keyword-face)
271 (2 font-lock-function-name-face))))
273 (defun geiser-racket--keywords ()
274 (append geiser-racket-font-lock-forms
275 (when geiser-racket-extra-keywords
276 `((,(format "[[(]%s\\>" (regexp-opt geiser-racket-extra-keywords 1))
277 . 1)))))
279 (geiser-syntax--scheme-indent
280 (begin0 1)
281 (case-lambda: 0)
282 (class* defun)
283 (compound-unit/sig 0)
284 (define: defun)
285 (for 1)
286 (for* 1)
287 (for*/and 1)
288 (for*/first 1)
289 (for*/fold 2)
290 (for*/hash 1)
291 (for*/hasheq 1)
292 (for*/hasheqv 1)
293 (for*/last 1)
294 (for*/list 1)
295 (for*/lists 2)
296 (for*/or 1)
297 (for*/product 1)
298 (for*/set 1)
299 (for*/seteq 1)
300 (for*/seteqv 1)
301 (for*/sum 1)
302 (for*/vector 1)
303 (for/and 1)
304 (for/first 1)
305 (for/fold 2)
306 (for/hash 1)
307 (for/hasheq 1)
308 (for/hasheqv 1)
309 (for/last 1)
310 (for/list 1)
311 (for/lists 2)
312 (for/or 1)
313 (for/product 1)
314 (for/set 1)
315 (for/seteq 1)
316 (for/seteqv 1)
317 (for/sum 1)
318 (for/vector 1)
319 (instantiate 2)
320 (interface 1)
321 (lambda/kw 1)
322 (lambda: 1)
323 (let*-values: 1)
324 (let+ 1)
325 (let-values: 1)
326 (let/cc: 1)
327 (let: 1)
328 (letrec-values: 1)
329 (letrec: 1)
330 (local 1)
331 (match-let 1)
332 (match-let-values 1)
333 (match/values 1)
334 (mixin 2)
335 (module defun)
336 (module+ defun)
337 (module* defun)
338 (parameterize-break 1)
339 (quasisyntax/loc 1)
340 (send* 1)
341 (splicing-let 1)
342 (splicing-let-syntax 1)
343 (splicing-let-syntaxes 1)
344 (splicing-let-values 1)
345 (splicing-letrec 1)
346 (splicing-letrec-syntax 1)
347 (splicing-letrec-syntaxes 1)
348 (splicing-letrec-syntaxes+values 1)
349 (splicing-letrec-values 1)
350 (splicing-local 1)
351 (struct 1)
352 (syntax-id-rules defun)
353 (syntax/loc 1)
354 (type-case defun)
355 (unit defun)
356 (unit/sig 2)
357 (with-handlers 1)
358 (with-handlers: 1))
361 ;;; Startup
363 (defun geiser-racket--startup (remote)
364 (set (make-local-variable 'compilation-error-regexp-alist)
365 `(("^ *\\([^:(\t\n]+\\):\\([0-9]+\\):\\([0-9]+\\):" 1 2 3)))
366 (compilation-setup t)
367 (if geiser-image-cache-dir
368 (geiser-eval--send/wait
369 `(:eval (image-cache ,geiser-image-cache-dir) geiser/user))
370 (setq geiser-image-cache-dir
371 (geiser-eval--send/result '(:eval (image-cache) geiser/user)))))
374 ;;; Additional commands
376 (defvar geiser-racket--submodule-history ())
378 (defun geiser-racket--submodule-form (name)
379 (format "module[+*]? %s"
380 (cond ((eq 1 name) "")
381 ((numberp name)
382 (read-string "Submodule name: " nil
383 'geiser-racket--submodule-history))
384 ((stringp name) name)
385 t "")))
387 (defun geiser-racket-toggle-submodules (&optional name)
388 "Toggle visibility of submodule forms.
390 Use a prefix to be asked for a submodule name."
391 (interactive "p")
392 (geiser-edit--toggle-visibility (geiser-racket--submodule-form name)))
394 (defun geiser-racket-show-submodules (&optional name)
395 "Unconditionally shows all submodule forms.
397 Use a prefix to be asked for a submodule name."
398 (interactive "p")
399 (cond ((eq 1 name) (geiser-edit--show-all))
400 (t (geiser-edit--show (geiser-racket--submodule-form name)))))
402 (defun geiser-racket-hide-submodules (&optional name)
403 "Unconditionally hides all visible submodules.
405 Use a prefix to be asked for a submodule name."
406 (interactive "p")
407 (geiser-edit--hide (geiser-racket--submodule-form name)))
410 ;;; Implementation definition:
412 (define-geiser-implementation racket
413 (unsupported-procedures '(callers callees generic-methods))
414 (binary geiser-racket--binary)
415 (arglist geiser-racket--parameters)
416 (repl-startup geiser-racket--startup)
417 (prompt-regexp geiser-racket--prompt-regexp)
418 (marshall-procedure geiser-racket--geiser-procedure)
419 (find-module geiser-racket--get-module)
420 (enter-command geiser-racket--enter-command)
421 (import-command geiser-racket--import-command)
422 (exit-command geiser-racket--exit-command)
423 (find-symbol-begin geiser-racket--symbol-begin)
424 (display-error geiser-racket--display-error)
425 (external-help geiser-racket--external-help)
426 (check-buffer geiser-racket--guess)
427 (keywords geiser-racket--keywords)
428 (case-sensitive geiser-racket-case-sensitive-p)
429 (binding-forms geiser-racket--binding-forms)
430 (binding-forms* geiser-racket--binding-forms*))
432 (geiser-impl--add-to-alist 'regexp "\\.ss$" 'racket t)
433 (geiser-impl--add-to-alist 'regexp "\\.rkt$" 'racket t)
435 (defun run-gracket ()
436 "Start the Racket REPL using gracket instead of plain racket."
437 (interactive)
438 (let ((geiser-racket-use-gracket-p t))
439 (run-racket)))
442 (provide 'geiser-racket)