1 ;;; geiser-repl.el --- Geiser's REPL
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>.
12 (require 'geiser-company
)
13 (require 'geiser-autodoc
)
14 (require 'geiser-edit
)
15 (require 'geiser-impl
)
16 (require 'geiser-eval
)
17 (require 'geiser-connection
)
18 (require 'geiser-menu
)
19 (require 'geiser-custom
)
20 (require 'geiser-base
)
29 (defgroup geiser-repl nil
30 "Interacting with the Geiser REPL."
33 (geiser-custom--defcustom geiser-repl-use-other-window t
34 "Whether to Use a window other than the current buffer's when
35 switching to the Geiser REPL buffer."
39 (geiser-custom--defcustom geiser-repl-window-allow-split t
40 "Whether to allow window splitting when switching to the Geiser
45 (geiser-custom--defcustom geiser-repl-history-filename
(expand-file-name "~/.geiser_history")
46 "File where REPL input history is saved, so that it persists between sessions.
47 This is actually the base name: the concrete Scheme
48 implementation name gets appended to it."
52 (geiser-custom--defcustom geiser-repl-history-size comint-input-ring-size
53 "Maximum size of the saved REPL input history."
57 (geiser-custom--defcustom geiser-repl-history-no-dups-p t
58 "Whether to skip duplicates when recording history."
62 (geiser-custom--defcustom geiser-repl-autodoc-p t
63 "Whether to enable `geiser-autodoc-mode' in the REPL by default."
67 (geiser-custom--defcustom geiser-repl-company-p t
68 "Whether to use company-mode for completion, if available."
72 (geiser-custom--defcustom geiser-repl-read-only-prompt-p t
73 "Whether the REPL's prompt should be read-only."
77 (geiser-custom--defcustom geiser-repl-auto-indent-p t
78 "Whether newlines for incomplete sexps are autoindented."
82 (geiser-custom--defcustom geiser-repl-forget-old-errors-p t
83 "Whether to forget old errors upon entering a new expression.
85 When on (the default), every time a new expression is entered in
86 the REPL old error messages are flushed, and using \\[next-error]
87 afterwards will jump only to error locations produced by the new
92 (geiser-custom--defcustom geiser-repl-query-on-exit-p nil
93 "Whether to prompt for confirmation on \\[geiser-repl-exit]."
97 (geiser-custom--defcustom geiser-repl-default-host
"localhost"
98 "Default host when connecting to remote REPLs."
102 (geiser-custom--defcustom geiser-repl-default-port
37146
103 "Default port for connecting to remote REPLs."
108 ;;; Geiser REPL buffers and processes:
110 (defvar geiser-repl--repls nil
)
111 (defvar geiser-repl--closed-repls nil
)
113 (make-variable-buffer-local
114 (defvar geiser-repl--repl nil
))
116 (defsubst geiser-repl--set-this-buffer-repl
(r)
117 (setq geiser-repl--repl r
))
119 (defun geiser-repl--live-p ()
120 (and geiser-repl--repl
121 (get-buffer-process geiser-repl--repl
)))
123 (defun geiser-repl--repl/impl
(impl &optional repls
)
125 (dolist (repl (or repls geiser-repl--repls
))
126 (when (buffer-live-p repl
)
127 (with-current-buffer repl
128 (when (eq geiser-impl--implementation impl
)
129 (throw 'repl repl
)))))))
131 (defun geiser-repl--set-up-repl (impl)
132 (or (and (not impl
) geiser-repl--repl
)
133 (setq geiser-repl--repl
135 geiser-impl--implementation
136 (geiser-impl--guess))))
137 (when impl
(geiser-repl--repl/impl impl
))))))
139 (defun geiser-repl--active-impls ()
141 (dolist (repl geiser-repl--repls act
)
142 (with-current-buffer repl
143 (add-to-list 'act geiser-impl--implementation
)))))
145 (defsubst geiser-repl--repl-name
(impl)
146 (format "%s REPL" (geiser-impl--impl-str impl
)))
148 (defun geiser-repl--to-repl-buffer (impl)
149 (unless (and (eq major-mode
'geiser-repl-mode
)
150 (not (get-buffer-process (current-buffer))))
151 (let* ((old (geiser-repl--repl/impl impl geiser-repl--closed-repls
))
152 (old (and (buffer-live-p old
)
153 (not (get-buffer-process old
))
157 (generate-new-buffer (format "* %s *"
158 (geiser-repl--repl-name impl
)))))))
160 (geiser-impl--set-buffer-implementation impl
))
162 (geiser-impl--define-caller geiser-repl--binary binary
()
163 "A variable or function returning the path to the scheme binary
164 for this implementation.")
166 (geiser-impl--define-caller geiser-repl--arglist arglist
()
167 "A function taking no arguments and returning a list of
168 arguments to be used when invoking the scheme binary.")
170 (geiser-impl--define-caller geiser-repl--prompt-regexp prompt-regexp
()
171 "A variable (or thunk returning a value) giving the regular
172 expression for this implementation's scheme prompt.")
174 (geiser-impl--define-caller
175 geiser-repl--debugger-prompt-regexp debugger-prompt-regexp
()
176 "A variable (or thunk returning a value) giving the regular
177 expression for this implementation's debugging prompt.")
179 (geiser-impl--define-caller
180 geiser-repl--debugger-preamble-regexp debugger-preamble-regexp
()
181 "A variable (or thunk returning a value) used to determine whether
182 the REPL has entered debugging mode.")
184 (geiser-impl--define-caller geiser-repl--startup startup
()
185 "Function taking no parameters that is called after the REPL
186 has been initialised. All Geiser functionality is available to
189 (geiser-impl--define-caller geiser-repl--enter-cmd enter-command
(module)
190 "Function taking a module designator and returning a REPL enter
191 module command as a string")
193 (geiser-impl--define-caller geiser-repl--import-cmd import-command
(module)
194 "Function taking a module designator and returning a REPL import
195 module command as a string")
197 (geiser-impl--define-caller geiser-repl--exit-cmd exit-command
()
198 "Function returning the REPL exit command as a string")
200 (make-variable-buffer-local
201 (defvar geiser-repl--address nil
))
203 (defsubst geiser-repl--host
() (car geiser-repl--address
))
204 (defsubst geiser-repl--port
() (cdr geiser-repl--address
))
205 (defsubst geiser-repl--remote-p
() geiser-repl--address
)
207 (defun geiser-repl--get-address ()
208 (let ((defhost (or (geiser-repl--host) geiser-repl-default-host
))
209 (defport (or (geiser-repl--port) geiser-repl-default-port
)))
210 (cons (read-string (format "Host (default %s): " defhost
) nil nil defhost
)
211 (read-number "Port: " defport
))))
213 (defun geiser-repl--save-remote-data (remote address
)
214 (setq geiser-repl--address
(and remote address
))
216 (setq header-line-format
(format "Host: %s Port: %s"
218 (geiser-repl--port)))))
220 (defun geiser-repl--start-repl (impl &optional remote
)
221 (message "Starting Geiser REPL for %s ..." impl
)
222 (geiser-repl--to-repl-buffer impl
)
223 (let ((program (if remote
(geiser-repl--get-address)
224 (geiser-repl--binary impl
)))
225 (args (geiser-repl--arglist impl
))
226 (prompt-rx (geiser-repl--prompt-regexp impl
))
227 (deb-prompt-rx (geiser-repl--debugger-prompt-regexp impl
))
228 (deb-preamble-rx (geiser-repl--debugger-preamble-regexp impl
))
229 (cname (geiser-repl--repl-name impl
)))
230 (unless (and program prompt-rx
)
231 (error "Sorry, I don't know how to start a REPL for %s" impl
))
232 (set (make-local-variable 'comint-prompt-regexp
) prompt-rx
)
233 (geiser-repl--save-remote-data remote program
)
235 (apply 'make-comint-in-buffer
236 `(,cname
,(current-buffer) ,program nil
,@args
))
237 (error (insert "Unable to start REPL:\n\n"
238 (error-message-string err
) "\n")
239 (error "Couldn't start Geiser")))
240 (geiser-repl--wait-for-prompt 10000)
241 (geiser-repl--history-setup)
242 (geiser-con--setup-connection (current-buffer)
246 (add-to-list 'geiser-repl--repls
(current-buffer))
247 (geiser-repl--set-this-buffer-repl (current-buffer))
248 (geiser-repl--startup impl
)))
250 (defun geiser-repl--process ()
251 (let ((buffer (geiser-repl--set-up-repl geiser-impl--implementation
)))
252 (or (and (buffer-live-p buffer
) (get-buffer-process buffer
))
253 (error "No Geiser REPL for this buffer (try M-x run-geiser)"))))
255 (setq geiser-eval--default-proc-function
'geiser-repl--process
)
257 (defun geiser-repl--wait-for-prompt (timeout)
258 (let ((p (point)) (seen) (buffer (current-buffer)))
259 (while (and (not seen
)
261 (get-buffer-process buffer
))
263 (setq timeout
(- timeout
100))
265 (setq seen
(re-search-forward comint-prompt-regexp nil t
)))
266 (goto-char (point-max))
267 (unless seen
(error "No prompt found!"))))
270 ;;; Interface: starting and interacting with geiser REPL:
272 (defun geiser-repl--read-impl (prompt &optional active
)
273 (geiser-impl--read-impl prompt
(and active
(geiser-repl--active-impls))))
275 (defsubst geiser-repl--only-impl-p
()
276 (and (null (cdr geiser-active-implementations
))
277 (car geiser-active-implementations
)))
279 (defun run-geiser (impl)
280 "Start a new Geiser REPL."
282 (list (or (geiser-repl--only-impl-p)
283 (and (eq major-mode
'geiser-repl-mode
)
284 geiser-impl--implementation
)
285 (geiser-repl--read-impl
286 "Start Geiser for scheme implementation: "))))
287 (geiser-repl--start-repl impl
))
289 (defun geiser-connect (impl)
290 "Start a new Geiser REPL connected to a remote Scheme process."
292 (list (or (geiser-repl--only-impl-p)
293 (and (eq major-mode
'geiser-repl-mode
)
294 geiser-impl--implementation
)
295 (geiser-repl--read-impl
296 "Scheme implementation: "))))
297 (geiser-repl--start-repl impl t
))
299 (make-variable-buffer-local
300 (defvar geiser-repl--last-scm-buffer nil
))
302 (defun switch-to-geiser (&optional ask impl buffer
)
303 "Switch to running Geiser REPL.
304 With prefix argument, ask for which one if more than one is running.
305 If no REPL is running, execute `run-geiser' to start a fresh one."
307 (let* ((impl (or impl geiser-impl--implementation
))
308 (repl (cond ((and (not ask
) (not impl
)
309 (or geiser-repl--repl
(car geiser-repl--repls
))))
310 ((and (not ask
) impl
(geiser-repl--repl/impl impl
)))))
311 (pop-up-windows geiser-repl-window-allow-split
))
312 (cond ((and (eq (current-buffer) repl
)
313 (not (eq repl buffer
))
314 (buffer-live-p geiser-repl--last-scm-buffer
))
315 (pop-to-buffer geiser-repl--last-scm-buffer
))
316 (repl (pop-to-buffer repl
))
317 ((geiser-repl--remote-p) (geiser-connect impl
))
318 (t (run-geiser impl
)))
319 (when (and buffer
(eq major-mode
'geiser-repl-mode
))
320 (setq geiser-repl--last-scm-buffer buffer
))))
322 (defalias 'geiser
'switch-to-geiser
)
324 (defun geiser-repl--send (cmd)
325 (when (and cmd
(eq major-mode
'geiser-repl-mode
))
326 (goto-char (point-max))
329 (let ((comint-input-filter (lambda (x) nil
)))
330 (comint-send-input nil t
))))
332 (defun switch-to-geiser-module (&optional module buffer
)
333 "Switch to running Geiser REPL and try to enter a given module."
335 (let* ((module (or module
336 (geiser-completion--read-module "Switch to module: ")))
338 (geiser-repl--enter-cmd geiser-impl--implementation
340 (unless (eq major-mode
'geiser-repl-mode
)
341 (switch-to-geiser nil nil
(or buffer
(current-buffer))))
342 (geiser-repl--send cmd
)))
344 (defun geiser-repl-import-module (&optional module
)
345 "Import a given module in the current namespace of the REPL."
347 (let* ((module (or module
348 (geiser-completion--read-module "Import module: ")))
350 (geiser-repl--import-cmd geiser-impl--implementation
352 (switch-to-geiser nil nil
(current-buffer))
353 (geiser-repl--send cmd
)))
355 (defun geiser-repl-exit (&optional arg
)
356 "Exit the current REPL.
357 With a prefix argument, force exit by killing the scheme process."
359 (when (or (not geiser-repl-query-on-exit-p
)
360 (y-or-n-p "Really quit this REPL? "))
361 (let ((cmd (and (not arg
)
362 (geiser-repl--exit-cmd geiser-impl--implementation
))))
364 (when (stringp cmd
) (geiser-repl--send cmd
))
365 (comint-kill-subjob)))))
367 (defun geiser-repl-nuke ()
368 "Try this command if the REPL becomes unresponsive."
370 (goto-char (point-max))
371 (comint-kill-region comint-last-input-start
(point))
372 (comint-redirect-cleanup)
373 (geiser-con--setup-connection (current-buffer)
375 geiser-con--debugging-prompt-regexp
376 geiser-con--debugging-preamble-regexp
))
379 ;;; REPL history and clean-up:
381 (defsubst geiser-repl--history-file
()
382 (format "%s.%s" geiser-repl-history-filename geiser-impl--implementation
))
384 (defun geiser-repl--on-quit ()
385 (comint-write-input-ring)
386 (let ((cb (current-buffer))
387 (impl geiser-impl--implementation
)
388 (comint-prompt-read-only nil
))
389 (setq geiser-repl--repls
(remove cb geiser-repl--repls
))
390 (dolist (buffer (buffer-list))
391 (when (buffer-live-p buffer
)
392 (with-current-buffer buffer
393 (when (and (eq geiser-impl--implementation impl
)
394 (equal cb geiser-repl--repl
))
395 (geiser-repl--set-up-repl geiser-impl--implementation
)))))))
397 (defun geiser-repl--sentinel (proc event
)
398 (let ((pb (process-buffer proc
)))
399 (when (buffer-live-p pb
)
400 (with-current-buffer pb
401 (let ((comint-prompt-read-only nil
)
402 (comint-input-ring-file-name (geiser-repl--history-file)))
403 (geiser-repl--on-quit)
404 (push pb geiser-repl--closed-repls
)
405 (when (buffer-name (current-buffer))
406 (comint-kill-region comint-last-input-start
(point))
407 (insert "\nIt's been nice interacting with you!\n")
408 (insert "Press C-c C-z to bring me back.\n" )))))))
410 (defun geiser-repl--on-kill ()
411 (geiser-repl--on-quit)
412 (setq geiser-repl--closed-repls
413 (remove (current-buffer) geiser-repl--closed-repls
)))
415 (defun geiser-repl--input-filter (str)
416 (not (or (geiser-con--is-debugging)
417 (string-match "^\\s *$" str
)
418 (string-match "^,quit *$" str
))))
420 (defun geiser-repl--old-input ()
424 (buffer-substring (point) end
))))
426 (defun geiser-repl--history-setup ()
427 (set (make-local-variable 'comint-input-ring-file-name
)
428 (geiser-repl--history-file))
429 (set (make-local-variable 'comint-input-ring-size
) geiser-repl-history-size
)
430 (set (make-local-variable 'comint-input-filter
) 'geiser-repl--input-filter
)
431 (set (make-local-variable 'comint-get-old-input
) 'geiser-repl--old-input
)
432 (add-hook 'kill-buffer-hook
'geiser-repl--on-kill nil t
)
433 (comint-read-input-ring t
)
434 (set-process-sentinel (get-buffer-process (current-buffer))
435 'geiser-repl--sentinel
))
438 ;;; geiser-repl mode:
440 (defun geiser-repl--bol ()
442 (when (= (point) (comint-bol)) (beginning-of-line)))
444 (defun geiser-repl--beginning-of-defun ()
446 (when comint-last-prompt-overlay
447 (narrow-to-region (overlay-end comint-last-prompt-overlay
) (point)))
448 (let ((beginning-of-defun-function nil
))
449 (beginning-of-defun))))
451 (defun geiser-repl--module-function (&optional ignore
) :f
)
453 (defun geiser-repl--doc-module ()
455 (let ((geiser-eval--get-module-function
456 (geiser-impl--method 'find-module geiser-impl--implementation
)))
457 (geiser-doc-module)))
459 (defun geiser-repl--newline-and-indent ()
462 (narrow-to-region comint-last-input-start
(point-max))
466 (defun geiser-repl--nesting-level ()
467 (let ((begin (if comint-last-prompt-overlay
468 (overlay-end comint-last-prompt-overlay
)
469 (save-excursion (geiser-repl--bol) (point)))))
471 (narrow-to-region begin
(point-max))
472 (geiser-syntax--nesting-level))))
474 (defun geiser-repl--send-input ()
475 (let* ((proc (get-buffer-process (current-buffer)))
476 (pmark (and proc
(process-mark proc
)))
477 (intxt (and pmark
(buffer-substring pmark
(point)))))
479 (when (and geiser-repl-forget-old-errors-p
480 (not (geiser-con--is-debugging)))
481 (compilation-forget-errors))
483 (when (string-match "^\\s-*$" intxt
)
484 (comint-send-string proc
(geiser-eval--scheme-str '(:ge no-values
)))
485 (comint-send-string proc
"\n")))))
487 (defun geiser-repl--maybe-send ()
491 (if (<= (geiser-repl--nesting-level) 0)
492 (geiser-repl--send-input)
494 (if geiser-repl-auto-indent-p
495 (geiser-repl--newline-and-indent)
498 (define-derived-mode geiser-repl-mode comint-mode
"REPL"
499 "Major mode for interacting with an inferior scheme repl process.
500 \\{geiser-repl-mode-map}"
501 (scheme-mode-variables)
502 (set (make-local-variable 'mode-line-process
) nil
)
503 (set (make-local-variable 'comint-use-prompt-regexp
) nil
)
504 (set (make-local-variable 'comint-prompt-read-only
)
505 geiser-repl-read-only-prompt-p
)
506 (set (make-local-variable 'beginning-of-defun-function
)
507 'geiser-repl--beginning-of-defun
)
508 (set (make-local-variable 'comint-input-ignoredups
)
509 geiser-repl-history-no-dups-p
)
510 (setq geiser-eval--get-module-function
'geiser-repl--module-function
)
511 (when geiser-repl-autodoc-p
512 (geiser--save-msg (geiser-autodoc-mode 1)))
513 (setq geiser-autodoc--inhibit-function
'geiser-con--is-debugging
)
514 (geiser-company--setup geiser-repl-company-p
)
515 (setq geiser-smart-tab-mode-string
"")
516 (geiser-smart-tab-mode t
)
517 ;; enabling compilation-shell-minor-mode without the annoying highlighter
518 (compilation-setup t
))
520 (define-key geiser-repl-mode-map
"\C-d" 'delete-char
)
521 (define-key geiser-repl-mode-map
"\C-m" 'geiser-repl--maybe-send
)
522 (define-key geiser-repl-mode-map
[return] 'geiser-repl--maybe-send)
523 (define-key geiser-repl-mode-map "\C-j" 'geiser-repl--newline-and-indent)
525 (define-key geiser-repl-mode-map "\C-a" 'geiser-repl--bol)
526 (define-key geiser-repl-mode-map (kbd "<home>") 'geiser-repl--bol)
528 (geiser-menu--defmenu repl geiser-repl-mode-map
529 ("Complete symbol" ((kbd "TAB") (kbd "M-TAB"))
530 geiser-completion--complete-symbol :enable (symbol-at-point))
531 ("Complete module name" ((kbd "C-.") (kbd "M-`"))
532 geiser-completion--complete-module :enable (symbol-at-point))
533 ("Edit symbol" "\M-." geiser-edit-symbol-at-point
534 :enable (symbol-at-point))
536 ("Switch to module..." "\C-c\C-m" switch-to-geiser-module)
537 ("Import module..." "\C-c\C-i" geiser-repl-import-module)
539 ("Previous matching input" "\M-p" comint-previous-matching-input-from-input
540 "Previous input matching current")
541 ("Next matching input" "\M-n" comint-next-matching-input-from-input
542 "Next input matching current")
543 ("Previous input" "\C-c\M-p" comint-previous-input)
544 ("Next input" "\C-c\M-n" comint-next-input)
546 (mode "Autodoc mode" ("\C-c\C-da" "\C-c\C-d\C-a") geiser-autodoc-mode)
547 ("Symbol documentation" ("\C-c\C-dd" "\C-c\C-d\C-d")
548 geiser-doc-symbol-at-point
549 "Documentation for symbol at point" :enable (symbol-at-point))
550 ("Module documentation" ("\C-c\C-dm" "\C-c\C-d\C-m") geiser-repl--doc-module
551 "Documentation for module at point" :enable (symbol-at-point))
553 ("Kill Scheme interpreter" "\C-c\C-q" geiser-repl-exit
554 :enable (geiser-repl--live-p))
555 ("Restart" "\C-c\C-z" switch-to-geiser :enable (not (geiser-repl--live-p)))
556 ("Revive REPL" "\C-c\C-k" geiser-repl-nuke
557 "Use this command if the REPL becomes irresponsive"
558 :enable (geiser-repl--live-p))
560 (custom "REPL options" geiser-repl))
562 (define-key geiser-repl-mode-map [menu-bar completion] 'undefined)
567 (defun geiser-repl--repl-list ()
569 (dolist (repl geiser-repl--repls lst)
570 (when (buffer-live-p repl)
571 (with-current-buffer repl
572 (push geiser-impl--implementation lst))))))
574 (defun geiser-repl--restore (impls)
576 (when impl (run-geiser impl))))
578 (defun geiser-repl-unload-function ()
579 (dolist (repl geiser-repl--repls)
580 (when (buffer-live-p repl)
581 (kill-buffer repl))))
584 (provide 'geiser-repl)
585 ;;; geiser-repl.el ends here