World Playground Deceit.net

Emacs: separate minibuffer history for a command


Just updated my init.el today and I had a hard time finding an easy or definitive answer to this question on the web, so here's a small writeup.

The gist of it is simple: I have an interactive command (using the magic interactive form) and when using savehist-mode, I want it to have a separate history that won't pollute the global minibuffer one and vice versa.

Here's the command concerned:

(defun sed-on-region (start end script)
  "Run a `sed -E` script on a region"
  (interactive "*r\ns`sed -E` script: ")
  (call-process-region start end "sed" t t t "-E" script))

Note the interactive string format specifying that:

*It should error out when used in a read-only buffer.
rThe two first parameters are the current region's start and end.
s…The third is a string read from the minibuffer using … as prompt.

At that point, I dived into the usual rabbit hole: furious perusing of emacs' doc, various StackOverflow posts and even mailing list archives to end up - after a few half-baked abominations - at the "proper" solution. Which is to use the second interactive argument descriptor style: a form that is evaluated to get a list of arguments to pass to the command.

Then it was just a matter of putting the Lego pieces together:

(defvar sed-on-region-history nil)
(with-eval-after-load 'savehist
  (push 'sed-on-region-history savehist-additional-variables))

(defun sed-on-region (start end script)
  "Run a `sed -E` script on a region"
  (interactive
   (progn
     (barf-if-buffer-read-only)
     (list (region-beginning) (region-end)
           (read-string "`sed -E` script: " nil 'sed-on-region-history))))
  (call-process-region start end "sed" t t t "-E" script))

Ta-da! Well, I say "just", but in truth I did struggle a bit with interactive (mostly because I tried to guess by example when I should have sat down to RTFM) and finding out which function was being called by the read-only buffer check * (barf-if-buffer-read-only, emacs' legendary doc did fail me here).

Here's the rest of my config for the curious.