help-gnu-emacs
[Top][All Lists]
Advanced

[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/



reply via email to

[Prev in Thread] Current Thread [Next in Thread]