不是所有的repl程序都像gapl一样自带--emacs参数支持,比如sbcl,它的cli支持很有限,连一个像样的行编辑环境都没有。那怎么才能在cli场景下用的舒服一点呢?我们可以给它接上一个重定向io的gnu readline命令行,这就是rlwrap程序。
虽然Emacs并不是为处理流设计的,它自己内部也没有一套stream IO接口,但是我们还有差不多能用的comint,能跟字符设备进行一定的交互。已知,emacs目前有大量的inferior-*-mode用于运行外部解释器,所以,emacs -nw或许是个更不错的选择。
#!/bin/zsh
emwrap(){
local exp
case $1 in
lisp) # prefer SLIME if it is a common lisp implementation
exp="(slime ${2:+\"${@:2}\"})"
;;
scheme) # prefer geiser, because run-* has been obsoleted in geiser
if [[ -z $2 ]]; then exp="(call-interactively 'geiser)"; else exp="(geiser '$2)"; fi
;;
(tcl|octave)
if [[ -z $2 ]]; then exp="(call-interactively 'inferior-$1)";
else exp="(inferior-$1 \"${@:2}\")"; fi
;;
(lua|python|php|prolog|gnuplot|haskell|ocaml)
exp="(call-interactively 'run-$1)"
;;
*)
exp="(comint-run \"$1\" '($(echo ${@:2}|xargs -n1|xargs -I {} echo -n ' "{}"')))"
;;
esac
emacs -nw --eval "(progn $exp (delete-other-windows))"
}
使用例:
emwrap lisp
emwrap lisp ecl
对于需要完整VT特性进行交互的程序,比如说当下流行的claude,comint提供不了完整的支持,但是我们还有神奇的vterm,只要把comint-run给换成vterm-send-string,也就是换成下面的表达式:
*)
exp="(progn (vterm) (vterm-send-string \"$*\") (vterm-send-return))"
;;
就可以用vterm当作你的wrapper了。使用例:
emwrap ollama run qwen3-vl
emwrap claude