以下代码模拟终端操作(1.开启一个 Emacs 并处于等待输入状态;2.输入 "foo\nbar"
):
(let* ((process-connection-type nil)
(comint-use-prompt-regexp t)
(comint-prompt-read-only t)
(comint-highlight-prompt t)
(comint-prompt-regexp "ELPL> ")
(cmdlist
`("/Applications/Emacs-26.1.app/Contents/MacOS/Emacs"
"--batch"
"--eval"
,(format "%S"
'(while t
(condition-case err
(print (eval (read (read-from-minibuffer "ELPL> "))))
(error
(print err)))))))
(buf (apply 'make-comint-in-buffer "elpl"
(generate-new-buffer-name "*elpl*")
(car cmdlist) nil (cdr cmdlist))))
(with-current-buffer buf
(view-buffer-other-window buf)
(insert (format "%S" "foo\nbar"))
(comint-send-input)
))
从结果可以看出,输入的 "foo\nbar"
被分成 "foo
和 bar"
两部分执行了,所以出现了两个错误:
"foo
bar"
ELPL>
(end-of-file)
ELPL>
(void-variable bar)
ELPL>
(一度用 (process-send-string (get-buffer-process "*elpl*") "\"foo\nbar\"")
成功发送过多行字符,现在却无法重现。之前乱糟糟的测试代码没有保存,忘记当时还做了哪些设置。又或者只是幻觉?)
貌似 (while t ... (read-from-minibuffer ...))
是按行读的,你一次性给它发送
1
2
这个循环还是会运行两次。有三个思路
1 个赞
cireu
3
(princ
(with-temp-buffer
(ignore-errors
(while t
(insert (read-from-minibuffer "") "\n")))
(buffer-substring-no-properties (point-min) (point-max))))
试试这个,这样就可以读取到EOF
替换 \n
无法解决手动输入的问题。
这个我也尝试过。在 Emacs 中手动编辑的时候,虽然 "foo<C-j>
看起来没有立即提交,而是在下一行继续编辑。但子进程其实已经对 "foo
求值了,只不是等到回车的时候,才一起打印出来。
read-from-minibuffer
第三个参数是 keymap,默认值 minibuffer-local-map
,其中预设了:
(10 . exit-minibuffer)
(13 . exit-minibuffer)
10 就是按键 C-j
,看起来应该就是这个按键使得 read-from-minibuffer
退出,并且把不完整的输入送给 (eval)
求值。然而当我把 C-j
重设了也没有什么影响:
(let* ((cmdlist
`("/Applications/Emacs-26.1.app/Contents/MacOS/Emacs"
"--batch"
"--eval"
,(format "%S"
'(let ((map (make-sparse-keymap))
(i 0))
(define-key map (kbd "C-j") nil)
(define-key map (kbd "RET") 'exit-minibuffer)
(print map)
(while t
(condition-case err
(print (eval
(concat
(format "[%s] " i)
(read-from-minibuffer "ELPL> " nil map))))
(error
(print err)))
(setq i (1+ i)))))))
(buf (apply 'make-comint-in-buffer "elpl"
(generate-new-buffer-name "*elpl*")
(car cmdlist) nil (cdr cmdlist))))
(with-current-buffer buf
(view-buffer-other-window buf)
(insert (format "%S" "foo\nbar"))
(comint-send-input)
))
结果依然是分两句执行:
"foo
bar"
(keymap (13 . exit-minibuffer) (10))
ELPL>
"[0] \"foo"
ELPL>
"[1] bar\""
ELPL>
或许是由于 read-from-minibuffer
底层写死了遇到 \n
就返回:
这样貌似可行:
(let ((s ""))
(while t
(setq s (concat s "\n" (read-from-minibuffer "ELPL> ")))
(condition-case err
(progn
(print (eval (read s)))
(setq s ""))
(end-of-file)
(error
(setq s "")
(print err)))))
效果:
"foo
bar"
ELPL>
"foo
bar"
ELPL> "foo
bar
baz"
ELPL>
"foo
bar
baz"
ELPL> (+ 1
2
3
)
ELPL>
6
ELPL> (+ 1
ELPL> 2
ELPL> 3
ELPL> 4
ELPL> )
10
ELPL>
1 个赞
用当前的 Emacs 的话:
(concat invocation-directory invocation-name)
;; => "/Users/xcy/src/emacs-mac/mac/Emacs.app/Contents/MacOS/Emacs"
1 个赞
赞!
不过在我这边的效果是:
ELPL> (+ 1
2
3
4
)
ELPL> ELPL> ELPL> ELPL>
10
所以我把续行的 prompt 去掉了,更像是 IELM
:
(let ((s ""))
(while t
(setq s (concat s "\n" (read-from-minibuffer
(if (string= s "")
"ELPL> "
""))))
(condition-case err
(progn
(print (eval (read s)))
(setq s ""))
(end-of-file)
(error
(setq s "")
(print err)))))
1 个赞
应该是因为我上面的换行是手打的 C-q C-j,不是用 insert
。