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-completion
)
16 (require 'geiser-impl
)
17 (require 'geiser-eval
)
18 (require 'geiser-connection
)
19 (require 'geiser-menu
)
20 (require 'geiser-custom
)
21 (require 'geiser-base
)
30 (defgroup geiser-repl nil
31 "Interacting with the Geiser REPL."
34 (geiser-custom--defcustom geiser-repl-use-other-window t
35 "Whether to Use a window other than the current buffer's when
36 switching to the Geiser REPL buffer."
40 (geiser-custom--defcustom geiser-repl-window-allow-split t
41 "Whether to allow window splitting when switching to the Geiser
46 (geiser-custom--defcustom geiser-repl-history-filename
(expand-file-name "~/.geiser_history")
47 "File where REPL input history is saved, so that it persists between sessions.
48 This is actually the base name: the concrete Scheme
49 implementation name gets appended to it."
53 (geiser-custom--defcustom geiser-repl-history-size comint-input-ring-size
54 "Maximum size of the saved REPL input history."
58 (geiser-custom--defcustom geiser-repl-history-no-dups-p t
59 "Whether to skip duplicates when recording history."
63 (geiser-custom--defcustom geiser-repl-autodoc-p t
64 "Whether to enable `geiser-autodoc-mode' in the REPL by default."
68 (geiser-custom--defcustom geiser-repl-company-p t
69 "Whether to use company-mode for completion, if available."
73 (geiser-custom--defcustom geiser-repl-read-only-prompt-p t
74 "Whether the REPL's prompt should be read-only."
78 (geiser-custom--defcustom geiser-repl-auto-indent-p t
79 "Whether newlines for incomplete sexps are autoindented."
83 (geiser-custom--defcustom geiser-repl-forget-old-errors-p t
84 "Whether to forget old errors upon entering a new expression.
86 When on (the default), every time a new expression is entered in
87 the REPL old error messages are flushed, and using \\[next-error]
88 afterwards will jump only to error locations produced by the new
93 (geiser-custom--defcustom geiser-repl-query-on-exit-p nil
94 "Whether to prompt for confirmation on \\[geiser-repl-exit]."
98 (geiser-custom--defcustom geiser-repl-default-host
"localhost"
99 "Default host when connecting to remote REPLs."
103 (geiser-custom--defcustom geiser-repl-default-port
37146
104 "Default port for connecting to remote REPLs."
109 ;;; Geiser REPL buffers and processes:
111 (defvar geiser-repl--repls nil
)
112 (defvar geiser-repl--closed-repls nil
)
114 (make-variable-buffer-local
115 (defvar geiser-repl--repl nil
))
117 (defsubst geiser-repl--set-this-buffer-repl
(r)
118 (setq geiser-repl--repl r
))
120 (defun geiser-repl--live-p ()
121 (and geiser-repl--repl
122 (get-buffer-process geiser-repl--repl
)))
124 (defun geiser-repl--repl/impl
(impl &optional repls
)
126 (dolist (repl (or repls geiser-repl--repls
))
127 (when (buffer-live-p repl
)
128 (with-current-buffer repl
129 (when (eq geiser-impl--implementation impl
)
130 (throw 'repl repl
)))))))
132 (defun geiser-repl--set-up-repl (impl)
133 (or (and (not impl
) geiser-repl--repl
)
134 (setq geiser-repl--repl
136 geiser-impl--implementation
137 (geiser-impl--guess))))
138 (when impl
(geiser-repl--repl/impl impl
))))))
140 (defun geiser-repl--active-impls ()
142 (dolist (repl geiser-repl--repls act
)
143 (with-current-buffer repl
144 (add-to-list 'act geiser-impl--implementation
)))))
146 (defsubst geiser-repl--repl-name
(impl)
147 (format "%s REPL" (geiser-impl--impl-str impl
)))
149 (defun geiser-repl--to-repl-buffer (impl)
150 (unless (and (eq major-mode
'geiser-repl-mode
)
151 (not (get-buffer-process (current-buffer))))
152 (let* ((old (geiser-repl--repl/impl impl geiser-repl--closed-repls
))
153 (old (and (buffer-live-p old
)
154 (not (get-buffer-process old
))
158 (generate-new-buffer (format "* %s *"
159 (geiser-repl--repl-name impl
)))))
162 (geiser-impl--set-buffer-implementation impl
)))))
164 (geiser-impl--define-caller geiser-repl--binary binary
()
165 "A variable or function returning the path to the scheme binary
166 for this implementation.")
168 (geiser-impl--define-caller geiser-repl--arglist arglist
()
169 "A function taking no arguments and returning a list of
170 arguments to be used when invoking the scheme binary.")
172 (geiser-impl--define-caller geiser-repl--prompt-regexp prompt-regexp
()
173 "A variable (or thunk returning a value) giving the regular
174 expression for this implementation's scheme prompt.")
176 (geiser-impl--define-caller
177 geiser-repl--debugger-prompt-regexp debugger-prompt-regexp
()
178 "A variable (or thunk returning a value) giving the regular
179 expression for this implementation's debugging prompt.")
181 (geiser-impl--define-caller
182 geiser-repl--debugger-preamble-regexp debugger-preamble-regexp
()
183 "A variable (or thunk returning a value) used to determine whether
184 the REPL has entered debugging mode.")
186 (geiser-impl--define-caller geiser-repl--startup startup
()
187 "Function taking no parameters that is called after the REPL
188 has been initialised. All Geiser functionality is available to
191 (geiser-impl--define-caller geiser-repl--enter-cmd enter-command
(module)
192 "Function taking a module designator and returning a REPL enter
193 module command as a string")
195 (geiser-impl--define-caller geiser-repl--import-cmd import-command
(module)
196 "Function taking a module designator and returning a REPL import
197 module command as a string")
199 (geiser-impl--define-caller geiser-repl--exit-cmd exit-command
()
200 "Function returning the REPL exit command as a string")
202 (make-variable-buffer-local
203 (defvar geiser-repl--address nil
))
205 (defsubst geiser-repl--host
() (car geiser-repl--address
))
206 (defsubst geiser-repl--port
() (cdr geiser-repl--address
))
207 (defsubst geiser-repl--remote-p
() geiser-repl--address
)
209 (defun geiser-repl--get-address ()
210 (let ((defhost (or (geiser-repl--host) geiser-repl-default-host
))
211 (defport (or (geiser-repl--port) geiser-repl-default-port
)))
212 (cons (read-string (format "Host (default %s): " defhost
) nil nil defhost
)
213 (read-number "Port: " defport
))))
215 (defun geiser-repl--save-remote-data (remote address
)
216 (setq geiser-repl--address
(and remote address
))
218 (setq header-line-format
(format "Host: %s Port: %s"
220 (geiser-repl--port)))))
222 (defun geiser-repl--start-repl (impl &optional remote
)
223 (message "Starting Geiser REPL for %s ..." impl
)
224 (geiser-repl--to-repl-buffer impl
)
225 (let ((program (if remote
(geiser-repl--get-address)
226 (geiser-repl--binary impl
)))
227 (args (geiser-repl--arglist impl
))
228 (prompt-rx (geiser-repl--prompt-regexp impl
))
229 (deb-prompt-rx (geiser-repl--debugger-prompt-regexp impl
))
230 (deb-preamble-rx (geiser-repl--debugger-preamble-regexp impl
))
231 (cname (geiser-repl--repl-name impl
)))
232 (unless (and program prompt-rx
)
233 (error "Sorry, I don't know how to start a REPL for %s" impl
))
234 (set (make-local-variable 'comint-prompt-regexp
) prompt-rx
)
235 (geiser-repl--save-remote-data remote program
)
237 (apply 'make-comint-in-buffer
238 `(,cname
,(current-buffer) ,program nil
,@args
))
239 (error (insert "Unable to start REPL:\n\n"
240 (error-message-string err
) "\n")
241 (error "Couldn't start Geiser")))
242 (geiser-repl--wait-for-prompt 10000)
243 (geiser-repl--history-setup)
244 (geiser-con--setup-connection (current-buffer)
248 (add-to-list 'geiser-repl--repls
(current-buffer))
249 (geiser-repl--set-this-buffer-repl (current-buffer))
250 (geiser-repl--startup impl
)))
252 (defun geiser-repl--process ()
253 (let ((buffer (geiser-repl--set-up-repl geiser-impl--implementation
)))
254 (or (and (buffer-live-p buffer
) (get-buffer-process buffer
))
255 (error "No Geiser REPL for this buffer (try M-x run-geiser)"))))
257 (setq geiser-eval--default-proc-function
'geiser-repl--process
)
259 (defun geiser-repl--wait-for-prompt (timeout)
260 (let ((p (point)) (seen) (buffer (current-buffer)))
261 (while (and (not seen
)
263 (get-buffer-process buffer
))
265 (setq timeout
(- timeout
100))
267 (setq seen
(re-search-forward comint-prompt-regexp nil t
)))
268 (goto-char (point-max))
269 (unless seen
(error "No prompt found!"))))
272 ;;; Interface: starting and interacting with geiser REPL:
274 (defun geiser-repl--read-impl (prompt &optional active
)
275 (geiser-impl--read-impl prompt
(and active
(geiser-repl--active-impls))))
277 (defsubst geiser-repl--only-impl-p
()
278 (and (null (cdr geiser-active-implementations
))
279 (car geiser-active-implementations
)))
281 (defun run-geiser (impl)
282 "Start a new Geiser REPL."
284 (list (or (geiser-repl--only-impl-p)
285 (and (eq major-mode
'geiser-repl-mode
)
286 geiser-impl--implementation
)
287 (geiser-repl--read-impl
288 "Start Geiser for scheme implementation: "))))
289 (geiser-repl--start-repl impl
))
291 (defun geiser-connect (impl)
292 "Start a new Geiser REPL connected to a remote Scheme process."
294 (list (or (geiser-repl--only-impl-p)
295 (and (eq major-mode
'geiser-repl-mode
)
296 geiser-impl--implementation
)
297 (geiser-repl--read-impl
298 "Scheme implementation: "))))
299 (geiser-repl--start-repl impl t
))
301 (make-variable-buffer-local
302 (defvar geiser-repl--last-scm-buffer nil
))
304 (defun switch-to-geiser (&optional ask impl buffer
)
305 "Switch to running Geiser REPL.
306 With prefix argument, ask for which one if more than one is running.
307 If no REPL is running, execute `run-geiser' to start a fresh one."
309 (let* ((impl (or impl geiser-impl--implementation
))
310 (repl (cond ((and (not ask
) (not impl
)
311 (or geiser-repl--repl
(car geiser-repl--repls
))))
312 ((and (not ask
) impl
(geiser-repl--repl/impl impl
)))))
313 (pop-up-windows geiser-repl-window-allow-split
))
314 (cond ((and (eq (current-buffer) repl
)
315 (not (eq repl buffer
))
316 (buffer-live-p geiser-repl--last-scm-buffer
))
317 (pop-to-buffer geiser-repl--last-scm-buffer
))
318 (repl (pop-to-buffer repl
))
319 ((geiser-repl--remote-p) (geiser-connect impl
))
320 (t (run-geiser impl
)))
321 (when (and buffer
(eq major-mode
'geiser-repl-mode
))
322 (setq geiser-repl--last-scm-buffer buffer
))))
324 (defalias 'geiser
'switch-to-geiser
)
326 (defun geiser-repl--send (cmd)
327 (when (and cmd
(eq major-mode
'geiser-repl-mode
))
328 (goto-char (point-max))
331 (let ((comint-input-filter (lambda (x) nil
)))
332 (comint-send-input nil t
))))
334 (defun switch-to-geiser-module (&optional module buffer
)
335 "Switch to running Geiser REPL and try to enter a given module."
337 (let* ((module (or module
338 (geiser-completion--read-module "Switch to module: ")))
340 (geiser-repl--enter-cmd geiser-impl--implementation
342 (unless (eq major-mode
'geiser-repl-mode
)
343 (switch-to-geiser nil nil
(or buffer
(current-buffer))))
344 (geiser-repl--send cmd
)))
346 (defun geiser-repl-import-module (&optional module
)
347 "Import a given module in the current namespace of the REPL."
349 (let* ((module (or module
350 (geiser-completion--read-module "Import module: ")))
352 (geiser-repl--import-cmd geiser-impl--implementation
354 (switch-to-geiser nil nil
(current-buffer))
355 (geiser-repl--send cmd
)))
357 (defun geiser-repl-exit (&optional arg
)
358 "Exit the current REPL.
359 With a prefix argument, force exit by killing the scheme process."
361 (when (or (not geiser-repl-query-on-exit-p
)
362 (y-or-n-p "Really quit this REPL? "))
363 (let ((cmd (and (not arg
)
364 (geiser-repl--exit-cmd geiser-impl--implementation
))))
366 (when (stringp cmd
) (geiser-repl--send cmd
))
367 (comint-kill-subjob)))))
369 (defun geiser-repl-nuke ()
370 "Try this command if the REPL becomes unresponsive."
372 (goto-char (point-max))
373 (comint-kill-region comint-last-input-start
(point))
374 (comint-redirect-cleanup)
375 (geiser-con--setup-connection (current-buffer)
377 geiser-con--debugging-prompt-regexp
378 geiser-con--debugging-preamble-regexp
))
381 ;;; REPL history and clean-up:
383 (defsubst geiser-repl--history-file
()
384 (format "%s.%s" geiser-repl-history-filename geiser-impl--implementation
))
386 (defun geiser-repl--on-quit ()
387 (comint-write-input-ring)
388 (let ((cb (current-buffer))
389 (impl geiser-impl--implementation
)
390 (comint-prompt-read-only nil
))
391 (setq geiser-repl--repls
(remove cb geiser-repl--repls
))
392 (dolist (buffer (buffer-list))
393 (when (buffer-live-p buffer
)
394 (with-current-buffer buffer
395 (when (and (eq geiser-impl--implementation impl
)
396 (equal cb geiser-repl--repl
))
397 (geiser-repl--set-up-repl geiser-impl--implementation
)))))))
399 (defun geiser-repl--sentinel (proc event
)
400 (let ((pb (process-buffer proc
)))
401 (when (buffer-live-p pb
)
402 (with-current-buffer pb
403 (let ((comint-prompt-read-only nil
)
404 (comint-input-ring-file-name (geiser-repl--history-file)))
405 (geiser-repl--on-quit)
406 (push pb geiser-repl--closed-repls
)
407 (when (buffer-name (current-buffer))
408 (comint-kill-region comint-last-input-start
(point))
409 (insert "\nIt's been nice interacting with you!\n")
410 (insert "Press C-c C-z to bring me back.\n" )))))))
412 (defun geiser-repl--on-kill ()
413 (geiser-repl--on-quit)
414 (setq geiser-repl--closed-repls
415 (remove (current-buffer) geiser-repl--closed-repls
)))
417 (defun geiser-repl--input-filter (str)
418 (not (or (geiser-con--is-debugging)
419 (string-match "^\\s *$" str
)
420 (string-match "^,quit *$" str
))))
422 (defun geiser-repl--old-input ()
426 (buffer-substring (point) end
))))
428 (defun geiser-repl--history-setup ()
429 (set (make-local-variable 'comint-input-ring-file-name
)
430 (geiser-repl--history-file))
431 (set (make-local-variable 'comint-input-ring-size
) geiser-repl-history-size
)
432 (set (make-local-variable 'comint-input-filter
) 'geiser-repl--input-filter
)
433 (set (make-local-variable 'comint-get-old-input
) 'geiser-repl--old-input
)
434 (add-hook 'kill-buffer-hook
'geiser-repl--on-kill nil t
)
435 (comint-read-input-ring t
)
436 (set-process-sentinel (get-buffer-process (current-buffer))
437 'geiser-repl--sentinel
))
440 ;;; geiser-repl mode:
442 (defun geiser-repl--bol ()
444 (when (= (point) (comint-bol)) (beginning-of-line)))
446 (defun geiser-repl--beginning-of-defun ()
448 (when comint-last-prompt-overlay
449 (narrow-to-region (overlay-end comint-last-prompt-overlay
) (point)))
450 (let ((beginning-of-defun-function nil
))
451 (beginning-of-defun))))
453 (defun geiser-repl--module-function (&optional ignore
) :f
)
455 (defun geiser-repl--doc-module ()
457 (let ((geiser-eval--get-module-function
458 (geiser-impl--method 'find-module geiser-impl--implementation
)))
459 (geiser-doc-module)))
461 (defun geiser-repl--newline-and-indent ()
464 (narrow-to-region comint-last-input-start
(point-max))
468 (defun geiser-repl--last-prompt-end ()
469 (if comint-last-prompt-overlay
470 (overlay-end comint-last-prompt-overlay
)
471 (save-excursion (geiser-repl--bol) (point))))
473 (defun geiser-repl--last-prompt-start ()
474 (if comint-last-prompt-overlay
475 (overlay-start comint-last-prompt-overlay
)
476 (save-excursion (geiser-repl--bol) (point))))
478 (defun geiser-repl--nesting-level ()
480 (narrow-to-region (geiser-repl--last-prompt-end) (point-max))
481 (geiser-syntax--nesting-level)))
483 (defun geiser-repl--send-input ()
484 (let* ((proc (get-buffer-process (current-buffer)))
485 (pmark (and proc
(process-mark proc
)))
486 (intxt (and pmark
(buffer-substring pmark
(point)))))
488 (when (and geiser-repl-forget-old-errors-p
489 (not (geiser-con--is-debugging)))
490 (compilation-forget-errors))
492 (when (string-match "^\\s-*$" intxt
)
493 (comint-send-string proc
(geiser-eval--scheme-str '(:ge no-values
)))
494 (comint-send-string proc
"\n")))))
496 (defun geiser-repl--maybe-send ()
499 (cond ((< p
(geiser-repl--last-prompt-start))
500 (ignore-errors (compile-goto-error)))
501 ((progn (end-of-line) (<= (geiser-repl--nesting-level) 0))
502 (geiser-repl--send-input))
504 (if geiser-repl-auto-indent-p
505 (geiser-repl--newline-and-indent)
508 (defun geiser-repl--tab (n)
509 "If we're after the last prompt, complete symbol or indent (if
510 there's no symbol at point). Otherwise, go to next error in the REPL
513 (if (> (point) (geiser-repl--last-prompt-end))
514 (geiser-completion--maybe-complete)
515 (compilation-next-error n
)))
517 (defun geiser-repl--previous-error (n)
518 "Go to previous error in the REPL buffer."
520 (compilation-next-error (- n
)))
522 (define-derived-mode geiser-repl-mode comint-mode
"REPL"
523 "Major mode for interacting with an inferior scheme repl process.
524 \\{geiser-repl-mode-map}"
525 (scheme-mode-variables)
526 (set (make-local-variable 'mode-line-process
) nil
)
527 (set (make-local-variable 'comint-use-prompt-regexp
) nil
)
528 (set (make-local-variable 'comint-prompt-read-only
)
529 geiser-repl-read-only-prompt-p
)
530 (set (make-local-variable 'beginning-of-defun-function
)
531 'geiser-repl--beginning-of-defun
)
532 (set (make-local-variable 'comint-input-ignoredups
)
533 geiser-repl-history-no-dups-p
)
534 (setq geiser-eval--get-module-function
'geiser-repl--module-function
)
535 (when geiser-repl-autodoc-p
536 (geiser--save-msg (geiser-autodoc-mode 1)))
537 (setq geiser-autodoc--inhibit-function
'geiser-con--is-debugging
)
538 (geiser-company--setup geiser-repl-company-p
)
539 ;; enabling compilation-shell-minor-mode without the annoying highlighter
540 (compilation-setup t
))
542 (define-key geiser-repl-mode-map
"\C-d" 'delete-char
)
543 (define-key geiser-repl-mode-map
"\C-m" 'geiser-repl--maybe-send
)
544 (define-key geiser-repl-mode-map
[return] 'geiser-repl--maybe-send)
545 (define-key geiser-repl-mode-map "\C-j" 'geiser-repl--newline-and-indent)
546 (define-key geiser-repl-mode-map (kbd "TAB") 'geiser-repl--tab)
547 (define-key geiser-repl-mode-map [backtab] 'geiser-repl--previous-error)
549 (define-key geiser-repl-mode-map "\C-a" 'geiser-repl--bol)
550 (define-key geiser-repl-mode-map (kbd "<home>") 'geiser-repl--bol)
552 (geiser-menu--defmenu repl geiser-repl-mode-map
553 ("Complete symbol" ((kbd "M-TAB"))
554 geiser-repl--tab :enable (symbol-at-point))
555 ("Complete module name" ((kbd "C-.") (kbd "M-`"))
556 geiser-completion--complete-module :enable (symbol-at-point))
557 ("Edit symbol" "\M-." geiser-edit-symbol-at-point
558 :enable (symbol-at-point))
560 ("Switch to module..." "\C-c\C-m" switch-to-geiser-module)
561 ("Import module..." "\C-c\C-i" geiser-repl-import-module)
563 ("Previous matching input" "\M-p" comint-previous-matching-input-from-input
564 "Previous input matching current")
565 ("Next matching input" "\M-n" comint-next-matching-input-from-input
566 "Next input matching current")
567 ("Previous input" "\C-c\M-p" comint-previous-input)
568 ("Next input" "\C-c\M-n" comint-next-input)
570 (mode "Autodoc mode" ("\C-c\C-da" "\C-c\C-d\C-a") geiser-autodoc-mode)
571 ("Symbol documentation" ("\C-c\C-dd" "\C-c\C-d\C-d")
572 geiser-doc-symbol-at-point
573 "Documentation for symbol at point" :enable (symbol-at-point))
574 ("Module documentation" ("\C-c\C-dm" "\C-c\C-d\C-m") geiser-repl--doc-module
575 "Documentation for module at point" :enable (symbol-at-point))
577 ("Kill Scheme interpreter" "\C-c\C-q" geiser-repl-exit
578 :enable (geiser-repl--live-p))
579 ("Restart" "\C-c\C-z" switch-to-geiser :enable (not (geiser-repl--live-p)))
580 ("Revive REPL" "\C-c\C-k" geiser-repl-nuke
581 "Use this command if the REPL becomes irresponsive"
582 :enable (geiser-repl--live-p))
584 (custom "REPL options" geiser-repl))
586 (define-key geiser-repl-mode-map [menu-bar completion] 'undefined)
591 (defun geiser-repl--repl-list ()
593 (dolist (repl geiser-repl--repls lst)
594 (when (buffer-live-p repl)
595 (with-current-buffer repl
596 (push geiser-impl--implementation lst))))))
598 (defun geiser-repl--restore (impls)
600 (when impl (run-geiser impl))))
602 (defun geiser-repl-unload-function ()
603 (dolist (repl geiser-repl--repls)
604 (when (buffer-live-p repl)
605 (kill-buffer repl))))
608 (provide 'geiser-repl)
609 ;;; geiser-repl.el ends here