[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
How to interact with a shell-like process behind the scenes?
From: |
mbork |
Subject: |
How to interact with a shell-like process behind the scenes? |
Date: |
Wed, 21 May 2025 05:40:01 +0200 |
Hi all,
I have a program which displays a prompt on stdout, accepts a line of
input, does something with it, displays the results (possibly in many
lines, in a known format, e.g. JSON), and shows the prompt again.
I need Emacs to be able to interact with it, as in: send the said line
of input and receive the said output. I don't want the /user/ to do
that, that is, I don't need it to be interactive in any way -- just that
some Elisp function can "wrap" the process, as in, accept the input,
send it to the process, and receive the output, most probably via
a callback. How do I do that?
I read about `make-process' and `start-process', but I am a bot worried
about making it robust. What if the external process gets killed, for
example? I probably should detect it and start it anew, etc. I have
a feeling that it's probable that Emacs already has something which
could help me.
I'm reading now about Comint mode. From what I see, it's not
necessarily what I want, because I don't really need an interactive,
REPL-like thing, but something working in the background.
On the other hand, I asked an Internet parrot, and it suggested Comint
mode with the following (obviously wrong) snippet:
--8<---------------cut here---------------start------------->8---
(require 'comint)
(defun my/send-to-repl (input callback)
(let* ((buffer-name "*my-repl*")
(proc-name "my-repl")
(command '("my-repl-binary" "--some-arg"))
(proc (get-buffer-process buffer-name)))
(unless (and proc (process-live-p proc))
(setq proc
(make-comint-in-buffer proc-name buffer-name
(car command) nil
(cdr command))))
(with-current-buffer buffer-name
(let ((start (point-max)))
(goto-char start)
(insert input)
(comint-send-input)
(set-process-filter
proc
(lambda (proc output)
;; Accumulate and call callback when prompt is detected
(with-current-buffer (process-buffer proc)
(insert output)
(when (string-match "my-repl-prompt>" output)
(let ((response (buffer-substring start (point-max))))
(funcall callback response)))))))))
--8<---------------cut here---------------end--------------->8---
It's wrong because it assigns the output of `make-comint-in-buffer' to
`proc', while the Elisp manual says that that function returns the
/buffer/. Another Internet parrot produced another code which starts
with defining a minor mode - again, from what I understand, this doesn't
make much sense, since I don't really need anything but the Fundamental
mode (the buffer won't be used for user interaction and can even be
a hidden one).
So, should I use Comint mode for that or something else? If so, where
can I find examples of code using it? From what I grepped in the Emacs
sources, most if not all cases where Comint mode is used are for
interactive sessions.
Thanks in advance,
--
Marcin Borkowski
https://mbork.pl
https://crimsonelevendelightpetrichor.net/
Re: How to interact with a shell-like process behind the scenes?, mbork, 2025/05/22