Fifteen ways to use Embark

Update (2021-10-16): While this list was intended as a demonstration of the kinds of things you can do with Embark, there has been some interest by readers in reproducing these demos exactly on their machines. So I have added a “Play by play” section under each demo listing the sequence of actions in the demo.

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 to change the action 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.

A recipe for reproducing these demos

I use Embark in conjunction with various Consult commands (consult-grep, consult-locate, consult-dir etc) in Emacs 27.2. If you want to reproduce these demos exactly in your Emacs, you will need the following packages:

  • embark
  • marginalia
  • vertico
  • consult
  • embark-consult
  • orderless
  • consult-dir
  • ace-window
  • 0x0

Additionally you may need to bind the relevant commands (embark-act, embark-export, consult-*) to suitable keys.

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:

Play by play

  1. With two or more windows open, call ace-window
  2. Press ? to bring up the dispatch menu.
  3. Press the dispatch key to split a window horizontally (v in my video)
  4. Press the ace-window key corresponding to the buffer you want to split (e in my video)
  5. Repeat steps 1 and 2
  6. Press the dispatch key to split a window vertically (s in my video)
  7. Press the ace-window key corresponding to the buffer you want to split (w in my video)

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!

Play by play

  1. Run a command that requires selecting a file, bookmark or buffer, perhaps switch-to-buffer
  2. Select one and run embark-act
  3. Run the my/embark-ace-action with o (see below)
  4. Select the window where you want the buffer to be placed, OR
  5. Split an existing window with v or b (see aw-dispatch-alist) followed by a window selection, and display the buffer in the new 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)
         (let ((aw-dispatch-always t))
           (aw-switch-to-window (aw-select nil))
           (call-interactively (symbol-function ',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))

I also add actions to open the buffer in a vertical or horizontal split. But you probably don’t need this, since you can do this and a lot more with ace-window’s dispatch menu!

(eval-when-compile
  (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 "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))

Copy a file to a remote location when finding a file

Play by play

  1. Run any command that requires selecting a file, perhaps find-file-other-window
  2. Select one and run embark-act
  3. Run the copy-file action with c. Embark has a key for this but you can also M-x copy-file here.
  4. Navigate to the destination path. In the video I used the consult-dir package to instantly switch the path to one of my bookmarks, a remote location.
  5. Press RET to copy the file. You can type in a name to copy it as.

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:

Play by play

  1. Run any command that requires selecting a file, perhaps find-file-other-window
  2. Select one and run embark-act with a prefix argument. That is, if your binding for embark-act is C-., run C-u C-..
  3. Run the copy-file action with c. Embark has a key for this but you can also M-x copy-file here.
  4. Navigate to the destination path. In the video I used the consult-dir package to instantly switch the path to one of my bookmarks, a remote location.
  5. Press RET to copy the file. You can type in a name to copy it as.
  6. Continue to use your find-file-other-window prompt as before.

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:

Play by play

  1. Run any command that requires you to make a selection using the minibuffer. The selection can be anything, it just has to display some text.
  2. In the video I chose a distant directory with consult-dir and selected a file in that directory.
  3. Run embark-act
  4. Press i to insert the text of the selection into the main buffer. In the video I used I instead to insert the selected file’s relative path. I does different things based on the type of object you’re selecting. For example, I with a buffer candidate inserts the contents of the buffer instead.


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

A perfect example of But First I need to…:

Play by play

  1. Run any command that requires selecting a file, perhaps find-file
  2. I switched to a distant directory using consult-dir.
  3. Select a file and run embark-act with a prefix argument. That is, if your binding for embark-act is C-., run C-u C-..
  4. Press & to run the async-shell-command action. Embark has a key for this in its keymaps but you could run M-x async-shell-command or call its default keybinding (M-&) instead.
  5. Type in the command at the prompt. The file name is already filled in for you. I used the file shell command for more info on a file.
  6. Press RET to run the command and return to the find-file prompt.

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:

Play by play

  1. Run any command that requires selecting a file. I used consult-locate to locate a root-owned file on my filesystem.
  2. Select one and run embark-act with a prefix argument. That is, if your binding for embark-act is C-., run C-u C-..
  3. Select the sudo-find-file action with S. Note: You’ll need to add this action to the keymap, see below. Alternatively you can run M-x sudo-find-file or its global binding.

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

Play by play

  1. Select a region of text in a buffer.
  2. Run embark-act.
  3. Press U to choose the 0x0-dwim action. Note: You’ll need to add this action to the keymap, see below.
  4. The region text will be uploaded to 0x0 and URL added to the kill-ring. (See message at the end of the video.)

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

Play by play

  1. Run any command that requires selecting a package, perhaps describe-package (C-h P by default)
  2. Select a package and run embark-act
  3. Press u to run the embark-browse-package-url action.

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.

Play by play

  1. Move point to a variable in a buffer. (Alternatively, run a command that requires selecting a variable at the minibuffer, like describe-variable)
  2. Run embark-act.
  3. Press = to run the set-variable action. Embark has a key for this in its keymaps, but you could call M-x set-variable instead.
  4. Set the new value of the variable.

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.

Play by play

  1. Move point to a command name in a buffer. (Alternatively, run a command that requires selecting a command at the minibuffer, like describe-command)
  2. Run embark-act.
  3. Press g to run the global-set-key action. Embark has a key for this in its keymaps, but you could call M-x global-set-key instead.
  4. Set the new keybinding for the command.

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. There is also local-set-key in the embark keymap.


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:

Play by play

  1. Run any command that requires selecting a package, perhaps describe-package (C-h P by default)
  2. (Optional) Type in some text to filter the completions list
  3. Run embark-export

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:

Play by play

  1. When visiting any file, run imenu or a version of it (I ran consult-imenu-all)
  2. (Optional) Type in some text to filter the completions list
  3. Run embark-export. (This actually runs embark-collect under the hood. You could run embark-collect directly instead, but it’s simpler to just use one command.)
  4. Press RET in the persistent collections buffer to navigate to that symbol in the file.

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:

Play by play

  1. Run any command that requires selecting a file. I used consult-fd to find all files matching a pattern below a directory.
  2. (Optional) Type in some text to filter the completions list. With consult-fd you do this by adding a # to end the string to search on and begin the string used to filter the results.
  3. Run embark-export.

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.

Play by play

  1. Run any command that requires selecting a buffer. I used consult-buffer.
  2. Type in some text to filter the completions list:
    • I pressed b SPC to narrow the consult-buffer list to buffers, then
    • typed in ! followed by * to omit all buffers2 that begin with a *, i.e. “special” buffers.
  3. Run embark-export.


Export variable candidates to a customize buffer

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

Play by play

  1. Run a command that requires selecting a variable, like describe-variable
  2. Type in some text to filter the completions list
  3. Run embark-export

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.

Play by play

  1. Run a command that generates a list of grep-style matches. I used consult-ripgrep in the demo. Other Consult-based options are consult-grep, consult-git-grep and consult-line.
  2. Type in some text to find grep results.
  3. Not necessary: I flipped through the various files that matched with vertico-next-group and vertico-previous-group.
  4. Run embark-export.
  5. Turn on next-error-follow-minor-mode in the grep buffer with C-c C-f. This jumps each match as I…
  6. …navigate the grep buffer with M-n and M-p (compilation-next-error and compilation-previous-error), and move to the next/previous matching file with } and {.

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:

Play by play

  1. Run any command involving minibuffer selection, consult-buffer in the video.
  2. (Optional) type in something to filter the completions list or select a buffer.
  3. Press TAB to switch to the list of embark actions.
  4. Press TAB again to switch back to the list of candidates.
  5. Search for an action by name (in this case “kill”) to filter the list of actions
  6. Erase and search for a different action (“diff”) and choose the diff-buffer-with-file action
  7. Press RET to run diff-buffer-with-file on the selected buffer
  8. (Optional) Navigate the diff hunks with diff-hunk-next and diff-hunk-prev
  9. (Optional) Fold the diff sections with outline-cycle
  10. Run consult-buffer again and select a buffer
  11. Switch to the actions list again with TAB
  12. Press @ to call an action by its keybinding instead of selecting it by name
  13. Call the embark action for killing a buffer with k to kill the selected buffer.

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:

  • embark by Omar Antolin Camarena, who’s been a pleasure to interact with and pester with my requests for features. To add custom actions to the embark keymaps or otherwise customize Embark, I suggest perusing the README. It’s as readable and informative as they come.
  • consult for its various enhancements to Emacs’ builtins. consult-locate and consult-find (actually consult-fd) to find files, consult-imenu for a colorful imenu with grouping and consult-ripgrep to grep across a directory.
  • marginalia for the annotations in the minibuffer. Co-maintained by Omar Antolin and Daniel Mendler.
  • vertico as the minibuffer completion interface. Consult, Vertico and Marginalia are all authored by Daniel Mendler, who I’m convinced never sleeps. I didn’t even mention Corfu.
  • The orderless completion style, also by Omar Antolin, to match pieces of text against minibuffer candidates independently. Together these five packages form the MOVEC pentagram, a composable enhancement suite that integrates Emacs’ loosely bound libraries into a modern and cohesive whole.
  • consult-dir to switch directories quickly. I used this multiple times above to navigate to distant directories when in the minibuffer prompt.
  • popper to make embark-collect, help and other ephemeral buffers behave when they appear on screen.
  • ace-window by abo-abo, whose dispatch-keys idea in Ace-Window and Avy I promptly ripped off for Popper. If I understand correctly his Ivy-Occur was an early influence on what became Embark-Collect as well.
  • 0x0 by William Vaughn. I use this far more often than I thought I would.

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. ↩︎

Add a comment, or comment via email





John H
2021-10-26
How do you create the graphics? inkscape?G
Karthik
2021-10-26
@John H: Yeah, these were created with Inkscape.
Isaac
2021-11-03
Is there a way to persist, embark actions. Like I wanted to open a json file, later I realized it’s a single long line big file, so I did embark-act on it and ran a shell command to beautify it. Now, this is working fine, but I don’t know how to write the shell command result to the same file and open it. Thanks!