Emacs Redux: All Aboard Embark!

Wait 5 sec.

Yesterday I published a post aboutJinx, where Imentioned in passing that dropping Flyspell freed up C-. and C-; forembark, and that this would be a topicfor another article. Well, here it is!Embark has been around for quite a few years, and pretty much everyone in thevertico/consult crowd swears by it. Somehow I never got around to adoptingit until my recent config overhaul, andnow I keep asking myself why I waited so long.So, What’s Embark?Embark, written by Omar Antolín Camarena, is often described as a keyboard-drivenright-click menu for Emacs, and I think that’s a pretty apt description. Youpoint it at something - a minibuffer candidate, a file name, a URL, a symbol -press C-. (embark-act) and you get a menu of actions that make sense forthat particular thing. A few examples: On a buffer in C-x b? You can kill it, rename it, or diff it against itsfile without leaving the minibuffer. On a file in C-x C-f? You can delete it, rename it, copy its path, or openit in another window. On a URL in a buffer? You can browse it or copy it. On an Elisp symbol? You can jump to its definition or look up itsdocumentation.The beauty of it is that you don’t have to abort the minibuffer session,fiddle with dired or remember some obscure command name - the action comes toyou, right where you are.There’s also embark-dwim (C-; here), which skips the menu and runs thedefault action directly - it opens the URL at point, visits the file at pointand so on. I find myself using it all the time for the common cases andreaching for the full embark-act menu when I need something more exotic.The Killer Feature: embark-exportActing on a single thing is nice, but embark-export is where Embark reallyshines. It takes the entire set of minibuffer candidates and dumps it into abuffer of the appropriate major mode: consult-ripgrep/consult-grep results become a grep-mode buffer consult-line results become an occur buffer file candidates become a dired buffer buffer candidates become an ibuffer bufferYou trigger the export from within the action menu - e.g. C-. E while in themiddle of a consult-ripgrep session.In other words - you start a quick search in the minibuffer, and if it turnsout to be something bigger, you promote it to a proper buffer you can navigate,save for later, or (my favorite) edit in place withwgrep (C-c C-p in the exportedgrep buffer) and apply the changes across all the matched files. Once thisworkflow clicks, there’s no going back.Tips and TricksA few more things I picked up in my first days with Embark that you’llprobably want to know about: Press C-h right after C-. to get a searchable completing-read menu ofevery applicable action, and run the one you pick. This is both the best wayto discover what Embark can do and a lifesaver when you’ve forgotten a key. C-. i inserts the current candidate in the buffer you came from and C-. wcopies it to the kill-ring. Sounds mundane, but grabbing a file path or avariable name from the minibuffer this way quickly becomes second nature. C-. A (embark-act-all) acts on all the candidates at once - thinkkilling every buffer matching your current input in one go. C-. B (embark-become) replaces the current minibuffer command whilekeeping the input you’ve typed. Started C-x C-f, but realized you actuallyneed consult-buffer? No need to abort and start over - just “become” theother command. By default acting on a candidate exits the minibuffer. Call embark-act witha prefix argument (C-u C-.) to keep the session alive - handy when you wantto, say, delete a few files in a row. (see embark-quit-after-action if youprefer to flip the default)Embark Meets ProjectileBeing the Projectile maintainer, Iobviously had to check how well Embark plays with it. The answer is - prettywell, actually! Projectile advertises the completion category of itscandidates, and Embark knows how to resolve a project-relative path likelisp/init-git.el to a full path before acting on it.1 This means thewhole file action arsenal works in projectile-find-file: C-. w - copy the full path of a project file without visiting it C-. r - rename a file (no dired detour needed) C-. d - delete a file you just realized shouldn’t exist C-. j - jump to the file’s location in diredprojectile-switch-project is even more fun, as there the candidates are theproject directories themselves: C-. j - browse a project in dired without actually switching to it C-. $ - open eshell directly in some project’s root (Embark is smartenough to run shell actions in the target’s directory) C-. w - copy a project’s path C-. E - export the entire list of your known projects to a dired bufferThat last one makes for a pretty nice project dashboard, if you ask me.You can also teach embark-become about Projectile. Out of the box itsfile/buffer map offers p for the built-in project-find-file, but we caneasily add a Projectile equivalent:(with-eval-after-load 'embark (keymap-set embark-become-file+buffer-map "P" #'projectile-find-file))Now when you start a plain C-x C-f and realize the file is somewhere deep inthe current project, C-. B P will re-run your input throughprojectile-find-file instead. No retyping needed.Note: One small asymmetry - C-. E in projectile-find-file currentlyproduces an Embark collect buffer rather than a dired buffer, as theexporters operate on the raw completion category. Still useful (you can keepacting on the candidates from there), just don’t expect wdired magic.Note: The exporters for the various consult commands live in theembark-consult package, so you’ll want that one too if you’re a consultuser.The SetupHere’s the relevant bit of my config:(use-package embark :ensure t :bind (("C-." . embark-act) ("C-;" . embark-dwim) ("C-h B" . embark-bindings)) :init (setq prefix-help-command #'embark-prefix-help-command))(use-package embark-consult :ensure t :hook (embark-collect-mode . consult-preview-at-point-mode))C-. and C-; are the bindings suggested by Embark’s README and they arequite comfortable, provided nothing else is squatting on them.2 Theprefix-help-command bit is a small gem on its own - press C-h after anyprefix (say C-x) and instead of the regular help buffer you get acompleting-read over all the bindings in it, which you can narrow and executedirectly.Embark doesn’t really care which minibuffer completion UI you’re using - itworks fine with vertico, icomplete and even the default completion setup.Closing ThoughtsEmbark is one of those packages that are a bit hard to explain, but make totalsense the moment you try them - the “aha” moment for me came the first time Iexported a consult-ripgrep session. It has certainly earned its place in thesmall set of packages I’d install on day one.Are you using Embark already? What are your favorite actions? I’d love to hearabout them in the comments!That’s all I have for you today. Keep acting (on all the things)! Under the hood the path resolution goes through project.el, so thisworks in any VC-backed project - which covers pretty much every Projectileproject out there. ↩ In my case Flyspell was hogging both of them, which is partially whatprompted its replacement. ↩