Support for implementation-specific font lock keywords
[geiser.git] / elisp / geiser-racket.el
blobac2e1f66cd628544c26065bcac6a78186561cff0
1 ;; geiser-racket.el -- geiser support for Racket scheme
3 ;; Copyright (C) 2009, 2010 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" "unless" "when" "with-handlers")
61 "Extra keywords highlighted in Racket buffers."
62 :type '(repeat string)
63 :group 'geiser-racket)
66 ;;; REPL support:
68 (defsubst geiser-racket--real-binary ()
69 (if geiser-racket-use-gracket-p
70 geiser-racket-gracket-binary
71 geiser-racket-binary))
73 (defun geiser-racket--binary ()
74 (let ((binary (geiser-racket--real-binary)))
75 (if (listp binary) (car binary) binary)))
77 (defun geiser-racket--parameters ()
78 "Return a list with all parameters needed to start racket.
79 This function uses `geiser-racket-init-file' if it exists."
80 (let ((init-file (and (stringp geiser-racket-init-file)
81 (expand-file-name geiser-racket-init-file)))
82 (binary (geiser-racket--real-binary)))
83 `("-i" "-q"
84 "-S" ,(expand-file-name "racket/" geiser-scheme-dir)
85 ,@(apply 'append (mapcar (lambda (p) (list "-S" p)) geiser-racket-collects))
86 ,@(and (listp binary) (cdr binary))
87 ,@(and init-file (file-readable-p init-file) (list "-f" init-file))
88 "-f" ,(expand-file-name "racket/geiser.rkt" geiser-scheme-dir))))
90 (defconst geiser-racket--prompt-regexp "\\(mzscheme\\|racket\\)@[^ ]*?> ")
93 ;;; Evaluation support:
95 (defun geiser-racket--language ()
96 (save-excursion
97 (goto-char (point-min))
98 (if (re-search-forward
99 "^\\(?:#lang\\|(module +[^ ]+?\\) +\\([^ ]+?\\|([^)]+)\\) *$" nil t)
100 (car (geiser-syntax--read-from-string (match-string-no-properties 1)))
101 "#f")))
103 (defun geiser-racket--enter-command (module)
104 (when (stringp module)
105 (cond ((zerop (length module)) ",enter #f")
106 ((file-name-absolute-p module) (format ",enter (file %S)" module))
107 (t (format ",enter %s" module)))))
109 (defun geiser-racket--geiser-procedure (proc &rest args)
110 (case proc
111 ((eval compile)
112 (format ",geiser-eval %s %s %s"
113 (or (car args) "#f")
114 (geiser-racket--language)
115 (mapconcat 'identity (cdr args) " ")))
116 ((load-file compile-file)
117 (format ",geiser-eval geiser/main racket (geiser:%s %s)"
118 proc (car args)))
119 ((no-values) ",geiser-no-values")
120 (t (format ",apply geiser:%s (%s)" proc (mapconcat 'identity args " ")))))
122 (defconst geiser-racket--module-re
123 "^(module +\\([^ ]+\\)")
125 (defun geiser-racket--explicit-module ()
126 (save-excursion
127 (goto-char (point-min))
128 (and (re-search-forward geiser-racket--module-re nil t)
129 (ignore-errors
130 (car (geiser-syntax--read-from-string
131 (match-string-no-properties 1)))))))
133 (defsubst geiser-racket--implicit-module ()
134 (save-excursion
135 (goto-char (point-min))
136 (if (re-search-forward "^#lang " nil t)
137 (buffer-file-name)
138 :f)))
140 (defun geiser-racket--get-module (&optional module)
141 (cond ((and (null module) (buffer-file-name)))
142 ;; (geiser-racket--explicit-module)
143 ((null module) (geiser-racket--implicit-module))
144 ((symbolp module) module)
145 ((and (stringp module) (file-name-absolute-p module)) module)
146 ((stringp module) (intern module))
147 (t nil)))
149 (defun geiser-racket--symbol-begin (module)
150 (save-excursion (skip-syntax-backward "^-()>") (point)))
152 (defun geiser-racket--import-command (module)
153 (and (stringp module)
154 (not (zerop (length module)))
155 (format "(require %s)" module)))
157 (defun geiser-racket--exit-command ()
158 (comint-send-eof)
159 (get-buffer-process (current-buffer)))
161 (defconst geiser-racket--binding-forms
162 '(for for/list for/hash for/hasheq for/and for/or
163 for/lists for/first for/last for/fold
164 for: for/list: for/hash: for/hasheq: for/and: for/or:
165 for/lists: for/first: for/last: for/fold:))
167 (defconst geiser-racket--binding-forms*
168 '(for* for*/list for*/lists for*/hash for*/hasheq for*/and
169 for*/or for*/first for*/last for*/fold
170 for*: for*/list: for*/lists: for*/hash: for*/hasheq: for*/and:
171 for*/or: for*/first: for*/last: for*/fold:))
173 ;;; External help
175 (defsubst geiser-racket--get-help (symbol module)
176 (geiser-eval--send/wait
177 `(:eval (get-help ',symbol '(:module ,module)) geiser/autodoc)))
179 (defun geiser-racket--external-help (id module)
180 (message "Requesting help for '%s'..." id)
181 (let ((out (geiser-eval--retort-output
182 (geiser-racket--get-help id module))))
183 (when (and out (string-match " but provided by:\n +\\(.+\\)\n" out))
184 (geiser-racket--get-help id (match-string 1 out))))
185 (minibuffer-message "%s done" (current-message))
189 ;;; Error display
191 (defconst geiser-racket--file-rxs
192 '(nil
193 "path:\"?\\([^>\"\n]+\\)\"?>"
194 "module: \"\\([^>\"\n]+\\)\""))
196 (defconst geiser-racket--geiser-file-rx
197 (format "^%s/?racket/geiser" (regexp-quote geiser-scheme-dir)))
199 (defun geiser-racket--purge-trace ()
200 (save-excursion
201 (while (re-search-forward geiser-racket--geiser-file-rx nil t)
202 (kill-whole-line))))
204 (defun geiser-racket--display-error (module key msg)
205 (when key
206 (insert "Error: ")
207 (geiser-doc--insert-button key nil 'racket)
208 (newline 2))
209 (when msg
210 (let ((p (point)))
211 (insert msg)
212 (when key
213 (let ((end (point)))
214 (goto-char p)
215 (geiser-racket--purge-trace)
216 (mapc 'geiser-edit--buttonize-files geiser-racket--file-rxs)
217 (goto-char end)
218 (newline)))))
219 (or key (not (zerop (length msg)))))
222 ;;; Trying to ascertain whether a buffer is mzscheme scheme:
224 (defun geiser-racket--guess ()
225 (or (save-excursion
226 (goto-char (point-min))
227 (re-search-forward "#lang " nil t))
228 (geiser-racket--explicit-module)))
231 ;;; Keywords
232 (defun geiser-racket--keywords ()
233 (when geiser-racket-extra-keywords
234 `((,(format "[[(]%s\\>" (regexp-opt geiser-racket-extra-keywords 1))
235 . 1))))
238 ;;; Remote REPLs
240 (defun connect-to-racket ()
241 "Start a Racket REPL connected to a remote process.
243 The remote process needs to be running a REPL server started
244 using start-geiser, a procedure in the geiser/server module."
245 (interactive)
246 (geiser-connect 'racket))
250 ;;; Implementation definition:
252 (define-geiser-implementation racket
253 (unsupported-procedures '(callers callees generic-methods))
254 (binary geiser-racket--binary)
255 (arglist geiser-racket--parameters)
256 (prompt-regexp geiser-racket--prompt-regexp)
257 (marshall-procedure geiser-racket--geiser-procedure)
258 (find-module geiser-racket--get-module)
259 (enter-command geiser-racket--enter-command)
260 (import-command geiser-racket--import-command)
261 (exit-command geiser-racket--exit-command)
262 (find-symbol-begin geiser-racket--symbol-begin)
263 (display-error geiser-racket--display-error)
264 (display-help geiser-racket--external-help)
265 (check-buffer geiser-racket--guess)
266 (keywords geiser-racket--keywords)
267 (binding-forms geiser-racket--binding-forms)
268 (binding-forms* geiser-racket--binding-forms*))
270 (geiser-impl--add-to-alist 'regexp "\\.ss$" 'racket t)
271 (geiser-impl--add-to-alist 'regexp "\\.rkt$" 'racket t)
273 (defun run-gracket ()
274 "Start the Racket REPL using gracket instead of plain racket."
275 (interactive)
276 (let ((geiser-racket-use-gracket-p t))
277 (run-racket)))
280 (provide 'geiser-racket)