-
-
Notifications
You must be signed in to change notification settings - Fork 59
Additional Actions
This page lists examples of additional actions that might be useful. Bind actions in the maps of embark-keymap-alist
to make use of them within embark.
The symbol-overlay package is useful to create persistent symbol/identifier overlay highlights. Embark already comes with embark-toggle-highlight
for this purpose, but symbol-overlay has a few more capabilities, like renaming the marked identifier and jumping between them. symbol-overlay realizes this feature with a local keymap bound to the overlays.
(define-key embark-identifier-map "y" #'symbol-overlay-put)
In some programming languages like Rust or Ruby identifier styles are mixed. Sometimes it can be useful to cycle between the styles using the string-inflection package.
(define-key embark-identifier-map "-" #'string-inflection-cycle)
(add-to-list 'embark-repeat-actions #'string-inflection-cycle) ;; repeatable action!
The titlecase package helps with capitalizing English headlines. It can be useful to bind the command titlecase-line
in the embark-heading-map
. Furthermore there is a titlecase-region
command which could go to the embark-region-map
.
(define-key embark-heading-map "T" #'titlecase-line)
(define-key embark-region-map "T" #'titlecase-region)
You can bind these in embark-file-map
.
(autoload 'gnus-dired-attach "gnus-dired")
(defun embark-attach-file (file)
"Attach FILE to an email message.
The message to which FILE is attached is chosen as for `gnus-dired-attach`,
that is: if no message buffers are found a new email is started; if some
message buffer exist you are asked whether you want to start a new email
anyway, if you say no and there is only one message buffer the attachements
are place there, otherwise you are prompted for a message buffer."
(interactive "fAttach: ")
(gnus-dired-attach (list file)))
(defun embark-magit-status (file)
"Run `magit-status` on repo containing the embark target."
(interactive "GFile: ")
(magit-status (locate-dominating-file file ".git")))
Don't forget you have to install them as well as quelpa-use-package
to use :quelpa
keyword for package installation
(use-package embark
:bind
(:map
minibuffer-local-completion-map
("M-o" . embark-act)
:map embark-file-map
("s" . sudo-edit)
("l" . vlf))
:quelpa
(embark :repo "oantolin/embark" :fetcher github))
Simpler sudo edit which doesn't cover all the edge cases:
(defun +embark-sudo-edit ()
(interactive)
(find-file (concat "/sudo:root@localhost:"
(expand-file-name (read-file-name "Find file as root: ")))))
(embark-define-keymap embark-straight-map
("u" straight-visit-package-website)
("r" straight-get-recipe)
("i" straight-use-package)
("c" straight-check-package)
("F" straight-pull-package)
("f" straight-fetch-package)
("p" straight-push-package)
("n" straight-normalize-package)
("m" straight-merge-package))
(add-to-list 'embark-keymap-alist '(straight . embark-straight-map))
(add-to-list 'marginalia-prompt-categories '("recipe\\|package" . straight))
Password-store actions using password-store.el
(embark-define-keymap embark-password-store-actions
"Keymap for actions for password-store."
("c" password-store-copy)
("f" password-store-copy-field)
("i" password-store-insert)
("I" password-store-generate)
("r" password-store-rename)
("e" password-store-edit)
("k" password-store-remove)
("U" password-store-url))
(add-to-list 'embark-keymap-alist '(password-store . embark-password-store-actions))
;; Either add a prompt classifier or overwrite password-store--completing-read
(add-to-list 'marginalia-prompt-categories '("Password entry" . password-store))
Instead of adding the marginalia-prompt-categories
classifier, it is also possible to overwrite the completing-read
function of password-store.el
directly.
(defun password-store--completing-read ()
"Read a password entry in the minibuffer, with completion."
(completing-read
"Password entry: "
(let ((passwords (password-store-list)))
(lambda (string pred action)
(if (eq action 'metadata)
'(metadata (category . password-store))
(complete-with-action action passwords string pred))))))
The GNU Hyperbole package is full of interesting ideas, one of which is to provide a convenient way to execute readable written representations of keyboard macros found in text buffers. Hyperbole's notation surrounds these in braces, so, for example {M-< hello! C-M-f}
will go to the beginning of the buffer, insert the string "hello!" and then move forward one s-expression. We can teach Embark to recognize this notation and to offer several actions for one of these textual keyboard macros:
- Run it!, by far the most useful action.
- Save it on the keyboard macro ring, which means it becomes the current keyboard macro and can be executed with
C-x e
or<f4>
in the default bindings. - Name it, which prompts for a symbol and create a new command you can run with
M-x
. - Bind it, which prompts for a key sequence and binds it to the keyboard macro (this has some convenient special treatment for keybindings of the form
C-x C-k [0-9A-Z]
, seekmacro-bind-to-key
for details).
Here's the code:
(defun embark-kmacro-target ()
"Target a textual kmacro in braces."
(save-excursion
(let ((beg (progn (skip-chars-backward "^{}\n") (point)))
(end (progn (skip-chars-forward "^{}\n") (point))))
(when (and (eq (char-before beg) ?{) (eq (char-after end) ?}))
`(kmacro ,(buffer-substring-no-properties beg end)
. (,(1- beg) . ,(1+ end)))))))
(add-to-list 'embark-target-finders 'embark-kmacro-target)
(defun embark-kmacro-run (arg kmacro)
(interactive "p\nsKmacro: ")
(kmacro-call-macro arg t nil (kbd kmacro)))
(defun embark-kmacro-save (kmacro)
(interactive "sKmacro: ")
(kmacro-push-ring)
(setq last-kbd-macro (kbd kmacro)))
(defun embark-kmacro-name (kmacro name)
(interactive "sKmacro: \nSName: ")
(let ((last-kbd-macro (kbd kmacro)))
(kmacro-name-last-macro name)))
(defun embark-kmacro-bind (kmacro)
(interactive "sKmacro: \n")
(let ((last-kbd-macro (kbd kmacro)))
(kmacro-bind-to-key nil)))
(embark-define-keymap embark-kmacro-map
"Actions on kmacros."
("RET" embark-kmacro-run)
("s" embark-kmacro-save)
("n" embark-kmacro-name)
("b" embark-kmacro-bind))
(add-to-list 'embark-keymap-alist '(kmacro . embark-kmacro-map))
You can use embark-act
as a leader key to perform actions on the current buffer or file:
(defun embark-target-this-buffer-file ()
(cons 'this-buffer-file (or (buffer-file-name) (buffer-name))))
(add-to-list 'embark-target-finders #'embark-target-this-buffer-file 'append)
(add-to-list 'embark-keymap-alist '(this-buffer-file . this-buffer-file-map))
(embark-define-keymap this-buffer-file-map
"Commands to act on current file or buffer."
("l" load-file)
("b" byte-compile-file)
("S" sudo-find-file)
("r" rename-file-and-buffer)
("d" diff-buffer-with-file)
("=" ediff-buffers)
("C-=" ediff-files)
("!" shell-command)
("&" async-shell-command)
("x" consult-file-externally)
("c" copy-file)
("k" kill-buffer)
("z" bury-buffer)
("|" embark-shell-command-on-buffer)
("g" revert-buffer))
Now running embark-act
anywhere in a buffer should present these actions on that buffer. However, depending on the cursor position this-buffer-file-map
may be shadowed by other embark keymaps, such as for actions on expressions or defuns. To get around this you can either call embark-cycle
or define a new function:
(defun embark-act-on-buffer-file (&optional arg)
(interactive "P")
(let ((embark-target-finders '(embark-target-this-buffer-file)))
(embark-act arg)))
(global-set-key (kbd "C-c o") 'embark-act-on-buffer-file)
Now embark-act-on-buffer-file
functions like a leader-key for file/buffer actions.
This adds an action in consult-outline
to narrow to the selected heading and expand it.
(defun my/consult-outline-narrow-heading (heading)
"Narrow to and expand HEADING."
(embark-consult-goto-location heading)
(outline-mark-subtree)
(and
(use-region-p)
(narrow-to-region (region-beginning) (region-end))
(deactivate-mark))
(outline-show-subtree))
(embark-define-keymap embark-consult-outline-map
"Keymap for embark actions in `consult-outline'."
("r" my/consult-outline-narrow-heading))
(defun with-embark-consult-outline-map (fn &rest args)
"Let-bind `embark-keymap-alist' to include `consult-location'."
(let ((embark-keymap-alist
(cons '(consult-location . embark-consult-outline-map) embark-keymap-alist)))
(apply fn args)))
(advice-add 'consult-outline :around #'with-embark-consult-outline-map)
For those who use outshine-mode, a simpler definition of the narrowing function:
(defun my/consult-outline-narrow-heading (heading)
"Narrow to and expand HEADING."
(embark-consult-goto-location heading)
(outshine-narrow-to-subtree)
(outline-show-subtree))
Sometimes it's useful to embark-act
on the result of an Elisp expression, not on the expression itself. For example one might have code like this:
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
By default there is no easy way to evaluate the inner expression and invoke embark-act
on it to open the actual file. The following action provides exactly this:
(defun embark-act-with-eval (expression)
"Evaluate EXPRESSION and call `embark-act' on the result."
(interactive "sExpression: ")
(with-temp-buffer
(insert (eval (read expression)))
(embark-act)))
Bind it in embark-variable-map
and embark-expression-map
for best results.
See also the Consult Wiki and the Vertico Wiki!