1 ;;; geiser-repl.el --- Geiser's REPL
3 ;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019, 2020 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
)
14 (require 'geiser-autodoc
)
15 (require 'geiser-edit
)
16 (require 'geiser-completion
)
17 (require 'geiser-syntax
)
18 (require 'geiser-impl
)
19 (require 'geiser-eval
)
20 (require 'geiser-connection
)
21 (require 'geiser-menu
)
22 (require 'geiser-image
)
23 (require 'geiser-custom
)
24 (require 'geiser-base
)
34 (defgroup geiser-repl nil
35 "Interacting with the Geiser REPL."
38 (geiser-custom--defcustom geiser-repl-buffer-name-function
39 'geiser-repl-buffer-name
40 "Function used to define the name of a REPL buffer.
41 The function is called with a single argument - an implementation
42 symbol (e.g., `guile', `chicken', etc.)."
43 :type
'(choice (function-item geiser-repl-buffer-name
)
44 (function :tag
"Other function"))
47 (geiser-custom--defcustom geiser-repl-use-other-window t
48 "Whether to Use a window other than the current buffer's when
49 switching to the Geiser REPL buffer."
53 (geiser-custom--defcustom geiser-repl-window-allow-split t
54 "Whether to allow window splitting when switching to the Geiser
59 (geiser-custom--defcustom geiser-repl-history-filename
60 (expand-file-name "~/.geiser_history")
61 "File where REPL input history is saved, so that it persists between sessions.
63 This is actually the base name: the concrete Scheme
64 implementation name gets appended to it."
68 (geiser-custom--defcustom geiser-repl-history-size comint-input-ring-size
69 "Maximum size of the saved REPL input history."
73 (geiser-custom--defcustom geiser-repl-history-no-dups-p t
74 "Whether to skip duplicates when recording history."
78 (geiser-custom--defcustom geiser-repl-save-debugging-history-p nil
79 "Whether to skip debugging input in REPL history.
81 By default, REPL interactions while scheme is in the debugger are
82 not added to the REPL command history. Set this variable to t to
87 (geiser-custom--defcustom geiser-repl-autodoc-p t
88 "Whether to enable `geiser-autodoc-mode' in the REPL by default."
92 (geiser-custom--defcustom geiser-repl-company-p t
93 "Whether to use company-mode for completion, if available."
97 (geiser-custom--defcustom geiser-repl-read-only-prompt-p t
98 "Whether the REPL's prompt should be read-only."
102 (geiser-custom--defcustom geiser-repl-read-only-output-p t
103 "Whether the REPL's output should be read-only."
107 (geiser-custom--defcustom geiser-repl-highlight-output-p nil
108 "Whether to syntax highlight REPL output."
112 (geiser-custom--defcustom geiser-repl-auto-indent-p t
113 "Whether newlines for incomplete sexps are autoindented."
117 (geiser-custom--defcustom geiser-repl-send-on-return-p t
118 "Sends input to REPL when ENTER is pressed in a balanced S-expression,
119 regardless of cursor positioning.
121 When off, pressing ENTER inside a balance S-expression will
122 introduce a new line without sending input to the inferior
123 Scheme process. This option is useful when using minor modes
124 which might do parentheses balancing, or when entering additional
125 arguments inside an existing expression.
127 When on (the default), pressing ENTER inside a balanced S-expression
128 will send the input to the inferior Scheme process regardless of the
133 (geiser-custom--defcustom geiser-repl-forget-old-errors-p t
134 "Whether to forget old errors upon entering a new expression.
136 When on (the default), every time a new expression is entered in
137 the REPL old error messages are flushed, and using \\[next-error]
138 afterwards will jump only to error locations produced by the new
143 (geiser-custom--defcustom geiser-repl-skip-version-check-p nil
144 "Whether to skip version checks for the Scheme executable.
146 When set, Geiser won't check the version of the Scheme
147 interpreter when starting a REPL, saving a few tenths of a
153 (geiser-custom--defcustom geiser-repl-query-on-exit-p nil
154 "Whether to prompt for confirmation on \\[geiser-repl-exit]."
158 (geiser-custom--defcustom geiser-repl-delete-last-output-on-exit-p nil
159 "Whether to delete partial outputs when the REPL's process exits."
163 (geiser-custom--defcustom geiser-repl-query-on-kill-p t
164 "Whether to prompt for confirmation when killing a REPL buffer with
169 (geiser-custom--defcustom geiser-repl-default-host
"localhost"
170 "Default host when connecting to remote REPLs."
174 (geiser-custom--defcustom geiser-repl-default-port
37146
175 "Default port for connecting to remote REPLs."
179 (geiser-custom--defcustom geiser-repl-startup-time
10000
180 "Time, in milliseconds, to wait for Racket to startup.
181 If you have a slow system, try to increase this time."
185 (geiser-custom--defcustom geiser-repl-inline-images-p t
186 "Whether to display inline images in the REPL."
190 (geiser-custom--defcustom geiser-repl-auto-display-images-p t
191 "Whether to automatically invoke the external viewer to display
192 images popping up in the REPL.
194 See also `geiser-debug-auto-display-images-p'."
198 (geiser-custom--defface repl-input
199 'comint-highlight-input geiser-repl
"evaluated input highlighting")
201 (geiser-custom--defface repl-output
202 'font-lock-string-face geiser-repl
"REPL output")
204 (geiser-custom--defface repl-prompt
205 'comint-highlight-prompt geiser-repl
"REPL prompt")
209 ;;; Implementation-dependent parameters
211 (geiser-impl--define-caller geiser-repl--binary binary
()
212 "A variable or function returning the path to the scheme binary
213 for this implementation.")
215 (geiser-impl--define-caller geiser-repl--arglist arglist
()
216 "A function taking no arguments and returning a list of
217 arguments to be used when invoking the scheme binary.")
219 (geiser-impl--define-caller geiser-repl--prompt-regexp prompt-regexp
()
220 "A variable (or thunk returning a value) giving the regular
221 expression for this implementation's geiser scheme prompt.")
223 (geiser-impl--define-caller
224 geiser-repl--debugger-prompt-regexp debugger-prompt-regexp
()
225 "A variable (or thunk returning a value) giving the regular
226 expression for this implementation's debugging prompt.")
228 (geiser-impl--define-caller geiser-repl--startup repl-startup
(remote)
229 "Function taking no parameters that is called after the REPL
230 has been initialised. All Geiser functionality is available to
233 (geiser-impl--define-caller geiser-repl--enter-cmd enter-command
(module)
234 "Function taking a module designator and returning a REPL enter
235 module command as a string")
237 (geiser-impl--define-caller geiser-repl--import-cmd import-command
(module)
238 "Function taking a module designator and returning a REPL import
239 module command as a string")
241 (geiser-impl--define-caller geiser-repl--exit-cmd exit-command
()
242 "Function returning the REPL exit command as a string")
244 (geiser-impl--define-caller geiser-repl--version version-command
(binary)
245 "Function returning the version of the corresponding scheme process,
246 given its full path.")
248 (geiser-impl--define-caller geiser-repl--min-version minimum-version
()
249 "A variable providing the minimum required scheme version, as a string.")
252 ;;; Geiser REPL buffers and processes:
254 (defvar geiser-repl--repls nil
)
255 (defvar geiser-repl--closed-repls nil
)
257 (defvar geiser-repl--last-output-start nil
)
258 (defvar geiser-repl--last-output-end nil
)
260 (make-variable-buffer-local
261 (defvar geiser-repl--repl nil
))
263 (defsubst geiser-repl--set-this-buffer-repl
(r)
264 (setq geiser-repl--repl r
))
266 (defun geiser-repl--live-p ()
267 (and geiser-repl--repl
268 (get-buffer-process geiser-repl--repl
)))
270 (defun geiser-repl--repl/impl
(impl &optional repls
)
272 (dolist (repl (or repls geiser-repl--repls
))
273 (when (buffer-live-p repl
)
274 (with-current-buffer repl
275 (when (eq geiser-impl--implementation impl
)
276 (throw 'repl repl
)))))))
278 (defun geiser-repl--set-up-repl (impl)
279 (or (and (not impl
) geiser-repl--repl
)
280 (setq geiser-repl--repl
282 geiser-impl--implementation
283 (geiser-impl--guess))))
284 (when impl
(geiser-repl--repl/impl impl
))))))
286 (defun geiser-repl--active-impls ()
288 (dolist (repl geiser-repl--repls act
)
289 (with-current-buffer repl
290 (add-to-list 'act geiser-impl--implementation
)))))
292 (defsubst geiser-repl--repl-name
(impl)
293 (format "%s REPL" (geiser-impl--impl-str impl
)))
295 (defsubst geiser-repl--buffer-name
(impl)
296 (funcall geiser-repl-buffer-name-function impl
))
298 (defun geiser-repl-buffer-name (impl)
299 "Return default name of the REPL buffer for implementation IMPL."
300 (format "* %s *" (geiser-repl--repl-name impl
)))
302 (defun geiser-repl--switch-to-buffer (buffer)
303 (unless (eq buffer
(current-buffer))
304 (let ((pop-up-windows geiser-repl-window-allow-split
))
305 (if geiser-repl-use-other-window
306 (switch-to-buffer-other-window buffer
)
307 (switch-to-buffer buffer
)))))
309 (defun geiser-repl--to-repl-buffer (impl)
310 (unless (and (eq major-mode
'geiser-repl-mode
)
311 (eq geiser-impl--implementation impl
)
312 (not (get-buffer-process (current-buffer))))
313 (let* ((old (geiser-repl--repl/impl impl geiser-repl--closed-repls
))
314 (old (and (buffer-live-p old
)
315 (not (get-buffer-process old
))
317 (geiser-repl--switch-to-buffer
318 (or old
(generate-new-buffer (geiser-repl--buffer-name impl
))))
321 (geiser-impl--set-buffer-implementation impl
)
322 (geiser-syntax--add-kws t
)))))
324 (defun geiser-repl--read-impl (prompt &optional active
)
325 (geiser-impl--read-impl prompt
(and active
(geiser-repl--active-impls))))
327 (defsubst geiser-repl--only-impl-p
()
328 (and (null (cdr geiser-active-implementations
))
329 (car geiser-active-implementations
)))
331 (defun geiser-repl--get-impl (prompt)
332 (or (geiser-repl--only-impl-p)
333 (and (eq major-mode
'geiser-repl-mode
) geiser-impl--implementation
)
334 (geiser-repl--read-impl prompt
)))
339 (defun geiser-repl--last-prompt-end ()
340 (cond ((and (boundp 'comint-last-prompt
) (markerp (cdr comint-last-prompt
)))
341 (marker-position (cdr comint-last-prompt
)))
342 ((and (boundp 'comint-last-prompt-overlay
) comint-last-prompt-overlay
)
343 (overlay-end comint-last-prompt-overlay
))
346 (min (+ 1 (point)) (point-max))))))
348 (defun geiser-repl--last-prompt-start ()
349 (cond ((and (boundp 'comint-last-prompt
) (markerp (car comint-last-prompt
)))
350 (marker-position (car comint-last-prompt
)))
351 ((and (boundp 'comint-last-prompt-overlay
) comint-last-prompt-overlay
)
352 (overlay-start comint-last-prompt-overlay
))
353 (t (save-excursion (geiser-repl--bol) (point)))))
358 (make-variable-buffer-local
359 (defvar geiser-repl--address nil
))
361 (make-variable-buffer-local
362 (defvar geiser-repl--connection nil
))
364 (defun geiser-repl--local-p ()
365 "Return non-nil, if current REPL is local (connected to socket)."
366 (stringp geiser-repl--address
))
368 (defun geiser-repl--remote-p ()
369 "Return non-nil, if current REPL is remote (connected to host:port)."
370 (consp geiser-repl--address
))
372 (defsubst geiser-repl--host
() (car geiser-repl--address
))
373 (defsubst geiser-repl--port
() (cdr geiser-repl--address
))
375 (defun geiser-repl--read-address (&optional host port
)
376 (let ((defhost (or (geiser-repl--host) geiser-repl-default-host
))
377 (defport (or (geiser-repl--port) geiser-repl-default-port
)))
379 (read-string (format "Host (default %s): " defhost
)
381 (or port
(read-number "Port: " defport
)))))
383 (defun geiser-repl--autodoc-mode (n)
384 (when (or geiser-repl-autodoc-p
(< n
0))
385 (geiser--save-msg (geiser-autodoc-mode n
))))
387 (defun geiser-repl--save-remote-data (address)
388 (setq geiser-repl--address address
)
389 (cond ((consp address
)
390 (setq header-line-format
391 (format "Host: %s Port: %s"
393 (geiser-repl--port))))
395 (setq header-line-format
396 (format "Socket: %s" address
)))))
398 (defun geiser-repl--fontify-output-region (beg end
)
399 "Apply highlighting to a REPL output region."
400 (remove-text-properties beg end
'(font-lock-face nil face nil
))
401 (if geiser-repl-highlight-output-p
402 (geiser-syntax--fontify-syntax-region beg end
)
403 (geiser-repl--fontify-plaintext beg end
)))
405 (defun geiser-repl--fontify-plaintext (start end
)
406 "Fontify REPL output plainly."
409 '(font-lock-fontified t
411 font-lock-multiline t
412 font-lock-face geiser-font-lock-repl-output
)))
414 (defun geiser-repl--narrow-to-prompt ()
415 "Narrow to active prompt region and return t, otherwise returns nil."
416 (let* ((proc (get-buffer-process (current-buffer)))
417 (pmark (and proc
(process-mark proc
)))
418 (intxt (when (>= (point) (marker-position pmark
))
420 (if comint-eol-on-send
421 (if comint-use-prompt-regexp
423 (goto-char (field-end))))
424 (buffer-substring pmark
(point)))))
425 (prompt-beg (marker-position pmark
))
426 (prompt-end (+ prompt-beg
(length intxt
))))
427 (when (> (length intxt
) 0)
428 (narrow-to-region prompt-beg prompt-end
)
431 (defun geiser-repl--wrap-fontify-region-function (beg end
&optional loudly
)
433 (when (geiser-repl--narrow-to-prompt)
434 (let ((font-lock-dont-widen t
))
435 (font-lock-default-fontify-region (point-min) (point-max) nil
)))))
437 (defun geiser-repl--wrap-unfontify-region-function (beg end
&optional loudly
)
439 (when (geiser-repl--narrow-to-prompt)
440 (let ((font-lock-dont-widen t
))
441 (font-lock-default-unfontify-region (point-min) (point-max))))))
443 (defun geiser-repl--output-filter (txt)
444 (let ((mark-output nil
))
446 (goto-char (point-max))
447 (re-search-backward comint-prompt-regexp
)
448 ;; move to start of line to prevent accidentally marking a REPL prompt
450 ;; Only mark output which:
451 ;; a) is not on the REPL output line
452 ;; b) has at least one character
454 ;; This makes the magic number for distance 3 -- as the newline
455 ;; after executing expression is also counted. This is due to the point
456 ;; being set before comint-send-input.
458 ;; Restriction a) applies due to our inability to distinguish between
459 ;; output from the REPL, and the REPL prompt output.
460 (let ((distance (- (point) geiser-repl--last-output-start
)))
463 (set-marker geiser-repl--last-output-end
(point)))))
465 (with-silent-modifications
466 (add-text-properties (1+ geiser-repl--last-output-start
)
467 geiser-repl--last-output-end
468 `(read-only ,geiser-repl-read-only-output-p
))
469 (geiser-repl--fontify-output-region geiser-repl--last-output-start
470 geiser-repl--last-output-end
)
471 (geiser--font-lock-ensure geiser-repl--last-output-start
472 geiser-repl--last-output-end
))))
474 (geiser-con--connection-update-debugging geiser-repl--connection txt
)
475 (geiser-image--replace-images geiser-repl-inline-images-p
476 geiser-repl-auto-display-images-p
)
477 (when (string-match-p (geiser-con--connection-prompt geiser-repl--connection
)
479 (geiser-autodoc--disinhibit-autodoc)))
481 (defun geiser-repl--check-version (impl)
482 (when (not geiser-repl-skip-version-check-p
)
483 (let ((v (geiser-repl--version impl
(geiser-repl--binary impl
)))
484 (r (geiser-repl--min-version impl
)))
485 (when (and v r
(geiser--version< v r
))
486 (error "Geiser requires %s version %s but detected %s" impl r v
)))))
488 (defun geiser-repl--start-repl (impl address
)
489 (message "Starting Geiser REPL ...")
490 (when (not address
) (geiser-repl--check-version impl
))
491 (let ((buffer (current-buffer)))
492 (geiser-repl--to-repl-buffer impl
)
493 (setq geiser-repl--last-scm-buffer buffer
))
495 (goto-char (point-max))
496 (geiser-repl--autodoc-mode -
1)
497 (let* ((prompt-rx (geiser-repl--prompt-regexp impl
))
498 (deb-prompt-rx (geiser-repl--debugger-prompt-regexp impl
))
499 (prompt (geiser-con--combined-prompt prompt-rx deb-prompt-rx
)))
501 (error "Sorry, I don't know how to start a REPL for %s" impl
))
502 (geiser-repl--save-remote-data address
)
503 (geiser-repl--start-scheme impl address prompt
)
504 (geiser-repl--quit-setup)
505 (geiser-repl--history-setup)
506 (add-to-list 'geiser-repl--repls
(current-buffer))
507 (geiser-repl--set-this-buffer-repl (current-buffer))
508 (setq geiser-repl--connection
509 (geiser-con--make-connection (get-buffer-process (current-buffer))
512 (geiser-repl--startup impl address
)
513 (geiser-repl--autodoc-mode 1)
514 (geiser-company--setup geiser-repl-company-p
)
515 (add-hook 'comint-output-filter-functions
516 'geiser-repl--output-filter
519 (set-process-query-on-exit-flag (get-buffer-process (current-buffer))
520 geiser-repl-query-on-kill-p
)
521 (message "%s up and running!" (geiser-repl--repl-name impl
))))
523 (defun geiser-repl--start-scheme (impl address prompt
)
524 (setq comint-prompt-regexp prompt
)
525 (let* ((name (geiser-repl--repl-name impl
))
526 (buff (current-buffer))
527 (args (cond ((consp address
) (list address
))
528 ((stringp address
) '(()))
529 (t `(,(geiser-repl--binary impl
)
531 ,@(geiser-repl--arglist impl
))))))
533 (if (and address
(stringp address
))
534 ;; Connect over a Unix-domain socket.
535 (let ((proc (make-network-process :name
(buffer-name buff
)
539 ;; brittleness warning: this is stuff
540 ;; make-comint-in-buffer sets up, via comint-exec, when
541 ;; it creates its own process, something we're doing
542 ;; here by ourselves.
543 (set-process-filter proc
'comint-output-filter
)
544 (goto-char (point-max))
545 (set-marker (process-mark proc
) (point)))
546 (apply 'make-comint-in-buffer
`(,name
,buff
,@args
)))
547 (error (insert "Unable to start REPL:\n"
548 (error-message-string err
)
550 (error "Couldn't start Geiser: %s" err
)))
551 (geiser-repl--wait-for-prompt geiser-repl-startup-time
)))
553 (defun geiser-repl--wait-for-prompt (timeout)
554 (let ((p (point)) (seen) (buffer (current-buffer)))
555 (while (and (not seen
)
557 (get-buffer-process buffer
))
559 (setq timeout
(- timeout
100))
561 (setq seen
(re-search-forward comint-prompt-regexp nil t
)))
562 (goto-char (point-max))
563 (unless seen
(error "%s" "No prompt found!"))))
565 (defun geiser-repl--is-debugging ()
566 (let ((dp (geiser-con--connection-debug-prompt geiser-repl--connection
)))
569 (goto-char (geiser-repl--last-prompt-start))
570 (re-search-forward dp
(geiser-repl--last-prompt-end) t
)))))
572 (defun geiser-repl--connection* ()
573 (let ((buffer (geiser-repl--set-up-repl geiser-impl--implementation
)))
574 (and (buffer-live-p buffer
)
575 (get-buffer-process buffer
)
576 (with-current-buffer buffer geiser-repl--connection
))))
578 (defun geiser-repl--connection ()
579 (or (geiser-repl--connection*)
580 (error "No Geiser REPL for this buffer (try M-x run-geiser)")))
582 (setq geiser-eval--default-connection-function
'geiser-repl--connection
)
584 (defun geiser-repl--prepare-send ()
585 (geiser-image--clean-cache)
586 (geiser-autodoc--inhibit-autodoc)
587 (geiser-con--connection-deactivate geiser-repl--connection
))
589 (defun geiser-repl--send (cmd &optional save-history
)
590 "Send CMD input string to the current REPL buffer.
591 If SAVE-HISTORY is non-nil, save CMD in the REPL history."
592 (when (and cmd
(eq major-mode
'geiser-repl-mode
))
593 (geiser-repl--prepare-send)
594 (goto-char (point-max))
597 (let ((comint-input-filter (if save-history
600 (comint-send-input nil t
))))
602 (defun geiser-repl-interrupt ()
604 (when (get-buffer-process (current-buffer))
605 (interrupt-process nil comint-ptyp
)))
610 (defconst geiser-repl--history-separator
"\n}{\n")
612 (defsubst geiser-repl--history-file
()
613 (format "%s.%s" geiser-repl-history-filename geiser-impl--implementation
))
615 (defun geiser-repl--read-input-ring ()
616 (let ((comint-input-ring-file-name (geiser-repl--history-file))
617 (comint-input-ring-separator geiser-repl--history-separator
)
618 (buffer-file-coding-system 'utf-8
))
619 (comint-read-input-ring t
)))
621 (defun geiser-repl--write-input-ring ()
622 (let ((comint-input-ring-file-name (geiser-repl--history-file))
623 (comint-input-ring-separator geiser-repl--history-separator
)
624 (buffer-file-coding-system 'utf-8
))
625 (comint-write-input-ring)))
627 (defun geiser-repl--history-setup ()
628 (set (make-local-variable 'comint-input-ring-size
) geiser-repl-history-size
)
629 (set (make-local-variable 'comint-input-filter
) 'geiser-repl--input-filter
)
630 (geiser-repl--read-input-ring))
635 (defun geiser-repl--on-quit ()
636 (geiser-repl--write-input-ring)
637 (let ((cb (current-buffer))
638 (impl geiser-impl--implementation
)
639 (comint-prompt-read-only nil
))
640 (geiser-con--connection-deactivate geiser-repl--connection t
)
641 (geiser-con--connection-close geiser-repl--connection
)
642 (setq geiser-repl--repls
(remove cb geiser-repl--repls
))
643 (dolist (buffer (buffer-list))
644 (when (buffer-live-p buffer
)
645 (with-current-buffer buffer
646 (when (and (eq geiser-impl--implementation impl
)
647 (equal cb geiser-repl--repl
))
648 (geiser-repl--set-up-repl geiser-impl--implementation
)))))))
650 (defun geiser-repl--sentinel (proc event
)
651 (let ((pb (process-buffer proc
)))
652 (when (buffer-live-p pb
)
653 (with-current-buffer pb
654 (let ((comint-prompt-read-only nil
)
655 (comint-input-ring-file-name (geiser-repl--history-file))
656 (comint-input-ring-separator geiser-repl--history-separator
))
657 (geiser-repl--on-quit)
658 (push pb geiser-repl--closed-repls
)
659 (goto-char (point-max))
660 (when geiser-repl-delete-last-output-on-exit-p
661 (comint-kill-region comint-last-input-start
(point)))
662 (insert "\nIt's been nice interacting with you!\n")
663 (insert "Press C-c C-z to bring me back.\n"))))))
665 (defun geiser-repl--on-kill ()
666 (geiser-repl--on-quit)
667 (setq geiser-repl--closed-repls
668 (remove (current-buffer) geiser-repl--closed-repls
)))
670 (defun geiser-repl--input-filter (str)
671 (not (or (and (not geiser-repl-save-debugging-history-p
)
672 (geiser-repl--is-debugging))
673 (string-match "^\\s *$" str
)
674 (string-match "^,quit *$" str
))))
676 (defun geiser-repl--old-input ()
680 (buffer-substring (point) end
))))
682 (defun geiser-repl--quit-setup ()
683 (add-hook 'kill-buffer-hook
'geiser-repl--on-kill nil t
)
684 (set-process-sentinel (get-buffer-process (current-buffer))
685 'geiser-repl--sentinel
))
688 ;;; geiser-repl mode:
690 (defun geiser-repl--bol ()
692 (when (= (point) (comint-bol)) (beginning-of-line)))
694 (defun geiser-repl--beginning-of-defun ()
696 (narrow-to-region (geiser-repl--last-prompt-end) (point))
697 (let ((beginning-of-defun-function nil
))
698 (beginning-of-defun))))
700 (defun geiser-repl--module-function (&optional module
)
701 (if (and module geiser-eval--get-impl-module
)
702 (funcall geiser-eval--get-impl-module module
)
705 (defun geiser-repl--doc-module ()
707 (let ((geiser-eval--get-module-function
708 (geiser-impl--method 'find-module geiser-impl--implementation
)))
709 (geiser-doc-module)))
711 (defun geiser-repl--newline-and-indent ()
714 (narrow-to-region comint-last-input-start
(point-max))
718 (defun geiser-repl--nesting-level ()
720 (narrow-to-region (geiser-repl--last-prompt-end) (point-max))
721 (geiser-syntax--nesting-level)))
723 (defun geiser-repl--is-input ()
724 (not (eq (field-at-pos (point)) 'output
)))
726 (defun geiser-repl--grab-input ()
727 (let ((pos (comint-bol)))
728 (goto-char (point-max))
729 (insert (field-string-no-properties pos
))))
731 (defun geiser-repl--send-input ()
732 (set-marker geiser-repl--last-output-start
(point-max))
734 (let* ((proc (get-buffer-process (current-buffer)))
735 (pmark (and proc
(process-mark proc
)))
736 (intxt (and pmark
(buffer-substring pmark
(point))))
739 (and geiser-repl-forget-old-errors-p
740 (not (geiser-repl--is-debugging))
741 (compilation-forget-errors))
742 (geiser-repl--prepare-send)
744 (when (string-match "^\\s-*$" intxt
)
745 (comint-send-string proc
(geiser-eval--scheme-str '(:ge no-values
)))
746 (comint-send-string proc
"\n")))))
748 (defun geiser-repl--maybe-send ()
751 (cond ((< p
(geiser-repl--last-prompt-start))
752 (if (geiser-repl--is-input)
753 (geiser-repl--grab-input)
754 (ignore-errors (compile-goto-error))))
755 ((let ((inhibit-field-text-motion t
))
756 (when geiser-repl-send-on-return-p
758 (<= (geiser-repl--nesting-level) 0))
759 (geiser-repl--send-input))
761 (if geiser-repl-auto-indent-p
762 (geiser-repl--newline-and-indent)
765 (defun geiser-repl-tab-dwim (n)
766 "If we're after the last prompt, complete symbol or indent (if
767 there's no symbol at point). Otherwise, go to next error in the REPL
770 (if (>= (point) (geiser-repl--last-prompt-end))
771 (or (completion-at-point)
773 (compilation-next-error n
)))
775 (defun geiser-repl--previous-error (n)
776 "Go to previous error in the REPL buffer."
778 (compilation-next-error (- n
)))
780 (defun geiser-repl-clear-buffer ()
781 "Delete the output generated by the scheme process."
783 (let ((inhibit-read-only t
))
784 (delete-region (point-min) (geiser-repl--last-prompt-start))
785 (when (< (point) (geiser-repl--last-prompt-end))
786 (goto-char (geiser-repl--last-prompt-end)))
789 (define-derived-mode geiser-repl-mode comint-mode
"REPL"
790 "Major mode for interacting with an inferior scheme repl process.
791 \\{geiser-repl-mode-map}"
792 (scheme-mode-variables)
793 (set (make-local-variable 'geiser-repl--last-output-start
) (point-marker))
794 (set (make-local-variable 'geiser-repl--last-output-end
) (point-marker))
795 (set (make-local-variable 'face-remapping-alist
)
796 '((comint-highlight-prompt geiser-font-lock-repl-prompt
)
797 (comint-highlight-input geiser-font-lock-repl-input
)))
798 (set (make-local-variable 'mode-line-process
) nil
)
799 (set (make-local-variable 'comint-use-prompt-regexp
) nil
)
800 (set (make-local-variable 'comint-prompt-read-only
)
801 geiser-repl-read-only-prompt-p
)
802 (setq comint-process-echoes nil
)
803 (set (make-local-variable 'beginning-of-defun-function
)
804 'geiser-repl--beginning-of-defun
)
805 (set (make-local-variable 'comint-input-ignoredups
)
806 geiser-repl-history-no-dups-p
)
807 (setq geiser-eval--get-module-function
'geiser-repl--module-function
)
808 (geiser-completion--setup t
)
809 (setq geiser-smart-tab-mode-string
"")
810 (geiser-smart-tab-mode t
)
812 (setq-local font-lock-fontify-region-function
813 #'geiser-repl--wrap-fontify-region-function
)
814 (setq-local font-lock-unfontify-region-function
815 #'geiser-repl--wrap-unfontify-region-function
)
817 ;; enabling compilation-shell-minor-mode without the annoying highlighter
818 (compilation-setup t
))
820 (define-key geiser-repl-mode-map
"\C-d" 'delete-char
)
821 (define-key geiser-repl-mode-map
"\C-m" 'geiser-repl--maybe-send
)
822 (define-key geiser-repl-mode-map
[return] 'geiser-repl--maybe-send)
823 (define-key geiser-repl-mode-map "\C-j" 'geiser-repl--newline-and-indent)
824 (define-key geiser-repl-mode-map (kbd "TAB") 'geiser-repl-tab-dwim)
825 (define-key geiser-repl-mode-map [backtab] 'geiser-repl--previous-error)
827 (define-key geiser-repl-mode-map "\C-a" 'geiser-repl--bol)
828 (define-key geiser-repl-mode-map (kbd "<home>") 'geiser-repl--bol)
830 (geiser-menu--defmenu repl geiser-repl-mode-map
831 ("Complete symbol" ((kbd "M-TAB"))
832 completion-at-point :enable (geiser--symbol-at-point))
833 ("Complete module name" ((kbd "C-.") (kbd "M-`"))
834 geiser-completion--complete-module :enable (geiser--symbol-at-point))
835 ("Edit symbol" "\M-." geiser-edit-symbol-at-point
836 :enable (geiser--symbol-at-point))
838 ("Load scheme file..." "\C-c\C-l" geiser-load-file)
839 ("Switch to module..." "\C-c\C-m" switch-to-geiser-module)
840 ("Import module..." "\C-c\C-i" geiser-repl-import-module)
841 ("Add to load path..." "\C-c\C-r" geiser-add-to-load-path)
843 ("Previous matching input" "\M-p" comint-previous-matching-input-from-input
844 "Previous input matching current")
845 ("Next matching input" "\M-n" comint-next-matching-input-from-input
846 "Next input matching current")
847 ("Previous prompt" "\C-c\C-p" geiser-repl-previous-prompt)
848 ("Next prompt" "\C-c\C-n" geiser-repl-next-prompt)
849 ("Previous input" "\C-c\M-p" comint-previous-input)
850 ("Next input" "\C-c\M-n" comint-next-input)
852 ("Interrupt evaluation" ("\C-c\C-k" "\C-c\C-c" "\C-ck")
853 geiser-repl-interrupt)
855 (mode "Autodoc mode" ("\C-c\C-da" "\C-c\C-d\C-a") geiser-autodoc-mode)
856 ("Symbol documentation" ("\C-c\C-dd" "\C-c\C-d\C-d")
857 geiser-doc-symbol-at-point
858 "Documentation for symbol at point" :enable (geiser--symbol-at-point))
859 ("Lookup symbol in manual" ("\C-c\C-di" "\C-c\C-d\C-i")
860 geiser-doc-look-up-manual
861 "Documentation for symbol at point" :enable (geiser--symbol-at-point))
862 ("Module documentation" ("\C-c\C-dm" "\C-c\C-d\C-m") geiser-repl--doc-module
863 "Documentation for module at point" :enable (geiser--symbol-at-point))
865 ("Clear buffer" "\C-c\M-o" geiser-repl-clear-buffer
866 "Clean up REPL buffer, leaving just a lonely prompt")
867 ("Toggle ()/[]" ("\C-c\C-e\C-[" "\C-c\C-e[") geiser-squarify)
868 ("Insert λ" ("\C-c\\" "\C-c\C-\\") geiser-insert-lambda)
870 ("Kill Scheme interpreter" "\C-c\C-q" geiser-repl-exit
871 :enable (geiser-repl--live-p))
872 ("Restart" "\C-c\C-z" switch-to-geiser :enable (not (geiser-repl--live-p)))
875 (custom "REPL options" geiser-repl))
877 (define-key geiser-repl-mode-map [menu-bar completion] 'undefined)
882 (defun run-geiser (impl)
883 "Start a new Geiser REPL."
885 (list (geiser-repl--get-impl "Start Geiser for scheme implementation: ")))
886 (geiser-repl--start-repl impl nil))
888 (defalias 'geiser 'run-geiser)
890 (defun geiser-connect (impl &optional host port)
891 "Start a new Geiser REPL connected to a remote Scheme process."
893 (list (geiser-repl--get-impl "Connect to Scheme implementation: ")))
894 (geiser-repl--start-repl impl (geiser-repl--read-address host port)))
896 (defun geiser-connect-local (impl socket)
897 "Start a new Geiser REPL connected to a remote Scheme process
898 over a Unix-domain socket."
900 (list (geiser-repl--get-impl "Connect to Scheme implementation: ")
901 (expand-file-name (read-file-name "Socket file name: "))))
902 (geiser-repl--start-repl impl socket))
904 (make-variable-buffer-local
905 (defvar geiser-repl--last-scm-buffer nil))
907 (defun geiser-repl--maybe-remember-scm-buffer (buffer)
909 (eq 'scheme-mode (with-current-buffer buffer major-mode))
910 (eq major-mode 'geiser-repl-mode))
911 (setq geiser-repl--last-scm-buffer buffer)))
913 (defun switch-to-geiser (&optional ask impl buffer)
914 "Switch to running Geiser REPL.
916 If REPL is the current buffer, switch to the previously used
919 With prefix argument, ask for which one if more than one is running.
920 If no REPL is running, execute `run-geiser' to start a fresh one."
922 (let* ((impl (or impl geiser-impl--implementation))
923 (in-repl (eq major-mode 'geiser-repl-mode))
924 (in-live-repl (and in-repl (get-buffer-process (current-buffer))))
927 (geiser-repl--repl/impl impl)
928 (or geiser-repl--repl (car geiser-repl--repls))))))
930 (when (and (not (eq repl buffer))
931 (buffer-live-p geiser-repl--last-scm-buffer))
932 (geiser-repl--switch-to-buffer geiser-repl--last-scm-buffer)))
933 (repl (geiser-repl--switch-to-buffer repl))
934 ((geiser-repl--remote-p)
935 (geiser-connect impl (geiser-repl--host) (geiser-repl--port)))
936 ((geiser-repl--local-p)
937 (geiser-connect-local impl geiser-repl--address))
938 (impl (run-geiser impl))
939 (t (call-interactively 'run-geiser)))
940 (geiser-repl--maybe-remember-scm-buffer buffer)))
942 (defun switch-to-geiser-module (&optional module buffer)
943 "Switch to running Geiser REPL and try to enter a given module."
945 (let* ((module (or module
946 (geiser-completion--read-module
947 "Switch to module (default top-level): ")))
949 (geiser-repl--enter-cmd geiser-impl--implementation
951 (unless (eq major-mode 'geiser-repl-mode)
952 (switch-to-geiser nil nil (or buffer (current-buffer))))
953 (geiser-repl--send cmd)))
955 (defun geiser-repl-import-module (&optional module)
956 "Import a given module in the current namespace of the REPL."
958 (let* ((module (or module
959 (geiser-completion--read-module "Import module: ")))
961 (geiser-repl--import-cmd geiser-impl--implementation
963 (switch-to-geiser nil nil (current-buffer))
964 (geiser-repl--send cmd)))
966 (defun geiser-repl-exit (&optional arg)
967 "Exit the current REPL.
968 With a prefix argument, force exit by killing the scheme process."
970 (when (or (not geiser-repl-query-on-exit-p)
971 (y-or-n-p "Really quit this REPL? "))
972 (geiser-con--connection-deactivate geiser-repl--connection t)
973 (let ((cmd (and (not arg)
974 (geiser-repl--exit-cmd geiser-impl--implementation))))
976 (when (stringp cmd) (geiser-repl--send cmd))
977 (comint-kill-subjob)))))
979 (defun geiser-repl-next-prompt (n)
983 (re-search-forward comint-prompt-regexp nil 'go n)))
985 (defun geiser-repl-previous-prompt (n)
989 (when (re-search-backward comint-prompt-regexp nil 'go n)
990 (goto-char (match-end 0)))))
995 (defun geiser-repl--repl-list ()
997 (dolist (repl geiser-repl--repls lst)
998 (when (buffer-live-p repl)
999 (with-current-buffer repl
1000 (push (cons geiser-impl--implementation
1001 geiser-repl--address)
1004 (defun geiser-repl--restore (impls)
1005 (dolist (impl impls)
1008 (geiser-repl--start-repl (car impl) (cdr impl))
1009 (error (message (error-message-string err)))))))
1011 (defun geiser-repl-unload-function ()
1012 (dolist (repl geiser-repl--repls)
1013 (when (buffer-live-p repl)
1014 (with-current-buffer repl
1015 (let ((geiser-repl-query-on-exit-p nil)) (geiser-repl-exit))
1020 (provide 'geiser-repl)
1024 ;; After providing 'geiser-repl, so that impls can use us.
1025 (mapc 'geiser-impl--load-impl geiser-active-implementations)