Embark is a fantastic and thoughtfully designed package for Emacs that flips Emacs’ action → object ordering without adding a learning curve. It’s completely changed how I use Emacs, and I’m going to show you why.

By default, Emacs’ action model is to specify the action (find-file), then the object (the file):

This mirrors how one does things in a shell: The difference is that before you submit your shell command, you’re free to edit both the action and the object, since it’s just a line of text. In Emacs you can change the object freely, but you’d have to hit C-g and call a different command.

Things work the other way in a GUI program like a file manager. You select some representation of the object (usually an icon), then choose the action you would like to perform on it:

Either paradigm works fine, but this is Emacs, there’s no reason to choose just one! Embark lets you go back and forth between the two patterns. So you can call find-file (say) and pick a file, only to realize that you want to do something else with it, like open it in another window, or copy the file to a different directory:

With Embark, this is a breeze.

embark-act: Actually…. & But first…

embark-act is your “Actually…” command. As in, I called package-install and picked a package but actually I’d like to read the package description instead!

embark-act is your “Yes, but first…” command as well. As in, I called find-file but first I’d like to copy it elsewhere to be safe, then continue to open this file!

Or perhaps you want to think of it as a keyboard driven analog of a “right-click menu” in a GUI environment. That works too, but the former maps better to the idea of “late-binding” and laziness that I think of Embark as enabling.

Emacs makes you specify and fix the action/verb first (find-file, say), then choose the thing it acts on (the file). If you call embark-act, this is reversed. Now the object (file) is fixed, and you’re free to choose the action.

I know: It sounds like I’m describing Helm actions. The difference is that Embark works everywhere, across all types of “objects”, and with every initial and wait-I-changed-my-mind command. There is no predetermined set of alternative actions configured to work with another predetermined set of initial actions. No one (including yourself) needs to have anticipated in advance what actions go together.1 This uniform, consistent integration into Emacs makes the difference between them one of kind and not of quantity, although it takes a bit of time to see this.

This means you can start a command and select a candidate in the minibuffer, then call embark-act and M-x some-other-command to run that command on the candidate instead. If you are about to kill a buffer with C-x k but want to switch to it instead, you can call embark-act followed by C-x b. You can even do this without losing the kill-buffer prompt if you just want a quick peek at the buffer!

The categories of objects Embark understands covers most common cases: filenames, buffers, bookmarks, URLs, text regions, variables, commands, symbols and more.

When you call embark-act, Embark also activates a keymap with direct access to common actions you might want to run for each category of object. This makes it unnecessary to use M-x to run your I-changed-my-mind action all the time, although you always have that option. You can, of course, add your own commands to this keymap as I do below.

I use embark-act literally hundreds of times every day. Here are a few of my common uses. A few of these are built in, others need some elisp to work, all are surprisingly useful. To be clear, this list barely scratches the surface of the sphere of possibilities with Embark.

Open any buffer by splitting any window

This needs a little background. The ace-window package allows you to switch to a window based on keyboard hints. A less well known feature is that it also provides a “dispatch menu” that lets you act on windows in ways beyond simply switching to them:

So you can kill windows, move them around, split them and more by using the dispatch keys. (Hit ? to bring up the dispatch menu.)

Now: You can call ace-window via Embark to display a candidate anywhere, including in splits that you create using the above dispatch menu. This means any buffer/file/bookmark I open is always placed exactly where I want it to be on the screen.

In the below demo, I open a bookmark (with consult-bookmark), a file (with find-file) and a buffer (with consult-buffer) in sequence. Each time, I run embark-act and select the ace-window action, which activates ace-window. You can then display the buffer in any existing window by making a selection with ace-window. I actually go one step further in the demo: I split one of the existing windows using ace-window's dispatch feature from above and display the relevant buffer in that split!

To get this to work, you’ll need to add a few ace-window functions to the Embark file actions map:

(eval-when-compile
  (defmacro my/embark-ace-action (fn)
    `(defun ,(intern (concat "my/embark-ace-" (symbol-name fn))) ()
       (interactive)
       (with-demoted-errors "%s"
         (require 'ace-window)
         (aw-switch-to-window (aw-select nil))
         (call-interactively (symbol-function ',fn)))))

  (defmacro my/embark-split-action (fn split-type)
    `(defun ,(intern (concat "my/embark-"
                             (symbol-name fn)
                             "-"
                             (car (last  (split-string
                                          (symbol-name split-type) "-"))))) ()
       (interactive)
       (funcall #',split-type)
       (call-interactively #',fn))))

(define-key embark-file-map     (kbd "o") (my/embark-ace-action find-file))
(define-key embark-buffer-map   (kbd "o") (my/embark-ace-action switch-to-buffer))
(define-key embark-bookmark-map (kbd "o") (my/embark-ace-action bookmark-jump))

(define-key embark-file-map     (kbd "2") (my/embark-split-action find-file split-window-below))
(define-key embark-buffer-map   (kbd "2") (my/embark-split-action switch-to-buffer split-window-below))
(define-key embark-bookmark-map (kbd "2") (my/embark-split-action bookmark-jump split-window-below))

(define-key embark-file-map     (kbd "3") (my/embark-split-action find-file split-window-right))
(define-key embark-buffer-map   (kbd "3") (my/embark-split-action switch-to-buffer split-window-right))
(define-key embark-bookmark-map (kbd "3") (my/embark-split-action bookmark-jump split-window-right))

The ace-window action needs only the first macro, but I threw in some extras.


Copy a file to a remote location when finding a file

Here’s what happened. In any file prompt, you can call embark-act and select the copy action to copy the file instead. (You could just as well call M-x copy-file.) In this case I then use consult-dir to insert a bookmark that points to my server into the destination prompt, and the file is copied using Tramp.

You can even do this without losing the find-file prompt! Calling embark-act with a prefix argument keeps the prompt alive:

At the end I quit the find-file prompt manually and check the remote directory to ensure that the file has been copied.


Insert a minibuffer candidate into the buffer

Simple but very convenient:


Run a shell command on a minibuffer candidate file without losing your session

A perfect example of But First I need to…:

I called the “file” shell command for more info on the file without ending the find-file prompt.


Open a file as root without losing your session

Emacs’ version of forgetting to add sudo before the command. In the shell you can go back to the start of the prompt and type it in, or engage in the sudo !! ritual. In Emacs I use an Embark action:

Like before, this works from any file prompt but the command I started with was consult-locate. For the sudo program there is the sudo-edit package, although I used a snippet from my init file that I can’t ascertain the provenance of anymore:

(defun sudo-find-file (file)
  "Open FILE as root."
  (interactive "FOpen file as root: ")
  (when (file-writable-p file)
    (user-error "File is user writeable, aborting sudo"))
  (find-file (if (file-remote-p file)
                 (concat "/" (file-remote-p file 'method) ":"
                         (file-remote-p file 'user) "@" (file-remote-p file 'host)
                         "|sudo:root@"
                         (file-remote-p file 'host) ":" (file-remote-p file 'localname))
               (concat "/sudo:root@localhost:" file))))

To use sudo-find-file as an Embark action, you can run it (with M-x or a global keybinding) after calling embark-act, or shorten the process further by adding an entry to Embark’s file actions map:

(define-key embark-file-map (kbd "S") 'sudo-find-file)

Upload a region of text to 0x0

I’m using the 0x0 package for the 0x0-dwim function. When called as an Embark action on a URL, this shortens it. When called on a file, it uploads the file. The echo area message at the end (from 0x0-dwim) tells me the upload URL has been copied to the kill ring. As with the other examples, you can call 0x0-dwim after running embark-act or define a short key for it in one of Embark’s keymaps:

(define-key embark-region-map (kbd "U") '0x0-dwim)

Visit a package’s URL from the minibuffer

In this case I ran the describe-package command before going “Actually… URL please", but in this example as all the others, there’s nothing special about describe-package. Any command that gives you a list of packages at the minibuffer will proffer the same set of Embark actions.


Set a variable from anywhere it appears in a buffer

Super handy for quickly setting variables, especially when testing code.

In this case Embark has an entry for set-variable in its variables keymap (bound to =), but you can just call M-x set-variable.


Add a keybinding for a command name from anywhere it appears

Set all the keys.

Embark provides an action in its keymap to run global-set-key, but you could just call M-x global-set-key after running embark-act with the point on a command name.


embark-export: I want a gist, so give me a list

If that was everything Embark did I’d be a happy camper. But embark-act isn’t even its best feature. That would be the gem of composability that is embark-export (and its lesser kin embark-collect). These commands create persistent collections from minibuffer candidate lists: It’s one part ivy-occur and one part glue that ties together Emacs libraries better than Emacs does. The examples illustrate why.


Export Emacs package candidates to a package menu

Want a package-menu-mode buffer with all packages involving shells in Emacs? embark-export has you covered:

The clever idea behind embark-export is to reuse Emacs’ built-in functionality whenever possible: the package-menu library already handles displaying packages. If you’re generating a list of packages with user-specified conditions, why reinvent the wheel?


Collect imenu candidates in an “imenu-list”

embark-collect creates persistent collections of minibuffer completion candidates (filtered by user input) in a way that basically obsoletes every “listing” package for me. In this example I create a filtered list of imenu items that sticks around and that I can use to navigate around the file:

That’s consult-imenu + some user input + embark-collect. I didn’t show this in the demo, but all embark-act actions are available in the Collections buffer, and you can even call them directly (i.e. without calling embark-act first) by turning on embark-collect-direct-action-minor-mode.


Export file candidates to a dired-buffer

Have a list of files you arrived at in a tortuous manner that you want to keep around? dired was created to list files, and embark-export respects this:

This obsoletes find-name-dired, another “listing” based feature.


Export buffer candidates to ibuffer

You saw this coming: Any list of buffers gets exported to an ibuffer.

Before exporting I filtered away2 the “special” buffers that start with *.


Export variable candidates to a customize buffer

A list of variables is exported by embark-export into a customize buffer:

This is a great way to transition from looking up a variable to a full-fledged apropos on relevant items when you need to.


Export grep or line candidates to a grep buffer

Any occur-like results (from consult-line, grep, xref etc) get exported into a grep buffer.

Note that this is a regular grep buffer, so you can use all your tricks, like wgrep to edit the grep buffer and save changes in all the files.


BONUS: Use Embark Actions like Helm

In the above examples, the available embark actions were displayed in some window in the frame. Embark has multiple “prompters” listing the preset actions, and with a little elbow grease you can set up something similar to Helm3:

Here I switch back and forth between the list of actions and the list of candidates (like in Helm) with C-<tab>. In the actions list you can either type the action (matched with completing-read), or call the action directly by prepending its keybinding with @.

Elbow grease:

(defun with-minibuffer-keymap (keymap)
  (lambda (fn &rest args)
    (minibuffer-with-setup-hook
        (lambda ()
          (use-local-map
           (make-composed-keymap keymap (current-local-map))))
      (apply fn args))))

(defvar embark-completing-read-prompter-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "<tab>") 'abort-recursive-edit)
    map))

(advice-add 'embark-completing-read-prompter :around
            (with-minibuffer-keymap embark-completing-read-prompter-map))
(define-key vertico-map (kbd "<tab>") 'embark-act-with-completing-read)

  (defun embark-act-with-completing-read (&optional arg)
    (interactive "P")
    (let* ((embark-prompter 'embark-completing-read-prompter)
           (act (propertize "Act" 'face 'highlight))
           (embark-indicator (lambda (_keymap targets) nil)))
      (embark-act arg)))

Replace vertico-map above with your completion system of choice’s active minibuffer keymap. The default is minibuffer-local-completion-map.

Remember that unlike with Helm, you’re not restricted to these actions when you use Embark! You can call literally any command that it makes sense to with its keybinding or with M-x after running embark-act.


33%

That’s fifteen useful Embark thingamajigs, and I didn’t get to mention embark-become. Or embark-prefix-help-map, embark-which-key-prompter, or Embark’s targets and target cycling, or half a dozen more thoughtful features and niceties about Embark. Maybe next time.

I’ll conclude instead by mentioning the main packages I used in the above demos:

Finally a quick note for Doom Emacs users: Doom ships with Embark out of the box (as of Sep 2021), you don’t need to do anything besides looking up the keys for embark-act and embark-collect.

Despite what these examples suggest, I estimate that I use less than a third of what Embark provides. Even so, in allowing me to change or chain actions at any time, it lets me pilot Emacs by the seat of my pants! A second, unforeseen benefit is that it makes commands and listings that I would never use available in a frictionless way: commands like transpose-regions and apply-macro-to-region-lines, or custom dired, ibuffer and package-menu listings that are interactively inaccessible otherwise4. The ability to quickly whip up such buffers makes knowhing how to use dired or ibuffer pay off several fold. In composing such features seamlessly with minibuffer interaction or with text-regions, Embark acts as a lever to amplify the power of Emacs’ myriad built in commands and libraries.


  1. Although of course, Helm and Embark both do a good job with their presets. ↩︎

  2. To match the inverse of an input string with !, I used a feature of the orderless package for Emacs. ↩︎

  3. Yes, it’s not fully Helm-style since it still uses the minibuffer instead of a buffer to show the candidates/actions. You could use vertico-buffer if that’s a sticking point. ↩︎

  4. Technically custom package-menu listings are accessible. From the full package listing (M-x list-packages), you can filter package names by regexp with / n. ↩︎