Emacs 9 - Concatenating processes

Publish date: Apr 20, 2020
Tags: emacs, subprocess, sentinel, compile

Demo

Concatenating Processes

Create a Simple Process

Emacs allows you to create background processes very easily. Consider you want to run a compilation process before you save a file. It would look like this:

(defun compile-after-save ()
  "Start a process to compile a C file gcc using hard-coded parameters"
  (let ((process-name "compile-test")
        (filename (buffer-file-name)))

    (start-process process-name process-name
                   "gcc" "-o"
                   (file-name-sans-extension (file-name-nondirectory filename))
                   filename)))

Fair enough, we can run this function every time we save a file using M-x eval-expression and calling:

(add-hook 'after-save-hook 'compile-after-save nil t)

It’s Done in Background

OK, now we have the compiled code, it would be nice to run the program. As I want to have nicely decoupled - and it’s a toy project just to show the concatenation of processes - I will listen to the process events and when this process finishes I will trigger a new process to run the program. Yay!

First let’s create a function that runs the code in a new process and display it in a new window. I created that function knowing it will be triggered by a process event.

In the code it verifies if the process event is the string finished\n and then starts a process to run the compiled file.

(defun run-after-compile (process event)
  (let ((buffer (current-buffer)))
    (princ
     (format "Process: %s had the event '%s'" process event))
    (if (equal event "finished\n")
        (let ((process-name "run-test"))
          (start-process process-name process-name
                         (format "%s" (file-name-sans-extension (buffer-file-name))))
          (view-buffer-other-window process-name t)
          (switch-to-buffer-other-frame buffer)))))

Triggering the run function

To trigger the function we want to wait the compilation to complete and after that call the function. In Emacs subprocesses it is done using a sentinel to watch the events in the spawned process. I will store the process in a variable named after-save-compile-process.

We need to change the compilation function to set the variable to point to the compilation process and add the sentinel as follows:

(defvar after-save-compile-process)

;; and somewhere inside the function `compile-after-save` I add:
(setq after-save-compile-process
      (start-process process-name process-name
                     "gcc" "-o"
                     (file-name-sans-extension (file-name-nondirectory filename))
                     filename))

(set-process-sentinel after-save-compile-process 'run-after-compile)

Full implementation and usage

(defvar after-save-compile-process)

(defun compile-after-save ()
  "Start a process to compile a C file gcc using hard-coded parameters"
  (let ((process-name "compile-test")
        (filename (buffer-file-name)))

    (setq after-save-compile-process
          (start-process process-name process-name
                         "gcc" "-o"
                         (file-name-sans-extension (file-name-nondirectory filename))
                         filename))
    (set-process-sentinel after-save-compile-process 'run-after-compile)))

(defun run-after-compile (process event)
  (let ((buffer (current-buffer)))
    (princ
     (format "Process: %s had the event '%s'" process event))
    (if (equal event "finished\n")
        (let ((process-name "run-test"))
          (start-process process-name process-name
                         (format "%s" (file-name-sans-extension (buffer-file-name))))
          (view-buffer-other-window process-name t)
          (switch-to-buffer-other-frame buffer)))))

Documentation

If you really wanna learn about subprocesses in Emacs, stop reading crappy blog posts and go the docs.