Add :repl/quit and :exit gestures to the REPL
^D (EOF) exits cleanly in canonical mode but some terminals and editors don't deliver it, leaving the user stuck. Accepting :repl/quit or :exit as the first form of a line gives a reliable keyword exit that works everywhere. The check parses the line with read-string rather than checking the evaluated value, so a nested value that happens to print as the keyword can't trigger an exit.
This commit is contained in:
parent
9e53ba4248
commit
649e33fe3b
2 changed files with 33 additions and 7 deletions
|
|
@ -67,5 +67,26 @@ else
|
||||||
fails=$((fails + 1))
|
fails=$((fails + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# REPL must exit on :repl/quit / :exit — a reliable exit that works in any
|
||||||
|
# terminal, unlike ^D (which some terminals/editors don't deliver as EOF).
|
||||||
|
# Pipe: an evaluable form, the quit keyword, then a sentinel that must NOT run.
|
||||||
|
repl_out="$(printf '(+ 1000 23)\n:repl/quit\n(* 999 9)\n' | bin/joltc repl 2>/dev/null)"
|
||||||
|
if printf '%s' "$repl_out" | grep -q '1023' && ! printf '%s' "$repl_out" | grep -q '8991'; then
|
||||||
|
pass=$((pass + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: repl should exit on :repl/quit before later forms"
|
||||||
|
printf '%s\n' "$repl_out" | sed 's/^/ | /'
|
||||||
|
fails=$((fails + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
repl_out="$(printf '(- 2024 1)\n:exit\n(* 999 9)\n' | bin/joltc repl 2>/dev/null)"
|
||||||
|
if printf '%s' "$repl_out" | grep -q '2023' && ! printf '%s' "$repl_out" | grep -q '8991'; then
|
||||||
|
pass=$((pass + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: repl should exit on :exit before later forms"
|
||||||
|
printf '%s\n' "$repl_out" | sed 's/^/ | /'
|
||||||
|
fails=$((fails + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
echo "cli smoke: $pass passed, $fails failed"
|
echo "cli smoke: $pass passed, $fails failed"
|
||||||
[ "$fails" -eq 0 ]
|
[ "$fails" -eq 0 ]
|
||||||
|
|
|
||||||
|
|
@ -93,17 +93,22 @@
|
||||||
;; loaded — same context a run gets, so (require '[some.lib]) works in the REPL.
|
;; loaded — same context a run gets, so (require '[some.lib]) works in the REPL.
|
||||||
(try (apply-project! (deps/resolve-project (project-dir)))
|
(try (apply-project! (deps/resolve-project (project-dir)))
|
||||||
(catch :default _ nil))
|
(catch :default _ nil))
|
||||||
(println ";; jolt repl — ^D to exit")
|
(println ";; jolt repl — :repl/quit or ^D to exit")
|
||||||
(loop []
|
(loop []
|
||||||
(print "user=> ") (flush)
|
(print "user=> ") (flush)
|
||||||
(let [line (read-line)]
|
(let [line (read-line)]
|
||||||
(when line
|
(when line
|
||||||
|
;; :repl/quit / :exit exit the loop — a reliable gesture that works in any
|
||||||
|
;; terminal, unlike ^D (some terminals/editors don't deliver it as EOF).
|
||||||
|
(if (#{:repl/quit :exit} (try (read-string line) (catch :default _ nil)))
|
||||||
|
nil
|
||||||
|
(do
|
||||||
(try (println (pr-str (load-string line)))
|
(try (println (pr-str (load-string line)))
|
||||||
(catch :default e
|
(catch :default e
|
||||||
(println "error:" (or (ex-message e)
|
(println "error:" (or (ex-message e)
|
||||||
(try ((resolve 'jolt.host/condition-message) e) (catch :default _ nil))
|
(try ((resolve 'jolt.host/condition-message) e) (catch :default _ nil))
|
||||||
(pr-str e)))))
|
(pr-str e)))))
|
||||||
(recur)))))
|
(recur)))))))
|
||||||
|
|
||||||
;; A deps.edn :tasks entry: a string is a shell command; a map is {:main-opts …}.
|
;; A deps.edn :tasks entry: a string is a shell command; a map is {:main-opts …}.
|
||||||
(defn- run-task [name more]
|
(defn- run-task [name more]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue