💾 Archived View for gem.librehacker.com › gemlog › starlog › 20240304-0.gmi captured on 2024-03-21 at 15:09:35. Gemini links have been rewritten to link to archived content

View Raw

More Information

➡️ Next capture (2024-06-16)

-=-=-=-=-=-=-

Emacs: Gnome Notification After Async Command (publ. 2024-03-04)

The last few weeks I've been moving more of my work out of Gnome Console into Emacs. A lot of that Console work was just calling one-off shell commands, so I figured why not call those commands using shell-command or async-shell-command from Emacs. I take the the commonly used, longer-running commands and wrap them in async-shell-command inside of a simple interactive function with an easy to remember name.

The thing I was missing after the switch, however, is that after command completion, Gnome Console would send a notification to Gnome shell, which would pop up below the notification area and do a good job of getting my attention. This was helpful after running a command that takes an twenty minutes to complete, so I want some kind of notification when it is done. Emacs, however, just sends a message to the echo area, which is easily missed and often quickly removed by some other command.

Fortunately, Gnome Shell includes a command-line utility called "notify-send" which simply requires a string passed-in as an argument. Example usage would be "guix system reconfigure mysystem.scm && notify-send done". That basically works, but it is a bother to add that onto each call to async-shell-command, and also it doesn't pass the command name or termination signal on to the notification.

Unfortunately, Emacs doesn't currently (29.1) have a hook available to do anything after the async process terminates. I e-mailed emacs-devel suggesting such a hook. In the meantime, it is possible to get what I want by overriding the sentinel function from simple.el. Here is the original function:

(defun shell-command-sentinel (process signal)
  (when (memq (process-status process) '(exit signal))
    (shell-command-set-point-after-cmd (process-buffer process))
    (message "%s: %s."
             (car (cdr (cdr (process-command process))))
             (substring signal 0 -1))))

And here is the override function in my init.el:

(defun shell-command-sentinel (process signal)
  (when (memq (process-status process) '(exit signal))
    (shell-command-set-point-after-cmd (process-buffer process))
    (let ((command-string (car (cdr (cdr (process-command process)))))
          (signal-string (substring signal 0 -1)))
      (message "%s: %s." command-string signal-string)
      (start-process "notify" nil "notify-send"
                     (concat command-string ": " signal-string)))))

I still think it would be good to have a real hook there, with the sentinel passing the process and signal to the callback(s). But this override is sufficient for my present needs.