dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

commit 4086a9af2165c898671ecf1df1760a297cc50a0b
parent 609ec70d11075271a6e5d727c2a9a2d1f8cc7eac
Author: Lukas Henkel <lh@entf.net>
Date:   Sat,  8 Jul 2023 14:37:46 +0200

Install embark

Diffstat:
Aelpa/embark-0.22.1.signed | 2++
Aelpa/embark-0.22.1/.dir-locals.el | 6++++++
Aelpa/embark-0.22.1/.elpaignore | 2++
Aelpa/embark-0.22.1/CHANGELOG.org | 37+++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/README-elpa | 1352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/README.org | 1170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/dir | 18++++++++++++++++++
Aelpa/embark-0.22.1/embark-autoloads.el | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/embark-org.el | 497+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/embark-pkg.el | 2++
Aelpa/embark-0.22.1/embark.el | 4363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/embark.info | 1406+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-0.22.1/embark.texi | 1436+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-consult-0.7.signed | 2++
Aelpa/embark-consult-0.7/.dir-locals.el | 6++++++
Aelpa/embark-consult-0.7/.elpaignore | 2++
Aelpa/embark-consult-0.7/README-elpa | 1299+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-consult-0.7/README.org | 1117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-consult-0.7/embark-consult-autoloads.el | 33+++++++++++++++++++++++++++++++++
Aelpa/embark-consult-0.7/embark-consult-pkg.el | 2++
Aelpa/embark-consult-0.7/embark-consult.el | 427+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/embark-consult-0.7/embark-org.el | 432+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minit.el | 9++++++++-
23 files changed, 13820 insertions(+), 1 deletion(-)

diff --git a/elpa/embark-0.22.1.signed b/elpa/embark-0.22.1.signed @@ -0,0 +1 @@ +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2023-04-20T23:05:02+0200 using RSA +\ No newline at end of file diff --git a/elpa/embark-0.22.1/.dir-locals.el b/elpa/embark-0.22.1/.dir-locals.el @@ -0,0 +1,6 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((emacs-lisp-mode + (show-trailing-whitespace . t) + (indent-tabs-mode . nil))) diff --git a/elpa/embark-0.22.1/.elpaignore b/elpa/embark-0.22.1/.elpaignore @@ -0,0 +1 @@ +LICENSE +\ No newline at end of file diff --git a/elpa/embark-0.22.1/CHANGELOG.org b/elpa/embark-0.22.1/CHANGELOG.org @@ -0,0 +1,37 @@ +#+title: Embark changelog +* Version 0.22.1 (2020-04-20) +** New feature: selections +Now users can select several targets to make an ad hoc collection. The +commands =embark-act-all=, =embark-export= and =embark-collect= will act on +the selection if it is non-empty. To select or deselect a target use +the =embark-select= action (bound to =SPC= in =embark-general-map=). If you +have some targets selected, then using =embark-select= through +=embark-act-all= will deselect them. + +Before this change the Embark Collect buffers had their own +implementation of selections which has been removed. This is how to +translate the old bindings to the new feature (which is available in +all buffers, not just Embark Collect buffers!): + +| Task | Old binding | New binding | +|--------------------+-------------+---------------| +| Mark a candidate | m | a SPC | +| Unmark a candidate | u | a SPC | +| Unmark all | U | A SPC | +| Mark all [1] | t | A SPC | +| Toggle all marks | t | not available | + +[1] Marking all candidates (with either the old =t= or the new =A SPC=) +requires that there are no marked candidates to begin with. + +In order to make room for the binding of =embark-select= to +=SPC=, some other key bindings were moved: + +- =mark= in =embark-general-map= was moved to =C-SPC=. +- =outline-mark-subtree= in =embark-heading-map= was moved to =C-SPC=. +- =whitespace-cleanup-region= in =embark-region-map= was moved to =F=. + +* Version 0.21.1 (2020-01-30) +- Finally started this changelog on 2023-04-20. Known issues with the + changelog: it started very late, the first entry is not very + informative. diff --git a/elpa/embark-0.22.1/README-elpa b/elpa/embark-0.22.1/README-elpa @@ -0,0 +1,1352 @@ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + EMBARK: EMACS MINI-BUFFER ACTIONS ROOTED IN + KEYMAPS + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + + + +1 Overview +══════════ + + Embark makes it easy to choose a command to run based on what is near + point, both during a minibuffer completion session (in a way familiar + to Helm or Counsel users) and in normal buffers. Bind the command + `embark-act' to a key and it acts like prefix-key for a keymap of + /actions/ (commands) relevant to the /target/ around point. With point + on an URL in a buffer you can open the URL in a browser or eww or + download the file it points to. If while switching buffers you spot an + old one, you can kill it right there and continue to select another. + Embark comes preconfigured with over a hundred actions for common + types of targets such as files, buffers, identifiers, s-expressions, + sentences; and it is easy to add more actions and more target types. + Embark can also collect all the candidates in a minibuffer to an + occur-like buffer or export them to a buffer in a major-mode specific + to the type of candidates, such as dired for a set of files, ibuffer + for a set of buffers, or customize for a set of variables. + + +1.1 Acting on targets +───────────────────── + + You can think of `embark-act' as a keyboard-based version of a + right-click contextual menu. The `embark-act' command (which you + should bind to a convenient key), acts as a prefix for a keymap + offering you relevant /actions/ to use on a /target/ determined by the + context: + + • In the minibuffer, the target is the current top completion + candidate. + • In the `*Completions*' buffer the target is the completion at point. + • In a regular buffer, the target is the region if active, or else the + file, symbol, URL, s-expression or defun at point. + + Multiple targets can be present at the same location and you can cycle + between them by repeating the `embark-act' key binding. The type of + actions offered depend on the type of the target. Here is a sample of + a few of the actions offered in the default configuration: + + • For files you get offered actions like deleting, copying, renaming, + visiting in another window, running a shell command on the file, + etc. + • For buffers the actions include switching to or killing the buffer. + • For package names the actions include installing, removing or + visiting the homepage. + • For Emacs Lisp symbols the actions include finding the definition, + looking up documentation, evaluating (which for a variable + immediately shows the value, but for a function lets you pass it + some arguments first). There are some actions specific to variables, + such as setting the value directly or though the customize system, + and some actions specific to commands, such as binding it to a key. + + By default when you use `embark-act' if you don't immediately select + an action, after a short delay Embark will pop up a buffer showing a + list of actions and their corresponding key bindings. If you are using + `embark-act' outside the minibuffer, Embark will also highlight the + current target. These behaviors are configurable via the variable + `embark-indicators'. Instead of selecting an action via its key + binding, you can select it by name with completion by typing `C-h' + after `embark-act'. + + Everything is easily configurable: determining the current target, + classifying it, and deciding which actions are offered for each type + in the classification. The above introduction just mentions part of + the default configuration. + + Configuring which actions are offered for a type is particularly easy + and requires no programming: the variable `embark-keymap-alist' + associates target types with variables containing keymaps, and those + keymaps containing bindings for the actions. (To examine the available + categories and their associated keymaps, you can use `C-h v + embark-keymap-alist' or customize that variable.) For example, in the + default configuration the type `file' is associated with the symbol + `embark-file-map'. That symbol names a keymap with single-letter key + bindings for common Emacs file commands, for instance `c' is bound to + `copy-file'. This means that if you are in the minibuffer after + running a command that prompts for a file, such as `find-file' or + `rename-file', you can copy a file by running `embark-act' and then + pressing `c'. + + These action keymaps are very convenient but not strictly necessary + when using `embark-act': you can use any command that reads from the + minibuffer as an action and the target of the action will be inserted + at the first minibuffer prompt. After running `embark-act' all of your + key bindings and even `execute-extended-command' can be used to run a + command. For example, if you want to replace all occurrences of the + symbol at point, just use `M-%' as the action, there is no need to + bind `query-replace' in one of Embark's keymaps. Also, those action + keymaps are normal Emacs keymaps and you should feel free to bind in + them whatever commands you find useful as actions and want to be + available through convenient bindings. + + The actions in `embark-general-map' are available no matter what type + of completion you are in the middle of. By default this includes + bindings to save the current candidate in the kill ring and to insert + the current candidate in the previously selected buffer (the buffer + that was current when you executed a command that opened up the + minibuffer). + + Emacs's minibuffer completion system includes metadata indicating the + /category/ of what is being completed. For example, `find-file''s + metadata indicates a category of `file' and `switch-to-buffer''s + metadata indicates a category of `buffer'. Embark has the related + notion of the /type/ of a target for actions, and by default when + category metadata is present it is taken to be the type of minibuffer + completion candidates when used as targets. Emacs commands often do + not set useful category metadata so the [Marginalia] package, which + supplies this missing metadata, is highly recommended for use with + Embark. + + Embark's default configuration has actions for the following target + types: files, buffers, symbols, packages, URLs, bookmarks, and as a + somewhat special case, actions for when the region is active. You can + read about the [default actions and their key bindings] on the GitHub + project wiki. + + +[Marginalia] <https://github.com/minad/marginalia> + +[default actions and their key bindings] +<https://github.com/oantolin/embark/wiki/Default-Actions> + + +1.2 The default action on a target +────────────────────────────────── + + Embark has a notion of default action for a target: + + • If the target is a minibuffer completion candidate, then the default + action is whatever command opened the minibuffer in the first place. + For example if you run `kill-buffer', then the default action will + be to kill buffers. + • If the target comes from a regular buffer (i.e., not a minibuffer), + then the default action is whatever is bound to `RET' in the keymap + of actions for that type of target. For example, in Embark's default + configuration for a URL found at point the default action is + `browse-url', because `RET' is bound to `browse-url' in the + `embark-url-map' keymap. + + To run the default action you can press `RET' after running + `embark-act'. Note that if there are several different targets at a + given location, each has its own default action, so first cycle to the + target you want and then press `RET' to run the corresponding default + action. + + There is also `embark-dwim' which runs the default action for the + first target found. It's pretty handy in non-minibuffer buffers: with + Embark's default configuration it will: + + • Open the file at point. + • Open the URL at point in a web browser (using the `browse-url' + command). + • Compose a new email to the email address at point. + • In an Emacs Lisp buffer, if point is on an opening parenthesis or + right after a closing one, it will evaluate the corresponding + expression. + • Go to the definition of an Emacs Lisp function, variable or macro at + point. + • Find the file corresponding to an Emacs Lisp library at point. + + +1.3 Working with sets of possible targets +───────────────────────────────────────── + + Besides acting individually on targets, Embark lets you work + collectively on a set of target /candidates/. For example, while you + are in the minibuffer the candidates are simply the possible + completions of your input. Embark provides three main commands to work + on candidate sets: + + • The `embark-act-all' command runs the same action on each of the + current candidates. It is just like using `embark-act' on each + candidate in turn. (Because you can easily act on many more + candidates than you meant to, by default Embark asks you to confirm + uses of `embark-act-all'; you can turn this off by setting the user + option `embark-confirm-act-all' to `nil'.) + + • The `embark-collect' command produces a buffer listing all the + current candidates, for you to peruse and run actions on at your + leisure. The candidates are displayed as a list showing additional + annotations. + + The Embark Collect buffer is somewhat "dired-like": you can select + and deselect candidates through the `embark-select' action (bound to + `SPC'). In an Embark Collect buffer `embark-act' is bound to `a' and + `embark-act-all' is bound to `A'; `embark-act-all' will act on all + currently marked candidates if there any, and will act on all + candidates if none are marked. In particular, this means that `a + SPC' will toggle whether the candidate at point is selected, and `A + SPC' will select all candidates if none are selected, or deselect + all selected candidates if there are some. + + • The `embark-export' command tries to open a buffer in an appropriate + major mode for the set of candidates. If the candidates are files + export produces a Dired buffer; if they are buffers, you get an + Ibuffer buffer; and if they are packages you get a buffer in package + menu mode. + + If you use the grepping commands from the [Consult] package, + `consult-grep', `consult-git-grep' or `consult-ripgrep', then you + should install the `embark-consult' package, which adds support for + exporting a list of grep results to an honest grep-mode buffer, on + which you can even use [wgrep] if you wish. + + When in doubt choosing between exporting and collecting, a good rule + of thumb is to always prefer `embark-export' since when an exporter to + a special major mode is available for a given type of target, it will + be more featureful than an Embark collect buffer, and if no such + exporter is configured the `embark-export' command falls back to the + generic `embark-collect'. + + These commands are always available as "actions" (although they do not + act on just the current target but on all candidates) for `embark-act' + and are bound to `A', `S' (for "snapshot"), and `E', respectively, in + `embark-general-map'. This means that you do not have to bind your own + key bindings for these (although you can, of course!), just a key + binding for `embark-act'. + + In Embark Collect or Embark Export buffers that were obtained by + running `embark-collect' or `embark-export' from within a minibuffer + completion session, `g' is bound to a command that restarts the + completion session, that is, the command that opened the minibuffer is + run again and the minibuffer contents restored. You can then interact + normally with the command, perhaps editing the minibuffer contents, + and, if you wish, you can rerun `embark-collect' or `embark-export' to + get an updated buffer. + + +[Consult] <https://github.com/minad/consult/> + +[wgrep] <https://github.com/mhayashi1120/Emacs-wgrep> + +1.3.1 Selecting some targets to make an ad hoc candidate set +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + The commands for working with sets of candidates just described, + namely `embark-act-all', `embark-export' and `embark-collect' by + default work with all candidates defined in the current context. For + example, in the minibuffer they operate on all currently completion + candidates, or in a dired buffer they work on all marked files (or all + files if none are marked). Embark also has a notion of /selection/, + where you can accumulate an ad hoc list of targets for these commands + to work on. + + The selection is controlled by using the `embark-select' action (which + must be run as an action through `embark-act'), bound to `SPC' in + `embark-general-map' so that it is always available. Calling this + action on a target toggles its membership in the current buffer's + Embark selection; that is, it adds it to selection if not selected and + removes it from the selection if it was selected. Whenever the + selection for a buffer is non-empty, the commands `embark-act-all', + `embark-export' and `embark-collect' will act on the selection. + + To deselect all selected targets, you can use the `embark-select' + action through `embark-act-all', since this will run `embark-select' + on each member of the current selection. Similarly if no targets are + selected and you are in a minibuffer completion session, running + `embark-select' from `embark-act-all' will select all the current + completion candidates. + + This functionality is supported everywhere: + + • In the minibuffer this gives a convenient way to act on several + completion candidates that don't follow any simple pattern: just go + through the completions selecting the ones you want, then use + `embark-act-all'. For example, you could attach several files at + once to an email. + • For Embark Collect buffers this functionality enables a dired-like + workflow, in which you mark various candidates and apply an action + to all at once. (It supersedes a previous ad hoc dired-like + interface that was implemented only in Embark Collect buffers, with + a slightly different interface.) + • In a eww buffer you could use this to select various links you wish + to follow up on, and then collect them into a buffer. Similarly, + while reading Emacs's info manual you could select some symbols you + want to read more about and export them to an `apropos-mode' buffer. + • You can use selections in regular text or programming buffers to do + complex editing operations. For example, if you have three + paragraphs scattered over a file and you want to bring them + together, you can select each one, insert them all somewhere and + finally delete all of them (from their original locations). + + +1.3.2 `embark-live' a live-updating variant of `embark-collect' +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + Finally, there is also an `embark-live' variant of the + `embark-collect' command which automatically updates the collection + after each change in the source buffer. Users of a completion UI that + automatically updates and displays the candidate list (such as + Vertico, Icomplete, Fido-mode, or MCT) will probably not want to use + `embark-live' from the minibuffer as they will then have two live + updating displays of the completion candidates! + + A more likely use of `embark-live' is to be called from a regular + buffer to display a sort of live updating "table of contents" for the + buffer. This depends on having appropriate candidate collectors + configured in `embark-candidate-collectors'. There are not many in + Embark's default configuration, but you can try this experiment: open + a dired buffer in a directory that has very many files, mark a few, + and run `embark-live'. You'll get an Embark Collect buffer containing + only the marked files, which updates as you mark or unmark files in + dired. To make `embark-live' genuinely useful other candidate + collectors are required. The `embark-consult' package (documented + near the end of this manual) contains a few: one for imenu items and + one for outline headings as used by `outline-minor-mode'. Those + collectors really do give `embark-live' a table-of-contents feel. + + +1.4 Switching to a different command without losing what you've typed +───────────────────────────────────────────────────────────────────── + + Embark also has the `embark-become' command which is useful for when + you run a command, start typing at the minibuffer and realize you + meant a different command. The most common case for me is that I run + `switch-to-buffer', start typing a buffer name and realize I haven't + opened the file I had in mind yet! I'll use this situation as a + running example to illustrate `embark-become'. When this happens I + can, of course, press `C-g' and then run `find-file' and open the + file, but this requires retyping the portion of the file name you + already typed. This process can be streamlined with `embark-become': + while still in the `switch-to-buffer' you can run `embark-become' and + effectively make the `switch-to-buffer' command become `find-file' for + this run. + + You can bind `embark-become' to a key in `minibuffer-local-map', but + it is also available as an action under the letter `B' (uppercase), so + you don't need a binding if you already have one for `embark-act'. So, + assuming I have `embark-act' bound to, say, `C-.', once I realize I + haven't open the file I can type `C-. B C-x C-f' to have + `switch-to-buffer' become `find-file' without losing what I have + already typed in the minibuffer. + + But for even more convenience, `embark-become' offers shorter key + bindings for commands you are likely to want the current command to + become. When you use `embark-become' it looks for the current command + in all keymaps named in the list `embark-become-keymaps' and then + activates all keymaps that contain it. For example, the default value + of `embark-become-keymaps' contains a keymap + `embark-become-file+buffer-map' with bindings for several commands + related to files and buffers, in particular, it binds + `switch-to-buffer' to `b' and `find-file' to `f'. So when I + accidentally try to switch to a buffer for a file I haven't opened + yet, `embark-become' finds that the command I ran, `switch-to-buffer', + is in the keymap `embark-become-file+buffer-map', so it activates that + keymap (and any others that also contain a binding for + `switch-to-buffer'). The end result is that I can type `C-. B f' to + switch to `find-file'. + + +2 Quick start +═════════════ + + The easiest way to install Embark is from GNU ELPA, just run `M-x + package-install RET embark RET'. (It is also available on MELPA.) It + is highly recommended to also install [Marginalia] (also available on + GNU ELPA), so that Embark can offer you preconfigured actions in more + contexts. For `use-package' users, the following is a very reasonable + starting configuration: + + ┌──── + │ (use-package marginalia + │ :ensure t + │ :config + │ (marginalia-mode)) + │ + │ (use-package embark + │ :ensure t + │ + │ :bind + │ (("C-." . embark-act) ;; pick some comfortable binding + │ ("C-;" . embark-dwim) ;; good alternative: M-. + │ ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + │ + │ :init + │ + │ ;; Optionally replace the key help with a completing-read interface + │ (setq prefix-help-command #'embark-prefix-help-command) + │ + │ ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc + │ ;; strategy, if you want to see the documentation from multiple providers. + │ (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) + │ ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) + │ + │ :config + │ + │ ;; Hide the mode line of the Embark live/completions buffers + │ (add-to-list 'display-buffer-alist + │ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + │ nil + │ (window-parameters (mode-line-format . none))))) + │ + │ ;; Consult users will also want the embark-consult package. + │ (use-package embark-consult + │ :ensure t ; only need to install it, embark loads it after consult if found + │ :hook + │ (embark-collect-mode . consult-preview-at-point-mode)) + └──── + + About the suggested key bindings for `embark-act' and `embark-dwim': + • Those key bindings are unlikely to work in the terminal, but + terminal users are probably well aware of this and will know to + select different bindings. + • The suggested `C-.' binding is used by default in (at least some + installations of) GNOME to input emojis, and Emacs doesn't even get + a chance to respond to the binding. You can select a different key + binding for `embark-act' or use `ibus-setup' to change the shortcut + for emoji insertion (Emacs 29 will likely use `C-x 8 e e', in case + you want to set the same one system-wide). + • The suggested alternative of `M-.' for `embark-dwim' is bound by + default to `xref-find-definitions'. That is a very useful command + but overwriting it with `embark-dwim' is sensible since in Embark's + default configuration, `embark-dwim' will also find the definition + of the identifier at point. (Note that `xref-find-definitions' with + a prefix argument prompts you for an identifier, `embark-dwim' does + not cover this case). + + Other Embark commands such as `embark-act-all', `embark-become', + `embark-collect', and `embark-export' can be run through `embark-act' + as actions bound to `A', `B', `S' (for "snapshot"), and `E' + respectively, and thus don't really need a dedicated key binding, but + feel free to bind them directly if you so wish. If you do choose to + bind them directly, you'll probably want to bind them in + `minibuffer-local-map', since they are most useful in the minibuffer + (in fact, `embark-become' only works in the minibuffer). + + The command `embark-dwim' executes the default action at + point. Another good keybinding for `embark-dwim' is `M-.' since + `embark-dwim' acts like `xref-find-definitions' on the symbol at + point. `C-.' can be seen as a right-click context menu at point and + `M-.' acts like left-click. The keybindings are mnemonic, both act at + the point (`.'). + + Embark needs to know what your minibuffer completion system considers + to be the list of candidates and which one is the current candidate. + Embark works out of the box if you use Emacs's default tab completion, + the built-in `icomplete-mode' or `fido-mode', or the third-party + packages [Vertico] or [Ivy]. + + If you are a [Helm] or [Ivy] user you are unlikely to want Embark + since those packages include comprehensive functionality for acting on + minibuffer completion candidates. (Embark does come with Ivy + integration despite this.) + + +[Marginalia] <https://github.com/minad/marginalia> + +[Vertico] <https://github.com/minad/vertico> + +[Ivy] <https://github.com/abo-abo/swiper> + +[Helm] <https://emacs-helm.github.io/helm/> + + +3 Advanced configuration +════════════════════════ + +3.1 Showing information about available targets and actions +─────────────────────────────────────────────────────────── + + By default, if you run `embark-act' and do not immediately select an + action, after a short delay Embark will pop up a buffer called + `*Embark Actions*' containing a list of available actions with their + key bindings. You can scroll that buffer with the mouse of with the + usual commands `scroll-other-window' and `scroll-other-window-down' + (bound by default to `C-M-v' and `C-M-S-v'). + + That functionality is provided by the `embark-mixed-indicator', but + Embark has other indicators that can provide information about the + target and its type, what other targets you can cycle to, and which + actions have key bindings in the action map for the current type of + target. Any number of indicators can be active at once and the user + option `embark-indicators' should be set to a list of the desired + indicators. + + Embark comes with the following indicators: + + • `embark-minimal-indicator': shows a messages in the echo area or + minibuffer prompt showing the current target and the types of all + targets starting with the current one; this one is on by default. + + • `embark-highlight-indicator': highlights the target at point; also + on by default. + + • `embark-verbose-indicator': displays a table of actions and their + key bindings in a buffer; this is not on by default, in favor of the + mixed indicator described next. + + • `embark-mixed-indicator': starts out by behaving as the minimal + indicator but after a short delay acts as the verbose indicator; + this is on by default. + + • `embark-isearch-highlight-indicator': this only does something when + the current target is the symbol at point, in which case it lazily + highlights all occurrences of that symbol in the current buffer, + like isearch; also on by default. + + Users of the popular [which-key] package may prefer to use the + `embark-which-key-indicator' from the [Embark wiki]. Just copy its + definition from the wiki into your configuration and customize the + `embark-indicators' user option to exclude the mixed and verbose + indicators and to include `embark-which-key-indicator'. + + +[which-key] <https://github.com/justbur/emacs-which-key> + +[Embark wiki] +<https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt> + + +3.2 Selecting commands via completions instead of key bindings +────────────────────────────────────────────────────────────── + + As an alternative to reading the list of actions in the verbose or + mixed indicators (see the previous section for a description of + these), you can press the `embark-help-key', which is `C-h' by default + (but you may prefer `?' to free up `C-h' for use as a prefix) after + running `embark-act'. Pressing the help key will prompt you for the + name of an action with completion (but feel free to enter a command + that is not among the offered candidates!), and will also remind you + of the key bindings. You can press `embark-keymap-prompter-key', which + is `@' by default, at the prompt and then one of the key bindings to + enter the name of the corresponding action. + + You may think that with the `*Embark Actions*' buffer popping up to + remind you of the key bindings you'd never want to use completion to + select an action by name, but personally I find that typing a small + portion of the action name to narrow down the list of candidates feels + significantly faster than visually scanning the entire list of + actions. + + If you find you prefer entering actions that way, you can configure + embark to always prompt you for actions by setting the variable + `embark-prompter' to `embark-completing-read-prompter'. + + +3.3 Quitting the minibuffer after an action +─────────────────────────────────────────── + + By default, if you call `embark-act' from the minibuffer it quits the + minibuffer after performing the action. You can change this by setting + the user option `embark-quit-after-action' to `nil'. Having + `embark-act' /not/ quit the minibuffer can be useful to turn commands + into little "thing managers". For example, you can use `find-file' as + a little file manager or `describe-package' as a little package + manager: you can run those commands, perform a series of actions, and + then quit the command. + + If you want to control the quitting behavior in a fine-grained manner + depending on the action, you can set `embark-quit-after-action' to an + alist, associating commands to either `t' for quitting or `nil' for + not quitting. When using an alist, you can use the special key `t' to + specify the default behavior. For example, to specify that by default + actions should not quit the minibuffer but that using `kill-buffer' as + an action should quit, you can use the following configuration: + + ┌──── + │ (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) + └──── + + The variable `embark-quit-after-action' only specifies a default, that + is, it only controls whether or not `embark-act' quits the minibuffer + when you call it without a prefix argument, and you can select the + opposite behavior to what the variable says by calling `embark-act' + with `C-u'. Also note that both the variable + `embark-quit-after-action' and `C-u' have no effect when you call + `embark-act' outside the minibuffer. + + If you find yourself using the quitting and non-quitting variants of + `embark-act' about equally often, independently of the action, you may + prefer to simply have separate commands for them instead of a single + command that you call with `C-u' half the time. You could, for + example, keep the default exiting behavior of `embark-act' and define + a non-quitting version as follows: + + ┌──── + │ (defun embark-act-noquit () + │ "Run action but don't quit the minibuffer afterwards." + │ (interactive) + │ (let ((embark-quit-after-action nil)) + │ (embark-act))) + └──── + + +3.4 Running some setup after injecting the target +───────────────────────────────────────────────── + + You can customize what happens after the target is inserted at the + minibuffer prompt of an action. There are + `embark-target-injection-hooks', that are run by default after + injecting the target into the minibuffer. The variable + `embark-target-injection-hooks' is an alist associating commands to + their setup hooks. There are two special keys: if no setup hook is + specified for a given action, the hook associated to `t' is run; and + the hook associated to `:always' is run regardless of the + action. (This variable used to have the less explicit name of + `embark-setup-action-hooks', so please update your configuration.) + + For example, consider using `shell-command' as an action during file + completion. It would be useful to insert a space before the target + file name and to leave the point at the beginning, so you can + immediately type the shell command to run on that file. That's why in + Embark's default configuration there is an entry in + `embark-target-injection-hooks' associating `shell-command' to a hook + that includes `embark--shell-prep', a simple helper function that + quotes all the spaces in the file name, inserts an extra space at the + beginning of the line and leaves point to the left of it. + + Now, the preparation that `embark--shell-prep' does would be useless + if Embark did what it normally does after it inserts the target of the + action at the minibuffer prompt, which is to "press `RET'" for you, + accepting the target as is; if Embark did that for `shell-command' you + wouldn't get a chance to type in the command to execute! That is why + in Embark's default configuration the entry for `shell-command' in + `embark-target-injection-hooks' also contains the function + `embark--allow-edit'. + + Embark used to have a dedicated variable `embark-allow-edit-actions' + to which you could add commands for which Embark should forgo pressing + `RET' for you after inserting the target. Since its effect can also be + achieved via the general `embark-target-injection-hooks' mechanism, + that variable has been removed to simply Embark. Be sure to update + your configuration; if you had something like: + + ┌──── + │ (add-to-list 'embark-allow-edit-actions 'my-command) + └──── + + you should replace it with: + + ┌──── + │ (push 'embark--allow-edit + │ (alist-get 'my-command embark-target-injection-hooks)) + └──── + + + Also note that while you could abuse `embark--allow-edit' so that you + have to confirm "dangerous" actions such as `delete-file', it is + better to implement confirmation by adding the `embark--confirm' + function to the appropriate entry of a different hook alist, namely, + `embark-pre-action-hooks'. + + Besides `embark--allow-edit', Embark comes with another function that + is of general utility in action setup hooks: + `embark--ignore-target'. Use it for commands that do prompt you in the + minibuffer but for which inserting the target would be + inappropriate. This is not a common situation but does occasionally + arise. For example it is used by default for + `shell-command-on-region': that command is used as an action for + region targets, and it prompts you for a shell command; you typically + do /not/ want the target, that is the contents of the region, to be + entered at that prompt! + + +3.5 Running hooks before, after or around an action +─────────────────────────────────────────────────── + + Embark has three variables, `embark-pre-action-hooks', + `embark-post-action-hooks' and `embark-around-action-hooks', which are + alists associating commands to hooks that should run before or after + or as around advice for the command when used as an action. As with + `embark-target-injection-hooks', there are two special keys for the + alists: `t' designates the default hook to run when no specific hook + is specified for a command; and the hook associated to `:always' runs + regardless. + + The default values of those variables are fairly extensive, adding + creature comforts to make running actions a smooth experience. Embark + comes with several functions intended to be added to these hooks, and + used in the default values of `embark-pre-action-hooks', + `embark-post-action-hooks' and `embark-around-action-hooks'. + + For pre-action hooks: + + `embark--confirm' + Prompt the user for confirmation before executing the + action. This is used be default for commands deemed "dangerous", + or, more accurately, hard to undo, such as `delete-file' and + `kill-buffer'. + + `embark--unmark-target' + Unmark the active region. Use this for commands you want to act + on the region contents but without the region being active. The + default configuration uses this function as a pre-action hook + for `occur' and `query-replace', for example, so that you can + use them as actions with region targets to search the whole + buffer for the text contained in the region. Without this + pre-action hook using `occur' as an action for a region target + would be pointless: it would search for the the region contents + /in the region/, (typically, due to the details of regexps) + finding only one match! + + `embark--beginning-of-target' + Move to the beginning of the target (for targets that report + bounds). This is used by default for backward motion commands + such as `backward-sexp', so that they don't accidentally leave + you on the current target. + + `embark--end-of-target' + Move to the end of the target. This is used similarly to the + previous function, but also for commands that act on the last + s-expression like `eval-last-sexp'. This allow you to act on an + s-expression from anywhere inside it and still use + `eval-last-sexp' as an action. + + `embark--xref-push-markers' + Push the current location on the xref marker stack. Use this for + commands that take you somewhere and for which you'd like to be + able to come back to where you were using + `xref-pop-marker-stack'. This is used by default for + `find-library'. + + For post-action hooks: + + `embark--restart' + Restart the command currently prompting in the minibuffer, so + that the list of completion candidates is updated. This is + useful as a post action hook for commands that delete or rename + a completion candidate; for example the default value of + `embark-post-action-hooks' uses it for `delete-file', + `kill-buffer', `rename-file', `rename-buffer', etc. + + For around-action hooks: + + `embark--mark-target' + Save existing mark and point location, mark the target and run + the action. Most targets at point outside the minibuffer report + which region of the buffer they correspond to (this is the + information used by `embark-highlight-indicator' to know what + portion of the buffer to highlight); this function marks that + region. It is useful as an around action hook for commands that + expect a region to be marked, for example, it is used by default + for `indent-region' so that it works on s-expression targets, or + for `fill-region' so that it works on paragraph targets. + + `embark--cd' + Run the action with `default-directory' set to the directory + associated to the current target. The target should be of type + `file', `buffer', `bookmark' or `library', and the associated + directory is what you'd expect in each case. + + `embark--narrow-to-target' + Run the action with buffer narrowed to current target. Use this + as an around hook to localize the effect of actions that don't + already work on just the region. In the default configuration it + is used for `repunctuate-sentences'. + + `embark--save-excursion' + Run the action restoring point at the end. The current default + configuration doesn't use this but it is available for users. + + +3.6 Creating your own keymaps +───────────────────────────── + + All internal keymaps are defined with the standard helper macro + `defvar-keymap'. For example a simple version of the file action + keymap could be defined as follows: + + ┌──── + │ (defvar-keymap embark-file-map + │ :doc "Example keymap with a few file actions" + │ :parent embark-general-map + │ "d" #'delete-file + │ "r" #'rename-file + │ "c" #'copy-file) + └──── + + These action keymaps are perfectly normal Emacs keymaps. You may want + to inherit from the `embark-general-map' if you want to access the + default Embark actions. Note that `embark-collect' and `embark-export' + are also made available via `embark-general-map'. + + +3.7 Defining actions for new categories of targets +────────────────────────────────────────────────── + + It is easy to configure Embark to provide actions for new types of + targets, either in the minibuffer or outside it. I present below two + very detailed examples of how to do this. At several points I'll + explain more than one way to proceed, typically with the easiest + option first. I include the alternative options since there will be + similar situations where the easiest option is not available. + + +3.7.1 New minibuffer target example - tab-bar tabs +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + As an example, take the new [tab bars] from Emacs 27. I'll explain how + to configure Embark to offer tab-specific actions when you use the + tab-bar-mode commands that mention tabs by name. The configuration + explained here is now built-in to Embark (and Marginalia), but it's + still a good self-contained example. In order to setup up tab actions + you would need to: (1) make sure Embark knows those commands deal with + tabs, (2) define a keymap for tab actions and configure Embark so it + knows that's the keymap you want. + + +[tab bars] +<https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html> + +◊ 3.7.1.1 Telling Embark about commands that prompt for tabs by name + + For step (1), it would be great if the `tab-bar-mode' commands + reported the completion category `tab' when asking you for a tab with + completion. (All built-in Emacs commands that prompt for file names, + for example, do have metadata indicating that they want a `file'.) + They do not, unfortunately, and I will describe a couple of ways to + deal with this. + + Maybe the easiest thing is to configure [Marginalia] to enhance those + commands. All of the `tab-bar-*-tab-by-name' commands have the words + "tab by name" in the minibuffer prompt, so you can use: + + ┌──── + │ (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) + └──── + + That's it! But in case you are ever in a situation where you don't + already have commands that prompt for the targets you want, I'll + describe how writing your own command with appropriate `category' + metadata looks: + + ┌──── + │ (defun my-select-tab-by-name (tab) + │ (interactive + │ (list + │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + │ (tab-bar-tabs)) + │ (user-error "No tabs found")))) + │ (completing-read + │ "Tabs: " + │ (lambda (string predicate action) + │ (if (eq action 'metadata) + │ '(metadata (category . tab)) + │ (complete-with-action + │ action tab-list string predicate))))))) + │ (tab-bar-select-tab-by-name tab)) + └──── + + As you can see, the built-in support for setting the category + meta-datum is not very easy to use or pretty to look at. To help with + this I recommend the `consult--read' function from the excellent + [Consult] package. With that function we can rewrite the command as + follows: + + ┌──── + │ (defun my-select-tab-by-name (tab) + │ (interactive + │ (list + │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + │ (tab-bar-tabs)) + │ (user-error "No tabs found")))) + │ (consult--read tab-list + │ :prompt "Tabs: " + │ :category 'tab)))) + │ (tab-bar-select-tab-by-name tab)) + └──── + + Much nicer! No matter how you define the `my-select-tab-by-name' + command, the first approach with Marginalia and prompt detection has + the following advantages: you get the `tab' category for all the + `tab-bar-*-bar-by-name' commands at once, also, you enhance built-in + commands, instead of defining new ones. + + + [Marginalia] <https://github.com/minad/marginalia> + + [Consult] <https://github.com/minad/consult/> + + +◊ 3.7.1.2 Defining and configuring a keymap for tab actions + + Let's say we want to offer select, rename and close actions for tabs + (in addition to Embark general actions, such as saving the tab name to + the kill-ring, which you get for free). Then this will do: + + ┌──── + │ (defvar-keymap embark-tab-actions + │ :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + │ :parent embark-general-map + │ "s" #'tab-bar-select-tab-by-name + │ "r" #'tab-bar-rename-tab-by-name + │ "k" #'tab-bar-close-tab-by-name) + │ + │ (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) + └──── + + What if after using this for a while you feel closing the tab without + confirmation is dangerous? You have a couple of options: + + 1. You can keep using the `tab-bar-close-tab-by-name' command, but + have Embark ask you for confirmation: + ┌──── + │ (push #'embark--confirm + │ (alist-get 'tab-bar-close-tab-by-name + │ embark-pre-action-hooks)) + └──── + + 2. You can write your own command that prompts for confirmation and + use that instead of `tab-bar-close-tab-by-name' in the above + keymap: + ┌──── + │ (defun my-confirm-close-tab-by-name (tab) + │ (interactive "sTab to close: ") + │ (when (y-or-n-p (format "Close tab '%s'? " tab)) + │ (tab-bar-close-tab-by-name tab))) + └──── + + Notice that this is a command you can also use directly from `M-x' + independently of Embark. Using it from `M-x' leaves something to be + desired, though, since you don't get completion for the tab names. + You can fix this if you wish as described in the previous section. + + +3.7.2 New target example in regular buffers - short Wikipedia links +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + Say you want to teach Embark to treat text of the form + `wikipedia:Garry_Kasparov' in any regular buffer as a link to + Wikipedia, with actions to open the Wikipedia page in eww or an + external browser or to save the URL of the page in the kill-ring. We + can take advantage of the actions that Embark has preconfigured for + URLs, so all we need to do is teach Embark that + `wikipedia:Garry_Kasparov' stands for the URL + `https://en.wikipedia.org/wiki/Garry_Kasparov'. + + You can be as fancy as you want with the recognized syntax. Here, to + keep the example simple, I'll assume the link matches the regexp + `wikipedia:[[:alnum:]_]+'. We will write a function that looks for a + match surrounding point, and returns a dotted list of the form `'(url + URL-OF-THE-PAGE START . END)' where `START' and `END' are the buffer + positions bounding the target, and are used by Embark to highlight it + if you have `embark-highlight-indicator' included in the list + `embark-indicators'. (There are a couple of other options for the + return value of a target finder: the bounding positions are optional + and a single target finder is allowed to return multiple targets; see + the documentation for `embark-target-finders' for details.) + + ┌──── + │ (defun my-short-wikipedia-link () + │ "Target a link at point of the form wikipedia:Page_Name." + │ (save-excursion + │ (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) + │ (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + │ (str (buffer-substring-no-properties start end))) + │ (save-match-data + │ (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + │ `(url + │ ,(format "https://en.wikipedia.org/wiki/%s" + │ (match-string 1 str)) + │ ,start . ,end)))))) + │ + │ (add-to-list 'embark-target-finders 'my-short-wikipedia-link) + └──── + + +4 How does Embark call the actions? +═══════════════════════════════════ + + Embark actions are normal Emacs commands, that is, functions with an + interactive specification. In order to execute an action, Embark calls + the command with `call-interactively', so the command reads user input + exactly as if run directly by the user. For example the command may + open a minibuffer and read a string (`read-from-minibuffer') or open a + completion interface (`completing-read'). If this happens, Embark + takes the target string and inserts it automatically into the + minibuffer, simulating user input this way. After inserting the + string, Embark exits the minibuffer, submitting the input. (The + immediate minibuffer exit can be disabled for specific actions in + order to allow editing the input; this is done by adding the + `embark--allow-edit' function to the appropriate entry of + `embark-target-injection-hooks'). Embark inserts the target string at + the first minibuffer opened by the action command, and if the command + happens to prompt the user for input more than once, the user still + interacts with the second and further prompts in the normal + fashion. Note that if a command does not prompt the user for input in + the minibuffer, Embark still allows you to use it as an action, but of + course, never inserts the target anywhere. (There are plenty of + examples in the default configuration of commands that do not prompt + the user bound to keys in the action maps, most of the region actions, + for instance.) + + This is how Embark manages to reuse normal commands as actions. The + mechanism allows you to use as Embark actions commands that were not + written with Embark in mind (and indeed almost all actions that are + bound by default in Embark's action keymaps are standard Emacs + commands). It also allows you to write new custom actions in such a + way that they are useful even without Embark. + + Staring from version 28.1, Emacs has a variable + `y-or-n-p-use-read-key', which when set to `t' causes `y-or-n-p' to + use `read-key' instead of `read-from-minibuffer'. Setting + `y-or-n-p-use-read-key' to `t' is recommended for Embark users because + it keeps Embark from attempting to insert the target at a `y-or-n-p' + prompt, which would almost never be sensible. Also consider this as a + warning to structure your own action commands so that if they use + `y-or-n-p', they do so only after the prompting for the target. + + Here is a simple example illustrating the various ways of reading + input from the user mentioned above. Bind the following commands to + the `embark-symbol-map' to be used as actions, then put the point on + some symbol and run them with `embark-act': + + ┌──── + │ (defun example-action-command1 () + │ (interactive) + │ (message "The input was `%s'." (read-from-minibuffer "Input: "))) + │ + │ (defun example-action-command2 (arg input1 input2) + │ (interactive "P\nsInput 1: \nsInput 2: ") + │ (message "The first input %swas `%s', and the second was `%s'." + │ (if arg "truly " "") + │ input1 + │ input2)) + │ + │ (defun example-action-command3 () + │ (interactive) + │ (message "Your selection was `%s'." + │ (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + │ + │ (defun example-action-command4 () + │ (interactive) + │ (message "I don't prompt you for input and thus ignore the target!")) + │ + │ (keymap-set embark-symbol-map "X 1" #'example-action-command1) + │ (keymap-set embark-symbol-map "X 2" #'example-action-command2) + │ (keymap-set embark-symbol-map "X 3" #'example-action-command3) + │ (keymap-set embark-symbol-map "X 4" #'example-action-command4) + └──── + + Also note that if you are using the key bindings to call actions, you + can pass prefix arguments to actions in the normal way. For example, + you can use `C-u X2' with the above demonstration actions to make the + message printed by `example-action-command2' more emphatic. This + ability to pass prefix arguments to actions is useful for some actions + in the default configuration, such as + `embark-shell-command-on-buffer'. + + +4.1 Non-interactive functions as actions +──────────────────────────────────────── + + Alternatively, Embark does support one other type of action: a + non-interactive function of a single argument. The target is passed as + argument to the function. For example: + + ┌──── + │ (defun example-action-function (target) + │ (message "The target was `%s'." target)) + │ + │ (keymap-set embark-symbol-map "X 4" #'example-action-function) + └──── + + Note that normally binding non-interactive functions in a keymap is + useless, since when attempting to run them using the key binding you + get an error message similar to "Wrong type argument: commandp, + example-action-function". In general it is more flexible to write any + new Embark actions as commands, that is, as interactive functions, + because that way you can also run them directly, without Embark. But + there are a couple of reasons to use non-interactive functions as + actions: + + 1. You may already have the function lying around, and it is + convenient to simply reuse it. + + 2. For command actions the targets can only be simple string, with no + text properties. For certain advanced uses you may want the action + to receive a string /with/ some text properties, or even a + non-string target. + + +5 Embark, Marginalia and Consult +════════════════════════════════ + + Embark cooperates well with the [Marginalia] and [Consult] packages. + Neither of those packages is a dependency of Embark, but both are + highly recommended companions to Embark, for opposite reasons: + Marginalia greatly enhances Embark's usefulness, while Embark can help + enhance Consult. + + In the remainder of this section I'll explain what exactly Marginalia + does for Embark, and what Embark can do for Consult. + + +[Marginalia] <https://github.com/minad/marginalia> + +[Consult] <https://github.com/minad/consult> + +5.1 Marginalia +────────────── + + Embark comes with actions for symbols (commands, functions, variables + with actions such as finding the definition, looking up the + documentation, evaluating, etc.) in the `embark-symbol-map' keymap, + and for packages (actions like install, delete, browse url, etc.) in + the `embark-package-keymap'. + + Unfortunately Embark does not automatically offers you these keymaps + when relevant, because many built-in Emacs commands don't report + accurate category metadata. For example, a command like + `describe-package', which reads a package name from the minibuffer, + does not have metadata indicating this fact. + + In an earlier Embark version, there were functions to supply this + missing metadata, but they have been moved to Marginalia, which + augments many Emacs command to report accurate category metadata. + Simply activating `marginalia-mode' allows Embark to offer you the + package and symbol actions when appropriate again. Candidate + annotations in the Embark collect buffer are also provided by the + Marginalia package: + + • If you install Marginalia and activate `marginalia-mode', Embark + Collect buffers will use the Marginalia annotations automatically. + + • If you don't install Marginalia, you will see only the annotations + that come with Emacs (such as key bindings in `M-x', or the unicode + characters in `C-x 8 RET'). + + +5.2 Consult +─────────── + + The excellent Consult package provides many commands that use + minibuffer completion, via the `completing-read' function; plenty of + its commands can be considered enhanced versions of built-in Emacs + commands, and some are completely new functionality. One common + enhancement provided in all commands for which it makes sense is + preview functionality, for example `consult-buffer' will show you a + quick preview of a buffer before you actually switch to it. + + If you use both Consult and Embark you should install the + `embark-consult' package which provides integration between the + two. It provides exporters for several Consult commands and also + tweaks the behavior of many Consult commands when used as actions with + `embark-act' in subtle ways that you may not even notice, but make for + a smoother experience. You need only install it to get these benefits: + Embark will automatically load it after Consult if found. + + The `embark-consult' package provides the following exporters: + + • You can use `embark-export' from `consult-line', `consult-outline', + or `consult-mark' to obtain an `occur-mode' buffer. As with the + built-in `occur' command you use that buffer to jump to a match and + after that, you can then use `next-error' and `previous-error' to + navigate to other matches. You can also press `e' to activate + `occur-edit-mode' and edit the matches in place! + + • You can export from any of the Consult asynchronous search commands, + `consult-grep', `consult-git-grep', or `consult-ripgrep' to get a + `grep-mode' buffer. Here too you can use `next-error' and + `previous-error' to navigate among matches, and, if you install the + [wgrep] package, you can use it to edit the matches in place. + + In both cases, pressing `g' will rerun the Consult command you had + exported from and re-enter the input you had typed (which is similar + to reverting but a little more flexible). You can then proceed to + re-export if that's what you want, but you can also edit the input + changing the search terms or simply cancel if you see you are done + with that search. + + The `embark-consult' also contains some candidates collectors that + allow you to run `embark-live' to get a live-updating table of + contents for your buffer: + + • `embark-consult-outline-candidates' produces the outline headings of + the current buffer, using `consult-outline'. + • `embark-consult-imenu-candidates' produces the imenu items of the + current buffer, using `consult-imenu'. + • `embark-consult-imenu-or-outline-candidates' is a simple combination + of the two previous functions: it produces imenu items in buffers + deriving from `prog-mode' and otherwise outline headings. + + The way to configure `embark-live' (or `embark-collect' and + `embark-export' for that matter) to use one of these function is to + add it at the end of the `embark-candidate-collectors' list. The + `embark-consult' package by default adds the last one, which seems to + be the most sensible default. + + Besides those exporters and candidate collectors, the `embark-consult' + package provides many subtle tweaks and small integrations between + Embark and Consult. Some examples are: + + • The asynchronous search commands will start in the directory + associated to the Embark target if that target is a file, buffer, + bookmark or Emacs Lisp library. + + • For all other target types, a Consult search command (asynchronous + or not) will search for the text of the target but leave the + minibuffer open so you can interact with the Consult command. + + • `consult-imenu' will search for the target and take you directly to + the location if it matches a unique imenu entry, otherwise it will + leave the minibuffer open so you can navigate among the matches. + + +[wgrep] <http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el +> + + +6 Resources +═══════════ + + If you want to learn more about how others have used Embark here are + some links to read: + + • [Fifteen ways to use Embark], a blog post by Karthik Chikmagalur. + • [Protesilaos Stavrou's dotemacs], look for the section called + "Extended minibuffer actions and more (embark.el and + prot-embark.el)" + + And some videos to watch: + + • [Embark and my extras] by Protesilaos Stavrou. + • [Embark – Key features and tweaks] by Raoul Comninos on the + Emacs-Elements YouTube channel. + • [Livestreamed: Adding an Embark context action to send a stream + message] by Sacha Chua. + • [System Crafters Live! - The Many Uses of Embark] by David Wilson. + • [Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] + by Mike Zamansky. + + +[Fifteen ways to use Embark] +<https://karthinks.com/software/fifteen-ways-to-use-embark/> + +[Protesilaos Stavrou's dotemacs] <https://protesilaos.com/dotemacs/> + +[Embark and my extras] +<https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/> + +[Embark – Key features and tweaks] <https://youtu.be/qpoQiiinCtY> + +[Livestreamed: Adding an Embark context action to send a stream message] +<https://youtu.be/WsxXr1ncukY> + +[System Crafters Live! - The Many Uses of Embark] +<https://youtu.be/qk2Is_sC8Lk> + +[Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] +<https://youtu.be/5ffb2at2d7w> + + +7 Contributions +═══════════════ + + Contributions to Embark are very welcome. There is a [wish list] for + actions, target finders, candidate collectors and exporters. For other + ideas you have for Embark, feel free to open an issue on the [issue + tracker]. Any neat configuration tricks you find might be a good fit + for the [wiki]. + + Code contributions are very welcome too, but since Embark is now on + GNU ELPA, copyright assignment to the FSF is required before you can + contribute code. + + +[wish list] <https://github.com/oantolin/embark/issues/95> + +[issue tracker] <https://github.com/oantolin/embark/issues> + +[wiki] <https://github.com/oantolin/embark/wiki> + + +8 Acknowledgments +═════════════════ + + While I, Omar Antolín Camarena, have written most of the Embark code + and remain very stubborn about some of the design decisions, Embark + has received substantial help from a number of other people which this + document has neglected to mention for far too long. In particular, + Daniel Mendler has been absolutely invaluable, implementing several + important features, and providing a lot of useful advice. + + Code contributions: + + • [Daniel Mendler] + • [Clemens Radermacher] + • [José Antonio Ortega Ruiz] + • [Itai Y. Efrat] + • [a13] + • [jakanakaevangeli] + • [mihakam] + • [Brian Leung] + • [Karthik Chikmagalur] + • [Roshan Shariff] + • [condy0919] + • [Damien Cassou] + • [JimDBh] + + Advice and useful discussions: + + • [Daniel Mendler] + • [Protesilaos Stavrou] + • [Clemens Radermacher] + • [Howard Melman] + • [Augusto Stoffel] + • [Bruce d'Arcus] + • [JD Smith] + • [Karthik Chikmagalur] + • [jakanakaevangeli] + • [Itai Y. Efrat] + • [Mohsin Kaleem] + + +[Daniel Mendler] <https://github.com/minad> + +[Clemens Radermacher] <https://github.com/clemera/> + +[José Antonio Ortega Ruiz] <https://codeberg.org/jao/> + +[Itai Y. Efrat] <https://github.com/iyefrat> + +[a13] <https://github.com/a13> + +[jakanakaevangeli] <https://github.com/jakanakaevangeli> + +[mihakam] <https://github.com/mihakam> + +[Brian Leung] <https://github.com/leungbk> + +[Karthik Chikmagalur] <https://github.com/karthink> + +[Roshan Shariff] <https://github.com/roshanshariff> + +[condy0919] <https://github.com/condy0919> + +[Damien Cassou] <https://github.com/DamienCassou> + +[JimDBh] <https://github.com/JimDBh> + +[Protesilaos Stavrou] <https://gitlab.com/protesilaos/> + +[Howard Melman] <https://github.com/hmelman/> + +[Augusto Stoffel] <https://github.com/astoff> + +[Bruce d'Arcus] <https://github.com/bdarcus> + +[JD Smith] <https://github.com/jdtsmith> + +[Mohsin Kaleem] <https://github.com/mohkale> diff --git a/elpa/embark-0.22.1/README.org b/elpa/embark-0.22.1/README.org @@ -0,0 +1,1170 @@ +#+TITLE: Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +#+OPTIONS: d:nil +#+EXPORT_FILE_NAME: embark.texi +#+TEXINFO_DIR_CATEGORY: Emacs misc features +#+TEXINFO_DIR_TITLE: Embark: (embark). +#+TEXINFO_DIR_DESC: Emacs Mini-Buffer Actions Rooted in Keymaps + +#+html: <a href="http://elpa.gnu.org/packages/embark.html"><img alt="GNU ELPA" src="https://elpa.gnu.org/packages/embark.svg"/></a> +#+html: <a href="http://elpa.gnu.org/devel/embark.html"><img alt="GNU-devel ELPA" src="https://elpa.gnu.org/devel/embark.svg"/></a> +#+html: <a href="https://melpa.org/#/embark"><img alt="MELPA" src="https://melpa.org/packages/embark-badge.svg"/></a> +#+html: <a href="https://stable.melpa.org/#/embark"><img alt="MELPA Stable" src="https://stable.melpa.org/packages/embark-badge.svg"/></a> + +* Overview + +Embark makes it easy to choose a command to run based on what is near +point, both during a minibuffer completion session (in a way familiar +to Helm or Counsel users) and in normal buffers. Bind the command +=embark-act= to a key and it acts like prefix-key for a keymap of +/actions/ (commands) relevant to the /target/ around point. With point on +an URL in a buffer you can open the URL in a browser or eww or +download the file it points to. If while switching buffers you spot an +old one, you can kill it right there and continue to select another. +Embark comes preconfigured with over a hundred actions for common +types of targets such as files, buffers, identifiers, s-expressions, +sentences; and it is easy to add more actions and more target types. +Embark can also collect all the candidates in a minibuffer to an +occur-like buffer or export them to a buffer in a major-mode specific +to the type of candidates, such as dired for a set of files, ibuffer +for a set of buffers, or customize for a set of variables. + +** Acting on targets + +You can think of =embark-act= as a keyboard-based version of a +right-click contextual menu. The =embark-act= command (which you should +bind to a convenient key), acts as a prefix for a keymap offering you +relevant /actions/ to use on a /target/ determined by the context: + +- In the minibuffer, the target is the current top completion + candidate. +- In the =*Completions*= buffer the target is the completion at point. +- In a regular buffer, the target is the region if active, or else the + file, symbol, URL, s-expression or defun at point. + +Multiple targets can be present at the same location and you can cycle +between them by repeating the =embark-act= key binding. The type of +actions offered depend on the type of the target. Here is a sample of +a few of the actions offered in the default configuration: + +- For files you get offered actions like deleting, copying, + renaming, visiting in another window, running a shell command on the + file, etc. +- For buffers the actions include switching to or killing the buffer. +- For package names the actions include installing, removing or + visiting the homepage. +- For Emacs Lisp symbols the actions include finding the definition, + looking up documentation, evaluating (which for a variable + immediately shows the value, but for a function lets you pass it + some arguments first). There are some actions specific to variables, + such as setting the value directly or though the customize system, + and some actions specific to commands, such as binding it to a key. + +By default when you use =embark-act= if you don't immediately select an +action, after a short delay Embark will pop up a buffer showing a list +of actions and their corresponding key bindings. If you are using +=embark-act= outside the minibuffer, Embark will also highlight the +current target. These behaviors are configurable via the variable +=embark-indicators=. Instead of selecting an action via its key binding, +you can select it by name with completion by typing =C-h= after +=embark-act=. + +Everything is easily configurable: determining the current target, +classifying it, and deciding which actions are offered for each type +in the classification. The above introduction just mentions part of +the default configuration. + +Configuring which actions are offered for a type is particularly easy +and requires no programming: the variable =embark-keymap-alist= +associates target types with variables containing keymaps, and those +keymaps containing bindings for the actions. (To examine the available +categories and their associated keymaps, you can use =C-h v +embark-keymap-alist= or customize that variable.) For example, in the +default configuration the type =file= is associated with the symbol +=embark-file-map=. That symbol names a keymap with single-letter key +bindings for common Emacs file commands, for instance =c= is bound to +=copy-file=. This means that if you are in the minibuffer after running +a command that prompts for a file, such as =find-file= or =rename-file=, +you can copy a file by running =embark-act= and then pressing =c=. + +These action keymaps are very convenient but not strictly necessary +when using =embark-act=: you can use any command that reads from the +minibuffer as an action and the target of the action will be inserted +at the first minibuffer prompt. After running =embark-act= all of your +key bindings and even =execute-extended-command= can be used to run a +command. For example, if you want to replace all occurrences of the +symbol at point, just use =M-%= as the action, there is no need to bind +=query-replace= in one of Embark's keymaps. Also, those action keymaps +are normal Emacs keymaps and you should feel free to bind in them +whatever commands you find useful as actions and want to be available +through convenient bindings. + +The actions in =embark-general-map= are available no matter what type +of completion you are in the middle of. By default this includes +bindings to save the current candidate in the kill ring and to insert +the current candidate in the previously selected buffer (the buffer +that was current when you executed a command that opened up the +minibuffer). + +Emacs's minibuffer completion system includes metadata indicating the +/category/ of what is being completed. For example, =find-file='s +metadata indicates a category of =file= and =switch-to-buffer='s metadata +indicates a category of =buffer=. Embark has the related notion of the +/type/ of a target for actions, and by default when category metadata +is present it is taken to be the type of minibuffer completion +candidates when used as targets. Emacs commands often do not set +useful category metadata so the [[https://github.com/minad/marginalia][Marginalia]] package, which supplies +this missing metadata, is highly recommended for use with Embark. + +Embark's default configuration has actions for the following target +types: files, buffers, symbols, packages, URLs, bookmarks, and as a +somewhat special case, actions for when the region is active. You can +read about the [[https://github.com/oantolin/embark/wiki/Default-Actions][default actions and their key bindings]] on the GitHub +project wiki. + +** The default action on a target + +Embark has a notion of default action for a target: + +- If the target is a minibuffer completion candidate, then the default + action is whatever command opened the minibuffer in the first place. + For example if you run =kill-buffer=, then the default action will be + to kill buffers. +- If the target comes from a regular buffer (i.e., not a minibuffer), + then the default action is whatever is bound to =RET= in the keymap of + actions for that type of target. For example, in Embark's default + configuration for a URL found at point the default action is + =browse-url=, because =RET= is bound to =browse-url= in the =embark-url-map= + keymap. + +To run the default action you can press =RET= after running =embark-act=. +Note that if there are several different targets at a given location, +each has its own default action, so first cycle to the target you want +and then press =RET= to run the corresponding default action. + +There is also =embark-dwim= which runs the default action for the first +target found. It's pretty handy in non-minibuffer buffers: with +Embark's default configuration it will: + +- Open the file at point. +- Open the URL at point in a web browser (using the =browse-url= + command). +- Compose a new email to the email address at point. +- In an Emacs Lisp buffer, if point is on an opening parenthesis or + right after a closing one, it will evaluate the corresponding + expression. +- Go to the definition of an Emacs Lisp function, variable or macro at + point. +- Find the file corresponding to an Emacs Lisp library at point. + +** Working with sets of possible targets + +Besides acting individually on targets, Embark lets you work +collectively on a set of target /candidates/. For example, while you are +in the minibuffer the candidates are simply the possible completions +of your input. Embark provides three main commands to work on candidate +sets: + +- The =embark-act-all= command runs the same action on each of the + current candidates. It is just like using =embark-act= on each + candidate in turn. (Because you can easily act on many more + candidates than you meant to, by default Embark asks you to confirm + uses of =embark-act-all=; you can turn this off by setting the user + option =embark-confirm-act-all= to =nil=.) + +- The =embark-collect= command produces a buffer listing all the current + candidates, for you to peruse and run actions on at your leisure. + The candidates are displayed as a list showing additional annotations. + + The Embark Collect buffer is somewhat "dired-like": you can select + and deselect candidates through the =embark-select= action (bound to + =SPC=). In an Embark Collect buffer =embark-act= is bound to =a= and + =embark-act-all= is bound to =A=; =embark-act-all= will act on all + currently marked candidates if there any, and will act on all + candidates if none are marked. In particular, this means that =a SPC= + will toggle whether the candidate at point is selected, and =A SPC= + will select all candidates if none are selected, or deselect all + selected candidates if there are some. + +- The =embark-export= command tries to open a buffer in an appropriate + major mode for the set of candidates. If the candidates are files + export produces a Dired buffer; if they are buffers, you get an + Ibuffer buffer; and if they are packages you get a buffer in + package menu mode. + + If you use the grepping commands from the [[https://github.com/minad/consult/][Consult]] package, + =consult-grep=, =consult-git-grep= or =consult-ripgrep=, then you should + install the =embark-consult= package, which adds support for exporting a + list of grep results to an honest grep-mode buffer, on which you can + even use [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] if you wish. + +When in doubt choosing between exporting and collecting, a good rule +of thumb is to always prefer =embark-export= since when an exporter to a +special major mode is available for a given type of target, it will be +more featureful than an Embark collect buffer, and if no such exporter +is configured the =embark-export= command falls back to the generic +=embark-collect=. + +These commands are always available as "actions" (although they do not +act on just the current target but on all candidates) for =embark-act= +and are bound to =A=, =S= (for "snapshot"), and =E=, respectively, in +=embark-general-map=. This means that you do not have to bind your own +key bindings for these (although you can, of course!), just a key +binding for =embark-act=. + +In Embark Collect or Embark Export buffers that were obtained by +running =embark-collect= or =embark-export= from within a minibuffer +completion session, =g= is bound to a command that restarts the +completion session, that is, the command that opened the minibuffer is +run again and the minibuffer contents restored. You can then interact +normally with the command, perhaps editing the minibuffer contents, +and, if you wish, you can rerun =embark-collect= or =embark-export= to get +an updated buffer. + +*** Selecting some targets to make an ad hoc candidate set + +The commands for working with sets of candidates just described, +namely =embark-act-all=, =embark-export= and =embark-collect= by default +work with all candidates defined in the current context. For example, +in the minibuffer they operate on all currently completion candidates, +or in a dired buffer they work on all marked files (or all files if +none are marked). Embark also has a notion of /selection/, where you can +accumulate an ad hoc list of targets for these commands to work on. + +The selection is controlled by using the =embark-select= action (which +must be run as an action through =embark-act=), bound to =SPC= in +=embark-general-map= so that it is always available. Calling this action +on a target toggles its membership in the current buffer's Embark +selection; that is, it adds it to selection if not selected and +removes it from the selection if it was selected. Whenever the +selection for a buffer is non-empty, the commands =embark-act-all=, +=embark-export= and =embark-collect= will act on the selection. + +To deselect all selected targets, you can use the =embark-select= action +through =embark-act-all=, since this will run =embark-select= on each +member of the current selection. Similarly if no targets are selected +and you are in a minibuffer completion session, running =embark-select= +from =embark-act-all= will select all the current completion candidates. + +This functionality is supported everywhere: + +- In the minibuffer this gives a convenient way to act on several + completion candidates that don't follow any simple pattern: just go + through the completions selecting the ones you want, then use + =embark-act-all=. For example, you could attach several files at once + to an email. +- For Embark Collect buffers this functionality enables a dired-like + workflow, in which you mark various candidates and apply an action + to all at once. (It supersedes a previous ad hoc dired-like + interface that was implemented only in Embark Collect buffers, with + a slightly different interface.) +- In a eww buffer you could use this to select various links you wish + to follow up on, and then collect them into a buffer. Similarly, + while reading Emacs's info manual you could select some symbols you + want to read more about and export them to an =apropos-mode= buffer. +- You can use selections in regular text or programming buffers to do + complex editing operations. For example, if you have three + paragraphs scattered over a file and you want to bring them + together, you can select each one, insert them all somewhere and + finally delete all of them (from their original locations). + + +*** =embark-live= a live-updating variant of =embark-collect= + +Finally, there is also an =embark-live= variant of the =embark-collect= +command which automatically updates the collection after each change +in the source buffer. Users of a completion UI that automatically +updates and displays the candidate list (such as Vertico, Icomplete, +Fido-mode, or MCT) will probably not want to use +=embark-live= from the minibuffer as they will then have two live +updating displays of the completion candidates! + +A more likely use of =embark-live= is to be called from a regular buffer +to display a sort of live updating "table of contents" for the buffer. +This depends on having appropriate candidate collectors configured in +=embark-candidate-collectors=. There are not many in Embark's default +configuration, but you can try this experiment: open a dired buffer in +a directory that has very many files, mark a few, and run =embark-live=. +You'll get an Embark Collect buffer containing only the marked files, +which updates as you mark or unmark files in dired. To make +=embark-live= genuinely useful other candidate collectors are required. +The =embark-consult= package (documented near the end of this manual) +contains a few: one for imenu items and one for outline headings as +used by =outline-minor-mode=. Those collectors really do give +=embark-live= a table-of-contents feel. + +** Switching to a different command without losing what you've typed + +Embark also has the =embark-become= command which is useful for when +you run a command, start typing at the minibuffer and realize you +meant a different command. The most common case for me is that I run +=switch-to-buffer=, start typing a buffer name and realize I haven't +opened the file I had in mind yet! I'll use this situation as a +running example to illustrate =embark-become=. When this happens I can, +of course, press =C-g= and then run =find-file= and open the file, but +this requires retyping the portion of the file name you already +typed. This process can be streamlined with =embark-become=: while still +in the =switch-to-buffer= you can run =embark-become= and effectively +make the =switch-to-buffer= command become =find-file= for this run. + +You can bind =embark-become= to a key in =minibuffer-local-map=, but it is +also available as an action under the letter =B= (uppercase), so you +don't need a binding if you already have one for =embark-act=. So, +assuming I have =embark-act= bound to, say, =C-.=, once I realize I +haven't open the file I can type =C-. B C-x C-f= to have +=switch-to-buffer= become =find-file= without losing what I have already +typed in the minibuffer. + +But for even more convenience, =embark-become= offers shorter key +bindings for commands you are likely to want the current command to +become. When you use =embark-become= it looks for the current command in +all keymaps named in the list =embark-become-keymaps= and then activates +all keymaps that contain it. For example, the default value of +=embark-become-keymaps= contains a keymap =embark-become-file+buffer-map= +with bindings for several commands related to files and buffers, in +particular, it binds =switch-to-buffer= to =b= and =find-file= to =f=. So when +I accidentally try to switch to a buffer for a file I haven't opened +yet, =embark-become= finds that the command I ran, =switch-to-buffer=, is +in the keymap =embark-become-file+buffer-map=, so it activates that +keymap (and any others that also contain a binding for +=switch-to-buffer=). The end result is that I can type =C-. B f= to +switch to =find-file=. + +* Quick start + +The easiest way to install Embark is from GNU ELPA, just run =M-x +package-install RET embark RET=. (It is also available on MELPA.) It is +highly recommended to also install [[https://github.com/minad/marginalia][Marginalia]] (also available on GNU +ELPA), so that Embark can offer you preconfigured actions in more +contexts. For =use-package= users, the following is a very reasonable +starting configuration: + +#+begin_src emacs-lisp + (use-package marginalia + :ensure t + :config + (marginalia-mode)) + + (use-package embark + :ensure t + + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc + ;; strategy, if you want to see the documentation from multiple providers. + (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) + ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) + + :config + + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + + ;; Consult users will also want the embark-consult package. + (use-package embark-consult + :ensure t ; only need to install it, embark loads it after consult if found + :hook + (embark-collect-mode . consult-preview-at-point-mode)) +#+end_src + +About the suggested key bindings for =embark-act= and =embark-dwim=: +- Those key bindings are unlikely to work in the terminal, but + terminal users are probably well aware of this and will know to + select different bindings. +- The suggested =C-.= binding is used by default in (at least some + installations of) GNOME to input emojis, and Emacs doesn't even get + a chance to respond to the binding. You can select a different key + binding for =embark-act= or use =ibus-setup= to change the shortcut for + emoji insertion (Emacs 29 will likely use =C-x 8 e e=, in case you + want to set the same one system-wide). +- The suggested alternative of =M-.= for =embark-dwim= is bound by default + to =xref-find-definitions=. That is a very useful command but + overwriting it with =embark-dwim= is sensible since in Embark's + default configuration, =embark-dwim= will also find the definition of + the identifier at point. (Note that =xref-find-definitions= with a + prefix argument prompts you for an identifier, =embark-dwim= does not + cover this case). + +Other Embark commands such as =embark-act-all=, =embark-become=, +=embark-collect=, and =embark-export= can be run through =embark-act= as +actions bound to =A=, =B=, =S= (for "snapshot"), and =E= respectively, and +thus don't really need a dedicated key binding, but feel free to bind +them directly if you so wish. If you do choose to bind them directly, +you'll probably want to bind them in =minibuffer-local-map=, since they +are most useful in the minibuffer (in fact, =embark-become= only works +in the minibuffer). + +The command =embark-dwim= executes the default action at point. Another good +keybinding for =embark-dwim= is =M-.= since =embark-dwim= acts like +=xref-find-definitions= on the symbol at point. =C-.= can be seen as a +right-click context menu at point and =M-.= acts like left-click. The +keybindings are mnemonic, both act at the point (=.=). + +Embark needs to know what your minibuffer completion system considers +to be the list of candidates and which one is the current candidate. +Embark works out of the box if you use Emacs's default tab completion, +the built-in =icomplete-mode= or =fido-mode=, or the third-party packages +[[https://github.com/minad/vertico][Vertico]] or [[https://github.com/abo-abo/swiper][Ivy]]. + +If you are a [[https://emacs-helm.github.io/helm/][Helm]] or [[https://github.com/abo-abo/swiper][Ivy]] user you are unlikely to want Embark since +those packages include comprehensive functionality for acting on +minibuffer completion candidates. (Embark does come with Ivy +integration despite this.) + +* Advanced configuration +** Showing information about available targets and actions + +By default, if you run =embark-act= and do not immediately select an +action, after a short delay Embark will pop up a buffer called =*Embark +Actions*= containing a list of available actions with their key +bindings. You can scroll that buffer with the mouse of with the usual +commands =scroll-other-window= and =scroll-other-window-down= (bound by +default to =C-M-v= and =C-M-S-v=). + +That functionality is provided by the =embark-mixed-indicator=, but +Embark has other indicators that can provide information about the +target and its type, what other targets you can cycle to, and which +actions have key bindings in the action map for the current type of +target. Any number of indicators can be active at once and the user +option =embark-indicators= should be set to a list of the desired +indicators. + +Embark comes with the following indicators: + +- =embark-minimal-indicator=: shows a messages in the echo area or + minibuffer prompt showing the current target and the types of all + targets starting with the current one; this one is on by default. + +- =embark-highlight-indicator=: highlights the target at point; + also on by default. + +- =embark-verbose-indicator=: displays a table of actions and their key + bindings in a buffer; this is not on by default, in favor of the + mixed indicator described next. + +- =embark-mixed-indicator=: starts out by behaving as the minimal + indicator but after a short delay acts as the verbose indicator; + this is on by default. + +- =embark-isearch-highlight-indicator=: this only does something when + the current target is the symbol at point, in which case it + lazily highlights all occurrences of that symbol in the current + buffer, like isearch; also on by default. + +Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the +=embark-which-key-indicator= from the [[https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt][Embark wiki]]. Just copy its +definition from the wiki into your configuration and customize the +=embark-indicators= user option to exclude the mixed and verbose +indicators and to include =embark-which-key-indicator=. + +** Selecting commands via completions instead of key bindings + +As an alternative to reading the list of actions in the verbose or +mixed indicators (see the previous section for a description of +these), you can press the =embark-help-key=, which is =C-h= by default +(but you may prefer =?= to free up =C-h= for use as a prefix) after +running =embark-act=. Pressing the help key will prompt you for the name +of an action with completion (but feel free to enter a command that is +not among the offered candidates!), and will also remind you of the +key bindings. You can press =embark-keymap-prompter-key=, which is =@= by +default, at the prompt and then one of the key bindings to enter the +name of the corresponding action. + +You may think that with the =*Embark Actions*= buffer popping up to +remind you of the key bindings you'd never want to use completion to +select an action by name, but personally I find that typing a small +portion of the action name to narrow down the list of candidates feels +significantly faster than visually scanning the entire list of actions. + +If you find you prefer entering actions that way, you can configure +embark to always prompt you for actions by setting the variable +=embark-prompter= to =embark-completing-read-prompter=. + +** Quitting the minibuffer after an action + +By default, if you call =embark-act= from the minibuffer it quits the +minibuffer after performing the action. You can change this by setting +the user option =embark-quit-after-action= to =nil=. Having =embark-act= /not/ +quit the minibuffer can be useful to turn commands into little "thing +managers". For example, you can use =find-file= as a little file manager +or =describe-package= as a little package manager: you can run those +commands, perform a series of actions, and then quit the command. + +If you want to control the quitting behavior in a fine-grained manner +depending on the action, you can set =embark-quit-after-action= to an +alist, associating commands to either =t= for quitting or =nil= for not +quitting. When using an alist, you can use the special key =t= to +specify the default behavior. For example, to specify that by default +actions should not quit the minibuffer but that using =kill-buffer= as +an action should quit, you can use the following configuration: + +#+begin_src emacs-lisp + (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) +#+end_src + +The variable =embark-quit-after-action= only specifies a default, that +is, it only controls whether or not =embark-act= quits the minibuffer +when you call it without a prefix argument, and you can select the +opposite behavior to what the variable says by calling =embark-act= with +=C-u=. Also note that both the variable =embark-quit-after-action= and =C-u= +have no effect when you call =embark-act= outside the minibuffer. + +If you find yourself using the quitting and non-quitting variants of +=embark-act= about equally often, independently of the action, you may +prefer to simply have separate commands for them instead of a single +command that you call with =C-u= half the time. You could, for example, +keep the default exiting behavior of =embark-act= and define a +non-quitting version as follows: + +#+begin_src emacs-lisp + (defun embark-act-noquit () + "Run action but don't quit the minibuffer afterwards." + (interactive) + (let ((embark-quit-after-action nil)) + (embark-act))) +#+end_src + +** Running some setup after injecting the target + +You can customize what happens after the target is inserted at the +minibuffer prompt of an action. There are +=embark-target-injection-hooks=, that are run by default after injecting +the target into the minibuffer. The variable +=embark-target-injection-hooks= is an alist associating commands to +their setup hooks. There are two special keys: if no setup hook is +specified for a given action, the hook associated to =t= is run; and the +hook associated to =:always= is run regardless of the action. (This +variable used to have the less explicit name of +=embark-setup-action-hooks=, so please update your configuration.) + +For example, consider using =shell-command= as an action during file +completion. It would be useful to insert a space before the target +file name and to leave the point at the beginning, so you can +immediately type the shell command to run on that file. That's why in +Embark's default configuration there is an entry in +=embark-target-injection-hooks= associating =shell-command= to a hook that +includes =embark--shell-prep=, a simple helper function that quotes all +the spaces in the file name, inserts an extra space at the beginning +of the line and leaves point to the left of it. + +Now, the preparation that =embark--shell-prep= does would be useless if +Embark did what it normally does after it inserts the target of the +action at the minibuffer prompt, which is to "press =RET=" for you, +accepting the target as is; if Embark did that for =shell-command= you +wouldn't get a chance to type in the command to execute! That is why +in Embark's default configuration the entry for =shell-command= in +=embark-target-injection-hooks= also contains the function +=embark--allow-edit=. + +Embark used to have a dedicated variable =embark-allow-edit-actions= to +which you could add commands for which Embark should forgo pressing +=RET= for you after inserting the target. Since its effect can also be +achieved via the general =embark-target-injection-hooks= mechanism, that +variable has been removed to simply Embark. Be sure to update your +configuration; if you had something like: + +#+begin_src emacs-lisp + (add-to-list 'embark-allow-edit-actions 'my-command) +#+end_src + +you should replace it with: + +#+begin_src emacs-lisp + (push 'embark--allow-edit + (alist-get 'my-command embark-target-injection-hooks)) +#+end_src + + +Also note that while you could abuse =embark--allow-edit= so that you +have to confirm "dangerous" actions such as =delete-file=, it is better +to implement confirmation by adding the =embark--confirm= function to +the appropriate entry of a different hook alist, namely, +=embark-pre-action-hooks=. + +Besides =embark--allow-edit=, Embark comes with another function that is +of general utility in action setup hooks: =embark--ignore-target=. Use +it for commands that do prompt you in the minibuffer but for which +inserting the target would be inappropriate. This is not a common +situation but does occasionally arise. For example it is used by +default for =shell-command-on-region=: that command is used as an action +for region targets, and it prompts you for a shell command; you +typically do /not/ want the target, that is the contents of the region, +to be entered at that prompt! + +** Running hooks before, after or around an action + +Embark has three variables, =embark-pre-action-hooks=, +=embark-post-action-hooks= and =embark-around-action-hooks=, which are +alists associating commands to hooks that should run before or after +or as around advice for the command when used as an action. As with +=embark-target-injection-hooks=, there are two special keys for the +alists: =t= designates the default hook to run when no specific hook is +specified for a command; and the hook associated to =:always= runs +regardless. + +The default values of those variables are fairly extensive, adding +creature comforts to make running actions a smooth experience. Embark +comes with several functions intended to be added to these hooks, and +used in the default values of =embark-pre-action-hooks=, +=embark-post-action-hooks= and =embark-around-action-hooks=. + +For pre-action hooks: + +- =embark--confirm= :: Prompt the user for confirmation before executing + the action. This is used be default for commands deemed "dangerous", + or, more accurately, hard to undo, such as =delete-file= and + =kill-buffer=. + +- =embark--unmark-target= :: Unmark the active region. Use this for + commands you want to act on the region contents but without the + region being active. The default configuration uses this function as + a pre-action hook for =occur= and =query-replace=, for example, so that + you can use them as actions with region targets to search the whole + buffer for the text contained in the region. Without this pre-action + hook using =occur= as an action for a region target would be + pointless: it would search for the the region contents /in the + region/, (typically, due to the details of regexps) finding only one + match! + +- =embark--beginning-of-target= :: Move to the beginning of the target + (for targets that report bounds). This is used by default for + backward motion commands such as =backward-sexp=, so that they don't + accidentally leave you on the current target. + +- =embark--end-of-target= :: Move to the end of the target. This is used + similarly to the previous function, but also for commands that act + on the last s-expression like =eval-last-sexp=. This allow you to act + on an s-expression from anywhere inside it and still use + =eval-last-sexp= as an action. + +- =embark--xref-push-markers= :: Push the current location on the xref + marker stack. Use this for commands that take you somewhere and for + which you'd like to be able to come back to where you were using + =xref-pop-marker-stack=. This is used by default for =find-library=. + +For post-action hooks: + +- =embark--restart= :: Restart the command currently prompting in the + minibuffer, so that the list of completion candidates is updated. + This is useful as a post action hook for commands that delete or + rename a completion candidate; for example the default value of + =embark-post-action-hooks= uses it for =delete-file=, =kill-buffer=, + =rename-file=, =rename-buffer=, etc. + +For around-action hooks: + +- =embark--mark-target= :: Save existing mark and point location, mark + the target and run the action. Most targets at point outside the + minibuffer report which region of the buffer they correspond to + (this is the information used by =embark-highlight-indicator= to + know what portion of the buffer to highlight); this function marks + that region. It is useful as an around action hook for commands that + expect a region to be marked, for example, it is used by default for + =indent-region= so that it works on s-expression targets, or for + =fill-region= so that it works on paragraph targets. + +- =embark--cd= :: Run the action with =default-directory= set to the + directory associated to the current target. The target should be of + type =file=, =buffer=, =bookmark= or =library=, and the associated directory + is what you'd expect in each case. + +- =embark--narrow-to-target= :: Run the action with buffer narrowed to + current target. Use this as an around hook to localize the effect of + actions that don't already work on just the region. In the default + configuration it is used for =repunctuate-sentences=. + +- =embark--save-excursion= :: Run the action restoring point at the end. + The current default configuration doesn't use this but it is + available for users. + +** Creating your own keymaps + +All internal keymaps are defined with the standard helper macro +=defvar-keymap=. For example a simple version of the file action keymap +could be defined as follows: + +#+BEGIN_SRC emacs-lisp + (defvar-keymap embark-file-map + :doc "Example keymap with a few file actions" + :parent embark-general-map + "d" #'delete-file + "r" #'rename-file + "c" #'copy-file) +#+END_SRC + +These action keymaps are perfectly normal Emacs +keymaps. You may want to inherit from the =embark-general-map= if you +want to access the default Embark actions. Note that =embark-collect= +and =embark-export= are also made available via =embark-general-map=. + +** Defining actions for new categories of targets + +It is easy to configure Embark to provide actions for new types of +targets, either in the minibuffer or outside it. I present below two +very detailed examples of how to do this. At several points I'll +explain more than one way to proceed, typically with the easiest +option first. I include the alternative options since there will be +similar situations where the easiest option is not available. + +*** New minibuffer target example - tab-bar tabs + +As an example, take the new [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html][tab bars]] from Emacs 27. I'll explain how +to configure Embark to offer tab-specific actions when you use the +tab-bar-mode commands that mention tabs by name. The configuration +explained here is now built-in to Embark (and Marginalia), but it's +still a good self-contained example. In order to setup up tab actions +you would need to: (1) make sure Embark knows those commands deal with +tabs, (2) define a keymap for tab actions and configure Embark so it +knows that's the keymap you want. + +**** Telling Embark about commands that prompt for tabs by name + +For step (1), it would be great if the =tab-bar-mode= commands reported +the completion category =tab= when asking you for a tab with +completion. (All built-in Emacs commands that prompt for file names, +for example, do have metadata indicating that they want a =file=.) They +do not, unfortunately, and I will describe a couple of ways to deal +with this. + +Maybe the easiest thing is to configure [[https://github.com/minad/marginalia][Marginalia]] to enhance those +commands. All of the =tab-bar-*-tab-by-name= commands have the words +"tab by name" in the minibuffer prompt, so you can use: + +#+begin_src emacs-lisp + (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) +#+end_src + +That's it! But in case you are ever in a situation where you don't +already have commands that prompt for the targets you want, I'll +describe how writing your own command with appropriate =category= +metadata looks: + +#+begin_src emacs-lisp + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (completing-read + "Tabs: " + (lambda (string predicate action) + (if (eq action 'metadata) + '(metadata (category . tab)) + (complete-with-action + action tab-list string predicate))))))) + (tab-bar-select-tab-by-name tab)) +#+end_src + +As you can see, the built-in support for setting the category +meta-datum is not very easy to use or pretty to look at. To help with +this I recommend the =consult--read= function from the excellent +[[https://github.com/minad/consult/][Consult]] package. With that function we can rewrite the command as +follows: + +#+begin_src emacs-lisp + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (consult--read tab-list + :prompt "Tabs: " + :category 'tab)))) + (tab-bar-select-tab-by-name tab)) +#+end_src + +Much nicer! No matter how you define the =my-select-tab-by-name= +command, the first approach with Marginalia and prompt detection has +the following advantages: you get the =tab= category for all the +=tab-bar-*-bar-by-name= commands at once, also, you enhance built-in +commands, instead of defining new ones. + +**** Defining and configuring a keymap for tab actions + + Let's say we want to offer select, rename and close actions for tabs + (in addition to Embark general actions, such as saving the tab name to + the kill-ring, which you get for free). Then this will do: + + #+begin_src emacs-lisp + (defvar-keymap embark-tab-actions + :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + :parent embark-general-map + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + + (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) + #+end_src + + What if after using this for a while you feel closing the tab + without confirmation is dangerous? You have a couple of options: + + 1. You can keep using the =tab-bar-close-tab-by-name= command, but have + Embark ask you for confirmation: + #+begin_src emacs-lisp + (push #'embark--confirm + (alist-get 'tab-bar-close-tab-by-name + embark-pre-action-hooks)) + #+end_src + + 2. You can write your own command that prompts for confirmation and + use that instead of =tab-bar-close-tab-by-name= in the above keymap: + #+begin_src emacs-lisp + (defun my-confirm-close-tab-by-name (tab) + (interactive "sTab to close: ") + (when (y-or-n-p (format "Close tab '%s'? " tab)) + (tab-bar-close-tab-by-name tab))) + #+end_src + + Notice that this is a command you can also use directly from =M-x= + independently of Embark. Using it from =M-x= leaves something to be + desired, though, since you don't get completion for the tab names. + You can fix this if you wish as described in the previous section. + +*** New target example in regular buffers - short Wikipedia links + +Say you want to teach Embark to treat text of the form +=wikipedia:Garry_Kasparov= in any regular buffer as a link to Wikipedia, +with actions to open the Wikipedia page in eww or an external browser +or to save the URL of the page in the kill-ring. We can take advantage +of the actions that Embark has preconfigured for URLs, so all we need +to do is teach Embark that =wikipedia:Garry_Kasparov= stands for the URL +=https://en.wikipedia.org/wiki/Garry_Kasparov=. + +You can be as fancy as you want with the recognized syntax. Here, to +keep the example simple, I'll assume the link matches the regexp +=wikipedia:[[:alnum:]_]+=. We will write a function that looks for a +match surrounding point, and returns a dotted list of the form ='(url +URL-OF-THE-PAGE START . END)= where =START= and =END= are the buffer +positions bounding the target, and are used by Embark to highlight it +if you have =embark-highlight-indicator= included in the list +=embark-indicators=. (There are a couple of other options for the return +value of a target finder: the bounding positions are optional and a +single target finder is allowed to return multiple targets; see the +documentation for =embark-target-finders= for details.) + +#+begin_src emacs-lisp + (defun my-short-wikipedia-link () + "Target a link at point of the form wikipedia:Page_Name." + (save-excursion + (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) + (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + (str (buffer-substring-no-properties start end))) + (save-match-data + (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + `(url + ,(format "https://en.wikipedia.org/wiki/%s" + (match-string 1 str)) + ,start . ,end)))))) + + (add-to-list 'embark-target-finders 'my-short-wikipedia-link) +#+end_src + +* How does Embark call the actions? + + Embark actions are normal Emacs commands, that is, functions with an + interactive specification. In order to execute an action, Embark + calls the command with =call-interactively=, so the command reads user + input exactly as if run directly by the user. For example the + command may open a minibuffer and read a string + (=read-from-minibuffer=) or open a completion interface + (=completing-read=). If this happens, Embark takes the target string + and inserts it automatically into the minibuffer, simulating user + input this way. After inserting the string, Embark exits the + minibuffer, submitting the input. (The immediate minibuffer exit can + be disabled for specific actions in order to allow editing the + input; this is done by adding the =embark--allow-edit= function to the + appropriate entry of =embark-target-injection-hooks=). Embark inserts + the target string at the first minibuffer opened by the action + command, and if the command happens to prompt the user for input + more than once, the user still interacts with the second and further + prompts in the normal fashion. Note that if a command does not + prompt the user for input in the minibuffer, Embark still allows you + to use it as an action, but of course, never inserts the target + anywhere. (There are plenty of examples in the default configuration + of commands that do not prompt the user bound to keys in the action + maps, most of the region actions, for instance.) + + This is how Embark manages to reuse normal commands as actions. The + mechanism allows you to use as Embark actions commands that were not + written with Embark in mind (and indeed almost all actions that are + bound by default in Embark's action keymaps are standard Emacs + commands). It also allows you to write new custom actions in such a + way that they are useful even without Embark. + + Staring from version 28.1, Emacs has a variable + =y-or-n-p-use-read-key=, which when set to =t= causes =y-or-n-p= to use + =read-key= instead of =read-from-minibuffer=. Setting + =y-or-n-p-use-read-key= to =t= is recommended for Embark users because + it keeps Embark from attempting to insert the target at a =y-or-n-p= + prompt, which would almost never be sensible. Also consider this as + a warning to structure your own action commands so that if they use + =y-or-n-p=, they do so only after the prompting for the target. + + Here is a simple example illustrating the various ways of reading + input from the user mentioned above. Bind the following commands to + the =embark-symbol-map= to be used as actions, then put the point on + some symbol and run them with =embark-act=: + + #+begin_src emacs-lisp + (defun example-action-command1 () + (interactive) + (message "The input was `%s'." (read-from-minibuffer "Input: "))) + + (defun example-action-command2 (arg input1 input2) + (interactive "P\nsInput 1: \nsInput 2: ") + (message "The first input %swas `%s', and the second was `%s'." + (if arg "truly " "") + input1 + input2)) + + (defun example-action-command3 () + (interactive) + (message "Your selection was `%s'." + (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + + (defun example-action-command4 () + (interactive) + (message "I don't prompt you for input and thus ignore the target!")) + + (keymap-set embark-symbol-map "X 1" #'example-action-command1) + (keymap-set embark-symbol-map "X 2" #'example-action-command2) + (keymap-set embark-symbol-map "X 3" #'example-action-command3) + (keymap-set embark-symbol-map "X 4" #'example-action-command4) + #+end_src + + Also note that if you are using the key bindings to call actions, + you can pass prefix arguments to actions in the normal way. For + example, you can use =C-u X2= with the above demonstration actions to + make the message printed by =example-action-command2= more emphatic. + This ability to pass prefix arguments to actions is useful for some + actions in the default configuration, such as + =embark-shell-command-on-buffer=. + +** Non-interactive functions as actions + + Alternatively, Embark does support one other type of action: a + non-interactive function of a single argument. The target is passed + as argument to the function. For example: + + #+begin_src emacs-lisp + (defun example-action-function (target) + (message "The target was `%s'." target)) + + (keymap-set embark-symbol-map "X 4" #'example-action-function) + #+end_src + + Note that normally binding non-interactive functions in a keymap is + useless, since when attempting to run them using the key binding you + get an error message similar to "Wrong type argument: commandp, + example-action-function". In general it is more flexible to write + any new Embark actions as commands, that is, as interactive + functions, because that way you can also run them directly, without + Embark. But there are a couple of reasons to use non-interactive + functions as actions: + + 1. You may already have the function lying around, and it is + convenient to simply reuse it. + + 2. For command actions the targets can only be simple string, with + no text properties. For certain advanced uses you may want the + action to receive a string /with/ some text properties, or even a + non-string target. + +* Embark, Marginalia and Consult + +Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. +Neither of those packages is a dependency of Embark, but both are +highly recommended companions to Embark, for opposite reasons: +Marginalia greatly enhances Embark's usefulness, while Embark can help +enhance Consult. + +In the remainder of this section I'll explain what exactly Marginalia +does for Embark, and what Embark can do for Consult. + +** Marginalia + +Embark comes with actions for symbols (commands, functions, variables +with actions such as finding the definition, looking up the +documentation, evaluating, etc.) in the =embark-symbol-map= keymap, and +for packages (actions like install, delete, browse url, etc.) in the +=embark-package-keymap=. + +Unfortunately Embark does not automatically offers you these keymaps +when relevant, because many built-in Emacs commands don't report +accurate category metadata. For example, a command like +=describe-package=, which reads a package name from the minibuffer, +does not have metadata indicating this fact. + +In an earlier Embark version, there were functions to supply this +missing metadata, but they have been moved to Marginalia, which +augments many Emacs command to report accurate category metadata. +Simply activating =marginalia-mode= allows Embark to offer you the +package and symbol actions when appropriate again. Candidate +annotations in the Embark collect buffer are also provided by the +Marginalia package: + +- If you install Marginalia and activate =marginalia-mode=, Embark + Collect buffers will use the Marginalia annotations automatically. + +- If you don't install Marginalia, you will see only the annotations + that come with Emacs (such as key bindings in =M-x=, or the unicode + characters in =C-x 8 RET=). + +** Consult + +The excellent Consult package provides many commands that use +minibuffer completion, via the =completing-read= function; plenty of its +commands can be considered enhanced versions of built-in Emacs +commands, and some are completely new functionality. One common +enhancement provided in all commands for which it makes sense is +preview functionality, for example =consult-buffer= will show you a +quick preview of a buffer before you actually switch to it. + +If you use both Consult and Embark you should install the +=embark-consult= package which provides integration between the two. It +provides exporters for several Consult commands and also tweaks the +behavior of many Consult commands when used as actions with =embark-act= +in subtle ways that you may not even notice, but make for a smoother +experience. You need only install it to get these benefits: Embark +will automatically load it after Consult if found. + +The =embark-consult= package provides the following exporters: + +- You can use =embark-export= from =consult-line=, =consult-outline=, or + =consult-mark= to obtain an =occur-mode= buffer. As with the built-in + =occur= command you use that buffer to jump to a match and after that, + you can then use =next-error= and =previous-error= to navigate to other + matches. You can also press =e= to activate =occur-edit-mode= and edit + the matches in place! + +- You can export from any of the Consult asynchronous search commands, + =consult-grep=, =consult-git-grep=, or =consult-ripgrep= to get a + =grep-mode= buffer. Here too you can use =next-error= and =previous-error= + to navigate among matches, and, if you install the [[http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el ][wgrep]] package, + you can use it to edit the matches in place. + +In both cases, pressing =g= will rerun the Consult command you had +exported from and re-enter the input you had typed (which is similar +to reverting but a little more flexible). You can then proceed to +re-export if that's what you want, but you can also edit the input +changing the search terms or simply cancel if you see you are done +with that search. + +The =embark-consult= also contains some candidates collectors that allow +you to run =embark-live= to get a live-updating table of contents for +your buffer: + +- =embark-consult-outline-candidates= produces the outline headings of + the current buffer, using =consult-outline=. +- =embark-consult-imenu-candidates= produces the imenu items of + the current buffer, using =consult-imenu=. +- =embark-consult-imenu-or-outline-candidates= is a simple combination + of the two previous functions: it produces imenu items in buffers + deriving from =prog-mode= and otherwise outline headings. + +The way to configure =embark-live= (or =embark-collect= and =embark-export= +for that matter) to use one of these function is to add it at the end +of the =embark-candidate-collectors= list. The =embark-consult= package by +default adds the last one, which seems to be the most sensible +default. + +Besides those exporters and candidate collectors, the =embark-consult= +package provides many subtle tweaks and small integrations between +Embark and Consult. Some examples are: + +- The asynchronous search commands will start in the directory + associated to the Embark target if that target is a file, buffer, + bookmark or Emacs Lisp library. + + - For all other target types, a Consult search command (asynchronous + or not) will search for the text of the target but leave the + minibuffer open so you can interact with the Consult command. + +- =consult-imenu= will search for the target and take you directly to + the location if it matches a unique imenu entry, otherwise it will + leave the minibuffer open so you can navigate among the matches. + +* Resources + +If you want to learn more about how others have used Embark here are +some links to read: + +- [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]], a blog post by Karthik Chikmagalur. +- [[https://protesilaos.com/dotemacs/][Protesilaos Stavrou's dotemacs]], look for the section called + "Extended minibuffer actions and more (embark.el and + prot-embark.el)" + +And some videos to watch: + +- [[https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/][Embark and my extras]] by Protesilaos Stavrou. +- [[https://youtu.be/qpoQiiinCtY][Embark -- Key features and tweaks]] by Raoul Comninos on the + Emacs-Elements YouTube channel. +- [[https://youtu.be/WsxXr1ncukY][Livestreamed: Adding an Embark context action to send a stream + message]] by Sacha Chua. +- [[https://youtu.be/qk2Is_sC8Lk][System Crafters Live! - The Many Uses of Embark]] by David Wilson. +- [[https://youtu.be/5ffb2at2d7w][Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark]] by + Mike Zamansky. + +* Contributions + +Contributions to Embark are very welcome. There is a [[https://github.com/oantolin/embark/issues/95][wish list]] for +actions, target finders, candidate collectors and exporters. For other +ideas you have for Embark, feel free to open an issue on the [[https://github.com/oantolin/embark/issues][issue +tracker]]. Any neat configuration tricks you find might be a good fit +for the [[https://github.com/oantolin/embark/wiki][wiki]]. + +Code contributions are very welcome too, but since Embark is now on +GNU ELPA, copyright assignment to the FSF is required before you can +contribute code. + +* Acknowledgments + +While I, Omar Antolín Camarena, have written most of the Embark code +and remain very stubborn about some of the design decisions, Embark +has received substantial help from a number of other people which this +document has neglected to mention for far too long. In particular, +Daniel Mendler has been absolutely invaluable, implementing several +important features, and providing a lot of useful advice. + +Code contributions: + +- [[https://github.com/minad][Daniel Mendler]] +- [[https://github.com/clemera/][Clemens Radermacher]] +- [[https://codeberg.org/jao/][José Antonio Ortega Ruiz]] +- [[https://github.com/iyefrat][Itai Y. Efrat]] +- [[https://github.com/a13][a13]] +- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] +- [[https://github.com/mihakam][mihakam]] +- [[https://github.com/leungbk][Brian Leung]] +- [[https://github.com/karthink][Karthik Chikmagalur]] +- [[https://github.com/roshanshariff][Roshan Shariff]] +- [[https://github.com/condy0919][condy0919]] +- [[https://github.com/DamienCassou][Damien Cassou]] +- [[https://github.com/JimDBh][JimDBh]] + +Advice and useful discussions: + +- [[https://github.com/minad][Daniel Mendler]] +- [[https://gitlab.com/protesilaos/][Protesilaos Stavrou]] +- [[https://github.com/clemera/][Clemens Radermacher]] +- [[https://github.com/hmelman/][Howard Melman]] +- [[https://github.com/astoff][Augusto Stoffel]] +- [[https://github.com/bdarcus][Bruce d'Arcus]] +- [[https://github.com/jdtsmith][JD Smith]] +- [[https://github.com/karthink][Karthik Chikmagalur]] +- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] +- [[https://github.com/iyefrat][Itai Y. Efrat]] +- [[https://github.com/mohkale][Mohsin Kaleem]] diff --git a/elpa/embark-0.22.1/dir b/elpa/embark-0.22.1/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs<Return>" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs misc features +* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. diff --git a/elpa/embark-0.22.1/embark-autoloads.el b/elpa/embark-0.22.1/embark-autoloads.el @@ -0,0 +1,201 @@ +;;; embark-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) + + + +;;; Generated autoloads from embark.el + +(defun embark--record-this-command nil "\ +Record command which opened the minibuffer. +We record this because it will be the default action. +This function is meant to be added to `minibuffer-setup-hook'." (setq-local embark--command this-command)) +(add-hook 'minibuffer-setup-hook #'embark--record-this-command) +(autoload 'embark-eldoc-first-target "embark" "\ +Eldoc function reporting the first Embark target at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'. + +(fn REPORT &rest _)") +(autoload 'embark-eldoc-target-types "embark" "\ +Eldoc function reporting the types of all Embark targets at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'. + +(fn REPORT &rest _)") +(autoload 'embark-bindings-in-keymap "embark" "\ +Explore command key bindings in KEYMAP with `completing-read'. +The selected command will be executed. Interactively, prompt the +user for a KEYMAP variable. + +(fn KEYMAP)" t) +(autoload 'embark-bindings "embark" "\ +Explore all current command key bindings with `completing-read'. +The selected command will be executed. + +If NO-GLOBAL is non-nil (interactively, if called with a prefix +argument) omit global key bindings; this leaves key bindings from +minor mode maps and the local map (usually set by the major +mode), but also less common keymaps such as those from a text +property or overlay, or the overriding maps: +`overriding-terminal-local-map' and `overriding-local-map'. + +(fn NO-GLOBAL)" t) +(autoload 'embark-bindings-at-point "embark" "\ +Explore all key bindings at point with `completing-read'. +The selected command will be executed. + +This command lists key bindings found in keymaps specified by the +text properties `keymap' or `local-map', from either buffer text +or an overlay. These are not widely used in Emacs, and when they +are used can be somewhat hard to discover. Examples of locations +that have such a keymap are links and images in `eww' buffers, +attachment links in `gnus' article buffers, and the 'Stash' line +in a `vc-dir' buffer." t) +(autoload 'embark-prefix-help-command "embark" "\ +Prompt for and run a command bound in the prefix used for this command. +The prefix described consists of all but the last event of the +key sequence that ran this command. This function is intended to +be used as a value for `prefix-help-command'. + +In addition to using completion to select a command, you can also +type @ and the key binding (without the prefix)." t) +(autoload 'embark-act "embark" "\ +Prompt the user for an action and perform it. +The targets of the action are chosen by `embark-target-finders'. +By default, if called from a minibuffer the target is the top +completion candidate. When called from a non-minibuffer buffer +there can multiple targets and you can cycle among them by using +`embark-cycle' (which is bound by default to the same key +binding `embark-act' is, but see `embark-cycle-key'). + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument. + +If instead you call this from outside the minibuffer, the first +ARG targets are skipped over (if ARG is negative the skipping is +done by cycling backwards) and cycling starts from the following +target. + +(fn &optional ARG)" t) +(autoload 'embark-act-all "embark" "\ +Prompt the user for an action and perform it on each candidate. +The candidates are chosen by `embark-candidate-collectors'. +By default, if called from a minibuffer the candidates are the +completion candidates. + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument. + +(fn &optional ARG)" t) +(autoload 'embark-dwim "embark" "\ +Run the default action on the current target. +The target of the action is chosen by `embark-target-finders'. + +If the target comes from minibuffer completion, then the default +action is the command that opened the minibuffer in the first +place, unless overridden by `embark-default-action-overrides'. + +For targets that do not come from minibuffer completion +(typically some thing at point in a regular buffer) and whose +type is not listed in `embark-default-action-overrides', the +default action is given by whatever binding RET has in the action +keymap for the target's type. + +See `embark-act' for the meaning of the prefix ARG. + +(fn &optional ARG)" t) +(autoload 'embark-become "embark" "\ +Make current command become a different command. +Take the current minibuffer input as initial input for new +command. The new command can be run normally using key bindings or +\\[execute-extended-command], but if the current command is found in a keymap in +`embark-become-keymaps', that keymap is activated to provide +convenient access to the other commands in it. + +If FULL is non-nil (interactively, if called with a prefix +argument), the entire minibuffer contents are used as the initial +input of the new command. By default only the part of the +minibuffer contents between the current completion boundaries is +taken. What this means is fairly technical, but (1) usually +there is no difference: the completion boundaries include the +entire minibuffer contents, and (2) the most common case where +these notions differ is file completion, in which case the +completion boundaries single out the path component containing +point. + +(fn &optional FULL)" t) +(autoload 'embark-collect "embark" "\ +Create an Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Collect\". + +In Embark Collect buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-collect' +from within a minibuffer completion session. In that case +rerunning just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally with +the command, perhaps editing the minibuffer contents, and, if you +wish, you can rerun `embark-collect' to get an updated buffer." t) +(autoload 'embark-live "embark" "\ +Create a live-updating Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Live\"." t) +(autoload 'embark-export "embark" "\ +Create a type-specific buffer to manage current candidates. +The variable `embark-exporters-alist' controls how to make the +buffer for each type of completion. + +In Embark Export buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-export' +from within a minibuffer completion session. In that case +reverting just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally +with the command, perhaps editing the minibuffer contents, and, +if you wish, you can rerun `embark-export' to get an updated +buffer." t) +(register-definition-prefixes "embark" '("embark-")) + + +;;; Generated autoloads from embark-org.el + +(register-definition-prefixes "embark-org" '("embark-org-")) + +;;; End of scraped data + +(provide 'embark-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; embark-autoloads.el ends here diff --git a/elpa/embark-0.22.1/embark-org.el b/elpa/embark-0.22.1/embark-org.el @@ -0,0 +1,497 @@ +;;; embark-org.el --- Embark targets and actions for Org Mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package configures the Embark package for use in Org Mode +;; buffers. It teaches Embark a number of Org related targets and +;; appropriate actions. Currently it has table cells, whole tables, +;; source blocks and links. Targets to add: headings (Embark already +;; has generic support for outlines, so we just nee to add Org +;; specific actions), timestamps, etc. + +;;; Code: + +(require 'embark) +(require 'org) +(require 'org-element) + +;;; Basic target finder for Org + +;; There are very many org element and objects types, we'll only +;; recognize those for which there are specific actions we can put in +;; a keymap, or for even if there aren't any specific actions, if it's +;; import to be able to kill, delete or duplicate (embark-insert) them +;; conveniently. I'll start conservatively and we can add more later + +(defconst embark-org--types + '( + babel-call + ;; bold + ;; center-block + ;; citation + ;; citation-reference + ;; clock + ;; code + ;; comment + ;; comment-block + ;; diary-sexp + ;; drawer + ;; dynamic-block + ;; entity + ;; example-block + ;; export-block + ;; export-snippet + ;; fixed-width + footnote-definition + footnote-reference + ;; headline ; the bounds include the entire subtree! + ;; horizontal-rule + ;; inline-babel-call + ;; inline-src-block + ;; inlinetask + ;; italic + item + ;; keyword + ;; latex-environment + ;; latex-fragment + ;; line-break + link + ;; macro + ;; node-property + ;; paragraph ; the existing general support seems fine + plain-list + ;; planning + ;; property-drawer + ;; quote-block + ;; radio-target + ;; section + ;; special-block + src-block + ;; statistics-cookie + ;; strike-through + ;; subscript + ;; superscript + table ; supported via a specific target finder + table-cell + ;; table-row ; we'll put row & column actions in the cell map + ;; target ; I think there are no useful actions for radio targets + timestamp + ;; underline + ;; verbatim + ;; verse-block + ) + "Supported Org object and element types.") + +(defun embark-org-target-element-context () + "Target all Org elements or objects around point." + (when (derived-mode-p 'org-mode 'org-agenda-mode) + (cl-loop + for elt = (org-element-lineage (org-element-context) embark-org--types t) + then (org-element-lineage elt embark-org--types) + while elt + ;; clip bounds to narrowed portion of buffer + for begin = (max (org-element-property :begin elt) (point-min)) + for end = (min (org-element-property :end elt) (point-max)) + for target = (buffer-substring begin end) + ;; Adjust table-cell to exclude final |. (Why is that there?) + ;; Note: We are not doing this is an embark transformer because we + ;; want to adjust the bounds too. + ;; TODO? If more adjustments like this become necessary, add a + ;; nice mechanism for doing them. + when (and (eq (car elt) 'table-cell) (string-suffix-p "|" target)) + do (setq target (string-trim (string-remove-suffix "|" target)) + end (1- end)) + collect `(,(intern (format "org-%s" (car elt))) ,target ,begin . ,end)))) + +(if-let (((not (memq 'embark-org-target-element-context embark-target-finders))) + (tail (memq 'embark-target-active-region embark-target-finders))) + (push 'embark-org-target-element-context (cdr tail)) + (push 'embark-org-target-element-context embark-target-finders)) + +;;; Custom Org actions + +(defvar org-export-with-toc) + +(defun embark-org-copy-as-markdown (start end) + "Export the region from START to END to markdown and save on the `kill-ring'." + (interactive "r") + (require 'ox) + (kill-new + (let (org-export-with-toc) + (string-trim + (org-export-string-as (buffer-substring-no-properties start end) 'md t)))) + (deactivate-mark)) + +(add-to-list 'embark-pre-action-hooks + '(embark-org-copy-as-markdown embark--mark-target)) + +(keymap-set embark-region-map "M" #'embark-org-copy-as-markdown) ; good idea? + +;;; Tables + +(dolist (motion '(org-table-move-cell-up org-table-move-cell-down + org-table-move-cell-left org-table-move-cell-right)) + (add-to-list 'embark-repeat-actions motion)) + +(push 'embark--ignore-target + (alist-get 'org-table-edit-field embark-target-injection-hooks)) + +(defvar-keymap embark-org-table-cell-map + :doc "Keymap for actions the current cells, column or row of an Org table." + :parent embark-general-map + ;; TODO: default action? + "<up>" #'org-table-move-cell-up + "<down>" #'org-table-move-cell-down + "<left>" #'org-table-move-cell-left + "<right>" #'org-table-move-cell-right + "=" #'org-table-eval-formula + "e" #'org-table-edit-field + "g" #'org-table-recalculate) + +(defvar-keymap embark-org-table-map + :doc "Keymap for actions on entire Org table." + :parent embark-general-map + ;; TODO: default action? + "=" #'org-table-edit-formulas + "s" #'org-table-sort-lines + "t" #'org-table-transpose-table-at-point + "c" #'org-table-convert + "f" #'org-table-follow-field-mode + "y" #'org-table-paste-rectangle + "d" #'org-table-toggle-formula-debugger + "i" #'org-table-iterate + "e" #'org-table-export) + +(push 'embark--ignore-target ; prompts for file name + (alist-get 'org-table-export embark-target-injection-hooks)) + +(add-to-list 'embark-keymap-alist '(org-table . embark-org-table-map)) + +(add-to-list 'embark-keymap-alist '(org-table-cell . embark-org-table-cell-map)) + +;;; Links + +;; The link support has a slightly complicated design in order to +;; achieve the following goals: + +;; 1. RET should simply be org-open-at-point + +;; 2. When the link is to a file, URL, email address or elisp +;; expression or command, we want to offer the user actions for +;; that underlying type. + +;; 3. Even in those cases, we still want some actions to apply to the +;; entire link including description: actions to copy the link as +;; markdown, or just the link description or target. + +;; So the strategy is as follows (illustrated with file links): + +;; - The target will be just the file, without the description and +;; also without the "file:" prefix nor the "::line-number or search" +;; suffix. That way, file actions will correctly apply to it. + +;; - The type will not be 'file, but 'org-file-link that way we can +;; register a keymap for 'org-file-link that inherits from both +;; embark-org-link-map (with RET bound to org-open-at-point and a +;; few other generic link actions) and embark-file-map. + +;; - The commands to copy the link at point in some format will be +;; written as commands that act on the Org link at point. This way +;; they are independently (plausibly) useful, and we circumvent the +;; problem that the whole Org link is not actually the target (just +;; the inner file is!). + +;; Alternative design I considered: separate each target into two, a +;; whole link target which includes the description and brackets and +;; what not; and an "inner target" which is just the file or URL or +;; whatever. Cons of this approach: much target cycling is required! +;; First of all, an unadorned embark-dwim definitely should be +;; org-open-at-point, which means the whole link target would need +;; priority. That means that any file, URL, etc. actions would require +;; you to cycle first. This sounds very inconvenient, the above +;; slightly more complex design allows both whole-link and inner +;; target actions to work without cycling. + +(autoload 'org-attach-dir "org-attach") + +(defun embark-org--refine-link-type (_type target) + "Refine type of link TARGET if we have more specific actions available." + (when (string-match org-link-any-re target) + (let ((target (or (match-string-no-properties 2 target) + (match-string-no-properties 0 target)))) + (cond + ((string-prefix-p "http" target) + (cons 'org-url-link target)) + ((string-prefix-p "mailto:" target) + (cons 'org-email-link (string-remove-prefix "mailto:" target))) + ((string-prefix-p "file:" target) + (cons 'org-file-link + (replace-regexp-in-string + "::.*" "" (string-remove-prefix "file:" target)))) + ((string-prefix-p "attachment:" target) + (cons 'org-file-link + (expand-file-name + (replace-regexp-in-string + "::.*" "" (string-remove-prefix "attachment:" target)) + (org-attach-dir)))) + ((string-match-p "^[./]" target) + (cons 'org-file-link (abbreviate-file-name (expand-file-name target)))) + ((string-prefix-p "elisp:(" target) + (cons 'org-expression-link (string-remove-prefix "elisp:" target))) + ((string-prefix-p "elisp:" target) + (cons 'command (string-remove-prefix "elisp:" target))) + (t (cons 'org-link target)))))) + +(add-to-list 'embark-transformer-alist + '(org-link . embark-org--refine-link-type)) + +(defmacro embark-org-define-link-copier (name formula description) + "Define a command that copies the Org link at point according to FORMULA. +The command's name is formed by appending NAME to +embark-org-copy-link. The docstring includes the DESCRIPTION of +what part or in what format the link is copied." + `(defun ,(intern (format "embark-org-copy-link-%s" name)) () + ,(format "Copy to the kill-ring the Org link at point%s." description) + (interactive) + (when (org-in-regexp org-link-any-re) + (let* ((full (match-string-no-properties 0)) + (target (or (match-string-no-properties 2) + (match-string-no-properties 0))) + (description (match-string-no-properties 3)) + (kill ,formula)) + (ignore full target description) + (when kill + (message "Saved '%s'" kill) + (kill-new kill)))))) + +(embark-org-define-link-copier in-full full " in full") +(embark-org-define-link-copier description description "'s description") +(embark-org-define-link-copier target target "'s target") + +(defalias 'embark-org-copy-link-inner-target #'kill-new + "Copy inner part of the Org link at point's target. +For mailto and elisp links, the inner part is the portion of the +target after `mailto:' or `elisp:'. + +For file links the inner part is the file name, without the +`file:' prefix and without `::' suffix (used for line numbers, +IDs or search terms). + +For URLs the inner part is the whole target including the `http:' +or `https:' prefix. For any other type of link the inner part is +also the whole target.") + +(defvar-keymap embark-org-link-copy-map + :doc "Keymap for different ways to copy Org links to the kill-ring. + +You should bind w in this map to your most frequently used link +copying function. The default is for w to copy the \"inner +target\" (see `embark-org-copy-link-inner-target'); which is also +bound to i." + :parent nil + "w" #'embark-org-copy-link-inner-target + "f" #'embark-org-copy-link-in-full + "d" #'embark-org-copy-link-description + "t" #'embark-org-copy-link-target + "i" #'embark-org-copy-link-inner-target + "m" #'embark-org-copy-as-markdown) + +(fset 'embark-org-link-copy-map embark-org-link-copy-map) + +(defvar-keymap embark-org-link-map + :doc "Keymap for actions on Org links." + :parent embark-general-map + "RET" #'org-open-at-point + "'" #'org-insert-link + "w" #'embark-org-link-copy-map) + +;; The reason for this is left as an exercise to the reader. +;; Solution: Na ryvfc gnetrg znl cebzcg gur hfre sbe fbzrguvat! +(cl-pushnew 'embark--ignore-target + (alist-get 'org-open-at-point embark-target-injection-hooks)) +(cl-pushnew 'embark--ignore-target + (alist-get 'org-insert-link embark-target-injection-hooks)) + +(add-to-list 'embark-keymap-alist + '(org-link embark-org-link-map)) +(add-to-list 'embark-keymap-alist + '(org-url-link embark-org-link-map embark-url-map)) +(add-to-list 'embark-keymap-alist + '(org-email-link embark-org-link-map embark-email-map)) +(add-to-list 'embark-keymap-alist + '(org-file-link embark-org-link-map embark-file-map)) +(add-to-list 'embark-keymap-alist + '(org-expression-link embark-org-link-map embark-expression-map)) + +;;; Org headings + +(defun embark-org--refine-heading (type target) + "Refine TYPE of heading TARGET in Org buffers." + (cons + (if (derived-mode-p 'org-mode) 'org-heading type) + target)) + +(add-to-list 'embark-transformer-alist '(heading . embark-org--refine-heading)) + +(defvar-keymap embark-org-heading-map + :doc "Keymap for actions on Org headings." + :parent embark-heading-map + "RET" #'org-todo + "t" #'org-todo + "," #'org-priority + ":" #'org-set-tags-command + "k" #'org-cut-subtree + "N" #'org-narrow-to-subtree + "l" #'org-metaleft + "r" #'org-metaright + "S" #'org-sort + "R" #'org-refile + "a" #'org-archive-subtree-default-with-confirmation + "h" #'org-insert-heading-respect-content + "H" #'org-insert-todo-heading-respect-content + "L" #'org-store-link) + +(dolist (cmd '(org-todo org-metaright org-metaleft org-metaup org-metadown + org-shiftmetaleft org-shiftmetaright org-cycle org-shifttab)) + (cl-pushnew cmd embark-repeat-actions)) + +(cl-pushnew 'embark--ignore-target + (alist-get 'org-set-tags-command embark-target-injection-hooks)) + +(cl-pushnew '(org-heading . embark-org-heading-map) embark-keymap-alist) + +;;; Source blocks and babel calls + +(defun embark-org-copy-block-contents () + "Save contents of source block at point to the `kill-ring'." + (interactive) + (when (org-in-src-block-p) + (let ((contents (nth 2 (org-src--contents-area (org-element-at-point))))) + (with-temp-buffer + (insert contents) + (org-do-remove-indentation) + (kill-new (buffer-substring (point-min) (point-max))))))) + +(defvar-keymap embark-org-src-block-map + :doc "Keymap for actions on Org source blocks." + :parent embark-general-map + "RET" #'org-babel-execute-src-block + "SPC" #'org-babel-mark-block + "TAB" #'org-indent-block + "c" #'embark-org-copy-block-contents + "h" #'org-babel-check-src-block + "k" #'org-babel-remove-result-one-or-many + "p" #'org-babel-previous-src-block + "n" #'org-babel-next-src-block + "t" #'org-babel-tangle + "s" #'org-babel-switch-to-session + "l" #'org-babel-load-in-session + "'" #'org-edit-special + "/" #'org-babel-demarcate-block + "N" #'org-narrow-to-block) + +(cl-defun embark-org--at-block-head (&rest rest &key run &allow-other-keys) + "Save excursion and RUN the action at the head of the current block. +Applies RUN to the REST of the arguments." + (save-excursion + (org-babel-goto-src-block-head) + (apply run rest))) + +(cl-pushnew #'embark-org--at-block-head + (alist-get 'org-indent-block embark-around-action-hooks)) + +(dolist (motion '(org-babel-next-src-block org-babel-previous-src-block)) + (add-to-list 'embark-repeat-actions motion)) + +(add-to-list 'embark-keymap-alist '(org-src-block . embark-org-src-block-map)) + +;;; List items + +(defvar-keymap embark-org-item-map + :doc "Keymap for actions on Org list items." + :parent embark-general-map + "RET" #'org-toggle-checkbox + "c" #'org-toggle-checkbox + "t" #'org-toggle-item + "n" #'org-next-item + "p" #'org-previous-item + "<left>" #'org-outdent-item + "<right>" #'org-indent-item + "<up>" #'org-move-item-up + "<down>" #'org-move-item-down + ">" #'org-indent-item-tree + "<" #'org-outdent-item-tree) + +(dolist (cmd '(org-toggle-checkbox + org-toggle-item + org-next-item + org-previous-item + org-outdent-item + org-indent-item + org-move-item-up + org-move-item-down + org-indent-item-tree + org-outdent-item-tree)) + (add-to-list 'embark-repeat-actions cmd)) + +(add-to-list 'embark-keymap-alist '(org-item . embark-org-item-map)) + +;;; Org plain lists + +(defvar-keymap embark-org-plain-list-map + :doc "Keymap for actions on plain Org lists." + :parent embark-general-map + "RET" #'org-list-repair + "r" #'org-list-repair + "s" #'org-sort-list + "b" #'org-cycle-list-bullet + "t" #'org-list-make-subtree + "c" #'org-toggle-checkbox) + +(add-to-list 'embark-repeat-actions 'org-cycle-list-bullet) + +(add-to-list 'embark-keymap-alist '(org-plain-list . embark-org-plain-list-map)) + +(cl-defun embark-org--toggle-checkboxes + (&rest rest &key run type &allow-other-keys) + "Around action hook for `org-toggle-checkbox'. +See `embark-around-action-hooks' for the keyword arguments RUN and TYPE. +REST are the remaining arguments." + (apply (if (eq type 'org-plain-list) #'embark--mark-target run) + :type type + rest)) + +(cl-pushnew #'embark-org--toggle-checkboxes + (alist-get 'org-toggle-checkbox embark-around-action-hooks)) + +;;; "Encode" region using Org export in place + +(defvar-keymap embark-org-export-in-place-map + :doc "Keymap for actions which replace the region by an exported version." + :parent embark-general-map + "m" #'org-md-convert-region-to-md + "h" #'org-html-convert-region-to-html + "a" #'org-ascii-convert-region-to-ascii + "l" #'org-latex-convert-region-to-latex) + +(fset 'embark-org-export-in-place-map embark-org-export-in-place-map) + +(keymap-set embark-encode-map "o" 'embark-org-export-in-place-map) + +(provide 'embark-org) +;;; embark-org.el ends here diff --git a/elpa/embark-0.22.1/embark-pkg.el b/elpa/embark-0.22.1/embark-pkg.el @@ -0,0 +1,2 @@ +;; Generated package description from embark.el -*- no-byte-compile: t -*- +(define-package "embark" "0.22.1" "Conveniently act on minibuffer completions" '((emacs "27.1") (compat "29.1.4.0")) :commit "c914efe881df2bc6a2bd35cc7ee975d3e9d4a418" :authors '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) :maintainer '("Omar Antolín Camarena" . "omar@matem.unam.mx") :keywords '("convenience") :url "https://github.com/oantolin/embark") diff --git a/elpa/embark-0.22.1/embark.el b/elpa/embark-0.22.1/embark.el @@ -0,0 +1,4363 @@ +;;; embark.el --- Conveniently act on minibuffer completions -*- lexical-binding: t; -*- + +;; Copyright (C) 2021, 2022 Free Software Foundation, Inc. + +;; Author: Omar Antolín Camarena <omar@matem.unam.mx> +;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx> +;; Keywords: convenience +;; Version: 0.22.1 +;; Homepage: https://github.com/oantolin/embark +;; Package-Requires: ((emacs "27.1") (compat "29.1.4.0")) + +;; This file is part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package provides a sort of right-click contextual menu for +;; Emacs, accessed through the `embark-act' command (which you should +;; bind to a convenient key), offering you relevant actions to use on +;; a target determined by the context: + +;; - In the minibuffer, the target is the current best completion +;; candidate. +;; - In the `*Completions*' buffer the target is the completion at point. +;; - In a regular buffer, the target is the region if active, or else the +;; file, symbol or url at point. + +;; The type of actions offered depend on the type of the target: + +;; - For files you get offered actions like deleting, copying, +;; renaming, visiting in another window, running a shell command on the +;; file, etc. +;; - For buffers the actions include switching to or killing the buffer. +;; - For package names the actions include installing, removing or +;; visiting the homepage. + +;; Everything is easily configurable: determining the current target, +;; classifying it, and deciding with actions are offered for each type +;; in the classification. The above introduction just mentions part of +;; the default configuration. + +;; Configuring which actions are offered for a type is particularly +;; easy and requires no programming: the `embark-keymap-alist' +;; variable associates target types with variable containing keymaps, +;; and those keymaps containing binds for the actions. For example, +;; in the default configuration the type `file' is associated with the +;; symbol `embark-file-keymap'. That symbol names a keymap with +;; single-letter key bindings for common Emacs file commands, for +;; instance `c' is bound to `copy-file'. This means that if while you +;; are in the minibuffer after running a command that prompts for a +;; file, such as `find-file' or `rename-file', you can copy a file by +;; running `embark-act' and then pressing `c'. + +;; These action keymaps are very convenient but not strictly necessary +;; when using `embark-act': you can use any command that reads from the +;; minibuffer as an action and the target of the action will be inserted +;; at the first minibuffer prompt. After running `embark-act' all of your +;; key bindings and even `execute-extended-command' can be used to run a +;; command. The action keymaps are normal Emacs keymaps and you should +;; feel free to bind in them whatever commands you find useful as actions. + +;; The actions in `embark-general-map' are available no matter what +;; type of completion you are in the middle of. By default this +;; includes bindings to save the current candidate in the kill ring +;; and to insert the current candidate in the previously selected +;; buffer (the buffer that was current when you executed a command +;; that opened up the minibuffer). + +;; You can read about the Embark GitHub project wiki: +;; https://github.com/oantolin/embark/wiki/Default-Actions + +;; Besides acting individually on targets, Embark lets you work +;; collectively on a set of target candidates. For example, while +;; you are in the minibuffer the candidates are simply the possible +;; completions of your input. Embark provides three commands to work +;; on candidate sets: + +;; - The `embark-act-all' command runs the same action on each of the +;; current candidates. It is just like using `embark-act' on each +;; candidate in turn. + +;; - The `embark-collect' command produces a buffer listing all +;; candidates, for you to peruse and run actions on at your leisure. +;; The candidates are displayed as a list showing additional +;; annotations. + +;; - The `embark-export' command tries to open a buffer in an +;; appropriate major mode for the set of candidates. If the +;; candidates are files export produces a Dired buffer; if they are +;; buffers, you get an Ibuffer buffer; and if they are packages you +;; get a buffer in package menu mode. + +;; These are always available as "actions" (although they do not act +;; on just the current target but on all candidates) for embark-act +;; and are bound to A, S (for "snapshot") and E, respectively, in +;; embark-general-map. This means that you do not have to bind your +;; own key bindings for these (although you can, of course), just a +;; key binding for `embark-act'. + +;;; Code: + + +(require 'compat) +(eval-when-compile (require 'subr-x)) + +(require 'ffap) ; used to recognize file and url targets + +;;; User facing options + +(defgroup embark nil + "Emacs Mini-Buffer Actions Rooted in Keymaps." + :link '(info-link :tag "Info Manual" "(embark)") + :link '(url-link :tag "Homepage" "https://github.com/oantolin/embark") + :link '(emacs-library-link :tag "Library Source" "embark.el") + :group 'minibuffer + :prefix "embark-") + +(defcustom embark-keymap-alist + '((file embark-file-map) + (library embark-library-map) + (environment-variables embark-file-map) ; they come up in file completion + (url embark-url-map) + (email embark-email-map) + (buffer embark-buffer-map) + (tab embark-tab-map) + (expression embark-expression-map) + (identifier embark-identifier-map) + (defun embark-defun-map) + (symbol embark-symbol-map) + (face embark-face-map) + (command embark-command-map) + (variable embark-variable-map) + (function embark-function-map) + (minor-mode embark-command-map) + (unicode-name embark-unicode-name-map) + (package embark-package-map) + (bookmark embark-bookmark-map) + (region embark-region-map) + (sentence embark-sentence-map) + (paragraph embark-paragraph-map) + (kill-ring embark-kill-ring-map) + (heading embark-heading-map) + (t embark-general-map)) + "Alist of action types and corresponding keymaps. +The special key t is associated with the default keymap to use. +Each value can be either a single symbol whose value is a keymap, +or a list of such symbols." + :type '(alist :key-type (symbol :tag "Target type") + :value-type (choice (variable :tag "Keymap") + (repeat :tag "Keymaps" variable)))) + +(defcustom embark-target-finders + '(embark-target-top-minibuffer-completion + embark-target-active-region + embark-target-text-heading-at-point + embark-target-collect-candidate + embark-target-completion-at-point + embark-target-bug-reference-at-point + embark-target-package-at-point + embark-target-email-at-point + embark-target-url-at-point + embark-target-file-at-point + embark-target-custom-variable-at-point + embark-target-identifier-at-point + embark-target-library-file-at-point + embark-target-expression-at-point + embark-target-sentence-at-point + embark-target-paragraph-at-point + embark-target-defun-at-point + embark-target-prog-heading-at-point) + "List of functions to determine the target in current context. +Each function should take no arguments and return one of: + +1. a cons (TYPE . TARGET) where TARGET is a string and TYPE is a + symbol (which is looked up in `embark-keymap-alist' to + determine which additional keybindings for actions to setup); + +2. a dotted list of the form (TYPE TARGET START . END), where + START and END are the buffer positions bounding TARGET, used + for highlighting; or + +3. a possibly empty list of targets, each of type 1 or 2 (in + particular if a target finder does not find any targets, it + should return nil)." + :type 'hook) + +(defcustom embark-transformer-alist + '((minor-mode . embark--lookup-lighter-minor-mode) + (embark-keybinding . embark--keybinding-command) + (project-file . embark--project-file-full-path) + (package . embark--remove-package-version) + (multi-category . embark--refine-multi-category) + (file . embark--simplify-path)) + "Alist associating type to functions for transforming targets. +Each function should take a type and a target string and return a +pair of the form a `cons' of the new type and the new target." + :type '(alist :key-type symbol :value-type function)) + +(defcustom embark-become-keymaps + '(embark-become-help-map + embark-become-file+buffer-map + embark-become-shell-command-map + embark-become-match-map) + "List of keymaps for `embark-become'. +Each keymap groups a set of related commands that can +conveniently become one another." + :type '(repeat variable)) + +(defcustom embark-prompter 'embark-keymap-prompter + "Function used to prompt the user for actions. +This should be set to a function that prompts the use for an +action and returns the symbol naming the action command. The +default value, `embark-keymap-prompter' activates the type +specific action keymap given in `embark-keymap-alist'. +There is also `embark-completing-read-prompter' which +prompts for an action with completion." + :type '(choice (const :tag "Use action keymaps" embark-keymap-prompter) + (const :tag "Read action with completion" + embark-completing-read-prompter) + (function :tag "Other"))) + +(defcustom embark-keymap-prompter-key "@" + "Key to switch to the keymap prompter from `embark-completing-read-prompter'. + +The key must be either nil or a string. The +string must be accepted by `key-valid-p'." + :type '(choice key (const :tag "None" nil))) + +(defcustom embark-cycle-key nil + "Key used for `embark-cycle'. + +If the key is set to nil it defaults to the global binding of +`embark-act'. The key must be a string which is accepted by +`key-valid-p'." + :type '(choice key (const :tag "Use embark-act key" nil))) + +(defcustom embark-help-key "C-h" + "Key used for help. + +The key must be either nil or a string. The +string must be accepted by `key-valid-p'." + :type '(choice (const "C-h") + (const "?") + (const :tag "None" nil) + key)) + +(defcustom embark-keybinding-repeat + (propertize "*" 'face 'embark-keybinding-repeat) + "Indicator string for repeatable keybindings. +Keybindings are formatted by the `completing-read' prompter and +the verbose indicator." + :type 'string) + +(defface embark-keybinding-repeat + '((t :inherit font-lock-builtin-face)) + "Face used to indicate keybindings as repeatable.") + +(defface embark-keybinding '((t :inherit success)) + "Face used to display key bindings. +Used by `embark-completing-read-prompter' and `embark-keymap-help'.") + +(defface embark-keymap '((t :slant italic)) + "Face used to display keymaps. +Used by `embark-completing-read-prompter' and `embark-keymap-help'.") + +(defface embark-target '((t :inherit highlight)) + "Face used to highlight the target at point during `embark-act'.") + +(defcustom embark-quit-after-action t + "Should `embark-act' quit the minibuffer? +This controls whether calling `embark-act' without a prefix +argument quits the minibuffer or not. You can always get the +opposite behavior to that indicated by this variable by calling +`embark-act' with \\[universal-argument]. + +Note that `embark-act' can also be called from outside the +minibuffer and this variable is irrelevant in that case. + +In addition to t or nil this variable can also be set to an +alist to specify the minibuffer quitting behavior per command. +In the alist case one can additionally use the key t to +prescribe a default for commands not used as alist keys." + :type '(choice (const :tag "Always quit" t) + (const :tag "Never quit" nil) + (alist :tag "Configure per action" + :key-type (choice (function :tag "Action") + (const :tag "All other actions" t)) + :value-type (choice (const :tag "Quit") + (const :tag "Do not quit"))))) + +(defcustom embark-confirm-act-all t + "Should `embark-act-all' prompt the user for confirmation? +Even if this variable is nil you may still be prompted to confirm +some uses of `embark-act-all', namely, for those actions whose +entry in `embark-pre-action-hooks' includes `embark--confirm'." + :type 'boolean) + +(defcustom embark-default-action-overrides nil + "Alist associating target types with overriding default actions. +When the source of a target is minibuffer completion, the default +action for it is usually the command that opened the minibuffer +in the first place but this can be overridden for a given type by +an entry in this list. + +For example, if you run `delete-file' the default action for its +completion candidates is `delete-file' itself. You may prefer to +make `find-file' the default action for all files, even if they +were obtained from a `delete-file' prompt. In that case you can +configure that by adding an entry to this variable pairing `file' +with `find-file'. + +In addition to target types, you can also use as keys in this alist, +pairs of a target type and a command name. Such a pair indicates that +the override only applies if the target was obtained from minibuffer +completion from that command. For example adding an +entry (cons (cons \\='file \\='delete-file) \\='find-file) to this alist would +indicate that for files at the prompt of the `delete-file' command, +`find-file' should be used as the default action." + :type '(alist :key-type (choice (symbol :tag "Type") + (cons (symbol :tag "Type") + (symbol :tag "Command"))) + :value-type (function :tag "Default action"))) + +(defcustom embark-target-injection-hooks + '((async-shell-command embark--allow-edit embark--shell-prep) + (shell-command embark--allow-edit embark--shell-prep) + (pp-eval-expression embark--eval-prep) + (eval-expression embark--eval-prep) + (package-delete embark--force-complete) + ;; commands evaluating code found in the buffer, which may in turn prompt + (embark-pp-eval-defun embark--ignore-target) + (eval-defun embark--ignore-target) + (eval-last-sexp embark--ignore-target) + (embark-eval-replace embark--ignore-target) + ;; commands which prompt for something that is *not* the target + (write-region embark--ignore-target) + (append-to-file embark--ignore-target) + (shell-command-on-region embark--ignore-target) + (format-encode-region embark--ignore-target) + (format-decode-region embark--ignore-target) + (xref-find-definitions embark--ignore-target) + (xref-find-references embark--ignore-target) + (sort-regexp-fields embark--ignore-target) + (align-regexp embark--ignore-target)) + "Alist associating commands with post-injection setup hooks. +For commands appearing as keys in this alist, run the +corresponding value as a setup hook after injecting the target +into in the minibuffer and before acting on it. The hooks must +accept arbitrary keyword arguments. The :action command, the +:target string and target :type are always present. For actions +at point the target :bounds are passed too. The default pre-action +hook is specified by the entry with key t. Furthermore, hooks with +the key :always are executed always." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-pre-action-hooks + `(;; commands that need to position point at the beginning or end + (eval-last-sexp embark--end-of-target) + (indent-pp-sexp embark--beginning-of-target) + (backward-up-list embark--beginning-of-target) + (backward-list embark--beginning-of-target) + (forward-list embark--end-of-target) + (forward-sexp embark--end-of-target) + (backward-sexp embark--beginning-of-target) + (raise-sexp embark--beginning-of-target) + (kill-sexp embark--beginning-of-target) + (mark-sexp embark--beginning-of-target) + (transpose-sexps embark--end-of-target) + (transpose-sentences embark--end-of-target) + (transpose-paragraphs embark--end-of-target) + (forward-sentence embark--end-of-target) + (backward-sentence embark--beginning-of-target) + (backward-paragraph embark--beginning-of-target) + (embark-insert embark--end-of-target) + ;; commands we want to be able to jump back from + ;; (embark-find-definition achieves this by calling + ;; xref-find-definitions which pushes the markers itself) + (find-library embark--xref-push-marker) + ;; commands which prompt the user for confirmation before running + (delete-file embark--confirm) + (delete-directory embark--confirm) + (kill-buffer embark--confirm) + (embark-kill-buffer-and-window embark--confirm) + (bookmark-delete embark--confirm) + (package-delete embark--confirm) + (,'tab-bar-close-tab-by-name embark--confirm) ;; Avoid package-lint warning + ;; search for region contents outside said region + (embark-isearch embark--unmark-target) + (occur embark--unmark-target) + (query-replace embark--beginning-of-target embark--unmark-target) + (query-replace-regexp embark--beginning-of-target embark--unmark-target) + ;; mark pseudo-action + (mark embark--mark-target) + ;; shells in new buffers + (shell embark--universal-argument) + (eshell embark--universal-argument) + ;; do the actual work of selecting & deselecting targets + (embark-select embark--select)) + "Alist associating commands with pre-action hooks. +The hooks are run right before an action is embarked upon. See +`embark-target-injection-hooks' for information about the hook +arguments and more details." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-post-action-hooks + `((bookmark-delete embark--restart) + (bookmark-rename embark--restart) + (delete-file embark--restart) + (embark-kill-ring-remove embark--restart) + (embark-recentf-remove embark--restart) + (embark-history-remove embark--restart) + (rename-file embark--restart) + (copy-file embark--restart) + (delete-directory embark--restart) + (make-directory embark--restart) + (kill-buffer embark--restart) + (embark-rename-buffer embark--restart) + (,'tab-bar-rename-tab-by-name embark--restart) ;; Avoid package-lint warning + (,'tab-bar-close-tab-by-name embark--restart) + (package-delete embark--restart)) + "Alist associating commands with post-action hooks. +The hooks are run after an embarked upon action concludes. See +`embark-target-injection-hooks' for information about the hook +arguments and more details." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-around-action-hooks + '(;; use directory of target as default-directory + (shell embark--cd) + (eshell embark--cd) + ;; narrow to target for duration of action + (repunctuate-sentences embark--narrow-to-target) + ;; mark the target preserving point and previous mark + (kill-region embark--mark-target) + (kill-ring-save embark--mark-target) + (indent-region embark--mark-target) + (ispell-region embark--mark-target) + (fill-region embark--mark-target) + (upcase-region embark--mark-target) + (downcase-region embark--mark-target) + (capitalize-region embark--mark-target) + (count-words-region embark--mark-target) + (count-words embark--mark-target) + (shell-command-on-region embark--mark-target) + (delete-region embark--mark-target) + (format-encode-region embark--mark-target) + (format-decode-region embark--mark-target) + (write-region embark--mark-target) + (append-to-file embark--mark-target) + (shell-command-on-region embark--mark-target) + (embark-eval-replace embark--mark-target)) + "Alist associating commands with post-action hooks. +The hooks are run instead of the embarked upon action. The hook +can decide whether or not to run the action or it can run it +in some special environment, like inside a let-binding or inside +`save-excursion'. Each hook is called with keyword argument :run +providing a function encapsulating the following around hooks and +the action; the hook additionally receives the keyword arguments +used for other types of action hooks, for more details see +`embark-target-injection-hooks'." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-multitarget-actions '(embark-insert embark-copy-as-kill) + "Commands for which `embark-act-all' should pass a list of targets. +Normally `embark-act-all' runs the same action on each candidate +separately, but when a command included in this variable's value +is used as an action, `embark-act-all' will instead call it +non-interactively with a single argument: the list of all +candidates. For commands on this list `embark-act' behaves +similarly: it calls them non-interactively with a single +argument: a one element list containing the target." + :type '(repeat function)) + +(defcustom embark-repeat-actions + '((mark . region) + ;; outline commands + outline-next-visible-heading outline-previous-visible-heading + outline-forward-same-level outline-backward-same-level + outline-demote outline-promote + outline-show-subtree (outline-mark-subtree . region) + outline-move-subtree-up outline-move-subtree-down + outline-up-heading outline-hide-subtree outline-cycle + ;; org commands (remapped outline commands) + org-forward-heading-same-level org-backward-heading-same-level + org-next-visible-heading org-previous-visible-heading + org-demote-subtree org-promote-subtree + org-show-subtree (org-mark-subtree . region) + org-move-subtree-up org-move-subtree-down + ;; transpose commands + transpose-sexps transpose-sentences transpose-paragraphs + ;; movement + embark-next-symbol embark-previous-symbol + backward-up-list backward-list forward-list forward-sexp + backward-sexp forward-sentence backward-sentence + forward-paragraph backward-paragraph) + "List of repeatable actions. +When you use a command on this list as an Embark action from +outside the minibuffer, `embark-act' does not exit but instead +lets you act again on the possibly new target you reach. + +By default, after using one of these actions, when `embark-act' +looks for targets again, it will start the target cycle at the +same type as the previously acted upon target; that is, you +\"don't loose your place in the target cycle\". + +Sometimes, however, you'll want to prioritize a different type of +target to continue acting on. The main example of this that if +you use a marking command as an action, you almost always want to +act on the region next. For those cases, in addition to +commands, you can also place on this list a pair of a command and +the desired starting type for the target cycle for the next +action." + :type '(repeat (choice function + (cons function + (symbol :tag "Next target type"))))) + +;;; Stashing information for actions in buffer local variables + +(defvar-local embark--type nil + "Cache for the completion type, meant to be set buffer-locally.") + +(defvar-local embark--target-buffer nil + "Cache for the previous buffer, meant to be set buffer-locally.") + +(defvar-local embark--target-window nil + "Cache for the previous window, meant to be set buffer-locally. +Since windows can be reused to display different buffers, this +window should only be used if it displays the buffer stored in +the variable `embark--target-buffer'.") + +(defvar-local embark--command nil + "Command that started the completion session.") + +(defvar-local embark--toggle-quit nil + "Should we toggle the default quitting behavior for the next action?") + +(defun embark--minibuffer-point () + "Return length of minibuffer contents." + (max 0 (- (point) (minibuffer-prompt-end)))) + +(defun embark--default-directory () + "Guess a reasonable default directory for the current candidates." + (if (and (minibufferp) minibuffer-completing-file-name) + (let ((end (minibuffer-prompt-end)) + (contents (minibuffer-contents))) + (expand-file-name + (substitute-in-file-name + (buffer-substring + end + (+ end + (or (cdr + (last + (completion-all-completions + contents + minibuffer-completion-table + minibuffer-completion-predicate + (embark--minibuffer-point)))) + (cl-position ?/ contents :from-end t) + 0)))))) + default-directory)) + +(defun embark--target-buffer () + "Return buffer that should be targeted by Embark actions." + (cond + ((and (minibufferp) (minibuffer-selected-window)) + (window-buffer (minibuffer-selected-window))) + ((and embark--target-buffer (buffer-live-p embark--target-buffer)) + embark--target-buffer) + (t (current-buffer)))) + +(defun embark--target-window (&optional display) + "Return window which should be selected when Embark actions run. +If DISPLAY is non-nil, call `display-buffer' to produce the +window if necessary." + (cond + ((and (minibufferp) (minibuffer-selected-window)) + (minibuffer-selected-window)) + ((and embark--target-window + (window-live-p embark--target-window) + (or (not (buffer-live-p embark--target-buffer)) + (eq (window-buffer embark--target-window) embark--target-buffer))) + embark--target-window) + ((and embark--target-buffer (buffer-live-p embark--target-buffer)) + (or (get-buffer-window embark--target-buffer) + (when display (display-buffer embark--target-buffer)))) + (display (selected-window)))) + +(defun embark--cache-info (buffer) + "Cache information needed for actions in variables local to BUFFER. +BUFFER defaults to the current buffer." + (let ((cmd embark--command) + (dir (embark--default-directory)) + (target-buffer (embark--target-buffer)) + (target-window (embark--target-window))) + (with-current-buffer buffer + (setq embark--command cmd + default-directory dir + embark--target-buffer target-buffer + embark--target-window target-window)))) + +(defun embark--cache-info--completion-list () + "Cache information needed for actions in a *Completions* buffer. +Meant to be be added to `completion-setup-hook'." + ;; when completion-setup-hook hook runs, the *Completions* buffer is + ;; available in the variable standard-output + (embark--cache-info standard-output) + (with-current-buffer standard-output + (when (minibufferp completion-reference-buffer) + (setq embark--type + (completion-metadata-get + (with-current-buffer completion-reference-buffer + (embark--metadata)) + 'category))))) + +;; We have to add this *after* completion-setup-function because that's +;; when the buffer is put in completion-list-mode and turning the mode +;; on kills all local variables! So we use a depth of 5. +(add-hook 'completion-setup-hook #'embark--cache-info--completion-list 5) + +;;;###autoload +(progn + (defun embark--record-this-command () + "Record command which opened the minibuffer. +We record this because it will be the default action. +This function is meant to be added to `minibuffer-setup-hook'." + (setq-local embark--command this-command)) + (add-hook 'minibuffer-setup-hook #'embark--record-this-command)) + +;;; Internal variables + +(defvar embark--prompter-history nil + "History used by the `embark-completing-read-prompter'.") + +;;; Core functionality + +(defconst embark--verbose-indicator-buffer " *Embark Actions*") + +(defvar embark--minimal-indicator-overlay nil) + +(defun embark--metadata () + "Return current minibuffer completion metadata." + (completion-metadata + (buffer-substring-no-properties + (minibuffer-prompt-end) + (max (minibuffer-prompt-end) (point))) + minibuffer-completion-table + minibuffer-completion-predicate)) + +(defun embark-target-active-region () + "Target the region if active." + (when (use-region-p) + (let ((start (region-beginning)) + (end (region-end))) + `(region ,(buffer-substring start end) . (,start . ,end))))) + +(autoload 'dired-get-filename "dired") +(declare-function image-dired-original-file-name "image-dired") + +(defun embark-target-file-at-point () + "Target file at point. +This function mostly relies on `ffap-file-at-point', with the +following exceptions: + +- In `dired-mode', it uses `dired-get-filename' instead. + +- In `imaged-dired-thumbnail-mode', it uses + `image-dired-original-file-name' instead." + (if-let (file (or (and (derived-mode-p 'dired-mode) + (dired-get-filename t 'no-error-if-not-filep)) + (and (derived-mode-p 'image-dired-thumbnail-mode) + (image-dired-original-file-name)))) + (save-excursion + (end-of-line) + `(file ,(abbreviate-file-name (expand-file-name file)) + ,(save-excursion + (re-search-backward " " (line-beginning-position) 'noerror) + (1+ (point))) + . ,(point))) + (when-let* ((ffap-file (ffap-file-at-point)) + (tap-file (thing-at-point 'filename)) + ((not (or (ffap-url-p tap-file) (ffap-el-mode tap-file))))) + `(file ,(abbreviate-file-name (expand-file-name ffap-file)) + ;; TODO the boundaries may be wrong, this should be generalized. + ;; Unfortunately ffap does not make the bounds available. + . ,(bounds-of-thing-at-point 'filename))))) + +(defun embark-target-library-file-at-point () + "Target the file of the Emacs Lisp library at point. +The function `embark-target-file-at-point' could also easily +target Emacs Lisp library files, the only reason it doesn't is so +that library files and other types of file targets can be given +different priorities in `embark-target-finders'." + (when-let* ((name (thing-at-point 'filename)) + (lib (ffap-el-mode name))) + `(file ,lib . ,(bounds-of-thing-at-point 'filename)))) + +(defun embark-target-bug-reference-at-point () + "Target a bug reference at point." + (when-let ((ov (seq-find (lambda (ov) (overlay-get ov 'bug-reference-url)) + (overlays-at (point))))) + `(url ,(overlay-get ov 'bug-reference-url) + ,(overlay-start ov) . ,(overlay-end ov)))) + +(defun embark-target-package-at-point () + "Target the package on the current line in a packages buffer." + (when (derived-mode-p 'package-menu-mode) + (when-let ((pkg (get-text-property (point) 'tabulated-list-id))) + `(package ,(symbol-name (package-desc-name pkg)) + ,(line-beginning-position) . ,(line-end-position))))) + +(defun embark-target-email-at-point () + "Target the email address at point." + (when-let ((email (thing-at-point 'email))) + (when (string-prefix-p "mailto:" email) + (setq email (string-remove-prefix "mailto:" email))) + `(email ,email . ,(bounds-of-thing-at-point 'email)))) + +(defun embark-target-url-at-point () + "Target the URL at point." + (if-let ((url (or (get-text-property (point) 'shr-url) + (get-text-property (point) 'image-url)))) + `(url ,url + ,(previous-single-property-change + (min (1+ (point)) (point-max)) 'mouse-face nil (point-min)) + . ,(next-single-property-change + (point) 'mouse-face nil (point-max))) + (when-let ((url (thing-at-point 'url))) + `(url ,url . ,(thing-at-point-bounds-of-url-at-point t))))) + +(declare-function widget-at "wid-edit") + +(defun embark-target-custom-variable-at-point () + "Target the variable corresponding to the customize widget at point." + (when (derived-mode-p 'Custom-mode) + (save-excursion + (beginning-of-line) + (when-let* ((widget (widget-at (point))) + (var (and (eq (car widget) 'custom-visibility) + (plist-get (cdr widget) :parent))) + (sym (and (eq (car var) 'custom-variable) + (plist-get (cdr var) :value)))) + `(variable + ,(symbol-name sym) + ,(point) + . ,(progn + (re-search-forward ":" (line-end-position) 'noerror) + (point))))))) + +;; NOTE: There is also (thing-at-point 'list), however it does +;; not work on strings and requires the point to be inside the +;; parentheses. This version here is slightly more general. +(defun embark-target-expression-at-point () + "Target expression at point." + (cl-flet ((syntax-p (class &optional (delta 0)) + (and (<= (point-min) (+ (point) delta) (point-max)) + (eq (pcase class + ('open 4) ('close 5) ('prefix 6) ('string 7)) + (syntax-class (syntax-after (+ (point) delta))))))) + (when-let + ((start + (pcase-let ((`(_ ,open _ ,string _ _ _ _ ,start _ _) (syntax-ppss))) + (ignore-errors ; set start=nil if delimiters are unbalanced + (cond + (string start) + ((or (syntax-p 'open) (syntax-p 'prefix)) + (save-excursion (backward-prefix-chars) (point))) + ((syntax-p 'close -1) + (save-excursion + (backward-sexp) (backward-prefix-chars) (point))) + ((syntax-p 'string) (point)) + ((syntax-p 'string -1) (scan-sexps (point) -1)) + (t open))))) + (end (ignore-errors (scan-sexps start 1)))) + (unless (eq start (car (bounds-of-thing-at-point 'defun))) + `(expression ,(buffer-substring start end) ,start . ,end))))) + +(defmacro embark-define-thingatpt-target (thing &rest modes) + "Define a target finder for THING using the thingatpt library. +If any MODES are given, the target finder only applies to buffers +in one of those major modes." + (declare (indent 1)) + `(defun ,(intern (format "embark-target-%s-at-point" thing)) () + ,(format "Target %s at point." thing) + (when ,(if modes `(derived-mode-p ,@(mapcar (lambda (m) `',m) modes)) t) + (when-let (bounds (bounds-of-thing-at-point ',thing)) + (cons ',thing (cons + (buffer-substring (car bounds) (cdr bounds)) + bounds)))))) + +(embark-define-thingatpt-target defun) +(embark-define-thingatpt-target sentence + text-mode help-mode Info-mode man-common) +(embark-define-thingatpt-target paragraph + text-mode help-mode Info-mode man-common) + +(defun embark--identifier-types (identifier) + "Return list of target types appropriate for IDENTIFIER." + (let ((symbol (intern-soft identifier))) + (if (not + (or (derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode) + (and (not (derived-mode-p 'prog-mode)) + symbol + (or (boundp symbol) (fboundp symbol) (symbol-plist symbol))))) + '(identifier) + (let* ((library (ffap-el-mode identifier)) + (types + (append + (and (commandp symbol) '(command)) + (and symbol (boundp symbol) (not (keywordp symbol)) '(variable)) + (and (fboundp symbol) (not (commandp symbol)) '(function)) + (and (facep symbol) '(face)) + (and library '(library)) + (and (featurep 'package) (embark--package-desc symbol) + '(package))))) + (when (and library + (looking-back "\\(?:require\\|use-package\\).*" + (line-beginning-position))) + (setq types (embark--rotate types (cl-position 'library types)))) + (or types '(symbol)))))) + +(defun embark-target-identifier-at-point () + "Target identifier at point. + +In Emacs Lisp and IELM buffers the identifier is promoted to a +symbol, for which more actions are available. Identifiers are +also promoted to symbols if they are interned Emacs Lisp symbols +and found in a buffer in a major mode that is not derived from +`prog-mode' (this is intended for when you might be reading or +writing about Emacs). + +As a convenience, in Org Mode an initial ' or surrounding == or +~~ are removed." + (when-let (bounds (bounds-of-thing-at-point 'symbol)) + (let ((name (buffer-substring (car bounds) (cdr bounds)))) + (when (derived-mode-p 'org-mode) + (cond ((string-prefix-p "'" name) + (setq name (substring name 1)) + (cl-incf (car bounds))) + ((string-match-p "^\\([=~]\\).*\\1$" name) + (setq name (substring name 1 -1)) + (cl-incf (car bounds)) + (cl-decf (cdr bounds))))) + (mapcar (lambda (type) `(,type ,name . ,bounds)) + (embark--identifier-types name))))) + +(defun embark-target-heading-at-point () + "Target the outline heading at point." + (let ((beg (line-beginning-position)) + (end (line-end-position))) + (when (save-excursion + (goto-char beg) + (and (bolp) + (looking-at + ;; default definition from outline.el + (or (bound-and-true-p outline-regexp) "[*\^L]+")))) + (require 'outline) ;; Ensure that outline commands are available + `(heading ,(buffer-substring beg end) ,beg . ,end)))) + +(defun embark-target-text-heading-at-point () + "Target the outline heading at point in text modes." + (when (derived-mode-p 'text-mode) + (embark-target-heading-at-point))) + +(defun embark-target-prog-heading-at-point () + "Target the outline heading at point in programming modes." + (when (derived-mode-p 'prog-mode) + (embark-target-heading-at-point))) + +(defun embark-target-top-minibuffer-completion () + "Target the top completion candidate in the minibuffer. +Return the category metadatum as the type of the target. + +This target finder is meant for the default completion UI and +completion UI highly compatible with it, like Icomplete. +Many completion UIs can still work with Embark but will need +their own target finder. See for example +`embark--vertico-selected'." + (when (and (minibufferp) minibuffer-completion-table) + (pcase-let* ((`(,category . ,candidates) (embark-minibuffer-candidates)) + (contents (minibuffer-contents)) + (top (if (test-completion contents + minibuffer-completion-table + minibuffer-completion-predicate) + contents + (let ((completions (completion-all-sorted-completions))) + (if (null completions) + contents + (concat + (substring contents + 0 (or (cdr (last completions)) 0)) + (car completions))))))) + (cons category (or (car (member top candidates)) top))))) + +(defun embark-target-collect-candidate () + "Target the collect candidate at point." + (when (derived-mode-p 'embark-collect-mode) + (when-let ((button + (pcase (get-text-property (point) 'tabulated-list-column-name) + ("Candidate" (button-at (point))) + ("Annotation" (previous-button (point))))) + (start (button-start button)) + (end (button-end button)) + (candidate (tabulated-list-get-id))) + `(,embark--type + ,(if (eq embark--type 'file) + (abbreviate-file-name (expand-file-name candidate)) + candidate) + ,start . ,end)))) + +(defun embark-target-completion-at-point () + "Return the completion candidate at point in a completions buffer." + (when (derived-mode-p 'completion-list-mode) + (if (not (get-text-property (point) 'mouse-face)) + (user-error "No completion here") + ;; this fairly delicate logic is taken from `choose-completion' + (let (beg end) + (cond + ((and (not (eobp)) (get-text-property (point) 'mouse-face)) + (setq end (point) beg (1+ (point)))) + ((and (not (bobp)) + (get-text-property (1- (point)) 'mouse-face)) + (setq end (1- (point)) beg (point))) + (t (user-error "No completion here"))) + (setq beg (previous-single-property-change beg 'mouse-face)) + (setq end (or (next-single-property-change end 'mouse-face) + (point-max))) + (let ((raw (or (get-text-property beg 'completion--string) + (buffer-substring beg end)))) + `(,embark--type + ,(if (eq embark--type 'file) + (abbreviate-file-name (expand-file-name raw)) + raw) + ,beg . ,end)))))) + +(defun embark--cycle-key () + "Return the key to use for `embark-cycle'." + (if embark-cycle-key + (if (key-valid-p embark-cycle-key) + (key-parse embark-cycle-key) + (error "`embark-cycle-key' is invalid")) + (car (where-is-internal #'embark-act)))) + +(defun embark--raw-action-keymap (type) + "Return raw action map for targets of given TYPE. +This does not take into account the default action, help key or +cycling bindings, just what's registered in +`embark-keymap-alist'." + (make-composed-keymap + (mapcar #'symbol-value + (let ((actions (or (alist-get type embark-keymap-alist) + (alist-get t embark-keymap-alist)))) + (ensure-list actions))))) + +(defun embark--action-keymap (type cycle) + "Return action keymap for targets of given TYPE. +If CYCLE is non-nil bind `embark-cycle'." + (make-composed-keymap + (let ((map (make-sparse-keymap)) + (default-action (embark--default-action type))) + (define-key map [13] default-action) + (when-let ((cycle-key (and cycle (embark--cycle-key)))) + (define-key map cycle-key #'embark-cycle)) + (when embark-help-key + (keymap-set map embark-help-key #'embark-keymap-help)) + map) + (embark--raw-action-keymap type))) + +(defun embark--truncate-target (target) + "Truncate TARGET string." + (unless (stringp target) + (setq target (format "%s" target))) + (if-let (pos (string-match-p "\n" target)) + (concat (car (split-string target "\n" 'omit-nulls "\\s-*")) "…") + target)) + +;;;###autoload +(defun embark-eldoc-first-target (report &rest _) + "Eldoc function reporting the first Embark target at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'." + (when-let (((not (minibufferp))) + (target (car (embark--targets)))) + (funcall report + (format "Embark on %s ‘%s’" + (plist-get target :type) + (embark--truncate-target (plist-get target :target)))))) + +;;;###autoload +(defun embark-eldoc-target-types (report &rest _) + "Eldoc function reporting the types of all Embark targets at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'." + (when-let (((not (minibufferp))) + (targets (embark--targets))) + (funcall report + (format "Embark target types: %s" + (mapconcat + (lambda (target) (symbol-name (plist-get target :type))) + targets + ", "))))) + +(defun embark--format-targets (target shadowed-targets rep) + "Return a formatted string indicating the TARGET of an action. + +This is used internally by the minimal indicator and for the +targets section of the verbose indicator. The string will also +mention any SHADOWED-TARGETS. A non-nil REP indicates we are in +a repeating sequence of actions." + (let ((act (propertize + (cond + ((plist-get target :multi) "∀ct") + (rep "Rep") + (t "Act")) + 'face 'highlight))) + (cond + ((eq (plist-get target :type) 'embark-become) + (propertize "Become" 'face 'highlight)) + ((and (minibufferp) + (not (eq 'embark-keybinding + (completion-metadata-get + (embark--metadata) + 'category)))) + ;; we are in a minibuffer but not from the + ;; completing-read prompter, use just "Act" + act) + ((plist-get target :multi) + (format "%s on %s %ss" + act + (plist-get target :multi) + (plist-get target :type))) + (t (format + "%s on %s%s ‘%s’" + act + (plist-get target :type) + (if shadowed-targets + (format (propertize "(%s)" 'face 'shadow) + (mapconcat + (lambda (target) (symbol-name (plist-get target :type))) + shadowed-targets + ", ")) + "") + (embark--truncate-target (plist-get target :target))))))) + +(defun embark-minimal-indicator () + "Minimal indicator, appearing in the minibuffer prompt or echo area. +This indicator displays a message showing the types of all +targets, starting with the current target, and the value of the +current target. The message is displayed in the echo area, or if +the minibuffer is open, the message is added to the prompt." + (lambda (&optional keymap targets _prefix) + (if (null keymap) + (when embark--minimal-indicator-overlay + (delete-overlay embark--minimal-indicator-overlay) + (setq-local embark--minimal-indicator-overlay nil)) + (let ((indicator (embark--format-targets + (car targets) (cdr targets) + (eq (lookup-key keymap [13]) #'embark-done)))) + (if (not (minibufferp)) + (message "%s" indicator) + (unless embark--minimal-indicator-overlay + (setq-local embark--minimal-indicator-overlay + (make-overlay (point-min) (point-min) + (current-buffer) t t))) + (overlay-put embark--minimal-indicator-overlay + 'before-string (concat indicator + (if (<= (length indicator) + (* 0.4 (frame-width))) + " " + "\n")))))))) + +(defun embark--read-key-sequence (update) + "Read key sequence, call UPDATE function with prefix keys." + (let (timer prefix) + (unwind-protect + (progn + (when (functionp update) + (setq timer (run-at-time + 0.05 0.05 + (lambda () + (let ((new-prefix (this-single-command-keys))) + (unless (equal prefix new-prefix) + (setq prefix new-prefix) + (when (/= (length prefix) 0) + (funcall update prefix)))))))) + (read-key-sequence-vector nil nil nil t 'cmd-loop)) + (when timer + (cancel-timer timer))))) + +(defvar embark-indicators) ; forward declaration + +(defun embark-keymap-prompter (keymap update) + "Let the user choose an action using the bindings in KEYMAP. +Besides the bindings in KEYMAP, the user is free to use all their +key bindings and even \\[execute-extended-command] to select a command. +UPDATE is the indicator update function." + (let* ((keys (let ((overriding-terminal-local-map keymap)) + (embark--read-key-sequence update))) + (cmd (let ((overriding-terminal-local-map keymap)) + (key-binding keys 'accept-default)))) + ;; Set last-command-event as it would be from the command loop. + ;; Previously we only set it locally for digit-argument and for + ;; the mouse scroll commands handled in this function. But other + ;; commands can need it too! For example, electric-pair-mode users + ;; may wish to bind ( to self-insert-command in embark-region-map. + ;; Also, as described in issue #402, there are circumstances where + ;; you might run consult-narrow through the embark-keymap-prompter. + (setq last-command-event (aref keys (1- (length keys)))) + (pcase cmd + ((or 'embark-keymap-help + (and 'nil ; cmd is nil but last key is help-char + (guard (eq help-char (aref keys (1- (length keys))))))) + (let ((embark-indicators + (cl-set-difference embark-indicators + '(embark-verbose-indicator + embark-mixed-indicator))) + (prefix-map + (if (eq cmd 'embark-keymap-help) + keymap + (let ((overriding-terminal-local-map keymap)) + (key-binding (seq-take keys (1- (length keys))) + 'accept-default))))) + (when-let ((win (get-buffer-window embark--verbose-indicator-buffer + 'visible))) + (quit-window 'kill-buffer win)) + (embark-completing-read-prompter prefix-map update))) + ((or 'universal-argument 'universal-argument-more + 'negative-argument 'digit-argument 'embark-toggle-quit) + ;; prevent `digit-argument' from modifying the overriding map + (let ((overriding-terminal-local-map overriding-terminal-local-map)) + (command-execute cmd)) + (embark-keymap-prompter + (make-composed-keymap universal-argument-map keymap) + update)) + ((or 'minibuffer-keyboard-quit 'abort-recursive-edit 'abort-minibuffers) + nil) + ((guard (let ((def (lookup-key keymap keys))) ; if directly + ; bound, then obey + (and def (not (numberp def))))) ; number means "invalid prefix" + cmd) + ('self-insert-command + (minibuffer-message "Not an action") + (embark-keymap-prompter keymap update)) + ((or 'scroll-other-window 'scroll-other-window-down) + (let ((minibuffer-scroll-window + ;; NOTE: Here we special case the verbose indicator! + (or (get-buffer-window embark--verbose-indicator-buffer 'visible) + minibuffer-scroll-window))) + (ignore-errors (command-execute cmd))) + (embark-keymap-prompter keymap update)) + ((or 'scroll-bar-toolkit-scroll 'mwheel-scroll 'mac-mwheel-scroll) + (funcall cmd last-command-event) + (embark-keymap-prompter keymap update)) + ('execute-extended-command + (intern-soft (read-extended-command))) + ((or 'keyboard-quit 'keyboard-escape-quit) + nil) + (_ cmd)))) + +(defun embark--command-name (cmd) + "Return an appropriate name for CMD. +If CMD is a symbol, use its symbol name; for lambdas, use the +first line of the documentation string; otherwise use the word +\"unnamed\"." + (concat ; fresh copy, so we can freely add text properties + (cond + ((stringp (car-safe cmd)) (car cmd)) + ((eq (car-safe cmd) 'menu-item) (cadr cmd)) + ((keymapp cmd) + (propertize (if (symbolp cmd) (format "+%s" cmd) "<keymap>") + 'face 'embark-keymap)) + ((symbolp cmd) + (let ((name (symbol-name cmd))) + (if (string-prefix-p "embark-action--" name) ; direct action mode + (format "(%s)" (string-remove-prefix "embark-action--" name)) + name))) + ((when-let (doc (and (functionp cmd) (ignore-errors (documentation cmd)))) + (save-match-data + (when (string-match "^\\(.*\\)$" doc) + (match-string 1 doc))))) + (t "<unnamed>")))) + +;; Taken from Marginalia, needed by the verbose indicator. +;; We cannot use the completion annotators in this case. +(defconst embark--advice-regexp + (rx bos + (1+ (seq (? "This function has ") + (or ":before" ":after" ":around" ":override" + ":before-while" ":before-until" ":after-while" + ":after-until" ":filter-args" ":filter-return") + " advice: " (0+ nonl) "\n")) + "\n") + "Regexp to match lines about advice in function documentation strings.") + +;; Taken from Marginalia, needed by the verbose indicator. +;; We cannot use the completion annotators in this case. +(defun embark--function-doc (sym) + "Documentation string of function SYM." + (let ((vstr (and (symbolp sym) (keymapp sym) (boundp sym) + (eq (symbol-function sym) (symbol-value sym)) + (documentation-property sym 'variable-documentation)))) + (when-let (str (or (ignore-errors (documentation sym)) vstr)) + ;; Replace standard description with variable documentation + (when (and vstr (string-match-p "\\`Prefix command" str)) + (setq str vstr)) + (save-match-data + (if (string-match embark--advice-regexp str) + (substring str (match-end 0)) + str))))) + +(defun embark--action-repeatable-p (action) + "Is ACTION repeatable? +When the return value is non-nil it will be the desired starting +point of the next target cycle or t to indicate the default, +namely that the target cycle for the next action should begin at +the type of the current target." + (or (cdr (assq action embark-repeat-actions)) + (and (memq action embark-repeat-actions) t))) + +(defun embark--formatted-bindings (keymap &optional nested) + "Return the formatted keybinding of KEYMAP. +The keybindings are returned in their order of appearance. +If NESTED is non-nil subkeymaps are not flattened." + (let* ((commands + (cl-loop for (key . def) in (embark--all-bindings keymap nested) + for name = (embark--command-name def) + for cmd = (keymap--menu-item-binding def) + unless (memq cmd '(nil embark-keymap-help + negative-argument digit-argument)) + collect (list name cmd key + (concat + (if (eq (car-safe def) 'menu-item) + "menu-item" + (key-description key)))))) + (width (cl-loop for (_name _cmd _key desc) in commands + maximize (length desc))) + (default) + (candidates + (cl-loop for item in commands + for (name cmd key desc) = item + for desc-rep = + (concat + (propertize desc 'face 'embark-keybinding) + (and (embark--action-repeatable-p cmd) + embark-keybinding-repeat)) + for formatted = + (propertize + (concat desc-rep + (make-string (- width (length desc-rep) -1) ?\s) + name) + 'embark-command cmd) + when (equal key [13]) + do (setq default formatted) + collect (cons formatted item)))) + (cons candidates default))) + +(defun embark--with-category (category candidates) + "Return completion table for CANDIDATES of CATEGORY with sorting disabled." + (lambda (string predicate action) + (if (eq action 'metadata) + `(metadata (display-sort-function . identity) + (cycle-sort-function . identity) + (category . ,category)) + (complete-with-action + action candidates string predicate)))) + +(defun embark-completing-read-prompter (keymap update &optional no-default) + "Prompt via completion for a command bound in KEYMAP. +If NO-DEFAULT is t, no default value is passed to`completing-read'. + +UPDATE is the indicator update function. It is not used directly +here, but if the user switches to `embark-keymap-prompter', the +UPDATE function is passed to it." + (let* ((candidates+def (embark--formatted-bindings keymap)) + (candidates (car candidates+def)) + (def (and (not no-default) (cdr candidates+def))) + (buf (current-buffer)) + (choice + (catch 'choice + (minibuffer-with-setup-hook + (lambda () + (let ((map (make-sparse-keymap))) + (define-key map "\M-q" + (lambda () + (interactive) + (with-current-buffer buf + (embark-toggle-quit)))) + (when-let (cycle (embark--cycle-key)) + ;; Rebind `embark-cycle' in order allow cycling + ;; from the `completing-read' prompter. Additionally + ;; `embark-cycle' can be selected via + ;; `completing-read'. The downside is that this breaks + ;; recursively acting on the candidates of type + ;; embark-keybinding in the `completing-read' prompter. + (define-key map cycle + (cond + ((eq (lookup-key keymap cycle) 'embark-cycle) + (lambda () + (interactive) + (throw 'choice 'embark-cycle))) + ((null embark-cycle-key) + (lambda () + (interactive) + (minibuffer-message + "No cycling possible; press `%s' again to act." + (key-description cycle)) + (define-key map cycle #'embark-act)))))) + (when embark-keymap-prompter-key + (keymap-set map embark-keymap-prompter-key + (lambda () + (interactive) + (message "Press key binding") + (let ((cmd (embark-keymap-prompter keymap update))) + (if (null cmd) + (user-error "Unknown key") + (throw 'choice cmd)))))) + (use-local-map + (make-composed-keymap map (current-local-map))))) + (completing-read + "Command: " + (embark--with-category 'embark-keybinding candidates) + nil nil nil 'embark--prompter-history def))))) + (pcase (assoc choice candidates) + (`(,_formatted ,_name ,cmd ,key ,_desc) + ;; Set last-command-event as it would be from the command loop. + (setq last-command-event (aref key (1- (length key)))) + cmd) + ('nil (intern-soft choice))))) + +;;; Verbose action indicator + +(defgroup embark-indicators nil + "Indicators display information about actions and targets." + :group 'embark) + +(defcustom embark-indicators + '(embark-mixed-indicator + embark-highlight-indicator + embark-isearch-highlight-indicator) + "Indicator functions to use when acting or becoming. +The indicator functions are called from both `embark-act' and +from `embark-become' and should display information about this to +the user, such as: which of those two commands is running; a +description of the key bindings that are available for actions or +commands to become; and, in the case of `embark-act', the type +and value of the targets, and whether other targets are available +via `embark-cycle'. The indicator function is free to display as +much or as little of this information as desired and can use any +Emacs interface elements to do so. + +Embark comes with five such indicators: + +- `embark-minimal-indicator', which does not display any + information about keybindings, but does display types and + values of action targets in the echo area or minibuffer prompt, + +- `embark-verbose-indicator', which pops up a buffer containing + detailed information including key bindings and the first line + of the docstring of the commands they run, and + +- `embark-mixed-indicator', which combines the minimal and the + verbose indicator: the minimal indicator is shown first and the + verbose popup is shown after `embark-mixed-indicator-delay' + seconds. + +- `embark-highlight-indicator', which highlights the target + at point. + +- `embark-isearch-highlight-indicator', which when the target at + point is an identifier or symbol, lazily highlights all + occurrences of it. + +The protocol for indicator functions is as follows: + +When called from `embark-act', an indicator function is called +without arguments. The indicator function should then return a +closure, which captures the indicator state. The returned +closure must accept up to three optional arguments, the action +keymap, the targets (plists as returned by `embark--targets') and +the prefix keys typed by the user so far. The keymap, targets +and prefix keys may be updated when cycling targets at point +resulting in multiple calls to the closure. When called from +`embark-become', the indicator closure will be called with the +keymap of commands to become, a fake target list containing a +single target of type `embark-become' and whose value is the +minibuffer input, and the prefix set to nil. Note, in +particular, that if an indicator function wishes to distinguish +between `embark-act' and `embark-become' it should check whether +the `car' of the first target is `embark-become'. + +After the action has been performed the indicator closure is +called without arguments, such that the indicator can perform the +necessary cleanup work. For example, if the indicator adds +overlays, it should remove these overlays. The indicator should +be written in a way that it is safe to call it for cleanup more +than once, in fact, it should be able to handle any sequence of +update and cleanup calls ending in a call for cleanup. + +NOTE: Experience shows that the indicator calling convention may +change again in order to support more action features. The +calling convention should currently be considered unstable. +Please keep this in mind when writing a custom indicator +function, or when using the `which-key' indicator function from +the wiki." + :type '(repeat + (choice + (const :tag "Verbose indicator" embark-verbose-indicator) + (const :tag "Minimal indicator" embark-minimal-indicator) + (const :tag "Mixed indicator" embark-mixed-indicator) + (const :tag "Highlight target" embark-highlight-indicator) + (const :tag "Highlight all occurrences" + embark-isearch-highlight-indicator) + (function :tag "Other")))) + +(defface embark-verbose-indicator-documentation + '((t :inherit completions-annotations)) + "Face used by the verbose action indicator to display binding descriptions. +Used by `embark-verbose-indicator'.") + +(defface embark-verbose-indicator-title '((t :height 1.1 :weight bold)) + "Face used by the verbose action indicator for the title. +Used by `embark-verbose-indicator'.") + +(defface embark-verbose-indicator-shadowed '((t :inherit shadow)) + "Face used by the verbose action indicator for the shadowed targets. +Used by `embark-verbose-indicator'.") + +(defcustom embark-verbose-indicator-display-action + '(display-buffer-reuse-window) + "Parameters added to `display-buffer-alist' to show the actions buffer. +See the docstring of `display-buffer' for information on what +display actions and parameters are available." + :type `(choice + (const :tag "Reuse some window" + (display-buffer-reuse-window)) + (const :tag "Below target buffer" + (display-buffer-below-selected + (window-height . fit-window-to-buffer))) + (const :tag "Bottom of frame (fixed-size)" + (display-buffer-at-bottom)) + (const :tag "Bottom of frame (resizes during cycling)" + (display-buffer-at-bottom + (window-height . fit-window-to-buffer))) + (const :tag "Side window on the right" + (display-buffer-in-side-window (side . right))) + (const :tag "Side window on the left" + (display-buffer-in-side-window (side . left))) + (sexp :tag "Other"))) + +(defcustom embark-verbose-indicator-excluded-actions nil + "Commands not displayed by `embark-verbose-indicator'. +This variable should be set to a list of symbols and regexps. +The verbose indicator will exclude from its listing any commands +matching an element of this list." + :type '(choice + (const :tag "Exclude nothing" nil) + (const :tag "Exclude Embark general actions" + (embark-collect embark-live embark-export + embark-cycle embark-act-all embark-keymap-help + embark-become embark-isearch)) + (repeat :tag "Other" (choice regexp symbol)))) + +(defcustom embark-verbose-indicator-buffer-sections + `(target "\n" shadowed-targets " " cycle "\n" bindings) + "List of sections to display in the verbose indicator buffer, in order. +You can use either a symbol designating a concrete section (one +of the keywords below, but without the colon), a string literal +or a function returning a string or list of strings to insert and +that accepts the following keyword arguments: + +- `:target', the target as a cons of type and value, +- `:shadowed-targets', a list of conses for the other targets, +- `:bindings' a list returned by `embark--formatted-bindings', and +- `:cycle', a string describing the key binding of `embark-cycle'." + :type '(repeat + (choice (const :tag "Current target name" target) + (const :tag "List of other shadowed targets" shadowed-targets) + (const :tag "Key bindings" bindings) + (const :tag "Cycle indicator" cycle) + (string :tag "Literal string") + (function :tag "Custom function")))) + +(defcustom embark-verbose-indicator-nested t + "Whether the verbose indicator should use nested keymap navigation. +When this variable is non-nil the actions buffer displayed by +`embark-verbose-indicator' will include any prefix keys found in +the keymap it is displaying, and will update to show what is +bound under the prefix if the prefix is pressed. If this +variable is nil, then the actions buffer will contain a flat list +of all full key sequences bound in the keymap." + :type 'boolean) + +(defun embark--verbose-indicator-excluded-p (cmd) + "Return non-nil if CMD should be excluded from the verbose indicator." + (seq-find (lambda (x) + (if (symbolp x) + (eq cmd x) + (string-match-p x (symbol-name cmd)))) + embark-verbose-indicator-excluded-actions)) + +(cl-defun embark--verbose-indicator-section-target + (&key targets bindings &allow-other-keys) + "Format the TARGETS section for the indicator buffer. +BINDINGS is the formatted list of keybindings." + (let ((result (embark--format-targets + (car targets) + nil ; the shadowed targets section deals with these + (cl-find 'embark-done bindings :key #'caddr :test #'eq)))) + (add-face-text-property 0 (length result) + 'embark-verbose-indicator-title + 'append + result) + result)) + +(cl-defun embark--verbose-indicator-section-cycle + (&key cycle shadowed-targets &allow-other-keys) + "Format the CYCLE key section for the indicator buffer. +SHADOWED-TARGETS is the list of other targets." + (concat + (and cycle (propertize (format "(%s to cycle)" cycle) + 'face 'embark-verbose-indicator-shadowed)) + (and shadowed-targets "\n"))) + +(cl-defun embark--verbose-indicator-section-shadowed-targets + (&key shadowed-targets &allow-other-keys) + "Format the SHADOWED-TARGETS section for the indicator buffer." + (when shadowed-targets + (propertize (format "Shadowed targets at point: %s" + (string-join shadowed-targets ", ")) + 'face 'embark-verbose-indicator-shadowed))) + +(cl-defun embark--verbose-indicator-section-bindings + (&key bindings &allow-other-keys) + "Format the BINDINGS section for the indicator buffer." + (let* ((max-width (apply #'max (cons 0 (mapcar (lambda (x) + (string-width (car x))) + bindings)))) + (fmt (format "%%-%ds" (1+ max-width))) + (result nil)) + (dolist (binding bindings (string-join (nreverse result))) + (let ((cmd (caddr binding))) + (unless (embark--verbose-indicator-excluded-p cmd) + (let ((keys (format fmt (car binding))) + (doc (embark--function-doc cmd))) + (push (format "%s%s\n" keys + (propertize + (car (split-string (or doc "") "\n")) + 'face 'embark-verbose-indicator-documentation)) + result))))))) + +(defun embark--verbose-indicator-update (keymap targets) + "Update verbose indicator buffer. +The arguments are the new KEYMAP and TARGETS." + (with-current-buffer (get-buffer-create embark--verbose-indicator-buffer) + (let* ((inhibit-read-only t) + (bindings + (embark--formatted-bindings keymap embark-verbose-indicator-nested)) + (bindings (car bindings)) + (shadowed-targets (mapcar + (lambda (x) (symbol-name (plist-get x :type))) + (cdr targets))) + (cycle (let ((ck (where-is-internal #'embark-cycle keymap))) + (and ck (key-description (car ck)))))) + (setq-local cursor-type nil) + (setq-local truncate-lines t) + (setq-local buffer-read-only t) + (erase-buffer) + (dolist (section embark-verbose-indicator-buffer-sections) + (insert + (if (stringp section) + section + (or (funcall + (let ((prefixed (intern (format + "embark--verbose-indicator-section-%s" + section)))) + (cond + ((fboundp prefixed) prefixed) + ((fboundp section) section) + (t (error "Undefined verbose indicator section `%s'" + section)))) + :targets targets :shadowed-targets shadowed-targets + :bindings bindings :cycle cycle) + "")))) + (goto-char (point-min))))) + +(defun embark-verbose-indicator () + "Indicator that displays a table of key bindings in a buffer. +The default display includes the type and value of the current +target, the list of other target types, and a table of key +bindings, actions and the first line of their docstrings. + +The order and formatting of these items is completely +configurable through the variable +`embark-verbose-indicator-buffer-sections'. + +If the keymap being shown contains prefix keys, the table of key +bindings can either show just the prefixes and update once the +prefix is pressed, or it can contain a flat list of all full key +sequences bound in the keymap. This is controlled by the +variable `embark-verbose-indicator-nested'. + +To reduce clutter in the key binding table, one can set the +variable `embark-verbose-indicator-excluded-actions' to a list +of symbols and regexps matching commands to exclude from the +table. + +To configure how a window is chosen to display this buffer, see +the variable `embark-verbose-indicator-display-action'." + (lambda (&optional keymap targets prefix) + (if (not keymap) + (when-let ((win (get-buffer-window embark--verbose-indicator-buffer + 'visible))) + (quit-window 'kill-buffer win)) + (embark--verbose-indicator-update + (if (and prefix embark-verbose-indicator-nested) + ;; Lookup prefix keymap globally if not found in action keymap + (let ((overriding-terminal-local-map keymap)) + (key-binding prefix 'accept-default)) + keymap) + targets) + (let ((display-buffer-alist + `(,@display-buffer-alist + (,(regexp-quote embark--verbose-indicator-buffer) + ,@embark-verbose-indicator-display-action)))) + (display-buffer embark--verbose-indicator-buffer))))) + +(defcustom embark-mixed-indicator-delay 0.5 + "Time in seconds after which the verbose indicator is shown. +The mixed indicator starts by showing the minimal indicator and +after this delay shows the verbose indicator." + :type '(choice (const :tag "No delay" 0) + (number :tag "Delay in seconds"))) + +(defcustom embark-mixed-indicator-both nil + "Show both indicators, even after the verbose indicator appeared." + :type 'boolean) + +(defun embark-mixed-indicator () + "Mixed indicator showing keymap and targets. +The indicator shows the `embark-minimal-indicator' by default. +After `embark-mixed-indicator-delay' seconds, the +`embark-verbose-indicator' is shown. This which-key-like approach +ensures that Embark stays out of the way for quick actions. The +helpful keybinding reminder still pops up automatically without +further user intervention." + (let ((vindicator (embark-verbose-indicator)) + (mindicator (embark-minimal-indicator)) + vindicator-active + vtimer) + (lambda (&optional keymap targets prefix) + ;; Always cancel the timer. + ;; 1. When updating, cancel timer, since the user has pressed + ;; a key before the timer elapsed. + ;; 2. For cleanup, the timer must also be canceled. + (when vtimer + (cancel-timer vtimer) + (setq vtimer nil)) + (if (not keymap) + (progn + (funcall vindicator) + (when mindicator + (funcall mindicator))) + (when mindicator + (funcall mindicator keymap targets prefix)) + (if vindicator-active + (funcall vindicator keymap targets prefix) + (setq vtimer + (run-at-time + embark-mixed-indicator-delay nil + (lambda () + (when (and (not embark-mixed-indicator-both) mindicator) + (funcall mindicator) + (setq mindicator nil)) + (setq vindicator-active t) + (funcall vindicator keymap targets prefix))))))))) + +;;;###autoload +(defun embark-bindings-in-keymap (keymap) + "Explore command key bindings in KEYMAP with `completing-read'. +The selected command will be executed. Interactively, prompt the +user for a KEYMAP variable." + (interactive + (list + (symbol-value + (intern-soft + (completing-read + "Keymap: " + (embark--with-category + 'variable + (cl-loop for x being the symbols + if (and (boundp x) (keymapp (symbol-value x))) + collect (symbol-name x))) + nil t nil 'variable-name-history + (let ((major-mode-map + (concat (symbol-name major-mode) "-map"))) + (when (intern-soft major-mode-map) major-mode-map))))))) + (when-let (command (embark-completing-read-prompter keymap nil 'no-default)) + (call-interactively command))) + +;;;###autoload +(defun embark-bindings (no-global) + "Explore all current command key bindings with `completing-read'. +The selected command will be executed. + +If NO-GLOBAL is non-nil (interactively, if called with a prefix +argument) omit global key bindings; this leaves key bindings from +minor mode maps and the local map (usually set by the major +mode), but also less common keymaps such as those from a text +property or overlay, or the overriding maps: +`overriding-terminal-local-map' and `overriding-local-map'." + (interactive "P") + (embark-bindings-in-keymap + (make-composed-keymap + (let ((all-maps (current-active-maps t))) + (if no-global (remq global-map all-maps) all-maps))))) + +;;;###autoload +(defun embark-bindings-at-point () + "Explore all key bindings at point with `completing-read'. +The selected command will be executed. + +This command lists key bindings found in keymaps specified by the +text properties `keymap' or `local-map', from either buffer text +or an overlay. These are not widely used in Emacs, and when they +are used can be somewhat hard to discover. Examples of locations +that have such a keymap are links and images in `eww' buffers, +attachment links in `gnus' article buffers, and the 'Stash' line +in a `vc-dir' buffer." + (interactive) + (let ((keymaps (delq nil (list (get-char-property (point) 'keymap) + (get-char-property (point) 'local-map))))) + (unless keymaps + (user-error "No key bindings found at point")) + (embark-bindings-in-keymap (make-composed-keymap keymaps)))) + +;;;###autoload +(defun embark-prefix-help-command () + "Prompt for and run a command bound in the prefix used for this command. +The prefix described consists of all but the last event of the +key sequence that ran this command. This function is intended to +be used as a value for `prefix-help-command'. + +In addition to using completion to select a command, you can also +type @ and the key binding (without the prefix)." + (interactive) + (when-let ((keys (this-command-keys-vector)) + (prefix (seq-take keys (1- (length keys)))) + (keymap (key-binding prefix 'accept-default))) + (embark-bindings-in-keymap keymap))) + +(defun embark--prompt (indicators keymap targets) + "Call the prompter with KEYMAP and INDICATORS. +The TARGETS are displayed for actions outside the minibuffer." + (mapc (lambda (i) (funcall i keymap targets)) indicators) + (condition-case nil + (minibuffer-with-setup-hook + (lambda () + ;; if the prompter opens its own minibuffer, show + ;; the indicator there too + (let ((inner-indicators (mapcar #'funcall embark-indicators))) + (mapc (lambda (i) (funcall i keymap targets)) inner-indicators) + (add-hook 'minibuffer-exit-hook + (lambda () (mapc #'funcall inner-indicators)) + nil t))) + (let ((enable-recursive-minibuffers t)) + (funcall embark-prompter keymap + (lambda (prefix) + (mapc (lambda (i) (funcall i keymap targets prefix)) + indicators))))) + (quit nil))) + +(defvar embark--run-after-command-functions nil + "Abnormal hook, used by `embark--run-after-command'.") + +(defun embark--run-after-command (fn &rest args) + "Call FN with ARGS after the current commands finishes. +If multiple functions are queued with this function during the +same command, they will be called in the order from the one +queued most recently to the one queued least recently." + ;; We don't simply add FN to `post-command-hook' because FN may recursively + ;; call this function. In that case, FN would modify `post-command-hook' + ;; from within post-command-hook, which doesn't behave properly in our case. + ;; We use our own abnormal hook and run it from PCH in a way that it is OK to + ;; modify it from within its own functions. + (unless embark--run-after-command-functions + (let (pch timer has-run) + (setq pch + (lambda () + (remove-hook 'post-command-hook pch) + (cancel-timer timer) + (unless has-run + (setq has-run t) + (while embark--run-after-command-functions + ;; The following funcall may recursively call + ;; `embark--run-after-command', modifying + ;; `embark--run-after-command-functions'. This is why this + ;; loop has to be implemented carefully. We have to pop the + ;; function off the hook before calling it. Using `dolist' + ;; on the hook would also be incorrect, because it wouldn't + ;; take modifications of this hook into account. + (with-demoted-errors "embark PCH: %S" + (condition-case nil + (funcall (pop embark--run-after-command-functions)) + (quit (message "Quit")))))))) + (add-hook 'post-command-hook pch 'append) + ;; Generally we prefer `post-command-hook' because it plays well with + ;; keyboard macros. In some cases, `post-command-hook' isn't run after + ;; exiting a recursive edit, so set up the following timer as a backup. + (setq timer (run-at-time 0 nil pch)))) + + ;; Keep the default-directory alive, since this is often overwritten, + ;; for example by Consult commands. + ;; TODO it might be necessary to add more dynamically bound variables + ;; here. What we actually want are functions `capture-dynamic-scope' + ;; and `eval-in-dynamic-scope', but this does not exist? + (let ((dir default-directory)) + (push (lambda () + (let ((default-directory dir)) + (apply fn args))) + embark--run-after-command-functions))) + +(defun embark--quit-and-run (fn &rest args) + "Quit the minibuffer and then call FN with ARGS. +If called outside the minibuffer, simply apply FN to ARGS." + (if (not (minibufferp)) + (apply fn args) + (apply #'embark--run-after-command fn args) + (embark--run-after-command #'set 'ring-bell-function ring-bell-function) + (setq ring-bell-function #'ignore) + (if (fboundp 'minibuffer-quit-recursive-edit) + (minibuffer-quit-recursive-edit) + (abort-recursive-edit)))) + +(defun embark--run-action-hooks (hooks action target quit) + "Run HOOKS for ACTION. +The HOOKS argument must be alist. The keys t and :always are +treated specially. The :always hooks are executed always and the +t hooks are the default hooks, for when there are no +command-specific hooks for ACTION. The QUIT, ACTION and TARGET +arguments are passed to the hooks as keyword arguments." + (mapc (lambda (h) (apply h :action action :quit quit target)) + (or (alist-get action hooks) + (alist-get t hooks))) + (mapc (lambda (h) (apply h :action action :quit quit target)) + (alist-get :always hooks))) + +(defun embark--run-around-action-hooks (action target quit) + "Run the `embark-around-action-hooks' for ACTION. +All the applicable around hooks are composed in the order they +are present in `embark-around-action-hooks'. The keys t and +:always in `embark-around-action-hooks' are treated specially. +The :always hooks are executed always (outermost) and the t hooks +are the default hooks, for when there are no command-specific +hooks for ACTION. The QUIT, ACTION and TARGET arguments are +passed to the hooks as keyword arguments." + (apply + (seq-reduce + (lambda (fn hook) + (lambda (&rest args) (apply hook (plist-put args :run fn)))) + (let ((hooks embark-around-action-hooks)) + (reverse + (append (or (alist-get action hooks) (alist-get t hooks)) + (alist-get :always hooks)))) + (lambda (&rest args) + (command-execute (plist-get args :action)))) + :action action :quit quit target)) + +(defun embark--act (action target &optional quit) + "Perform ACTION injecting the TARGET. +If called from a minibuffer with non-nil QUIT, quit the +minibuffer before executing the action." + (if (memq action '(embark-become ; these actions should run in + embark-collect ; the current buffer, not the + embark-live ; target buffer + embark-export + embark-select + embark-act-all)) + (progn + (embark--run-action-hooks embark-pre-action-hooks action target quit) + (unwind-protect (embark--run-around-action-hooks action target quit) + (embark--run-action-hooks embark-post-action-hooks + action target quit))) + (let* ((command embark--command) + (prefix prefix-arg) + (action-window (embark--target-window t)) + (directory default-directory) + (inject + (lambda () + (let ((contents (minibuffer-contents))) + (delete-minibuffer-contents) + (insert + (propertize + (substring-no-properties (plist-get target :target)) + 'embark--initial-input contents))) + (if (memq 'ivy--queue-exhibit post-command-hook) + ;; Ivy has special needs: (1) for file names + ;; ivy-immediate-done is not equivalent to + ;; exit-minibuffer, (2) it needs a chance to run + ;; its post command hook first, so use depth 10 + (add-hook 'post-command-hook 'ivy-immediate-done 10 t) + (add-hook 'post-command-hook #'exit-minibuffer nil t)) + (embark--run-action-hooks embark-target-injection-hooks + action target quit))) + (dedicate (and (derived-mode-p 'embark-collect-mode) + (not (window-dedicated-p)) + (selected-window))) + (multi (memq action embark-multitarget-actions)) + (run-action + (if (and (commandp action) (not multi)) + (lambda () + (let (final-window) + (when dedicate (set-window-dedicated-p dedicate t)) + (unwind-protect + (with-selected-window action-window + (let ((enable-recursive-minibuffers t) + (embark--command command) + (prefix-arg prefix) + ;; the next two avoid mouse dialogs + (use-dialog-box nil) + (last-nonmenu-event 13) + (default-directory directory)) + (embark--run-action-hooks embark-pre-action-hooks + action target quit) + (minibuffer-with-setup-hook inject + ;; pacify commands that use (this-command-keys) + (when (= (length (this-command-keys)) 0) + (set--this-command-keys + (if (characterp last-command-event) + (string last-command-event) + "\r"))) + (setq this-command action) + (embark--run-around-action-hooks + action target quit))) + (setq final-window (selected-window))) + (embark--run-action-hooks embark-post-action-hooks + action target quit) + (when dedicate (set-window-dedicated-p dedicate nil))) + (unless (eq final-window action-window) + (select-window final-window)))) + ;; TODO uniformize the command and non-interactive cases? + (let ((argument + (if multi + (or (plist-get target :candidates) ; embark-act-all + (list (plist-get target :target))) + (plist-get target :target)))) + (lambda () + (with-selected-window action-window + (embark--run-action-hooks embark-pre-action-hooks + action target quit) + (unwind-protect + (let ((current-prefix-arg prefix) + (default-directory directory)) + (funcall action argument)) + (embark--run-action-hooks embark-post-action-hooks + action target quit)))))))) + (setq prefix-arg nil) + (if quit (embark--quit-and-run run-action) (funcall run-action))))) + +(defun embark--refine-multi-category (_type target) + "Refine `multi-category' TARGET to its actual type." + (or (get-text-property 0 'multi-category target) + (cons 'general target))) + +(defun embark--simplify-path (_type target) + "Simplify and '//' or '~/' in the TARGET file path." + (cons 'file (substitute-in-file-name target))) + +(defun embark--keybinding-command (_type target) + "Treat an `embark-keybinding' TARGET as a command." + (when-let ((cmd (get-text-property 0 'embark-command target))) + (cons 'command (format "%s" cmd)))) + +(defun embark--lookup-lighter-minor-mode (_type target) + "If TARGET is a lighter, look up its minor mode. + +The `describe-minor-mode' command has as completion candidates +both minor-modes and their lighters. This function replaces the +lighters by their minor modes, so actions expecting a function +work on them." + (cons 'minor-mode + (let ((symbol (intern-soft target))) + (if (and symbol (boundp symbol)) + target + (symbol-name (lookup-minor-mode-from-indicator target)))))) + +(declare-function project-current "project") +(declare-function project-roots "project") +(declare-function project-root "project") + +(defun embark--project-file-full-path (_type target) + "Get full path of project file TARGET." + ;; TODO project-find-file can be called from outside all projects in + ;; which case it prompts for a project first; we don't support that + ;; case yet, since there is no current project. + (cons 'file + (if-let ((project (project-current)) + (root (if (fboundp 'project-root) + (project-root project) + (with-no-warnings + (car (project-roots project)))))) + (expand-file-name target root) + target))) + +(defun embark--remove-package-version (_type target) + "Remove version number from a versioned package TARGET." + (cons 'package (replace-regexp-in-string "-[0-9.]+$" "" target))) + +(defun embark--targets () + "Retrieve current targets. + +An initial guess at the current targets and their types is +determined by running the functions in `embark-target-finders'. +Each function should either return nil, a pair of a type symbol +and target string or a triple of a type symbol, target string and +target bounds. + +In the minibuffer only the first target finder returning non-nil +is taken into account. When finding targets at point in other +buffers, all target finder function is executed. + +For each target, the type is then looked up as a key in the +variable `embark-transformer-alist'. If there is a transformer +for the type, it is called with the type and target, and must +return a `cons' of the transformed type and transformed target. + +The return value of `embark--targets' is a list of plists. Each +plist concerns one target, and has keys `:type', `:target', +`:orig-type', `:orig-target' and `:bounds'." + (let (targets) + (run-hook-wrapped + 'embark-target-finders + (lambda (fun) + (dolist (found (when-let (result (funcall fun)) + (if (consp (car result)) result (list result)))) + (let* ((type (or (car found) 'general)) + (target+bounds (cdr found)) + (target (if (consp target+bounds) + (car target+bounds) + target+bounds)) + (bounds (and (consp target+bounds) (cdr target+bounds))) + (full-target + (append + (list :orig-type type :orig-target target :bounds bounds) + (if-let (transform (alist-get type embark-transformer-alist)) + (let ((trans (funcall transform type target))) + (list :type (car trans) :target (cdr trans))) + (list :type type :target target))))) + (push full-target targets))) + (and targets (minibufferp)))) + (cl-delete-duplicates + (nreverse targets) + :test (lambda (t1 t2) + (and (equal (plist-get t1 :target) (plist-get t2 :target)) + (eq (plist-get t1 :type) (plist-get t2 :type))))))) + +(defun embark--default-action (type) + "Return default action for the given TYPE of target. +The most common case is that the target comes from minibuffer +completion, in which case the default action is the command that +opened the minibuffer in the first place. This can be overridden +by `embark-default-action-overrides'. + +For targets that do not come from minibuffer completion +\(typically some thing at point in a regular buffer) and whose +type is not listed in `embark-default-action-overrides', the +default action is given by whatever binding RET has in the action +keymap for the given type." + (or (alist-get (cons type embark--command) embark-default-action-overrides + nil nil #'equal) + (alist-get type embark-default-action-overrides) + (alist-get t embark-default-action-overrides) + embark--command + (lookup-key (embark--raw-action-keymap type) "\r"))) + +(defun embark--rotate (list k) + "Rotate LIST by K elements and return the rotated list." + (setq k (mod k (length list))) + (append (seq-drop list k) (seq-take list k))) + +(defun embark--orig-target (target) + "Convert TARGET to original target." + (plist-put + (plist-put + (copy-sequence target) + :target (plist-get target :orig-target)) + :type (plist-get target :orig-type))) + +(defun embark--quit-p (action) + "Determine whether to quit the minibuffer after ACTION. +This function consults `embark-quit-after-action' to decide +whether or not the user wishes to quit the minibuffer after +performing the ACTION, assuming this is done from a minibuffer." + (let* ((cfg embark-quit-after-action) + (quit (if (consp cfg) (alist-get action cfg (alist-get t cfg)) cfg))) + (when embark--toggle-quit (setq quit (not quit))) + (setq embark--toggle-quit nil) + quit)) + +;;;###autoload +(defun embark-act (&optional arg) + "Prompt the user for an action and perform it. +The targets of the action are chosen by `embark-target-finders'. +By default, if called from a minibuffer the target is the top +completion candidate. When called from a non-minibuffer buffer +there can multiple targets and you can cycle among them by using +`embark-cycle' (which is bound by default to the same key +binding `embark-act' is, but see `embark-cycle-key'). + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument. + +If instead you call this from outside the minibuffer, the first +ARG targets are skipped over (if ARG is negative the skipping is +done by cycling backwards) and cycling starts from the following +target." + (interactive "P") + (let* ((targets (or (embark--targets) (user-error "No target found"))) + (indicators (mapcar #'funcall embark-indicators)) + (default-done nil)) + (when arg + (if (minibufferp) + (embark-toggle-quit) + (setq targets (embark--rotate targets (prefix-numeric-value arg))))) + (unwind-protect + (while + (let* ((target (car targets)) + (action + (or (embark--prompt + indicators + (let ((embark-default-action-overrides + (if default-done + `((t . ,default-done)) + embark-default-action-overrides))) + (embark--action-keymap (plist-get target :type) + (cdr targets))) + targets) + (user-error "Canceled"))) + (default-action (or default-done + (embark--default-action + (plist-get target :type))))) + (cond + ;; When acting twice in the minibuffer, do not restart + ;; `embark-act'. Otherwise the next `embark-act' will + ;; find a target in the original buffer. + ((eq action #'embark-act) + (message "Press an action key")) + ((eq action #'embark-cycle) + (setq targets (embark--rotate + targets (prefix-numeric-value prefix-arg)))) + (t + ;; if the action is non-repeatable, cleanup indicator now + (let ((repeat (embark--action-repeatable-p action))) + (unless repeat (mapc #'funcall indicators)) + (condition-case err + (embark--act + action + (if (and (eq action default-action) + (eq action embark--command) + (not (memq action embark-multitarget-actions))) + (embark--orig-target target) + target) + (embark--quit-p action)) + (user-error + (funcall (if repeat #'message #'user-error) + "%s" (cadr err)))) + (when-let (new-targets (and repeat (embark--targets))) + ;; Terminate repeated prompter on default action, + ;; when repeating. Jump to the region type if the + ;; region is active after the action, or else to the + ;; current type again. + (setq default-done #'embark-done + targets + (embark--rotate + new-targets + (or (cl-position-if + (let ((desired-type + (if (eq repeat t) + (plist-get (car targets) :type) + repeat))) + (lambda (x) + (eq (plist-get x :type) desired-type))) + new-targets) + 0))))))))) + (mapc #'funcall indicators)))) + +(defun embark--maybe-transform-candidates () + "Collect candidates and see if they all transform to the same type. +Return a plist with keys `:type', `:orig-type', `:candidates', and +`:orig-candidates'." + (pcase-let* ((`(,type . ,candidates) + (run-hook-with-args-until-success 'embark-candidate-collectors)) + (bounds (mapcar #'cdr-safe candidates))) + (setq candidates + (mapcar (lambda (x) (if (consp x) (car x) x)) candidates)) + (when (eq type 'file) + (let ((dir (embark--default-directory))) + (setq candidates + (mapcar (lambda (cand) + (abbreviate-file-name (expand-file-name cand dir))) + candidates)))) + (append + (list :orig-type type :orig-candidates candidates :bounds bounds) + (or (when candidates + (when-let ((transformer (alist-get type embark-transformer-alist))) + (pcase-let* ((`(,new-type . ,first-cand) + (funcall transformer type (car candidates)))) + (let ((new-candidates (list first-cand))) + (when (cl-every + (lambda (cand) + (pcase-let ((`(,t-type . ,t-cand) + (funcall transformer type cand))) + (when (eq t-type new-type) + (push t-cand new-candidates) + t))) + (cdr candidates)) + (list :type new-type + :candidates (nreverse new-candidates))))))) + (list :type type :candidates candidates))))) + +;;;###autoload +(defun embark-act-all (&optional arg) + "Prompt the user for an action and perform it on each candidate. +The candidates are chosen by `embark-candidate-collectors'. +By default, if called from a minibuffer the candidates are the +completion candidates. + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument." + (interactive "P") + (let* ((transformed (embark--maybe-transform-candidates)) + (type (plist-get transformed :type)) + (orig-type (plist-get transformed :orig-type)) + (candidates + (or (cl-mapcar + (lambda (cand orig-cand bounds) + (list :type type :target cand + :bounds (when bounds + (cons (copy-marker (car bounds)) + (copy-marker (cdr bounds)))) + :orig-type orig-type :orig-target orig-cand)) + (plist-get transformed :candidates) + (plist-get transformed :orig-candidates) + (plist-get transformed :bounds)) + (user-error "No candidates to act on"))) + (indicators (mapcar #'funcall embark-indicators))) + (when arg (embark-toggle-quit)) + (unwind-protect + (let* ((action + (or (embark--prompt + indicators (embark--action-keymap type nil) + (list (list :type type :multi (length candidates)))) + (user-error "Canceled"))) + (prefix prefix-arg) + (act (lambda (candidate) + (cl-letf (((symbol-function 'embark--restart) #'ignore) + ((symbol-function 'embark--confirm) #'ignore)) + (let ((prefix-arg prefix)) + (embark--act action candidate))))) + (quit (embark--quit-p action))) + (when (and (eq action (embark--default-action type)) + (eq action embark--command)) + (setq candidates (mapcar #'embark--orig-target candidates))) + (when (or (not (or embark-confirm-act-all + (memq 'embark--confirm + (alist-get action embark-pre-action-hooks)))) + (y-or-n-p (format "Run %s on %d %ss? " + action (length candidates) type))) + (if (memq action embark-multitarget-actions) + (let ((prefix-arg prefix)) + (embark--act action transformed quit)) + (if quit + (embark--quit-and-run #'mapc act candidates) + (mapc act candidates) + (when (memq 'embark--restart + (alist-get action embark-post-action-hooks)) + (embark--restart)))))) + (dolist (cand candidates) + (when-let ((bounds (plist-get cand :bounds))) + (set-marker (car bounds) nil) ; yay, manual memory management! + (set-marker (cdr bounds) nil))) + (setq prefix-arg nil) + (mapc #'funcall indicators)))) + +(defun embark-highlight-indicator () + "Action indicator highlighting the target at point." + (let (overlay) + (lambda (&optional keymap targets _prefix) + (let ((bounds (plist-get (car targets) :bounds))) + (when (and overlay (or (not keymap) (not bounds))) + (delete-overlay overlay) + (setq overlay nil)) + (when bounds + (if overlay + (move-overlay overlay (car bounds) (cdr bounds)) + (setq overlay (make-overlay (car bounds) (cdr bounds)))) + (overlay-put overlay 'face 'embark-target) + (overlay-put overlay 'window (selected-window)) + ;; high priority to override both bug reference and the lazy + ;; isearch highlights in embark-isearch-highlight-indicator + (overlay-put overlay 'priority 1001)))))) + +(defun embark-isearch-highlight-indicator () + "Action indicator highlighting all occurrences of the identifier at point. +This indicator only does something for targets which are +identifiers or symbols. For those it uses `isearch''s lazy +highlighting feature to highlight all occurrences of the target in +the buffer. This indicator is best used in conjunction with +`embark-highlight-indicator': by using them both you get the +target and the other occurrences of it highlighted in different +colors." + (lambda (&optional _keymap targets _prefix) + (if (and (not (minibufferp)) + (memq (plist-get (car targets) :orig-type) '(symbol identifier))) + (let ((isearch-string (plist-get (car targets) :target)) + (isearch-regexp-function #'isearch-symbol-regexp)) + (isearch-lazy-highlight-new-loop)) + (setq isearch-lazy-highlight-last-string nil) + (lazy-highlight-cleanup t)))) + +(defun embark-cycle (_arg) + "Cycle over the next ARG targets at point. +If ARG is negative, cycle backwards." + (interactive "p") + (user-error "Not meant to be called directly")) + +(defun embark-done () + "Terminate sequence of repeated actions." + (interactive)) + +;;;###autoload +(defun embark-dwim (&optional arg) + "Run the default action on the current target. +The target of the action is chosen by `embark-target-finders'. + +If the target comes from minibuffer completion, then the default +action is the command that opened the minibuffer in the first +place, unless overridden by `embark-default-action-overrides'. + +For targets that do not come from minibuffer completion +\(typically some thing at point in a regular buffer) and whose +type is not listed in `embark-default-action-overrides', the +default action is given by whatever binding RET has in the action +keymap for the target's type. + +See `embark-act' for the meaning of the prefix ARG." + (interactive "P") + (if-let ((targets (embark--targets))) + (let* ((target + (or (nth + (if (or (null arg) (minibufferp)) + 0 + (mod (prefix-numeric-value arg) (length targets))) + targets))) + (type (plist-get target :type)) + (default-action (embark--default-action type)) + (action (or (command-remapping default-action) default-action))) + (unless action + (user-error "No default action for %s targets" type)) + (when (and arg (minibufferp)) (setq embark--toggle-quit t)) + (embark--act action + (if (and (eq default-action embark--command) + (not (memq default-action + embark-multitarget-actions))) + (embark--orig-target target) + target) + (embark--quit-p action))) + (user-error "No target found"))) + +(defun embark--become-keymap () + "Return keymap of commands to become for current command." + (let ((map (make-composed-keymap + (cl-loop for keymap-name in embark-become-keymaps + for keymap = (symbol-value keymap-name) + when (where-is-internal embark--command (list keymap)) + collect keymap)))) + (when embark-help-key + (keymap-set map embark-help-key #'embark-keymap-help)) + map)) + +;;;###autoload +(defun embark-become (&optional full) + "Make current command become a different command. +Take the current minibuffer input as initial input for new +command. The new command can be run normally using key bindings or +\\[execute-extended-command], but if the current command is found in a keymap in +`embark-become-keymaps', that keymap is activated to provide +convenient access to the other commands in it. + +If FULL is non-nil (interactively, if called with a prefix +argument), the entire minibuffer contents are used as the initial +input of the new command. By default only the part of the +minibuffer contents between the current completion boundaries is +taken. What this means is fairly technical, but (1) usually +there is no difference: the completion boundaries include the +entire minibuffer contents, and (2) the most common case where +these notions differ is file completion, in which case the +completion boundaries single out the path component containing +point." + (interactive "P") + (unless (minibufferp) + (user-error "Not in a minibuffer")) + (let* ((target (if full + (minibuffer-contents) + (pcase-let ((`(,beg . ,end) (embark--boundaries))) + (substring (minibuffer-contents) beg + (+ end (embark--minibuffer-point)))))) + (keymap (embark--become-keymap)) + (targets `((:type embark-become :target ,target))) + (indicators (mapcar #'funcall embark-indicators)) + (become (unwind-protect + (embark--prompt indicators keymap targets) + (mapc #'funcall indicators)))) + (unless become + (user-error "Canceled")) + (embark--become-command become target))) + +(defun embark--become-command (command input) + "Quit current minibuffer and start COMMAND with INPUT." + (embark--quit-and-run + (lambda () + (minibuffer-with-setup-hook + (lambda () + (delete-minibuffer-contents) + (insert input)) + (let ((use-dialog-box nil) ;; avoid mouse dialogs + (last-nonmenu-event 13)) + (setq this-command command) + (command-execute command)))))) + +(defmacro embark-define-keymap (&rest _) + "Obsolete macro, use `defvar-keymap' instead." + (error "`embark-define-keymap' has been deprecated in Embark 0.21. +Use standard methods for defining keymaps, such as `defvar-keymap'. +Remember to make `embark-general-map' the parent if appropriate")) + +;;; Embark collect + +(defgroup embark-collect nil + "Buffers for acting on collected Embark targets." + :group 'embark) + +(defcustom embark-candidate-collectors + '(embark-selected-candidates + embark-minibuffer-candidates + embark-completions-buffer-candidates + embark-dired-candidates + embark-ibuffer-candidates + embark-embark-collect-candidates + embark-custom-candidates) + "List of functions that collect all candidates in a given context. +These are used to fill an Embark Collect buffer. Each function +should return either nil (to indicate it found no candidates) or +a list whose first element is a symbol indicating the type of +candidates and whose `cdr' is the list of candidates, each of +which should be either a string or a dotted list of the +form (TARGET START . END), where START and END are the buffer +positions bounding the TARGET string." + :type 'hook) + +(defcustom embark-exporters-alist + '((buffer . embark-export-ibuffer) + (file . embark-export-dired) + (package . embark-export-list-packages) + (bookmark . embark-export-bookmarks) + (variable . embark-export-customize-variable) + (face . embark-export-customize-face) + (symbol . embark-export-apropos) + (minor-mode . embark-export-apropos) + (function . embark-export-apropos) + (command . embark-export-apropos) + (t . embark-collect)) + "Alist associating completion types to export functions. +Each function should take a list of strings which are candidates +for actions and make a buffer appropriate to manage them. For +example, the default is to make a Dired buffer for files, and an +ibuffer for buffers. + +The key t is also allowed in the alist, and the corresponding +value indicates the default function to use for other types. The +default is `embark-collect'" + :type '(alist :key-type symbol :value-type function)) + +(defcustom embark-after-export-hook nil + "Hook run after `embark-export' in the newly created buffer." + :type 'hook) + +(defface embark-collect-candidate '((t :inherit default)) + "Face for candidates in Embark Collect buffers.") + +(defface embark-collect-group-title + '((t :inherit shadow :slant italic)) + "Face for group titles in Embark Collect buffers.") + +(defface embark-collect-group-separator + '((t :inherit shadow :strike-through t italic)) + "Face for group titles in Embark Collect buffers.") + +(defcustom embark-collect-group-format + (concat + (propertize " " 'face 'embark-collect-group-separator) + (propertize " %s " 'face 'embark-collect-group-title) + (propertize " " 'face 'completions-group-separator + 'display '(space :align-to right))) + "Format string used for the group title in Embark Collect buffers." + :type 'string) + +(defface embark-collect-annotation '((t :inherit completions-annotations)) + "Face for annotations in Embark Collect. +This is only used for annotation that are not already fontified.") + +(defvar-local embark--rerun-function nil + "Function to rerun the collect or export that made the current buffer.") + +(autoload 'package-delete "package") +(declare-function package--from-builtin "package") +(declare-function package-desc-extras "package") +(declare-function package-desc-name "package") +(defvar package--builtins) +(defvar package-alist) +(defvar package-archive-contents) +(defvar package--initialized) + +(defun embark--package-desc (pkg) + "Return the description structure for package PKG." + (or ; found this in `describe-package-1' + (car (alist-get pkg package-alist)) + (if-let ((built-in (assq pkg package--builtins))) + (package--from-builtin built-in) + (car (alist-get pkg package-archive-contents))))) + +(defun embark-minibuffer-candidates () + "Return all current completion candidates from the minibuffer." + (when (minibufferp) + (let* ((all (completion-all-completions + (minibuffer-contents) + minibuffer-completion-table + minibuffer-completion-predicate + (embark--minibuffer-point))) + (last (last all))) + (when last (setcdr last nil)) + (cons + (completion-metadata-get (embark--metadata) 'category) + all)))) + +(defun embark-sorted-minibuffer-candidates () + "Return a sorted list of current minibuffer completion candidates. +This using the same sort order that `icomplete' and +`minibuffer-force-complete' use. The intended usage is that you +replace `embark-minibuffer-candidates' with this function in the +list `embark-candidate-collectors'." + (when (minibufferp) + (cons + (completion-metadata-get (embark--metadata) 'category) + (nconc (cl-copy-list (completion-all-sorted-completions)) nil)))) + +(declare-function dired-get-marked-files "dired") +(declare-function dired-move-to-filename "dired") +(declare-function dired-move-to-end-of-filename "dired") + +(defun embark-dired-candidates () + "Return marked or all files shown in Dired buffer. +If any buffer is marked, return marked buffers; otherwise, return +all buffers." + (when (derived-mode-p 'dired-mode) + (cons 'file + (or + ;; dired-get-marked-files returns the file on the current + ;; line if no marked files are found; and when the fourth + ;; argument is non-nil, the "no marked files" case is + ;; distinguished from the "single marked file" case by + ;; returning (list t marked-file) in the latter + (let ((marked (dired-get-marked-files t nil nil t))) + (and (cdr marked) + (if (eq (car marked) t) (cdr marked) marked))) + (save-excursion + (goto-char (point-min)) + (let (files) + (while (not (eobp)) + (when-let (file (dired-get-filename t t)) + (push `(,file + ,(progn (dired-move-to-filename) (point)) + . ,(progn (dired-move-to-end-of-filename t) (point))) + files)) + (forward-line)) + (nreverse files))))))) + +(autoload 'ibuffer-marked-buffer-names "ibuffer") +(declare-function ibuffer-map-lines-nomodify "ibuffer") + +(defun embark-ibuffer-candidates () + "Return marked or all buffers listed in ibuffer buffer. +If any buffer is marked, return marked buffers; otherwise, return +all buffers." + (when (derived-mode-p 'ibuffer-mode) + (cons 'buffer + (or (ibuffer-marked-buffer-names) + (let (buffers) + (ibuffer-map-lines-nomodify + (lambda (buffer _mark) + (push (buffer-name buffer) buffers))) + (nreverse buffers)))))) + +(defun embark-embark-collect-candidates () + "Return candidates in Embark Collect buffer. +This makes `embark-export' work in Embark Collect buffers." + (when (derived-mode-p 'embark-collect-mode) + (cons embark--type + (save-excursion + (goto-char (point-min)) + (let (all) + (push (cdr (embark-target-collect-candidate)) all) + (while (forward-button 1 nil nil t) + (push (cdr (embark-target-collect-candidate)) all)) + (nreverse all)))))) + +(defun embark-completions-buffer-candidates () + "Return all candidates in a completions buffer." + (when (derived-mode-p 'completion-list-mode) + (cons + embark--type + (save-excursion + (goto-char (point-min)) + (next-completion 1) + (let (all) + (while (not (eobp)) + (push (cdr (embark-target-completion-at-point)) all) + (next-completion 1)) + (nreverse all)))))) + +(defun embark-custom-candidates () + "Return all variables and faces listed in this `Custom-mode' buffer." + (when (derived-mode-p 'Custom-mode) + (cons 'symbol ; gets refined to variable or face when acted upon + (save-excursion + (goto-char (point-min)) + (let (symbols) + (while (not (eobp)) + (when-let (widget (widget-at (point))) + (when (eq (car widget) 'custom-visibility) + (push + `(,(symbol-name + (plist-get (cdr (plist-get (cdr widget) :parent)) + :value)) + ,(point) + . ,(progn + (re-search-forward ":" (line-end-position) 'noerror) + (point))) + symbols))) + (forward-line)) + (nreverse symbols)))))) + + +(defun embark-collect--target () + "Return the Embark Collect candidate at point. +This takes into account `embark-transformer-alist'." + (let ((embark-target-finders '(embark-target-collect-candidate))) + (car (embark--targets)))) + +(defun embark--action-command (action) + "Turn an ACTION into a command to perform the action. +Returns the name of the command." + (let ((name (intern (format "embark-action--%s" + (embark--command-name action))))) + (fset name (lambda (arg) + (interactive "P") + (when-let (target (embark-collect--target)) + (let ((prefix-arg arg)) + (embark--act action target))))) + (when (fboundp action) + (put name 'function-documentation (documentation action))) + name)) + +(defun embark--all-bindings (keymap &optional nested) + "Return an alist of all bindings in KEYMAP. +If NESTED is non-nil subkeymaps are not flattened." + (let (bindings maps) + (map-keymap + (lambda (key def) + (cond + ((keymapp def) + (if nested + (push (cons (vector key) def) maps) + (dolist (bind (embark--all-bindings def)) + (push (cons (vconcat (vector key) (car bind)) (cdr bind)) + maps)))) + (def (push (cons (vector key) def) bindings)))) + (keymap-canonicalize keymap)) + (nconc (nreverse bindings) (nreverse maps)))) + +(defun embark-collect--direct-action-map (type) + "Return a direct action keymap for targets of given TYPE." + (let* ((actions (embark--action-keymap type nil)) + (map (make-sparse-keymap))) + (set-keymap-parent map button-map) + (pcase-dolist (`(,key . ,cmd) (embark--all-bindings actions)) + (unless (or (equal key [13]) + (memq cmd '(digit-argument negative-argument))) + (define-key map key (if (eq cmd 'embark-keymap-help) + #'embark-bindings-at-point + (embark--action-command cmd))))) + map)) + +(define-minor-mode embark-collect-direct-action-minor-mode + "Bind type-specific actions directly (without need for `embark-act')." + :init-value nil + :lighter " Act" + (unless (derived-mode-p 'embark-collect-mode) + (user-error "Not in an Embark Collect buffer")) + (save-excursion + (goto-char (point-min)) + (let ((inhibit-read-only t) maps) + (while (progn + (when (tabulated-list-get-id) + (put-text-property + (point) (button-end (point)) 'keymap + (if embark-collect-direct-action-minor-mode + (when-let ((target (embark-collect--target)) + (type (plist-get target :type))) + (or (alist-get type maps) + (setf (alist-get type maps) + (embark-collect--direct-action-map type))))))) + (forward-button 1 nil nil t)))))) + +(define-button-type 'embark-collect-entry + 'face 'embark-collect-candidate + 'action 'embark-collect-choose) + +(declare-function outline-toggle-children "outline") +(define-button-type 'embark-collect-group + 'face 'embark-collect-group-title + 'action (lambda (_) (outline-toggle-children))) + +(defun embark--boundaries () + "Get current minibuffer completion boundaries." + (let ((contents (minibuffer-contents)) + (pt (embark--minibuffer-point))) + (completion-boundaries + (substring contents 0 pt) + minibuffer-completion-table + minibuffer-completion-predicate + (substring contents pt)))) + +(defun embark-collect-choose (entry) + "Run default action on Embark Collect ENTRY." + (pcase-let ((`(,type ,text ,start . ,end) + (save-excursion + (goto-char entry) + (embark-target-collect-candidate)))) + (embark--act (embark--default-action type) + (list :target text + :type type + :bounds (cons start end))))) + +(defvar-keymap embark-collect-mode-map + :doc "Keymap for Embark collect mode." + :parent tabulated-list-mode-map + "a" #'embark-act + "A" #'embark-act-all + "M-a" #'embark-collect-direct-action-minor-mode + "E" #'embark-export + "s" #'isearch-forward + "n" #'forward-button + "p" #'backward-button + "}" 'outline-next-heading + "{" 'outline-previous-heading + "<remap> <forward-paragraph>" 'outline-next-heading + "<remap> <backward-paragraph>" 'outline-previous-heading + "<remap> <revert-buffer>" #'embark-rerun-collect-or-export) + +(defconst embark-collect--outline-string (string #x210000) + "Special string used for outline headings in Embark Collect buffers. +Chosen to be extremely unlikely to appear in a candidate.") + +(define-derived-mode embark-collect-mode tabulated-list-mode "Embark Collect" + "List of candidates to be acted on. +The command `embark-act' is bound `embark-collect-mode-map', but +you might prefer to change the key binding to match your other +key binding for it. Or alternatively you might want to enable the +embark collect direct action minor mode by adding the function +`embark-collect-direct-action-minor-mode' to +`embark-collect-mode-hook'. + +Reverting an Embark Collect buffer has slightly unusual behavior +if the buffer was obtained by running `embark-collect' from +within a minibuffer completion session. In that case reverting +just restarts the completion session, that is, the command that +opened the minibuffer is run again and the minibuffer contents +restored. You can then interact normally with the command, +perhaps editing the minibuffer contents, and, if you wish, you +can rerun `embark-collect' to get an updated buffer." + :interactive nil :abbrev-table nil :syntax-table nil) + +(defun embark-collect--metadatum (type metadatum) + "Get METADATUM for current buffer's candidates. +For non-minibuffers, assume candidates are of given TYPE." + (if (minibufferp) + (or (completion-metadata-get (embark--metadata) metadatum) + (plist-get completion-extra-properties + (intern (format ":%s" metadatum)))) + ;; otherwise fake some metadata for Marginalia users's benefit + (completion-metadata-get `((category . ,type)) metadatum))) + +(defun embark-collect--affixator (type) + "Get affixation function for current buffer's candidates. +For non-minibuffers, assume candidates are of given TYPE." + (or (embark-collect--metadatum type 'affixation-function) + (let ((annotator + (or (embark-collect--metadatum type 'annotation-function) + (lambda (_) "")))) + (lambda (candidates) + (mapcar (lambda (c) + (if-let (a (funcall annotator c)) (list c "" a) c)) + candidates))))) + +(defun embark--for-display (string) + "Return visibly equivalent STRING without display and invisible properties." + (let ((len (length string)) (pos 0) chunks) + (while (/= pos len) + (let ((dis (next-single-property-change pos 'display string len)) + (display (get-text-property pos 'display string))) + (if (stringp display) + (progn (push display chunks) (setq pos dis)) + (while (/= pos dis) + (let ((inv (next-single-property-change pos 'invisible string dis))) + (unless (get-text-property pos 'invisible string) + (unless (and (= pos 0) (= inv len)) + ;; avoid allocation for full string + (push (substring string pos inv) chunks))) + (setq pos inv)))))) + (if chunks (apply #'concat (nreverse chunks)) string))) + +(defun embark-collect--format-entries (candidates grouper) + "Format CANDIDATES for `tabulated-list-mode' grouped by GROUPER. +The GROUPER is either nil or a function like the `group-function' +completion metadatum, that is, a function of two arguments, the +first of which is a candidate and the second controls what is +computed: if nil, the title of the group the candidate belongs +to, and if non-nil, a rewriting of the candidate (useful to +simplify the candidate so it doesn't repeat the group title, for +example)." + (let ((max-width 0) + (transform + (if grouper (lambda (cand) (funcall grouper cand t)) #'identity))) + (setq tabulated-list-entries + (mapcan + (lambda (group) + (cons + `(nil [(,(concat (propertize embark-collect--outline-string + 'invisible t) + (format embark-collect-group-format (car group))) + type embark-collect-group) + ("" skip t)]) + (mapcar + (pcase-lambda (`(,cand ,prefix ,annotation)) + (let* ((display (embark--for-display (funcall transform cand))) + (length (length annotation)) + (faces (text-property-not-all + 0 length 'face nil annotation))) + (setq max-width (max max-width (+ (string-width prefix) + (string-width display)))) + (when faces + (add-face-text-property 0 length 'default t annotation)) + `(,cand + [(,(propertize display 'line-prefix prefix) + type embark-collect-entry) + (,annotation + skip t + ,@(unless faces + '(face embark-collect-annotation)))]))) + (cdr group)))) + (if grouper + (seq-group-by (lambda (item) (funcall grouper (car item) nil)) + candidates) + (list (cons "" candidates))))) + (if (null grouper) + (pop tabulated-list-entries) + (setq-local outline-regexp embark-collect--outline-string) + (outline-minor-mode)) + (setq tabulated-list-format + `[("Candidate" ,max-width t) ("Annotation" 0 t)]))) + +(defun embark-collect--update-candidates (buffer) + "Update candidates for Embark Collect BUFFER." + (let* ((transformed (embark--maybe-transform-candidates)) + (type (plist-get transformed :orig-type)) ; we need the originals for + (candidates (plist-get transformed :orig-candidates)) ; default action + (affixator (embark-collect--affixator type)) + (grouper (embark-collect--metadatum type 'group-function))) + (when (eq type 'file) + (let ((dir (buffer-local-value 'default-directory buffer))) + (setq candidates + (mapcar (lambda (cand) + (let ((rel (file-relative-name cand dir))) + (if (string-prefix-p "../" rel) cand rel))) + candidates)))) + (setq candidates (funcall affixator candidates)) + (with-current-buffer buffer + (setq embark--type type) + (embark-collect--format-entries candidates grouper)) + candidates)) + +(defun embark--collect (buffer-name) + "Create an Embark Collect buffer named BUFFER-NAME. + +The function `generate-new-buffer-name' is used to ensure the +buffer has a unique name." + (let ((buffer (generate-new-buffer buffer-name)) + (rerun (embark--rerun-function #'embark-collect))) + (with-current-buffer buffer + ;; we'll run the mode hooks once the buffer is displayed, so + ;; the hooks can make use of the window + (delay-mode-hooks (embark-collect-mode))) + + (embark--cache-info buffer) + (unless (embark-collect--update-candidates buffer) + (user-error "No candidates to collect")) + + (with-current-buffer buffer + (setq tabulated-list-use-header-line nil ; default to no header + header-line-format nil + tabulated-list--header-string nil) + (setq embark--rerun-function rerun)) + + (let ((window (display-buffer buffer))) + (with-selected-window window + (run-mode-hooks) + (tabulated-list-revert)) + (set-window-dedicated-p window t) + buffer))) + +(defun embark--descriptive-buffer-name (type) + "Return a descriptive name for an Embark collect or export buffer. +TYPE should be either `collect' or `export'." + (format "*Embark %s: %s*" + (capitalize (symbol-name type)) + (if (minibufferp) + (format "%s - %s" embark--command + (minibuffer-contents-no-properties)) + (buffer-name)))) + +;;;###autoload +(defun embark-collect () + "Create an Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Collect\". + +In Embark Collect buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-collect' +from within a minibuffer completion session. In that case +rerunning just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally with +the command, perhaps editing the minibuffer contents, and, if you +wish, you can rerun `embark-collect' to get an updated buffer." + (interactive) + (let ((buffer (embark--collect (embark--descriptive-buffer-name 'collect)))) + (when (minibufferp) + (embark--run-after-command #'pop-to-buffer buffer) + (embark--quit-and-run #'message nil)))) + +;;;###autoload +(defun embark-live () + "Create a live-updating Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Live\"." + (interactive) + (let ((live-buffer (embark--collect + (format "*Embark Live: %s*" + (if (minibufferp) + (format "M-x %s" embark--command) + (buffer-name))))) + (run-collect (make-symbol "run-collect")) + (stop-collect (make-symbol "stop-collect")) + timer) + (setf (symbol-function stop-collect) + (lambda () + (remove-hook 'change-major-mode-hook stop-collect t) + (remove-hook 'after-change-functions run-collect t))) + (setf (symbol-function run-collect) + (lambda (_1 _2 _3) + (unless timer + (setq timer + (run-with-idle-timer + 0.05 nil + (lambda () + (if (not (buffer-live-p live-buffer)) + (funcall stop-collect) + (embark-collect--update-candidates live-buffer) + (with-current-buffer live-buffer + ;; TODO figure out why I can't restore point + (tabulated-list-print t t)) + (setq timer nil)))))))) + (add-hook 'after-change-functions run-collect nil t) + (when (minibufferp) + (add-hook 'change-major-mode-hook stop-collect nil t)))) + +(defun embark--rerun-function (kind) + "Return a rerun function for an export or collect buffer in this context. +The parameter KIND should be either `embark-export' or `embark-collect'." + (let ((buffer (or embark--target-buffer (embark--target-buffer))) + (command embark--command)) + (cl-flet ((rerunner (action) + (lambda (&rest _) + (quit-window 'kill-buffer) + (with-current-buffer + (if (buffer-live-p buffer) buffer (current-buffer)) + (let ((embark--command command)) + (funcall action)))))) + (if (minibufferp) + (rerunner + (let ((input (minibuffer-contents-no-properties))) + (lambda () + (minibuffer-with-setup-hook + (lambda () + (delete-minibuffer-contents) + (insert input)) + (setq this-command embark--command) + (command-execute embark--command))))) + (rerunner kind))))) + +(defun embark-rerun-collect-or-export () + "Rerun the `embark-collect' or `embark-export' that created this buffer." + (interactive) + (if embark--rerun-function + (funcall embark--rerun-function) + (user-error "No function to rerun collect or export found"))) + +;;;###autoload +(defun embark-export () + "Create a type-specific buffer to manage current candidates. +The variable `embark-exporters-alist' controls how to make the +buffer for each type of completion. + +In Embark Export buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-export' +from within a minibuffer completion session. In that case +reverting just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally +with the command, perhaps editing the minibuffer contents, and, +if you wish, you can rerun `embark-export' to get an updated +buffer." + (interactive) + (let* ((transformed (embark--maybe-transform-candidates)) + (candidates (or (plist-get transformed :candidates) + (user-error "No candidates for export"))) + (type (plist-get transformed :type))) + (let ((exporter (or (alist-get type embark-exporters-alist) + (alist-get t embark-exporters-alist)))) + (if (eq exporter 'embark-collect) + (embark-collect) + (let* ((after embark-after-export-hook) + (cmd embark--command) + (name (embark--descriptive-buffer-name 'export)) + (rerun (embark--rerun-function #'embark-export)) + (buffer (save-excursion + (funcall exporter candidates) + (rename-buffer name t) + (current-buffer)))) + (embark--quit-and-run + (lambda () + (pop-to-buffer buffer) + (setq embark--rerun-function rerun) + (use-local-map + (make-composed-keymap + '(keymap + (remap keymap + (revert-buffer . embark-rerun-collect-or-export))) + (current-local-map))) + (let ((embark-after-export-hook after) + (embark--command cmd)) + (run-hooks 'embark-after-export-hook))))))))) + +(defmacro embark--export-rename (buffer title &rest body) + "Run BODY and rename BUFFER to Embark export buffer with TITLE." + (declare (indent 2)) + (let ((saved (make-symbol "saved"))) + `(let ((,saved (embark-rename-buffer + ,buffer " *Embark Saved*" t))) + ,@body + (set-buffer (embark-rename-buffer + ,buffer ,(format "*Embark Export %s*" title) t)) + (when ,saved (embark-rename-buffer ,saved ,buffer))))) + +(defun embark--export-customize (items type pred) + "Create a customization buffer listing ITEMS. +TYPE is the items type. +PRED is a predicate function used to filter the items." + (custom-buffer-create + (cl-loop for item in items + for sym = (intern-soft item) + when (and sym (funcall pred sym)) collect `(,sym ,type)))) + +(autoload 'apropos-parse-pattern "apropos") +(autoload 'apropos-symbols-internal "apropos") +(defun embark-export-apropos (symbols) + "Create apropos buffer listing SYMBOLS." + (embark--export-rename "*Apropos*" "Apropos" + (apropos-parse-pattern "") ;; Initialize apropos pattern + ;; HACK: Ensure that order of exported symbols is kept. + (cl-letf (((symbol-function #'sort) (lambda (list _pred) (nreverse list)))) + (apropos-symbols-internal + (delq nil (mapcar #'intern-soft symbols)) + (bound-and-true-p apropos-do-all))))) + +(defun embark-export-customize-face (faces) + "Create a customization buffer listing FACES." + (embark--export-customize faces 'custom-face #'facep)) + +(defun embark-export-customize-variable (variables) + "Create a customization buffer listing VARIABLES." + ;; The widget library serializes/deserializes the values. We advise + ;; the serialization in order to avoid errors for nonserializable + ;; variables. + (cl-letf* ((ht (make-hash-table :test #'equal)) + (orig-read (symbol-function #'read)) + (orig-write (symbol-function 'widget-sexp-value-to-internal)) + ((symbol-function #'read) + (lambda (&optional str) + (condition-case nil + (funcall orig-read str) + (error (gethash str ht))))) + ((symbol-function 'widget-sexp-value-to-internal) + (lambda (widget val) + (let ((str (funcall orig-write widget val))) + (puthash str val ht) + str)))) + (embark--export-customize variables 'custom-variable #'boundp))) + +(defun embark-export-ibuffer (buffers) + "Create an ibuffer buffer listing BUFFERS." + (ibuffer t "*Embark Export Ibuffer*" + `((predicate . (member (buffer-name) ',buffers))))) + +(autoload 'dired-check-switches "dired") +(declare-function dired-unadvertise "dired") +(defvar dired-directory) + +(defun embark-export-dired (files) + "Create a Dired buffer listing FILES." + (setq files (mapcar #'directory-file-name + (cl-remove-if-not #'file-exists-p files))) + (when (dired-check-switches dired-listing-switches "A" "almost-all") + (setq files (cl-remove-if + (lambda (path) + (let ((file (file-name-nondirectory path))) + (or (string= file ".") (string= file "..")))) + files))) + (let* ((dir (or (file-name-directory (try-completion "" files)) "")) + (buf (dired-noselect + (cons (expand-file-name dir) + (mapcar (lambda (file) (string-remove-prefix dir file)) + files))))) + ;; unadvertise this buffer to avoid reuse + (with-current-buffer buf + (dired-unadvertise (car dired-directory)) ; avoid reuse of this buffer + (rename-buffer (format "*Embark Export Dired %s*" default-directory))) + (pop-to-buffer buf))) + +(autoload 'package-menu-mode "package") +(autoload 'package-menu--generate "package") + +(defun embark-export-list-packages (packages) + "Create a package menu mode buffer listing PACKAGES." + (let ((buf (generate-new-buffer "*Embark Export Packages*"))) + (with-current-buffer buf + (package-menu-mode) + (package-menu--generate nil (mapcar #'intern packages))) + (pop-to-buffer buf))) + +(defvar bookmark-alist) + +(defun embark-export-bookmarks (bookmarks) + "Create a `bookmark-bmenu-mode' buffer listing BOOKMARKS." + (embark--export-rename "*Bookmark List*" "Bookmarks" + (let ((bookmark-alist + (cl-remove-if-not + (lambda (bmark) + (member (car bmark) bookmarks)) + bookmark-alist))) + (bookmark-bmenu-list)))) + +;;; Multiple target selection + +(defface embark-selected '((t (:inherit match))) + "Face for selected candidates.") + +(defvar-local embark--selection nil + "Buffer local list of selected targets. +Add or remove elements to this list using the `embark-select' +action.") + +(cl-defun embark--select + (&key orig-target orig-type bounds &allow-other-keys) + "Add or remove ORIG-TARGET of given ORIG-TYPE to the selection. +If BOUNDS are given, also highlight the target when selecting it." + (cl-flet ((multi-type (x) (car (get-text-property 0 'multi-category x)))) + (if-let* ((existing (seq-find + (pcase-lambda (`(,cand . ,ov)) + (and + (equal cand orig-target) + (if (and bounds ov) + (and (= (car bounds) (overlay-start ov)) + (= (cdr bounds) (overlay-end ov))) + (let ((cand-type (multi-type cand))) + (or (eq cand-type orig-type) + (eq cand-type (multi-type orig-target))))))) + embark--selection))) + (progn + (when (cdr existing) (delete-overlay (cdr existing))) + (setq embark--selection (delq existing embark--selection))) + (let ((target (copy-sequence orig-target)) overlay) + (when bounds + (setq overlay (make-overlay (car bounds) (cdr bounds))) + (overlay-put overlay 'face 'embark-selected) + (overlay-put overlay 'priority 1001)) + (add-text-properties 0 (length orig-target) + `(multi-category ,(cons orig-type orig-target)) + target) + (push (cons target overlay) embark--selection))))) + +(defalias 'embark-select #'ignore + "Add or remove the target from the current buffer's selection. +You can act on all selected targets at once with `embark-act-all'.") + +(defun embark-selected-candidates () + "Return currently selected candidates in the buffer." + (when embark--selection + (cl-flet ((unwrap (x) (get-text-property 0 'multi-category x))) + (let* ((first-type (car (unwrap (caar embark--selection)))) + (same (cl-every (lambda (item) + (eq (car (unwrap (car item))) first-type)) + embark--selection)) + (extract (if same + (pcase-lambda (`(,cand . ,overlay)) + (cons (cdr (unwrap cand)) overlay)) + #'identity))) + (cons + (if same first-type 'multi-category) + (nreverse + (mapcar + (lambda (item) + (pcase-let ((`(,cand . ,ov) (funcall extract item))) + (if ov `(,cand ,(overlay-start ov) . ,(overlay-end ov)) cand))) + embark--selection))))))) + +;;; Integration with external packages, mostly completion UIs + +;; marginalia + +;; Ensure that the Marginalia cache is reset, such that +;; `embark-toggle-variable-value' updates the display (See #540). +(with-eval-after-load 'marginalia + (push 'marginalia--cache-reset (alist-get :always embark-post-action-hooks))) + +;; vertico + +(declare-function vertico--candidate "ext:vertico") +(declare-function vertico--update "ext:vertico") +(declare-function vertico--remove-face "ext:vertico") +(defvar vertico--input) +(defvar vertico--candidates) +(defvar vertico--base) + +(defun embark--vertico-selected () + "Target the currently selected item in Vertico. +Return the category metadatum as the type of the target." + (when vertico--input + ;; Force candidate computation, if candidates are not yet available. + (vertico--update) + (cons (completion-metadata-get (embark--metadata) 'category) + (vertico--candidate)))) + +(defun embark--vertico-candidates () + "Collect the current Vertico candidates. +Return the category metadatum as the type of the candidates." + (when vertico--input + ;; Force candidate computation, if candidates are not yet available. + (vertico--update) + (cons (completion-metadata-get (embark--metadata) 'category) + vertico--candidates))) + +(defun embark--vertico-indicator () + "Embark indicator highlighting the current Vertico candidate." + (let ((fr face-remapping-alist)) + (lambda (&optional keymap _targets _prefix) + (when vertico--input + (setq-local face-remapping-alist + (if keymap + (cons '(vertico-current . embark-target) fr) + fr)))))) + +(with-eval-after-load 'vertico + (cl-defmethod vertico--format-candidate + :around (cand prefix suffix index start &context (embark--selection cons)) + (when (cl-find (concat vertico--base (nth index vertico--candidates)) + embark--selection + :test #'equal :key #'car) + (setq cand (copy-sequence cand)) + (add-face-text-property 0 (length cand) 'embark-selected t cand)) + (cl-call-next-method cand prefix suffix index start)) + (add-hook 'embark-indicators #'embark--vertico-indicator) + (add-hook 'embark-target-finders #'embark--vertico-selected) + (add-hook 'embark-candidate-collectors #'embark--vertico-candidates) + (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) + (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) + +;; ivy + +(declare-function ivy--expand-file-name "ext:ivy") +(declare-function ivy-state-current "ext:ivy") +(defvar ivy-text) +(defvar ivy-last) +(defvar ivy--old-cands) ; this stores the current candidates :) +(defvar ivy--length) + +(defun embark--ivy-selected () + "Target the currently selected item in Ivy. +Return the category metadatum as the type of the target." + ;; my favorite way of detecting Ivy + (when (memq 'ivy--queue-exhibit post-command-hook) + (cons + (completion-metadata-get (embark--metadata) 'category) + (ivy--expand-file-name + (if (and (> ivy--length 0) + (stringp (ivy-state-current ivy-last))) + (ivy-state-current ivy-last) + ivy-text))))) + +(defun embark--ivy-candidates () + "Return all current Ivy candidates." + ;; my favorite way of detecting Ivy + (when (memq 'ivy--queue-exhibit post-command-hook) + (cons + ;; swiper-isearch uses swiper-isearch-function as a completion + ;; table, but it doesn't understand metadata queries + (ignore-errors + (completion-metadata-get (embark--metadata) 'category)) + ivy--old-cands))) + +(with-eval-after-load 'ivy + (add-hook 'embark-target-finders #'embark--ivy-selected) + (add-hook 'embark-candidate-collectors #'embark--ivy-candidates) + (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) + (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) + +;;; Custom actions + +(defvar embark-separator-history nil + "Input history for the separators used by some embark commands. +The commands that prompt for a string separator are +`embark-insert' and `embark-copy-as-kill'.") + +(defun embark-keymap-help () + "Prompt for an action to perform or command to become and run it." + (interactive) + (user-error "Not meant to be called directly")) + +(defun embark-toggle-quit () + "Toggle whether the following action quits the minibuffer." + (interactive) + (when (minibufferp) + (setq embark--toggle-quit (not embark--toggle-quit)) + (if (consp embark-quit-after-action) + (message "Will %sobey embark-quit-after-action." + (if embark--toggle-quit "dis" "")) + (message + "Will %squit minibuffer after action" + (if (eq embark--toggle-quit embark-quit-after-action) "not " ""))))) + +(defun embark--separator (strings) + "Return a separator to join the STRINGS together. +With a prefix argument, prompt the user (unless STRINGS has 0 or +1 elements, in which case a separator is not needed)." + (if (and current-prefix-arg (cdr strings)) + (read-string "Separator: " nil 'embark-separator-history) + "\n")) + +(defun embark-copy-as-kill (strings) + "Join STRINGS and save on the `kill-ring'. +With a prefix argument, prompt for the separator to join the +STRINGS, which defaults to a newline." + (kill-new (string-join strings (embark--separator strings)))) + +(defun embark-insert (strings) + "Join STRINGS and insert the result at point. +With a prefix argument, prompt for the separator to join the +STRINGS, which defaults to a newline. + +Some whitespace is also inserted if necessary to avoid having the +inserted string blend into the existing buffer text. More +precisely: + +1. If the inserted string does not contain newlines, a space may +be added before or after it as needed to avoid inserting a word +constituent character next to an existing word constituent. + +2. For a multiline inserted string, newlines may be added before +or after as needed to ensure the inserted string is on lines of +its own." + (let ((multiline (seq-some (lambda (s) (string-match-p "\n" s)) strings)) + (separator (embark--separator strings))) + (cl-labels ((maybe-space () + (and (looking-at "\\w") (looking-back "\\w" 1) + (insert " "))) + (maybe-newline () + (or (looking-back "^[ \t]*" 40) (looking-at "\n\n") + (newline-and-indent))) + (maybe-whitespace () + (if multiline (maybe-newline) (maybe-space))) + (ins-string () + (let ((start (point))) + (insert (string-join strings separator)) + (save-excursion (goto-char start) (maybe-whitespace)) + (when (looking-back "\n" 1) (delete-char -1)) + (save-excursion (maybe-whitespace))))) + (if buffer-read-only + (with-selected-window (other-window-for-scrolling) + (ins-string)) + (ins-string))))) + +;; For Emacs 28 dired-jump will be moved to dired.el, but it seems +;; that since it already has an autoload in Emacs 28, this next +;; autoload is ignored. +(autoload 'dired-jump "dired-x" nil t) + +(defun embark-dired-jump (file &optional other-window) + "Open Dired buffer in directory containing FILE and move to its line. +When called with a prefix argument OTHER-WINDOW, open Dired in other window." + (interactive "fJump to Dired file: \nP") + (dired-jump other-window file)) + +(defun embark--read-from-history (prompt candidates &optional category) + "Read with completion from list of history CANDIDATES of CATEGORY. +Sorting and history are disabled. PROMPT is the prompt message." + (completing-read prompt + (embark--with-category category candidates) + nil t nil t)) + +(defun embark-kill-ring-remove (text) + "Remove TEXT from `kill-ring'." + (interactive (list (embark--read-from-history + "Remove from kill-ring: " kill-ring 'kill-ring))) + (embark-history-remove text) + (setq kill-ring (delete text kill-ring))) + +(defvar recentf-list) +(defun embark-recentf-remove (file) + "Remove FILE from the list of recent files." + (interactive (list (embark--read-from-history + "Remove recent file: " recentf-list 'file))) + (embark-history-remove (expand-file-name file)) + (embark-history-remove (abbreviate-file-name file)) + (when (and (boundp 'recentf-list) (fboundp 'recentf-expand-file-name)) + (setq recentf-list (delete (recentf-expand-file-name file) recentf-list)))) + +(defun embark-history-remove (str) + "Remove STR from `minibuffer-history-variable'. +Many completion UIs sort by history position. This command can be used +to remove entries from the history, such that they are not sorted closer +to the top." + (interactive (list (embark--read-from-history + "Remove history item: " + (if (eq minibuffer-history-variable t) + (user-error "No minibuffer history") + (symbol-value minibuffer-history-variable))))) + (unless (eq minibuffer-history-variable t) + (set minibuffer-history-variable + (delete str (symbol-value minibuffer-history-variable))))) + +(defvar xref-backend-functions) + +(defun embark-find-definition (symbol) + "Find definition of Emacs Lisp SYMBOL." + (interactive "sSymbol: ") + (let ((xref-backend-functions (lambda () 'elisp))) + (xref-find-definitions symbol))) + +(defun embark-info-lookup-symbol (symbol) + "Display the definition of SYMBOL, from the Elisp manual." + (interactive "SSymbol: ") + (info-lookup-symbol symbol 'emacs-lisp-mode)) + +(defun embark-rename-buffer (buffer newname &optional unique) + "Rename BUFFER to NEWNAME, optionally making it UNIQUE. +Interactively, you can set UNIQUE with a prefix argument. +Returns the new name actually used." + (interactive "bBuffer: \nBRename %s to: \nP") + (when-let ((buf (get-buffer buffer))) + (with-current-buffer buf + (rename-buffer newname unique)))) + +(defun embark--package-url (pkg) + "Return homepage for package PKG." + (when-let (desc (embark--package-desc pkg)) + (alist-get :url (package-desc-extras desc)))) + +(defun embark--prompt-for-package () + "Prompt user for a package name." + ;; this code is taken from the interactive spec of describe-package + (unless package--initialized + (package-initialize t)) + (intern + (completing-read "Package: " + (append (mapcar #'car package-alist) + (mapcar #'car package-archive-contents) + (mapcar #'car package--builtins))))) + +(defun embark-browse-package-url (pkg) + "Open homepage for package PKG with `browse-url'." + (interactive (list (embark--prompt-for-package))) + (if-let ((url (embark--package-url pkg))) + (browse-url url) + (user-error "No homepage found for `%s'" pkg))) + +(defun embark-save-package-url (pkg) + "Save URL of homepage for package PKG on the `kill-ring'." + (interactive (list (embark--prompt-for-package))) + (if-let ((url (embark--package-url pkg))) + (kill-new url) + (user-error "No homepage found for `%s'" pkg))) + +(defun embark-save-variable-value (var) + "Save value of VAR in the `kill-ring'." + (interactive "SVariable: ") + (kill-new (string-trim (pp-to-string (symbol-value var))))) + +(defun embark-insert-variable-value (var) + "Insert value of VAR." + (interactive "SVariable: ") + (insert (string-trim (pp-to-string (symbol-value var))))) + +(defun embark-toggle-variable (var &optional local) + "Toggle value of boolean variable VAR. +If prefix LOCAL is non-nil make variable local." + (interactive "SVariable: \nP") + (let ((val (symbol-value var))) + (unless (memq val '(nil t)) + (user-error "Not a boolean variable")) + (when local + (make-local-variable var)) + (funcall (or (get var 'custom-set) 'set) var (not val)))) + +(defun embark-insert-relative-path (file) + "Insert relative path to FILE. +The insert path is relative to `default-directory'." + (interactive "FFile: ") + (insert (file-relative-name (substitute-in-file-name file)))) + +(defun embark-save-relative-path (file) + "Save the relative path to FILE in the kill ring. +The insert path is relative to `default-directory'." + (interactive "FFile: ") + (kill-new (file-relative-name (substitute-in-file-name file)))) + +(defun embark-shell-command-on-buffer (buffer command &optional replace) + "Run shell COMMAND on contents of BUFFER. +Called with \\[universal-argument], replace contents of buffer +with command output. For replacement behavior see +`shell-command-dont-erase-buffer' setting." + (interactive + (list + (read-buffer "Buffer: " nil t) + (read-shell-command "Shell command: ") + current-prefix-arg)) + (with-current-buffer buffer + (shell-command-on-region (point-min) (point-max) + command + (and replace (current-buffer))))) + +(defun embark-open-externally (file) + "Open FILE using system's default application." + (interactive "fOpen: ") + (if (and (eq system-type 'windows-nt) + (fboundp 'w32-shell-execute)) + (w32-shell-execute "open" file) + (call-process (pcase system-type + ('darwin "open") + ('cygwin "cygstart") + (_ "xdg-open")) + nil 0 nil + (expand-file-name file)))) + +(defun embark-bury-buffer (buf) + "Bury buffer BUF." + (interactive "bBuffer: ") + (if-let (win (get-buffer-window buf)) + (with-selected-window win + (bury-buffer)) + (bury-buffer))) + +(defun embark-kill-buffer-and-window (buf) + "Kill buffer BUF and delete its window." + (interactive "bBuffer: ") + (when-let (buf (get-buffer buf)) + (if-let (win (get-buffer-window buf)) + (with-selected-window win + (kill-buffer-and-window)) + (kill-buffer buf)))) + +(defun embark-save-unicode-character (char) + "Save Unicode character CHAR to kill ring." + (interactive + (list (read-char-by-name "Insert character (Unicode name or hex): "))) + (kill-new (format "%c" char))) + +(defun embark-isearch () + "Prompt for string in the minibuffer and start isearch. +Unlike isearch, this command reads the string from the +minibuffer, which means it can be used as an Embark action." + (interactive) + (isearch-mode t) + (isearch-edit-string)) + +(defun embark-toggle-highlight () + "Toggle symbol highlighting using `highlight-symbol-at-point'." + (interactive) + (let ((regexp (find-tag-default-as-symbol-regexp)) + (highlighted (cl-find-if #'boundp + '(hi-lock-interactive-lighters + hi-lock-interactive-patterns)))) + (if (and highlighted (assoc regexp (symbol-value highlighted))) + (unhighlight-regexp regexp) + (highlight-symbol-at-point)))) + +(defun embark-next-symbol () + "Jump to next occurrence of symbol at point. +The search respects symbol boundaries." + (interactive) + (if-let ((symbol (thing-at-point 'symbol))) + (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) + (when (looking-at regexp) + (forward-symbol 1)) + (unless (re-search-forward regexp nil t) + (user-error "Symbol `%s' not found" symbol))) + (user-error "No symbol at point"))) + +(defun embark-previous-symbol () + "Jump to previous occurrence of symbol at point. +The search respects symbol boundaries." + (interactive) + (if-let ((symbol (thing-at-point 'symbol))) + (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) + (when (looking-back regexp (- (point) (length symbol))) + (forward-symbol -1)) + (unless (re-search-backward regexp nil t) + (user-error "Symbol `%s' not found" symbol))) + (user-error "No symbol at point"))) + +(defun embark-compose-mail (address) + "Compose email to ADDRESS." + ;; The only reason we cannot use compose-mail directly is its + ;; interactive specification, which just supllies nil for the + ;; address (and several other arguments). + (interactive "sTo: ") + (compose-mail address)) + +(autoload 'pp-display-expression "pp") + +(defun embark-pp-eval-defun (edebug) + "Run `eval-defun' and pretty print the result. +With a prefix argument EDEBUG, instrument the code for debugging." + (interactive "P") + (cl-letf (((symbol-function #'eval-expression-print-format) + (lambda (result) + (pp-display-expression result "*Pp Eval Output*")))) + (eval-defun edebug))) + +(defun embark-eval-replace () + "Evaluate region and replace with evaluated result." + (interactive) + (let ((beg (region-beginning)) + (end (region-end))) + (save-excursion + (goto-char end) + (insert (prin1-to-string + (eval (read (buffer-substring beg end)) lexical-binding))) + (delete-region beg end)))) + +(when (< emacs-major-version 29) + (defun embark-elp-restore-package (prefix) + "Remove instrumentation from functions with names starting with PREFIX." + (interactive "SPrefix: ") + (when (fboundp 'elp-restore-list) + (elp-restore-list + (mapcar #'intern + (all-completions (symbol-name prefix) + obarray 'elp-profilable-p)))))) + +(defmacro embark--define-hash (algorithm) + "Define command which computes hash from a string. +ALGORITHM is the hash algorithm symbol understood by `secure-hash'." + `(defun ,(intern (format "embark-hash-%s" algorithm)) (str) + ,(format "Compute %s hash of STR and store it in the kill ring." algorithm) + (interactive "sString: ") + (let ((hash (secure-hash ',algorithm str))) + (kill-new hash) + (message "%s: %s" ',algorithm hash)))) + +(embark--define-hash md5) +(embark--define-hash sha1) +(embark--define-hash sha224) +(embark--define-hash sha256) +(embark--define-hash sha384) +(embark--define-hash sha512) + +(defun embark-encode-url (start end) + "Properly URI-encode the region between START and END in current buffer." + (interactive "r") + (let ((encoded (url-encode-url (buffer-substring-no-properties start end)))) + (delete-region start end) + (insert encoded))) + +(defun embark-decode-url (start end) + "Decode the URI-encoded region between START and END in current buffer." + (interactive "r") + (let ((decoded (url-unhex-string (buffer-substring-no-properties start end)))) + (delete-region start end) + (insert decoded))) + +(defvar epa-replace-original-text) +(defun embark-epa-decrypt-region (start end) + "Decrypt region between START and END." + (interactive "r") + (let ((epa-replace-original-text t)) + (epa-decrypt-region start end))) + +(defvar eww-download-directory) +(autoload 'eww-download-callback "eww") + +(defun embark-download-url (url) + "Download URL to `eww-download-directory'." + (interactive "sDownload URL: ") + (let ((dir eww-download-directory)) + (when (functionp dir) (setq dir (funcall dir))) + (access-file dir "Download failed") + (url-retrieve + url #'eww-download-callback + (if (>= emacs-major-version 28) (list url dir) (list url))))) + +;;; Setup and pre-action hooks + +(defun embark--restart (&rest _) + "Restart current command with current input. +Use this to refresh the list of candidates for commands that do +not handle that themselves." + (when (minibufferp) + (embark--become-command embark--command (minibuffer-contents)))) + +(defun embark--shell-prep (&rest _) + "Prepare target for use as argument for a shell command. +This quotes the spaces, inserts an extra space at the beginning +and leaves the point to the left of it." + (let ((contents (minibuffer-contents))) + (delete-minibuffer-contents) + (insert " " (shell-quote-wildcard-pattern contents)) + (goto-char (minibuffer-prompt-end)))) + +(defun embark--force-complete (&rest _) + "Select first minibuffer completion candidate matching target." + (minibuffer-force-complete)) + +(cl-defun embark--eval-prep (&key type &allow-other-keys) + "If target's TYPE is: variable, skip edit; function, wrap in parens." + (when (memq type '(command function)) + (embark--allow-edit) + (goto-char (minibuffer-prompt-end)) + (insert "(") + (goto-char (point-max)) + (insert ")") + (backward-char))) + +(cl-defun embark--beginning-of-target (&key bounds &allow-other-keys) + "Go to beginning of the target BOUNDS." + (when (number-or-marker-p bounds) + (goto-char (car bounds)))) + +(cl-defun embark--end-of-target (&key bounds &allow-other-keys) + "Go to end of the target BOUNDS." + (when (number-or-marker-p bounds) + (goto-char (cdr bounds)))) + +(cl-defun embark--mark-target (&rest rest &key run bounds &allow-other-keys) + "Mark the target if its BOUNDS are known. +After marking the target, this calls RUN with the REST of its arguments." + (cond + ((and bounds run) + (save-mark-and-excursion + (set-mark (cdr bounds)) + (goto-char (car bounds)) + (apply run :bounds bounds rest))) + (bounds ;; used as pre- or post-action hook + (set-mark (cdr bounds)) + (goto-char (car bounds))) + (run (apply run rest)))) + +(cl-defun embark--unmark-target (&rest _) + "Deactivate the region target." + (deactivate-mark t)) + +(cl-defun embark--narrow-to-target + (&rest rest &key run bounds &allow-other-keys) + "Narrow buffer to target if its BOUNDS are known. +Intended for use as an Embark around-action hook. This function +runs RUN with the buffer narrowed to given BOUNDS passing along +the REST of the arguments." + (if bounds + (save-excursion + (save-restriction + (narrow-to-region (car bounds) (cdr bounds)) + (goto-char (car bounds)) + (apply run :bounds bounds rest))) + (apply run rest))) + +(defun embark--allow-edit (&rest _) + "Allow editing the target." + (remove-hook 'post-command-hook #'exit-minibuffer t) + (remove-hook 'post-command-hook 'ivy-immediate-done t)) + +(defun embark--ignore-target (&rest _) + "Ignore the target." + (let ((contents + (get-text-property (minibuffer-prompt-end) 'embark--initial-input))) + (delete-minibuffer-contents) + (when contents (insert contents))) + (embark--allow-edit)) + +(autoload 'xref-push-marker-stack "xref") +(defun embark--xref-push-marker (&rest _) + "Push a marker onto the xref marker stack." + (xref-push-marker-stack)) + +(cl-defun embark--confirm (&key action target &allow-other-keys) + "Ask for confirmation before running the ACTION on the TARGET." + (unless (y-or-n-p (format "Run %s on %s? " action target)) + (user-error "Canceled"))) + +(defun embark--associated-directory (target type) + "Return directory associated to TARGET of given TYPE. +The supported values of TYPE are file, buffer, bookmark and +library, which have an obvious notion of associated directory." + (pcase type + ('file + (file-name-directory target)) + ('buffer + (buffer-local-value 'default-directory (get-buffer target))) + ('bookmark + (file-name-directory (bookmark-location target))) + ('library + (file-name-directory (locate-library target))))) + +(autoload 'bookmark-location "bookmark") +(cl-defun embark--cd (&rest rest &key run target type &allow-other-keys) + "Run action with `default-directory' set to the directory of TARGET. +The supported values of TYPE are file, buffer, bookmark and +library, which have an obvious notion of associated directory. +The REST of the arguments are also passed to RUN." + (let ((default-directory + (or (embark--associated-directory target type) default-directory))) + (apply run :target target :type type rest))) + +(cl-defun embark--save-excursion (&rest rest &key run &allow-other-keys) + "Run action without moving point. +This simply calls RUN with the REST of its arguments inside +`save-excursion'." + (save-excursion (apply run rest))) + +(defun embark--universal-argument (&rest _) + "Run action with a universal prefix argument." + (setq prefix-arg '(4))) + +;;; keymaps + +(defvar-keymap embark-meta-map + :doc "Keymap for non-action Embark functions." + "-" #'negative-argument + "0" #'digit-argument + "1" #'digit-argument + "2" #'digit-argument + "3" #'digit-argument + "4" #'digit-argument + "5" #'digit-argument + "6" #'digit-argument + "7" #'digit-argument + "8" #'digit-argument + "9" #'digit-argument) + +(defvar-keymap embark-general-map + :doc "Keymap for Embark general actions." + :parent embark-meta-map + "i" #'embark-insert + "w" #'embark-copy-as-kill + "q" #'embark-toggle-quit + "E" #'embark-export + "S" #'embark-collect + "L" #'embark-live + "B" #'embark-become + "A" #'embark-act-all + "C-s" #'embark-isearch + "C-SPC" #'mark + "DEL" #'delete-region + "SPC" #'embark-select) + +(defvar-keymap embark-encode-map + :doc "Keymap for Embark region encoding actions." + "r" #'rot13-region + "." #'morse-region + "-" #'unmorse-region + "s" #'studlify-region + "m" #'embark-hash-md5 + "1" #'embark-hash-sha1 + "2" #'embark-hash-sha256 + "3" #'embark-hash-sha384 + "4" #'embark-hash-sha224 + "5" #'embark-hash-sha512 + "f" #'format-encode-region + "F" #'format-decode-region + "b" #'base64-encode-region + "B" #'base64-decode-region + "u" #'embark-encode-url + "U" #'embark-decode-url + "c" #'epa-encrypt-region + "C" #'embark-epa-decrypt-region) + +(fset 'embark-encode-map embark-encode-map) + +(defvar-keymap embark-sort-map + :doc "Keymap for Embark actions that sort the region" + "l" #'sort-lines + "P" #'sort-pages + "f" #'sort-fields + "c" #'sort-columns + "p" #'sort-paragraphs + "r" #'sort-regexp-fields + "n" #'sort-numeric-fields) + +(fset 'embark-sort-map embark-sort-map) + +;; these will have autoloads in Emacs 28 +(autoload 'calc-grab-sum-down "calc" nil t) +(autoload 'calc-grab-sum-across "calc" nil t) + +;; this has had an autoload cookie since at least Emacs 26 +;; but that autoload doesn't seem to work for me +(autoload 'org-table-convert-region "org-table" nil t) + +(defvar-keymap embark-region-map + :doc "Keymap for Embark actions on the active region." + :parent embark-general-map + "u" #'upcase-region + "l" #'downcase-region + "c" #'capitalize-region + "|" #'shell-command-on-region + "e" #'eval-region + "<" #'embark-eval-replace + "a" #'align + "A" #'align-regexp + "i" #'indent-rigidly + "I" #'embark-insert + "TAB" #'indent-region + "f" #'fill-region + "p" #'fill-region-as-paragraph + "$" #'ispell-region + "=" #'count-words-region + "F" #'whitespace-cleanup-region + "t" #'transpose-regions + "o" #'org-table-convert-region + ";" #'comment-or-uncomment-region + "W" #'write-region + "+" #'append-to-file + "m" #'apply-macro-to-region-lines + "n" #'narrow-to-region + "*" #'calc-grab-region + ":" #'calc-grab-sum-down + "_" #'calc-grab-sum-across + "r" #'reverse-region + "d" #'delete-duplicate-lines + "b" #'browse-url-of-region + "h" #'shr-render-region + "'" #'expand-region-abbrevs + "v" #'vc-region-history + "R" #'repunctuate-sentences + "s" 'embark-sort-map + ">" 'embark-encode-map) + +(defvar-keymap embark-vc-file-map + :doc "Keymap for Embark VC file actions." + "d" #'vc-delete-file + "r" #'vc-rename-file + "i" #'vc-ignore) + +(fset 'embark-vc-file-map embark-vc-file-map) + +(defvar-keymap embark-file-map + :doc "Keymap for Embark file actions." + :parent embark-general-map + "RET" #'find-file + "f" #'find-file + "F" #'find-file-literally + "o" #'find-file-other-window + "d" #'delete-file + "D" #'delete-directory + "r" #'rename-file + "c" #'copy-file + "j" #'embark-dired-jump + "!" #'shell-command + "&" #'async-shell-command + "$" #'eshell + "<" #'insert-file + "m" #'chmod + "=" #'ediff-files + "+" #'make-directory + "\\" #'embark-recentf-remove + "I" #'embark-insert-relative-path + "W" #'embark-save-relative-path + "x" #'embark-open-externally + "e" #'eww-open-file + "l" #'load-file + "b" #'byte-compile-file + "R" #'byte-recompile-directory + "v" 'embark-vc-file-map) + +(defvar-keymap embark-kill-ring-map + :doc "Keymap for `kill-ring' commands." + :parent embark-general-map + "\\" #'embark-kill-ring-remove) + +(defvar-keymap embark-url-map + :doc "Keymap for Embark url actions." + :parent embark-general-map + "RET" #'browse-url + "b" #'browse-url + "d" #'embark-download-url + "e" #'eww) + +(defvar-keymap embark-email-map + :doc "Keymap for Embark email actions." + :parent embark-general-map + "RET" #'embark-compose-mail + "c" #'embark-compose-mail) + +(defvar-keymap embark-library-map + :doc "Keymap for operations on Emacs Lisp libraries." + :parent embark-general-map + "RET" #'find-library + "l" #'load-library + "f" #'find-library + "h" #'finder-commentary + "a" #'apropos-library + "L" #'locate-library + "m" #'info-display-manual + "$" #'eshell) + +(defvar-keymap embark-buffer-map + :doc "Keymap for Embark buffer actions." + :parent embark-general-map + "RET" #'switch-to-buffer + "k" #'kill-buffer + "b" #'switch-to-buffer + "o" #'switch-to-buffer-other-window + "z" #'embark-bury-buffer + "K" #'embark-kill-buffer-and-window + "r" #'embark-rename-buffer + "=" #'ediff-buffers + "|" #'embark-shell-command-on-buffer + "<" #'insert-buffer + "$" #'eshell) + +(defvar-keymap embark-tab-map + :doc "Keymap for actions for tab-bar tabs." + :parent embark-general-map + "RET" #'tab-bar-select-tab-by-name + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + +(defvar-keymap embark-identifier-map + :doc "Keymap for Embark identifier actions." + :parent embark-general-map + "RET" #'xref-find-definitions + "h" #'display-local-help + "H" #'embark-toggle-highlight + "d" #'xref-find-definitions + "r" #'xref-find-references + "a" #'xref-find-apropos + "s" #'info-lookup-symbol + "n" #'embark-next-symbol + "p" #'embark-previous-symbol + "'" #'expand-abbrev + "$" #'ispell-word + "o" #'occur) + +(defvar-keymap embark-expression-map + :doc "Keymap for Embark expression actions." + :parent embark-general-map + "RET" #'pp-eval-expression + "e" #'pp-eval-expression + "<" #'embark-eval-replace + "m" #'pp-macroexpand-expression + "TAB" #'indent-region + "r" #'raise-sexp + "t" #'transpose-sexps + "k" #'kill-region + "u" #'backward-up-list + "n" #'forward-list + "p" #'backward-list) + +(defvar-keymap embark-defun-map + :doc "Keymap for Embark defun actions." + :parent embark-expression-map + "RET" #'embark-pp-eval-defun + "e" #'embark-pp-eval-defun + "c" #'compile-defun + "l" #'elint-defun + "D" #'edebug-defun + "o" #'checkdoc-defun + "N" #'narrow-to-defun) + +;; Use quoted symbols to avoid byte-compiler warnings. +(defvar-keymap embark-heading-map + :doc "Keymap for Embark heading actions." + :parent embark-general-map + "RET" 'outline-show-subtree + "TAB" 'outline-cycle ;; New in Emacs 28! + "C-SPC" 'outline-mark-subtree + "n" 'outline-next-visible-heading + "p" 'outline-previous-visible-heading + "f" 'outline-forward-same-level + "b" 'outline-backward-same-level + "^" 'outline-move-subtree-up + "v" 'outline-move-subtree-down + "u" 'outline-up-heading + "s" 'outline-show-subtree + "d" 'outline-hide-subtree + ">" 'outline-demote + "<" 'outline-promote) + +(defvar-keymap embark-symbol-map + :doc "Keymap for Embark symbol actions." + :parent embark-identifier-map + "RET" #'embark-find-definition + "h" #'describe-symbol + "s" #'embark-info-lookup-symbol + "d" #'embark-find-definition + "e" #'pp-eval-expression + "a" #'apropos + "\\" #'embark-history-remove) + +(defvar-keymap embark-face-map + :doc "Keymap for Embark face actions." + :parent embark-symbol-map + "h" #'describe-face + "c" #'customize-face + "+" #'make-face-bold + "-" #'make-face-unbold + "/" #'make-face-italic + "|" #'make-face-unitalic + "!" #'invert-face + "f" #'set-face-foreground + "b" #'set-face-background) + +(defvar-keymap embark-variable-map + :doc "Keymap for Embark variable actions." + :parent embark-symbol-map + "=" #'set-variable + "c" #'customize-set-variable + "u" #'customize-variable + "v" #'embark-save-variable-value + "<" #'embark-insert-variable-value + "t" #'embark-toggle-variable) + +(defvar-keymap embark-function-map + :doc "Keymap for Embark function actions." + :parent embark-symbol-map + "m" #'elp-instrument-function ;; m=measure + "M" 'elp-restore-function ;; quoted, not autoloaded + "k" #'debug-on-entry ;; breaKpoint (running out of letters, really) + "K" #'cancel-debug-on-entry + "t" #'trace-function + "T" 'untrace-function) ;; quoted, not autoloaded + +(defvar-keymap embark-command-map + :doc "Keymap for Embark command actions." + :parent embark-function-map + "x" #'execute-extended-command + "I" #'Info-goto-emacs-command-node + "b" #'where-is + "g" #'global-set-key + "l" #'local-set-key) + +(defvar-keymap embark-package-map + :doc "Keymap for Embark package actions." + :parent embark-general-map + "RET" #'describe-package + "h" #'describe-package + "i" #'package-install + "I" #'embark-insert + "d" #'package-delete + "r" #'package-reinstall + "u" #'embark-browse-package-url + "W" #'embark-save-package-url + "a" #'package-autoremove + "g" #'package-refresh-contents + "m" #'elp-instrument-package ;; m=measure + "M" (if (fboundp 'embark-elp-restore-package) + 'embark-elp-restore-package + 'elp-restore-package)) + +(defvar-keymap embark-bookmark-map + :doc "Keymap for Embark bookmark actions." + :parent embark-general-map + "RET" #'bookmark-jump + "s" #'bookmark-set + "d" #'bookmark-delete + "r" #'bookmark-rename + "R" #'bookmark-relocate + "l" #'bookmark-locate + "<" #'bookmark-insert + "j" #'bookmark-jump + "o" #'bookmark-jump-other-window + "f" #'bookmark-jump-other-frame + "a" 'bookmark-show-annotation + "e" 'bookmark-edit-annotation + "$" #'eshell) + +(defvar-keymap embark-unicode-name-map + :doc "Keymap for Embark Unicode name actions." + :parent embark-general-map + "RET" #'insert-char + "I" #'insert-char + "W" #'embark-save-unicode-character) + +(defvar-keymap embark-prose-map + :doc "Keymap for Embark actions for dealing with prose." + :parent embark-general-map + "$" #'ispell-region + "f" #'fill-region + "u" #'upcase-region + "l" #'downcase-region + "c" #'capitalize-region + "F" #'whitespace-cleanup-region + "=" #'count-words-region) + +(defvar-keymap embark-sentence-map + :doc "Keymap for Embark actions for dealing with sentences." + :parent embark-prose-map + "t" #'transpose-sentences + "n" #'forward-sentence + "p" #'backward-sentence) + +(defvar-keymap embark-paragraph-map + :doc "Keymap for Embark actions for dealing with paragraphs." + :parent embark-prose-map + "t" #'transpose-paragraphs + "n" #'forward-paragraph + "p" #'backward-paragraph + "R" #'repunctuate-sentences) + +(defvar-keymap embark-become-help-map + :doc "Keymap for Embark help actions." + :parent embark-meta-map + "V" #'apropos-variable + "U" #'apropos-user-option + "C" #'apropos-command + "v" #'describe-variable + "f" #'describe-function + "s" #'describe-symbol + "F" #'describe-face + "p" #'describe-package + "i" #'describe-input-method) + +(autoload 'recentf-open-files "recentf" nil t) + +(defvar-keymap embark-become-file+buffer-map + :doc "Embark become keymap for files and buffers." + :parent embark-meta-map + "f" #'find-file + "4 f" #'find-file-other-window + "." #'find-file-at-point + "p" #'project-find-file + "r" #'recentf-open-files + "b" #'switch-to-buffer + "4 b" #'switch-to-buffer-other-window + "l" #'locate + "L" #'find-library + "v" #'vc-dir) + +(defvar-keymap embark-become-shell-command-map + :doc "Embark become keymap for shell commands." + :parent embark-meta-map + "!" #'shell-command + "&" #'async-shell-command + "c" #'comint-run + "t" #'term) + +(defvar-keymap embark-become-match-map + :doc "Embark become keymap for search." + :parent embark-meta-map + "o" #'occur + "k" #'keep-lines + "f" #'flush-lines + "c" #'count-matches) + +(provide 'embark) + +;; Check that embark-consult is installed. If Embark is used in +;; combination with Consult, you should install the integration package, +;; such that features like embark-export from consult-grep work as +;; expected. + +(with-eval-after-load 'consult + (unless (require 'embark-consult nil 'noerror) + (warn "The package embark-consult should be installed if you use both Embark and Consult"))) + +(with-eval-after-load 'org + (require 'embark-org)) + +;;; embark.el ends here diff --git a/elpa/embark-0.22.1/embark.info b/elpa/embark-0.22.1/embark.info @@ -0,0 +1,1406 @@ +This is docJMGGb8.info, produced by makeinfo version 6.7 from +embark.texi. + +INFO-DIR-SECTION Emacs misc features +START-INFO-DIR-ENTRY +* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. +END-INFO-DIR-ENTRY + + +File: docJMGGb8.info, Node: Top, Next: Overview, Up: (dir) + +Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +*************************************************** + +* Menu: + +* Overview:: +* Quick start:: +* Advanced configuration:: +* How does Embark call the actions?:: +* Embark, Marginalia and Consult: Embark Marginalia and Consult. +* Resources:: +* Contributions:: +* Acknowledgments:: + +— The Detailed Node Listing — + +Overview + +* Acting on targets:: +* The default action on a target:: +* Working with sets of possible targets:: +* Switching to a different command without losing what you've typed:: + +Working with sets of possible targets + +* Selecting some targets to make an ad hoc candidate set:: +* embark-live a live-updating variant of embark-collect:: + +Advanced configuration + +* Showing information about available targets and actions:: +* Selecting commands via completions instead of key bindings:: +* Quitting the minibuffer after an action:: +* Running some setup after injecting the target:: +* Running hooks before, after or around an action: Running hooks before after or around an action. +* Creating your own keymaps:: +* Defining actions for new categories of targets:: + +Defining actions for new categories of targets + +* New minibuffer target example - tab-bar tabs:: +* New target example in regular buffers - short Wikipedia links:: + +How does Embark call the actions? + +* Non-interactive functions as actions:: + +Embark, Marginalia and Consult + +* Marginalia:: +* Consult:: + + + +File: docJMGGb8.info, Node: Overview, Next: Quick start, Prev: Top, Up: Top + +1 Overview +********** + +Embark makes it easy to choose a command to run based on what is near +point, both during a minibuffer completion session (in a way familiar to +Helm or Counsel users) and in normal buffers. Bind the command +‘embark-act’ to a key and it acts like prefix-key for a keymap of +_actions_ (commands) relevant to the _target_ around point. With point +on an URL in a buffer you can open the URL in a browser or eww or +download the file it points to. If while switching buffers you spot an +old one, you can kill it right there and continue to select another. +Embark comes preconfigured with over a hundred actions for common types +of targets such as files, buffers, identifiers, s-expressions, +sentences; and it is easy to add more actions and more target types. +Embark can also collect all the candidates in a minibuffer to an +occur-like buffer or export them to a buffer in a major-mode specific to +the type of candidates, such as dired for a set of files, ibuffer for a +set of buffers, or customize for a set of variables. + +* Menu: + +* Acting on targets:: +* The default action on a target:: +* Working with sets of possible targets:: +* Switching to a different command without losing what you've typed:: + + +File: docJMGGb8.info, Node: Acting on targets, Next: The default action on a target, Up: Overview + +1.1 Acting on targets +===================== + +You can think of ‘embark-act’ as a keyboard-based version of a +right-click contextual menu. The ‘embark-act’ command (which you should +bind to a convenient key), acts as a prefix for a keymap offering you +relevant _actions_ to use on a _target_ determined by the context: + + • In the minibuffer, the target is the current top completion + candidate. + • In the ‘*Completions*’ buffer the target is the completion at + point. + • In a regular buffer, the target is the region if active, or else + the file, symbol, URL, s-expression or defun at point. + + Multiple targets can be present at the same location and you can +cycle between them by repeating the ‘embark-act’ key binding. The type +of actions offered depend on the type of the target. Here is a sample +of a few of the actions offered in the default configuration: + + • For files you get offered actions like deleting, copying, renaming, + visiting in another window, running a shell command on the file, + etc. + • For buffers the actions include switching to or killing the buffer. + • For package names the actions include installing, removing or + visiting the homepage. + • For Emacs Lisp symbols the actions include finding the definition, + looking up documentation, evaluating (which for a variable + immediately shows the value, but for a function lets you pass it + some arguments first). There are some actions specific to + variables, such as setting the value directly or though the + customize system, and some actions specific to commands, such as + binding it to a key. + + By default when you use ‘embark-act’ if you don’t immediately select +an action, after a short delay Embark will pop up a buffer showing a +list of actions and their corresponding key bindings. If you are using +‘embark-act’ outside the minibuffer, Embark will also highlight the +current target. These behaviors are configurable via the variable +‘embark-indicators’. Instead of selecting an action via its key +binding, you can select it by name with completion by typing ‘C-h’ after +‘embark-act’. + + Everything is easily configurable: determining the current target, +classifying it, and deciding which actions are offered for each type in +the classification. The above introduction just mentions part of the +default configuration. + + Configuring which actions are offered for a type is particularly easy +and requires no programming: the variable ‘embark-keymap-alist’ +associates target types with variables containing keymaps, and those +keymaps containing bindings for the actions. (To examine the available +categories and their associated keymaps, you can use ‘C-h v +embark-keymap-alist’ or customize that variable.) For example, in the +default configuration the type ‘file’ is associated with the symbol +‘embark-file-map’. That symbol names a keymap with single-letter key +bindings for common Emacs file commands, for instance ‘c’ is bound to +‘copy-file’. This means that if you are in the minibuffer after running +a command that prompts for a file, such as ‘find-file’ or ‘rename-file’, +you can copy a file by running ‘embark-act’ and then pressing ‘c’. + + These action keymaps are very convenient but not strictly necessary +when using ‘embark-act’: you can use any command that reads from the +minibuffer as an action and the target of the action will be inserted at +the first minibuffer prompt. After running ‘embark-act’ all of your key +bindings and even ‘execute-extended-command’ can be used to run a +command. For example, if you want to replace all occurrences of the +symbol at point, just use ‘M-%’ as the action, there is no need to bind +‘query-replace’ in one of Embark’s keymaps. Also, those action keymaps +are normal Emacs keymaps and you should feel free to bind in them +whatever commands you find useful as actions and want to be available +through convenient bindings. + + The actions in ‘embark-general-map’ are available no matter what type +of completion you are in the middle of. By default this includes +bindings to save the current candidate in the kill ring and to insert +the current candidate in the previously selected buffer (the buffer that +was current when you executed a command that opened up the minibuffer). + + Emacs’s minibuffer completion system includes metadata indicating the +_category_ of what is being completed. For example, ‘find-file’’s +metadata indicates a category of ‘file’ and ‘switch-to-buffer’’s +metadata indicates a category of ‘buffer’. Embark has the related +notion of the _type_ of a target for actions, and by default when +category metadata is present it is taken to be the type of minibuffer +completion candidates when used as targets. Emacs commands often do not +set useful category metadata so the Marginalia +(https://github.com/minad/marginalia) package, which supplies this +missing metadata, is highly recommended for use with Embark. + + Embark’s default configuration has actions for the following target +types: files, buffers, symbols, packages, URLs, bookmarks, and as a +somewhat special case, actions for when the region is active. You can +read about the default actions and their key bindings +(https://github.com/oantolin/embark/wiki/Default-Actions) on the GitHub +project wiki. + + +File: docJMGGb8.info, Node: The default action on a target, Next: Working with sets of possible targets, Prev: Acting on targets, Up: Overview + +1.2 The default action on a target +================================== + +Embark has a notion of default action for a target: + + • If the target is a minibuffer completion candidate, then the + default action is whatever command opened the minibuffer in the + first place. For example if you run ‘kill-buffer’, then the + default action will be to kill buffers. + • If the target comes from a regular buffer (i.e., not a minibuffer), + then the default action is whatever is bound to ‘RET’ in the keymap + of actions for that type of target. For example, in Embark’s + default configuration for a URL found at point the default action + is ‘browse-url’, because ‘RET’ is bound to ‘browse-url’ in the + ‘embark-url-map’ keymap. + + To run the default action you can press ‘RET’ after running +‘embark-act’. Note that if there are several different targets at a +given location, each has its own default action, so first cycle to the +target you want and then press ‘RET’ to run the corresponding default +action. + + There is also ‘embark-dwim’ which runs the default action for the +first target found. It’s pretty handy in non-minibuffer buffers: with +Embark’s default configuration it will: + + • Open the file at point. + • Open the URL at point in a web browser (using the ‘browse-url’ + command). + • Compose a new email to the email address at point. + • In an Emacs Lisp buffer, if point is on an opening parenthesis or + right after a closing one, it will evaluate the corresponding + expression. + • Go to the definition of an Emacs Lisp function, variable or macro + at point. + • Find the file corresponding to an Emacs Lisp library at point. + + +File: docJMGGb8.info, Node: Working with sets of possible targets, Next: Switching to a different command without losing what you've typed, Prev: The default action on a target, Up: Overview + +1.3 Working with sets of possible targets +========================================= + +Besides acting individually on targets, Embark lets you work +collectively on a set of target _candidates_. For example, while you +are in the minibuffer the candidates are simply the possible completions +of your input. Embark provides three main commands to work on candidate +sets: + + • The ‘embark-act-all’ command runs the same action on each of the + current candidates. It is just like using ‘embark-act’ on each + candidate in turn. (Because you can easily act on many more + candidates than you meant to, by default Embark asks you to confirm + uses of ‘embark-act-all’; you can turn this off by setting the user + option ‘embark-confirm-act-all’ to ‘nil’.) + + • The ‘embark-collect’ command produces a buffer listing all the + current candidates, for you to peruse and run actions on at your + leisure. The candidates are displayed as a list showing additional + annotations. + + The Embark Collect buffer is somewhat “dired-like”: you can select + and deselect candidates through the ‘embark-select’ action (bound + to ‘SPC’). In an Embark Collect buffer ‘embark-act’ is bound to + ‘a’ and ‘embark-act-all’ is bound to ‘A’; ‘embark-act-all’ will act + on all currently marked candidates if there any, and will act on + all candidates if none are marked. In particular, this means that + ‘a SPC’ will toggle whether the candidate at point is selected, and + ‘A SPC’ will select all candidates if none are selected, or + deselect all selected candidates if there are some. + + • The ‘embark-export’ command tries to open a buffer in an + appropriate major mode for the set of candidates. If the + candidates are files export produces a Dired buffer; if they are + buffers, you get an Ibuffer buffer; and if they are packages you + get a buffer in package menu mode. + + If you use the grepping commands from the Consult + (https://github.com/minad/consult/) package, ‘consult-grep’, + ‘consult-git-grep’ or ‘consult-ripgrep’, then you should install + the ‘embark-consult’ package, which adds support for exporting a + list of grep results to an honest grep-mode buffer, on which you + can even use wgrep (https://github.com/mhayashi1120/Emacs-wgrep) if + you wish. + + When in doubt choosing between exporting and collecting, a good rule +of thumb is to always prefer ‘embark-export’ since when an exporter to a +special major mode is available for a given type of target, it will be +more featureful than an Embark collect buffer, and if no such exporter +is configured the ‘embark-export’ command falls back to the generic +‘embark-collect’. + + These commands are always available as “actions” (although they do +not act on just the current target but on all candidates) for +‘embark-act’ and are bound to ‘A’, ‘S’ (for “snapshot”), and ‘E’, +respectively, in ‘embark-general-map’. This means that you do not have +to bind your own key bindings for these (although you can, of course!), +just a key binding for ‘embark-act’. + + In Embark Collect or Embark Export buffers that were obtained by +running ‘embark-collect’ or ‘embark-export’ from within a minibuffer +completion session, ‘g’ is bound to a command that restarts the +completion session, that is, the command that opened the minibuffer is +run again and the minibuffer contents restored. You can then interact +normally with the command, perhaps editing the minibuffer contents, and, +if you wish, you can rerun ‘embark-collect’ or ‘embark-export’ to get an +updated buffer. + +* Menu: + +* Selecting some targets to make an ad hoc candidate set:: +* embark-live a live-updating variant of embark-collect:: + + +File: docJMGGb8.info, Node: Selecting some targets to make an ad hoc candidate set, Next: embark-live a live-updating variant of embark-collect, Up: Working with sets of possible targets + +1.3.1 Selecting some targets to make an ad hoc candidate set +------------------------------------------------------------ + +The commands for working with sets of candidates just described, namely +‘embark-act-all’, ‘embark-export’ and ‘embark-collect’ by default work +with all candidates defined in the current context. For example, in the +minibuffer they operate on all currently completion candidates, or in a +dired buffer they work on all marked files (or all files if none are +marked). Embark also has a notion of _selection_, where you can +accumulate an ad hoc list of targets for these commands to work on. + + The selection is controlled by using the ‘embark-select’ action +(which must be run as an action through ‘embark-act’), bound to ‘SPC’ in +‘embark-general-map’ so that it is always available. Calling this +action on a target toggles its membership in the current buffer’s Embark +selection; that is, it adds it to selection if not selected and removes +it from the selection if it was selected. Whenever the selection for a +buffer is non-empty, the commands ‘embark-act-all’, ‘embark-export’ and +‘embark-collect’ will act on the selection. + + To deselect all selected targets, you can use the ‘embark-select’ +action through ‘embark-act-all’, since this will run ‘embark-select’ on +each member of the current selection. Similarly if no targets are +selected and you are in a minibuffer completion session, running +‘embark-select’ from ‘embark-act-all’ will select all the current +completion candidates. + + This functionality is supported everywhere: + + • In the minibuffer this gives a convenient way to act on several + completion candidates that don’t follow any simple pattern: just go + through the completions selecting the ones you want, then use + ‘embark-act-all’. For example, you could attach several files at + once to an email. + • For Embark Collect buffers this functionality enables a dired-like + workflow, in which you mark various candidates and apply an action + to all at once. (It supersedes a previous ad hoc dired-like + interface that was implemented only in Embark Collect buffers, with + a slightly different interface.) + • In a eww buffer you could use this to select various links you wish + to follow up on, and then collect them into a buffer. Similarly, + while reading Emacs’s info manual you could select some symbols you + want to read more about and export them to an ‘apropos-mode’ + buffer. + • You can use selections in regular text or programming buffers to do + complex editing operations. For example, if you have three + paragraphs scattered over a file and you want to bring them + together, you can select each one, insert them all somewhere and + finally delete all of them (from their original locations). + + +File: docJMGGb8.info, Node: embark-live a live-updating variant of embark-collect, Prev: Selecting some targets to make an ad hoc candidate set, Up: Working with sets of possible targets + +1.3.2 ‘embark-live’ a live-updating variant of ‘embark-collect’ +--------------------------------------------------------------- + +Finally, there is also an ‘embark-live’ variant of the ‘embark-collect’ +command which automatically updates the collection after each change in +the source buffer. Users of a completion UI that automatically updates +and displays the candidate list (such as Vertico, Icomplete, Fido-mode, +or MCT) will probably not want to use ‘embark-live’ from the minibuffer +as they will then have two live updating displays of the completion +candidates! + + A more likely use of ‘embark-live’ is to be called from a regular +buffer to display a sort of live updating “table of contents” for the +buffer. This depends on having appropriate candidate collectors +configured in ‘embark-candidate-collectors’. There are not many in +Embark’s default configuration, but you can try this experiment: open a +dired buffer in a directory that has very many files, mark a few, and +run ‘embark-live’. You’ll get an Embark Collect buffer containing only +the marked files, which updates as you mark or unmark files in dired. +To make ‘embark-live’ genuinely useful other candidate collectors are +required. The ‘embark-consult’ package (documented near the end of this +manual) contains a few: one for imenu items and one for outline headings +as used by ‘outline-minor-mode’. Those collectors really do give +‘embark-live’ a table-of-contents feel. + + +File: docJMGGb8.info, Node: Switching to a different command without losing what you've typed, Prev: Working with sets of possible targets, Up: Overview + +1.4 Switching to a different command without losing what you’ve typed +===================================================================== + +Embark also has the ‘embark-become’ command which is useful for when you +run a command, start typing at the minibuffer and realize you meant a +different command. The most common case for me is that I run +‘switch-to-buffer’, start typing a buffer name and realize I haven’t +opened the file I had in mind yet! I’ll use this situation as a running +example to illustrate ‘embark-become’. When this happens I can, of +course, press ‘C-g’ and then run ‘find-file’ and open the file, but this +requires retyping the portion of the file name you already typed. This +process can be streamlined with ‘embark-become’: while still in the +‘switch-to-buffer’ you can run ‘embark-become’ and effectively make the +‘switch-to-buffer’ command become ‘find-file’ for this run. + + You can bind ‘embark-become’ to a key in ‘minibuffer-local-map’, but +it is also available as an action under the letter ‘B’ (uppercase), so +you don’t need a binding if you already have one for ‘embark-act’. So, +assuming I have ‘embark-act’ bound to, say, ‘C-.’, once I realize I +haven’t open the file I can type ‘C-. B C-x C-f’ to have +‘switch-to-buffer’ become ‘find-file’ without losing what I have already +typed in the minibuffer. + + But for even more convenience, ‘embark-become’ offers shorter key +bindings for commands you are likely to want the current command to +become. When you use ‘embark-become’ it looks for the current command +in all keymaps named in the list ‘embark-become-keymaps’ and then +activates all keymaps that contain it. For example, the default value +of ‘embark-become-keymaps’ contains a keymap +‘embark-become-file+buffer-map’ with bindings for several commands +related to files and buffers, in particular, it binds ‘switch-to-buffer’ +to ‘b’ and ‘find-file’ to ‘f’. So when I accidentally try to switch to +a buffer for a file I haven’t opened yet, ‘embark-become’ finds that the +command I ran, ‘switch-to-buffer’, is in the keymap +‘embark-become-file+buffer-map’, so it activates that keymap (and any +others that also contain a binding for ‘switch-to-buffer’). The end +result is that I can type ‘C-. B f’ to switch to ‘find-file’. + + +File: docJMGGb8.info, Node: Quick start, Next: Advanced configuration, Prev: Overview, Up: Top + +2 Quick start +************* + +The easiest way to install Embark is from GNU ELPA, just run ‘M-x +package-install RET embark RET’. (It is also available on MELPA.) It +is highly recommended to also install Marginalia +(https://github.com/minad/marginalia) (also available on GNU ELPA), so +that Embark can offer you preconfigured actions in more contexts. For +‘use-package’ users, the following is a very reasonable starting +configuration: + + (use-package marginalia + :ensure t + :config + (marginalia-mode)) + + (use-package embark + :ensure t + + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc + ;; strategy, if you want to see the documentation from multiple providers. + (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) + ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) + + :config + + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + + ;; Consult users will also want the embark-consult package. + (use-package embark-consult + :ensure t ; only need to install it, embark loads it after consult if found + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + + About the suggested key bindings for ‘embark-act’ and ‘embark-dwim’: + • Those key bindings are unlikely to work in the terminal, but + terminal users are probably well aware of this and will know to + select different bindings. + • The suggested ‘C-.’ binding is used by default in (at least some + installations of) GNOME to input emojis, and Emacs doesn’t even get + a chance to respond to the binding. You can select a different key + binding for ‘embark-act’ or use ‘ibus-setup’ to change the shortcut + for emoji insertion (Emacs 29 will likely use ‘C-x 8 e e’, in case + you want to set the same one system-wide). + • The suggested alternative of ‘M-.’ for ‘embark-dwim’ is bound by + default to ‘xref-find-definitions’. That is a very useful command + but overwriting it with ‘embark-dwim’ is sensible since in Embark’s + default configuration, ‘embark-dwim’ will also find the definition + of the identifier at point. (Note that ‘xref-find-definitions’ + with a prefix argument prompts you for an identifier, ‘embark-dwim’ + does not cover this case). + + Other Embark commands such as ‘embark-act-all’, ‘embark-become’, +‘embark-collect’, and ‘embark-export’ can be run through ‘embark-act’ as +actions bound to ‘A’, ‘B’, ‘S’ (for “snapshot”), and ‘E’ respectively, +and thus don’t really need a dedicated key binding, but feel free to +bind them directly if you so wish. If you do choose to bind them +directly, you’ll probably want to bind them in ‘minibuffer-local-map’, +since they are most useful in the minibuffer (in fact, ‘embark-become’ +only works in the minibuffer). + + The command ‘embark-dwim’ executes the default action at point. +Another good keybinding for ‘embark-dwim’ is ‘M-.’ since ‘embark-dwim’ +acts like ‘xref-find-definitions’ on the symbol at point. ‘C-.’ can be +seen as a right-click context menu at point and ‘M-.’ acts like +left-click. The keybindings are mnemonic, both act at the point (‘.’). + + Embark needs to know what your minibuffer completion system considers +to be the list of candidates and which one is the current candidate. +Embark works out of the box if you use Emacs’s default tab completion, +the built-in ‘icomplete-mode’ or ‘fido-mode’, or the third-party +packages Vertico (https://github.com/minad/vertico) or Ivy +(https://github.com/abo-abo/swiper). + + If you are a Helm (https://emacs-helm.github.io/helm/) or Ivy +(https://github.com/abo-abo/swiper) user you are unlikely to want Embark +since those packages include comprehensive functionality for acting on +minibuffer completion candidates. (Embark does come with Ivy +integration despite this.) + + +File: docJMGGb8.info, Node: Advanced configuration, Next: How does Embark call the actions?, Prev: Quick start, Up: Top + +3 Advanced configuration +************************ + +* Menu: + +* Showing information about available targets and actions:: +* Selecting commands via completions instead of key bindings:: +* Quitting the minibuffer after an action:: +* Running some setup after injecting the target:: +* Running hooks before, after or around an action: Running hooks before after or around an action. +* Creating your own keymaps:: +* Defining actions for new categories of targets:: + + +File: docJMGGb8.info, Node: Showing information about available targets and actions, Next: Selecting commands via completions instead of key bindings, Up: Advanced configuration + +3.1 Showing information about available targets and actions +=========================================================== + +By default, if you run ‘embark-act’ and do not immediately select an +action, after a short delay Embark will pop up a buffer called ‘*Embark +Actions*’ containing a list of available actions with their key +bindings. You can scroll that buffer with the mouse of with the usual +commands ‘scroll-other-window’ and ‘scroll-other-window-down’ (bound by +default to ‘C-M-v’ and ‘C-M-S-v’). + + That functionality is provided by the ‘embark-mixed-indicator’, but +Embark has other indicators that can provide information about the +target and its type, what other targets you can cycle to, and which +actions have key bindings in the action map for the current type of +target. Any number of indicators can be active at once and the user +option ‘embark-indicators’ should be set to a list of the desired +indicators. + + Embark comes with the following indicators: + + • ‘embark-minimal-indicator’: shows a messages in the echo area or + minibuffer prompt showing the current target and the types of all + targets starting with the current one; this one is on by default. + + • ‘embark-highlight-indicator’: highlights the target at point; also + on by default. + + • ‘embark-verbose-indicator’: displays a table of actions and their + key bindings in a buffer; this is not on by default, in favor of + the mixed indicator described next. + + • ‘embark-mixed-indicator’: starts out by behaving as the minimal + indicator but after a short delay acts as the verbose indicator; + this is on by default. + + • ‘embark-isearch-highlight-indicator’: this only does something when + the current target is the symbol at point, in which case it lazily + highlights all occurrences of that symbol in the current buffer, + like isearch; also on by default. + + Users of the popular which-key +(https://github.com/justbur/emacs-which-key) package may prefer to use +the ‘embark-which-key-indicator’ from the Embark wiki +(https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt). +Just copy its definition from the wiki into your configuration and +customize the ‘embark-indicators’ user option to exclude the mixed and +verbose indicators and to include ‘embark-which-key-indicator’. + + +File: docJMGGb8.info, Node: Selecting commands via completions instead of key bindings, Next: Quitting the minibuffer after an action, Prev: Showing information about available targets and actions, Up: Advanced configuration + +3.2 Selecting commands via completions instead of key bindings +============================================================== + +As an alternative to reading the list of actions in the verbose or mixed +indicators (see the previous section for a description of these), you +can press the ‘embark-help-key’, which is ‘C-h’ by default (but you may +prefer ‘?’ to free up ‘C-h’ for use as a prefix) after running +‘embark-act’. Pressing the help key will prompt you for the name of an +action with completion (but feel free to enter a command that is not +among the offered candidates!), and will also remind you of the key +bindings. You can press ‘embark-keymap-prompter-key’, which is ‘@’ by +default, at the prompt and then one of the key bindings to enter the +name of the corresponding action. + + You may think that with the ‘*Embark Actions*’ buffer popping up to +remind you of the key bindings you’d never want to use completion to +select an action by name, but personally I find that typing a small +portion of the action name to narrow down the list of candidates feels +significantly faster than visually scanning the entire list of actions. + + If you find you prefer entering actions that way, you can configure +embark to always prompt you for actions by setting the variable +‘embark-prompter’ to ‘embark-completing-read-prompter’. + + +File: docJMGGb8.info, Node: Quitting the minibuffer after an action, Next: Running some setup after injecting the target, Prev: Selecting commands via completions instead of key bindings, Up: Advanced configuration + +3.3 Quitting the minibuffer after an action +=========================================== + +By default, if you call ‘embark-act’ from the minibuffer it quits the +minibuffer after performing the action. You can change this by setting +the user option ‘embark-quit-after-action’ to ‘nil’. Having +‘embark-act’ _not_ quit the minibuffer can be useful to turn commands +into little “thing managers”. For example, you can use ‘find-file’ as a +little file manager or ‘describe-package’ as a little package manager: +you can run those commands, perform a series of actions, and then quit +the command. + + If you want to control the quitting behavior in a fine-grained manner +depending on the action, you can set ‘embark-quit-after-action’ to an +alist, associating commands to either ‘t’ for quitting or ‘nil’ for not +quitting. When using an alist, you can use the special key ‘t’ to +specify the default behavior. For example, to specify that by default +actions should not quit the minibuffer but that using ‘kill-buffer’ as +an action should quit, you can use the following configuration: + + (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) + + The variable ‘embark-quit-after-action’ only specifies a default, +that is, it only controls whether or not ‘embark-act’ quits the +minibuffer when you call it without a prefix argument, and you can +select the opposite behavior to what the variable says by calling +‘embark-act’ with ‘C-u’. Also note that both the variable +‘embark-quit-after-action’ and ‘C-u’ have no effect when you call +‘embark-act’ outside the minibuffer. + + If you find yourself using the quitting and non-quitting variants of +‘embark-act’ about equally often, independently of the action, you may +prefer to simply have separate commands for them instead of a single +command that you call with ‘C-u’ half the time. You could, for example, +keep the default exiting behavior of ‘embark-act’ and define a +non-quitting version as follows: + + (defun embark-act-noquit () + "Run action but don't quit the minibuffer afterwards." + (interactive) + (let ((embark-quit-after-action nil)) + (embark-act))) + + +File: docJMGGb8.info, Node: Running some setup after injecting the target, Next: Running hooks before after or around an action, Prev: Quitting the minibuffer after an action, Up: Advanced configuration + +3.4 Running some setup after injecting the target +================================================= + +You can customize what happens after the target is inserted at the +minibuffer prompt of an action. There are +‘embark-target-injection-hooks’, that are run by default after injecting +the target into the minibuffer. The variable +‘embark-target-injection-hooks’ is an alist associating commands to +their setup hooks. There are two special keys: if no setup hook is +specified for a given action, the hook associated to ‘t’ is run; and the +hook associated to ‘:always’ is run regardless of the action. (This +variable used to have the less explicit name of +‘embark-setup-action-hooks’, so please update your configuration.) + + For example, consider using ‘shell-command’ as an action during file +completion. It would be useful to insert a space before the target file +name and to leave the point at the beginning, so you can immediately +type the shell command to run on that file. That’s why in Embark’s +default configuration there is an entry in +‘embark-target-injection-hooks’ associating ‘shell-command’ to a hook +that includes ‘embark--shell-prep’, a simple helper function that quotes +all the spaces in the file name, inserts an extra space at the beginning +of the line and leaves point to the left of it. + + Now, the preparation that ‘embark--shell-prep’ does would be useless +if Embark did what it normally does after it inserts the target of the +action at the minibuffer prompt, which is to “press ‘RET’” for you, +accepting the target as is; if Embark did that for ‘shell-command’ you +wouldn’t get a chance to type in the command to execute! That is why in +Embark’s default configuration the entry for ‘shell-command’ in +‘embark-target-injection-hooks’ also contains the function +‘embark--allow-edit’. + + Embark used to have a dedicated variable ‘embark-allow-edit-actions’ +to which you could add commands for which Embark should forgo pressing +‘RET’ for you after inserting the target. Since its effect can also be +achieved via the general ‘embark-target-injection-hooks’ mechanism, that +variable has been removed to simply Embark. Be sure to update your +configuration; if you had something like: + + (add-to-list 'embark-allow-edit-actions 'my-command) + + you should replace it with: + + (push 'embark--allow-edit + (alist-get 'my-command embark-target-injection-hooks)) + + Also note that while you could abuse ‘embark--allow-edit’ so that you +have to confirm “dangerous” actions such as ‘delete-file’, it is better +to implement confirmation by adding the ‘embark--confirm’ function to +the appropriate entry of a different hook alist, namely, +‘embark-pre-action-hooks’. + + Besides ‘embark--allow-edit’, Embark comes with another function that +is of general utility in action setup hooks: ‘embark--ignore-target’. +Use it for commands that do prompt you in the minibuffer but for which +inserting the target would be inappropriate. This is not a common +situation but does occasionally arise. For example it is used by +default for ‘shell-command-on-region’: that command is used as an action +for region targets, and it prompts you for a shell command; you +typically do _not_ want the target, that is the contents of the region, +to be entered at that prompt! + + +File: docJMGGb8.info, Node: Running hooks before after or around an action, Next: Creating your own keymaps, Prev: Running some setup after injecting the target, Up: Advanced configuration + +3.5 Running hooks before, after or around an action +=================================================== + +Embark has three variables, ‘embark-pre-action-hooks’, +‘embark-post-action-hooks’ and ‘embark-around-action-hooks’, which are +alists associating commands to hooks that should run before or after or +as around advice for the command when used as an action. As with +‘embark-target-injection-hooks’, there are two special keys for the +alists: ‘t’ designates the default hook to run when no specific hook is +specified for a command; and the hook associated to ‘:always’ runs +regardless. + + The default values of those variables are fairly extensive, adding +creature comforts to make running actions a smooth experience. Embark +comes with several functions intended to be added to these hooks, and +used in the default values of ‘embark-pre-action-hooks’, +‘embark-post-action-hooks’ and ‘embark-around-action-hooks’. + + For pre-action hooks: + +‘embark--confirm’ + Prompt the user for confirmation before executing the action. This + is used be default for commands deemed “dangerous”, or, more + accurately, hard to undo, such as ‘delete-file’ and ‘kill-buffer’. + +‘embark--unmark-target’ + Unmark the active region. Use this for commands you want to act on + the region contents but without the region being active. The + default configuration uses this function as a pre-action hook for + ‘occur’ and ‘query-replace’, for example, so that you can use them + as actions with region targets to search the whole buffer for the + text contained in the region. Without this pre-action hook using + ‘occur’ as an action for a region target would be pointless: it + would search for the the region contents _in the region_, + (typically, due to the details of regexps) finding only one match! + +‘embark--beginning-of-target’ + Move to the beginning of the target (for targets that report + bounds). This is used by default for backward motion commands such + as ‘backward-sexp’, so that they don’t accidentally leave you on + the current target. + +‘embark--end-of-target’ + Move to the end of the target. This is used similarly to the + previous function, but also for commands that act on the last + s-expression like ‘eval-last-sexp’. This allow you to act on an + s-expression from anywhere inside it and still use ‘eval-last-sexp’ + as an action. + +‘embark--xref-push-markers’ + Push the current location on the xref marker stack. Use this for + commands that take you somewhere and for which you’d like to be + able to come back to where you were using ‘xref-pop-marker-stack’. + This is used by default for ‘find-library’. + + For post-action hooks: + +‘embark--restart’ + Restart the command currently prompting in the minibuffer, so that + the list of completion candidates is updated. This is useful as a + post action hook for commands that delete or rename a completion + candidate; for example the default value of + ‘embark-post-action-hooks’ uses it for ‘delete-file’, + ‘kill-buffer’, ‘rename-file’, ‘rename-buffer’, etc. + + For around-action hooks: + +‘embark--mark-target’ + Save existing mark and point location, mark the target and run the + action. Most targets at point outside the minibuffer report which + region of the buffer they correspond to (this is the information + used by ‘embark-highlight-indicator’ to know what portion of the + buffer to highlight); this function marks that region. It is + useful as an around action hook for commands that expect a region + to be marked, for example, it is used by default for + ‘indent-region’ so that it works on s-expression targets, or for + ‘fill-region’ so that it works on paragraph targets. + +‘embark--cd’ + Run the action with ‘default-directory’ set to the directory + associated to the current target. The target should be of type + ‘file’, ‘buffer’, ‘bookmark’ or ‘library’, and the associated + directory is what you’d expect in each case. + +‘embark--narrow-to-target’ + Run the action with buffer narrowed to current target. Use this as + an around hook to localize the effect of actions that don’t already + work on just the region. In the default configuration it is used + for ‘repunctuate-sentences’. + +‘embark--save-excursion’ + Run the action restoring point at the end. The current default + configuration doesn’t use this but it is available for users. + + +File: docJMGGb8.info, Node: Creating your own keymaps, Next: Defining actions for new categories of targets, Prev: Running hooks before after or around an action, Up: Advanced configuration + +3.6 Creating your own keymaps +============================= + +All internal keymaps are defined with the standard helper macro +‘defvar-keymap’. For example a simple version of the file action keymap +could be defined as follows: + + (defvar-keymap embark-file-map + :doc "Example keymap with a few file actions" + :parent embark-general-map + "d" #'delete-file + "r" #'rename-file + "c" #'copy-file) + + These action keymaps are perfectly normal Emacs keymaps. You may +want to inherit from the ‘embark-general-map’ if you want to access the +default Embark actions. Note that ‘embark-collect’ and ‘embark-export’ +are also made available via ‘embark-general-map’. + + +File: docJMGGb8.info, Node: Defining actions for new categories of targets, Prev: Creating your own keymaps, Up: Advanced configuration + +3.7 Defining actions for new categories of targets +================================================== + +It is easy to configure Embark to provide actions for new types of +targets, either in the minibuffer or outside it. I present below two +very detailed examples of how to do this. At several points I’ll +explain more than one way to proceed, typically with the easiest option +first. I include the alternative options since there will be similar +situations where the easiest option is not available. + +* Menu: + +* New minibuffer target example - tab-bar tabs:: +* New target example in regular buffers - short Wikipedia links:: + + +File: docJMGGb8.info, Node: New minibuffer target example - tab-bar tabs, Next: New target example in regular buffers - short Wikipedia links, Up: Defining actions for new categories of targets + +3.7.1 New minibuffer target example - tab-bar tabs +-------------------------------------------------- + +As an example, take the new tab bars +(https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html) +from Emacs 27. I’ll explain how to configure Embark to offer +tab-specific actions when you use the tab-bar-mode commands that mention +tabs by name. The configuration explained here is now built-in to +Embark (and Marginalia), but it’s still a good self-contained example. +In order to setup up tab actions you would need to: (1) make sure Embark +knows those commands deal with tabs, (2) define a keymap for tab actions +and configure Embark so it knows that’s the keymap you want. + + 1. Telling Embark about commands that prompt for tabs by name + + For step (1), it would be great if the ‘tab-bar-mode’ commands + reported the completion category ‘tab’ when asking you for a tab + with completion. (All built-in Emacs commands that prompt for file + names, for example, do have metadata indicating that they want a + ‘file’.) They do not, unfortunately, and I will describe a couple + of ways to deal with this. + + Maybe the easiest thing is to configure Marginalia + (https://github.com/minad/marginalia) to enhance those commands. + All of the ‘tab-bar-*-tab-by-name’ commands have the words “tab by + name” in the minibuffer prompt, so you can use: + + (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) + + That’s it! But in case you are ever in a situation where you don’t + already have commands that prompt for the targets you want, I’ll + describe how writing your own command with appropriate ‘category’ + metadata looks: + + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (completing-read + "Tabs: " + (lambda (string predicate action) + (if (eq action 'metadata) + '(metadata (category . tab)) + (complete-with-action + action tab-list string predicate))))))) + (tab-bar-select-tab-by-name tab)) + + As you can see, the built-in support for setting the category + meta-datum is not very easy to use or pretty to look at. To help + with this I recommend the ‘consult--read’ function from the + excellent Consult (https://github.com/minad/consult/) package. + With that function we can rewrite the command as follows: + + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (consult--read tab-list + :prompt "Tabs: " + :category 'tab)))) + (tab-bar-select-tab-by-name tab)) + + Much nicer! No matter how you define the ‘my-select-tab-by-name’ + command, the first approach with Marginalia and prompt detection + has the following advantages: you get the ‘tab’ category for all + the ‘tab-bar-*-bar-by-name’ commands at once, also, you enhance + built-in commands, instead of defining new ones. + + 2. Defining and configuring a keymap for tab actions + + Let’s say we want to offer select, rename and close actions for + tabs (in addition to Embark general actions, such as saving the tab + name to the kill-ring, which you get for free). Then this will do: + + (defvar-keymap embark-tab-actions + :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + :parent embark-general-map + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + + (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) + + What if after using this for a while you feel closing the tab + without confirmation is dangerous? You have a couple of options: + + 1. You can keep using the ‘tab-bar-close-tab-by-name’ command, + but have Embark ask you for confirmation: + (push #'embark--confirm + (alist-get 'tab-bar-close-tab-by-name + embark-pre-action-hooks)) + + 2. You can write your own command that prompts for confirmation + and use that instead of ‘tab-bar-close-tab-by-name’ in the + above keymap: + (defun my-confirm-close-tab-by-name (tab) + (interactive "sTab to close: ") + (when (y-or-n-p (format "Close tab '%s'? " tab)) + (tab-bar-close-tab-by-name tab))) + + Notice that this is a command you can also use directly from + ‘M-x’ independently of Embark. Using it from ‘M-x’ leaves + something to be desired, though, since you don’t get + completion for the tab names. You can fix this if you wish as + described in the previous section. + + +File: docJMGGb8.info, Node: New target example in regular buffers - short Wikipedia links, Prev: New minibuffer target example - tab-bar tabs, Up: Defining actions for new categories of targets + +3.7.2 New target example in regular buffers - short Wikipedia links +------------------------------------------------------------------- + +Say you want to teach Embark to treat text of the form +‘wikipedia:Garry_Kasparov’ in any regular buffer as a link to Wikipedia, +with actions to open the Wikipedia page in eww or an external browser or +to save the URL of the page in the kill-ring. We can take advantage of +the actions that Embark has preconfigured for URLs, so all we need to do +is teach Embark that ‘wikipedia:Garry_Kasparov’ stands for the URL +‘https://en.wikipedia.org/wiki/Garry_Kasparov’. + + You can be as fancy as you want with the recognized syntax. Here, to +keep the example simple, I’ll assume the link matches the regexp +‘wikipedia:[[:alnum:]_]+’. We will write a function that looks for a +match surrounding point, and returns a dotted list of the form ‘'(url +URL-OF-THE-PAGE START . END)’ where ‘START’ and ‘END’ are the buffer +positions bounding the target, and are used by Embark to highlight it if +you have ‘embark-highlight-indicator’ included in the list +‘embark-indicators’. (There are a couple of other options for the +return value of a target finder: the bounding positions are optional and +a single target finder is allowed to return multiple targets; see the +documentation for ‘embark-target-finders’ for details.) + + (defun my-short-wikipedia-link () + "Target a link at point of the form wikipedia:Page_Name." + (save-excursion + (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) + (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + (str (buffer-substring-no-properties start end))) + (save-match-data + (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + `(url + ,(format "https://en.wikipedia.org/wiki/%s" + (match-string 1 str)) + ,start . ,end)))))) + + (add-to-list 'embark-target-finders 'my-short-wikipedia-link) + + +File: docJMGGb8.info, Node: How does Embark call the actions?, Next: Embark Marginalia and Consult, Prev: Advanced configuration, Up: Top + +4 How does Embark call the actions? +*********************************** + +Embark actions are normal Emacs commands, that is, functions with an +interactive specification. In order to execute an action, Embark calls +the command with ‘call-interactively’, so the command reads user input +exactly as if run directly by the user. For example the command may +open a minibuffer and read a string (‘read-from-minibuffer’) or open a +completion interface (‘completing-read’). If this happens, Embark takes +the target string and inserts it automatically into the minibuffer, +simulating user input this way. After inserting the string, Embark +exits the minibuffer, submitting the input. (The immediate minibuffer +exit can be disabled for specific actions in order to allow editing the +input; this is done by adding the ‘embark--allow-edit’ function to the +appropriate entry of ‘embark-target-injection-hooks’). Embark inserts +the target string at the first minibuffer opened by the action command, +and if the command happens to prompt the user for input more than once, +the user still interacts with the second and further prompts in the +normal fashion. Note that if a command does not prompt the user for +input in the minibuffer, Embark still allows you to use it as an action, +but of course, never inserts the target anywhere. (There are plenty of +examples in the default configuration of commands that do not prompt the +user bound to keys in the action maps, most of the region actions, for +instance.) + + This is how Embark manages to reuse normal commands as actions. The +mechanism allows you to use as Embark actions commands that were not +written with Embark in mind (and indeed almost all actions that are +bound by default in Embark’s action keymaps are standard Emacs +commands). It also allows you to write new custom actions in such a way +that they are useful even without Embark. + + Staring from version 28.1, Emacs has a variable +‘y-or-n-p-use-read-key’, which when set to ‘t’ causes ‘y-or-n-p’ to use +‘read-key’ instead of ‘read-from-minibuffer’. Setting +‘y-or-n-p-use-read-key’ to ‘t’ is recommended for Embark users because +it keeps Embark from attempting to insert the target at a ‘y-or-n-p’ +prompt, which would almost never be sensible. Also consider this as a +warning to structure your own action commands so that if they use +‘y-or-n-p’, they do so only after the prompting for the target. + + Here is a simple example illustrating the various ways of reading +input from the user mentioned above. Bind the following commands to the +‘embark-symbol-map’ to be used as actions, then put the point on some +symbol and run them with ‘embark-act’: + + (defun example-action-command1 () + (interactive) + (message "The input was `%s'." (read-from-minibuffer "Input: "))) + + (defun example-action-command2 (arg input1 input2) + (interactive "P\nsInput 1: \nsInput 2: ") + (message "The first input %swas `%s', and the second was `%s'." + (if arg "truly " "") + input1 + input2)) + + (defun example-action-command3 () + (interactive) + (message "Your selection was `%s'." + (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + + (defun example-action-command4 () + (interactive) + (message "I don't prompt you for input and thus ignore the target!")) + + (keymap-set embark-symbol-map "X 1" #'example-action-command1) + (keymap-set embark-symbol-map "X 2" #'example-action-command2) + (keymap-set embark-symbol-map "X 3" #'example-action-command3) + (keymap-set embark-symbol-map "X 4" #'example-action-command4) + + Also note that if you are using the key bindings to call actions, you +can pass prefix arguments to actions in the normal way. For example, +you can use ‘C-u X2’ with the above demonstration actions to make the +message printed by ‘example-action-command2’ more emphatic. This +ability to pass prefix arguments to actions is useful for some actions +in the default configuration, such as ‘embark-shell-command-on-buffer’. + +* Menu: + +* Non-interactive functions as actions:: + + +File: docJMGGb8.info, Node: Non-interactive functions as actions, Up: How does Embark call the actions? + +4.1 Non-interactive functions as actions +======================================== + +Alternatively, Embark does support one other type of action: a +non-interactive function of a single argument. The target is passed as +argument to the function. For example: + + (defun example-action-function (target) + (message "The target was `%s'." target)) + + (keymap-set embark-symbol-map "X 4" #'example-action-function) + + Note that normally binding non-interactive functions in a keymap is +useless, since when attempting to run them using the key binding you get +an error message similar to “Wrong type argument: commandp, +example-action-function”. In general it is more flexible to write any +new Embark actions as commands, that is, as interactive functions, +because that way you can also run them directly, without Embark. But +there are a couple of reasons to use non-interactive functions as +actions: + + 1. You may already have the function lying around, and it is + convenient to simply reuse it. + + 2. For command actions the targets can only be simple string, with no + text properties. For certain advanced uses you may want the action + to receive a string _with_ some text properties, or even a + non-string target. + + +File: docJMGGb8.info, Node: Embark Marginalia and Consult, Next: Resources, Prev: How does Embark call the actions?, Up: Top + +5 Embark, Marginalia and Consult +******************************** + +Embark cooperates well with the Marginalia +(https://github.com/minad/marginalia) and Consult +(https://github.com/minad/consult) packages. Neither of those packages +is a dependency of Embark, but both are highly recommended companions to +Embark, for opposite reasons: Marginalia greatly enhances Embark’s +usefulness, while Embark can help enhance Consult. + + In the remainder of this section I’ll explain what exactly Marginalia +does for Embark, and what Embark can do for Consult. + +* Menu: + +* Marginalia:: +* Consult:: + + +File: docJMGGb8.info, Node: Marginalia, Next: Consult, Up: Embark Marginalia and Consult + +5.1 Marginalia +============== + +Embark comes with actions for symbols (commands, functions, variables +with actions such as finding the definition, looking up the +documentation, evaluating, etc.) in the ‘embark-symbol-map’ keymap, and +for packages (actions like install, delete, browse url, etc.) in the +‘embark-package-keymap’. + + Unfortunately Embark does not automatically offers you these keymaps +when relevant, because many built-in Emacs commands don’t report +accurate category metadata. For example, a command like +‘describe-package’, which reads a package name from the minibuffer, does +not have metadata indicating this fact. + + In an earlier Embark version, there were functions to supply this +missing metadata, but they have been moved to Marginalia, which augments +many Emacs command to report accurate category metadata. Simply +activating ‘marginalia-mode’ allows Embark to offer you the package and +symbol actions when appropriate again. Candidate annotations in the +Embark collect buffer are also provided by the Marginalia package: + + • If you install Marginalia and activate ‘marginalia-mode’, Embark + Collect buffers will use the Marginalia annotations automatically. + + • If you don’t install Marginalia, you will see only the annotations + that come with Emacs (such as key bindings in ‘M-x’, or the unicode + characters in ‘C-x 8 RET’). + + +File: docJMGGb8.info, Node: Consult, Prev: Marginalia, Up: Embark Marginalia and Consult + +5.2 Consult +=========== + +The excellent Consult package provides many commands that use minibuffer +completion, via the ‘completing-read’ function; plenty of its commands +can be considered enhanced versions of built-in Emacs commands, and some +are completely new functionality. One common enhancement provided in +all commands for which it makes sense is preview functionality, for +example ‘consult-buffer’ will show you a quick preview of a buffer +before you actually switch to it. + + If you use both Consult and Embark you should install the +‘embark-consult’ package which provides integration between the two. It +provides exporters for several Consult commands and also tweaks the +behavior of many Consult commands when used as actions with ‘embark-act’ +in subtle ways that you may not even notice, but make for a smoother +experience. You need only install it to get these benefits: Embark will +automatically load it after Consult if found. + + The ‘embark-consult’ package provides the following exporters: + + • You can use ‘embark-export’ from ‘consult-line’, ‘consult-outline’, + or ‘consult-mark’ to obtain an ‘occur-mode’ buffer. As with the + built-in ‘occur’ command you use that buffer to jump to a match and + after that, you can then use ‘next-error’ and ‘previous-error’ to + navigate to other matches. You can also press ‘e’ to activate + ‘occur-edit-mode’ and edit the matches in place! + + • You can export from any of the Consult asynchronous search + commands, ‘consult-grep’, ‘consult-git-grep’, or ‘consult-ripgrep’ + to get a ‘grep-mode’ buffer. Here too you can use ‘next-error’ and + ‘previous-error’ to navigate among matches, and, if you install the + wgrep + (http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el) + package, you can use it to edit the matches in place. + + In both cases, pressing ‘g’ will rerun the Consult command you had +exported from and re-enter the input you had typed (which is similar to +reverting but a little more flexible). You can then proceed to +re-export if that’s what you want, but you can also edit the input +changing the search terms or simply cancel if you see you are done with +that search. + + The ‘embark-consult’ also contains some candidates collectors that +allow you to run ‘embark-live’ to get a live-updating table of contents +for your buffer: + + • ‘embark-consult-outline-candidates’ produces the outline headings + of the current buffer, using ‘consult-outline’. + • ‘embark-consult-imenu-candidates’ produces the imenu items of the + current buffer, using ‘consult-imenu’. + • ‘embark-consult-imenu-or-outline-candidates’ is a simple + combination of the two previous functions: it produces imenu items + in buffers deriving from ‘prog-mode’ and otherwise outline + headings. + + The way to configure ‘embark-live’ (or ‘embark-collect’ and +‘embark-export’ for that matter) to use one of these function is to add +it at the end of the ‘embark-candidate-collectors’ list. The +‘embark-consult’ package by default adds the last one, which seems to be +the most sensible default. + + Besides those exporters and candidate collectors, the +‘embark-consult’ package provides many subtle tweaks and small +integrations between Embark and Consult. Some examples are: + + • The asynchronous search commands will start in the directory + associated to the Embark target if that target is a file, buffer, + bookmark or Emacs Lisp library. + + • For all other target types, a Consult search command + (asynchronous or not) will search for the text of the target + but leave the minibuffer open so you can interact with the + Consult command. + + • ‘consult-imenu’ will search for the target and take you directly to + the location if it matches a unique imenu entry, otherwise it will + leave the minibuffer open so you can navigate among the matches. + + +File: docJMGGb8.info, Node: Resources, Next: Contributions, Prev: Embark Marginalia and Consult, Up: Top + +6 Resources +*********** + +If you want to learn more about how others have used Embark here are +some links to read: + + • Fifteen ways to use Embark + (https://karthinks.com/software/fifteen-ways-to-use-embark/), a + blog post by Karthik Chikmagalur. + • Protesilaos Stavrou’s dotemacs (https://protesilaos.com/dotemacs/), + look for the section called “Extended minibuffer actions and more + (embark.el and prot-embark.el)” + + And some videos to watch: + + • Embark and my extras + (https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/) + by Protesilaos Stavrou. + • Embark – Key features and tweaks (https://youtu.be/qpoQiiinCtY) by + Raoul Comninos on the Emacs-Elements YouTube channel. + • Livestreamed: Adding an Embark context action to send a stream + message (https://youtu.be/WsxXr1ncukY) by Sacha Chua. + • System Crafters Live! - The Many Uses of Embark + (https://youtu.be/qk2Is_sC8Lk) by David Wilson. + • Marginalia, Consult and Embark by Mike Zamansky. + + +File: docJMGGb8.info, Node: Contributions, Next: Acknowledgments, Prev: Resources, Up: Top + +7 Contributions +*************** + +Contributions to Embark are very welcome. There is a wish list +(https://github.com/oantolin/embark/issues/95) for actions, target +finders, candidate collectors and exporters. For other ideas you have +for Embark, feel free to open an issue on the issue tracker +(https://github.com/oantolin/embark/issues). Any neat configuration +tricks you find might be a good fit for the wiki +(https://github.com/oantolin/embark/wiki). + + Code contributions are very welcome too, but since Embark is now on +GNU ELPA, copyright assignment to the FSF is required before you can +contribute code. + + +File: docJMGGb8.info, Node: Acknowledgments, Prev: Contributions, Up: Top + +8 Acknowledgments +***************** + +While I, Omar Antolín Camarena, have written most of the Embark code and +remain very stubborn about some of the design decisions, Embark has +received substantial help from a number of other people which this +document has neglected to mention for far too long. In particular, +Daniel Mendler has been absolutely invaluable, implementing several +important features, and providing a lot of useful advice. + + Code contributions: + + • Daniel Mendler (https://github.com/minad) + • Clemens Radermacher (https://github.com/clemera/) + • José Antonio Ortega Ruiz (https://codeberg.org/jao/) + • Itai Y. Efrat (https://github.com/iyefrat) + • a13 (https://github.com/a13) + • jakanakaevangeli (https://github.com/jakanakaevangeli) + • mihakam (https://github.com/mihakam) + • Brian Leung (https://github.com/leungbk) + • Karthik Chikmagalur (https://github.com/karthink) + • Roshan Shariff (https://github.com/roshanshariff) + • condy0919 (https://github.com/condy0919) + • Damien Cassou (https://github.com/DamienCassou) + • JimDBh (https://github.com/JimDBh) + + Advice and useful discussions: + + • Daniel Mendler (https://github.com/minad) + • Protesilaos Stavrou (https://gitlab.com/protesilaos/) + • Clemens Radermacher (https://github.com/clemera/) + • Howard Melman (https://github.com/hmelman/) + • Augusto Stoffel (https://github.com/astoff) + • Bruce d’Arcus (https://github.com/bdarcus) + • JD Smith (https://github.com/jdtsmith) + • Karthik Chikmagalur (https://github.com/karthink) + • jakanakaevangeli (https://github.com/jakanakaevangeli) + • Itai Y. Efrat (https://github.com/iyefrat) + • Mohsin Kaleem (https://github.com/mohkale) + + + +Tag Table: +Node: Top223 +Node: Overview1710 +Node: Acting on targets3019 +Node: The default action on a target8564 +Node: Working with sets of possible targets10474 +Node: Selecting some targets to make an ad hoc candidate set14562 +Node: embark-live a live-updating variant of embark-collect17662 +Node: Switching to a different command without losing what you've typed19360 +Node: Quick start21937 +Node: Advanced configuration26676 +Node: Showing information about available targets and actions27261 +Node: Selecting commands via completions instead of key bindings29873 +Node: Quitting the minibuffer after an action31480 +Node: Running some setup after injecting the target33936 +Node: Running hooks before after or around an action37552 +Node: Creating your own keymaps42431 +Node: Defining actions for new categories of targets43338 +Node: New minibuffer target example - tab-bar tabs44110 +Ref: Telling Embark about commands that prompt for tabs by name45016 +Ref: Defining and configuring a keymap for tab actions47879 +Node: New target example in regular buffers - short Wikipedia links49670 +Node: How does Embark call the actions?51933 +Node: Non-interactive functions as actions56276 +Node: Embark Marginalia and Consult57633 +Node: Marginalia58357 +Node: Consult59864 +Node: Resources64045 +Node: Contributions65192 +Node: Acknowledgments65905 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/embark-0.22.1/embark.texi b/elpa/embark-0.22.1/embark.texi @@ -0,0 +1,1435 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename embark.info +@settitle Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +@documentencoding UTF-8 +@documentlanguage en +@c %**end of header + +@dircategory Emacs misc features +@direntry +* Embark: (embark). Emacs Mini-Buffer Actions Rooted in Keymaps. +@end direntry + +@finalout +@titlepage +@title Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +@author Omar Antolín Camarena +@end titlepage + +@contents + +@ifnottex +@node Top +@top Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +@end ifnottex + +@menu +* Overview:: +* Quick start:: +* Advanced configuration:: +* How does Embark call the actions?:: +* Embark, Marginalia and Consult: Embark Marginalia and Consult. +* Resources:: +* Contributions:: +* Acknowledgments:: + +@detailmenu +--- The Detailed Node Listing --- + +Overview + +* Acting on targets:: +* The default action on a target:: +* Working with sets of possible targets:: +* Switching to a different command without losing what you've typed:: + +Working with sets of possible targets + +* Selecting some targets to make an ad hoc candidate set:: +* @samp{embark-live} a live-updating variant of @samp{embark-collect}:: + +Advanced configuration + +* Showing information about available targets and actions:: +* Selecting commands via completions instead of key bindings:: +* Quitting the minibuffer after an action:: +* Running some setup after injecting the target:: +* Running hooks before, after or around an action: Running hooks before after or around an action. +* Creating your own keymaps:: +* Defining actions for new categories of targets:: + +Defining actions for new categories of targets + +* New minibuffer target example - tab-bar tabs:: +* New target example in regular buffers - short Wikipedia links:: + +How does Embark call the actions? + +* Non-interactive functions as actions:: + +Embark, Marginalia and Consult + +* Marginalia:: +* Consult:: + +@end detailmenu +@end menu + +@node Overview +@chapter Overview + +Embark makes it easy to choose a command to run based on what is near +point, both during a minibuffer completion session (in a way familiar +to Helm or Counsel users) and in normal buffers. Bind the command +@samp{embark-act} to a key and it acts like prefix-key for a keymap of +@emph{actions} (commands) relevant to the @emph{target} around point. With point on +an URL in a buffer you can open the URL in a browser or eww or +download the file it points to. If while switching buffers you spot an +old one, you can kill it right there and continue to select another. +Embark comes preconfigured with over a hundred actions for common +types of targets such as files, buffers, identifiers, s-expressions, +sentences; and it is easy to add more actions and more target types. +Embark can also collect all the candidates in a minibuffer to an +occur-like buffer or export them to a buffer in a major-mode specific +to the type of candidates, such as dired for a set of files, ibuffer +for a set of buffers, or customize for a set of variables. + +@menu +* Acting on targets:: +* The default action on a target:: +* Working with sets of possible targets:: +* Switching to a different command without losing what you've typed:: +@end menu + +@node Acting on targets +@section Acting on targets + +You can think of @samp{embark-act} as a keyboard-based version of a +right-click contextual menu. The @samp{embark-act} command (which you should +bind to a convenient key), acts as a prefix for a keymap offering you +relevant @emph{actions} to use on a @emph{target} determined by the context: + +@itemize +@item +In the minibuffer, the target is the current top completion +candidate. +@item +In the @samp{*Completions*} buffer the target is the completion at point. +@item +In a regular buffer, the target is the region if active, or else the +file, symbol, URL, s-expression or defun at point. +@end itemize + +Multiple targets can be present at the same location and you can cycle +between them by repeating the @samp{embark-act} key binding. The type of +actions offered depend on the type of the target. Here is a sample of +a few of the actions offered in the default configuration: + +@itemize +@item +For files you get offered actions like deleting, copying, +renaming, visiting in another window, running a shell command on the +file, etc. +@item +For buffers the actions include switching to or killing the buffer. +@item +For package names the actions include installing, removing or +visiting the homepage. +@item +For Emacs Lisp symbols the actions include finding the definition, +looking up documentation, evaluating (which for a variable +immediately shows the value, but for a function lets you pass it +some arguments first). There are some actions specific to variables, +such as setting the value directly or though the customize system, +and some actions specific to commands, such as binding it to a key. +@end itemize + +By default when you use @samp{embark-act} if you don't immediately select an +action, after a short delay Embark will pop up a buffer showing a list +of actions and their corresponding key bindings. If you are using +@samp{embark-act} outside the minibuffer, Embark will also highlight the +current target. These behaviors are configurable via the variable +@samp{embark-indicators}. Instead of selecting an action via its key binding, +you can select it by name with completion by typing @samp{C-h} after +@samp{embark-act}. + +Everything is easily configurable: determining the current target, +classifying it, and deciding which actions are offered for each type +in the classification. The above introduction just mentions part of +the default configuration. + +Configuring which actions are offered for a type is particularly easy +and requires no programming: the variable @samp{embark-keymap-alist} +associates target types with variables containing keymaps, and those +keymaps containing bindings for the actions. (To examine the available +categories and their associated keymaps, you can use @samp{C-h v +embark-keymap-alist} or customize that variable.) For example, in the +default configuration the type @samp{file} is associated with the symbol +@samp{embark-file-map}. That symbol names a keymap with single-letter key +bindings for common Emacs file commands, for instance @samp{c} is bound to +@samp{copy-file}. This means that if you are in the minibuffer after running +a command that prompts for a file, such as @samp{find-file} or @samp{rename-file}, +you can copy a file by running @samp{embark-act} and then pressing @samp{c}. + +These action keymaps are very convenient but not strictly necessary +when using @samp{embark-act}: you can use any command that reads from the +minibuffer as an action and the target of the action will be inserted +at the first minibuffer prompt. After running @samp{embark-act} all of your +key bindings and even @samp{execute-extended-command} can be used to run a +command. For example, if you want to replace all occurrences of the +symbol at point, just use @samp{M-%} as the action, there is no need to bind +@samp{query-replace} in one of Embark's keymaps. Also, those action keymaps +are normal Emacs keymaps and you should feel free to bind in them +whatever commands you find useful as actions and want to be available +through convenient bindings. + +The actions in @samp{embark-general-map} are available no matter what type +of completion you are in the middle of. By default this includes +bindings to save the current candidate in the kill ring and to insert +the current candidate in the previously selected buffer (the buffer +that was current when you executed a command that opened up the +minibuffer). + +Emacs's minibuffer completion system includes metadata indicating the +@emph{category} of what is being completed. For example, @samp{find-file}'s +metadata indicates a category of @samp{file} and @samp{switch-to-buffer}'s metadata +indicates a category of @samp{buffer}. Embark has the related notion of the +@emph{type} of a target for actions, and by default when category metadata +is present it is taken to be the type of minibuffer completion +candidates when used as targets. Emacs commands often do not set +useful category metadata so the @uref{https://github.com/minad/marginalia, Marginalia} package, which supplies +this missing metadata, is highly recommended for use with Embark. + +Embark's default configuration has actions for the following target +types: files, buffers, symbols, packages, URLs, bookmarks, and as a +somewhat special case, actions for when the region is active. You can +read about the @uref{https://github.com/oantolin/embark/wiki/Default-Actions, default actions and their key bindings} on the GitHub +project wiki. + +@node The default action on a target +@section The default action on a target + +Embark has a notion of default action for a target: + +@itemize +@item +If the target is a minibuffer completion candidate, then the default +action is whatever command opened the minibuffer in the first place. +For example if you run @samp{kill-buffer}, then the default action will be +to kill buffers. +@item +If the target comes from a regular buffer (i.e., not a minibuffer), +then the default action is whatever is bound to @samp{RET} in the keymap of +actions for that type of target. For example, in Embark's default +configuration for a URL found at point the default action is +@samp{browse-url}, because @samp{RET} is bound to @samp{browse-url} in the @samp{embark-url-map} +keymap. +@end itemize + +To run the default action you can press @samp{RET} after running @samp{embark-act}. +Note that if there are several different targets at a given location, +each has its own default action, so first cycle to the target you want +and then press @samp{RET} to run the corresponding default action. + +There is also @samp{embark-dwim} which runs the default action for the first +target found. It's pretty handy in non-minibuffer buffers: with +Embark's default configuration it will: + +@itemize +@item +Open the file at point. +@item +Open the URL at point in a web browser (using the @samp{browse-url} +command). +@item +Compose a new email to the email address at point. +@item +In an Emacs Lisp buffer, if point is on an opening parenthesis or +right after a closing one, it will evaluate the corresponding +expression. +@item +Go to the definition of an Emacs Lisp function, variable or macro at +point. +@item +Find the file corresponding to an Emacs Lisp library at point. +@end itemize + +@node Working with sets of possible targets +@section Working with sets of possible targets + +Besides acting individually on targets, Embark lets you work +collectively on a set of target @emph{candidates}. For example, while you are +in the minibuffer the candidates are simply the possible completions +of your input. Embark provides three main commands to work on candidate +sets: + +@itemize +@item +The @samp{embark-act-all} command runs the same action on each of the +current candidates. It is just like using @samp{embark-act} on each +candidate in turn. (Because you can easily act on many more +candidates than you meant to, by default Embark asks you to confirm +uses of @samp{embark-act-all}; you can turn this off by setting the user +option @samp{embark-confirm-act-all} to @samp{nil}.) + +@item +The @samp{embark-collect} command produces a buffer listing all the current +candidates, for you to peruse and run actions on at your leisure. +The candidates are displayed as a list showing additional annotations. + +The Embark Collect buffer is somewhat ``dired-like'': you can select +and deselect candidates through the @samp{embark-select} action (bound to +@samp{SPC}). In an Embark Collect buffer @samp{embark-act} is bound to @samp{a} and +@samp{embark-act-all} is bound to @samp{A}; @samp{embark-act-all} will act on all +currently marked candidates if there any, and will act on all +candidates if none are marked. In particular, this means that @samp{a SPC} +will toggle whether the candidate at point is selected, and @samp{A SPC} +will select all candidates if none are selected, or deselect all +selected candidates if there are some. + +@item +The @samp{embark-export} command tries to open a buffer in an appropriate +major mode for the set of candidates. If the candidates are files +export produces a Dired buffer; if they are buffers, you get an +Ibuffer buffer; and if they are packages you get a buffer in +package menu mode. + +If you use the grepping commands from the @uref{https://github.com/minad/consult/, Consult} package, +@samp{consult-grep}, @samp{consult-git-grep} or @samp{consult-ripgrep}, then you should +install the @samp{embark-consult} package, which adds support for exporting a +list of grep results to an honest grep-mode buffer, on which you can +even use @uref{https://github.com/mhayashi1120/Emacs-wgrep, wgrep} if you wish. +@end itemize + +When in doubt choosing between exporting and collecting, a good rule +of thumb is to always prefer @samp{embark-export} since when an exporter to a +special major mode is available for a given type of target, it will be +more featureful than an Embark collect buffer, and if no such exporter +is configured the @samp{embark-export} command falls back to the generic +@samp{embark-collect}. + +These commands are always available as ``actions'' (although they do not +act on just the current target but on all candidates) for @samp{embark-act} +and are bound to @samp{A}, @samp{S} (for ``snapshot''), and @samp{E}, respectively, in +@samp{embark-general-map}. This means that you do not have to bind your own +key bindings for these (although you can, of course!), just a key +binding for @samp{embark-act}. + +In Embark Collect or Embark Export buffers that were obtained by +running @samp{embark-collect} or @samp{embark-export} from within a minibuffer +completion session, @samp{g} is bound to a command that restarts the +completion session, that is, the command that opened the minibuffer is +run again and the minibuffer contents restored. You can then interact +normally with the command, perhaps editing the minibuffer contents, +and, if you wish, you can rerun @samp{embark-collect} or @samp{embark-export} to get +an updated buffer. + +@menu +* Selecting some targets to make an ad hoc candidate set:: +* @samp{embark-live} a live-updating variant of @samp{embark-collect}:: +@end menu + +@node Selecting some targets to make an ad hoc candidate set +@subsection Selecting some targets to make an ad hoc candidate set + +The commands for working with sets of candidates just described, +namely @samp{embark-act-all}, @samp{embark-export} and @samp{embark-collect} by default +work with all candidates defined in the current context. For example, +in the minibuffer they operate on all currently completion candidates, +or in a dired buffer they work on all marked files (or all files if +none are marked). Embark also has a notion of @emph{selection}, where you can +accumulate an ad hoc list of targets for these commands to work on. + +The selection is controlled by using the @samp{embark-select} action (which +must be run as an action through @samp{embark-act}), bound to @samp{SPC} in +@samp{embark-general-map} so that it is always available. Calling this action +on a target toggles its membership in the current buffer's Embark +selection; that is, it adds it to selection if not selected and +removes it from the selection if it was selected. Whenever the +selection for a buffer is non-empty, the commands @samp{embark-act-all}, +@samp{embark-export} and @samp{embark-collect} will act on the selection. + +To deselect all selected targets, you can use the @samp{embark-select} action +through @samp{embark-act-all}, since this will run @samp{embark-select} on each +member of the current selection. Similarly if no targets are selected +and you are in a minibuffer completion session, running @samp{embark-select} +from @samp{embark-act-all} will select all the current completion candidates. + +This functionality is supported everywhere: + +@itemize +@item +In the minibuffer this gives a convenient way to act on several +completion candidates that don't follow any simple pattern: just go +through the completions selecting the ones you want, then use +@samp{embark-act-all}. For example, you could attach several files at once +to an email. +@item +For Embark Collect buffers this functionality enables a dired-like +workflow, in which you mark various candidates and apply an action +to all at once. (It supersedes a previous ad hoc dired-like +interface that was implemented only in Embark Collect buffers, with +a slightly different interface.) +@item +In a eww buffer you could use this to select various links you wish +to follow up on, and then collect them into a buffer. Similarly, +while reading Emacs's info manual you could select some symbols you +want to read more about and export them to an @samp{apropos-mode} buffer. +@item +You can use selections in regular text or programming buffers to do +complex editing operations. For example, if you have three +paragraphs scattered over a file and you want to bring them +together, you can select each one, insert them all somewhere and +finally delete all of them (from their original locations). +@end itemize + +@node @samp{embark-live} a live-updating variant of @samp{embark-collect} +@subsection @samp{embark-live} a live-updating variant of @samp{embark-collect} + +Finally, there is also an @samp{embark-live} variant of the @samp{embark-collect} +command which automatically updates the collection after each change +in the source buffer. Users of a completion UI that automatically +updates and displays the candidate list (such as Vertico, Icomplete, +Fido-mode, or MCT) will probably not want to use +@samp{embark-live} from the minibuffer as they will then have two live +updating displays of the completion candidates! + +A more likely use of @samp{embark-live} is to be called from a regular buffer +to display a sort of live updating ``table of contents'' for the buffer. +This depends on having appropriate candidate collectors configured in +@samp{embark-candidate-collectors}. There are not many in Embark's default +configuration, but you can try this experiment: open a dired buffer in +a directory that has very many files, mark a few, and run @samp{embark-live}. +You'll get an Embark Collect buffer containing only the marked files, +which updates as you mark or unmark files in dired. To make +@samp{embark-live} genuinely useful other candidate collectors are required. +The @samp{embark-consult} package (documented near the end of this manual) +contains a few: one for imenu items and one for outline headings as +used by @samp{outline-minor-mode}. Those collectors really do give +@samp{embark-live} a table-of-contents feel. + +@node Switching to a different command without losing what you've typed +@section Switching to a different command without losing what you've typed + +Embark also has the @samp{embark-become} command which is useful for when +you run a command, start typing at the minibuffer and realize you +meant a different command. The most common case for me is that I run +@samp{switch-to-buffer}, start typing a buffer name and realize I haven't +opened the file I had in mind yet! I'll use this situation as a +running example to illustrate @samp{embark-become}. When this happens I can, +of course, press @samp{C-g} and then run @samp{find-file} and open the file, but +this requires retyping the portion of the file name you already +typed. This process can be streamlined with @samp{embark-become}: while still +in the @samp{switch-to-buffer} you can run @samp{embark-become} and effectively +make the @samp{switch-to-buffer} command become @samp{find-file} for this run. + +You can bind @samp{embark-become} to a key in @samp{minibuffer-local-map}, but it is +also available as an action under the letter @samp{B} (uppercase), so you +don't need a binding if you already have one for @samp{embark-act}. So, +assuming I have @samp{embark-act} bound to, say, @samp{C-.}, once I realize I +haven't open the file I can type @samp{C-. B C-x C-f} to have +@samp{switch-to-buffer} become @samp{find-file} without losing what I have already +typed in the minibuffer. + +But for even more convenience, @samp{embark-become} offers shorter key +bindings for commands you are likely to want the current command to +become. When you use @samp{embark-become} it looks for the current command in +all keymaps named in the list @samp{embark-become-keymaps} and then activates +all keymaps that contain it. For example, the default value of +@samp{embark-become-keymaps} contains a keymap @samp{embark-become-file+buffer-map} +with bindings for several commands related to files and buffers, in +particular, it binds @samp{switch-to-buffer} to @samp{b} and @samp{find-file} to @samp{f}. So when +I accidentally try to switch to a buffer for a file I haven't opened +yet, @samp{embark-become} finds that the command I ran, @samp{switch-to-buffer}, is +in the keymap @samp{embark-become-file+buffer-map}, so it activates that +keymap (and any others that also contain a binding for +@samp{switch-to-buffer}). The end result is that I can type @samp{C-. B f} to +switch to @samp{find-file}. + +@node Quick start +@chapter Quick start + +The easiest way to install Embark is from GNU ELPA, just run @samp{M-x +package-install RET embark RET}. (It is also available on MELPA@.) It is +highly recommended to also install @uref{https://github.com/minad/marginalia, Marginalia} (also available on GNU +ELPA), so that Embark can offer you preconfigured actions in more +contexts. For @samp{use-package} users, the following is a very reasonable +starting configuration: + +@lisp +(use-package marginalia + :ensure t + :config + (marginalia-mode)) + +(use-package embark + :ensure t + + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc + ;; strategy, if you want to see the documentation from multiple providers. + (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) + ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) + + :config + + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +;; Consult users will also want the embark-consult package. +(use-package embark-consult + :ensure t ; only need to install it, embark loads it after consult if found + :hook + (embark-collect-mode . consult-preview-at-point-mode)) +@end lisp + +About the suggested key bindings for @samp{embark-act} and @samp{embark-dwim}: +@itemize +@item +Those key bindings are unlikely to work in the terminal, but +terminal users are probably well aware of this and will know to +select different bindings. +@item +The suggested @samp{C-.} binding is used by default in (at least some +installations of) GNOME to input emojis, and Emacs doesn't even get +a chance to respond to the binding. You can select a different key +binding for @samp{embark-act} or use @samp{ibus-setup} to change the shortcut for +emoji insertion (Emacs 29 will likely use @samp{C-x 8 e e}, in case you +want to set the same one system-wide). +@item +The suggested alternative of @samp{M-.} for @samp{embark-dwim} is bound by default +to @samp{xref-find-definitions}. That is a very useful command but +overwriting it with @samp{embark-dwim} is sensible since in Embark's +default configuration, @samp{embark-dwim} will also find the definition of +the identifier at point. (Note that @samp{xref-find-definitions} with a +prefix argument prompts you for an identifier, @samp{embark-dwim} does not +cover this case). +@end itemize + +Other Embark commands such as @samp{embark-act-all}, @samp{embark-become}, +@samp{embark-collect}, and @samp{embark-export} can be run through @samp{embark-act} as +actions bound to @samp{A}, @samp{B}, @samp{S} (for ``snapshot''), and @samp{E} respectively, and +thus don't really need a dedicated key binding, but feel free to bind +them directly if you so wish. If you do choose to bind them directly, +you'll probably want to bind them in @samp{minibuffer-local-map}, since they +are most useful in the minibuffer (in fact, @samp{embark-become} only works +in the minibuffer). + +The command @samp{embark-dwim} executes the default action at point. Another good +keybinding for @samp{embark-dwim} is @samp{M-.} since @samp{embark-dwim} acts like +@samp{xref-find-definitions} on the symbol at point. @samp{C-.} can be seen as a +right-click context menu at point and @samp{M-.} acts like left-click. The +keybindings are mnemonic, both act at the point (@samp{.}). + +Embark needs to know what your minibuffer completion system considers +to be the list of candidates and which one is the current candidate. +Embark works out of the box if you use Emacs's default tab completion, +the built-in @samp{icomplete-mode} or @samp{fido-mode}, or the third-party packages +@uref{https://github.com/minad/vertico, Vertico} or @uref{https://github.com/abo-abo/swiper, Ivy}. + +If you are a @uref{https://emacs-helm.github.io/helm/, Helm} or @uref{https://github.com/abo-abo/swiper, Ivy} user you are unlikely to want Embark since +those packages include comprehensive functionality for acting on +minibuffer completion candidates. (Embark does come with Ivy +integration despite this.) + +@node Advanced configuration +@chapter Advanced configuration + +@menu +* Showing information about available targets and actions:: +* Selecting commands via completions instead of key bindings:: +* Quitting the minibuffer after an action:: +* Running some setup after injecting the target:: +* Running hooks before, after or around an action: Running hooks before after or around an action. +* Creating your own keymaps:: +* Defining actions for new categories of targets:: +@end menu + +@node Showing information about available targets and actions +@section Showing information about available targets and actions + +By default, if you run @samp{embark-act} and do not immediately select an +action, after a short delay Embark will pop up a buffer called @samp{*Embark +Actions*} containing a list of available actions with their key +bindings. You can scroll that buffer with the mouse of with the usual +commands @samp{scroll-other-window} and @samp{scroll-other-window-down} (bound by +default to @samp{C-M-v} and @samp{C-M-S-v}). + +That functionality is provided by the @samp{embark-mixed-indicator}, but +Embark has other indicators that can provide information about the +target and its type, what other targets you can cycle to, and which +actions have key bindings in the action map for the current type of +target. Any number of indicators can be active at once and the user +option @samp{embark-indicators} should be set to a list of the desired +indicators. + +Embark comes with the following indicators: + +@itemize +@item +@samp{embark-minimal-indicator}: shows a messages in the echo area or +minibuffer prompt showing the current target and the types of all +targets starting with the current one; this one is on by default. + +@item +@samp{embark-highlight-indicator}: highlights the target at point; +also on by default. + +@item +@samp{embark-verbose-indicator}: displays a table of actions and their key +bindings in a buffer; this is not on by default, in favor of the +mixed indicator described next. + +@item +@samp{embark-mixed-indicator}: starts out by behaving as the minimal +indicator but after a short delay acts as the verbose indicator; +this is on by default. + +@item +@samp{embark-isearch-highlight-indicator}: this only does something when +the current target is the symbol at point, in which case it +lazily highlights all occurrences of that symbol in the current +buffer, like isearch; also on by default. +@end itemize + +Users of the popular @uref{https://github.com/justbur/emacs-which-key, which-key} package may prefer to use the +@samp{embark-which-key-indicator} from the @uref{https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt, Embark wiki}. Just copy its +definition from the wiki into your configuration and customize the +@samp{embark-indicators} user option to exclude the mixed and verbose +indicators and to include @samp{embark-which-key-indicator}. + +@node Selecting commands via completions instead of key bindings +@section Selecting commands via completions instead of key bindings + +As an alternative to reading the list of actions in the verbose or +mixed indicators (see the previous section for a description of +these), you can press the @samp{embark-help-key}, which is @samp{C-h} by default +(but you may prefer @samp{?} to free up @samp{C-h} for use as a prefix) after +running @samp{embark-act}. Pressing the help key will prompt you for the name +of an action with completion (but feel free to enter a command that is +not among the offered candidates!), and will also remind you of the +key bindings. You can press @samp{embark-keymap-prompter-key}, which is @samp{@@} by +default, at the prompt and then one of the key bindings to enter the +name of the corresponding action. + +You may think that with the @samp{*Embark Actions*} buffer popping up to +remind you of the key bindings you'd never want to use completion to +select an action by name, but personally I find that typing a small +portion of the action name to narrow down the list of candidates feels +significantly faster than visually scanning the entire list of actions. + +If you find you prefer entering actions that way, you can configure +embark to always prompt you for actions by setting the variable +@samp{embark-prompter} to @samp{embark-completing-read-prompter}. + +@node Quitting the minibuffer after an action +@section Quitting the minibuffer after an action + +By default, if you call @samp{embark-act} from the minibuffer it quits the +minibuffer after performing the action. You can change this by setting +the user option @samp{embark-quit-after-action} to @samp{nil}. Having @samp{embark-act} @emph{not} +quit the minibuffer can be useful to turn commands into little ``thing +managers''. For example, you can use @samp{find-file} as a little file manager +or @samp{describe-package} as a little package manager: you can run those +commands, perform a series of actions, and then quit the command. + +If you want to control the quitting behavior in a fine-grained manner +depending on the action, you can set @samp{embark-quit-after-action} to an +alist, associating commands to either @samp{t} for quitting or @samp{nil} for not +quitting. When using an alist, you can use the special key @samp{t} to +specify the default behavior. For example, to specify that by default +actions should not quit the minibuffer but that using @samp{kill-buffer} as +an action should quit, you can use the following configuration: + +@lisp +(setq embark-quit-after-action '((kill-buffer . t) (t . nil))) +@end lisp + +The variable @samp{embark-quit-after-action} only specifies a default, that +is, it only controls whether or not @samp{embark-act} quits the minibuffer +when you call it without a prefix argument, and you can select the +opposite behavior to what the variable says by calling @samp{embark-act} with +@samp{C-u}. Also note that both the variable @samp{embark-quit-after-action} and @samp{C-u} +have no effect when you call @samp{embark-act} outside the minibuffer. + +If you find yourself using the quitting and non-quitting variants of +@samp{embark-act} about equally often, independently of the action, you may +prefer to simply have separate commands for them instead of a single +command that you call with @samp{C-u} half the time. You could, for example, +keep the default exiting behavior of @samp{embark-act} and define a +non-quitting version as follows: + +@lisp +(defun embark-act-noquit () + "Run action but don't quit the minibuffer afterwards." + (interactive) + (let ((embark-quit-after-action nil)) + (embark-act))) +@end lisp + +@node Running some setup after injecting the target +@section Running some setup after injecting the target + +You can customize what happens after the target is inserted at the +minibuffer prompt of an action. There are +@samp{embark-target-injection-hooks}, that are run by default after injecting +the target into the minibuffer. The variable +@samp{embark-target-injection-hooks} is an alist associating commands to +their setup hooks. There are two special keys: if no setup hook is +specified for a given action, the hook associated to @samp{t} is run; and the +hook associated to @samp{:always} is run regardless of the action. (This +variable used to have the less explicit name of +@samp{embark-setup-action-hooks}, so please update your configuration.) + +For example, consider using @samp{shell-command} as an action during file +completion. It would be useful to insert a space before the target +file name and to leave the point at the beginning, so you can +immediately type the shell command to run on that file. That's why in +Embark's default configuration there is an entry in +@samp{embark-target-injection-hooks} associating @samp{shell-command} to a hook that +includes @samp{embark--shell-prep}, a simple helper function that quotes all +the spaces in the file name, inserts an extra space at the beginning +of the line and leaves point to the left of it. + +Now, the preparation that @samp{embark--shell-prep} does would be useless if +Embark did what it normally does after it inserts the target of the +action at the minibuffer prompt, which is to ``press @samp{RET}'' for you, +accepting the target as is; if Embark did that for @samp{shell-command} you +wouldn't get a chance to type in the command to execute! That is why +in Embark's default configuration the entry for @samp{shell-command} in +@samp{embark-target-injection-hooks} also contains the function +@samp{embark--allow-edit}. + +Embark used to have a dedicated variable @samp{embark-allow-edit-actions} to +which you could add commands for which Embark should forgo pressing +@samp{RET} for you after inserting the target. Since its effect can also be +achieved via the general @samp{embark-target-injection-hooks} mechanism, that +variable has been removed to simply Embark. Be sure to update your +configuration; if you had something like: + +@lisp +(add-to-list 'embark-allow-edit-actions 'my-command) +@end lisp + +you should replace it with: + +@lisp +(push 'embark--allow-edit + (alist-get 'my-command embark-target-injection-hooks)) +@end lisp + + +Also note that while you could abuse @samp{embark--allow-edit} so that you +have to confirm ``dangerous'' actions such as @samp{delete-file}, it is better +to implement confirmation by adding the @samp{embark--confirm} function to +the appropriate entry of a different hook alist, namely, +@samp{embark-pre-action-hooks}. + +Besides @samp{embark--allow-edit}, Embark comes with another function that is +of general utility in action setup hooks: @samp{embark--ignore-target}. Use +it for commands that do prompt you in the minibuffer but for which +inserting the target would be inappropriate. This is not a common +situation but does occasionally arise. For example it is used by +default for @samp{shell-command-on-region}: that command is used as an action +for region targets, and it prompts you for a shell command; you +typically do @emph{not} want the target, that is the contents of the region, +to be entered at that prompt! + +@node Running hooks before after or around an action +@section Running hooks before, after or around an action + +Embark has three variables, @samp{embark-pre-action-hooks}, +@samp{embark-post-action-hooks} and @samp{embark-around-action-hooks}, which are +alists associating commands to hooks that should run before or after +or as around advice for the command when used as an action. As with +@samp{embark-target-injection-hooks}, there are two special keys for the +alists: @samp{t} designates the default hook to run when no specific hook is +specified for a command; and the hook associated to @samp{:always} runs +regardless. + +The default values of those variables are fairly extensive, adding +creature comforts to make running actions a smooth experience. Embark +comes with several functions intended to be added to these hooks, and +used in the default values of @samp{embark-pre-action-hooks}, +@samp{embark-post-action-hooks} and @samp{embark-around-action-hooks}. + +For pre-action hooks: + +@table @asis +@item @samp{embark--confirm} +Prompt the user for confirmation before executing +the action. This is used be default for commands deemed ``dangerous'', +or, more accurately, hard to undo, such as @samp{delete-file} and +@samp{kill-buffer}. + +@item @samp{embark--unmark-target} +Unmark the active region. Use this for +commands you want to act on the region contents but without the +region being active. The default configuration uses this function as +a pre-action hook for @samp{occur} and @samp{query-replace}, for example, so that +you can use them as actions with region targets to search the whole +buffer for the text contained in the region. Without this pre-action +hook using @samp{occur} as an action for a region target would be +pointless: it would search for the the region contents @emph{in the +region}, (typically, due to the details of regexps) finding only one +match! + +@item @samp{embark--beginning-of-target} +Move to the beginning of the target +(for targets that report bounds). This is used by default for +backward motion commands such as @samp{backward-sexp}, so that they don't +accidentally leave you on the current target. + +@item @samp{embark--end-of-target} +Move to the end of the target. This is used +similarly to the previous function, but also for commands that act +on the last s-expression like @samp{eval-last-sexp}. This allow you to act +on an s-expression from anywhere inside it and still use +@samp{eval-last-sexp} as an action. + +@item @samp{embark--xref-push-markers} +Push the current location on the xref +marker stack. Use this for commands that take you somewhere and for +which you'd like to be able to come back to where you were using +@samp{xref-pop-marker-stack}. This is used by default for @samp{find-library}. +@end table + +For post-action hooks: + +@table @asis +@item @samp{embark--restart} +Restart the command currently prompting in the +minibuffer, so that the list of completion candidates is updated. +This is useful as a post action hook for commands that delete or +rename a completion candidate; for example the default value of +@samp{embark-post-action-hooks} uses it for @samp{delete-file}, @samp{kill-buffer}, +@samp{rename-file}, @samp{rename-buffer}, etc. +@end table + +For around-action hooks: + +@table @asis +@item @samp{embark--mark-target} +Save existing mark and point location, mark +the target and run the action. Most targets at point outside the +minibuffer report which region of the buffer they correspond to +(this is the information used by @samp{embark-highlight-indicator} to +know what portion of the buffer to highlight); this function marks +that region. It is useful as an around action hook for commands that +expect a region to be marked, for example, it is used by default for +@samp{indent-region} so that it works on s-expression targets, or for +@samp{fill-region} so that it works on paragraph targets. + +@item @samp{embark--cd} +Run the action with @samp{default-directory} set to the +directory associated to the current target. The target should be of +type @samp{file}, @samp{buffer}, @samp{bookmark} or @samp{library}, and the associated directory +is what you'd expect in each case. + +@item @samp{embark--narrow-to-target} +Run the action with buffer narrowed to +current target. Use this as an around hook to localize the effect of +actions that don't already work on just the region. In the default +configuration it is used for @samp{repunctuate-sentences}. + +@item @samp{embark--save-excursion} +Run the action restoring point at the end. +The current default configuration doesn't use this but it is +available for users. +@end table + +@node Creating your own keymaps +@section Creating your own keymaps + +All internal keymaps are defined with the standard helper macro +@samp{defvar-keymap}. For example a simple version of the file action keymap +could be defined as follows: + +@lisp +(defvar-keymap embark-file-map + :doc "Example keymap with a few file actions" + :parent embark-general-map + "d" #'delete-file + "r" #'rename-file + "c" #'copy-file) +@end lisp + +These action keymaps are perfectly normal Emacs +keymaps. You may want to inherit from the @samp{embark-general-map} if you +want to access the default Embark actions. Note that @samp{embark-collect} +and @samp{embark-export} are also made available via @samp{embark-general-map}. + +@node Defining actions for new categories of targets +@section Defining actions for new categories of targets + +It is easy to configure Embark to provide actions for new types of +targets, either in the minibuffer or outside it. I present below two +very detailed examples of how to do this. At several points I'll +explain more than one way to proceed, typically with the easiest +option first. I include the alternative options since there will be +similar situations where the easiest option is not available. + +@menu +* New minibuffer target example - tab-bar tabs:: +* New target example in regular buffers - short Wikipedia links:: +@end menu + +@node New minibuffer target example - tab-bar tabs +@subsection New minibuffer target example - tab-bar tabs + +As an example, take the new @uref{https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html, tab bars} from Emacs 27. I'll explain how +to configure Embark to offer tab-specific actions when you use the +tab-bar-mode commands that mention tabs by name. The configuration +explained here is now built-in to Embark (and Marginalia), but it's +still a good self-contained example. In order to setup up tab actions +you would need to: (1) make sure Embark knows those commands deal with +tabs, (2) define a keymap for tab actions and configure Embark so it +knows that's the keymap you want. + +@enumerate +@item +@anchor{Telling Embark about commands that prompt for tabs by name}Telling Embark about commands that prompt for tabs by name + + +For step (1), it would be great if the @samp{tab-bar-mode} commands reported +the completion category @samp{tab} when asking you for a tab with +completion. (All built-in Emacs commands that prompt for file names, +for example, do have metadata indicating that they want a @samp{file}.) They +do not, unfortunately, and I will describe a couple of ways to deal +with this. + +Maybe the easiest thing is to configure @uref{https://github.com/minad/marginalia, Marginalia} to enhance those +commands. All of the @samp{tab-bar-*-tab-by-name} commands have the words +``tab by name'' in the minibuffer prompt, so you can use: + +@lisp +(add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) +@end lisp + +That's it! But in case you are ever in a situation where you don't +already have commands that prompt for the targets you want, I'll +describe how writing your own command with appropriate @samp{category} +metadata looks: + +@lisp +(defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (completing-read + "Tabs: " + (lambda (string predicate action) + (if (eq action 'metadata) + '(metadata (category . tab)) + (complete-with-action + action tab-list string predicate))))))) + (tab-bar-select-tab-by-name tab)) +@end lisp + +As you can see, the built-in support for setting the category +meta-datum is not very easy to use or pretty to look at. To help with +this I recommend the @samp{consult--read} function from the excellent +@uref{https://github.com/minad/consult/, Consult} package. With that function we can rewrite the command as +follows: + +@lisp +(defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (consult--read tab-list + :prompt "Tabs: " + :category 'tab)))) + (tab-bar-select-tab-by-name tab)) +@end lisp + +Much nicer! No matter how you define the @samp{my-select-tab-by-name} +command, the first approach with Marginalia and prompt detection has +the following advantages: you get the @samp{tab} category for all the +@samp{tab-bar-*-bar-by-name} commands at once, also, you enhance built-in +commands, instead of defining new ones. + +@item +@anchor{Defining and configuring a keymap for tab actions}Defining and configuring a keymap for tab actions + + +Let's say we want to offer select, rename and close actions for tabs +(in addition to Embark general actions, such as saving the tab name to +the kill-ring, which you get for free). Then this will do: + +@lisp +(defvar-keymap embark-tab-actions + :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + :parent embark-general-map + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + +(add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) +@end lisp + +What if after using this for a while you feel closing the tab +without confirmation is dangerous? You have a couple of options: + +@enumerate +@item +You can keep using the @samp{tab-bar-close-tab-by-name} command, but have +Embark ask you for confirmation: +@lisp +(push #'embark--confirm + (alist-get 'tab-bar-close-tab-by-name + embark-pre-action-hooks)) +@end lisp + +@item +You can write your own command that prompts for confirmation and +use that instead of @samp{tab-bar-close-tab-by-name} in the above keymap: +@lisp +(defun my-confirm-close-tab-by-name (tab) + (interactive "sTab to close: ") + (when (y-or-n-p (format "Close tab '%s'? " tab)) + (tab-bar-close-tab-by-name tab))) +@end lisp + +Notice that this is a command you can also use directly from @samp{M-x} +independently of Embark. Using it from @samp{M-x} leaves something to be +desired, though, since you don't get completion for the tab names. +You can fix this if you wish as described in the previous section. +@end enumerate +@end enumerate + +@node New target example in regular buffers - short Wikipedia links +@subsection New target example in regular buffers - short Wikipedia links + +Say you want to teach Embark to treat text of the form +@samp{wikipedia:Garry_Kasparov} in any regular buffer as a link to Wikipedia, +with actions to open the Wikipedia page in eww or an external browser +or to save the URL of the page in the kill-ring. We can take advantage +of the actions that Embark has preconfigured for URLs, so all we need +to do is teach Embark that @samp{wikipedia:Garry_Kasparov} stands for the URL +@samp{https://en.wikipedia.org/wiki/Garry_Kasparov}. + +You can be as fancy as you want with the recognized syntax. Here, to +keep the example simple, I'll assume the link matches the regexp +@samp{wikipedia:[[:alnum:]_]+}. We will write a function that looks for a +match surrounding point, and returns a dotted list of the form @samp{'(url +URL-OF-THE-PAGE START . END)} where @samp{START} and @samp{END} are the buffer +positions bounding the target, and are used by Embark to highlight it +if you have @samp{embark-highlight-indicator} included in the list +@samp{embark-indicators}. (There are a couple of other options for the return +value of a target finder: the bounding positions are optional and a +single target finder is allowed to return multiple targets; see the +documentation for @samp{embark-target-finders} for details.) + +@lisp +(defun my-short-wikipedia-link () + "Target a link at point of the form wikipedia:Page_Name." + (save-excursion + (let* ((start (progn (skip-chars-backward "[:alnum:]_:") (point))) + (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + (str (buffer-substring-no-properties start end))) + (save-match-data + (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + `(url + ,(format "https://en.wikipedia.org/wiki/%s" + (match-string 1 str)) + ,start . ,end)))))) + +(add-to-list 'embark-target-finders 'my-short-wikipedia-link) +@end lisp + +@node How does Embark call the actions? +@chapter How does Embark call the actions? + +Embark actions are normal Emacs commands, that is, functions with an +interactive specification. In order to execute an action, Embark +calls the command with @samp{call-interactively}, so the command reads user +input exactly as if run directly by the user. For example the +command may open a minibuffer and read a string +(@samp{read-from-minibuffer}) or open a completion interface +(@samp{completing-read}). If this happens, Embark takes the target string +and inserts it automatically into the minibuffer, simulating user +input this way. After inserting the string, Embark exits the +minibuffer, submitting the input. (The immediate minibuffer exit can +be disabled for specific actions in order to allow editing the +input; this is done by adding the @samp{embark--allow-edit} function to the +appropriate entry of @samp{embark-target-injection-hooks}). Embark inserts +the target string at the first minibuffer opened by the action +command, and if the command happens to prompt the user for input +more than once, the user still interacts with the second and further +prompts in the normal fashion. Note that if a command does not +prompt the user for input in the minibuffer, Embark still allows you +to use it as an action, but of course, never inserts the target +anywhere. (There are plenty of examples in the default configuration +of commands that do not prompt the user bound to keys in the action +maps, most of the region actions, for instance.) + +This is how Embark manages to reuse normal commands as actions. The +mechanism allows you to use as Embark actions commands that were not +written with Embark in mind (and indeed almost all actions that are +bound by default in Embark's action keymaps are standard Emacs +commands). It also allows you to write new custom actions in such a +way that they are useful even without Embark. + +Staring from version 28.1, Emacs has a variable +@samp{y-or-n-p-use-read-key}, which when set to @samp{t} causes @samp{y-or-n-p} to use +@samp{read-key} instead of @samp{read-from-minibuffer}. Setting +@samp{y-or-n-p-use-read-key} to @samp{t} is recommended for Embark users because +it keeps Embark from attempting to insert the target at a @samp{y-or-n-p} +prompt, which would almost never be sensible. Also consider this as +a warning to structure your own action commands so that if they use +@samp{y-or-n-p}, they do so only after the prompting for the target. + +Here is a simple example illustrating the various ways of reading +input from the user mentioned above. Bind the following commands to +the @samp{embark-symbol-map} to be used as actions, then put the point on +some symbol and run them with @samp{embark-act}: + +@lisp +(defun example-action-command1 () + (interactive) + (message "The input was `%s'." (read-from-minibuffer "Input: "))) + +(defun example-action-command2 (arg input1 input2) + (interactive "P\nsInput 1: \nsInput 2: ") + (message "The first input %swas `%s', and the second was `%s'." + (if arg "truly " "") + input1 + input2)) + +(defun example-action-command3 () + (interactive) + (message "Your selection was `%s'." + (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + +(defun example-action-command4 () + (interactive) + (message "I don't prompt you for input and thus ignore the target!")) + +(keymap-set embark-symbol-map "X 1" #'example-action-command1) +(keymap-set embark-symbol-map "X 2" #'example-action-command2) +(keymap-set embark-symbol-map "X 3" #'example-action-command3) +(keymap-set embark-symbol-map "X 4" #'example-action-command4) +@end lisp + +Also note that if you are using the key bindings to call actions, +you can pass prefix arguments to actions in the normal way. For +example, you can use @samp{C-u X2} with the above demonstration actions to +make the message printed by @samp{example-action-command2} more emphatic. +This ability to pass prefix arguments to actions is useful for some +actions in the default configuration, such as +@samp{embark-shell-command-on-buffer}. + +@menu +* Non-interactive functions as actions:: +@end menu + +@node Non-interactive functions as actions +@section Non-interactive functions as actions + +Alternatively, Embark does support one other type of action: a +non-interactive function of a single argument. The target is passed +as argument to the function. For example: + +@lisp +(defun example-action-function (target) + (message "The target was `%s'." target)) + +(keymap-set embark-symbol-map "X 4" #'example-action-function) +@end lisp + +Note that normally binding non-interactive functions in a keymap is +useless, since when attempting to run them using the key binding you +get an error message similar to ``Wrong type argument: commandp, +example-action-function''. In general it is more flexible to write +any new Embark actions as commands, that is, as interactive +functions, because that way you can also run them directly, without +Embark. But there are a couple of reasons to use non-interactive +functions as actions: + +@enumerate +@item +You may already have the function lying around, and it is +convenient to simply reuse it. + +@item +For command actions the targets can only be simple string, with +no text properties. For certain advanced uses you may want the +action to receive a string @emph{with} some text properties, or even a +non-string target. +@end enumerate + +@node Embark Marginalia and Consult +@chapter Embark, Marginalia and Consult + +Embark cooperates well with the @uref{https://github.com/minad/marginalia, Marginalia} and @uref{https://github.com/minad/consult, Consult} packages. +Neither of those packages is a dependency of Embark, but both are +highly recommended companions to Embark, for opposite reasons: +Marginalia greatly enhances Embark's usefulness, while Embark can help +enhance Consult. + +In the remainder of this section I'll explain what exactly Marginalia +does for Embark, and what Embark can do for Consult. + +@menu +* Marginalia:: +* Consult:: +@end menu + +@node Marginalia +@section Marginalia + +Embark comes with actions for symbols (commands, functions, variables +with actions such as finding the definition, looking up the +documentation, evaluating, etc.) in the @samp{embark-symbol-map} keymap, and +for packages (actions like install, delete, browse url, etc.) in the +@samp{embark-package-keymap}. + +Unfortunately Embark does not automatically offers you these keymaps +when relevant, because many built-in Emacs commands don't report +accurate category metadata. For example, a command like +@samp{describe-package}, which reads a package name from the minibuffer, +does not have metadata indicating this fact. + +In an earlier Embark version, there were functions to supply this +missing metadata, but they have been moved to Marginalia, which +augments many Emacs command to report accurate category metadata. +Simply activating @samp{marginalia-mode} allows Embark to offer you the +package and symbol actions when appropriate again. Candidate +annotations in the Embark collect buffer are also provided by the +Marginalia package: + +@itemize +@item +If you install Marginalia and activate @samp{marginalia-mode}, Embark +Collect buffers will use the Marginalia annotations automatically. + +@item +If you don't install Marginalia, you will see only the annotations +that come with Emacs (such as key bindings in @samp{M-x}, or the unicode +characters in @samp{C-x 8 RET}). +@end itemize + +@node Consult +@section Consult + +The excellent Consult package provides many commands that use +minibuffer completion, via the @samp{completing-read} function; plenty of its +commands can be considered enhanced versions of built-in Emacs +commands, and some are completely new functionality. One common +enhancement provided in all commands for which it makes sense is +preview functionality, for example @samp{consult-buffer} will show you a +quick preview of a buffer before you actually switch to it. + +If you use both Consult and Embark you should install the +@samp{embark-consult} package which provides integration between the two. It +provides exporters for several Consult commands and also tweaks the +behavior of many Consult commands when used as actions with @samp{embark-act} +in subtle ways that you may not even notice, but make for a smoother +experience. You need only install it to get these benefits: Embark +will automatically load it after Consult if found. + +The @samp{embark-consult} package provides the following exporters: + +@itemize +@item +You can use @samp{embark-export} from @samp{consult-line}, @samp{consult-outline}, or +@samp{consult-mark} to obtain an @samp{occur-mode} buffer. As with the built-in +@samp{occur} command you use that buffer to jump to a match and after that, +you can then use @samp{next-error} and @samp{previous-error} to navigate to other +matches. You can also press @samp{e} to activate @samp{occur-edit-mode} and edit +the matches in place! + +@item +You can export from any of the Consult asynchronous search commands, +@samp{consult-grep}, @samp{consult-git-grep}, or @samp{consult-ripgrep} to get a +@samp{grep-mode} buffer. Here too you can use @samp{next-error} and @samp{previous-error} +to navigate among matches, and, if you install the @uref{http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el , wgrep} package, +you can use it to edit the matches in place. +@end itemize + +In both cases, pressing @samp{g} will rerun the Consult command you had +exported from and re-enter the input you had typed (which is similar +to reverting but a little more flexible). You can then proceed to +re-export if that's what you want, but you can also edit the input +changing the search terms or simply cancel if you see you are done +with that search. + +The @samp{embark-consult} also contains some candidates collectors that allow +you to run @samp{embark-live} to get a live-updating table of contents for +your buffer: + +@itemize +@item +@samp{embark-consult-outline-candidates} produces the outline headings of +the current buffer, using @samp{consult-outline}. +@item +@samp{embark-consult-imenu-candidates} produces the imenu items of +the current buffer, using @samp{consult-imenu}. +@item +@samp{embark-consult-imenu-or-outline-candidates} is a simple combination +of the two previous functions: it produces imenu items in buffers +deriving from @samp{prog-mode} and otherwise outline headings. +@end itemize + +The way to configure @samp{embark-live} (or @samp{embark-collect} and @samp{embark-export} +for that matter) to use one of these function is to add it at the end +of the @samp{embark-candidate-collectors} list. The @samp{embark-consult} package by +default adds the last one, which seems to be the most sensible +default. + +Besides those exporters and candidate collectors, the @samp{embark-consult} +package provides many subtle tweaks and small integrations between +Embark and Consult. Some examples are: + +@itemize +@item +The asynchronous search commands will start in the directory +associated to the Embark target if that target is a file, buffer, +bookmark or Emacs Lisp library. + +@itemize +@item +For all other target types, a Consult search command (asynchronous +or not) will search for the text of the target but leave the +minibuffer open so you can interact with the Consult command. +@end itemize + +@item +@samp{consult-imenu} will search for the target and take you directly to +the location if it matches a unique imenu entry, otherwise it will +leave the minibuffer open so you can navigate among the matches. +@end itemize + +@node Resources +@chapter Resources + +If you want to learn more about how others have used Embark here are +some links to read: + +@itemize +@item +@uref{https://karthinks.com/software/fifteen-ways-to-use-embark/, Fifteen ways to use Embark}, a blog post by Karthik Chikmagalur. +@item +@uref{https://protesilaos.com/dotemacs/, Protesilaos Stavrou's dotemacs}, look for the section called +``Extended minibuffer actions and more (embark.el and +prot-embark.el)'' +@end itemize + +And some videos to watch: + +@itemize +@item +@uref{https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/, Embark and my extras} by Protesilaos Stavrou. +@item +@uref{https://youtu.be/qpoQiiinCtY, Embark -- Key features and tweaks} by Raoul Comninos on the +Emacs-Elements YouTube channel. +@item +@uref{https://youtu.be/WsxXr1ncukY, Livestreamed: Adding an Embark context action to send a stream +message} by Sacha Chua. +@item +@uref{https://youtu.be/qk2Is_sC8Lk, System Crafters Live! - The Many Uses of Embark} by David Wilson. +@item +@uref{https://youtu.be/5ffb2at2d7w, Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark} by +Mike Zamansky. +@end itemize + +@node Contributions +@chapter Contributions + +Contributions to Embark are very welcome. There is a @uref{https://github.com/oantolin/embark/issues/95, wish list} for +actions, target finders, candidate collectors and exporters. For other +ideas you have for Embark, feel free to open an issue on the @uref{https://github.com/oantolin/embark/issues, issue +tracker}. Any neat configuration tricks you find might be a good fit +for the @uref{https://github.com/oantolin/embark/wiki, wiki}. + +Code contributions are very welcome too, but since Embark is now on +GNU ELPA, copyright assignment to the FSF is required before you can +contribute code. + +@node Acknowledgments +@chapter Acknowledgments + +While I, Omar Antolín Camarena, have written most of the Embark code +and remain very stubborn about some of the design decisions, Embark +has received substantial help from a number of other people which this +document has neglected to mention for far too long. In particular, +Daniel Mendler has been absolutely invaluable, implementing several +important features, and providing a lot of useful advice. + +Code contributions: + +@itemize +@item +@uref{https://github.com/minad, Daniel Mendler} +@item +@uref{https://github.com/clemera/, Clemens Radermacher} +@item +@uref{https://codeberg.org/jao/, José Antonio Ortega Ruiz} +@item +@uref{https://github.com/iyefrat, Itai Y@. Efrat} +@item +@uref{https://github.com/a13, a13} +@item +@uref{https://github.com/jakanakaevangeli, jakanakaevangeli} +@item +@uref{https://github.com/mihakam, mihakam} +@item +@uref{https://github.com/leungbk, Brian Leung} +@item +@uref{https://github.com/karthink, Karthik Chikmagalur} +@item +@uref{https://github.com/roshanshariff, Roshan Shariff} +@item +@uref{https://github.com/condy0919, condy0919} +@item +@uref{https://github.com/DamienCassou, Damien Cassou} +@item +@uref{https://github.com/JimDBh, JimDBh} +@end itemize + +Advice and useful discussions: + +@itemize +@item +@uref{https://github.com/minad, Daniel Mendler} +@item +@uref{https://gitlab.com/protesilaos/, Protesilaos Stavrou} +@item +@uref{https://github.com/clemera/, Clemens Radermacher} +@item +@uref{https://github.com/hmelman/, Howard Melman} +@item +@uref{https://github.com/astoff, Augusto Stoffel} +@item +@uref{https://github.com/bdarcus, Bruce d'Arcus} +@item +@uref{https://github.com/jdtsmith, JD Smith} +@item +@uref{https://github.com/karthink, Karthik Chikmagalur} +@item +@uref{https://github.com/jakanakaevangeli, jakanakaevangeli} +@item +@uref{https://github.com/iyefrat, Itai Y@. Efrat} +@item +@uref{https://github.com/mohkale, Mohsin Kaleem} +@end itemize + +@bye +\ No newline at end of file diff --git a/elpa/embark-consult-0.7.signed b/elpa/embark-consult-0.7.signed @@ -0,0 +1 @@ +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2023-02-01T11:05:04+0100 using RSA +\ No newline at end of file diff --git a/elpa/embark-consult-0.7/.dir-locals.el b/elpa/embark-consult-0.7/.dir-locals.el @@ -0,0 +1,6 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((emacs-lisp-mode + (show-trailing-whitespace . t) + (indent-tabs-mode . nil))) diff --git a/elpa/embark-consult-0.7/.elpaignore b/elpa/embark-consult-0.7/.elpaignore @@ -0,0 +1 @@ +LICENSE +\ No newline at end of file diff --git a/elpa/embark-consult-0.7/README-elpa b/elpa/embark-consult-0.7/README-elpa @@ -0,0 +1,1299 @@ + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + EMBARK: EMACS MINI-BUFFER ACTIONS ROOTED IN + KEYMAPS + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + + + +1 Overview +══════════ + + Embark makes it easy to choose a command to run based on what is near + point, both during a minibuffer completion session (in a way familiar + to Helm or Counsel users) and in normal buffers. Bind the command + `embark-act' to a key and it acts like prefix-key for a keymap of + /actions/ (commands) relevant to the /target/ around point. With point + on an URL in a buffer you can open the URL in a browser or eww or + download the file it points to. If while switching buffers you spot an + old one, you can kill it right there and continue to select another. + Embark comes preconfigured with over a hundred actions for common + types of targets such as files, buffers, identifiers, s-expressions, + sentences; and it is easy to add more actions and more target types. + Embark can also collect all the candidates in a minibuffer to an + occur-like buffer or export them to a buffer in a major-mode specific + to the type of candidates, such as dired for a set of files, ibuffer + for a set of buffers, or customize for a set of variables. + + +1.1 Acting on targets +───────────────────── + + You can think of `embark-act' as a keyboard-based version of a + right-click contextual menu. The `embark-act' command (which you + should bind to a convenient key), acts as a prefix for a keymap + offering you relevant /actions/ to use on a /target/ determined by the + context: + + • In the minibuffer, the target is the current top completion + candidate. + • In the `*Completions*' buffer the target is the completion at point. + • In a regular buffer, the target is the region if active, or else the + file, symbol, URL, s-expression or defun at point. + + Multiple targets can be present at the same location and you can cycle + between them by repeating the `embark-act' key binding. The type of + actions offered depend on the type of the target. Here is a sample of + a few of the actions offered in the default configuration: + + • For files you get offered actions like deleting, copying, renaming, + visiting in another window, running a shell command on the file, + etc. + • For buffers the actions include switching to or killing the buffer. + • For package names the actions include installing, removing or + visiting the homepage. + • For Emacs Lisp symbols the actions include finding the definition, + looking up documentation, evaluating (which for a variable + immediately shows the value, but for a function lets you pass it + some arguments first). There are some actions specific to variables, + such as setting the value directly or though the customize system, + and some actions specific to commands, such as binding it to a key. + + By default when you use `embark-act' if you don't immediately select + an action, after a short delay Embark will pop up a buffer showing a + list of actions and their corresponding key bindings. If you are using + `embark-act' outside the minibuffer, Embark will also highlight the + current target. These behaviors are configurable via the variable + `embark-indicators'. Instead of selecting an action via its key + binding, you can select it by name with completion by typing `C-h' + after `embark-act'. + + Everything is easily configurable: determining the current target, + classifying it, and deciding which actions are offered for each type + in the classification. The above introduction just mentions part of + the default configuration. + + Configuring which actions are offered for a type is particularly easy + and requires no programming: the variable `embark-keymap-alist' + associates target types with variables containing keymaps, and those + keymaps containing bindings for the actions. (To examine the available + categories and their associated keymaps, you can use `C-h v + embark-keymap-alist' or customize that variable.) For example, in the + default configuration the type `file' is associated with the symbol + `embark-file-map'. That symbol names a keymap with single-letter key + bindings for common Emacs file commands, for instance `c' is bound to + `copy-file'. This means that if you are in the minibuffer after + running a command that prompts for a file, such as `find-file' or + `rename-file', you can copy a file by running `embark-act' and then + pressing `c'. + + These action keymaps are very convenient but not strictly necessary + when using `embark-act': you can use any command that reads from the + minibuffer as an action and the target of the action will be inserted + at the first minibuffer prompt. After running `embark-act' all of your + key bindings and even `execute-extended-command' can be used to run a + command. For example, if you want to replace all occurrences of the + symbol at point, just use `M-%' as the action, there is no need to + bind `query-replace' in one of Embark's keymaps. Also, those action + keymaps are normal Emacs keymaps and you should feel free to bind in + them whatever commands you find useful as actions and want to be + available through convenient bindings. + + The actions in `embark-general-map' are available no matter what type + of completion you are in the middle of. By default this includes + bindings to save the current candidate in the kill ring and to insert + the current candidate in the previously selected buffer (the buffer + that was current when you executed a command that opened up the + minibuffer). + + Emacs's minibuffer completion system includes metadata indicating the + /category/ of what is being completed. For example, `find-file''s + metadata indicates a category of `file' and `switch-to-buffer''s + metadata indicates a category of `buffer'. Embark has the related + notion of the /type/ of a target for actions, and by default when + category metadata is present it is taken to be the type of minibuffer + completion candidates when used as targets. Emacs commands often do + not set useful category metadata so the [Marginalia] package, which + supplies this missing metadata, is highly recommended for use with + Embark. + + Embark's default configuration has actions for the following target + types: files, buffers, symbols, packages, URLs, bookmarks, and as a + somewhat special case, actions for when the region is active. You can + read about the [default actions and their key bindings] on the GitHub + project wiki. + + +[Marginalia] <https://github.com/minad/marginalia> + +[default actions and their key bindings] +<https://github.com/oantolin/embark/wiki/Default-Actions> + + +1.2 The default action on a target +────────────────────────────────── + + Embark has a notion of default action for a target: + + • If the target is a minibuffer completion candidate, then the default + action is whatever command opened the minibuffer in the first place. + For example if you run `kill-buffer', then the default action will + be to kill buffers. + • If the target comes from a regular buffer (i.e., not a minibuffer), + then the default action is whatever is bound to `RET' in the keymap + of actions for that type of target. For example, in Embark's default + configuration for a URL found at point the default action is + `browse-url', because `RET' is bound to `browse-url' in the + `embark-url-map' keymap. + + To run the default action you can press `RET' after running + `embark-act'. Note that if there are several different targets at a + given location, each has its own default action, so first cycle to the + target you want and then press `RET' to run the corresponding default + action. + + There is also `embark-dwim' which runs the default action for the + first target found. It's pretty handy in non-minibuffer buffers: with + Embark's default configuration it will: + + • Open the file at point. + • Open the URL at point in a web browser (using the `browse-url' + command). + • Compose a new email to the email address at point. + • In an Emacs Lisp buffer, if point is on an opening parenthesis or + right after a closing one, it will evaluate the corresponding + expression. + • Go to the definition of an Emacs Lisp function, variable or macro at + point. + • Find the file corresponding to an Emacs Lisp library at point. + + +1.3 Working with sets of possible targets +───────────────────────────────────────── + + Besides acting individually on targets, Embark lets you work + collectively on a set of target /candidates/. For example, while you + are in the minibuffer the candidates are simply the possible + completions of your input. Embark provides three main commands to work + on candidate sets: + + • The `embark-act-all' command runs the same action on each of the + current candidates. It is just like using `embark-act' on each + candidate in turn. (Because you can easily act on many more + candidates than you meant to, by default Embark asks you to confirm + uses of `embark-act-all'; you can turn this off by setting the user + option `embark-confirm-act-all' to `nil'.) + + • The `embark-collect' command produces a buffer listing all the + current candidates, for you to peruse and run actions on at your + leisure. The candidates are displayed as a list showing additional + annotations. + + The Embark Collect buffer is "dired-like": you can mark and unmark + candidates with `m' and `u', you can unmark all marked candidates + with `U' or toggle the marks with `t'. In an Embark Collect buffer + `embark-act-all' is bound to `A' and will act on all currently + marked candidates if there any, and will act on all candidates if + none are marked. + + • The `embark-export' command tries to open a buffer in an appropriate + major mode for the set of candidates. If the candidates are files + export produces a Dired buffer; if they are buffers, you get an + Ibuffer buffer; and if they are packages you get a buffer in package + menu mode. + + If you use the grepping commands from the [Consult] package, + `consult-grep', `consult-git-grep' or `consult-ripgrep', then you + should install the `embark-consult' package, which adds support for + exporting a list of grep results to an honest grep-mode buffer, on + which you can even use [wgrep] if you wish. + + When in doubt choosing between exporting and collecting, a good rule + of thumb is to always prefer `embark-export' since when an exporter to + a special major mode is available for a given type of target, it will + be more featureful than an Embark collect buffer, and if no such + exporter is configured the `embark-export' command falls back to the + generic `embark-collect'. + + These commands are always available as "actions" (although they do not + act on just the current target but on all candidates) for `embark-act' + and are bound to `A', `S' (for "snapshot"), and `E', respectively, in + `embark-general-map'. This means that you do not have to bind your own + key bindings for these (although you can, of course!), just a key + binding for `embark-act'. + + In Embark Collect or Embark Export buffers that were obtained by + running `embark-collect' or `embark-export' from within a minibuffer + completion session, `g' is bound to a command that restarts the + completion session, that is, the command that opened the minibuffer is + run again and the minibuffer contents restored. You can then interact + normally with the command, perhaps editing the minibuffer contents, + and, if you wish, you can rerun `embark-collect' or `embark-export' to + get an updated buffer. + + +[Consult] <https://github.com/minad/consult/> + +[wgrep] <https://github.com/mhayashi1120/Emacs-wgrep> + +1.3.1 `embark-live' a live-updating variant of `embark-collect' +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + Finally, there is also an `embark-live' variant of the + `embark-collect' command which automatically updates the collection + after each change in the source buffer. Users of a completion UI that + automatically updates and displays the candidate list (such as + Vertico, Icomplete, Selectrum, Fido-mode, or MCT) will probably not + want to use `embark-live' from the minibuffer as they will then have + two live updating displays of the completion candidates! + + A more likely use of `embark-live' is to be called from a regular + buffer to display a sort of live updating "table of contents" for the + buffer. This depends on having appropriate candidate collectors + configured in `embark-candidate-collectors'. There are not many in + Embark's default configuration, but you can try this experiment: open + a dired buffer in a directory that has very many files, mark a few, + and run `embark-live'. You'll get an Embark Collect buffer containing + only the marked files, which updates as you mark or unmark files in + dired. To make `embark-live' genuinely useful other candidate + collectors are required. The `embark-consult' package (documented + near the end of this manual) contains a few: one for imenu items and + one for outline headings as used by `outline-minor-mode'. Those + collectors really do give `embark-live' a table-of-contents feel. + + +1.4 Switching to a different command without losing what you've typed +───────────────────────────────────────────────────────────────────── + + Embark also has the `embark-become' command which is useful for when + you run a command, start typing at the minibuffer and realize you + meant a different command. The most common case for me is that I run + `switch-to-buffer', start typing a buffer name and realize I haven't + opened the file I had in mind yet! I'll use this situation as a + running example to illustrate `embark-become'. When this happens I + can, of course, press `C-g' and then run `find-file' and open the + file, but this requires retyping the portion of the file name you + already typed. This process can be streamlined with `embark-become': + while still in the `switch-to-buffer' you can run `embark-become' and + effectively make the `switch-to-buffer' command become `find-file' for + this run. + + You can bind `embark-become' to a key in `minibuffer-local-map', but + it is also available as an action under the letter `B' (uppercase), so + you don't need a binding if you already have one for `embark-act'. So, + assuming I have `embark-act' bound to, say, `C-.', once I realize I + haven't open the file I can type `C-. B C-x C-f' to have + `switch-to-buffer' become `find-file' without losing what I have + already typed in the minibuffer. + + But for even more convenience, `embark-become' offers shorter key + bindings for commands you are likely to want the current command to + become. When you use `embark-become' it looks for the current command + in all keymaps named in the list `embark-become-keymaps' and then + activates all keymaps that contain it. For example, the default value + of `embark-become-keymaps' contains a keymap + `embark-become-file+buffer-map' with bindings for several commands + related to files and buffers, in particular, it binds + `switch-to-buffer' to `b' and `find-file' to `f'. So when I + accidentally try to switch to a buffer for a file I haven't opened + yet, `embark-become' finds that the command I ran, `switch-to-buffer', + is in the keymap `embark-become-file+buffer-map', so it activates that + keymap (and any others that also contain a binding for + `switch-to-buffer'). The end result is that I can type `C-. B f' to + switch to `find-file'. + + +2 Quick start +═════════════ + + The easiest way to install Embark is from GNU ELPA, just run `M-x + package-install RET embark RET'. (It is also available on MELPA.) It + is highly recommended to also install [Marginalia] (also available on + GNU ELPA), so that Embark can offer you preconfigured actions in more + contexts. For `use-package' users, the following is a very reasonable + starting configuration: + + ┌──── + │ (use-package marginalia + │ :ensure t + │ :config + │ (marginalia-mode)) + │ + │ (use-package embark + │ :ensure t + │ + │ :bind + │ (("C-." . embark-act) ;; pick some comfortable binding + │ ("C-;" . embark-dwim) ;; good alternative: M-. + │ ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + │ + │ :init + │ + │ ;; Optionally replace the key help with a completing-read interface + │ (setq prefix-help-command #'embark-prefix-help-command) + │ + │ :config + │ + │ ;; Hide the mode line of the Embark live/completions buffers + │ (add-to-list 'display-buffer-alist + │ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + │ nil + │ (window-parameters (mode-line-format . none))))) + │ + │ ;; Consult users will also want the embark-consult package. + │ (use-package embark-consult + │ :ensure t ; only need to install it, embark loads it after consult if found + │ :hook + │ (embark-collect-mode . consult-preview-at-point-mode)) + └──── + + About the suggested key bindings for `embark-act' and `embark-dwim': + • Those key bindings are unlikely to work in the terminal, but + terminal users are probably well aware of this and will know to + select different bindings. + • The suggested `C-.' binding is used by default in (at least some + installations of) GNOME to input emojis, and Emacs doesn't even get + a chance to respond to the binding. You can select a different key + binding for `embark-act' or use `ibus-setup' to change the shortcut + for emoji insertion (Emacs 29 will likely use `C-x 8 e e', in case + you want to set the same one system-wide). + • The suggested alternative of `M-.' for `embark-dwim' is bound by + default to `xref-find-definitions'. That is a very useful command + but overwriting it with `embark-dwim' is sensible since in Embark's + default configuration, `embark-dwim' will also find the definition + of the identifier at point. (Note that `xref-find-definitions' with + a prefix argument prompts you for an identifier, `embark-dwim' does + not cover this case). + + Other Embark commands such as `embark-act-all', `embark-become', + `embark-collect', and `embark-export' can be run through `embark-act' + as actions bound to `A', `B', `S' (for "snapshot"), and `E' + respectively, and thus don't really need a dedicated key binding, but + feel free to bind them directly if you so wish. If you do choose to + bind them directly, you'll probably want to bind them in + `minibuffer-local-map', since they are most useful in the minibuffer + (in fact, `embark-become' only works in the minibuffer). + + The command `embark-dwim' executes the default action at + point. Another good keybinding for `embark-dwim' is `M-.' since + `embark-dwim' acts like `xref-find-definitions' on the symbol at + point. `C-.' can be seen as a right-click context menu at point and + `M-.' acts like left-click. The keybindings are mnemonic, both act at + the point (`.'). + + Embark needs to know what your minibuffer completion system considers + to be the list of candidates and which one is the current candidate. + Embark works out of the box if you use Emacs's default tab completion, + the built-in `icomplete-mode' or `fido-mode', or the third-party + packages [Vertico], [Selectrum] or [Ivy]. + + If you are a [Helm] or [Ivy] user you are unlikely to want Embark + since those packages include comprehensive functionality for acting on + minibuffer completion candidates. (Embark does come with Ivy + integration despite this.) + + +[Marginalia] <https://github.com/minad/marginalia> + +[Vertico] <https://github.com/minad/vertico> + +[Selectrum] <https://github.com/raxod502/selectrum/> + +[Ivy] <https://github.com/abo-abo/swiper> + +[Helm] <https://emacs-helm.github.io/helm/> + + +3 Advanced configuration +════════════════════════ + +3.1 Showing information about available targets and actions +─────────────────────────────────────────────────────────── + + By default, if you run `embark-act' and do not immediately select an + action, after a short delay Embark will pop up a buffer called + `*Embark Actions*' containing a list of available actions with their + key bindings. You can scroll that buffer with the mouse of with the + usual commands `scroll-other-window' and `scroll-other-window-down' + (bound by default to `C-M-v' and `C-M-S-v'). + + That functionality is provided by the `embark-mixed-indicator', but + Embark has other indicators that can provide information about the + target and its type, what other targets you can cycle to, and which + actions have key bindings in the action map for the current type of + target. Any number of indicators can be active at once and the user + option `embark-indicators' should be set to a list of the desired + indicators. + + Embark comes with the following indicators: + + • `embark-minimal-indicator': shows a messages in the echo area or + minibuffer prompt showing the current target and the types of all + targets starting with the current one; this one is on by default. + + • `embark-highlight-indicator': highlights the target at point; also + on by default. + + • `embark-verbose-indicator': displays a table of actions and their + key bindings in a buffer; this is not on by default, in favor of the + mixed indicator described next. + + • `embark-mixed-indicator': starts out by behaving as the minimal + indicator but after a short delay acts as the verbose indicator; + this is on by default. + + • `embark-isearch-highlight-indicator': this only does something when + the current target is the symbol at point, in which case it lazily + highlights all occurrences of that symbol in the current buffer, + like isearch; also on by default. + + Users of the popular [which-key] package may prefer to use the + `embark-which-key-indicator' from the [Embark wiki]. Just copy its + definition from the wiki into your configuration and customize the + `embark-indicators' user option to exclude the mixed and verbose + indicators and to include `embark-which-key-indicator'. + + +[which-key] <https://github.com/justbur/emacs-which-key> + +[Embark wiki] +<https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt> + + +3.2 Selecting commands via completions instead of key bindings +────────────────────────────────────────────────────────────── + + As an alternative to reading the list of actions in the verbose or + mixed indicators (see the previous section for a description of + these), you can press the `embark-help-key', which is `C-h' by default + (but you may prefer `?' to free up `C-h' for use as a prefix) after + running `embark-act'. Pressing the help key will prompt you for the + name of an action with completion (but feel free to enter a command + that is not among the offered candidates!), and will also remind you + of the key bindings. You can press `embark-keymap-prompter-key', which + is `@' by default, at the prompt and then one of the key bindings to + enter the name of the corresponding action. + + You may think that with the `*Embark Actions*' buffer popping up to + remind you of the key bindings you'd never want to use completion to + select an action by name, but personally I find that typing a small + portion of the action name to narrow down the list of candidates feels + significantly faster than visually scanning the entire list of + actions. + + If you find you prefer entering actions that way, you can configure + embark to always prompt you for actions by setting the variable + `embark-prompter' to `embark-completing-read-prompter'. + + +3.3 Quitting the minibuffer after an action +─────────────────────────────────────────── + + By default, if you call `embark-act' from the minibuffer it quits the + minibuffer after performing the action. You can change this by setting + the user option `embark-quit-after-action' to `nil'. Having + `embark-act' /not/ quit the minibuffer can be useful to turn commands + into little "thing managers". For example, you can use `find-file' as + a little file manager or `describe-package' as a little package + manager: you can run those commands, perform a series of actions, and + then quit the command. + + If you want to control the quitting behavior in a fine-grained manner + depending on the action, you can set `embark-quit-after-action' to an + alist, associating commands to either `t' for quitting or `nil' for + not quitting. When using an alist, you can use the special key `t' to + specify the default behavior. For example, to specify that by default + actions should not quit the minibuffer but that using `kill-buffer' as + an action should quit, you can use the following configuration: + + ┌──── + │ (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) + └──── + + The variable `embark-quit-after-action' only specifies a default, that + is, it only controls whether or not `embark-act' quits the minibuffer + when you call it without a prefix argument, and you can select the + opposite behavior to what the variable says by calling `embark-act' + with `C-u'. Also note that both the variable + `embark-quit-after-action' and `C-u' have no effect when you call + `embark-act' outside the minibuffer. + + If you find yourself using the quitting and non-quitting variants of + `embark-act' about equally often, independently of the action, you may + prefer to simply have separate commands for them instead of a single + command that you call with `C-u' half the time. You could, for + example, keep the default exiting behavior of `embark-act' and define + a non-quitting version as follows: + + ┌──── + │ (defun embark-act-noquit () + │ "Run action but don't quit the minibuffer afterwards." + │ (interactive) + │ (let ((embark-quit-after-action nil)) + │ (embark-act))) + └──── + + +3.4 Running some setup after injecting the target +───────────────────────────────────────────────── + + You can customize what happens after the target is inserted at the + minibuffer prompt of an action. There are + `embark-target-injection-hooks', that are run by default after + injecting the target into the minibuffer. The variable + `embark-target-injection-hooks' is an alist associating commands to + their setup hooks. There are two special keys: if no setup hook is + specified for a given action, the hook associated to `t' is run; and + the hook associated to `:always' is run regardless of the + action. (This variable used to have the less explicit name of + `embark-setup-action-hooks', so please update your configuration.) + + For example, consider using `shell-command' as an action during file + completion. It would be useful to insert a space before the target + file name and to leave the point at the beginning, so you can + immediately type the shell command to run on that file. That's why in + Embark's default configuration there is an entry in + `embark-target-injection-hooks' associating `shell-command' to a hook + that includes `embark--shell-prep', a simple helper function that + quotes all the spaces in the file name, inserts an extra space at the + beginning of the line and leaves point to the left of it. + + Now, the preparation that `embark--shell-prep' does would be useless + if Embark did what it normally does after it inserts the target of the + action at the minibuffer prompt, which is to "press `RET'" for you, + accepting the target as is; if Embark did that for `shell-command' you + wouldn't get a chance to type in the command to execute! That is why + in Embark's default configuration the entry for `shell-command' in + `embark-target-injection-hooks' also contains the function + `embark--allow-edit'. + + Embark used to have a dedicated variable `embark-allow-edit-actions' + to which you could add commands for which Embark should forgo pressing + `RET' for you after inserting the target. Since its effect can also be + achieved via the general `embark-target-injection-hooks' mechanism, + that variable has been removed to simply Embark. Be sure to update + your configuration; if you had something like: + + ┌──── + │ (add-to-list 'embark-allow-edit-actions 'my-command) + └──── + + you should replace it with: + + ┌──── + │ (push 'embark--allow-edit + │ (alist-get 'my-command embark-target-injection-hooks)) + └──── + + + Also note that while you could abuse `embark--allow-edit' so that you + have to confirm "dangerous" actions such as `delete-file', it is + better to implement confirmation by adding the `embark--confirm' + function to the appropriate entry of a different hook alist, namely, + `embark-pre-action-hooks'. + + Besides `embark--allow-edit', Embark comes with another function that + is of general utility in action setup hooks: + `embark--ignore-target'. Use it for commands that do prompt you in the + minibuffer but for which inserting the target would be + inappropriate. This is not a common situation but does occasionally + arise. For example it is used by default for + `shell-command-on-region': that command is used as an action for + region targets, and it prompts you for a shell command; you typically + do /not/ want the target, that is the contents of the region, to be + entered at that prompt! + + +3.5 Running hooks before, after or around an action +─────────────────────────────────────────────────── + + Embark has three variables, `embark-pre-action-hooks', + `embark-post-action-hooks' and `embark-around-action-hooks', which are + alists associating commands to hooks that should run before or after + or as around advice for the command when used as an action. As with + `embark-target-injection-hooks', there are two special keys for the + alists: `t' designates the default hook to run when no specific hook + is specified for a command; and the hook associated to `:always' runs + regardless. + + The default values of those variables are fairly extensive, adding + creature comforts to make running actions a smooth experience. Embark + comes with several functions intended to be added to these hooks, and + used in the default values of `embark-pre-action-hooks', + `embark-post-action-hooks' and `embark-around-action-hooks'. + + For pre-action hooks: + + `embark--confirm' + Prompt the user for confirmation before executing the + action. This is used be default for commands deemed "dangerous", + or, more accurately, hard to undo, such as `delete-file' and + `kill-buffer'. + + `embark--unmark-target' + Unmark the active region. Use this for commands you want to act + on the region contents but without the region being active. The + default configuration uses this function as a pre-action hook + for `occur' and `query-replace', for example, so that you can + use them as actions with region targets to search the whole + buffer for the text contained in the region. Without this + pre-action hook using `occur' as an action for a region target + would be pointless: it would search for the the region contents + /in the region/, (typically, due to the details of regexps) + finding only one match! + + `embark--beginning-of-target' + Move to the beginning of the target (for targets that report + bounds). This is used by default for backward motion commands + such as `backward-sexp', so that they don't accidentally leave + you on the current target. + + `embark--end-of-target' + Move to the end of the target. This is used similarly to the + previous function, but also for commands that act on the last + s-expression like `eval-last-sexp'. This allow you to act on an + s-expression from anywhere inside it and still use + `eval-last-sexp' as an action. + + `embark--xref-push-markers' + Push the current location on the xref marker stack. Use this for + commands that take you somewhere and for which you'd like to be + able to come back to where you were using + `xref-pop-marker-stack'. This is used by default for + `find-library'. + + For post-action hooks: + + `embark--restart' + Restart the command currently prompting in the minibuffer, so + that the list of completion candidates is updated. This is + useful as a post action hook for commands that delete or rename + a completion candidate; for example the default value of + `embark-post-action-hooks' uses it for `delete-file', + `kill-buffer', `rename-file', `rename-buffer', etc. + + For around-action hooks: + + `embark--mark-target' + Save existing mark and point location, mark the target and run + the action. Most targets at point outside the minibuffer report + which region of the buffer they correspond to (this is the + information used by `embark-highlight-indicator' to know what + portion of the buffer to highlight); this function marks that + region. It is useful as an around action hook for commands that + expect a region to be marked, for example, it is used by default + for `indent-region' so that it works on s-expression targets, or + for `fill-region' so that it works on paragraph targets. + + `embark--cd' + Run the action with `default-directory' set to the directory + associated to the current target. The target should be of type + `file', `buffer', `bookmark' or `library', and the associated + directory is what you'd expect in each case. + + `embark--narrow-to-target' + Run the action with buffer narrowed to current target. Use this + as an around hook to localize the effect of actions that don't + already work on just the region. In the default configuration it + is used for `repunctuate-sentences'. + + `embark--save-excursion' + Run the action restoring point at the end. The current default + configuration doesn't use this but it is available for users. + + +3.6 Creating your own keymaps +───────────────────────────── + + All internal keymaps are defined with the standard helper macro + `defvar-keymap'. For example a simple version of the file action + keymap could be defined as follows: + + ┌──── + │ (defvar-keymap embark-file-map + │ :doc "Example keymap with a few file actions" + │ :parent embark-general-map + │ "d" #'delete-file + │ "r" #'rename-file + │ "c" #'copy-file) + └──── + + These action keymaps are perfectly normal Emacs keymaps. You may want + to inherit from the `embark-general-map' if you want to access the + default Embark actions. Note that `embark-collect' and `embark-export' + are also made available via `embark-general-map'. + + +3.7 Defining actions for new categories of targets +────────────────────────────────────────────────── + + It is easy to configure Embark to provide actions for new types of + targets, either in the minibuffer or outside it. I present below two + very detailed examples of how to do this. At several points I'll + explain more than one way to proceed, typically with the easiest + option first. I include the alternative options since there will be + similar situations where the easiest option is not available. + + +3.7.1 New minibuffer target example - tab-bar tabs +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + As an example, take the new [tab bars] from Emacs 27. I'll explain how + to configure Embark to offer tab-specific actions when you use the + tab-bar-mode commands that mention tabs by name. The configuration + explained here is now built-in to Embark (and Marginalia), but it's + still a good self-contained example. In order to setup up tab actions + you would need to: (1) make sure Embark knows those commands deal with + tabs, (2) define a keymap for tab actions and configure Embark so it + knows that's the keymap you want. + + +[tab bars] +<https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html> + +◊ 3.7.1.1 Telling Embark about commands that prompt for tabs by name + + For step (1), it would be great if the `tab-bar-mode' commands + reported the completion category `tab' when asking you for a tab with + completion. (All built-in Emacs commands that prompt for file names, + for example, do have metadata indicating that they want a `file'.) + They do not, unfortunately, and I will describe a couple of ways to + deal with this. + + Maybe the easiest thing is to configure [Marginalia] to enhance those + commands. All of the `tab-bar-*-tab-by-name' commands have the words + "tab by name" in the minibuffer prompt, so you can use: + + ┌──── + │ (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) + └──── + + That's it! But in case you are ever in a situation where you don't + already have commands that prompt for the targets you want, I'll + describe how writing your own command with appropriate `category' + metadata looks: + + ┌──── + │ (defun my-select-tab-by-name (tab) + │ (interactive + │ (list + │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + │ (tab-bar-tabs)) + │ (user-error "No tabs found")))) + │ (completing-read + │ "Tabs: " + │ (lambda (string predicate action) + │ (if (eq action 'metadata) + │ '(metadata (category . tab)) + │ (complete-with-action + │ action tab-list string predicate))))))) + │ (tab-bar-select-tab-by-name tab)) + └──── + + As you can see, the built-in support for setting the category + meta-datum is not very easy to use or pretty to look at. To help with + this I recommend the `consult--read' function from the excellent + [Consult] package. With that function we can rewrite the command as + follows: + + ┌──── + │ (defun my-select-tab-by-name (tab) + │ (interactive + │ (list + │ (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + │ (tab-bar-tabs)) + │ (user-error "No tabs found")))) + │ (consult--read tab-list + │ :prompt "Tabs: " + │ :category 'tab)))) + │ (tab-bar-select-tab-by-name tab)) + └──── + + Much nicer! No matter how you define the `my-select-tab-by-name' + command, the first approach with Marginalia and prompt detection has + the following advantages: you get the `tab' category for all the + `tab-bar-*-bar-by-name' commands at once, also, you enhance built-in + commands, instead of defining new ones. + + + [Marginalia] <https://github.com/minad/marginalia> + + [Consult] <https://github.com/minad/consult/> + + +◊ 3.7.1.2 Defining and configuring a keymap for tab actions + + Let's say we want to offer select, rename and close actions for tabs + (in addition to Embark general actions, such as saving the tab name to + the kill-ring, which you get for free). Then this will do: + + ┌──── + │ (defvar-keymap embark-tab-actions + │ :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + │ :parent embark-general-map + │ "s" #'tab-bar-select-tab-by-name + │ "r" #'tab-bar-rename-tab-by-name + │ "k" #'tab-bar-close-tab-by-name) + │ + │ (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) + └──── + + What if after using this for a while you feel closing the tab without + confirmation is dangerous? You have a couple of options: + + 1. You can keep using the `tab-bar-close-tab-by-name' command, but + have Embark ask you for confirmation: + ┌──── + │ (push #'embark--confirm + │ (alist-get 'tab-bar-close-tab-by-name + │ embark-pre-action-hooks)) + └──── + + 2. You can write your own command that prompts for confirmation and + use that instead of `tab-bar-close-tab-by-name' in the above + keymap: + ┌──── + │ (defun my-confirm-close-tab-by-name (tab) + │ (interactive "sTab to close: ") + │ (when (y-or-n-p (format "Close tab '%s'? " tab)) + │ (tab-bar-close-tab-by-name tab))) + └──── + + Notice that this is a command you can also use directly from `M-x' + independently of Embark. Using it from `M-x' leaves something to be + desired, though, since you don't get completion for the tab names. + You can fix this if you wish as described in the previous section. + + +3.7.2 New target example in regular buffers - short Wikipedia links +╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + + Say you want to teach Embark to treat text of the form + `wikipedia:Garry_Kasparov' in any regular buffer as a link to + Wikipedia, with actions to open the Wikipedia page in eww or an + external browser or to save the URL of the page in the kill-ring. We + can take advantage of the actions that Embark has preconfigured for + URLs, so all we need to do is teach Embark that + `wikipedia:Garry_Kasparov' stands for the URL + `https://en.wikipedia.org/wiki/Garry_Kasparov'. + + You can be as fancy as you want with the recognized syntax. Here, to + keep the example simple, I'll assume the link matches the regexp + `wikipedia:[[:alnum:]_]+'. We will write a function that looks for a + match surrounding point, and returns an improper list of the form + `'(url actual-url-of-the-page beg . end)' where `beg' and `end' are + the buffer positions where the target starts and ends, and are used by + Embark to highlight the target (if you have + `embark-highlight-indicator' included in the list + `embark-indicators'). + + ┌──── + │ (defun my-short-wikipedia-link () + │ "Target a link at point of the form wikipedia:Page_Name." + │ (save-excursion + │ (let* ((beg (progn (skip-chars-backward "[:alnum:]_:") (point))) + │ (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + │ (str (buffer-substring-no-properties beg end))) + │ (save-match-data + │ (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + │ `(url + │ ,(format "https://en.wikipedia.org/wiki/%s" + │ (match-string 1 str)) + │ ,beg . ,end)))))) + │ + │ (add-to-list 'embark-target-finders 'my-short-wikipedia-link) + └──── + + +4 How does Embark call the actions? +═══════════════════════════════════ + + Embark actions are normal Emacs commands, that is, functions with an + interactive specification. In order to execute an action, Embark calls + the command with `call-interactively', so the command reads user input + exactly as if run directly by the user. For example the command may + open a minibuffer and read a string (`read-from-minibuffer') or open a + completion interface (`completing-read'). If this happens, Embark + takes the target string and inserts it automatically into the + minibuffer, simulating user input this way. After inserting the + string, Embark exits the minibuffer, submitting the input. (The + immediate minibuffer exit can be disabled for specific actions in + order to allow editing the input; this is done by adding the + `embark--allow-edit' function to the appropriate entry of + `embark-target-injection-hooks'). Embark inserts the target string at + the first minibuffer opened by the action command, and if the command + happens to prompt the user for input more than once, the user still + interacts with the second and further prompts in the normal + fashion. Note that if a command does not prompt the user for input in + the minibuffer, Embark still allows you to use it as an action, but of + course, never inserts the target anywhere. (There are plenty of + examples in the default configuration of commands that do not prompt + the user bound to keys in the action maps, most of the region actions, + for instance.) + + This is how Embark manages to reuse normal commands as actions. The + mechanism allows you to use as Embark actions commands that were not + written with Embark in mind (and indeed almost all actions that are + bound by default in Embark's action keymaps are standard Emacs + commands). It also allows you to write new custom actions in such a + way that they are useful even without Embark. + + Staring from version 28.1, Emacs has a variable + `y-or-n-p-use-read-key', which when set to `t' causes `y-or-n-p' to + use `read-key' instead of `read-from-minibuffer'. Setting + `y-or-n-p-use-read-key' to `t' is recommended for Embark users because + it keeps Embark from attempting to insert the target at a `y-or-n-p' + prompt, which would almost never be sensible. Also consider this as a + warning to structure your own action commands so that if they use + `y-or-n-p', they do so only after the prompting for the target. + + Here is a simple example illustrating the various ways of reading + input from the user mentioned above. Bind the following commands to + the `embark-symbol-map' to be used as actions, then put the point on + some symbol and run them with `embark-act': + + ┌──── + │ (defun example-action-command1 () + │ (interactive) + │ (message "The input was `%s'." (read-from-minibuffer "Input: "))) + │ + │ (defun example-action-command2 (arg input1 input2) + │ (interactive "P\nsInput 1: \nsInput 2: ") + │ (message "The first input %swas `%s', and the second was `%s'." + │ (if arg "truly " "") + │ input1 + │ input2)) + │ + │ (defun example-action-command3 () + │ (interactive) + │ (message "Your selection was `%s'." + │ (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + │ + │ (defun example-action-command4 () + │ (interactive) + │ (message "I don't prompt you for input and thus ignore the target!")) + │ + │ (keymap-set embark-symbol-map "X 1" #'example-action-command1) + │ (keymap-set embark-symbol-map "X 2" #'example-action-command2) + │ (keymap-set embark-symbol-map "X 3" #'example-action-command3) + │ (keymap-set embark-symbol-map "X 4" #'example-action-command4) + └──── + + Also note that if you are using the key bindings to call actions, you + can pass prefix arguments to actions in the normal way. For example, + you can use `C-u X2' with the above demonstration actions to make the + message printed by `example-action-command2' more emphatic. This + ability to pass prefix arguments to actions is useful for some actions + in the default configuration, such as + `embark-shell-command-on-buffer'. + + +4.1 Non-interactive functions as actions +──────────────────────────────────────── + + Alternatively, Embark does support one other type of action: a + non-interactive function of a single argument. The target is passed as + argument to the function. For example: + + ┌──── + │ (defun example-action-function (target) + │ (message "The target was `%s'." target)) + │ + │ (keymap-set embark-symbol-map "X 4" #'example-action-function) + └──── + + Note that normally binding non-interactive functions in a keymap is + useless, since when attempting to run them using the key binding you + get an error message similar to "Wrong type argument: commandp, + example-action-function". In general it is more flexible to write any + new Embark actions as commands, that is, as interactive functions, + because that way you can also run them directly, without Embark. But + there are a couple of reasons to use non-interactive functions as + actions: + + 1. You may already have the function lying around, and it is + convenient to simply reuse it. + + 2. For command actions the targets can only be simple string, with no + text properties. For certain advanced uses you may want the action + to receive a string /with/ some text properties, or even a + non-string target. + + +5 Embark, Marginalia and Consult +════════════════════════════════ + + Embark cooperates well with the [Marginalia] and [Consult] packages. + Neither of those packages is a dependency of Embark, but both are + highly recommended companions to Embark, for opposite reasons: + Marginalia greatly enhances Embark's usefulness, while Embark can help + enhance Consult. + + In the remainder of this section I'll explain what exactly Marginalia + does for Embark, and what Embark can do for Consult. + + +[Marginalia] <https://github.com/minad/marginalia> + +[Consult] <https://github.com/minad/consult> + +5.1 Marginalia +────────────── + + Embark comes with actions for symbols (commands, functions, variables + with actions such as finding the definition, looking up the + documentation, evaluating, etc.) in the `embark-symbol-map' keymap, + and for packages (actions like install, delete, browse url, etc.) in + the `embark-package-keymap'. + + Unfortunately Embark does not automatically offers you these keymaps + when relevant, because many built-in Emacs commands don't report + accurate category metadata. For example, a command like + `describe-package', which reads a package name from the minibuffer, + does not have metadata indicating this fact. + + In an earlier Embark version, there were functions to supply this + missing metadata, but they have been moved to Marginalia, which + augments many Emacs command to report accurate category metadata. + Simply activating `marginalia-mode' allows Embark to offer you the + package and symbol actions when appropriate again. Candidate + annotations in the Embark collect buffer are also provided by the + Marginalia package: + + • If you install Marginalia and activate `marginalia-mode', Embark + Collect buffers will use the Marginalia annotations automatically. + + • If you don't install Marginalia, you will see only the annotations + that come with Emacs (such as key bindings in `M-x', or the unicode + characters in `C-x 8 RET'). + + +5.2 Consult +─────────── + + The excellent Consult package provides many commands that use + minibuffer completion, via the `completing-read' function; plenty of + its commands can be considered enhanced versions of built-in Emacs + commands, and some are completely new functionality. One common + enhancement provided in all commands for which it makes sense is + preview functionality, for example `consult-buffer' will show you a + quick preview of a buffer before you actually switch to it. + + If you use both Consult and Embark you should install the + `embark-consult' package which provides integration between the + two. It provides exporters for several Consult commands and also + tweaks the behavior of many Consult commands when used as actions with + `embark-act' in subtle ways that you may not even notice, but make for + a smoother experience. You need only install it to get these benefits: + Embark will automatically load it after Consult if found. + + The `embark-consult' package provides the following exporters: + + • You can use `embark-export' from `consult-line', `consult-outline', + or `consult-mark' to obtain an `occur-mode' buffer. As with the + built-in `occur' command you use that buffer to jump to a match and + after that, you can then use `next-error' and `previous-error' to + navigate to other matches. You can also press `e' to activate + `occur-edit-mode' and edit the matches in place! + + • You can export from any of the Consult asynchronous search commands, + `consult-grep', `consult-git-grep', or `consult-ripgrep' to get a + `grep-mode' buffer. Here too you can use `next-error' and + `previous-error' to navigate among matches, and, if you install the + [wgrep] package, you can use it to edit the matches in place. + + In both cases, pressing `g' will rerun the Consult command you had + exported from and re-enter the input you had typed (which is similar + to reverting but a little more flexible). You can then proceed to + re-export if that's what you want, but you can also edit the input + changing the search terms or simply cancel if you see you are done + with that search. + + The `embark-consult' also contains some candidates collectors that + allow you to run `embark-live' to get a live-updating table of + contents for your buffer: + + • `embark-consult-outline-candidates' produces the outline headings of + the current buffer, using `consult-outline'. + • `embark-consult-imenu-candidates' produces the imenu items of the + current buffer, using `consult-imenu'. + • `embark-consult-imenu-or-outline-candidates' is a simple combination + of the two previous functions: it produces imenu items in buffers + deriving from `prog-mode' and otherwise outline headings. + + The way to configure `embark-live' (or `embark-collect' and + `embark-export' for that matter) to use one of these function is to + add it at the end of the `embark-candidate-collectors' list. The + `embark-consult' package by default adds the last one, which seems to + be the most sensible default. + + Besides those exporters and candidate collectors, the `embark-consult' + package provides many subtle tweaks and small integrations between + Embark and Consult. For example, if you run `embark-collect' from any + of the the `consult-yank' family of commands, you'll see the Embark + Collect buffers has full multi-line kill-ring entries with zebra + stripes, so you can easily tell where they start and end. + + Some examples of little tweaks provided by `embark-consult' to the + behavior of Consult commands when used as Embark actions are: + + • The asynchronous search commands will start in the directory + associated to the Embark target if that target is a file, buffer, + bookmark or Emacs Lisp library. + + • For all other target types, a Consult search command (asynchronous + or not) will search for the text of the target but leave the + minibuffer open so you can interact with the Consult command. + + • `consult-imenu' will search for the target and take you directly to + the location if it matches a unique imenu entry, otherwise it will + leave the minibuffer open so you can navigate among the matches. + + +[wgrep] <http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el +> + + +6 Resources +═══════════ + + If you want to learn more about how others have used Embark here are + some links to read: + + • [Fifteen ways to use Embark], a blog post by Karthik Chikmagalur. + • [Protesilaos Stavrou's dotemacs], look for the section called + "Extended minibuffer actions and more (embark.el and + prot-embark.el)" + + And some videos to watch: + + • [Embark and my extras] by Protesilaos Stavrou. + • [Embark – Key features and tweaks] by Raoul Comninos on the + Emacs-Elements YouTube channel. + • [Livestreamed: Adding an Embark context action to send a stream + message] by Sacha Chua. + • [System Crafters Live! - The Many Uses of Embark] by David Wilson. + • [Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] + by Mike Zamansky. + + +[Fifteen ways to use Embark] +<https://karthinks.com/software/fifteen-ways-to-use-embark/> + +[Protesilaos Stavrou's dotemacs] <https://protesilaos.com/dotemacs/> + +[Embark and my extras] +<https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/> + +[Embark – Key features and tweaks] <https://youtu.be/qpoQiiinCtY> + +[Livestreamed: Adding an Embark context action to send a stream message] +<https://youtu.be/WsxXr1ncukY> + +[System Crafters Live! - The Many Uses of Embark] +<https://youtu.be/qk2Is_sC8Lk> + +[Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark] +<https://youtu.be/5ffb2at2d7w> + + +7 Contributions +═══════════════ + + Contributions to Embark are very welcome. There is a [wish list] for + actions, target finders, candidate collectors and exporters. For other + ideas you have for Embark, feel free to open an issue on the [issue + tracker]. Any neat configuration tricks you find might be a good fit + for the [wiki]. + + Code contributions are very welcome too, but since Embark is now on + GNU ELPA, copyright assignment to the FSF is required before you can + contribute code. + + +[wish list] <https://github.com/oantolin/embark/issues/95> + +[issue tracker] <https://github.com/oantolin/embark/issues> + +[wiki] <https://github.com/oantolin/embark/wiki> + + +8 Acknowledgments +═════════════════ + + While I, Omar Antolín Camarena, have written most of the Embark code + and remain very stubborn about some of the design decisions, Embark + has received substantial help from a number of other people which this + document has neglected to mention for far too long. In particular, + Daniel Mendler has been absolutely invaluable, implementing several + important features, and providing a lot of useful advice. + + Code contributions: + + • [Daniel Mendler] + • [Clemens Radermacher] + • [José Antonio Ortega Ruiz] + • [Itai Y. Efrat] + • [a13] + • [jakanakaevangeli] + • [mihakam] + • [Brian Leung] + • [Karthik Chikmagalur] + • [Roshan Shariff] + • [condy0919] + • [Damien Cassou] + • [JimDBh] + + Advice and useful discussions: + + • [Daniel Mendler] + • [Protesilaos Stavrou] + • [Clemens Radermacher] + • [Howard Melman] + • [Augusto Stoffel] + • [Bruce d'Arcus] + • [JD Smith] + • [Karthik Chikmagalur] + • [jakanakaevangeli] + • [Itai Y. Efrat] + • [Mohsin Kaleem] + + +[Daniel Mendler] <https://github.com/minad> + +[Clemens Radermacher] <https://github.com/clemera/> + +[José Antonio Ortega Ruiz] <https://codeberg.org/jao/> + +[Itai Y. Efrat] <https://github.com/iyefrat> + +[a13] <https://github.com/a13> + +[jakanakaevangeli] <https://github.com/jakanakaevangeli> + +[mihakam] <https://github.com/mihakam> + +[Brian Leung] <https://github.com/leungbk> + +[Karthik Chikmagalur] <https://github.com/karthink> + +[Roshan Shariff] <https://github.com/roshanshariff> + +[condy0919] <https://github.com/condy0919> + +[Damien Cassou] <https://github.com/DamienCassou> + +[JimDBh] <https://github.com/JimDBh> + +[Protesilaos Stavrou] <https://gitlab.com/protesilaos/> + +[Howard Melman] <https://github.com/hmelman/> + +[Augusto Stoffel] <https://github.com/astoff> + +[Bruce d'Arcus] <https://github.com/bdarcus> + +[JD Smith] <https://github.com/jdtsmith> + +[Mohsin Kaleem] <https://github.com/mohkale> diff --git a/elpa/embark-consult-0.7/README.org b/elpa/embark-consult-0.7/README.org @@ -0,0 +1,1117 @@ +#+TITLE: Embark: Emacs Mini-Buffer Actions Rooted in Keymaps +#+OPTIONS: d:nil +#+EXPORT_FILE_NAME: embark.texi +#+TEXINFO_DIR_CATEGORY: Emacs misc features +#+TEXINFO_DIR_TITLE: Embark: (embark). +#+TEXINFO_DIR_DESC: Emacs Mini-Buffer Actions Rooted in Keymaps + +#+html: <a href="http://elpa.gnu.org/packages/embark.html"><img alt="GNU ELPA" src="https://elpa.gnu.org/packages/embark.svg"/></a> +#+html: <a href="http://elpa.gnu.org/devel/embark.html"><img alt="GNU-devel ELPA" src="https://elpa.gnu.org/devel/embark.svg"/></a> +#+html: <a href="https://melpa.org/#/embark"><img alt="MELPA" src="https://melpa.org/packages/embark-badge.svg"/></a> +#+html: <a href="https://stable.melpa.org/#/embark"><img alt="MELPA Stable" src="https://stable.melpa.org/packages/embark-badge.svg"/></a> + +* Overview + +Embark makes it easy to choose a command to run based on what is near +point, both during a minibuffer completion session (in a way familiar +to Helm or Counsel users) and in normal buffers. Bind the command +=embark-act= to a key and it acts like prefix-key for a keymap of +/actions/ (commands) relevant to the /target/ around point. With point on +an URL in a buffer you can open the URL in a browser or eww or +download the file it points to. If while switching buffers you spot an +old one, you can kill it right there and continue to select another. +Embark comes preconfigured with over a hundred actions for common +types of targets such as files, buffers, identifiers, s-expressions, +sentences; and it is easy to add more actions and more target types. +Embark can also collect all the candidates in a minibuffer to an +occur-like buffer or export them to a buffer in a major-mode specific +to the type of candidates, such as dired for a set of files, ibuffer +for a set of buffers, or customize for a set of variables. + +** Acting on targets + +You can think of =embark-act= as a keyboard-based version of a +right-click contextual menu. The =embark-act= command (which you should +bind to a convenient key), acts as a prefix for a keymap offering you +relevant /actions/ to use on a /target/ determined by the context: + +- In the minibuffer, the target is the current top completion + candidate. +- In the =*Completions*= buffer the target is the completion at point. +- In a regular buffer, the target is the region if active, or else the + file, symbol, URL, s-expression or defun at point. + +Multiple targets can be present at the same location and you can cycle +between them by repeating the =embark-act= key binding. The type of +actions offered depend on the type of the target. Here is a sample of +a few of the actions offered in the default configuration: + +- For files you get offered actions like deleting, copying, + renaming, visiting in another window, running a shell command on the + file, etc. +- For buffers the actions include switching to or killing the buffer. +- For package names the actions include installing, removing or + visiting the homepage. +- For Emacs Lisp symbols the actions include finding the definition, + looking up documentation, evaluating (which for a variable + immediately shows the value, but for a function lets you pass it + some arguments first). There are some actions specific to variables, + such as setting the value directly or though the customize system, + and some actions specific to commands, such as binding it to a key. + +By default when you use =embark-act= if you don't immediately select an +action, after a short delay Embark will pop up a buffer showing a list +of actions and their corresponding key bindings. If you are using +=embark-act= outside the minibuffer, Embark will also highlight the +current target. These behaviors are configurable via the variable +=embark-indicators=. Instead of selecting an action via its key binding, +you can select it by name with completion by typing =C-h= after +=embark-act=. + +Everything is easily configurable: determining the current target, +classifying it, and deciding which actions are offered for each type +in the classification. The above introduction just mentions part of +the default configuration. + +Configuring which actions are offered for a type is particularly easy +and requires no programming: the variable =embark-keymap-alist= +associates target types with variables containing keymaps, and those +keymaps containing bindings for the actions. (To examine the available +categories and their associated keymaps, you can use =C-h v +embark-keymap-alist= or customize that variable.) For example, in the +default configuration the type =file= is associated with the symbol +=embark-file-map=. That symbol names a keymap with single-letter key +bindings for common Emacs file commands, for instance =c= is bound to +=copy-file=. This means that if you are in the minibuffer after running +a command that prompts for a file, such as =find-file= or =rename-file=, +you can copy a file by running =embark-act= and then pressing =c=. + +These action keymaps are very convenient but not strictly necessary +when using =embark-act=: you can use any command that reads from the +minibuffer as an action and the target of the action will be inserted +at the first minibuffer prompt. After running =embark-act= all of your +key bindings and even =execute-extended-command= can be used to run a +command. For example, if you want to replace all occurrences of the +symbol at point, just use =M-%= as the action, there is no need to bind +=query-replace= in one of Embark's keymaps. Also, those action keymaps +are normal Emacs keymaps and you should feel free to bind in them +whatever commands you find useful as actions and want to be available +through convenient bindings. + +The actions in =embark-general-map= are available no matter what type +of completion you are in the middle of. By default this includes +bindings to save the current candidate in the kill ring and to insert +the current candidate in the previously selected buffer (the buffer +that was current when you executed a command that opened up the +minibuffer). + +Emacs's minibuffer completion system includes metadata indicating the +/category/ of what is being completed. For example, =find-file='s +metadata indicates a category of =file= and =switch-to-buffer='s metadata +indicates a category of =buffer=. Embark has the related notion of the +/type/ of a target for actions, and by default when category metadata +is present it is taken to be the type of minibuffer completion +candidates when used as targets. Emacs commands often do not set +useful category metadata so the [[https://github.com/minad/marginalia][Marginalia]] package, which supplies +this missing metadata, is highly recommended for use with Embark. + +Embark's default configuration has actions for the following target +types: files, buffers, symbols, packages, URLs, bookmarks, and as a +somewhat special case, actions for when the region is active. You can +read about the [[https://github.com/oantolin/embark/wiki/Default-Actions][default actions and their key bindings]] on the GitHub +project wiki. + +** The default action on a target + +Embark has a notion of default action for a target: + +- If the target is a minibuffer completion candidate, then the default + action is whatever command opened the minibuffer in the first place. + For example if you run =kill-buffer=, then the default action will be + to kill buffers. +- If the target comes from a regular buffer (i.e., not a minibuffer), + then the default action is whatever is bound to =RET= in the keymap of + actions for that type of target. For example, in Embark's default + configuration for a URL found at point the default action is + =browse-url=, because =RET= is bound to =browse-url= in the =embark-url-map= + keymap. + +To run the default action you can press =RET= after running =embark-act=. +Note that if there are several different targets at a given location, +each has its own default action, so first cycle to the target you want +and then press =RET= to run the corresponding default action. + +There is also =embark-dwim= which runs the default action for the first +target found. It's pretty handy in non-minibuffer buffers: with +Embark's default configuration it will: + +- Open the file at point. +- Open the URL at point in a web browser (using the =browse-url= + command). +- Compose a new email to the email address at point. +- In an Emacs Lisp buffer, if point is on an opening parenthesis or + right after a closing one, it will evaluate the corresponding + expression. +- Go to the definition of an Emacs Lisp function, variable or macro at + point. +- Find the file corresponding to an Emacs Lisp library at point. + +** Working with sets of possible targets + +Besides acting individually on targets, Embark lets you work +collectively on a set of target /candidates/. For example, while you are +in the minibuffer the candidates are simply the possible completions +of your input. Embark provides three main commands to work on candidate +sets: + +- The =embark-act-all= command runs the same action on each of the + current candidates. It is just like using =embark-act= on each + candidate in turn. (Because you can easily act on many more + candidates than you meant to, by default Embark asks you to confirm + uses of =embark-act-all=; you can turn this off by setting the user + option =embark-confirm-act-all= to =nil=.) + +- The =embark-collect= command produces a buffer listing all the current + candidates, for you to peruse and run actions on at your leisure. + The candidates are displayed as a list showing additional annotations. + + The Embark Collect buffer is "dired-like": you can mark and unmark + candidates with =m= and =u=, you can unmark all marked candidates with =U= + or toggle the marks with =t=. In an Embark Collect buffer + =embark-act-all= is bound to =A= and will act on all currently marked + candidates if there any, and will act on all candidates if none are + marked. + +- The =embark-export= command tries to open a buffer in an appropriate + major mode for the set of candidates. If the candidates are files + export produces a Dired buffer; if they are buffers, you get an + Ibuffer buffer; and if they are packages you get a buffer in + package menu mode. + + If you use the grepping commands from the [[https://github.com/minad/consult/][Consult]] package, + =consult-grep=, =consult-git-grep= or =consult-ripgrep=, then you should + install the =embark-consult= package, which adds support for exporting a + list of grep results to an honest grep-mode buffer, on which you can + even use [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]] if you wish. + +When in doubt choosing between exporting and collecting, a good rule +of thumb is to always prefer =embark-export= since when an exporter to a +special major mode is available for a given type of target, it will be +more featureful than an Embark collect buffer, and if no such exporter +is configured the =embark-export= command falls back to the generic +=embark-collect=. + +These commands are always available as "actions" (although they do not +act on just the current target but on all candidates) for =embark-act= +and are bound to =A=, =S= (for "snapshot"), and =E=, respectively, in +=embark-general-map=. This means that you do not have to bind your own +key bindings for these (although you can, of course!), just a key +binding for =embark-act=. + +In Embark Collect or Embark Export buffers that were obtained by +running =embark-collect= or =embark-export= from within a minibuffer +completion session, =g= is bound to a command that restarts the +completion session, that is, the command that opened the minibuffer is +run again and the minibuffer contents restored. You can then interact +normally with the command, perhaps editing the minibuffer contents, +and, if you wish, you can rerun =embark-collect= or =embark-export= to get +an updated buffer. + +*** =embark-live= a live-updating variant of =embark-collect= + +Finally, there is also an =embark-live= variant of the =embark-collect= +command which automatically updates the collection after each change +in the source buffer. Users of a completion UI that automatically +updates and displays the candidate list (such as Vertico, Icomplete, +Selectrum, Fido-mode, or MCT) will probably not want to use +=embark-live= from the minibuffer as they will then have two live +updating displays of the completion candidates! + +A more likely use of =embark-live= is to be called from a regular buffer +to display a sort of live updating "table of contents" for the buffer. +This depends on having appropriate candidate collectors configured in +=embark-candidate-collectors=. There are not many in Embark's default +configuration, but you can try this experiment: open a dired buffer in +a directory that has very many files, mark a few, and run =embark-live=. +You'll get an Embark Collect buffer containing only the marked files, +which updates as you mark or unmark files in dired. To make +=embark-live= genuinely useful other candidate collectors are required. +The =embark-consult= package (documented near the end of this manual) +contains a few: one for imenu items and one for outline headings as +used by =outline-minor-mode=. Those collectors really do give +=embark-live= a table-of-contents feel. + +** Switching to a different command without losing what you've typed + +Embark also has the =embark-become= command which is useful for when +you run a command, start typing at the minibuffer and realize you +meant a different command. The most common case for me is that I run +=switch-to-buffer=, start typing a buffer name and realize I haven't +opened the file I had in mind yet! I'll use this situation as a +running example to illustrate =embark-become=. When this happens I can, +of course, press =C-g= and then run =find-file= and open the file, but +this requires retyping the portion of the file name you already +typed. This process can be streamlined with =embark-become=: while still +in the =switch-to-buffer= you can run =embark-become= and effectively +make the =switch-to-buffer= command become =find-file= for this run. + +You can bind =embark-become= to a key in =minibuffer-local-map=, but it is +also available as an action under the letter =B= (uppercase), so you +don't need a binding if you already have one for =embark-act=. So, +assuming I have =embark-act= bound to, say, =C-.=, once I realize I +haven't open the file I can type =C-. B C-x C-f= to have +=switch-to-buffer= become =find-file= without losing what I have already +typed in the minibuffer. + +But for even more convenience, =embark-become= offers shorter key +bindings for commands you are likely to want the current command to +become. When you use =embark-become= it looks for the current command in +all keymaps named in the list =embark-become-keymaps= and then activates +all keymaps that contain it. For example, the default value of +=embark-become-keymaps= contains a keymap =embark-become-file+buffer-map= +with bindings for several commands related to files and buffers, in +particular, it binds =switch-to-buffer= to =b= and =find-file= to =f=. So when +I accidentally try to switch to a buffer for a file I haven't opened +yet, =embark-become= finds that the command I ran, =switch-to-buffer=, is +in the keymap =embark-become-file+buffer-map=, so it activates that +keymap (and any others that also contain a binding for +=switch-to-buffer=). The end result is that I can type =C-. B f= to +switch to =find-file=. + +* Quick start + +The easiest way to install Embark is from GNU ELPA, just run =M-x +package-install RET embark RET=. (It is also available on MELPA.) It is +highly recommended to also install [[https://github.com/minad/marginalia][Marginalia]] (also available on GNU +ELPA), so that Embark can offer you preconfigured actions in more +contexts. For =use-package= users, the following is a very reasonable +starting configuration: + +#+begin_src emacs-lisp + (use-package marginalia + :ensure t + :config + (marginalia-mode)) + + (use-package embark + :ensure t + + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + :config + + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + + ;; Consult users will also want the embark-consult package. + (use-package embark-consult + :ensure t ; only need to install it, embark loads it after consult if found + :hook + (embark-collect-mode . consult-preview-at-point-mode)) +#+end_src + +About the suggested key bindings for =embark-act= and =embark-dwim=: +- Those key bindings are unlikely to work in the terminal, but + terminal users are probably well aware of this and will know to + select different bindings. +- The suggested =C-.= binding is used by default in (at least some + installations of) GNOME to input emojis, and Emacs doesn't even get + a chance to respond to the binding. You can select a different key + binding for =embark-act= or use =ibus-setup= to change the shortcut for + emoji insertion (Emacs 29 will likely use =C-x 8 e e=, in case you + want to set the same one system-wide). +- The suggested alternative of =M-.= for =embark-dwim= is bound by default + to =xref-find-definitions=. That is a very useful command but + overwriting it with =embark-dwim= is sensible since in Embark's + default configuration, =embark-dwim= will also find the definition of + the identifier at point. (Note that =xref-find-definitions= with a + prefix argument prompts you for an identifier, =embark-dwim= does not + cover this case). + +Other Embark commands such as =embark-act-all=, =embark-become=, +=embark-collect=, and =embark-export= can be run through =embark-act= as +actions bound to =A=, =B=, =S= (for "snapshot"), and =E= respectively, and +thus don't really need a dedicated key binding, but feel free to bind +them directly if you so wish. If you do choose to bind them directly, +you'll probably want to bind them in =minibuffer-local-map=, since they +are most useful in the minibuffer (in fact, =embark-become= only works +in the minibuffer). + +The command =embark-dwim= executes the default action at point. Another good +keybinding for =embark-dwim= is =M-.= since =embark-dwim= acts like +=xref-find-definitions= on the symbol at point. =C-.= can be seen as a +right-click context menu at point and =M-.= acts like left-click. The +keybindings are mnemonic, both act at the point (=.=). + +Embark needs to know what your minibuffer completion system considers +to be the list of candidates and which one is the current candidate. +Embark works out of the box if you use Emacs's default tab completion, +the built-in =icomplete-mode= or =fido-mode=, or the third-party packages +[[https://github.com/minad/vertico][Vertico]], [[https://github.com/raxod502/selectrum/][Selectrum]] or [[https://github.com/abo-abo/swiper][Ivy]]. + +If you are a [[https://emacs-helm.github.io/helm/][Helm]] or [[https://github.com/abo-abo/swiper][Ivy]] user you are unlikely to want Embark since +those packages include comprehensive functionality for acting on +minibuffer completion candidates. (Embark does come with Ivy +integration despite this.) + +* Advanced configuration +** Showing information about available targets and actions + +By default, if you run =embark-act= and do not immediately select an +action, after a short delay Embark will pop up a buffer called =*Embark +Actions*= containing a list of available actions with their key +bindings. You can scroll that buffer with the mouse of with the usual +commands =scroll-other-window= and =scroll-other-window-down= (bound by +default to =C-M-v= and =C-M-S-v=). + +That functionality is provided by the =embark-mixed-indicator=, but +Embark has other indicators that can provide information about the +target and its type, what other targets you can cycle to, and which +actions have key bindings in the action map for the current type of +target. Any number of indicators can be active at once and the user +option =embark-indicators= should be set to a list of the desired +indicators. + +Embark comes with the following indicators: + +- =embark-minimal-indicator=: shows a messages in the echo area or + minibuffer prompt showing the current target and the types of all + targets starting with the current one; this one is on by default. + +- =embark-highlight-indicator=: highlights the target at point; + also on by default. + +- =embark-verbose-indicator=: displays a table of actions and their key + bindings in a buffer; this is not on by default, in favor of the + mixed indicator described next. + +- =embark-mixed-indicator=: starts out by behaving as the minimal + indicator but after a short delay acts as the verbose indicator; + this is on by default. + +- =embark-isearch-highlight-indicator=: this only does something when + the current target is the symbol at point, in which case it + lazily highlights all occurrences of that symbol in the current + buffer, like isearch; also on by default. + +Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the +=embark-which-key-indicator= from the [[https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt][Embark wiki]]. Just copy its +definition from the wiki into your configuration and customize the +=embark-indicators= user option to exclude the mixed and verbose +indicators and to include =embark-which-key-indicator=. + +** Selecting commands via completions instead of key bindings + +As an alternative to reading the list of actions in the verbose or +mixed indicators (see the previous section for a description of +these), you can press the =embark-help-key=, which is =C-h= by default +(but you may prefer =?= to free up =C-h= for use as a prefix) after +running =embark-act=. Pressing the help key will prompt you for the name +of an action with completion (but feel free to enter a command that is +not among the offered candidates!), and will also remind you of the +key bindings. You can press =embark-keymap-prompter-key=, which is =@= by +default, at the prompt and then one of the key bindings to enter the +name of the corresponding action. + +You may think that with the =*Embark Actions*= buffer popping up to +remind you of the key bindings you'd never want to use completion to +select an action by name, but personally I find that typing a small +portion of the action name to narrow down the list of candidates feels +significantly faster than visually scanning the entire list of actions. + +If you find you prefer entering actions that way, you can configure +embark to always prompt you for actions by setting the variable +=embark-prompter= to =embark-completing-read-prompter=. + +** Quitting the minibuffer after an action + +By default, if you call =embark-act= from the minibuffer it quits the +minibuffer after performing the action. You can change this by setting +the user option =embark-quit-after-action= to =nil=. Having =embark-act= /not/ +quit the minibuffer can be useful to turn commands into little "thing +managers". For example, you can use =find-file= as a little file manager +or =describe-package= as a little package manager: you can run those +commands, perform a series of actions, and then quit the command. + +If you want to control the quitting behavior in a fine-grained manner +depending on the action, you can set =embark-quit-after-action= to an +alist, associating commands to either =t= for quitting or =nil= for not +quitting. When using an alist, you can use the special key =t= to +specify the default behavior. For example, to specify that by default +actions should not quit the minibuffer but that using =kill-buffer= as +an action should quit, you can use the following configuration: + +#+begin_src emacs-lisp + (setq embark-quit-after-action '((kill-buffer . t) (t . nil))) +#+end_src + +The variable =embark-quit-after-action= only specifies a default, that +is, it only controls whether or not =embark-act= quits the minibuffer +when you call it without a prefix argument, and you can select the +opposite behavior to what the variable says by calling =embark-act= with +=C-u=. Also note that both the variable =embark-quit-after-action= and =C-u= +have no effect when you call =embark-act= outside the minibuffer. + +If you find yourself using the quitting and non-quitting variants of +=embark-act= about equally often, independently of the action, you may +prefer to simply have separate commands for them instead of a single +command that you call with =C-u= half the time. You could, for example, +keep the default exiting behavior of =embark-act= and define a +non-quitting version as follows: + +#+begin_src emacs-lisp + (defun embark-act-noquit () + "Run action but don't quit the minibuffer afterwards." + (interactive) + (let ((embark-quit-after-action nil)) + (embark-act))) +#+end_src + +** Running some setup after injecting the target + +You can customize what happens after the target is inserted at the +minibuffer prompt of an action. There are +=embark-target-injection-hooks=, that are run by default after injecting +the target into the minibuffer. The variable +=embark-target-injection-hooks= is an alist associating commands to +their setup hooks. There are two special keys: if no setup hook is +specified for a given action, the hook associated to =t= is run; and the +hook associated to =:always= is run regardless of the action. (This +variable used to have the less explicit name of +=embark-setup-action-hooks=, so please update your configuration.) + +For example, consider using =shell-command= as an action during file +completion. It would be useful to insert a space before the target +file name and to leave the point at the beginning, so you can +immediately type the shell command to run on that file. That's why in +Embark's default configuration there is an entry in +=embark-target-injection-hooks= associating =shell-command= to a hook that +includes =embark--shell-prep=, a simple helper function that quotes all +the spaces in the file name, inserts an extra space at the beginning +of the line and leaves point to the left of it. + +Now, the preparation that =embark--shell-prep= does would be useless if +Embark did what it normally does after it inserts the target of the +action at the minibuffer prompt, which is to "press =RET=" for you, +accepting the target as is; if Embark did that for =shell-command= you +wouldn't get a chance to type in the command to execute! That is why +in Embark's default configuration the entry for =shell-command= in +=embark-target-injection-hooks= also contains the function +=embark--allow-edit=. + +Embark used to have a dedicated variable =embark-allow-edit-actions= to +which you could add commands for which Embark should forgo pressing +=RET= for you after inserting the target. Since its effect can also be +achieved via the general =embark-target-injection-hooks= mechanism, that +variable has been removed to simply Embark. Be sure to update your +configuration; if you had something like: + +#+begin_src emacs-lisp + (add-to-list 'embark-allow-edit-actions 'my-command) +#+end_src + +you should replace it with: + +#+begin_src emacs-lisp + (push 'embark--allow-edit + (alist-get 'my-command embark-target-injection-hooks)) +#+end_src + + +Also note that while you could abuse =embark--allow-edit= so that you +have to confirm "dangerous" actions such as =delete-file=, it is better +to implement confirmation by adding the =embark--confirm= function to +the appropriate entry of a different hook alist, namely, +=embark-pre-action-hooks=. + +Besides =embark--allow-edit=, Embark comes with another function that is +of general utility in action setup hooks: =embark--ignore-target=. Use +it for commands that do prompt you in the minibuffer but for which +inserting the target would be inappropriate. This is not a common +situation but does occasionally arise. For example it is used by +default for =shell-command-on-region=: that command is used as an action +for region targets, and it prompts you for a shell command; you +typically do /not/ want the target, that is the contents of the region, +to be entered at that prompt! + +** Running hooks before, after or around an action + +Embark has three variables, =embark-pre-action-hooks=, +=embark-post-action-hooks= and =embark-around-action-hooks=, which are +alists associating commands to hooks that should run before or after +or as around advice for the command when used as an action. As with +=embark-target-injection-hooks=, there are two special keys for the +alists: =t= designates the default hook to run when no specific hook is +specified for a command; and the hook associated to =:always= runs +regardless. + +The default values of those variables are fairly extensive, adding +creature comforts to make running actions a smooth experience. Embark +comes with several functions intended to be added to these hooks, and +used in the default values of =embark-pre-action-hooks=, +=embark-post-action-hooks= and =embark-around-action-hooks=. + +For pre-action hooks: + +- =embark--confirm= :: Prompt the user for confirmation before executing + the action. This is used be default for commands deemed "dangerous", + or, more accurately, hard to undo, such as =delete-file= and + =kill-buffer=. + +- =embark--unmark-target= :: Unmark the active region. Use this for + commands you want to act on the region contents but without the + region being active. The default configuration uses this function as + a pre-action hook for =occur= and =query-replace=, for example, so that + you can use them as actions with region targets to search the whole + buffer for the text contained in the region. Without this pre-action + hook using =occur= as an action for a region target would be + pointless: it would search for the the region contents /in the + region/, (typically, due to the details of regexps) finding only one + match! + +- =embark--beginning-of-target= :: Move to the beginning of the target + (for targets that report bounds). This is used by default for + backward motion commands such as =backward-sexp=, so that they don't + accidentally leave you on the current target. + +- =embark--end-of-target= :: Move to the end of the target. This is used + similarly to the previous function, but also for commands that act + on the last s-expression like =eval-last-sexp=. This allow you to act + on an s-expression from anywhere inside it and still use + =eval-last-sexp= as an action. + +- =embark--xref-push-markers= :: Push the current location on the xref + marker stack. Use this for commands that take you somewhere and for + which you'd like to be able to come back to where you were using + =xref-pop-marker-stack=. This is used by default for =find-library=. + +For post-action hooks: + +- =embark--restart= :: Restart the command currently prompting in the + minibuffer, so that the list of completion candidates is updated. + This is useful as a post action hook for commands that delete or + rename a completion candidate; for example the default value of + =embark-post-action-hooks= uses it for =delete-file=, =kill-buffer=, + =rename-file=, =rename-buffer=, etc. + +For around-action hooks: + +- =embark--mark-target= :: Save existing mark and point location, mark + the target and run the action. Most targets at point outside the + minibuffer report which region of the buffer they correspond to + (this is the information used by =embark-highlight-indicator= to + know what portion of the buffer to highlight); this function marks + that region. It is useful as an around action hook for commands that + expect a region to be marked, for example, it is used by default for + =indent-region= so that it works on s-expression targets, or for + =fill-region= so that it works on paragraph targets. + +- =embark--cd= :: Run the action with =default-directory= set to the + directory associated to the current target. The target should be of + type =file=, =buffer=, =bookmark= or =library=, and the associated directory + is what you'd expect in each case. + +- =embark--narrow-to-target= :: Run the action with buffer narrowed to + current target. Use this as an around hook to localize the effect of + actions that don't already work on just the region. In the default + configuration it is used for =repunctuate-sentences=. + +- =embark--save-excursion= :: Run the action restoring point at the end. + The current default configuration doesn't use this but it is + available for users. + +** Creating your own keymaps + +All internal keymaps are defined with the standard helper macro +=defvar-keymap=. For example a simple version of the file action keymap +could be defined as follows: + +#+BEGIN_SRC emacs-lisp + (defvar-keymap embark-file-map + :doc "Example keymap with a few file actions" + :parent embark-general-map + "d" #'delete-file + "r" #'rename-file + "c" #'copy-file) +#+END_SRC + +These action keymaps are perfectly normal Emacs +keymaps. You may want to inherit from the =embark-general-map= if you +want to access the default Embark actions. Note that =embark-collect= +and =embark-export= are also made available via =embark-general-map=. + +** Defining actions for new categories of targets + +It is easy to configure Embark to provide actions for new types of +targets, either in the minibuffer or outside it. I present below two +very detailed examples of how to do this. At several points I'll +explain more than one way to proceed, typically with the easiest +option first. I include the alternative options since there will be +similar situations where the easiest option is not available. + +*** New minibuffer target example - tab-bar tabs + +As an example, take the new [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html][tab bars]] from Emacs 27. I'll explain how +to configure Embark to offer tab-specific actions when you use the +tab-bar-mode commands that mention tabs by name. The configuration +explained here is now built-in to Embark (and Marginalia), but it's +still a good self-contained example. In order to setup up tab actions +you would need to: (1) make sure Embark knows those commands deal with +tabs, (2) define a keymap for tab actions and configure Embark so it +knows that's the keymap you want. + +**** Telling Embark about commands that prompt for tabs by name + +For step (1), it would be great if the =tab-bar-mode= commands reported +the completion category =tab= when asking you for a tab with +completion. (All built-in Emacs commands that prompt for file names, +for example, do have metadata indicating that they want a =file=.) They +do not, unfortunately, and I will describe a couple of ways to deal +with this. + +Maybe the easiest thing is to configure [[https://github.com/minad/marginalia][Marginalia]] to enhance those +commands. All of the =tab-bar-*-tab-by-name= commands have the words +"tab by name" in the minibuffer prompt, so you can use: + +#+begin_src emacs-lisp + (add-to-list 'marginalia-prompt-categories '("tab by name" . tab)) +#+end_src + +That's it! But in case you are ever in a situation where you don't +already have commands that prompt for the targets you want, I'll +describe how writing your own command with appropriate =category= +metadata looks: + +#+begin_src emacs-lisp + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (completing-read + "Tabs: " + (lambda (string predicate action) + (if (eq action 'metadata) + '(metadata (category . tab)) + (complete-with-action + action tab-list string predicate))))))) + (tab-bar-select-tab-by-name tab)) +#+end_src + +As you can see, the built-in support for setting the category +meta-datum is not very easy to use or pretty to look at. To help with +this I recommend the =consult--read= function from the excellent +[[https://github.com/minad/consult/][Consult]] package. With that function we can rewrite the command as +follows: + +#+begin_src emacs-lisp + (defun my-select-tab-by-name (tab) + (interactive + (list + (let ((tab-list (or (mapcar (lambda (tab) (cdr (assq 'name tab))) + (tab-bar-tabs)) + (user-error "No tabs found")))) + (consult--read tab-list + :prompt "Tabs: " + :category 'tab)))) + (tab-bar-select-tab-by-name tab)) +#+end_src + +Much nicer! No matter how you define the =my-select-tab-by-name= +command, the first approach with Marginalia and prompt detection has +the following advantages: you get the =tab= category for all the +=tab-bar-*-bar-by-name= commands at once, also, you enhance built-in +commands, instead of defining new ones. + +**** Defining and configuring a keymap for tab actions + + Let's say we want to offer select, rename and close actions for tabs + (in addition to Embark general actions, such as saving the tab name to + the kill-ring, which you get for free). Then this will do: + + #+begin_src emacs-lisp + (defvar-keymap embark-tab-actions + :doc "Keymap for actions for tab-bar tabs (when mentioned by name)." + :parent embark-general-map + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + + (add-to-list 'embark-keymap-alist '(tab . embark-tab-actions)) + #+end_src + + What if after using this for a while you feel closing the tab + without confirmation is dangerous? You have a couple of options: + + 1. You can keep using the =tab-bar-close-tab-by-name= command, but have + Embark ask you for confirmation: + #+begin_src emacs-lisp + (push #'embark--confirm + (alist-get 'tab-bar-close-tab-by-name + embark-pre-action-hooks)) + #+end_src + + 2. You can write your own command that prompts for confirmation and + use that instead of =tab-bar-close-tab-by-name= in the above keymap: + #+begin_src emacs-lisp + (defun my-confirm-close-tab-by-name (tab) + (interactive "sTab to close: ") + (when (y-or-n-p (format "Close tab '%s'? " tab)) + (tab-bar-close-tab-by-name tab))) + #+end_src + + Notice that this is a command you can also use directly from =M-x= + independently of Embark. Using it from =M-x= leaves something to be + desired, though, since you don't get completion for the tab names. + You can fix this if you wish as described in the previous section. + +*** New target example in regular buffers - short Wikipedia links + +Say you want to teach Embark to treat text of the form +=wikipedia:Garry_Kasparov= in any regular buffer as a link to Wikipedia, +with actions to open the Wikipedia page in eww or an external browser +or to save the URL of the page in the kill-ring. We can take advantage +of the actions that Embark has preconfigured for URLs, so all we need +to do is teach Embark that =wikipedia:Garry_Kasparov= stands for the URL +=https://en.wikipedia.org/wiki/Garry_Kasparov=. + +You can be as fancy as you want with the recognized syntax. Here, to +keep the example simple, I'll assume the link matches the regexp +=wikipedia:[[:alnum:]_]+=. We will write a function that looks for a +match surrounding point, and returns an improper list of the form +='(url actual-url-of-the-page beg . end)= where =beg= and =end= are the +buffer positions where the target starts and ends, and are used by +Embark to highlight the target (if you have =embark-highlight-indicator= +included in the list =embark-indicators=). + +#+begin_src emacs-lisp + (defun my-short-wikipedia-link () + "Target a link at point of the form wikipedia:Page_Name." + (save-excursion + (let* ((beg (progn (skip-chars-backward "[:alnum:]_:") (point))) + (end (progn (skip-chars-forward "[:alnum:]_:") (point))) + (str (buffer-substring-no-properties beg end))) + (save-match-data + (when (string-match "wikipedia:\\([[:alnum:]_]+\\)" str) + `(url + ,(format "https://en.wikipedia.org/wiki/%s" + (match-string 1 str)) + ,beg . ,end)))))) + + (add-to-list 'embark-target-finders 'my-short-wikipedia-link) +#+end_src + +* How does Embark call the actions? + + Embark actions are normal Emacs commands, that is, functions with an + interactive specification. In order to execute an action, Embark + calls the command with =call-interactively=, so the command reads user + input exactly as if run directly by the user. For example the + command may open a minibuffer and read a string + (=read-from-minibuffer=) or open a completion interface + (=completing-read=). If this happens, Embark takes the target string + and inserts it automatically into the minibuffer, simulating user + input this way. After inserting the string, Embark exits the + minibuffer, submitting the input. (The immediate minibuffer exit can + be disabled for specific actions in order to allow editing the + input; this is done by adding the =embark--allow-edit= function to the + appropriate entry of =embark-target-injection-hooks=). Embark inserts + the target string at the first minibuffer opened by the action + command, and if the command happens to prompt the user for input + more than once, the user still interacts with the second and further + prompts in the normal fashion. Note that if a command does not + prompt the user for input in the minibuffer, Embark still allows you + to use it as an action, but of course, never inserts the target + anywhere. (There are plenty of examples in the default configuration + of commands that do not prompt the user bound to keys in the action + maps, most of the region actions, for instance.) + + This is how Embark manages to reuse normal commands as actions. The + mechanism allows you to use as Embark actions commands that were not + written with Embark in mind (and indeed almost all actions that are + bound by default in Embark's action keymaps are standard Emacs + commands). It also allows you to write new custom actions in such a + way that they are useful even without Embark. + + Staring from version 28.1, Emacs has a variable + =y-or-n-p-use-read-key=, which when set to =t= causes =y-or-n-p= to use + =read-key= instead of =read-from-minibuffer=. Setting + =y-or-n-p-use-read-key= to =t= is recommended for Embark users because + it keeps Embark from attempting to insert the target at a =y-or-n-p= + prompt, which would almost never be sensible. Also consider this as + a warning to structure your own action commands so that if they use + =y-or-n-p=, they do so only after the prompting for the target. + + Here is a simple example illustrating the various ways of reading + input from the user mentioned above. Bind the following commands to + the =embark-symbol-map= to be used as actions, then put the point on + some symbol and run them with =embark-act=: + + #+begin_src emacs-lisp + (defun example-action-command1 () + (interactive) + (message "The input was `%s'." (read-from-minibuffer "Input: "))) + + (defun example-action-command2 (arg input1 input2) + (interactive "P\nsInput 1: \nsInput 2: ") + (message "The first input %swas `%s', and the second was `%s'." + (if arg "truly " "") + input1 + input2)) + + (defun example-action-command3 () + (interactive) + (message "Your selection was `%s'." + (completing-read "Select: " '("E" "M" "B" "A" "R" "K")))) + + (defun example-action-command4 () + (interactive) + (message "I don't prompt you for input and thus ignore the target!")) + + (keymap-set embark-symbol-map "X 1" #'example-action-command1) + (keymap-set embark-symbol-map "X 2" #'example-action-command2) + (keymap-set embark-symbol-map "X 3" #'example-action-command3) + (keymap-set embark-symbol-map "X 4" #'example-action-command4) + #+end_src + + Also note that if you are using the key bindings to call actions, + you can pass prefix arguments to actions in the normal way. For + example, you can use =C-u X2= with the above demonstration actions to + make the message printed by =example-action-command2= more emphatic. + This ability to pass prefix arguments to actions is useful for some + actions in the default configuration, such as + =embark-shell-command-on-buffer=. + +** Non-interactive functions as actions + + Alternatively, Embark does support one other type of action: a + non-interactive function of a single argument. The target is passed + as argument to the function. For example: + + #+begin_src emacs-lisp + (defun example-action-function (target) + (message "The target was `%s'." target)) + + (keymap-set embark-symbol-map "X 4" #'example-action-function) + #+end_src + + Note that normally binding non-interactive functions in a keymap is + useless, since when attempting to run them using the key binding you + get an error message similar to "Wrong type argument: commandp, + example-action-function". In general it is more flexible to write + any new Embark actions as commands, that is, as interactive + functions, because that way you can also run them directly, without + Embark. But there are a couple of reasons to use non-interactive + functions as actions: + + 1. You may already have the function lying around, and it is + convenient to simply reuse it. + + 2. For command actions the targets can only be simple string, with + no text properties. For certain advanced uses you may want the + action to receive a string /with/ some text properties, or even a + non-string target. + +* Embark, Marginalia and Consult + +Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. +Neither of those packages is a dependency of Embark, but both are +highly recommended companions to Embark, for opposite reasons: +Marginalia greatly enhances Embark's usefulness, while Embark can help +enhance Consult. + +In the remainder of this section I'll explain what exactly Marginalia +does for Embark, and what Embark can do for Consult. + +** Marginalia + +Embark comes with actions for symbols (commands, functions, variables +with actions such as finding the definition, looking up the +documentation, evaluating, etc.) in the =embark-symbol-map= keymap, and +for packages (actions like install, delete, browse url, etc.) in the +=embark-package-keymap=. + +Unfortunately Embark does not automatically offers you these keymaps +when relevant, because many built-in Emacs commands don't report +accurate category metadata. For example, a command like +=describe-package=, which reads a package name from the minibuffer, +does not have metadata indicating this fact. + +In an earlier Embark version, there were functions to supply this +missing metadata, but they have been moved to Marginalia, which +augments many Emacs command to report accurate category metadata. +Simply activating =marginalia-mode= allows Embark to offer you the +package and symbol actions when appropriate again. Candidate +annotations in the Embark collect buffer are also provided by the +Marginalia package: + +- If you install Marginalia and activate =marginalia-mode=, Embark + Collect buffers will use the Marginalia annotations automatically. + +- If you don't install Marginalia, you will see only the annotations + that come with Emacs (such as key bindings in =M-x=, or the unicode + characters in =C-x 8 RET=). + +** Consult + +The excellent Consult package provides many commands that use +minibuffer completion, via the =completing-read= function; plenty of its +commands can be considered enhanced versions of built-in Emacs +commands, and some are completely new functionality. One common +enhancement provided in all commands for which it makes sense is +preview functionality, for example =consult-buffer= will show you a +quick preview of a buffer before you actually switch to it. + +If you use both Consult and Embark you should install the +=embark-consult= package which provides integration between the two. It +provides exporters for several Consult commands and also tweaks the +behavior of many Consult commands when used as actions with =embark-act= +in subtle ways that you may not even notice, but make for a smoother +experience. You need only install it to get these benefits: Embark +will automatically load it after Consult if found. + +The =embark-consult= package provides the following exporters: + +- You can use =embark-export= from =consult-line=, =consult-outline=, or + =consult-mark= to obtain an =occur-mode= buffer. As with the built-in + =occur= command you use that buffer to jump to a match and after that, + you can then use =next-error= and =previous-error= to navigate to other + matches. You can also press =e= to activate =occur-edit-mode= and edit + the matches in place! + +- You can export from any of the Consult asynchronous search commands, + =consult-grep=, =consult-git-grep=, or =consult-ripgrep= to get a + =grep-mode= buffer. Here too you can use =next-error= and =previous-error= + to navigate among matches, and, if you install the [[http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep.el ][wgrep]] package, + you can use it to edit the matches in place. + +In both cases, pressing =g= will rerun the Consult command you had +exported from and re-enter the input you had typed (which is similar +to reverting but a little more flexible). You can then proceed to +re-export if that's what you want, but you can also edit the input +changing the search terms or simply cancel if you see you are done +with that search. + +The =embark-consult= also contains some candidates collectors that allow +you to run =embark-live= to get a live-updating table of contents for +your buffer: + +- =embark-consult-outline-candidates= produces the outline headings of + the current buffer, using =consult-outline=. +- =embark-consult-imenu-candidates= produces the imenu items of + the current buffer, using =consult-imenu=. +- =embark-consult-imenu-or-outline-candidates= is a simple combination + of the two previous functions: it produces imenu items in buffers + deriving from =prog-mode= and otherwise outline headings. + +The way to configure =embark-live= (or =embark-collect= and =embark-export= +for that matter) to use one of these function is to add it at the end +of the =embark-candidate-collectors= list. The =embark-consult= package by +default adds the last one, which seems to be the most sensible +default. + +Besides those exporters and candidate collectors, the =embark-consult= +package provides many subtle tweaks and small integrations between +Embark and Consult. For example, if you run =embark-collect= from any of +the the =consult-yank= family of commands, you'll see the Embark Collect +buffers has full multi-line kill-ring entries with zebra stripes, so +you can easily tell where they start and end. + +Some examples of little tweaks provided by =embark-consult= to the +behavior of Consult commands when used as Embark actions are: + +- The asynchronous search commands will start in the directory + associated to the Embark target if that target is a file, buffer, + bookmark or Emacs Lisp library. + + - For all other target types, a Consult search command (asynchronous + or not) will search for the text of the target but leave the + minibuffer open so you can interact with the Consult command. + +- =consult-imenu= will search for the target and take you directly to + the location if it matches a unique imenu entry, otherwise it will + leave the minibuffer open so you can navigate among the matches. + +* Resources + +If you want to learn more about how others have used Embark here are +some links to read: + +- [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]], a blog post by Karthik Chikmagalur. +- [[https://protesilaos.com/dotemacs/][Protesilaos Stavrou's dotemacs]], look for the section called + "Extended minibuffer actions and more (embark.el and + prot-embark.el)" + +And some videos to watch: + +- [[https://protesilaos.com/codelog/2021-01-09-emacs-embark-extras/][Embark and my extras]] by Protesilaos Stavrou. +- [[https://youtu.be/qpoQiiinCtY][Embark -- Key features and tweaks]] by Raoul Comninos on the + Emacs-Elements YouTube channel. +- [[https://youtu.be/WsxXr1ncukY][Livestreamed: Adding an Embark context action to send a stream + message]] by Sacha Chua. +- [[https://youtu.be/qk2Is_sC8Lk][System Crafters Live! - The Many Uses of Embark]] by David Wilson. +- [[https://youtu.be/5ffb2at2d7w][Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark]] by + Mike Zamansky. + +* Contributions + +Contributions to Embark are very welcome. There is a [[https://github.com/oantolin/embark/issues/95][wish list]] for +actions, target finders, candidate collectors and exporters. For other +ideas you have for Embark, feel free to open an issue on the [[https://github.com/oantolin/embark/issues][issue +tracker]]. Any neat configuration tricks you find might be a good fit +for the [[https://github.com/oantolin/embark/wiki][wiki]]. + +Code contributions are very welcome too, but since Embark is now on +GNU ELPA, copyright assignment to the FSF is required before you can +contribute code. + +* Acknowledgments + +While I, Omar Antolín Camarena, have written most of the Embark code +and remain very stubborn about some of the design decisions, Embark +has received substantial help from a number of other people which this +document has neglected to mention for far too long. In particular, +Daniel Mendler has been absolutely invaluable, implementing several +important features, and providing a lot of useful advice. + +Code contributions: + +- [[https://github.com/minad][Daniel Mendler]] +- [[https://github.com/clemera/][Clemens Radermacher]] +- [[https://codeberg.org/jao/][José Antonio Ortega Ruiz]] +- [[https://github.com/iyefrat][Itai Y. Efrat]] +- [[https://github.com/a13][a13]] +- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] +- [[https://github.com/mihakam][mihakam]] +- [[https://github.com/leungbk][Brian Leung]] +- [[https://github.com/karthink][Karthik Chikmagalur]] +- [[https://github.com/roshanshariff][Roshan Shariff]] +- [[https://github.com/condy0919][condy0919]] +- [[https://github.com/DamienCassou][Damien Cassou]] +- [[https://github.com/JimDBh][JimDBh]] + +Advice and useful discussions: + +- [[https://github.com/minad][Daniel Mendler]] +- [[https://gitlab.com/protesilaos/][Protesilaos Stavrou]] +- [[https://github.com/clemera/][Clemens Radermacher]] +- [[https://github.com/hmelman/][Howard Melman]] +- [[https://github.com/astoff][Augusto Stoffel]] +- [[https://github.com/bdarcus][Bruce d'Arcus]] +- [[https://github.com/jdtsmith][JD Smith]] +- [[https://github.com/karthink][Karthik Chikmagalur]] +- [[https://github.com/jakanakaevangeli][jakanakaevangeli]] +- [[https://github.com/iyefrat][Itai Y. Efrat]] +- [[https://github.com/mohkale][Mohsin Kaleem]] diff --git a/elpa/embark-consult-0.7/embark-consult-autoloads.el b/elpa/embark-consult-0.7/embark-consult-autoloads.el @@ -0,0 +1,33 @@ +;;; embark-consult-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (file-name-directory load-file-name)) (car load-path))) + + + +;;; Generated autoloads from embark-consult.el + +(register-definition-prefixes "embark-consult" '("embark-consult-")) + + +;;; Generated autoloads from embark-org.el + +(register-definition-prefixes "embark-org" '("embark-org-")) + +;;; End of scraped data + +(provide 'embark-consult-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; embark-consult-autoloads.el ends here diff --git a/elpa/embark-consult-0.7/embark-consult-pkg.el b/elpa/embark-consult-0.7/embark-consult-pkg.el @@ -0,0 +1,2 @@ +;; Generated package description from embark-consult.el -*- no-byte-compile: t -*- +(define-package "embark-consult" "0.7" "Consult integration for Embark" '((emacs "27.1") (embark "0.20") (consult "0.17")) :commit "127492fac048cecfdba145d496a902022a68d1d5" :authors '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) :maintainer '("Omar Antolín Camarena" . "omar@matem.unam.mx") :keywords '("convenience") :url "https://github.com/oantolin/embark") diff --git a/elpa/embark-consult-0.7/embark-consult.el b/elpa/embark-consult-0.7/embark-consult.el @@ -0,0 +1,427 @@ +;;; embark-consult.el --- Consult integration for Embark -*- lexical-binding: t; -*- + +;; Copyright (C) 2021, 2022 Free Software Foundation, Inc. + +;; Author: Omar Antolín Camarena <omar@matem.unam.mx> +;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx> +;; Keywords: convenience +;; Version: 0.7 +;; Homepage: https://github.com/oantolin/embark +;; Package-Requires: ((emacs "27.1") (embark "0.20") (consult "0.17")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package provides integration between Embark and Consult. The package +;; will be loaded automatically by Embark. + +;; Some of the functionality here was previously contained in Embark +;; itself: + +;; - Support for consult-buffer, so that you get the correct actions +;; for each type of entry in consult-buffer's list. + +;; - Support for consult-line, consult-outline, consult-mark and +;; consult-global-mark, so that the insert and save actions don't +;; include a weird unicode character at the start of the line, and so +;; you can export from them to an occur buffer (where occur-edit-mode +;; works!). + +;; Just load this package to get the above functionality, no further +;; configuration is necessary. + +;; Additionally this package contains some functionality that has +;; never been in Embark: access to Consult preview from auto-updating +;; Embark Collect buffer that is associated to an active minibuffer +;; for a Consult command. For information on Consult preview, see +;; Consult's info manual or its readme on GitHub. + +;; If you always want the minor mode enabled whenever it possible use: + +;; (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode) + +;; If you don't want the minor mode automatically on and prefer to +;; trigger the consult previews manually use this instead: + +;; (keymap-set embark-collect-mode-map "C-j" +;; #'consult-preview-at-point) + +;;; Code: + +(require 'embark) +(require 'consult) + +(eval-when-compile + (require 'cl-lib)) + +;;; Consult preview from Embark Collect buffers + +(defun embark-consult--collect-candidate () + "Return candidate at point in collect buffer." + (cadr (embark-target-collect-candidate))) + +(add-hook 'consult--completion-candidate-hook #'embark-consult--collect-candidate) + +;;; Support for consult-location + +(defun embark-consult--strip (string) + "Strip substrings marked with the `consult-strip' property from STRING." + (if (text-property-not-all 0 (length string) 'consult-strip nil string) + (let ((end (length string)) (pos 0) (chunks)) + (while (< pos end) + (let ((next (next-single-property-change pos 'consult-strip string end))) + (unless (get-text-property pos 'consult-strip string) + (push (substring string pos next) chunks)) + (setq pos next))) + (apply #'concat (nreverse chunks))) + string)) + +(defun embark-consult--target-strip (type target) + "Remove the unicode suffix character from a TARGET of TYPE." + (cons type (embark-consult--strip target))) + +(setf (alist-get 'consult-location embark-transformer-alist) + #'embark-consult--target-strip) + +(defun embark-consult-goto-location (target) + "Jump to consult location TARGET." + (consult--jump (car (consult--get-location target))) + (pulse-momentary-highlight-one-line (point))) + +(setf (alist-get 'consult-location embark-default-action-overrides) + #'embark-consult-goto-location) + +(defun embark-consult-export-occur (lines) + "Create an occur mode buffer listing LINES. +The elements of LINES are assumed to be values of category `consult-line'." + (let ((buf (generate-new-buffer "*Embark Export Occur*")) + (mouse-msg "mouse-2: go to this occurrence") + last-buf) + (with-current-buffer buf + (dolist (line lines) + (pcase-let* + ((`(,loc . ,num) (consult--get-location line)) + ;; the text properties added to the following strings are + ;; taken from occur-engine + (lineno (propertize (format "%7d:" num) + 'occur-prefix t + ;; Allow insertion of text at the end + ;; of the prefix (for Occur Edit mode). + 'front-sticky t + 'rear-nonsticky t + 'occur-target loc + 'follow-link t + 'help-echo mouse-msg)) + (contents (propertize (embark-consult--strip line) + 'occur-target loc + 'occur-match t + 'follow-link t + 'help-echo mouse-msg)) + (nl (propertize "\n" 'occur-target loc)) + (this-buf (marker-buffer loc))) + (unless (eq this-buf last-buf) + (insert (propertize + (format "lines from buffer: %s\n" this-buf) + 'face list-matching-lines-buffer-name-face)) + (setq last-buf this-buf)) + (insert (concat lineno contents nl)))) + (goto-char (point-min)) + (occur-mode)) + (pop-to-buffer buf))) + +(defun embark-consult--upgrade-markers () + "Upgrade consult-location cheap markers to real markers. +This function is meant to be added to `embark-collect-mode-hook'." + (when (eq embark--type 'consult-location) + (dolist (entry tabulated-list-entries) + (when (car entry) + (consult--get-location (car entry)))))) + +(setf (alist-get 'consult-location embark-exporters-alist) + #'embark-consult-export-occur) +(cl-pushnew #'embark-consult--upgrade-markers embark-collect-mode-hook) + +;;; Support for consult-grep + +(defvar grep-mode-line-matches) +(defvar grep-num-matches-found) +(defvar wgrep-header/footer-parser) +(declare-function wgrep-setup "ext:wgrep") + +(defvar-keymap embark-consult-revert-map + :doc "A keymap with a binding for revert-buffer." + :parent nil + "g" #'revert-buffer) + +(defun embark-consult-export-grep (lines) + "Create a grep mode buffer listing LINES." + (let ((buf (generate-new-buffer "*Embark Export Grep*")) + (count 0) + prop) + (with-current-buffer buf + (insert (propertize "Exported grep results:\n\n" 'wgrep-header t)) + (dolist (line lines) (insert line "\n")) + (goto-char (point-min)) + (while (setq prop (text-property-search-forward + 'face 'consult-highlight-match t)) + (cl-incf count) + (put-text-property (prop-match-beginning prop) + (prop-match-end prop) + 'font-lock-face + 'match)) + (goto-char (point-min)) + (grep-mode) + (when (> count 0) + (setq-local grep-num-matches-found count + mode-line-process grep-mode-line-matches)) + ;; Make this buffer current for next/previous-error + (setq next-error-last-buffer buf) + ;; Set up keymap before possible wgrep-setup, so that wgrep + ;; restores our binding too when the user finishes editing. + (use-local-map (make-composed-keymap + embark-consult-revert-map + (current-local-map))) + (setq-local wgrep-header/footer-parser #'ignore) + (when (fboundp 'wgrep-setup) (wgrep-setup))) + (pop-to-buffer buf))) + +(defun embark-consult-goto-grep (location) + "Go to LOCATION, which should be a string with a grep match." + ;; Actions are run in the target window, so in this case whatever + ;; window was selected when the command that produced the + ;; xref-location candidates ran. In particular, we inherit the + ;; default-directory of the buffer in that window, but we really + ;; want the default-directory of the minibuffer or collect window we + ;; call the action from, which is the previous window, since the + ;; location is given relative to that directory. + (let ((default-directory (with-selected-window (previous-window) + default-directory))) + (consult--jump (consult--grep-position location)) + (pulse-momentary-highlight-one-line (point)))) + +(setf (alist-get 'consult-grep embark-default-action-overrides) + #'embark-consult-goto-grep) +(setf (alist-get 'consult-grep embark-exporters-alist) + #'embark-consult-export-grep) + +;;; Support for consult-xref + +(declare-function xref--show-xref-buffer "ext:xref") +(declare-function consult-xref "ext:consult-xref") +(defvar xref-auto-jump-to-first-xref) +(defvar consult-xref--fetcher) + +(defun embark-consult-export-xref (items) + "Create an xref buffer listing ITEMS." + (cl-flet ((xref-items (items) + (mapcar (lambda (item) (get-text-property 0 'consult-xref item)) + items))) + (let ((fetcher consult-xref--fetcher) + (input (minibuffer-contents))) + (set-buffer + (xref--show-xref-buffer + (lambda () + (let ((candidates (funcall fetcher))) + (if (null (cdr candidates)) + candidates + (catch 'xref-items + (minibuffer-with-setup-hook + (lambda () + (insert input) + (add-hook + 'minibuffer-exit-hook + (lambda () + (throw 'xref-items + (xref-items + (or + (plist-get + (embark--maybe-transform-candidates) + :candidates) + (user-error "No candidates for export"))))) + nil t)) + (consult-xref fetcher)))))) + `((fetched-xrefs . ,(xref-items items)) + (window . ,(embark--target-window)) + (auto-jump . ,xref-auto-jump-to-first-xref) + (display-action))))))) + +(setf (alist-get 'consult-xref embark-exporters-alist) + #'embark-consult-export-xref) + +;;; Support for consult-find and consult-locate + +(setf (alist-get '(file . consult-find) embark-default-action-overrides) + #'find-file) + +(setf (alist-get '(file . consult-locate) embark-default-action-overrides) + #'find-file) + +;;; Support for consult-isearch + +(setf (alist-get 'consult-isearch embark-transformer-alist) + #'embark-consult--target-strip) + +;;; Support for consult-man and consult-info + +(defun embark-consult-man (cand) + "Default action override for `consult-man', open CAND man page." + (man (get-text-property 0 'consult-man cand))) + +(setf (alist-get 'consult-man embark-default-action-overrides) + #'embark-consult-man) + +(declare-function consult-info--action "ext:consult-info") + +(defun embark-consult-info (cand) + "Default action override for `consult-info', open CAND info manual." + (consult-info--action cand) + (pulse-momentary-highlight-one-line (point))) + +(setf (alist-get 'consult-info embark-default-action-overrides) + #'embark-consult-info) + +(setf (alist-get 'consult-info embark-transformer-alist) + #'embark-consult--target-strip) + +;;; Bindings for consult commands in embark keymaps + +(keymap-set embark-become-file+buffer-map "C b" #'consult-buffer) +(keymap-set embark-become-file+buffer-map "C 4 b" #'consult-buffer-other-window) + +;;; Support for Consult search commands + +(defvar-keymap embark-consult-sync-search-map + :doc "Keymap for Consult sync search commands" + :parent nil + "o" #'consult-outline + "i" 'consult-imenu + "I" 'consult-imenu-multi + "l" #'consult-line + "L" #'consult-line-multi) + +(defvar-keymap embark-consult-async-search-map + :doc "Keymap for Consult async search commands" + :parent nil + "g" #'consult-grep + "r" #'consult-ripgrep + "G" #'consult-git-grep + "f" #'consult-find + "F" #'consult-locate) + +(defvar embark-consult-search-map + (keymap-canonicalize + (make-composed-keymap embark-consult-sync-search-map + embark-consult-async-search-map)) + "Keymap for all Consult search commands.") + +(fset 'embark-consult-sync-search-map embark-consult-sync-search-map) +(keymap-set embark-become-match-map "C" 'embark-consult-sync-search-map) + +(cl-pushnew 'embark-consult-async-search-map embark-become-keymaps) + +(fset 'embark-consult-search-map embark-consult-search-map) +(keymap-set embark-general-map "C" 'embark-consult-search-map) + +(map-keymap + (lambda (_key cmd) + (cl-pushnew 'embark--allow-edit + (alist-get cmd embark-target-injection-hooks))) + embark-consult-search-map) + +(defun embark-consult--unique-match (&rest _) + "If there is a unique matching candidate, accept it. +This is intended to be used in `embark-target-injection-hooks'." + (let ((candidates (cdr (embark-minibuffer-candidates)))) + (if (or (null candidates) (cdr candidates)) + (embark--allow-edit) + (delete-minibuffer-contents) + (insert (car candidates))))) + +(dolist (cmd '(consult-outline consult-imenu consult-imenu-multi)) + (setf (alist-get cmd embark-target-injection-hooks) + (remq 'embark--allow-edit + (alist-get cmd embark-target-injection-hooks))) + (cl-pushnew #'embark-consult--unique-match + (alist-get cmd embark-target-injection-hooks))) + +(cl-defun embark-consult--prep-async (&key type target &allow-other-keys) + "Either add Consult's async separator or ignore the TARGET depending on TYPE. +If the TARGET of the given TYPE has an associated notion of +directory, we don't want to search for the text of target, but +rather just start a search in the associated directory. + +This is intended to be used in `embark-target-injection-hooks' +for any action that is a Consult async command." + (let* ((style (alist-get consult-async-split-style + consult-async-split-styles-alist)) + (initial (plist-get style :initial)) + (separator (plist-get style :separator)) + (directory (embark--associated-directory target type))) + (when directory + (delete-minibuffer-contents)) + (when initial + (goto-char (minibuffer-prompt-end)) + (insert initial) + (goto-char (point-max))) + (when (and separator (null directory)) + (goto-char (point-max)) + (insert separator)))) + +(map-keymap + (lambda (_key cmd) + (cl-pushnew #'embark--cd (alist-get cmd embark-around-action-hooks)) + (cl-pushnew #'embark-consult--prep-async + (alist-get cmd embark-target-injection-hooks))) + embark-consult-async-search-map) + +;;; Tables of contents for buffers: imenu and outline candidate collectors + +(defun embark-consult-outline-candidates () + "Collect all outline headings in the current buffer." + (cons 'consult-location (consult--outline-candidates))) + +(autoload 'consult-imenu--items "consult-imenu") + +(defun embark-consult-imenu-candidates () + "Collect all imenu items in the current buffer." + (cons 'imenu (mapcar #'car (consult-imenu--items)))) + +(declare-function consult-imenu--group "ext:consult-imenu") + +(defun embark-consult--imenu-group-function (type prop) + "Return a suitable group-function for imenu. +TYPE is the completion category. +PROP is the metadata property. +Meant as :after-until advice for `embark-collect--metadatum'." + (when (and (eq type 'imenu) (eq prop 'group-function)) + (consult-imenu--group))) + +(advice-add #'embark-collect--metadatum :after-until + #'embark-consult--imenu-group-function) + +(defun embark-consult-imenu-or-outline-candidates () + "Collect imenu items in prog modes buffer or outline headings otherwise." + (if (derived-mode-p 'prog-mode) + (embark-consult-imenu-candidates) + (embark-consult-outline-candidates))) + +(setf (alist-get 'imenu embark-default-action-overrides) 'consult-imenu) + +(add-to-list 'embark-candidate-collectors + #'embark-consult-imenu-or-outline-candidates + 'append) + +(provide 'embark-consult) +;;; embark-consult.el ends here diff --git a/elpa/embark-consult-0.7/embark-org.el b/elpa/embark-consult-0.7/embark-org.el @@ -0,0 +1,432 @@ +;;; embark-org.el --- Embark targets and actions for Org Mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package configures the Embark package for use in Org Mode +;; buffers. It teaches Embark a number of Org related targets and +;; appropriate actions. Currently it has table cells, whole tables, +;; source blocks and links. Targets to add: headings (Embark already +;; has generic support for outlines, so we just nee to add Org +;; specific actions), timestamps, etc. + +;;; Code: + +(require 'embark) +(require 'org) +(require 'org-element) + +;;; Basic target finder for Org + +;; There are very many org element and objects types, we'll only +;; recognize those for which there are specific actions we can put in +;; a keymap, or for even if there aren't any specific actions, if it's +;; import to be able to kill, delete or duplicate (embark-insert) them +;; conveniently. I'll start conservatively and we can add more later + +(defconst embark-org--types + '( + babel-call + ;; bold + ;; center-block + ;; citation + ;; citation-reference + ;; clock + ;; code + ;; comment + ;; comment-block + ;; diary-sexp + ;; drawer + ;; dynamic-block + ;; entity + ;; example-block + ;; export-block + ;; export-snippet + ;; fixed-width + footnote-definition + footnote-reference + ;; headline ; the bounds include the entire subtree! + ;; horizontal-rule + ;; inline-babel-call + ;; inline-src-block + ;; inlinetask + ;; italic + item + ;; keyword + ;; latex-environment + ;; latex-fragment + ;; line-break + link + ;; macro + ;; node-property + ;; paragraph ; the existing general support seems fine + plain-list + ;; planning + ;; property-drawer + ;; quote-block + ;; radio-target + ;; section + ;; special-block + src-block + ;; statistics-cookie + ;; strike-through + ;; subscript + ;; superscript + table ; supported via a specific target finder + table-cell + ;; table-row ; we'll put row & column actions in the cell map + ;; target ; I think there are no useful actions for radio targets + timestamp + ;; underline + ;; verbatim + ;; verse-block + ) + "Supported Org object and element types.") + +(defun embark-org-target-element-context () + "Target all Org elements or objects around point." + (when (derived-mode-p 'org-mode 'org-agenda-mode) + (cl-loop + for elt = (org-element-lineage (org-element-context) embark-org--types t) + then (org-element-lineage elt embark-org--types) + while elt + for begin = (org-element-property :begin elt) + for end = (org-element-property :end elt) + for target = (buffer-substring begin end) + ;; Adjust table-cell to exclude final |. (Why is that there?) + ;; Note: We are not doing this is an embark transformer because we + ;; want to adjust the bounds too. + ;; TODO? If more adjustments like this become necessary, add a + ;; nice mechanism for doing them. + when (and (eq (car elt) 'table-cell) (string-suffix-p "|" target)) + do (setq target (string-trim (string-remove-suffix "|" target)) + end (1- end)) + collect `(,(intern (format "org-%s" (car elt))) ,target ,begin . ,end)))) + +(add-to-list 'embark-target-finders 'embark-org-target-element-context) + +;;; Custom Org actions + +(defvar org-export-with-toc) + +(defun embark-org-copy-as-markdown (start end) + "Export the region from START to END to markdown and save on the `kill-ring'." + (interactive "r") + (require 'ox) + (kill-new + (let (org-export-with-toc) + (string-trim + (org-export-string-as (buffer-substring-no-properties start end) 'md t)))) + (deactivate-mark)) + +(add-to-list 'embark-pre-action-hooks + '(embark-org-copy-as-markdown embark--mark-target)) + +(keymap-set embark-region-map "M" #'embark-org-copy-as-markdown) ; good idea? + +;;; Tables + +(dolist (motion '(org-table-move-cell-up org-table-move-cell-down + org-table-move-cell-left org-table-move-cell-right)) + (add-to-list 'embark-repeat-actions motion)) + +(push 'embark--ignore-target + (alist-get 'org-table-edit-field embark-target-injection-hooks)) + +(defvar-keymap embark-org-table-cell-map + :doc "Keymap for actions the current cells, column or row of an Org table." + :parent embark-general-map + ;; TODO: default action? + "<up>" #'org-table-move-cell-up + "<down>" #'org-table-move-cell-down + "<left>" #'org-table-move-cell-left + "<right>" #'org-table-move-cell-right + "=" #'org-table-eval-formula + "e" #'org-table-edit-field + "g" #'org-table-recalculate) + +(defvar-keymap embark-org-table-map + :doc "Keymap for actions on entire Org table." + :parent embark-general-map + ;; TODO: default action? + "=" #'org-table-edit-formulas + "s" #'org-table-sort-lines + "t" #'org-table-transpose-table-at-point + "c" #'org-table-convert + "f" #'org-table-follow-field-mode + "y" #'org-table-paste-rectangle + "d" #'org-table-toggle-formula-debugger + "i" #'org-table-iterate + "e" #'org-table-export) + +(push 'embark--ignore-target ; prompts for file name + (alist-get 'org-table-export embark-target-injection-hooks)) + +(add-to-list 'embark-keymap-alist '(org-table . embark-org-table-map)) + +(add-to-list 'embark-keymap-alist '(org-table-cell . embark-org-table-cell-map)) + +;;; Links + +;; The link support has a slightly complicated design in order to +;; achieve the following goals: + +;; 1. RET should simply be org-open-at-point + +;; 2. When the link is to a file, URL, email address or elisp +;; expression or command, we want to offer the user actions for +;; that underlying type. + +;; 3. Even in those cases, we still want some actions to apply to the +;; entire link including description: actions to copy the link as +;; markdown, or just the link description or target. + +;; So the strategy is as follows (illustrated with file links): + +;; - The target will be just the file, without the description and +;; also without the "file:" prefix nor the "::line-number or search" +;; suffix. That way, file actions will correctly apply to it. + +;; - The type will not be 'file, but 'org-file-link that way we can +;; register a keymap for 'org-file-link that inherits from both +;; embark-org-link-map (with RET bound to org-open-at-point and a +;; few other generic link actions) and embark-file-map. + +;; - The commands to copy the link at point in some format will be +;; written as commands that act on the Org link at point. This way +;; they are independently (plausibly) useful, and we circumvent the +;; problem that the whole Org link is not actually the target (just +;; the inner file is!). + +;; Alternative design I considered: separate each target into two, a +;; whole link target which includes the description and brackets and +;; what not; and an "inner target" which is just the file or URL or +;; whatever. Cons of this approach: much target cycling is required! +;; First of all, an unadorned embark-dwim definitely should be +;; org-open-at-point, which means the whole link target would need +;; priority. That means that any file, URL, etc. actions would require +;; you to cycle first. This sounds very inconvenient, the above +;; slightly more complex design allows both whole-link and inner +;; target actions to work without cycling. + +(autoload 'org-attach-dir "org-attach") + +(defun embark-org--refine-link-type (_type target) + "Refine type of link TARGET if we have more specific actions available." + (when (string-match org-link-any-re target) + (let ((target (or (match-string-no-properties 2 target) + (match-string-no-properties 0 target)))) + (cond + ((string-prefix-p "http" target) + (cons 'org-url-link target)) + ((string-prefix-p "mailto:" target) + (cons 'org-email-link (string-remove-prefix "mailto:" target))) + ((string-prefix-p "file:" target) + (cons 'org-file-link + (replace-regexp-in-string + "::.*" "" (string-remove-prefix "file:" target)))) + ((string-prefix-p "attachment:" target) + (cons 'org-file-link + (expand-file-name + (replace-regexp-in-string + "::.*" "" (string-remove-prefix "attachment:" target)) + (org-attach-dir)))) + ((string-match-p "^[./]" target) + (cons 'org-file-link (abbreviate-file-name (expand-file-name target)))) + ((string-prefix-p "elisp:(" target) + (cons 'org-expression-link (string-remove-prefix "elisp:" target))) + ((string-prefix-p "elisp:" target) + (cons 'command (string-remove-prefix "elisp:" target))) + (t (cons 'org-link target)))))) + +(add-to-list 'embark-transformer-alist + '(org-link . embark-org--refine-link-type)) + +(defmacro embark-org-define-link-copier (name formula description) + "Define a command that copies the Org link at point according to FORMULA. +The command's name is formed by appending NAME to +embark-org-copy-link. The docstring includes the DESCRIPTION of +what part or in what format the link is copied." + `(defun ,(intern (format "embark-org-copy-link-%s" name)) () + ,(format "Copy to the kill-ring the Org link at point%s." description) + (interactive) + (when (org-in-regexp org-link-any-re) + (let* ((full (match-string-no-properties 0)) + (target (or (match-string-no-properties 2) + (match-string-no-properties 0))) + (description (match-string-no-properties 3)) + (kill ,formula)) + (ignore full target description) + (when kill + (message "Saved '%s'" kill) + (kill-new kill)))))) + +(embark-org-define-link-copier in-full full " in full") +(embark-org-define-link-copier description description "'s description") +(embark-org-define-link-copier target target "'s target") + +(defalias 'embark-org-copy-link-inner-target #'kill-new + "Copy inner part of the Org link at point's target. +For mailto and elisp links, the inner part is the portion of the +target after `mailto:' or `elisp:'. + +For file links the inner part is the file name, without the +`file:' prefix and without `::' suffix (used for line numbers, +IDs or search terms). + +For URLs the inner part is the whole target including the `http:' +or `https:' prefix. For any other type of link the inner part is +also the whole target.") + +(defvar-keymap embark-org-link-copy-map + :doc "Keymap for different ways to copy Org links to the kill-ring. + +You should bind w in this map to your most frequently used link +copying function. The default is for w to copy the \"inner +target\" (see `embark-org-copy-link-inner-target'); which is also +bound to i." + :parent nil + "w" #'embark-org-copy-link-inner-target + "f" #'embark-org-copy-link-in-full + "d" #'embark-org-copy-link-description + "t" #'embark-org-copy-link-target + "i" #'embark-org-copy-link-inner-target + "m" #'embark-org-copy-as-markdown) + +(fset 'embark-org-link-copy-map embark-org-link-copy-map) + +(defvar-keymap embark-org-link-map + :doc "Keymap for actions on Org links." + :parent embark-general-map + "RET" #'org-open-at-point + "'" #'org-insert-link + "w" #'embark-org-link-copy-map) + +;; The reason for this is left as an exercise to the reader. +;; Solution: Na ryvfc gnetrg znl cebzcg gur hfre sbe fbzrguvat! +(push 'embark--ignore-target + (alist-get 'org-open-at-point embark-target-injection-hooks)) + +(push 'embark--ignore-target + (alist-get 'org-insert-link embark-target-injection-hooks)) + +(add-to-list 'embark-keymap-alist + '(org-link embark-org-link-map)) +(add-to-list 'embark-keymap-alist + '(org-url-link embark-org-link-map embark-url-map)) +(add-to-list 'embark-keymap-alist + '(org-email-link embark-org-link-map embark-email-map)) +(add-to-list 'embark-keymap-alist + '(org-file-link embark-org-link-map embark-file-map)) +(add-to-list 'embark-keymap-alist + '(org-expression-link embark-org-link-map embark-expression-map)) + +;;; Source blocks and babel calls + +(defvar-keymap embark-org-src-block-map + :doc "Keymap for actions on Org source blocks." + :parent embark-general-map + "RET" #'org-babel-execute-src-block + "c" #'org-babel-check-src-block + "k" #'org-babel-remove-result-one-or-many + "p" #'org-babel-previous-src-block + "n" #'org-babel-next-src-block + "t" #'org-babel-tangle + "s" #'org-babel-switch-to-session + "l" #'org-babel-load-in-session + "'" #'org-edit-special) + +(dolist (motion '(org-babel-next-src-blockorg-babel-previous-src-block)) + (add-to-list 'embark-repeat-actions motion)) + +(add-to-list 'embark-keymap-alist '(org-src-block . embark-org-src-block-map)) + +;;; List items + +(defvar-keymap embark-org-item-map + :doc "Keymap for actions on Org list items." + :parent embark-general-map + "RET" #'org-toggle-checkbox + "c" #'org-toggle-checkbox + "t" #'org-toggle-item + "n" #'org-next-item + "p" #'org-previous-item + "<left>" #'org-outdent-item + "<right>" #'org-indent-item + "<up>" #'org-move-item-up + "<down>" #'org-move-item-down + ">" #'org-indent-item-tree + "<" #'org-outdent-item-tree) + +(dolist (cmd '(org-toggle-checkbox + org-toggle-item + org-next-item + org-previous-item + org-outdent-item + org-indent-item + org-move-item-up + org-move-item-down + org-indent-item-tree + org-outdent-item-tree)) + (add-to-list 'embark-repeat-actions cmd)) + +(add-to-list 'embark-keymap-alist '(org-item . embark-org-item-map)) + +;;; Org plain lists + +(defvar-keymap embark-org-plain-list-map + :doc "Keymap for actions on plain Org lists." + :parent embark-general-map + "RET" #'org-list-repair + "r" #'org-list-repair + "s" #'org-sort-list + "b" #'org-cycle-list-bullet + "t" #'org-list-make-subtree + "c" #'org-toggle-checkbox) + +(add-to-list 'embark-repeat-actions 'org-cycle-list-bullet) + +(add-to-list 'embark-keymap-alist '(org-plain-list . embark-org-plain-list-map)) + +(cl-defun embark-org--toggle-checkboxes + (&rest rest &key run type &allow-other-keys) + "Around action hook for `org-toggle-checkbox'. +See `embark-around-action-hooks' for the keyword arguments RUN and TYPE. +REST are the remaining arguments." + (apply (if (eq type 'org-plain-list) #'embark--mark-target run) + :type type + rest)) + +(cl-pushnew #'embark-org--toggle-checkboxes + (alist-get 'org-toggle-checkbox embark-around-action-hooks)) + +;;; "Encode" region using Org export in place + +(defvar-keymap embark-org-export-in-place-map + :doc "Keymap for actions which replace the region by an exported version." + :parent embark-general-map + "m" #'org-md-convert-region-to-md + "h" #'org-html-convert-region-to-html + "a" #'org-ascii-convert-region-to-ascii + "l" #'org-latex-convert-region-to-latex) + +(fset 'embark-org-export-in-place-map embark-org-export-in-place-map) + +(keymap-set embark-encode-map "o" 'embark-org-export-in-place-map) + +(provide 'embark-org) +;;; embark-org.el ends here diff --git a/init.el b/init.el @@ -130,6 +130,9 @@ ;; Isearch integration ("M-s e" . consult-isearch) + ("C-." . embark-act) + ("M-." . embark-dwim) + ("C-+" . er/expand-region) ("M-o" . ace-window) @@ -199,6 +202,9 @@ ("<mouse-9>" . sly-inspector-next)) sly) +(with-eval-after-load 'icomplete + (define-key icomplete-minibuffer-map (kbd "C-.") nil)) + (defun lh/elfeed-show-visit () (interactive) (let ((link (elfeed-entry-link elfeed-show-entry))) @@ -239,6 +245,7 @@ (add-hook 'emacs-lisp-mode-hook #'paredit-mode) ;; Aggressive Intent mode causes 100% CPU for me whenever the sly repl prints warnings (add-hook 'sly-mrepl-mode-hook (lambda () (aggressive-indent-mode -1))) +(add-hook 'embark-collect-mode-hook 'consult-preview-at-point-mode) ;; notmuch's tag selection doesnt work with consult-completing-read-multiple ;; items need to be trimmed @@ -358,7 +365,7 @@ ("melpa-stable" . "https://stable.melpa.org/packages/") ("melpa" . "https://melpa.org/packages/"))) '(package-selected-packages - '(all-the-icons-completion all-the-icons-ibuffer all-the-icons-dired sly-named-readtables sly-macrostep denote-refs denote-menu denote ox-epub ob-powershell powershell web-mode lexic editorconfig elfeed-tube-mpv elfeed-tube cider restclient-jq graphviz-dot-mode consult-eglot jq-mode multiple-cursors ob-restclient restclient vterm deadgrep helpful pdf-tools paredit-menu paredit corfu sly eglot aggressive-indent project nov nhexl-mode elfeed magit yaml-mode json-mode lua-mode go-mode geiser-guile geiser org-contrib org ace-window expand-region consult marginalia uuidgen request diminish which-key)) + '(embark-consult embark all-the-icons-completion all-the-icons-ibuffer all-the-icons-dired sly-named-readtables sly-macrostep denote-refs denote-menu denote ox-epub ob-powershell powershell web-mode lexic editorconfig elfeed-tube-mpv elfeed-tube cider restclient-jq graphviz-dot-mode consult-eglot jq-mode multiple-cursors ob-restclient restclient vterm deadgrep helpful pdf-tools paredit-menu paredit corfu sly eglot aggressive-indent project nov nhexl-mode elfeed magit yaml-mode json-mode lua-mode go-mode geiser-guile geiser org-contrib org ace-window expand-region consult marginalia uuidgen request diminish which-key)) '(pcomplete-ignore-case t t) '(pixel-scroll-precision-mode t) '(read-buffer-completion-ignore-case t)