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
)
17 (require 'geiser-impl
)
18 (require 'geiser-eval
)
19 (require 'geiser-connection
)
20 (require 'geiser-menu
)
21 (require 'geiser-custom
)
22 (require 'geiser-base
)
31 (defgroup geiser-repl nil
32 "Interacting with the Geiser REPL."
35 (geiser-custom--defcustom geiser-repl-use-other-window t
36 "Whether to Use a window other than the current buffer's when
37 switching to the Geiser REPL buffer."
41 (geiser-custom--defcustom geiser-repl-window-allow-split t
42 "Whether to allow window splitting when switching to the Geiser
47 (geiser-custom--defcustom geiser-repl-history-filename
(expand-file-name "~/.geiser_history")
48 "File where REPL input history is saved, so that it persists between sessions.
49 This is actually the base name: the concrete Scheme
50 implementation name gets appended to it."
54 (geiser-custom--defcustom geiser-repl-history-size comint-input-ring-size
55 "Maximum size of the saved REPL input history."
59 (geiser-custom--defcustom geiser-repl-history-no-dups-p t
60 "Whether to skip duplicates when recording history."
64 (geiser-custom--defcustom geiser-repl-autodoc-p t
65 "Whether to enable `geiser-autodoc-mode' in the REPL by default."
69 (geiser-custom--defcustom geiser-repl-company-p t
70 "Whether to use company-mode for completion, if available."
74 (geiser-custom--defcustom geiser-repl-read-only-prompt-p t
75 "Whether the REPL's prompt should be read-only."
79 (geiser-custom--defcustom geiser-repl-auto-indent-p t
80 "Whether newlines for incomplete sexps are autoindented."
84 (geiser-custom--defcustom geiser-repl-forget-old-errors-p t
85 "Whether to forget old errors upon entering a new expression.
87 When on (the default), every time a new expression is entered in
88 the REPL old error messages are flushed, and using \\[next-error]
89 afterwards will jump only to error locations produced by the new
94 (geiser-custom--defcustom geiser-repl-query-on-exit-p nil
95 "Whether to prompt for confirmation on \\[geiser-repl-exit]."
99 (geiser-custom--defcustom geiser-repl-default-host
"localhost"
100 "Default host when connecting to remote REPLs."
104 (geiser-custom--defcustom geiser-repl-default-port
37146
105 "Default port for connecting to remote REPLs."
110 ;;; Implementation-dependent parameters
112 (geiser-impl--define-caller
113 geiser-repl--debugger-prompt-regexp debugger-prompt-regexp
()
114 "A variable (or thunk returning a value) giving the regular
115 expression for this implementation's debugging prompt.")
117 (geiser-impl--define-caller geiser-repl--startup startup
()
118 "Function taking no parameters that is called after the REPL
119 has been initialised. All Geiser functionality is available to
122 (geiser-impl--define-caller geiser-repl--enter-cmd enter-command
(module)
123 "Function taking a module designator and returning a REPL enter
124 module command as a string")
126 (geiser-impl--define-caller geiser-repl--import-cmd import-command
(module)
127 "Function taking a module designator and returning a REPL import
128 module command as a string")
130 (geiser-impl--define-caller geiser-repl--exit-cmd exit-command
()
131 "Function returning the REPL exit command as a string")
134 ;;; Geiser REPL buffers and processes:
136 (defvar geiser-repl--repls nil
)
137 (defvar geiser-repl--closed-repls nil
)
139 (make-variable-buffer-local
140 (defvar geiser-repl--repl nil
))
142 (defsubst geiser-repl--set-this-buffer-repl
(r)
143 (setq geiser-repl--repl r
))
145 (defun geiser-repl--live-p ()
146 (and geiser-repl--repl
147 (get-buffer-process geiser-repl--repl
)))
149 (defun geiser-repl--repl/impl
(impl &optional repls
)
151 (dolist (repl (or repls geiser-repl--repls
))
152 (when (buffer-live-p repl
)
153 (with-current-buffer repl
154 (when (eq geiser-impl--implementation impl
)
155 (throw 'repl repl
)))))))
157 (defun geiser-repl--set-up-repl (impl)
158 (or (and (not impl
) geiser-repl--repl
)
159 (setq geiser-repl--repl
161 geiser-impl--implementation
162 (geiser-impl--guess))))
163 (when impl
(geiser-repl--repl/impl impl
))))))
165 (defun geiser-repl--active-impls ()
167 (dolist (repl geiser-repl--repls act
)
168 (with-current-buffer repl
169 (add-to-list 'act geiser-impl--implementation
)))))
171 (defsubst geiser-repl--repl-name
(impl)
172 (format "%s REPL" (geiser-impl--impl-str impl
)))
174 (defun geiser-repl--to-repl-buffer (impl)
175 (unless (and (eq major-mode
'geiser-repl-mode
)
176 (not (get-buffer-process (current-buffer))))
177 (let* ((old (geiser-repl--repl/impl impl geiser-repl--closed-repls
))
178 (old (and (buffer-live-p old
)
179 (not (get-buffer-process old
))
183 (generate-new-buffer (format "* %s *"
184 (geiser-repl--repl-name impl
)))))
187 (geiser-impl--set-buffer-implementation impl
)))))
189 (defun geiser-repl--read-impl (prompt &optional active
)
190 (geiser-impl--read-impl prompt
(and active
(geiser-repl--active-impls))))
192 (defsubst geiser-repl--only-impl-p
()
193 (and (null (cdr geiser-active-implementations
))
194 (car geiser-active-implementations
)))
196 (defun geiser-repl--get-impl (prompt)
197 (or (geiser-repl--only-impl-p)
198 (and (eq major-mode
'geiser-repl-mode
) geiser-impl--implementation
)
199 (geiser-repl--read-impl prompt
)))
204 (make-variable-buffer-local
205 (defvar geiser-repl--address nil
))
207 (make-variable-buffer-local
208 (defvar geiser-repl--connection nil
))
210 (make-variable-buffer-local
211 (defvar geiser-repl--remote-p nil
))
213 (make-variable-buffer-local
214 (defvar geiser-repl--inferior-buffer nil
))
216 (defsubst geiser-repl--host
() (car geiser-repl--address
))
217 (defsubst geiser-repl--port
() (cdr geiser-repl--address
))
219 (defun geiser-repl--get-address (&optional host port
)
220 (let ((defhost (or (geiser-repl--host) geiser-repl-default-host
))
221 (defport (or (geiser-repl--port) geiser-repl-default-port
)))
223 (read-string (format "Host (default %s): " defhost
)
225 (or port
(read-number "Port: " defport
)))))
227 (defun geiser-repl--save-remote-data (address remote
)
228 (setq geiser-repl--address address
)
229 (setq geiser-repl--remote-p remote
)
230 (setq header-line-format
(and remote
231 (format "Host: %s Port: %s"
233 (geiser-repl--port)))))
235 (defun geiser-repl--start-repl (impl host port remote
)
236 (message "Starting Geiser REPL for %s ..." impl
)
237 (geiser-repl--to-repl-buffer impl
)
238 (let ((address (geiser-repl--get-address host port
))
239 (prompt-rx (geiser-inf--prompt-regexp impl
))
240 (deb-prompt-rx (geiser-repl--debugger-prompt-regexp impl
))
241 (cname (geiser-repl--repl-name impl
)))
243 (error "Sorry, I don't know how to start a REPL for %s" impl
))
244 (geiser-repl--save-remote-data address remote
)
247 (setq geiser-repl--connection
248 (geiser-con--open-connection (car address
)
252 (set (make-local-variable 'comint-prompt-regexp
)
253 (geiser-con--connection-eot geiser-repl--connection
))
254 (apply 'make-comint-in-buffer
`(,cname
,(current-buffer) ,address
)))
255 (error (insert "Unable to start REPL:\n\n"
256 (error-message-string err
) "\n")
257 (error "Couldn't start Geiser")))
258 (geiser-inf--wait-for-prompt 10000)
259 (geiser-repl--history-setup)
260 (add-to-list 'geiser-repl--repls
(current-buffer))
261 (geiser-repl--set-this-buffer-repl (current-buffer))
262 (geiser-repl--startup impl
)
263 (message "Geiser REPL up and running!")))
265 (defun geiser-repl--connection ()
266 (let ((buffer (geiser-repl--set-up-repl geiser-impl--implementation
)))
267 (or (and (buffer-live-p buffer
)
268 (get-buffer-process buffer
)
269 (with-current-buffer buffer geiser-repl--connection
))
270 (error "No Geiser REPL for this buffer (try M-x run-geiser)"))))
272 (setq geiser-eval--default-connection-function
'geiser-repl--connection
)
274 (defun geiser-repl--send (cmd)
275 (when (and cmd
(eq major-mode
'geiser-repl-mode
))
276 (goto-char (point-max))
279 (let ((comint-input-filter (lambda (x) nil
)))
280 (comint-send-input nil t
))))
283 ;;; REPL history and clean-up:
285 (defsubst geiser-repl--history-file
()
286 (format "%s.%s" geiser-repl-history-filename geiser-impl--implementation
))
288 (defun geiser-repl--quit-inf ()
289 (when (buffer-live-p geiser-repl--inferior-buffer
)
290 (with-current-buffer geiser-repl--inferior-buffer
291 (let ((geiser-repl-query-on-exit-p nil
)) (geiser-repl-exit))
294 (defun geiser-repl--on-quit ()
295 (comint-write-input-ring)
296 (let ((cb (current-buffer))
297 (impl geiser-impl--implementation
)
298 (comint-prompt-read-only nil
))
299 (ignore-errors (geiser-con--connection-close geiser-repl--connection
))
300 (geiser-repl--quit-inf)
301 (setq geiser-repl--repls
(remove cb geiser-repl--repls
))
302 (dolist (buffer (buffer-list))
303 (when (buffer-live-p buffer
)
304 (with-current-buffer buffer
305 (when (and (eq geiser-impl--implementation impl
)
306 (equal cb geiser-repl--repl
))
307 (geiser-repl--set-up-repl geiser-impl--implementation
)))))))
309 (defun geiser-repl--sentinel (proc event
)
310 (let ((pb (process-buffer proc
)))
311 (when (buffer-live-p pb
)
312 (with-current-buffer pb
313 (let ((comint-prompt-read-only nil
)
314 (comint-input-ring-file-name (geiser-repl--history-file)))
315 (geiser-repl--on-quit)
316 (push pb geiser-repl--closed-repls
)
317 (when (buffer-name (current-buffer))
318 (comint-kill-region comint-last-input-start
(point))
319 (insert "\nIt's been nice interacting with you!\n")
320 (insert "Press C-c C-z to bring me back.\n" )))))))
322 (defun geiser-repl--on-kill ()
323 (geiser-repl--on-quit)
324 (setq geiser-repl--closed-repls
325 (remove (current-buffer) geiser-repl--closed-repls
)))
327 (defun geiser-repl--input-filter (str)
328 (not (or ;; (geiser-con--is-debugging)
329 (string-match "^\\s *$" str
)
330 (string-match "^,quit *$" str
))))
332 (defun geiser-repl--old-input ()
336 (buffer-substring (point) end
))))
338 (defun geiser-repl--history-setup ()
339 (set (make-local-variable 'comint-input-ring-file-name
)
340 (geiser-repl--history-file))
341 (set (make-local-variable 'comint-input-ring-size
) geiser-repl-history-size
)
342 (set (make-local-variable 'comint-input-filter
) 'geiser-repl--input-filter
)
343 (set (make-local-variable 'comint-get-old-input
) 'geiser-repl--old-input
)
344 (add-hook 'kill-buffer-hook
'geiser-repl--on-kill nil t
)
345 (comint-read-input-ring t
)
346 (set-process-sentinel (get-buffer-process (current-buffer))
347 'geiser-repl--sentinel
))
350 ;;; geiser-repl mode:
352 (defun geiser-repl--bol ()
354 (when (= (point) (comint-bol)) (beginning-of-line)))
356 (defun geiser-repl--beginning-of-defun ()
358 (when comint-last-prompt-overlay
359 (narrow-to-region (overlay-end comint-last-prompt-overlay
) (point)))
360 (let ((beginning-of-defun-function nil
))
361 (beginning-of-defun))))
363 (defun geiser-repl--module-function (&optional ignore
) :f
)
365 (defun geiser-repl--doc-module ()
367 (let ((geiser-eval--get-module-function
368 (geiser-impl--method 'find-module geiser-impl--implementation
)))
369 (geiser-doc-module)))
371 (defun geiser-repl--newline-and-indent ()
374 (narrow-to-region comint-last-input-start
(point-max))
378 (defun geiser-repl--last-prompt-end ()
379 (if comint-last-prompt-overlay
380 (overlay-end comint-last-prompt-overlay
)
381 (save-excursion (geiser-repl--bol) (point))))
383 (defun geiser-repl--last-prompt-start ()
384 (if comint-last-prompt-overlay
385 (overlay-start comint-last-prompt-overlay
)
386 (save-excursion (geiser-repl--bol) (point))))
388 (defun geiser-repl--nesting-level ()
390 (narrow-to-region (geiser-repl--last-prompt-end) (point-max))
391 (geiser-syntax--nesting-level)))
393 (defun geiser-repl--send-input ()
394 (let* ((proc (get-buffer-process (current-buffer)))
395 (pmark (and proc
(process-mark proc
)))
396 (intxt (and pmark
(buffer-substring pmark
(point)))))
398 (when (and geiser-repl-forget-old-errors-p
399 ;;; (not (geiser-con--is-debugging)))
401 (compilation-forget-errors))
403 (when (string-match "^\\s-*$" intxt
)
404 (comint-send-string proc
(geiser-eval--scheme-str '(:ge no-values
)))
405 (comint-send-string proc
"\n")))))
407 (defun geiser-repl--maybe-send ()
410 (cond ((< p
(geiser-repl--last-prompt-start))
411 (ignore-errors (compile-goto-error)))
412 ((progn (end-of-line) (<= (geiser-repl--nesting-level) 0))
413 (geiser-repl--send-input))
415 (if geiser-repl-auto-indent-p
416 (geiser-repl--newline-and-indent)
419 (defun geiser-repl--tab (n)
420 "If we're after the last prompt, complete symbol or indent (if
421 there's no symbol at point). Otherwise, go to next error in the REPL
424 (if (> (point) (geiser-repl--last-prompt-end))
425 (geiser-completion--maybe-complete)
426 (compilation-next-error n
)))
428 (defun geiser-repl--previous-error (n)
429 "Go to previous error in the REPL buffer."
431 (compilation-next-error (- n
)))
433 (define-derived-mode geiser-repl-mode comint-mode
"REPL"
434 "Major mode for interacting with an inferior scheme repl process.
435 \\{geiser-repl-mode-map}"
436 (scheme-mode-variables)
437 (set (make-local-variable 'mode-line-process
) nil
)
438 (set (make-local-variable 'comint-use-prompt-regexp
) nil
)
439 (set (make-local-variable 'comint-prompt-read-only
)
440 geiser-repl-read-only-prompt-p
)
441 (set (make-local-variable 'beginning-of-defun-function
)
442 'geiser-repl--beginning-of-defun
)
443 (set (make-local-variable 'comint-input-ignoredups
)
444 geiser-repl-history-no-dups-p
)
445 (setq geiser-eval--get-module-function
'geiser-repl--module-function
)
446 (when geiser-repl-autodoc-p
447 (geiser--save-msg (geiser-autodoc-mode 1)))
448 (geiser-company--setup geiser-repl-company-p
)
449 ;; enabling compilation-shell-minor-mode without the annoying highlighter
450 (compilation-setup t
))
452 (define-key geiser-repl-mode-map
"\C-d" 'delete-char
)
453 (define-key geiser-repl-mode-map
"\C-m" 'geiser-repl--maybe-send
)
454 (define-key geiser-repl-mode-map
[return] 'geiser-repl--maybe-send)
455 (define-key geiser-repl-mode-map "\C-j" 'geiser-repl--newline-and-indent)
456 (define-key geiser-repl-mode-map (kbd "TAB") 'geiser-repl--tab)
457 (define-key geiser-repl-mode-map [backtab] 'geiser-repl--previous-error)
459 (define-key geiser-repl-mode-map "\C-a" 'geiser-repl--bol)
460 (define-key geiser-repl-mode-map (kbd "<home>") 'geiser-repl--bol)
462 (geiser-menu--defmenu repl geiser-repl-mode-map
463 ("Complete symbol" ((kbd "M-TAB"))
464 geiser-repl--tab :enable (symbol-at-point))
465 ("Complete module name" ((kbd "C-.") (kbd "M-`"))
466 geiser-completion--complete-module :enable (symbol-at-point))
467 ("Edit symbol" "\M-." geiser-edit-symbol-at-point
468 :enable (symbol-at-point))
470 ("Switch to module..." "\C-c\C-m" switch-to-geiser-module)
471 ("Import module..." "\C-c\C-i" geiser-repl-import-module)
473 ("Previous matching input" "\M-p" comint-previous-matching-input-from-input
474 "Previous input matching current")
475 ("Next matching input" "\M-n" comint-next-matching-input-from-input
476 "Next input matching current")
477 ("Previous input" "\C-c\M-p" comint-previous-input)
478 ("Next input" "\C-c\M-n" comint-next-input)
480 (mode "Autodoc mode" ("\C-c\C-da" "\C-c\C-d\C-a") geiser-autodoc-mode)
481 ("Symbol documentation" ("\C-c\C-dd" "\C-c\C-d\C-d")
482 geiser-doc-symbol-at-point
483 "Documentation for symbol at point" :enable (symbol-at-point))
484 ("Module documentation" ("\C-c\C-dm" "\C-c\C-d\C-m") geiser-repl--doc-module
485 "Documentation for module at point" :enable (symbol-at-point))
487 ("Kill Scheme interpreter" "\C-c\C-q" geiser-repl-exit
488 :enable (geiser-repl--live-p))
489 ("Restart" "\C-c\C-z" switch-to-geiser :enable (not (geiser-repl--live-p)))
491 (custom "REPL options" geiser-repl))
493 (define-key geiser-repl-mode-map [menu-bar completion] 'undefined)
498 (defun run-geiser (impl)
499 "Start a new Geiser REPL."
501 (list (geiser-repl--get-impl "Start Geiser for scheme implementation: ")))
502 (let ((b/p (geiser-inf--run-scheme impl)))
503 (setq geiser-repl--inferior-buffer (car b/p))
504 (geiser-repl--start-repl impl "localhost" (cdr b/p) nil)))
506 (defalias 'geiser 'run-geiser)
508 (defun geiser-connect (impl &optional host port)
509 "Start a new Geiser REPL connected to a remote Scheme process."
511 (list (geiser-repl--get-impl "Connect to Scheme implementation: ")))
512 (geiser-repl--start-repl impl host port t))
514 (make-variable-buffer-local
515 (defvar geiser-repl--last-scm-buffer nil))
517 (defun switch-to-geiser (&optional ask impl buffer)
518 "Switch to running Geiser REPL.
519 With prefix argument, ask for which one if more than one is running.
520 If no REPL is running, execute `run-geiser' to start a fresh one."
522 (let* ((impl (or impl geiser-impl--implementation))
523 (in-repl (eq major-mode 'geiser-repl-mode))
524 (in-live-repl (and in-repl (get-buffer-process (current-buffer))))
525 (repl (cond ((and (not ask)
528 (or geiser-repl--repl (car geiser-repl--repls))))
532 (geiser-repl--repl/impl impl)))))
533 (pop-up-windows geiser-repl-window-allow-split))
534 (cond ((or in-live-repl
535 (and (eq (current-buffer) repl) (not (eq repl buffer))))
536 (when (buffer-live-p geiser-repl--last-scm-buffer)
537 (pop-to-buffer geiser-repl--last-scm-buffer)))
538 (repl (pop-to-buffer repl))
539 (geiser-repl--remote-p (geiser-connect impl))
540 (t (run-geiser impl)))
541 (when (and buffer (eq major-mode 'geiser-repl-mode))
542 (setq geiser-repl--last-scm-buffer buffer))))
544 (defun switch-to-geiser-module (&optional module buffer)
545 "Switch to running Geiser REPL and try to enter a given module."
547 (let* ((module (or module
548 (geiser-completion--read-module
549 "Switch to module (default top-level): ")))
551 (geiser-repl--enter-cmd geiser-impl--implementation
553 (unless (eq major-mode 'geiser-repl-mode)
554 (switch-to-geiser nil nil (or buffer (current-buffer))))
555 (geiser-repl--send cmd)))
557 (defun geiser-repl-import-module (&optional module)
558 "Import a given module in the current namespace of the REPL."
560 (let* ((module (or module
561 (geiser-completion--read-module "Import module: ")))
563 (geiser-repl--import-cmd geiser-impl--implementation
565 (switch-to-geiser nil nil (current-buffer))
566 (geiser-repl--send cmd)))
568 (defun geiser-repl-exit (&optional arg)
569 "Exit the current REPL.
570 With a prefix argument, force exit by killing the scheme process."
572 (when (or (not geiser-repl-query-on-exit-p)
573 (y-or-n-p "Really quit this REPL? "))
574 (let ((cmd (and (not arg)
575 (geiser-repl--exit-cmd geiser-impl--implementation))))
577 (when (stringp cmd) (geiser-repl--send cmd))
578 (comint-kill-subjob)))))
583 (defun geiser-repl--repl-list ()
585 (dolist (repl geiser-repl--repls lst)
586 (when (buffer-live-p repl)
587 (with-current-buffer repl
588 (push geiser-impl--implementation lst))))))
590 (defun geiser-repl--restore (impls)
592 (when impl (run-geiser impl))))
594 (defun geiser-repl-unload-function ()
595 (dolist (repl geiser-repl--repls)
596 (when (buffer-live-p repl)
597 (kill-buffer repl))))
600 (provide 'geiser-repl)
601 ;;; geiser-repl.el ends here