84 lines
3.3 KiB
Racket
84 lines
3.3 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/match
|
|
"fresh-line.rkt")
|
|
|
|
(provide current-sync/yield
|
|
get-interaction)
|
|
|
|
;; A channel to which a thread puts interactions that it reads using
|
|
;; the current-read-interaction handler (which can be set by a lang
|
|
;; from its configure-runtime, so, this should be compatible with
|
|
;; any lang, even non-sexpr langs).
|
|
;;
|
|
;; This is its own thread and channel for a couple reasons:
|
|
;;
|
|
;; - Issue #311. A consumer can use sync/timeout to avoid displaying a
|
|
;; prompt when multiple interactions are waiting.
|
|
;;
|
|
;; - Debugging. We can switch from the normal REPL to a debugger REPL,
|
|
;; without input being stuck inside a read call for the former.
|
|
;;
|
|
;; One wrinkle is we need to be careful about calling yield instead of
|
|
;; sync when the gui is active. See issue #326.
|
|
|
|
;; FIXME??: This used to be under the REPL custodian. Is it OK for it
|
|
;; _not_ to be, now? For instance what if user runs another file, but
|
|
;; this is still using the previous current-read-interaction value?
|
|
(define chan (make-channel))
|
|
|
|
(define (read-interaction/put-channel)
|
|
(define in ((current-get-interaction-input-port)))
|
|
(define (read-interaction)
|
|
(with-handlers ([exn:fail? values])
|
|
((current-read-interaction) (object-name in) in))) ;[^1]
|
|
(match (read-interaction)
|
|
[(? eof-object?) (sync in)] ;[^2]
|
|
[(? exn:fail? e) (channel-put chan e)] ;raise in other thread
|
|
[v (channel-put chan v)])
|
|
(read-interaction/put-channel))
|
|
|
|
(void (thread read-interaction/put-channel))
|
|
|
|
(define current-sync/yield (make-parameter sync)) ;see issue #326
|
|
|
|
(define (get-interaction prompt)
|
|
(match (or (sync/timeout 0.01 chan) ;see issue #311
|
|
(begin (display-prompt prompt)
|
|
((current-sync/yield) chan)))
|
|
[(? exn:fail? exn) (raise exn)]
|
|
[v v]))
|
|
|
|
(define (display-prompt str)
|
|
(flush-output (current-error-port))
|
|
(fresh-line)
|
|
(display str)
|
|
(display "> ")
|
|
(flush-output)
|
|
(zero-column!))
|
|
|
|
;; "Footnote" comments about make-prompt-read and many attempts to fix
|
|
;; issue #305.
|
|
;;
|
|
;; [^1]: datalog/lang expects each interaction to be EOF terminated.
|
|
;; This seems to be a DrRacket convention (?). We could make
|
|
;; that work here if we composed open-input-string with
|
|
;; read-line. But that would fail for valid multi-line
|
|
;; expressions in langs like racket/base e.g. "(+ 1\n2)". We
|
|
;; could have Emacs racket-repl-submit append some marker that
|
|
;; lets us know to combine multiple lines here -- but we'd have
|
|
;; to be careful to eat the marker and avoid combining lines
|
|
;; when the user is entering input for their own program that
|
|
;; uses `read-line` etc. Trying to be clever here is maybe not
|
|
;; smart. I _think_ the safest thing is for each lang like
|
|
;; datalog to implement current-read-interaction like it says on
|
|
;; the tin -- it can parse just one expression/statement from a
|
|
;; normal, "infinite" input port; if that means the lang parser
|
|
;; has to be tweaked for a single-expression/statement mode of
|
|
;; usage, so be it.
|
|
;;
|
|
;; [^2]: The eof-object? clause is here only for datalog/lang
|
|
;; configure-runtime.rkt. Its `the-read` returns eof if
|
|
;; char-ready? is false. WAT. Why doesn't it just block like a
|
|
;; normal read-interaction handler? Catch this and wait for more
|
|
;; input to be available before calling it again.
|