Keep your input, change the target
Switch commands mid-search with embark-become and carry on
I rely on a common set of commands for lookup and jumping around. And more often than not, I’m searching for something that I know exists somewhere, but it’s not coming up in the results. Because I’m typing in the wrong command interface.
And that’s precisely the use case for embark-become.
I used embark mostly for jumping to commands’ definitions from within M-x and killing multiple buffers with embark-act-all. Re-reading its documentation, I found it already solves a problem I’ve been tackling the hard way.
My partial solution was to keep some related commands on each other’s keymaps, using consult’s consult-customize macro. When I’m in consult-outline, for example, I use the same keybinding to escalate my input to consult-line, and from there to consult-line-multi, which is very handy.
Still, that doesn’t work as well for non-consult commands, and my workarounds were getting too involved and fragile. More importantly, they don’t prevent me from invoking the wrong keybinding and having to start over.
What embark-become does is find related commands by looking for the current minibuffer command in the keymaps from embark-become-keymaps. It then manages to replace the current command with another one while preserving the input you’ve just typed.
By becoming aware of that, I was able to erase many lines from my configuration and replace them with a custom keymap:
(defvar-keymap my/embark-become-map
:doc "My common lookup commands."
"b" #'consult-buffer
"f" #'find-file
"x" #'execute-extended-command
"h" #'consult-org-hop
"q" #'my/org-ql-find
"s" #'describe-symbol
"l" #'consult-line
"L" #'consult-line-multi
"o" #'consult-outline
"i" #'consult-imenu
"I" #'consult-imenu-multi
"c" #'my/org-capture-note)
(add-hook 'embark-become-keymaps 'my/embark-become-map)
I also bound embark-become to C-M-. in the minibuffer-local-map.
Now, whenever I’m typing something inside one of those commands, I can quickly switch to any other and keep the current input.
Custom commands #
Three of the commands above are specific to my usual workflow.
consult-org-hop is from a package of mine, org-hop. I use it daily to hop to any Org heading from my files by searching for its title, outline path, or tags.
When I need to find something that may be present in the contents of a heading, I rely on org-ql-find:
(defun my/org-ql-find (&optional arg)
"Run `org-ql-find' in Org mode buffers.
When ARG is non-nil, search only in current buffer."
(interactive "P")
(let ((org-ql-default-predicate 'rifle))
(if (and arg (derived-mode-p 'org-mode))
(org-ql-find (current-buffer))
(org-ql-find (org-buffer-list)))))
If I’m still unable to find what I want, or when the results aren’t really related with the topic I’m working on, I create a new note:
(defun my/org-capture-note (input)
"Run Org capture from current minibuffer INPUT."
(interactive "sNew note title: ")
(org-capture nil "n")
(insert (or input ""))
(goto-char (point-max)))
This command prompts for a note title, with the initial contents from the minibuffer input. After I adjust the writing, it runs org-capture and inserts the title where the template “n” leaves the cursor on in a regular capture. Finally, it moves the point to the end of the buffer, where I can proceed to write the new note’s contents.
Final thoughts #
With this simple setup, invoking a command by mistake becomes just a matter of reorientation, not interruption.
You keep moving forward by changing what you are matching against, and not what you are typing for.